diff --git a/demo/src/examples/BasicExample.vue b/demo/src/examples/BasicExample.vue index 61b18643..e6482493 100644 --- a/demo/src/examples/BasicExample.vue +++ b/demo/src/examples/BasicExample.vue @@ -13,15 +13,10 @@ export default defineComponent({ }, setup: () => { const content = ref( - new Delta([ - { insert: 'Gandalf', attributes: { bold: true } }, - { insert: ' the ' }, - { insert: 'Grey', attributes: { color: '#ccc' } }, - ]) + new Delta() ) return { content } }, }) - diff --git a/demo/src/examples/ContentType.vue b/demo/src/examples/ContentType.vue index 35ed218c..8324cc7f 100644 --- a/demo/src/examples/ContentType.vue +++ b/demo/src/examples/ContentType.vue @@ -50,10 +50,12 @@ export default defineComponent({ ]); contentHTML.value = '

This is a different HTML header

'; contentText.value = 'This is some more plain text'; + + setTimeout(() => + contentDelta.value.insert('\n I am also deeply reactive and a ref update works'), 200) } return { contentDelta, contentHTML, contentText, update } }, }) - diff --git a/packages/vue-quill/src/components/QuillEditor.ts b/packages/vue-quill/src/components/QuillEditor.ts index ff3f8c8a..176234a9 100644 --- a/packages/vue-quill/src/components/QuillEditor.ts +++ b/packages/vue-quill/src/components/QuillEditor.ts @@ -185,12 +185,18 @@ export const QuillEditor = defineComponent({ ) } + const maybeClone = (delta: Delta | string) => { + return typeof delta === 'object' ? delta.slice() : delta + } + const deltaHasValuesOtherThanRetain = (delta: Delta): boolean => { - return Object.values(delta).some((v) => !v.retain) + return Object.values(delta.ops).some( + (v) => !v.retain || Object.keys(v).length !== 1 + ) } - // eslint-disable-next-line vue/no-setup-props-destructure - let internalModel = props.content // Doesn't need reactivity + // Doesn't need reactivity, but does need to be cloned to avoid deep mutations always registering as equal + let internalModel: typeof props.content const internalModelEquals = (against: Delta | String | undefined) => { if (typeof internalModel === typeof against) { if (against === internalModel) { @@ -211,10 +217,7 @@ export const QuillEditor = defineComponent({ oldContents: Delta, source: Sources ) => { - // Quill should never be null at this point because we receive an event - // so content should not be undefined but let's make ts and eslint happy - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - internalModel = getContents()! + internalModel = maybeClone(getContents() as string | Delta) // Update v-model:content when text changes if (!internalModelEquals(props.content)) { ctx.emit('update:content', internalModel) @@ -302,6 +305,7 @@ export const QuillEditor = defineComponent({ } else { quill?.setContents(content as Delta, source) } + internalModel = maybeClone(content) } const getText = (index?: number, length?: number): string => { @@ -338,14 +342,14 @@ export const QuillEditor = defineComponent({ (newContent) => { if (!quill || !newContent || internalModelEquals(newContent)) return - internalModel = newContent // Restore the selection and cursor position after updating the content const selection = quill.getSelection() if (selection) { nextTick(() => quill?.setSelection(selection)) } setContents(newContent) - } + }, + { deep: true } ) watch(