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