]> BookStack Code Mirror - bookstack/blob - resources/js/wysiwyg/ui/framework/buttons.ts
Lexical: Added tracked container, added fullscreen action
[bookstack] / resources / js / wysiwyg / ui / framework / buttons.ts
1 import {BaseSelection} from "lexical";
2 import {EditorUiContext, EditorUiElement, EditorUiStateUpdate} from "./core";
3 import {el} from "../../helpers";
4
5 export interface EditorBasicButtonDefinition {
6     label: string;
7     icon?: string|undefined;
8 }
9
10 export interface EditorButtonDefinition extends EditorBasicButtonDefinition {
11     action: (context: EditorUiContext, button: EditorButton) => void;
12     isActive: (selection: BaseSelection|null, context: EditorUiContext) => boolean;
13     setup?: (context: EditorUiContext, button: EditorButton) => void;
14 }
15
16 export class EditorButton extends EditorUiElement {
17     protected definition: EditorButtonDefinition;
18     protected active: boolean = false;
19     protected completedSetup: boolean = false;
20     protected disabled: boolean = false;
21
22     constructor(definition: EditorButtonDefinition|EditorBasicButtonDefinition) {
23         super();
24
25         if ((definition as EditorButtonDefinition).action !== undefined) {
26             this.definition = definition as EditorButtonDefinition;
27         } else {
28             this.definition = {
29                 ...definition,
30                 action() {
31                     return false;
32                 },
33                 isActive: () => {
34                     return false;
35                 }
36             };
37         }
38     }
39
40     setContext(context: EditorUiContext) {
41         super.setContext(context);
42
43         if (this.definition.setup && !this.completedSetup) {
44             this.definition.setup(context, this);
45             this.completedSetup = true;
46         }
47     }
48
49     protected buildDOM(): HTMLButtonElement {
50
51         const label = this.getLabel();
52         let child: string|HTMLElement = label;
53         if (this.definition.icon) {
54             child = el('span', {class: 'editor-button-icon'});
55             child.innerHTML = this.definition.icon;
56         }
57
58         const button = el('button', {
59             type: 'button',
60             class: 'editor-button',
61             title: this.definition.icon ? label : null,
62             disabled: this.disabled ? 'true' : null,
63         }, [child]) as HTMLButtonElement;
64
65         button.addEventListener('click', this.onClick.bind(this));
66
67         return button;
68     }
69
70     protected onClick() {
71         this.definition.action(this.getContext(), this);
72     }
73
74     updateActiveState(selection: BaseSelection|null) {
75         const isActive = this.definition.isActive(selection, this.getContext());
76         this.setActiveState(isActive);
77     }
78
79     setActiveState(active: boolean) {
80         this.active = active;
81         this.dom?.classList.toggle('editor-button-active', this.active);
82     }
83
84     updateState(state: EditorUiStateUpdate): void {
85         this.updateActiveState(state.selection);
86     }
87
88     isActive(): boolean {
89         return this.active;
90     }
91
92     getLabel(): string {
93         return this.trans(this.definition.label);
94     }
95
96     toggleDisabled(disabled: boolean) {
97         this.disabled = disabled;
98         if (disabled) {
99             this.dom?.setAttribute('disabled', 'true');
100         } else {
101             this.dom?.removeAttribute('disabled');
102         }
103     }
104 }