5 $isElementNode, $isRootNode,
9 LexicalNode, RangeSelection
11 import {LexicalNodeMatcher} from "../nodes";
12 import {$generateNodesFromDOM} from "@lexical/html";
13 import {htmlToDom} from "./dom";
14 import {NodeHasAlignment, NodeHasInset} from "lexical/nodes/common";
15 import {$findMatchingParent} from "@lexical/utils";
17 function wrapTextNodes(nodes: LexicalNode[]): LexicalNode[] {
18 return nodes.map(node => {
19 if ($isTextNode(node)) {
20 const paragraph = $createParagraphNode();
21 paragraph.append(node);
28 export function $htmlToBlockNodes(editor: LexicalEditor, html: string): LexicalNode[] {
29 const dom = htmlToDom(html);
30 const nodes = $generateNodesFromDOM(editor, dom);
31 return wrapTextNodes(nodes);
34 export function $getParentOfType(node: LexicalNode, matcher: LexicalNodeMatcher): LexicalNode | null {
35 for (const parent of node.getParents()) {
36 if (matcher(parent)) {
44 export function $getAllNodesOfType(matcher: LexicalNodeMatcher, root?: ElementNode): LexicalNode[] {
51 for (const child of root.getChildren()) {
56 if ($isElementNode(child)) {
57 matches.push(...$getAllNodesOfType(matcher, child));
65 * Get the nearest root/block level node for the given position.
67 export function $getNearestBlockNodeForCoords(editor: LexicalEditor, x: number, y: number): LexicalNode | null {
68 // TODO - Take into account x for floated blocks?
69 const rootNodes = $getRoot().getChildren();
70 for (const node of rootNodes) {
71 const nodeDom = editor.getElementByKey(node.__key);
76 const bounds = nodeDom.getBoundingClientRect();
77 if (y <= bounds.bottom) {
85 export function $getNearestNodeBlockParent(node: LexicalNode): LexicalNode|null {
86 const isBlockNode = (node: LexicalNode): boolean => {
87 return ($isElementNode(node) || $isDecoratorNode(node)) && !node.isInline() && !$isRootNode(node);
90 if (isBlockNode(node)) {
94 return $findMatchingParent(node, isBlockNode);
97 export function $sortNodes(nodes: LexicalNode[]): LexicalNode[] {
98 const idChain: string[] = [];
99 const addIds = (n: ElementNode) => {
100 for (const child of n.getChildren()) {
101 idChain.push(child.getKey())
102 if ($isElementNode(child)) {
108 const root = $getRoot();
111 const sorted = Array.from(nodes);
112 sorted.sort((a, b) => {
113 const aIndex = idChain.indexOf(a.getKey());
114 const bIndex = idChain.indexOf(b.getKey());
115 return aIndex - bIndex;
121 export function $selectOrCreateAdjacent(node: LexicalNode, after: boolean): RangeSelection {
122 const nearestBlock = $getNearestNodeBlockParent(node) || node;
123 let target = after ? nearestBlock.getNextSibling() : nearestBlock.getPreviousSibling()
126 target = $createParagraphNode();
128 node.insertAfter(target)
130 node.insertBefore(target);
134 return after ? target.selectStart() : target.selectEnd();
137 export function nodeHasAlignment(node: object): node is NodeHasAlignment {
138 return '__alignment' in node;
141 export function nodeHasInset(node: object): node is NodeHasInset {
142 return '__inset' in node;