Refactor QuillyEditor and Quill Mention Extension for Improved Functionality
- Updated `QuillyEditor.vue` to enhance the handling of model updates and internal changes, ensuring better synchronization between the editor and the model value. Introduced a new `pasteHTML` function to manage HTML content pasting more effectively. - Refactored `quillMentionExtension.js` to improve the processing of pasted content, ensuring that only valid mentions are retained and transforming flat mention structures into nested ones for better data integrity. - Enhanced the SCSS styles in `app.scss` to apply list styles to ordered and unordered lists within the `.field-help` class, improving the visual presentation of help text. These changes aim to improve the overall functionality, maintainability, and user experience of the QuillyEditor component and its associated mention extension.
This commit is contained in:
parent
0b3011b4ee
commit
1a8af6257a
|
|
@ -33,15 +33,10 @@
|
|||
|
||||
let quillInstance = null
|
||||
const container = ref(null)
|
||||
let isInternalChange = false
|
||||
const model = ref(props.modelValue)
|
||||
|
||||
const setContents = (content) => {
|
||||
if (!quillInstance) return
|
||||
|
||||
isInternalChange = true
|
||||
quillInstance.root.innerHTML = content
|
||||
quillInstance.update()
|
||||
isInternalChange = false
|
||||
const pasteHTML = (instance) => {
|
||||
instance.clipboard.dangerouslyPasteHTML(props.modelValue || '', 'silent')
|
||||
}
|
||||
|
||||
const initializeQuill = () => {
|
||||
|
|
@ -58,11 +53,9 @@
|
|||
})
|
||||
|
||||
quillInstance.on('text-change', (delta, oldContents, source) => {
|
||||
if (!isInternalChange) {
|
||||
const html = quillInstance.root.innerHTML
|
||||
// Update local model only on user input
|
||||
model.value = quillInstance.getSemanticHTML()
|
||||
emit('text-change', { delta, oldContents, source })
|
||||
emit('update:modelValue', html)
|
||||
}
|
||||
})
|
||||
|
||||
quillInstance.on('editor-change', (eventName, ...args) => {
|
||||
|
|
@ -70,7 +63,8 @@
|
|||
})
|
||||
|
||||
if (props.modelValue) {
|
||||
setContents(props.modelValue)
|
||||
pasteHTML(quillInstance)
|
||||
model.value = quillInstance.getSemanticHTML()
|
||||
}
|
||||
|
||||
emit('ready', quillInstance)
|
||||
|
|
@ -81,11 +75,30 @@
|
|||
initializeQuill()
|
||||
})
|
||||
|
||||
watch(() => props.modelValue, (newValue) => {
|
||||
if (quillInstance && newValue !== quillInstance.root.innerHTML) {
|
||||
setContents(newValue || '')
|
||||
// Watch modelValue and paste HTML if has changes
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(newValue) => {
|
||||
if (!quillInstance) return
|
||||
if (newValue && newValue !== model.value) {
|
||||
pasteHTML(quillInstance)
|
||||
model.value = quillInstance.getSemanticHTML()
|
||||
} else if (!newValue) {
|
||||
quillInstance.setContents([])
|
||||
model.value = ''
|
||||
}
|
||||
}, { immediate: true })
|
||||
}
|
||||
)
|
||||
|
||||
// Watch model and update modelValue if has changes
|
||||
watch(model, (newValue, oldValue) => {
|
||||
if (!quillInstance) return
|
||||
if (newValue && newValue !== oldValue) {
|
||||
emit('update:modelValue', newValue)
|
||||
} else if (!newValue) {
|
||||
quillInstance.setContents([])
|
||||
}
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
if (quillInstance) {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import { reactive } from 'vue'
|
||||
import Quill from 'quill'
|
||||
const Inline = Quill.import('blots/inline')
|
||||
const Delta = Quill.import('delta')
|
||||
const Clipboard = Quill.import('modules/clipboard')
|
||||
|
||||
export default function registerMentionExtension(Quill) {
|
||||
|
|
@ -9,17 +8,42 @@ export default function registerMentionExtension(Quill) {
|
|||
class MentionClipboard extends Clipboard {
|
||||
convert(html) {
|
||||
const delta = super.convert(html)
|
||||
// Remove any mention formatting from pasted content
|
||||
return delta.reduce((newDelta, op) => {
|
||||
const processedDelta = delta.ops.reduce((newDelta, op) => {
|
||||
if (op.attributes && op.attributes.mention) {
|
||||
// Only keep mentions that have valid field IDs
|
||||
if (!op.attributes.mention['mention-field-id']) {
|
||||
const mentionData = op.attributes.mention
|
||||
let isValid = false
|
||||
// Check for nested structure
|
||||
if (
|
||||
mentionData &&
|
||||
typeof mentionData === 'object' &&
|
||||
mentionData.field &&
|
||||
typeof mentionData.field === 'object' &&
|
||||
mentionData.field.id
|
||||
) {
|
||||
isValid = true
|
||||
} else if (
|
||||
mentionData &&
|
||||
typeof mentionData === 'object' &&
|
||||
mentionData['mention-field-id']
|
||||
) {
|
||||
// Transform flat structure to nested structure
|
||||
op.attributes.mention = {
|
||||
field: {
|
||||
id: mentionData['mention-field-id'],
|
||||
name: mentionData['mention-field-name'] || '',
|
||||
},
|
||||
fallback: mentionData['mention-fallback'] || '',
|
||||
}
|
||||
isValid = true
|
||||
}
|
||||
if (!isValid) {
|
||||
delete op.attributes.mention
|
||||
}
|
||||
}
|
||||
newDelta.push(op)
|
||||
return newDelta
|
||||
}, new Delta())
|
||||
}, [])
|
||||
return processedDelta
|
||||
}
|
||||
}
|
||||
Quill.register('modules/clipboard', MentionClipboard, true)
|
||||
|
|
@ -43,7 +67,6 @@ export default function registerMentionExtension(Quill) {
|
|||
if (!data || !data.field || !data.field.id) {
|
||||
return
|
||||
}
|
||||
|
||||
node.setAttribute('contenteditable', 'false')
|
||||
node.setAttribute('mention', 'true')
|
||||
node.setAttribute('mention-field-id', data.field.id || '')
|
||||
|
|
@ -70,7 +93,7 @@ export default function registerMentionExtension(Quill) {
|
|||
|
||||
formats() {
|
||||
let formats = super.formats()
|
||||
formats['mention'] = MentionBlot.formats(this.domNode)
|
||||
formats['mention'] = MentionBlot.value(this.domNode)
|
||||
return formats
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -52,9 +52,17 @@ body.dark * {
|
|||
}
|
||||
|
||||
.field-help {
|
||||
p {
|
||||
p, ol, ul, li {
|
||||
@apply text-gray-400 dark:text-gray-500;
|
||||
}
|
||||
|
||||
ol {
|
||||
@apply list-decimal list-inside;
|
||||
}
|
||||
|
||||
ul {
|
||||
@apply list-disc list-inside;
|
||||
}
|
||||
}
|
||||
|
||||
.public-page {
|
||||
|
|
|
|||
Loading…
Reference in New Issue