]> BookStack Code Mirror - bookstack/blob - resources/js/wysiwyg/ui/framework/forms.ts
Lexical: Added basic list button/support
[bookstack] / resources / js / wysiwyg / ui / framework / forms.ts
1 import {EditorUiContext, EditorUiElement, EditorContainerUiElement} from "./core";
2 import {el} from "../../helpers";
3
4 export interface EditorFormFieldDefinition {
5     label: string;
6     name: string;
7     type: 'text' | 'select' | 'textarea';
8 }
9
10 export interface EditorSelectFormFieldDefinition extends EditorFormFieldDefinition {
11     type: 'select',
12     valuesByLabel: Record<string, string>
13 }
14
15 export interface EditorFormDefinition {
16     submitText: string;
17     action: (formData: FormData, context: EditorUiContext) => boolean;
18     fields: EditorFormFieldDefinition[];
19 }
20
21 export class EditorFormField extends EditorUiElement {
22     protected definition: EditorFormFieldDefinition;
23
24     constructor(definition: EditorFormFieldDefinition) {
25         super();
26         this.definition = definition;
27     }
28
29     setValue(value: string) {
30         const input = this.getDOMElement().querySelector('input,select,textarea') as HTMLInputElement;
31         input.value = value;
32     }
33
34     getName(): string {
35         return this.definition.name;
36     }
37
38     protected buildDOM(): HTMLElement {
39         const id = `editor-form-field-${this.definition.name}-${Date.now()}`;
40         let input: HTMLElement;
41
42         if (this.definition.type === 'select') {
43             const options = (this.definition as EditorSelectFormFieldDefinition).valuesByLabel
44             const labels = Object.keys(options);
45             const optionElems = labels.map(label => el('option', {value: options[label]}, [label]));
46             input = el('select', {id, name: this.definition.name, class: 'editor-form-field-input'}, optionElems);
47         } else if (this.definition.type === 'textarea') {
48             input = el('textarea', {id, name: this.definition.name, class: 'editor-form-field-input'});
49         } else {
50             input = el('input', {id, name: this.definition.name, class: 'editor-form-field-input'});
51         }
52
53         return el('div', {class: 'editor-form-field-wrapper'}, [
54             el('label', {class: 'editor-form-field-label', for: id}, [this.trans(this.definition.label)]),
55             input,
56         ]);
57     }
58 }
59
60 export class EditorForm extends EditorContainerUiElement {
61     protected definition: EditorFormDefinition;
62     protected onCancel: null|(() => void) = null;
63
64     constructor(definition: EditorFormDefinition) {
65         super(definition.fields.map(fieldDefinition => new EditorFormField(fieldDefinition)));
66         this.definition = definition;
67     }
68
69     setValues(values: Record<string, string>) {
70         for (const name of Object.keys(values)) {
71             const field = this.getFieldByName(name);
72             if (field) {
73                 field.setValue(values[name]);
74             }
75         }
76     }
77
78     setOnCancel(callback: () => void) {
79         this.onCancel = callback;
80     }
81
82     protected getFieldByName(name: string): EditorFormField|null {
83         for (const child of this.children as EditorFormField[]) {
84             if (child.getName() === name) {
85                 return child;
86             }
87         }
88
89         return null;
90     }
91
92     protected buildDOM(): HTMLElement {
93         const cancelButton = el('button', {type: 'button', class: 'editor-form-action-secondary'}, [this.trans('Cancel')]);
94         const form = el('form', {}, [
95             ...this.children.map(child => child.getDOMElement()),
96             el('div', {class: 'editor-form-actions'}, [
97                 cancelButton,
98                 el('button', {type: 'submit', class: 'editor-form-action-primary'}, [this.trans(this.definition.submitText)]),
99             ])
100         ]);
101
102         form.addEventListener('submit', (event) => {
103             event.preventDefault();
104             const formData = new FormData(form as HTMLFormElement);
105             this.definition.action(formData, this.getContext());
106         });
107
108         cancelButton.addEventListener('click', (event) => {
109             if (this.onCancel) {
110                 this.onCancel();
111             }
112         });
113
114         return form;
115     }
116 }