Two month of work
This commit is contained in:
@@ -0,0 +1,140 @@
|
||||
<script setup lang="ts">
|
||||
import { Editor, EditorContent, } from '@tiptap/vue-3'
|
||||
import StarterKit from '@tiptap/starter-kit'
|
||||
import { onBeforeUnmount, onMounted, ref, watch } from 'vue';
|
||||
import { ButtonGroup, ButtonGroupSeparator } from './ui/button-group';
|
||||
import { Button } from './ui/crm-button';
|
||||
import { Bold, Code2, Heading, Heading1, Heading2, Heading3, Heading4, Heading5, Heading6, Italic, List, ListOrdered, Pilcrow, Redo2, Strikethrough, Undo2 } from 'lucide-vue-next';
|
||||
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, DropdownMenuSeparator } from '@/components/ui/dropdown-menu'
|
||||
import Separator from './ui/separator/Separator.vue';
|
||||
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue?: string | null | undefined
|
||||
}>()
|
||||
const editor = ref<Editor>()
|
||||
const getContent = (): string => editor.value?.getHTML() || ''
|
||||
const isFocused = (): boolean => editor.value?.isFocused || false
|
||||
|
||||
defineExpose({ editor, getContent, isFocused });
|
||||
const emit = defineEmits(['update:modelValue', 'change:modelValue'])
|
||||
|
||||
// set editor content on prop change
|
||||
watch(() => props.modelValue, (newValue, oldValue) => {
|
||||
const isSame = editor.value?.getHTML() === newValue
|
||||
if (isSame) return
|
||||
editor.value?.commands.setContent(newValue as string)
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
editor.value = new Editor({
|
||||
extensions: [StarterKit],
|
||||
content: props.modelValue,
|
||||
onUpdate: () => {
|
||||
if (!editor.value) return
|
||||
emit('update:modelValue', editor.value.getHTML())
|
||||
},
|
||||
onBlur: () => {
|
||||
if (!editor.value) return
|
||||
emit('change:modelValue', editor.value.getHTML())
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
editor.value?.destroy()
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<template>
|
||||
|
||||
|
||||
<!-- Editor -->
|
||||
<div>
|
||||
|
||||
<!-- Menu -->
|
||||
<ButtonGroup class="editor-menu shadow border rounded-md overflow-clip z-1 bg-background">
|
||||
<Button @click="editor?.chain().focus().undo().run()" :disabled="!editor?.can().undo()" size="sm"
|
||||
variant="ghost">
|
||||
<Undo2 />
|
||||
</Button>
|
||||
<Button @click="editor?.chain().focus().redo().run()" :disabled="!editor?.can().redo()" size="sm"
|
||||
variant="ghost">
|
||||
<redo2 />
|
||||
</Button>
|
||||
|
||||
<ButtonGroupSeparator/>
|
||||
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger as-child>
|
||||
<Button size="sm" variant="ghost">
|
||||
<heading />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent>
|
||||
<DropdownMenuItem @click="editor?.chain().focus().clearNodes().run()" size="sm" variant="ghost">
|
||||
<pilcrow /> Absatz
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem @click="editor?.chain().focus().toggleHeading({ level: 1 }).run()">
|
||||
<heading1 /> Überschrift 1
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem @click="editor?.chain().focus().toggleHeading({ level: 2 }).run()">
|
||||
<heading2 /> Überschrift 2
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem @click="editor?.chain().focus().toggleHeading({ level: 3 }).run()">
|
||||
<heading3 /> Überschrift 3
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem @click="editor?.chain().focus().toggleHeading({ level: 4 }).run()">
|
||||
<heading4 /> Überschrift 4
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem @click="editor?.chain().focus().toggleHeading({ level: 5 }).run()">
|
||||
<heading5 /> Überschrift 5
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem @click="editor?.chain().focus().toggleHeading({ level: 6 }).run()">
|
||||
<heading6 /> Überschrift 6
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
<dropdown-menu>
|
||||
<dropdown-menu-trigger as-child>
|
||||
<Button size="sm" variant="ghost">
|
||||
<list />
|
||||
</Button>
|
||||
</dropdown-menu-trigger>
|
||||
<dropdown-menu-content>
|
||||
<dropdown-menu-item @click="editor?.chain().focus().toggleBulletList().run()">
|
||||
<list /> Ungeordnete Liste
|
||||
</dropdown-menu-item>
|
||||
<dropdown-menu-item @click="editor?.chain().focus().toggleOrderedList().run()"><list-ordered />
|
||||
Geordnete Liste</dropdown-menu-item>
|
||||
</dropdown-menu-content>
|
||||
</dropdown-menu>
|
||||
|
||||
<ButtonGroupSeparator/>
|
||||
|
||||
<Button @click="editor?.chain().focus().toggleBold().run()" :class="{ 'is-active': editor?.isActive('bold') }"
|
||||
size="sm" variant="ghost">
|
||||
<Bold />
|
||||
</Button>
|
||||
<Button @click="editor?.chain().focus().toggleItalic().run()" size="sm" variant="ghost">
|
||||
<Italic />
|
||||
</Button>
|
||||
<Button @click="editor?.chain().focus().toggleStrike().run()" size="sm" variant="ghost">
|
||||
<strikethrough />
|
||||
</Button>
|
||||
<Button @click="editor?.chain().focus().toggleCode().run()" size="sm" variant="ghost">
|
||||
<code2 />
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
|
||||
<EditorContent :editor="editor" class="editor mb-8" />
|
||||
<div class="absolute top-0.75 py-2 px-0.75 italic text-muted-foreground pointer-events-none"
|
||||
:class="{ 'hidden': !editor?.isEmpty }">Beschreibung</div>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<style></style>
|
||||
Reference in New Issue
Block a user