]> BookStack Code Mirror - bookstack/blob - resources/js/wysiwyg/ui/defaults/buttons/block-formats.ts
Lexical: Reorganised custom node code into lexical codebase
[bookstack] / resources / js / wysiwyg / ui / defaults / buttons / block-formats.ts
1 import {$createCalloutNode, $isCalloutNodeOfCategory, CalloutCategory} from "@lexical/rich-text/LexicalCalloutNode";
2 import {EditorButtonDefinition} from "../../framework/buttons";
3 import {EditorUiContext} from "../../framework/core";
4 import {$isParagraphNode, BaseSelection, LexicalNode} from "lexical";
5 import {$selectionContainsNodeType, $toggleSelectionBlockNodeType} from "../../../utils/selection";
6 import {
7     toggleSelectionAsBlockquote,
8     toggleSelectionAsHeading,
9     toggleSelectionAsParagraph
10 } from "../../../utils/formats";
11 import {$isHeadingNode, HeadingNode, HeadingTagType} from "@lexical/rich-text/LexicalHeadingNode";
12 import {$isQuoteNode} from "@lexical/rich-text/LexicalQuoteNode";
13
14 function buildCalloutButton(category: CalloutCategory, name: string): EditorButtonDefinition {
15     return {
16         label: name,
17         action(context: EditorUiContext) {
18             context.editor.update(() => {
19                 $toggleSelectionBlockNodeType(
20                     (node) => $isCalloutNodeOfCategory(node, category),
21                     () => $createCalloutNode(category),
22                 )
23             });
24         },
25         isActive(selection: BaseSelection|null): boolean {
26             return $selectionContainsNodeType(selection, (node) => $isCalloutNodeOfCategory(node, category));
27         }
28     };
29 }
30
31 export const infoCallout: EditorButtonDefinition = buildCalloutButton('info', 'Info');
32 export const dangerCallout: EditorButtonDefinition = buildCalloutButton('danger', 'Danger');
33 export const warningCallout: EditorButtonDefinition = buildCalloutButton('warning', 'Warning');
34 export const successCallout: EditorButtonDefinition = buildCalloutButton('success', 'Success');
35
36 const isHeaderNodeOfTag = (node: LexicalNode | null | undefined, tag: HeadingTagType) => {
37     return $isHeadingNode(node) && (node as HeadingNode).getTag() === tag;
38 };
39
40 function buildHeaderButton(tag: HeadingTagType, name: string): EditorButtonDefinition {
41     return {
42         label: name,
43         action(context: EditorUiContext) {
44             toggleSelectionAsHeading(context.editor, tag);
45         },
46         isActive(selection: BaseSelection|null): boolean {
47             return $selectionContainsNodeType(selection, (node) => isHeaderNodeOfTag(node, tag));
48         }
49     };
50 }
51
52 export const h2: EditorButtonDefinition = buildHeaderButton('h2', 'Large Header');
53 export const h3: EditorButtonDefinition = buildHeaderButton('h3', 'Medium Header');
54 export const h4: EditorButtonDefinition = buildHeaderButton('h4', 'Small Header');
55 export const h5: EditorButtonDefinition = buildHeaderButton('h5', 'Tiny Header');
56
57 export const blockquote: EditorButtonDefinition = {
58     label: 'Blockquote',
59     action(context: EditorUiContext) {
60         toggleSelectionAsBlockquote(context.editor);
61     },
62     isActive(selection: BaseSelection|null): boolean {
63         return $selectionContainsNodeType(selection, $isQuoteNode);
64     }
65 };
66
67 export const paragraph: EditorButtonDefinition = {
68     label: 'Paragraph',
69     action(context: EditorUiContext) {
70         toggleSelectionAsParagraph(context.editor);
71     },
72     isActive(selection: BaseSelection|null): boolean {
73         return $selectionContainsNodeType(selection, $isParagraphNode);
74     }
75 }