lexical
Version:
Lexical is an extensible text editor framework that provides excellent reliability, accessible and performance.
141 lines (121 loc) • 3.43 kB
text/typescript
/**
* 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,
DOMConversionOutput,
NodeKey,
SerializedLexicalNode,
} from '../LexicalNode';
import {LexicalNode} from '../LexicalNode';
import {
$applyNodeReplacement,
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 document.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;
}
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;
}
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 || '');
}