Files
harheimertc/components/RichTextEditor.vue
2025-12-20 10:17:16 +01:00

160 lines
3.2 KiB
Vue

<template>
<div>
<label
v-if="label"
class="block text-sm font-medium text-gray-700 mb-2"
>
{{ label }}
<span
v-if="required"
class="text-red-500"
>*</span>
</label>
<div
ref="editorContainer"
class="border border-gray-300 rounded-lg bg-white"
/>
<input
type="hidden"
:value="modelValue"
>
</div>
</template>
<script setup>
import { ref, watch, onMounted, onBeforeUnmount } from 'vue'
const props = defineProps({
modelValue: {
type: String,
default: ''
},
label: {
type: String,
default: ''
},
required: {
type: Boolean,
default: false
}
})
const emit = defineEmits(['update:modelValue'])
const editorContainer = ref(null)
let quill = null
onMounted(async () => {
if (process.client && editorContainer.value) {
// Dynamisch Quill nur im Client laden
const Quill = (await import('quill')).default
await import('quill/dist/quill.snow.css')
quill = new Quill(editorContainer.value, {
theme: 'snow',
modules: {
toolbar: [
[{ 'header': [1, 2, 3, false] }],
['bold', 'italic', 'underline', 'strike'],
[{ 'color': [] }, { 'background': [] }],
[{ 'list': 'ordered' }, { 'list': 'bullet' }],
[{ 'align': [] }],
['link', 'image'],
['blockquote', 'code-block'],
['clean']
]
},
placeholder: 'Newsletter-Inhalt eingeben...'
})
// Setze initialen Inhalt
if (props.modelValue) {
quill.root.innerHTML = props.modelValue
}
// Emitiere Änderungen
quill.on('text-change', () => {
const html = quill.root.innerHTML
// Prüfe ob Inhalt wirklich geändert wurde (nicht nur leere Tags)
const textContent = quill.getText().trim()
if (textContent || html !== '<p><br></p>') {
emit('update:modelValue', html)
} else {
emit('update:modelValue', '')
}
})
}
})
watch(() => props.modelValue, (newValue) => {
if (quill && quill.root.innerHTML !== newValue) {
// Temporär Event-Listener entfernen um Endlosschleife zu vermeiden
const currentContent = quill.root.innerHTML
if (currentContent !== newValue) {
quill.root.innerHTML = newValue || ''
}
}
})
onBeforeUnmount(() => {
if (quill) {
quill = null
}
})
</script>
<style>
/* Quill Editor Styles */
.ql-container {
font-family: Arial, sans-serif;
font-size: 14px;
min-height: 300px;
}
.ql-editor {
min-height: 300px;
}
.ql-editor.ql-blank::before {
color: #9ca3af;
font-style: normal;
}
.ql-toolbar {
border-top-left-radius: 0.5rem;
border-top-right-radius: 0.5rem;
border-bottom: 1px solid #e5e7eb;
}
.ql-container {
border-bottom-left-radius: 0.5rem;
border-bottom-right-radius: 0.5rem;
}
.ql-snow .ql-stroke {
stroke: #374151;
}
.ql-snow .ql-fill {
fill: #374151;
}
.ql-snow .ql-picker-label {
color: #374151;
}
.ql-snow .ql-tooltip {
background-color: #ffffff;
border: 1px solid #e5e7eb;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
}
.ql-snow .ql-tooltip input[type=text] {
border: 1px solid #d1d5db;
}
.ql-snow .ql-tooltip a.ql-action::after {
border-right: 1px solid #d1d5db;
}
</style>