160 lines
3.2 KiB
Vue
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>
|