]> BookStack Code Mirror - bookstack/blob - resources/js/wysiwyg/actions.ts
Lexical: Added common events support
[bookstack] / resources / js / wysiwyg / actions.ts
1 import {$getRoot, $getSelection, $isTextNode, LexicalEditor, LexicalNode, RootNode} from "lexical";
2 import {$generateHtmlFromNodes, $generateNodesFromDOM} from "@lexical/html";
3 import {$createCustomParagraphNode} from "./nodes/custom-paragraph";
4
5 function htmlToDom(html: string): Document {
6     const parser = new DOMParser();
7     return parser.parseFromString(html, 'text/html');
8 }
9
10 function wrapTextNodes(nodes: LexicalNode[]): LexicalNode[] {
11     return nodes.map(node => {
12         if ($isTextNode(node)) {
13             const paragraph = $createCustomParagraphNode();
14             paragraph.append(node);
15             return paragraph;
16         }
17         return node;
18     });
19 }
20
21 function appendNodesToRoot(root: RootNode, nodes: LexicalNode[]) {
22     root.append(...wrapTextNodes(nodes));
23 }
24
25 export function setEditorContentFromHtml(editor: LexicalEditor, html: string) {
26     const dom = htmlToDom(html);
27
28     editor.update(() => {
29         // Empty existing
30         const root = $getRoot();
31         for (const child of root.getChildren()) {
32             child.remove(true);
33         }
34
35         const nodes = $generateNodesFromDOM(editor, dom);
36         root.append(...wrapTextNodes(nodes));
37     });
38 }
39
40 export function appendHtmlToEditor(editor: LexicalEditor, html: string) {
41     const dom = htmlToDom(html);
42
43     editor.update(() => {
44         const root = $getRoot();
45         const nodes = $generateNodesFromDOM(editor, dom);
46         root.append(...wrapTextNodes(nodes));
47     });
48 }
49
50 export function prependHtmlToEditor(editor: LexicalEditor, html: string) {
51     const dom = htmlToDom(html);
52
53     editor.update(() => {
54         const root = $getRoot();
55         const nodes = wrapTextNodes($generateNodesFromDOM(editor, dom));
56         let reference = root.getChildren()[0];
57         for (let i = nodes.length - 1; i >= 0; i--) {
58             if (reference) {
59                 reference.insertBefore(nodes[i]);
60             } else {
61                 root.append(nodes[i])
62             }
63             reference = nodes[i];
64         }
65     });
66 }
67
68 export function insertHtmlIntoEditor(editor: LexicalEditor, html: string) {
69     const dom = htmlToDom(html);
70     editor.update(() => {
71         const selection = $getSelection();
72         const nodes = wrapTextNodes($generateNodesFromDOM(editor, dom));
73
74         const reference = selection?.getNodes()[0];
75         const referencesParents = reference?.getParents() || [];
76         const topLevel = referencesParents[referencesParents.length - 1];
77         if (topLevel && reference) {
78             for (let i = nodes.length - 1; i >= 0; i--) {
79                 reference.insertAfter(nodes[i]);
80             }
81         }
82     });
83 }
84
85 export function getEditorContentAsHtml(editor: LexicalEditor): Promise<string> {
86     return new Promise((resolve, reject) => {
87         editor.getEditorState().read(() => {
88             const html = $generateHtmlFromNodes(editor);
89             resolve(html);
90         });
91     });
92 }
93
94 export function focusEditor(editor: LexicalEditor) {
95     editor.focus(() => {}, {defaultSelection: "rootStart"});
96 }