fix(editor): ensure task list clicks are only fired once
Before this fix, clicking on a task list item with the same name as another one, both would get marked as done. This was due to the mechanism which walks the dom tree to look for the node to update used its content for comparison. To prevent this, this fix first added unique ids to all task list items and then compared the nodes based on their id instead of the content. Resolves https://kolaente.dev/vikunja/vikunja/issues/2091
This commit is contained in:
parent
5ab720d709
commit
3969f6ae66
@ -193,6 +193,7 @@ import {mergeAttributes} from '@tiptap/core'
|
||||
import {isEditorContentEmpty} from '@/helpers/editorContentEmpty'
|
||||
import inputPrompt from '@/helpers/inputPrompt'
|
||||
import {setLinkInEditor} from '@/components/input/editor/setLinkInEditor'
|
||||
import {createRandomID} from '@/helpers/randomId'
|
||||
|
||||
const {
|
||||
modelValue,
|
||||
@ -388,7 +389,20 @@ const editor = useEditor({
|
||||
CustomImage,
|
||||
|
||||
TaskList,
|
||||
TaskItem.configure({
|
||||
TaskItem.extend({
|
||||
addAttributes() {
|
||||
return {
|
||||
...this.parent?.(),
|
||||
id: {
|
||||
default: createRandomID,
|
||||
parseHTML: element => element.getAttribute('data-id'),
|
||||
renderHTML: attributes => ({
|
||||
'data-id': attributes.id,
|
||||
}),
|
||||
},
|
||||
}
|
||||
},
|
||||
}).configure({
|
||||
nested: true,
|
||||
onReadOnlyChecked: (node: Node, checked: boolean): boolean => {
|
||||
if (!isEditEnabled) {
|
||||
@ -400,7 +414,7 @@ const editor = useEditor({
|
||||
// https://github.com/ueberdosis/tiptap/issues/3676
|
||||
|
||||
editor.value!.state.doc.descendants((subnode, pos) => {
|
||||
if (node.eq(subnode)) {
|
||||
if (node.attrs.id === subnode.attrs.id) {
|
||||
const {tr} = editor.value!.state
|
||||
tr.setNodeMarkup(pos, undefined, {
|
||||
...node.attrs,
|
||||
@ -408,10 +422,10 @@ const editor = useEditor({
|
||||
})
|
||||
editor.value!.view.dispatch(tr)
|
||||
bubbleSave()
|
||||
return true
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
return true
|
||||
},
|
||||
}),
|
||||
@ -594,12 +608,19 @@ function clickTasklistCheckbox(event) {
|
||||
|
||||
watch(
|
||||
() => isEditing.value,
|
||||
editing => {
|
||||
nextTick(() => {
|
||||
const checkboxes = tiptapInstanceRef.value?.querySelectorAll('[data-checked]')
|
||||
async editing => {
|
||||
await nextTick()
|
||||
|
||||
let checkboxes = tiptapInstanceRef.value?.querySelectorAll('[data-checked]')
|
||||
if (typeof checkboxes === 'undefined' || checkboxes.length === 0) {
|
||||
// For some reason, this works when we check a second time.
|
||||
await nextTick()
|
||||
|
||||
checkboxes = tiptapInstanceRef.value?.querySelectorAll('[data-checked]')
|
||||
if (typeof checkboxes === 'undefined' || checkboxes.length === 0) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if (editing) {
|
||||
checkboxes.forEach(check => {
|
||||
@ -622,9 +643,9 @@ watch(
|
||||
|
||||
// We assume the first child contains the label element with the checkbox and the second child the actual label
|
||||
// When the actual label is clicked, we forward that click to the checkbox.
|
||||
check.children[1].removeEventListener('click', clickTasklistCheckbox)
|
||||
check.children[1].addEventListener('click', clickTasklistCheckbox)
|
||||
})
|
||||
})
|
||||
},
|
||||
{immediate: true},
|
||||
)
|
||||
@ -781,6 +802,7 @@ watch(
|
||||
|
||||
.ProseMirror {
|
||||
/* Table-specific styling */
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
table-layout: fixed;
|
||||
@ -836,6 +858,7 @@ watch(
|
||||
}
|
||||
|
||||
// Lists
|
||||
|
||||
ul {
|
||||
margin-left: .5rem;
|
||||
margin-top: 0 !important;
|
||||
|
Loading…
x
Reference in New Issue
Block a user