]> BookStack Code Mirror - bookstack/blob - resources/js/wysiwyg/ui/framework/forms.ts
Lexical: Started on form UI
[bookstack] / resources / js / wysiwyg / ui / framework / forms.ts
1 import {EditorUiContext, EditorUiElement} from "./core";
2 import {EditorContainerUiElement} from "./containers";
3 import {el} from "../../helpers";
4
5 export interface EditorFormFieldDefinition {
6     label: string;
7     name: string;
8     type: 'text' | 'select';
9 }
10
11 export interface EditorSelectFormFieldDefinition extends EditorFormFieldDefinition {
12     type: 'select',
13     valuesByLabel: Record<string, string>
14 }
15
16 export interface EditorFormDefinition {
17     submitText: string;
18     cancelText: string;
19     action: (formData: FormData, context: EditorUiContext) => boolean;
20     cancel: () => void;
21     fields: EditorFormFieldDefinition[];
22 }
23
24 export class EditorFormField extends EditorUiElement {
25     protected definition: EditorFormFieldDefinition;
26
27     constructor(definition: EditorFormFieldDefinition) {
28         super();
29         this.definition = definition;
30     }
31
32     protected buildDOM(): HTMLElement {
33         const id = `editor-form-field-${this.definition.name}-${Date.now()}`;
34         let input: HTMLElement;
35
36         if (this.definition.type === 'select') {
37             const options = (this.definition as EditorSelectFormFieldDefinition).valuesByLabel
38             const labels = Object.keys(options);
39             const optionElems = labels.map(label => el('option', {value: options[label]}, [label]));
40             input = el('select', {id, name: this.definition.name, class: 'editor-form-field-input'}, optionElems);
41         } else {
42             input = el('input', {id, name: this.definition.name, class: 'editor-form-field-input'});
43         }
44
45         return el('div', {class: 'editor-form-field-wrapper'}, [
46             el('label', {class: 'editor-form-field-label', for: id}, [this.trans(this.definition.label)]),
47             input,
48         ]);
49     }
50 }
51
52 export class EditorForm extends EditorContainerUiElement {
53     protected definition: EditorFormDefinition;
54
55     constructor(definition: EditorFormDefinition) {
56         super(definition.fields.map(fieldDefinition => new EditorFormField(fieldDefinition)));
57         this.definition = definition;
58     }
59
60     protected buildDOM(): HTMLElement {
61         const cancelButton = el('button', {type: 'button', class: 'editor-form-action-secondary'}, [this.trans(this.definition.cancelText)]);
62         const form = el('form', {}, [
63             ...this.children.map(child => child.getDOMElement()),
64             el('div', {class: 'editor-form-actions'}, [
65                 cancelButton,
66                 el('button', {type: 'submit', class: 'editor-form-action-primary'}, [this.trans(this.definition.submitText)]),
67             ])
68         ]);
69
70         form.addEventListener('submit', (event) => {
71             event.preventDefault();
72             const formData = new FormData(form as HTMLFormElement);
73             this.definition.action(formData, this.getContext());
74         });
75
76         cancelButton.addEventListener('click', (event) => {
77             this.definition.cancel();
78         });
79
80         return form;
81     }
82 }