]> BookStack Code Mirror - bookstack/blob - resources/js/wysiwyg/nodes/custom-list-item.ts
Lexical: Started table menu options
[bookstack] / resources / js / wysiwyg / nodes / custom-list-item.ts
1 import {$isListNode, ListItemNode, SerializedListItemNode} from "@lexical/list";
2 import {EditorConfig} from "lexical/LexicalEditor";
3 import {DOMExportOutput, LexicalEditor, LexicalNode} from "lexical";
4 import {el} from "../helpers";
5
6 function updateListItemChecked(
7     dom: HTMLElement,
8     listItemNode: ListItemNode,
9 ): void {
10     // Only set task list attrs for leaf list items
11     const shouldBeTaskItem = !$isListNode(listItemNode.getFirstChild());
12     dom.classList.toggle('task-list-item', shouldBeTaskItem);
13     if (listItemNode.__checked) {
14         dom.setAttribute('checked', 'checked');
15     } else {
16         dom.removeAttribute('checked');
17     }
18 }
19
20
21 export class CustomListItemNode extends ListItemNode {
22     static getType(): string {
23         return 'custom-list-item';
24     }
25
26     static clone(node: CustomListItemNode): CustomListItemNode {
27         return new CustomListItemNode(node.__value, node.__checked, node.__key);
28     }
29
30     createDOM(config: EditorConfig): HTMLElement {
31         const element = document.createElement('li');
32         const parent = this.getParent();
33
34         if ($isListNode(parent) && parent.getListType() === 'check') {
35             updateListItemChecked(element, this);
36         }
37
38         element.value = this.__value;
39
40         return element;
41     }
42
43     updateDOM(
44         prevNode: ListItemNode,
45         dom: HTMLElement,
46         config: EditorConfig,
47     ): boolean {
48         const parent = this.getParent();
49         if ($isListNode(parent) && parent.getListType() === 'check') {
50             updateListItemChecked(dom, this);
51         }
52         // @ts-expect-error - this is always HTMLListItemElement
53         dom.value = this.__value;
54
55         return false;
56     }
57
58     exportDOM(editor: LexicalEditor): DOMExportOutput {
59         const element = this.createDOM(editor._config);
60         element.style.textAlign = this.getFormatType();
61
62         if (element.classList.contains('task-list-item')) {
63             const input = el('input', {
64                 type: 'checkbox',
65                 disabled: 'disabled',
66             });
67             if (element.hasAttribute('checked')) {
68                 input.setAttribute('checked', 'checked');
69                 element.removeAttribute('checked');
70             }
71
72             element.prepend(input);
73         }
74
75         return {
76             element,
77         };
78     }
79
80     exportJSON(): SerializedListItemNode {
81         return {
82             ...super.exportJSON(),
83             type: 'custom-list-item',
84         };
85     }
86 }
87
88 export function $isCustomListItemNode(
89     node: LexicalNode | null | undefined,
90 ): node is CustomListItemNode {
91     return node instanceof CustomListItemNode;
92 }