import { Node as PMNode } from 'prosemirror-model'; import { EditorView, NodeView } from 'prosemirror-view'; /** * NodeView for task items: renders a real checkbox (contenteditable=false) * next to the editable content. Toggling writes the `checked` attr back to * the document as a single transaction. */ export class TaskItemView implements NodeView { dom: HTMLLIElement; contentDOM: HTMLElement; private checkbox: HTMLInputElement; constructor( private node: PMNode, view: EditorView, getPos: () => number | undefined ) { this.dom = document.createElement('li'); this.dom.className = 'nile-wysiwyg__task-item'; this.dom.dataset.taskItem = 'true'; this.dom.dataset.checked = String(node.attrs.checked); this.checkbox = document.createElement('input'); this.checkbox.type = 'checkbox'; this.checkbox.contentEditable = 'false'; this.checkbox.checked = node.attrs.checked; // Keep the selection in the editor while clicking. this.checkbox.addEventListener('mousedown', e => e.preventDefault()); this.checkbox.addEventListener('change', () => { const pos = getPos(); if (pos === undefined || !view.editable) { this.checkbox.checked = this.node.attrs.checked; return; } const current = view.state.doc.nodeAt(pos); if (!current) return; view.dispatch( view.state.tr.setNodeMarkup(pos, undefined, { ...current.attrs, checked: this.checkbox.checked, }) ); }); this.contentDOM = document.createElement('div'); this.contentDOM.className = 'nile-wysiwyg__task-content'; this.dom.append(this.checkbox, this.contentDOM); } update(node: PMNode): boolean { if (node.type !== this.node.type) return false; this.node = node; this.checkbox.checked = node.attrs.checked; this.dom.dataset.checked = String(node.attrs.checked); return true; } stopEvent(event: Event): boolean { return event.target === this.checkbox; } }