]> BookStack Code Mirror - bookstack/blob - resources/js/wysiwyg/nodes/custom-table.ts
Lexical: Wired table properties, and other buttons
[bookstack] / resources / js / wysiwyg / nodes / custom-table.ts
1 import {SerializedTableNode, TableNode} from "@lexical/table";
2 import {DOMConversion, DOMConversionMap, DOMConversionOutput, LexicalNode, Spread} from "lexical";
3 import {EditorConfig} from "lexical/LexicalEditor";
4
5 import {el, extractStyleMapFromElement, StyleMap} from "../utils/dom";
6 import {getTableColumnWidths} from "../utils/tables";
7
8 export type SerializedCustomTableNode = Spread<{
9     id: string;
10     colWidths: string[];
11     styles: Record<string, string>,
12 }, SerializedTableNode>
13
14 export class CustomTableNode extends TableNode {
15     __id: string = '';
16     __colWidths: string[] = [];
17     __styles: StyleMap = new Map;
18
19     static getType() {
20         return 'custom-table';
21     }
22
23     setId(id: string) {
24         const self = this.getWritable();
25         self.__id = id;
26     }
27
28     getId(): string {
29         const self = this.getLatest();
30         return self.__id;
31     }
32
33     setColWidths(widths: string[]) {
34         const self = this.getWritable();
35         self.__colWidths = widths;
36     }
37
38     getColWidths(): string[] {
39         const self = this.getLatest();
40         return self.__colWidths;
41     }
42
43     getStyles(): StyleMap {
44         const self = this.getLatest();
45         return new Map(self.__styles);
46     }
47
48     setStyles(styles: StyleMap): void {
49         const self = this.getWritable();
50         self.__styles = new Map(styles);
51     }
52
53     static clone(node: CustomTableNode) {
54         const newNode = new CustomTableNode(node.__key);
55         newNode.__id = node.__id;
56         newNode.__colWidths = node.__colWidths;
57         newNode.__styles = new Map(node.__styles);
58         return newNode;
59     }
60
61     createDOM(config: EditorConfig): HTMLElement {
62         const dom = super.createDOM(config);
63         const id = this.getId();
64         if (id) {
65             dom.setAttribute('id', id);
66         }
67
68         const colWidths = this.getColWidths();
69         if (colWidths.length > 0) {
70             const colgroup = el('colgroup');
71             for (const width of colWidths) {
72                 const col = el('col');
73                 if (width) {
74                     col.style.width = width;
75                 }
76                 colgroup.append(col);
77             }
78             dom.append(colgroup);
79         }
80
81         for (const [name, value] of this.__styles.entries()) {
82             dom.style.setProperty(name, value);
83         }
84
85         return dom;
86     }
87
88     updateDOM(): boolean {
89         return true;
90     }
91
92     exportJSON(): SerializedCustomTableNode {
93         return {
94             ...super.exportJSON(),
95             type: 'custom-table',
96             version: 1,
97             id: this.__id,
98             colWidths: this.__colWidths,
99             styles: Object.fromEntries(this.__styles),
100         };
101     }
102
103     static importJSON(serializedNode: SerializedCustomTableNode): CustomTableNode {
104         const node = $createCustomTableNode();
105         node.setId(serializedNode.id);
106         node.setColWidths(serializedNode.colWidths);
107         node.setStyles(new Map(Object.entries(serializedNode.styles)));
108         return node;
109     }
110
111     static importDOM(): DOMConversionMap|null {
112         return {
113             table(node: HTMLElement): DOMConversion|null {
114                 return {
115                     conversion: (element: HTMLElement): DOMConversionOutput|null => {
116                         const node = $createCustomTableNode();
117
118                         if (element.id) {
119                             node.setId(element.id);
120                         }
121
122                         const colWidths = getTableColumnWidths(element as HTMLTableElement);
123                         node.setColWidths(colWidths);
124                         node.setStyles(extractStyleMapFromElement(element));
125
126                         return {node};
127                     },
128                     priority: 1,
129                 };
130             },
131         };
132     }
133 }
134
135 export function $createCustomTableNode(): CustomTableNode {
136     return new CustomTableNode();
137 }
138
139 export function $isCustomTableNode(node: LexicalNode | null | undefined): node is CustomTableNode {
140     return node instanceof CustomTableNode;
141 }