import { DeesElement, html, property, customElement, css, type TemplateResult } from '@design.estate/dees-element'; import { idpElementStyles } from './tokens.js'; import './idp-input.js'; import './idp-checkbox.js'; export type TIdpFormStatus = 'idle' | 'pending' | 'success' | 'error'; export type TIdpFormData = Record; export interface IIdpFormSubmitEventDetail { data: TIdpFormData; form: IdpForm; } type TFormControl = HTMLElement & { name?: string; key?: string; value?: string; checked?: boolean; validate?: () => boolean; }; declare global { interface HTMLElementTagNameMap { 'idp-form': IdpForm; } } @customElement('idp-form') export class IdpForm extends DeesElement { public static demo = () => html` Continue `; public static demoGroups = ['idp.global v3 primitives']; @property({ type: String, reflect: true }) public accessor status: TIdpFormStatus = 'idle'; @property({ type: String }) public accessor statusMessage = ''; @property({ type: Boolean, reflect: true }) public accessor disabled = false; public static styles = [ ...idpElementStyles, css` :host { display: block; } form { display: flex; flex-direction: column; gap: 16px; } ::slotted(idp-form-submit) { margin-top: 2px; } .status { padding: 11px 13px; border-radius: 10px; border: 1px solid var(--idp-border); background: var(--idp-muted); color: var(--idp-muted-fg); font-size: 13px; line-height: 1.45; } .status.pending { border-color: color-mix(in srgb, var(--idp-accent), transparent 60%); color: var(--idp-accent); } .status.success { border-color: color-mix(in srgb, var(--idp-ok), transparent 60%); color: var(--idp-ok); } .status.error { border-color: color-mix(in srgb, var(--idp-destructive), transparent 60%); color: var(--idp-destructive); } `, ]; public setStatus(statusArg: TIdpFormStatus, messageArg = '') { this.status = statusArg; this.statusMessage = messageArg; } public resetStatus() { this.setStatus('idle', ''); } public submit() { this.handleSubmit(); } private getControls() { return Array.from(this.querySelectorAll('idp-input, idp-checkbox')) as TFormControl[]; } private validateControls() { return this.getControls().every((controlArg) => controlArg.validate ? controlArg.validate() : true); } private getFormData(): TIdpFormData { const data: TIdpFormData = {}; for (const control of this.getControls()) { const name = control.name || control.key; if (!name) { continue; } if (typeof control.checked === 'boolean') { data[name] = control.checked; } else { data[name] = control.value || ''; } } return data; } private handleSubmitRequest(eventArg: Event) { eventArg.preventDefault(); eventArg.stopPropagation(); this.handleSubmit(); } private handleSubmit(eventArg?: Event) { eventArg?.preventDefault(); if (this.disabled || this.status === 'pending') { return; } if (!this.validateControls()) { this.setStatus('error', 'Please check the highlighted fields.'); return; } this.dispatchEvent(new CustomEvent('idp-submit', { detail: { data: this.getFormData(), form: this, }, bubbles: true, composed: true, })); } public render(): TemplateResult { return html`
${this.statusMessage ? html`
${this.statusMessage}
` : html``}
`; } }