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