]> BookStack Code Mirror - bookstack/blob - resources/js/wysiwyg/nodes/index.ts
7b274eba13c83eabb09523d2e005d4f0163290d0
[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 {CustomListItemNode} from "./custom-list-item";
21 import {CustomTableCellNode} from "./custom-table-cell";
22 import {CustomTableRowNode} from "./custom-table-row";
23 import {CustomListNode} from "./custom-list";
24 import {HeadingNode} from "@lexical/rich-text/LexicalHeadingNode";
25 import {QuoteNode} from "@lexical/rich-text/LexicalQuoteNode";
26
27 /**
28  * Load the nodes for lexical.
29  */
30 export function getNodesForPageEditor(): (KlassConstructor<typeof LexicalNode> | LexicalNodeReplacement)[] {
31     return [
32         CalloutNode,
33         HeadingNode,
34         QuoteNode,
35         CustomListNode,
36         CustomListItemNode, // TODO - Alignment?
37         CustomTableNode,
38         CustomTableRowNode,
39         CustomTableCellNode,
40         ImageNode, // TODO - Alignment
41         HorizontalRuleNode,
42         DetailsNode, SummaryNode,
43         CodeBlockNode,
44         DiagramNode,
45         MediaNode, // TODO - Alignment
46         ParagraphNode,
47         LinkNode,
48         {
49             replace: ListNode,
50             with: (node: ListNode) => {
51                 return new CustomListNode(node.getListType(), node.getStart());
52             }
53         },
54         {
55             replace: ListItemNode,
56             with: (node: ListItemNode) => {
57                 return new CustomListItemNode(node.__value, node.__checked);
58             }
59         },
60         {
61             replace: TableNode,
62             with(node: TableNode) {
63                 return new CustomTableNode();
64             }
65         },
66         {
67             replace: TableRowNode,
68             with(node: TableRowNode) {
69                 return new CustomTableRowNode();
70             }
71         },
72         {
73             replace: TableCellNode,
74             with: (node: TableCellNode) => {
75                 const cell = new CustomTableCellNode(
76                     node.__headerState,
77                     node.__colSpan,
78                     node.__width,
79                 );
80                 cell.__rowSpan = node.__rowSpan;
81                 return cell;
82             }
83         },
84     ];
85 }
86
87 export function registerCommonNodeMutationListeners(context: EditorUiContext): void {
88     const decorated = [ImageNode, CodeBlockNode, DiagramNode];
89
90     const decorationDestroyListener = (mutations: Map<string, NodeMutation>): void => {
91         for (let [nodeKey, mutation] of mutations) {
92             if (mutation === "destroyed") {
93                 const decorator = context.manager.getDecoratorByNodeKey(nodeKey);
94                 if (decorator) {
95                     decorator.destroy(context);
96                 }
97             }
98         }
99     };
100
101     for (let decoratedNode of decorated) {
102         // Have to pass a unique function here since they are stored by lexical keyed on listener function.
103         context.editor.registerMutationListener(decoratedNode, (mutations) => decorationDestroyListener(mutations));
104     }
105 }
106
107 export type LexicalNodeMatcher = (node: LexicalNode|null|undefined) => boolean;
108 export type LexicalElementNodeCreator = () => ElementNode;