]> BookStack Code Mirror - bookstack/blob - resources/js/wysiwyg/lexical/table/LexicalTableRowNode.ts
Lexical: Merged custom table node code
[bookstack] / resources / js / wysiwyg / lexical / table / LexicalTableRowNode.ts
1 /**
2  * Copyright (c) Meta Platforms, Inc. and affiliates.
3  *
4  * This source code is licensed under the MIT license found in the
5  * LICENSE file in the root directory of this source tree.
6  *
7  */
8
9 import type {Spread} from 'lexical';
10
11 import {addClassNamesToElement} from '@lexical/utils';
12 import {
13   $applyNodeReplacement,
14   DOMConversionMap,
15   DOMConversionOutput,
16   EditorConfig,
17   ElementNode,
18   LexicalNode,
19   NodeKey,
20   SerializedElementNode,
21 } from 'lexical';
22
23 import {extractStyleMapFromElement, sizeToPixels, StyleMap} from "../../utils/dom";
24
25 export type SerializedTableRowNode = Spread<
26   {
27     styles: Record<string, string>,
28     height?: number,
29   },
30   SerializedElementNode
31 >;
32
33 /** @noInheritDoc */
34 export class TableRowNode extends ElementNode {
35   /** @internal */
36   __height?: number;
37   /** @internal */
38   __styles: StyleMap = new Map();
39
40   static getType(): string {
41     return 'tablerow';
42   }
43
44   static clone(node: TableRowNode): TableRowNode {
45     const newNode = new TableRowNode(node.__key);
46     newNode.__styles = new Map(node.__styles);
47     return newNode;
48   }
49
50   static importDOM(): DOMConversionMap | null {
51     return {
52       tr: (node: Node) => ({
53         conversion: $convertTableRowElement,
54         priority: 0,
55       }),
56     };
57   }
58
59   static importJSON(serializedNode: SerializedTableRowNode): TableRowNode {
60     const node = $createTableRowNode();
61
62     node.setStyles(new Map(Object.entries(serializedNode.styles)));
63
64     return node;
65   }
66
67   constructor(key?: NodeKey) {
68     super(key);
69   }
70
71   exportJSON(): SerializedTableRowNode {
72     return {
73       ...super.exportJSON(),
74       type: 'tablerow',
75       version: 1,
76       styles: Object.fromEntries(this.__styles),
77       height: this.__height || 0,
78     };
79   }
80
81   createDOM(config: EditorConfig): HTMLElement {
82     const element = document.createElement('tr');
83
84     if (this.__height) {
85       element.style.height = `${this.__height}px`;
86     }
87
88     for (const [name, value] of this.__styles.entries()) {
89       element.style.setProperty(name, value);
90     }
91
92     addClassNamesToElement(element, config.theme.tableRow);
93
94     return element;
95   }
96
97   isShadowRoot(): boolean {
98     return true;
99   }
100
101   getStyles(): StyleMap {
102     const self = this.getLatest();
103     return new Map(self.__styles);
104   }
105
106   setStyles(styles: StyleMap): void {
107     const self = this.getWritable();
108     self.__styles = new Map(styles);
109   }
110
111   setHeight(height: number): number | null | undefined {
112     const self = this.getWritable();
113     self.__height = height;
114     return this.__height;
115   }
116
117   getHeight(): number | undefined {
118     return this.getLatest().__height;
119   }
120
121   updateDOM(prevNode: TableRowNode): boolean {
122     return prevNode.__height !== this.__height
123         || prevNode.__styles !== this.__styles;
124   }
125
126   canBeEmpty(): false {
127     return false;
128   }
129
130   canIndent(): false {
131     return false;
132   }
133 }
134
135 export function $convertTableRowElement(domNode: Node): DOMConversionOutput {
136   const rowNode = $createTableRowNode();
137   const domNode_ = domNode as HTMLElement;
138
139   const height = sizeToPixels(domNode_.style.height);
140   rowNode.setHeight(height);
141
142   if (domNode instanceof HTMLElement) {
143     rowNode.setStyles(extractStyleMapFromElement(domNode));
144   }
145
146   return {node: rowNode};
147 }
148
149 export function $createTableRowNode(): TableRowNode {
150   return $applyNodeReplacement(new TableRowNode());
151 }
152
153 export function $isTableRowNode(
154   node: LexicalNode | null | undefined,
155 ): node is TableRowNode {
156   return node instanceof TableRowNode;
157 }