UNPKG

lexical

Version:

Lexical is an extensible text editor framework that provides excellent reliability, accessible and performance.

158 lines (138 loc) 4.03 kB
/** * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * */ import type {KlassConstructor} from '../LexicalEditor'; import { type DOMConversionMap, type DOMConversionOutput, LexicalNode, type NodeKey, type SerializedLexicalNode, } from '../LexicalNode'; import { $applyNodeReplacement, $getDocument, isBlockDomNode, isDOMTextNode, } from '../LexicalUtils'; export type SerializedLineBreakNode = SerializedLexicalNode; /** @noInheritDoc */ export class LineBreakNode extends LexicalNode { /** @internal */ declare ['constructor']: KlassConstructor<typeof LineBreakNode>; static getType(): string { return 'linebreak'; } static clone(node: LineBreakNode): LineBreakNode { return new LineBreakNode(node.__key); } constructor(key?: NodeKey) { super(key); } getTextContent(): '\n' { return '\n'; } createDOM(): HTMLElement { return $getDocument().createElement('br'); } updateDOM(): false { return false; } isInline(): true { return true; } static importDOM(): DOMConversionMap | null { return { br: (node: Node) => { if (isOnlyChildInBlockNode(node) || isLastChildInBlockNode(node)) { return null; } return { conversion: $convertLineBreakElement, priority: 0, }; }, }; } static importJSON( serializedLineBreakNode: SerializedLineBreakNode, ): LineBreakNode { return $createLineBreakNode().updateFromJSON(serializedLineBreakNode); } } function $convertLineBreakElement(node: Node): DOMConversionOutput { return {node: $createLineBreakNode()}; } export function $createLineBreakNode(): LineBreakNode { return $applyNodeReplacement(new LineBreakNode()); } export function $isLineBreakNode( node: LexicalNode | null | undefined, ): node is LineBreakNode { return node instanceof LineBreakNode; } /** * True when `node` is the sole non-whitespace child of a block DOM * element. Used by the LineBreak importer to drop stray `<br>` elements * that the legacy `$generateNodesFromDOM` also skipped (matches the * behavior of `LineBreakNode.importDOM`). * * @experimental */ export function isOnlyChildInBlockNode(node: Node): boolean { const parentElement = node.parentElement; if (parentElement !== null && isBlockDomNode(parentElement)) { const firstChild = parentElement.firstChild!; if ( firstChild === node || (firstChild.nextSibling === node && isWhitespaceDomTextNode(firstChild)) ) { const lastChild = parentElement.lastChild!; if ( lastChild === node || (lastChild.previousSibling === node && isWhitespaceDomTextNode(lastChild)) ) { return true; } } } return false; } /** * True when `node` is the trailing non-whitespace child of a block DOM * element (excluding the only-child case). Used by the LineBreak * importer to drop trailing `<br>` elements like the Apple-interchange * clipboard artifact (matches `LineBreakNode.importDOM`). * * @experimental */ export function isLastChildInBlockNode(node: Node): boolean { const parentElement = node.parentElement; if (parentElement !== null && isBlockDomNode(parentElement)) { // check if node is first child, because only child dont count const firstChild = parentElement.firstChild!; if ( firstChild === node || (firstChild.nextSibling === node && isWhitespaceDomTextNode(firstChild)) ) { return false; } // check if its last child const lastChild = parentElement.lastChild!; if ( lastChild === node || (lastChild.previousSibling === node && isWhitespaceDomTextNode(lastChild)) ) { return true; } } return false; } function isWhitespaceDomTextNode(node: Node): boolean { return isDOMTextNode(node) && /^( |\t|\r?\n)+$/.test(node.textContent || ''); }