]> BookStack Code Mirror - bookstack/blob - resources/js/wysiwyg/ui/framework/buttons.ts
Lexical: Reorganised some logic into manager
[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) => void;
12     isActive: (selection: BaseSelection|null) => 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());
72     }
73
74     updateActiveState(selection: BaseSelection|null) {
75         this.active = this.definition.isActive(selection);
76         this.dom?.classList.toggle('editor-button-active', this.active);
77     }
78
79     updateState(state: EditorUiStateUpdate): void {
80         this.updateActiveState(state.selection);
81     }
82
83     isActive(): boolean {
84         return this.active;
85     }
86
87     getLabel(): string {
88         return this.trans(this.definition.label);
89     }
90
91     toggleDisabled(disabled: boolean) {
92         this.disabled = disabled;
93         if (disabled) {
94             this.dom?.setAttribute('disabled', 'true');
95         } else {
96             this.dom?.removeAttribute('disabled');
97         }
98     }
99 }