]> BookStack Code Mirror - bookstack/blob - resources/js/wysiwyg/ui/framework/core.ts
b6fe52dcdff8a9109cd9cdb7a23449405cc58354
[bookstack] / resources / js / wysiwyg / ui / framework / core.ts
1 import {BaseSelection, LexicalEditor} from "lexical";
2 import {EditorUIManager} from "./manager";
3
4 import {el} from "../../utils/dom";
5
6 export type EditorUiStateUpdate = {
7     editor: LexicalEditor;
8     selection: BaseSelection|null;
9 };
10
11 export type EditorUiContext = {
12     editor: LexicalEditor; // Lexical editor instance
13     editorDOM: HTMLElement; // DOM element the editor is bound to
14     containerDOM: HTMLElement; // DOM element which contains all editor elements
15     scrollDOM: HTMLElement; // DOM element which is the main content scroll container
16     translate: (text: string) => string; // Translate function
17     manager: EditorUIManager; // UI Manager instance for this editor
18     options: Record<string, any>; // General user options which may be used by sub elements
19 };
20
21 export interface EditorUiBuilderDefinition {
22     build: () => EditorUiElement;
23 }
24
25 export function isUiBuilderDefinition(object: any): object is EditorUiBuilderDefinition {
26     return 'build' in object;
27 }
28
29 export abstract class EditorUiElement {
30     protected dom: HTMLElement|null = null;
31     private context: EditorUiContext|null = null;
32
33     protected abstract buildDOM(): HTMLElement;
34
35     setContext(context: EditorUiContext): void {
36         this.context = context;
37     }
38
39     getContext(): EditorUiContext {
40         if (this.context === null) {
41             throw new Error('Attempted to use EditorUIContext before it has been set');
42         }
43
44         return this.context;
45     }
46
47     getDOMElement(): HTMLElement {
48         if (!this.dom) {
49             this.dom = this.buildDOM();
50         }
51
52         return this.dom;
53     }
54
55     trans(text: string) {
56         return this.getContext().translate(text);
57     }
58
59     updateState(state: EditorUiStateUpdate): void {
60         return;
61     }
62 }
63
64 export class EditorContainerUiElement extends EditorUiElement {
65     protected children : EditorUiElement[] = [];
66
67     constructor(children: EditorUiElement[]) {
68         super();
69         this.children.push(...children);
70     }
71
72     protected buildDOM(): HTMLElement {
73         return el('div', {}, this.getChildren().map(child => child.getDOMElement()));
74     }
75
76     getChildren(): EditorUiElement[] {
77         return this.children;
78     }
79
80     protected addChildren(...children: EditorUiElement[]): void {
81         this.children.push(...children);
82     }
83
84     protected removeChildren(...children: EditorUiElement[]): void {
85         for (const child of children) {
86             this.removeChild(child);
87         }
88     }
89
90     protected removeChild(child: EditorUiElement) {
91         const index = this.children.indexOf(child);
92         if (index !== -1) {
93             this.children.splice(index, 1);
94         }
95     }
96
97     updateState(state: EditorUiStateUpdate): void {
98         for (const child of this.children) {
99             child.updateState(state);
100         }
101     }
102
103     setContext(context: EditorUiContext) {
104         super.setContext(context);
105         for (const child of this.getChildren()) {
106             child.setContext(context);
107         }
108     }
109 }
110
111 export class EditorSimpleClassContainer extends EditorContainerUiElement {
112     protected className;
113
114     constructor(className: string, children: EditorUiElement[]) {
115         super(children);
116         this.className = className;
117     }
118
119     protected buildDOM(): HTMLElement {
120         return el('div', {
121             class: this.className,
122         }, this.getChildren().map(child => child.getDOMElement()));
123     }
124 }
125