2 * Copyright (c) Meta Platforms, Inc. and affiliates.
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
9 import type {KlassConstructor} from '../LexicalEditor';
14 SerializedLexicalNode,
15 } from '../LexicalNode';
17 import {DOM_TEXT_TYPE} from '../LexicalConstants';
18 import {LexicalNode} from '../LexicalNode';
19 import {$applyNodeReplacement, isBlockDomNode} from '../LexicalUtils';
21 export type SerializedLineBreakNode = SerializedLexicalNode;
24 export class LineBreakNode extends LexicalNode {
25 ['constructor']!: KlassConstructor<typeof LineBreakNode>;
26 static getType(): string {
30 static clone(node: LineBreakNode): LineBreakNode {
31 return new LineBreakNode(node.__key);
34 constructor(key?: NodeKey) {
38 getTextContent(): '\n' {
42 createDOM(): HTMLElement {
43 return document.createElement('br');
50 static importDOM(): DOMConversionMap | null {
53 if (isOnlyChildInBlockNode(node) || isLastChildInBlockNode(node)) {
57 conversion: $convertLineBreakElement,
65 serializedLineBreakNode: SerializedLineBreakNode,
67 return $createLineBreakNode();
70 exportJSON(): SerializedLexicalNode {
78 function $convertLineBreakElement(node: Node): DOMConversionOutput {
79 return {node: $createLineBreakNode()};
82 export function $createLineBreakNode(): LineBreakNode {
83 return $applyNodeReplacement(new LineBreakNode());
86 export function $isLineBreakNode(
87 node: LexicalNode | null | undefined,
88 ): node is LineBreakNode {
89 return node instanceof LineBreakNode;
92 function isOnlyChildInBlockNode(node: Node): boolean {
93 const parentElement = node.parentElement;
94 if (parentElement !== null && isBlockDomNode(parentElement)) {
95 const firstChild = parentElement.firstChild!;
97 firstChild === node ||
98 (firstChild.nextSibling === node && isWhitespaceDomTextNode(firstChild))
100 const lastChild = parentElement.lastChild!;
102 lastChild === node ||
103 (lastChild.previousSibling === node &&
104 isWhitespaceDomTextNode(lastChild))
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!;
119 firstChild === node ||
120 (firstChild.nextSibling === node && isWhitespaceDomTextNode(firstChild))
125 // check if its last child
126 const lastChild = parentElement.lastChild!;
128 lastChild === node ||
129 (lastChild.previousSibling === node && isWhitespaceDomTextNode(lastChild))
137 function isWhitespaceDomTextNode(node: Node): boolean {
139 node.nodeType === DOM_TEXT_TYPE &&
140 /^( |\t|\r?\n)+$/.test(node.textContent || '')