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