You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and dots ('.'), can be up to 35 characters long. Letters must be lowercase.
 
 
 
 

278 lines
9.0 KiB

<script setup>
import { ref, onMounted, onBeforeUnmount, defineProps, defineEmits } from 'vue'
import { Editor, EditorContent } from '@tiptap/vue-3'
import StarterKit from '@tiptap/starter-kit'
import TextAlign from '@tiptap/extension-text-align'
import Underline from '@tiptap/extension-underline'
import Highlight from '@tiptap/extension-highlight'
import TextStyle from '@tiptap/extension-text-style'
import Mention from '@tiptap/extension-mention'
import Color from '@tiptap/extension-color'
import Link from '@tiptap/extension-link'
import Placeholder from '@tiptap/extension-placeholder'
import Typography from '@tiptap/extension-typography'
import CharacterCount from '@tiptap/extension-character-count'
import Image from '@tiptap/extension-image'
import TaskList from '@tiptap/extension-task-list'
import TaskItem from '@tiptap/extension-task-item'
import Table from '@tiptap/extension-table'
import TableRow from '@tiptap/extension-table-row'
import TableCell from '@tiptap/extension-table-cell'
import TableHeader from '@tiptap/extension-table-header'
import Tabs from './Tabs.vue'
import TextTab from './Tabs/TextTab.vue'
import BlockTab from './Tabs/BlockTab.vue'
import ListTab from './Tabs/ListTab.vue'
import MediaTab from './Tabs/MediaTab.vue'
import TableTab from './Tabs/TableTab.vue'
import HistoryTab from './Tabs/HistoryTab.vue'
import BubbleMenu from './BubbleMenu.vue'
import FloatingMenu from './FloatingMenu.vue'
const props = defineProps({
content: {
type: Object || String,
default: `
<h2 style="text-align: left">Bienvenue dans l'éditeur de page ✨</h2>
<p style="text-align: left">Voici les fonctionnalités disponibles :</p>
<h3 style="text-align: left">1. Mise en forme de texte</h3>
<p style="text-align: left">Essayez les boutons de la barre d'outils pour :</p>
<ul>
<li><p style="text-align: left"><strong>Gras</strong> (Ctrl+B)</p></li>
<li><p style="text-align: left"><em>Italique</em> (Ctrl+I)</p></li>
<li><p style="text-align: left"><s>Barré</s></p></li>
<li><p style="text-align: left">et plein d'autre</p></li>
</ul>
<h3 style="text-align: left">2. Titres et hiérarchie</h3>
<p style="text-align: left">Utilisez le menu déroulant pour choisir parmi 4 niveaux de titres</p>
<h1 style="text-align: left">Titre 1</h1>
<h2 style="text-align: left">Titre 2</h2>
<h3 style="text-align: left">Titre 3</h3>
<h4 style="text-align: left">Titre 4</h4>
<h3 style="text-align: left">3. Listes</h3>
<ul>
<li><p style="text-align: left">Liste à puces</p></li>
<li>
<p style="text-align: left">Structurez votre contenu</p>
<ul>
<li><p style="text-align: left">Liste dans une liste </p></li>
</ul>
</li>
</ul>
<ol>
<li><p style="text-align: left">Liste numérotée</p></li>
<li><p style="text-align: left">Étapes séquentielles</p></li>
</ol>
<h3 style="text-align: left">4. Element (Conteneur)</h3>
<p style="text-align: left">Les <em>conteneurs </em>structurent les éléments visuels de manière <strong>isolée</strong> et <strong>cohérente</strong>.</p>
<p style="text-align: left">Quelques examples :</p>
<blockquote>
<p style="text-align: left">Voici une citation</p>
<p style="text-align: left">- Vinayak Ambigapathy 2025</p>
</blockquote>
<p style="text-align: left"><br class="ProseMirror-trailingBreak"></p>
<pre>
<code>Voici une note </code>
</pre>
<p style="text-align: left">D’autre element arriverons bientot pour vous permettre de faire des page manifique.</p>
<h3>5. Tableaux structurés</h3>
<p>Créez des tableaux :</p>
<table class="demo-table">
<thead>
<tr>
<th>Fonctionnalité</th>
<th>Raccourci</th>
<th>Exemple</th>
</tr>
</thead>
<tbody>
<tr>
<td>Ligne horizontale</td>
<td>Ctrl+Shift+H</td>
<td><hr></td>
</tr>
<tr>
<td>Cellule de titre</td>
<td>Toujour en gras et au bord du tableau avec la cellule grisé</td>
<th>Titre</th>
</tr>
<tr>
<td>Cellule standard</td>
<td>La classique</td>
<td>Contenu</td>
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="3">Astuce : Utilisez Tab pour naviguer entre les cellules</td>
</tr>
</tfoot>
</table>
<p>Fonctionnalités disponibles :</p>
<ul>
<li>Insertion/suppression de lignes/colonnes</li>
<li>Fusion/séparation de cellules</li>
<li>Alignement du contenu</li>
</ul>
<p>Certaines de ces fonctionnalité ne fonctionne pas encore au mieux mais cela devrait changer bientot.</p>
<h3 style="text-align: left">6. Raccourcis clavier</h3>
<p style="text-align: left">Voici quelque racourci clavier. Essayez ces combinaisons :</p>
<ul>
<li><p style="text-align: left">Ctrl+Z → Annuler</p></li>
<li><p style="text-align: left">Ctrl+Shift+Z → Rétablir</p></li>
<li><p style="text-align: left">Ctrl+Shift+8 → Liste à puces</p></li>
<li><p style="text-align: left">Ctrl+Shift+7 → Liste numérotée</p></li>
</ul>
`
},
editable: {
type: Boolean,
default: true,
}
})
const emit = defineEmits(['update'])
const editor = ref(null)
const edit = ref(props.editable);
const activeTab = ref('text')
defineExpose({
editable: (flag) => {
edit.value = flag;
editor.value.setEditable(flag)
}
});
const ImageAlign = Image.extend({
addAttributes() {
return {
...this.parent?.(),
alignment: {
default: 'left',
renderHTML: attributes => {
return {
class: `image-align-${attributes.alignment}`,
}
},
},
}
},
})
// Initialize editor
onMounted(() => {
editor.value = new Editor({
content: props.content,
editorProps: {
attributes: {
class: 'tiptap-editor',
},
},
extensions: [
StarterKit.configure({
heading: { levels: [1, 2, 3, 4] }
}),
Underline,
Mention.configure({
HTMLAttributes: {
class: 'mention',
},
suggestion: {
items: ({ query }) => {
return [
"Tax", "Padda", "Example"
]
}
},
}),
Highlight.configure({
multicolor: true
}),
TextStyle,
TextAlign.configure({
types: ['heading', 'paragraph'],
alignments: ['left', 'center', 'right', 'justify'],
defaultAlignment: 'left',
}),
Color,
CharacterCount,
//Gapcursor,
Link.configure({
openOnClick: false,
autolink: true
}),
Placeholder.configure({
placeholder: 'Texte...'
}),
Typography,
ImageAlign.configure({
inline: true,
allowBase64: true
}),
TaskList,
TaskItem.configure({
nested: true
}),
Table.configure({
resizable: true
}),
TableRow,
TableCell,
TableHeader
],
onUpdate: ({ editor }) => {
emit('update', {
html: editor.getHTML(),
json: editor.getJSON()
})
}
});
editor.value.setEditable(edit);
emit('update', {
html: editor.value.getHTML(),
json: editor.value.getJSON()
})
})
// Clean up
onBeforeUnmount(() => {
if (editor.value) {
editor.value.destroy()
}
})
</script>
<template>
<div v-if="editor" class="notion-editor-container">
<!-- Toolbar based on active tab -->
<div v-show="edit" class="sticky top-16 right-0 left-0 z-10 bg-white border-b border-gray-200 py-2 flex space-x-2 flex-wrap">
<Tabs @active="(tab) => activeTab = tab"/>
<TextTab v-show="activeTab == 'text'" :editor="editor" />
<BlockTab v-show="activeTab == 'blocks'" :editor="editor"/>
<ListTab v-show="activeTab == 'lists'" :editor="editor"/>
<MediaTab v-show="activeTab == 'media'" :editor="editor"/>
<TableTab v-show="activeTab == 'tables'" :editor="editor"/>
<HistoryTab v-show="activeTab == 'history'" :editor="editor"/>
</div>
<BubbleMenu v-show="edit" :editor="editor" />
<!-- Floating Menu for Block Types -->
<FloatingMenu v-show="edit" :editor="editor" />
<!-- Editor Content -->
<editor-content
:editor="editor"
:class="'tiptap-editor min-h-[500px] h-full bg-white shadow-md'"
/>
<div class="sticky bottom-0 right-0 left-0 z-10
bg-white border-t border-gray-200 p-2 flex space-x-2
text-sm text-gray-700">
<p>{{ editor.storage.characterCount.words() }} words</p>
<p>|</p>
<p>{{ editor.storage.characterCount.characters() }} characters</p>
</div>
</div>
</template>
<style>
</style>