]> BookStack Code Mirror - bookstack/blob - resources/js/wysiwyg/ui/framework/containers.ts
Lexical: Added view/edit source code button/form/action
[bookstack] / resources / js / wysiwyg / ui / framework / containers.ts
1 import {EditorUiContext, EditorUiElement, EditorUiStateUpdate} from "./core";
2 import {el} from "../../helpers";
3 import {EditorButton} from "./buttons";
4
5 export class EditorContainerUiElement extends EditorUiElement {
6     protected children : EditorUiElement[];
7
8     constructor(children: EditorUiElement[]) {
9         super();
10         this.children = children;
11     }
12
13     protected buildDOM(): HTMLElement {
14         return el('div', {}, this.getChildren().map(child => child.getDOMElement()));
15     }
16
17     getChildren(): EditorUiElement[] {
18         return this.children;
19     }
20
21     updateState(state: EditorUiStateUpdate): void {
22         for (const child of this.children) {
23             child.updateState(state);
24         }
25     }
26
27     setContext(context: EditorUiContext) {
28         super.setContext(context);
29         for (const child of this.getChildren()) {
30             child.setContext(context);
31         }
32     }
33 }
34
35 export class EditorSimpleClassContainer extends EditorContainerUiElement {
36     protected className;
37
38     constructor(className: string, children: EditorUiElement[]) {
39         super(children);
40         this.className = className;
41     }
42
43     protected buildDOM(): HTMLElement {
44         return el('div', {
45             class: this.className,
46         }, this.getChildren().map(child => child.getDOMElement()));
47     }
48 }
49
50 export class EditorFormatMenu extends EditorContainerUiElement {
51     buildDOM(): HTMLElement {
52         const childElements: HTMLElement[] = this.getChildren().map(child => child.getDOMElement());
53         const menu = el('div', {
54             class: 'editor-format-menu-dropdown editor-dropdown-menu editor-menu-list',
55             hidden: 'true',
56         }, childElements);
57
58         const toggle = el('button', {
59             class: 'editor-format-menu-toggle editor-button',
60             type: 'button',
61         }, [this.trans('Formats')]);
62
63         const wrapper = el('div', {
64             class: 'editor-format-menu editor-dropdown-menu-container',
65         }, [toggle, menu]);
66
67         let clickListener: Function|null = null;
68
69         const hide = () => {
70             menu.hidden = true;
71             if (clickListener) {
72                 window.removeEventListener('click', clickListener as EventListener);
73             }
74         };
75
76         const show = () => {
77             menu.hidden = false
78             clickListener = (event: MouseEvent) => {
79                 if (!wrapper.contains(event.target as HTMLElement)) {
80                     hide();
81                 }
82             }
83             window.addEventListener('click', clickListener as EventListener);
84         };
85
86         toggle.addEventListener('click', event => {
87             menu.hasAttribute('hidden') ? show() : hide();
88         });
89         menu.addEventListener('mouseleave', hide);
90
91         return wrapper;
92     }
93
94     updateState(state: EditorUiStateUpdate) {
95         super.updateState(state);
96
97         for (const child of this.children) {
98             if (child instanceof EditorButton && child.isActive()) {
99                 this.updateToggleLabel(child.getLabel());
100                 return;
101             }
102         }
103
104         this.updateToggleLabel(this.trans('Formats'));
105     }
106
107     protected updateToggleLabel(text: string): void {
108         const button = this.getDOMElement().querySelector('button');
109         if (button) {
110             button.innerText = text;
111         }
112     }
113 }