]> BookStack Code Mirror - bookstack/blob - resources/js/wysiwyg/lexical/core/nodes/LexicalRootNode.ts
74c8d5a7f9b91efaefc7bcba3e3f23b814e76537
[bookstack] / resources / js / wysiwyg / lexical / core / nodes / LexicalRootNode.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 {LexicalNode, SerializedLexicalNode} from '../LexicalNode';
10 import type {SerializedElementNode} from './LexicalElementNode';
11
12 import invariant from 'lexical/shared/invariant';
13
14 import {NO_DIRTY_NODES} from '../LexicalConstants';
15 import {getActiveEditor, isCurrentlyReadOnlyMode} from '../LexicalUpdates';
16 import {$getRoot} from '../LexicalUtils';
17 import {$isDecoratorNode} from './LexicalDecoratorNode';
18 import {$isElementNode, ElementNode} from './LexicalElementNode';
19
20 export type SerializedRootNode<
21   T extends SerializedLexicalNode = SerializedLexicalNode,
22 > = SerializedElementNode<T>;
23
24 /** @noInheritDoc */
25 export class RootNode extends ElementNode {
26   /** @internal */
27   __cachedText: null | string;
28
29   static getType(): string {
30     return 'root';
31   }
32
33   static clone(): RootNode {
34     return new RootNode();
35   }
36
37   constructor() {
38     super('root');
39     this.__cachedText = null;
40   }
41
42   getTopLevelElementOrThrow(): never {
43     invariant(
44       false,
45       'getTopLevelElementOrThrow: root nodes are not top level elements',
46     );
47   }
48
49   getTextContent(): string {
50     const cachedText = this.__cachedText;
51     if (
52       isCurrentlyReadOnlyMode() ||
53       getActiveEditor()._dirtyType === NO_DIRTY_NODES
54     ) {
55       if (cachedText !== null) {
56         return cachedText;
57       }
58     }
59     return super.getTextContent();
60   }
61
62   remove(): never {
63     invariant(false, 'remove: cannot be called on root nodes');
64   }
65
66   replace<N = LexicalNode>(node: N): never {
67     invariant(false, 'replace: cannot be called on root nodes');
68   }
69
70   insertBefore(nodeToInsert: LexicalNode): LexicalNode {
71     invariant(false, 'insertBefore: cannot be called on root nodes');
72   }
73
74   insertAfter(nodeToInsert: LexicalNode): LexicalNode {
75     invariant(false, 'insertAfter: cannot be called on root nodes');
76   }
77
78   // View
79
80   updateDOM(prevNode: RootNode, dom: HTMLElement): false {
81     return false;
82   }
83
84   // Mutate
85
86   append(...nodesToAppend: LexicalNode[]): this {
87     for (let i = 0; i < nodesToAppend.length; i++) {
88       const node = nodesToAppend[i];
89       if (!$isElementNode(node) && !$isDecoratorNode(node)) {
90         invariant(
91           false,
92           'rootNode.append: Only element or decorator nodes can be appended to the root node',
93         );
94       }
95     }
96     return super.append(...nodesToAppend);
97   }
98
99   static importJSON(serializedNode: SerializedRootNode): RootNode {
100     // We don't create a root, and instead use the existing root.
101     const node = $getRoot();
102     node.setFormat(serializedNode.format);
103     node.setIndent(serializedNode.indent);
104     node.setDirection(serializedNode.direction);
105     return node;
106   }
107
108   exportJSON(): SerializedRootNode {
109     return {
110       children: [],
111       direction: this.getDirection(),
112       format: this.getFormatType(),
113       indent: this.getIndent(),
114       type: 'root',
115       version: 1,
116     };
117   }
118
119   collapseAtStart(): true {
120     return true;
121   }
122 }
123
124 export function $createRootNode(): RootNode {
125   return new RootNode();
126 }
127
128 export function $isRootNode(
129   node: RootNode | LexicalNode | null | undefined,
130 ): node is RootNode {
131   return node instanceof RootNode;
132 }