]> BookStack Code Mirror - bookstack/blob - resources/js/wysiwyg/nodes/index.ts
Lexical: Merged list nodes
[bookstack] / resources / js / wysiwyg / nodes / index.ts
1 import {CalloutNode} from './callout';
2 import {
3     ElementNode,
4     KlassConstructor,
5     LexicalNode,
6     LexicalNodeReplacement, NodeMutation,
7     ParagraphNode
8 } from "lexical";
9 import {LinkNode} from "@lexical/link";
10 import {ImageNode} from "./image";
11 import {DetailsNode, SummaryNode} from "./details";
12 import {ListItemNode, ListNode} from "@lexical/list";
13 import {TableCellNode, TableNode, TableRowNode} from "@lexical/table";
14 import {CustomTableNode} from "./custom-table";
15 import {HorizontalRuleNode} from "./horizontal-rule";
16 import {CodeBlockNode} from "./code-block";
17 import {DiagramNode} from "./diagram";
18 import {EditorUiContext} from "../ui/framework/core";
19 import {MediaNode} from "./media";
20 import {CustomTableCellNode} from "./custom-table-cell";
21 import {CustomTableRowNode} from "./custom-table-row";
22 import {HeadingNode} from "@lexical/rich-text/LexicalHeadingNode";
23 import {QuoteNode} from "@lexical/rich-text/LexicalQuoteNode";
24
25 /**
26  * Load the nodes for lexical.
27  */
28 export function getNodesForPageEditor(): (KlassConstructor<typeof LexicalNode> | LexicalNodeReplacement)[] {
29     return [
30         CalloutNode,
31         HeadingNode,
32         QuoteNode,
33         ListNode,
34         ListItemNode,
35         CustomTableNode,
36         CustomTableRowNode,
37         CustomTableCellNode,
38         ImageNode, // TODO - Alignment
39         HorizontalRuleNode,
40         DetailsNode, SummaryNode,
41         CodeBlockNode,
42         DiagramNode,
43         MediaNode, // TODO - Alignment
44         ParagraphNode,
45         LinkNode,
46         {
47             replace: TableNode,
48             with(node: TableNode) {
49                 return new CustomTableNode();
50             }
51         },
52         {
53             replace: TableRowNode,
54             with(node: TableRowNode) {
55                 return new CustomTableRowNode();
56             }
57         },
58         {
59             replace: TableCellNode,
60             with: (node: TableCellNode) => {
61                 const cell = new CustomTableCellNode(
62                     node.__headerState,
63                     node.__colSpan,
64                     node.__width,
65                 );
66                 cell.__rowSpan = node.__rowSpan;
67                 return cell;
68             }
69         },
70     ];
71 }
72
73 export function registerCommonNodeMutationListeners(context: EditorUiContext): void {
74     const decorated = [ImageNode, CodeBlockNode, DiagramNode];
75
76     const decorationDestroyListener = (mutations: Map<string, NodeMutation>): void => {
77         for (let [nodeKey, mutation] of mutations) {
78             if (mutation === "destroyed") {
79                 const decorator = context.manager.getDecoratorByNodeKey(nodeKey);
80                 if (decorator) {
81                     decorator.destroy(context);
82                 }
83             }
84         }
85     };
86
87     for (let decoratedNode of decorated) {
88         // Have to pass a unique function here since they are stored by lexical keyed on listener function.
89         context.editor.registerMutationListener(decoratedNode, (mutations) => decorationDestroyListener(mutations));
90     }
91 }
92
93 export type LexicalNodeMatcher = (node: LexicalNode|null|undefined) => boolean;
94 export type LexicalElementNodeCreator = () => ElementNode;