]> BookStack Code Mirror - bookstack/blob - resources/js/wysiwyg/utils/nodes.ts
Lexical: Added media resize support via drag handles
[bookstack] / resources / js / wysiwyg / utils / nodes.ts
1 import {$getRoot, $isElementNode, $isTextNode, ElementNode, LexicalEditor, LexicalNode} from "lexical";
2 import {LexicalNodeMatcher} from "../nodes";
3 import {$createCustomParagraphNode} from "../nodes/custom-paragraph";
4 import {$generateNodesFromDOM} from "@lexical/html";
5 import {htmlToDom} from "./dom";
6 import {NodeHasAlignment} from "../nodes/_common";
7
8 function wrapTextNodes(nodes: LexicalNode[]): LexicalNode[] {
9     return nodes.map(node => {
10         if ($isTextNode(node)) {
11             const paragraph = $createCustomParagraphNode();
12             paragraph.append(node);
13             return paragraph;
14         }
15         return node;
16     });
17 }
18
19 export function $htmlToBlockNodes(editor: LexicalEditor, html: string): LexicalNode[] {
20     const dom = htmlToDom(html);
21     const nodes = $generateNodesFromDOM(editor, dom);
22     return wrapTextNodes(nodes);
23 }
24
25 export function $getParentOfType(node: LexicalNode, matcher: LexicalNodeMatcher): LexicalNode | null {
26     for (const parent of node.getParents()) {
27         if (matcher(parent)) {
28             return parent;
29         }
30     }
31
32     return null;
33 }
34
35 export function $getAllNodesOfType(matcher: LexicalNodeMatcher, root?: ElementNode): LexicalNode[] {
36     if (!root) {
37         root = $getRoot();
38     }
39
40     const matches = [];
41
42     for (const child of root.getChildren()) {
43         if (matcher(child)) {
44             matches.push(child);
45         }
46
47         if ($isElementNode(child)) {
48             matches.push(...$getAllNodesOfType(matcher, child));
49         }
50     }
51
52     return matches;
53 }
54
55 /**
56  * Get the nearest root/block level node for the given position.
57  */
58 export function $getNearestBlockNodeForCoords(editor: LexicalEditor, x: number, y: number): LexicalNode | null {
59     // TODO - Take into account x for floated blocks?
60     const rootNodes = $getRoot().getChildren();
61     for (const node of rootNodes) {
62         const nodeDom = editor.getElementByKey(node.__key);
63         if (!nodeDom) {
64             continue;
65         }
66
67         const bounds = nodeDom.getBoundingClientRect();
68         if (y <= bounds.bottom) {
69             return node;
70         }
71     }
72
73     return null;
74 }
75
76 export function nodeHasAlignment(node: object): node is NodeHasAlignment {
77     return '__alignment' in node;
78 }