Skip to content

Commit 920f494

Browse files
committed
fix: deep reactivity of delta
1 parent f402ebb commit 920f494

File tree

2 files changed

+17
-7
lines changed

2 files changed

+17
-7
lines changed

demo/src/examples/ContentType.vue

+3-1
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,12 @@ export default defineComponent({
5050
]);
5151
contentHTML.value = '<h3>This is a different HTML header</h3>';
5252
contentText.value = 'This is some more plain text';
53+
54+
setTimeout(() =>
55+
contentDelta.value.insert('\n I am also deeply reactive and a ref update works'), 200)
5356
}
5457
5558
return { contentDelta, contentHTML, contentText, update }
5659
},
5760
})
5861
</script>
59-

packages/vue-quill/src/components/QuillEditor.ts

+14-6
Original file line numberDiff line numberDiff line change
@@ -185,12 +185,19 @@ export const QuillEditor = defineComponent({
185185
)
186186
}
187187

188+
const maybeClone = (delta: Delta | string) => {
189+
if (typeof delta === 'object') {
190+
console.log(delta.ops, delta.ops === delta.slice().ops)
191+
}
192+
return typeof delta === 'object' ? delta.slice() : delta
193+
}
194+
188195
const deltaHasValuesOtherThanRetain = (delta: Delta): boolean => {
189196
return Object.values(delta.ops).some((v) => !v.retain)
190197
}
191198

192-
// eslint-disable-next-line vue/no-setup-props-destructure
193-
let internalModel = props.content // Doesn't need reactivity
199+
// Doesn't need reactivity, but does need to be cloned to avoid deep mutations always registering as equal
200+
let internalModel: typeof props.content
194201
const internalModelEquals = (against: Delta | String | undefined) => {
195202
if (typeof internalModel === typeof against) {
196203
if (against === internalModel) {
@@ -214,7 +221,7 @@ export const QuillEditor = defineComponent({
214221
// Quill should never be null at this point because we receive an event
215222
// so content should not be undefined but let's make ts and eslint happy
216223
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
217-
internalModel = getContents()!
224+
internalModel = maybeClone(getContents()!)
218225
// Update v-model:content when text changes
219226
if (!internalModelEquals(props.content)) {
220227
ctx.emit('update:content', internalModel)
@@ -302,6 +309,7 @@ export const QuillEditor = defineComponent({
302309
} else {
303310
quill?.setContents(content as Delta, source)
304311
}
312+
internalModel = maybeClone(content)
305313
}
306314

307315
const getText = (index?: number, length?: number): string => {
@@ -320,7 +328,7 @@ export const QuillEditor = defineComponent({
320328
if (quill) quill.root.innerHTML = html
321329
}
322330

323-
const pasteHTML = (html: string, source: Sources = 'api') => {
331+
const pasteHTML = (html: string, source: Sources = 'user') => {
324332
const delta = quill?.clipboard.convert(html as {})
325333
if (delta) quill?.setContents(delta, source)
326334
}
@@ -338,14 +346,14 @@ export const QuillEditor = defineComponent({
338346
(newContent) => {
339347
if (!quill || !newContent || internalModelEquals(newContent)) return
340348

341-
internalModel = newContent
342349
// Restore the selection and cursor position after updating the content
343350
const selection = quill.getSelection()
344351
if (selection) {
345352
nextTick(() => quill?.setSelection(selection))
346353
}
347354
setContents(newContent)
348-
}
355+
},
356+
{ deep: true }
349357
)
350358

351359
watch(

0 commit comments

Comments
 (0)