import { customElement, html, type TemplateResult, DeesElement, type CSSResult, } from '@design.estate/dees-element'; import * as domtools from '@design.estate/dees-domtools'; import { DeesInputCheckbox } from './dees-input-checkbox.js'; import { DeesInputText } from './dees-input-text.js'; import { DeesInputQuantitySelector } from './dees-input-quantityselector.js'; import { DeesInputRadio } from './dees-input-radio.js'; import { DeesFormSubmit } from './dees-form-submit.js'; // Unified set for form input types const FORM_INPUT_TYPES = [ DeesInputCheckbox, DeesInputText, DeesInputQuantitySelector, DeesInputRadio ]; export type TFormInputElement = DeesInputCheckbox | DeesInputText | DeesInputQuantitySelector | DeesInputRadio; declare global { interface HTMLElementTagNameMap { 'dees-form': DeesForm; } } @customElement('dees-form') export class DeesForm extends DeesElement { public static demo = () => html` { const form: DeesForm = eventArg.currentTarget; form.setStatus('pending', 'authenticating...'); await domtools.plugins.smartdelay.delayFor(1000); form.setStatus('success', 'authenticated!'); }} > Submit `; public name: string = 'myform'; public changeSubject = new domtools.rxjs.Subject(); public readyDeferred = domtools.plugins.smartpromise.defer(); public render(): TemplateResult { return html` `; } public async firstUpdated() { const formChildren = this.getFormElements(); this.updateRequiredStatus(); for (const child of formChildren) { child.changeSubject.subscribe(async () => { const valueObject = await this.collectFormData(); this.changeSubject.next(valueObject); console.log(valueObject); this.updateRequiredStatus(); }); } await this.addBehaviours(); this.readyDeferred.resolve(); } public getFormElements(): Array { return (Array.from(this.children)).filter(child => FORM_INPUT_TYPES.includes(child.constructor as any) ) as unknown as TFormInputElement[]; } public getSubmitButton(): DeesFormSubmit | undefined { return Array.from(this.children).find(child => child instanceof DeesFormSubmit ) as DeesFormSubmit; } public async updateRequiredStatus() { console.log('checking the required status.'); let requiredOK = true; for (const childArg of this.getFormElements()) { if (childArg.required && !childArg.value) { requiredOK = false; } } if (this.getSubmitButton()) { this.getSubmitButton().disabled = !requiredOK; } } public async collectFormData() { const children = this.getFormElements(); const valueObject: { [key: string]: string | number | boolean } = {}; for (const child of children) { if (!child.key) { console.log(`form element with label "${child.label}" has no key. skipping.`); } valueObject[child.key] = child.value; } return valueObject; } public async gatherAndDispatch() { const valueObject = await this.collectFormData(); const formDataEvent = new CustomEvent('formData', { detail: { data: valueObject, }, bubbles: true, }); this.dispatchEvent(formDataEvent); console.log('dispatched data:'); console.log(valueObject); } public setStatus( visualStateArg: 'normal' | 'pending' | 'error' | 'success', textStateArg: string ) { const inputChildren = this.getFormElements(); const submitButton = this.getSubmitButton(); switch (visualStateArg) { case 'normal': submitButton.disabled = false; submitButton.status = 'normal'; for (const inputChild of inputChildren) { inputChild.disabled = false; } break; case 'pending': submitButton.disabled = true; submitButton.status = 'pending'; for (const inputChild of inputChildren) { inputChild.disabled = true; } break; case 'success': submitButton.disabled = true; submitButton.status = 'success'; for (const inputChild of inputChildren) { inputChild.disabled = true; } break; case 'error': submitButton.disabled = true; submitButton.status = 'error'; for (const inputChild of inputChildren) { inputChild.disabled = true; } break; } submitButton.text = textStateArg; } /** * resets the form */ reset() { const inputChildren = this.getFormElements(); const submitButton = this.getSubmitButton(); for (const inputChild of inputChildren) { inputChild.value = null; } this.setStatus('normal', 'Submit'); } public async addBehaviours() { // Use event delegation this.addEventListener('keydown', (event: KeyboardEvent) => { const target = event.target as DeesElement; if (!FORM_INPUT_TYPES.includes(target.constructor as any)) return; if (event.key === 'Enter') { const children = this.getFormElements(); const currentIndex = children.indexOf(target as any); if (currentIndex < children.length - 1) { children[currentIndex + 1].focus(); } else { target.blur(); this.getSubmitButton()?.focus(); } } }); } }