]> BookStack Code Mirror - bookstack/blob - resources/js/wysiwyg/helpers.ts
Lexical: Added button icon system
[bookstack] / resources / js / wysiwyg / helpers.ts
1 import {
2     $createParagraphNode,
3     $getSelection,
4     $isTextNode,
5     BaseSelection,
6     LexicalEditor, LexicalNode, TextFormatType
7 } from "lexical";
8 import {LexicalElementNodeCreator, LexicalNodeMatcher} from "./nodes";
9 import {$getNearestBlockElementAncestorOrThrow} from "@lexical/utils";
10 import {$setBlocksType} from "@lexical/selection";
11
12 export function el(tag: string, attrs: Record<string, string|null> = {}, children: (string|HTMLElement)[] = []): HTMLElement {
13     const el = document.createElement(tag);
14     const attrKeys = Object.keys(attrs);
15     for (const attr of attrKeys) {
16         if (attrs[attr] !== null) {
17             el.setAttribute(attr, attrs[attr] as string);
18         }
19     }
20
21     for (const child of children) {
22         if (typeof child === 'string') {
23             el.append(document.createTextNode(child));
24         } else {
25             el.append(child);
26         }
27     }
28
29     return el;
30 }
31
32 export function selectionContainsNodeType(selection: BaseSelection|null, matcher: LexicalNodeMatcher): boolean {
33     return getNodeFromSelection(selection, matcher) !== null;
34 }
35
36 export function getNodeFromSelection(selection: BaseSelection|null, matcher: LexicalNodeMatcher): LexicalNode|null {
37     if (!selection) {
38         return null;
39     }
40
41     for (const node of selection.getNodes()) {
42         if (matcher(node)) {
43             return node;
44         }
45
46         for (const parent of node.getParents()) {
47             if (matcher(parent)) {
48                 return parent;
49             }
50         }
51     }
52
53     return null;
54 }
55
56 export function selectionContainsTextFormat(selection: BaseSelection|null, format: TextFormatType): boolean {
57     if (!selection) {
58         return false;
59     }
60
61     for (const node of selection.getNodes()) {
62         if ($isTextNode(node) && node.hasFormat(format)) {
63             return true;
64         }
65     }
66
67     return false;
68 }
69
70 export function toggleSelectionBlockNodeType(editor: LexicalEditor, matcher: LexicalNodeMatcher, creator: LexicalElementNodeCreator) {
71     editor.update(() => {
72         const selection = $getSelection();
73         const blockElement = selection ? $getNearestBlockElementAncestorOrThrow(selection.getNodes()[0]) : null;
74         if (selection && matcher(blockElement)) {
75             $setBlocksType(selection, $createParagraphNode);
76         } else {
77             $setBlocksType(selection, creator);
78         }
79     });
80 }