]> BookStack Code Mirror - bookstack/blob - resources/js/wysiwyg/lexical/core/nodes/LexicalLineBreakNode.ts
2d28db08c12213a82f99eb95e6b7c25b0c27373c
[bookstack] / resources / js / wysiwyg / lexical / core / nodes / LexicalLineBreakNode.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 {KlassConstructor} from '../LexicalEditor';
10 import type {
11   DOMConversionMap,
12   DOMConversionOutput,
13   NodeKey,
14   SerializedLexicalNode,
15 } from '../LexicalNode';
16
17 import {DOM_TEXT_TYPE} from '../LexicalConstants';
18 import {LexicalNode} from '../LexicalNode';
19 import {$applyNodeReplacement, isBlockDomNode} from '../LexicalUtils';
20
21 export type SerializedLineBreakNode = SerializedLexicalNode;
22
23 /** @noInheritDoc */
24 export class LineBreakNode extends LexicalNode {
25   ['constructor']!: KlassConstructor<typeof LineBreakNode>;
26   static getType(): string {
27     return 'linebreak';
28   }
29
30   static clone(node: LineBreakNode): LineBreakNode {
31     return new LineBreakNode(node.__key);
32   }
33
34   constructor(key?: NodeKey) {
35     super(key);
36   }
37
38   getTextContent(): '\n' {
39     return '\n';
40   }
41
42   createDOM(): HTMLElement {
43     return document.createElement('br');
44   }
45
46   updateDOM(): false {
47     return false;
48   }
49
50   static importDOM(): DOMConversionMap | null {
51     return {
52       br: (node: Node) => {
53         if (isOnlyChildInBlockNode(node) || isLastChildInBlockNode(node)) {
54           return null;
55         }
56         return {
57           conversion: $convertLineBreakElement,
58           priority: 0,
59         };
60       },
61     };
62   }
63
64   static importJSON(
65     serializedLineBreakNode: SerializedLineBreakNode,
66   ): LineBreakNode {
67     return $createLineBreakNode();
68   }
69
70   exportJSON(): SerializedLexicalNode {
71     return {
72       type: 'linebreak',
73       version: 1,
74     };
75   }
76 }
77
78 function $convertLineBreakElement(node: Node): DOMConversionOutput {
79   return {node: $createLineBreakNode()};
80 }
81
82 export function $createLineBreakNode(): LineBreakNode {
83   return $applyNodeReplacement(new LineBreakNode());
84 }
85
86 export function $isLineBreakNode(
87   node: LexicalNode | null | undefined,
88 ): node is LineBreakNode {
89   return node instanceof LineBreakNode;
90 }
91
92 function isOnlyChildInBlockNode(node: Node): boolean {
93   const parentElement = node.parentElement;
94   if (parentElement !== null && isBlockDomNode(parentElement)) {
95     const firstChild = parentElement.firstChild!;
96     if (
97       firstChild === node ||
98       (firstChild.nextSibling === node && isWhitespaceDomTextNode(firstChild))
99     ) {
100       const lastChild = parentElement.lastChild!;
101       if (
102         lastChild === node ||
103         (lastChild.previousSibling === node &&
104           isWhitespaceDomTextNode(lastChild))
105       ) {
106         return true;
107       }
108     }
109   }
110   return false;
111 }
112
113 function isLastChildInBlockNode(node: Node): boolean {
114   const parentElement = node.parentElement;
115   if (parentElement !== null && isBlockDomNode(parentElement)) {
116     // check if node is first child, because only childs dont count
117     const firstChild = parentElement.firstChild!;
118     if (
119       firstChild === node ||
120       (firstChild.nextSibling === node && isWhitespaceDomTextNode(firstChild))
121     ) {
122       return false;
123     }
124
125     // check if its last child
126     const lastChild = parentElement.lastChild!;
127     if (
128       lastChild === node ||
129       (lastChild.previousSibling === node && isWhitespaceDomTextNode(lastChild))
130     ) {
131       return true;
132     }
133   }
134   return false;
135 }
136
137 function isWhitespaceDomTextNode(node: Node): boolean {
138   return (
139     node.nodeType === DOM_TEXT_TYPE &&
140     /^( |\t|\r?\n)+$/.test(node.textContent || '')
141   );
142 }