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