/** * Field types a form can render. Anything not listed renders as a text input. * `boolean`/`checkbox` are one checkbox; `select`/`radio`/`checkbox-group`/`multiselect`/`chooser` take `options`; * `slider` is an E.rangeSlider (as is `range` with `formConfig.slider`); `dropzone` is an E.dropzone (value: File[]); * `signature` is an E.signature pad (value: base64 PNG). * @typedef {'string'|'email'|'password'|'number'|'integer'|'float'|'boolean'|'checkbox'|'date'|'datetime'|'time'|'url'|'tel'|'color'|'range'|'file'|'hidden'|'signature'|'select'|'multiselect'|'textarea'|'radio'|'checkbox-group'|'chooser'|'slider'|'dropzone'|(string & {})} FormFieldType */ /** * A choice for select / radio / checkbox-group / multiselect / chooser fields. A plain string is both the * value and (capitalised) the label. Chooser options also read icon, description, badge, tooltip, etc. * @typedef {string|{value: any, label: string, [key: string]: any}} FormOption */ /** * An input-group addon for `formConfig.prefix` / `formConfig.suffix`: `{icon}` draws a Domma icon, * `{text}` escaped text, `{html}` raw HTML (the caller's responsibility); a string is shorthand for `{text}`. * @typedef {string|{icon: string}|{text: string|number}|{html: string}} FormAddon */ /** * How a field is presented (`fieldDef.formConfig`). Slider fields pass range/ticks/tickLabels/labels/format/ * tooltip/marks/minDistance/orientation/rtl/names/ariaLabels/className to E.rangeSlider; dropzone fields pass * accept/multiple/maxFiles/maxSize/minSize/previews/upload/autoUpload/retry/labels/list/className to E.dropzone. * @typedef {object} FormFieldConfig * @property {string} [placeholder] * @property {string} [hint] - Small text beside the label; also the message when `pattern` fails * @property {string} [helperText] - Text under the control (HTML) * @property {string} [tooltip] - Tooltip on the label (a slider field also passes it to E.rangeSlider) * @property {number} [span] - Columns the field spans in a grid layout * @property {boolean} [disabled] * @property {number} [rows] - textarea rows (default 3) * @property {number|'any'} [step] - Used when the field definition has no `step` * @property {string} [autocomplete] * @property {FormAddon} [prefix] * @property {FormAddon} [suffix] * @property {boolean} [reveal] - password: show / hide toggle * @property {boolean} [clear] - text-like inputs and textareas: a clear button * @property {boolean} [stepper] - number / integer / float: - and + buttons * @property {boolean|number} [counter] - text-like inputs and textareas: a character counter; true reads `maxLength` * @property {Record|((value: number, where: 'tick'|'mark'|'tooltip'|'aria') => string)} [labels] - Text for the input extras or the dropzone; a slider's value formatter * @property {string|string[]} [accept] - file / dropzone * @property {boolean} [multiple] - file / dropzone (a dropzone allows several unless false) * @property {boolean} [slider] - Render a `range` field as E.rangeSlider * @property {boolean} [range] - slider: two thumbs, value [low, high] * @property {import('./components/range-slider.js').RangeSliderOptions['ticks']} [ticks] * @property {boolean} [tickLabels] * @property {import('./components/range-slider.js').RangeSliderOptions['format']} [format] * @property {import('./components/range-slider.js').RangeSliderOptions['marks']} [marks] * @property {number} [minDistance] * @property {'horizontal'|'vertical'} [orientation] * @property {boolean|null} [rtl] * @property {[string, string]|null} [names] * @property {[string, string]|null} [ariaLabels] * @property {string} [className] * @property {number|null} [maxFiles] * @property {number|null} [maxSize] - Bytes * @property {number|null} [minSize] - Bytes * @property {boolean} [previews] * @property {import('./components/dropzone.js').DropzoneOptions['upload']} [upload] * @property {boolean} [autoUpload] * @property {number} [retry] * @property {boolean|string|HTMLElement} [list] */ /** * One field of a form blueprint. The same object also feeds `M.create` (the form builds its model from the * blueprint), so model keys such as `default`, `required` and `validate` apply to both. * @typedef {object} FormFieldDefinition * @property {FormFieldType} [type] - default 'string' * @property {string} [label] - default: the capitalised field name * @property {any} [default] * @property {boolean} [required] * @property {boolean} [readonly] - Renders the control disabled * @property {number} [min] * @property {number} [max] * @property {number|'any'} [step] * @property {number} [minLength] * @property {number} [maxLength] * @property {RegExp} [pattern] * @property {(value: any, data: Record) => boolean|string} [validate] - true passes; a string (or anything else) fails with that message * @property {FormOption[]} [options] - Choices for select / radio / checkbox-group / multiselect / chooser * @property {FormFieldConfig} [formConfig] * @property {(value: any, row: Record, index: number) => string} [tableRender] - `F.crud` table cell renderer * @property {'card'|'chip'} [variant] - chooser (default 'card') * @property {boolean} [multiple] - chooser: several choices (value is an array) * @property {'comfortable'|'compact'} [density] - chooser * @property {number} [columns] - chooser (default 3) * @property {string} [accent] - chooser: semantic name or CSS colour (default 'primary') * @property {'border'|'solid'|'glow'|'overlay'|'underline'} [accentStyle] - chooser * @property {boolean} [glow] - chooser * @property {string|null} [glowColour] - chooser * @property {string} [shadow] - chooser (default 'none') * @property {string|null} [shadowColour] - chooser */ /** * A form blueprint: field name -> definition. Fields render in key order unless `options.sections` says otherwise. * @typedef {{[field: string]: FormFieldDefinition}} FormBlueprint */ /** * A group of fields (`options.sections`); with a title it is a fieldset with a legend. * @typedef {object} FormSection * @property {string} [title] * @property {string[]} [fields] - Blueprint keys, in order * @property {'grid'|'stacked'} [layout] * @property {number} [columns] - grid columns (default 2) */ /** * @typedef {object} FormOptions * @property {'stacked'|'grid'|'inline'} [layout] - default 'stacked' * @property {2|3|number} [columns] - grid layout columns (2 or 3, default 2) * @property {'top'|'left'|'floating'} [labelPosition] - default 'top' * @property {boolean} [showLabels] - default true * @property {boolean} [showHelperText] - default true * @property {boolean} [showHints] - default true * @property {'blur'|'change'|'submit'} [validationMode] - default 'blur' * @property {'domma'|'bootstrap'|'tailwind'} [cssFramework] - default 'domma' * @property {string} [className] - default 'domma-form' * @property {string} [fieldClassName] - default 'domma-form-field' * @property {string} [labelClassName] - default 'form-label' * @property {string} [inputClassName] - default 'form-input' * @property {string} [errorClassName] - default 'form-error' * @property {string} [helperClassName] - default 'form-helper-text' * @property {string} [hintClassName] - default 'form-hint' * @property {boolean} [cardWrapper] - Wrap the form in a card (default false) * @property {FormSection[]} [sections] * @property {boolean} [showSubmitButton] - default true * @property {string} [submitText] - default 'Submit' * @property {string} [resetText] - Adds a reset button with this text * @property {'left'|'center'|'right'} [buttonAlignment] - default 'right' * @property {(data: Record, form: FormInstance) => void} [onSubmit] - A valid submit * @property {(errors: Record, form: FormInstance) => void} [onValidationError] - An invalid submit * @property {string} [persist] - Passed to the model: a storage key to keep the values under * @property {boolean} [autoSave] - Passed to the model */ /** * What `handleSubmit()` returns. * @typedef {object} FormSubmitResult * @property {boolean} isValid * @property {Record} data * @property {Record} errors - field name -> message */ /** * One step of `F.wizard`. * @typedef {object} FormWizardStep * @property {string} [title] - default 'Step ' * @property {string} [description] * @property {FormBlueprint} fields - The step's blueprint * @property {'stacked'|'grid'|'inline'} [layout] */ /** * `F.wizard` options; the rest are FormOptions applied to every step. * @typedef {FormOptions & { * schema: {steps: FormWizardStep[]}, * data?: Record, * prevText?: string, * nextText?: string, * finishText?: string, * onStepChange?: (stepIndex: number, data: Record) => void, * onValidation?: (isValid: boolean, errors: Record, stepIndex: number) => void, * onSubmit?: (data: Record, form: FormInstance) => void|Promise, * onError?: (error: any, data: Record, form: FormInstance) => void * }} FormWizardOptions */ /** * The control object `F.wizard` returns. * @typedef {object} FormWizard * @property {() => void} nextStep * @property {() => void} prevStep * @property {(stepIndex: number) => void} goToStep * @property {() => number} getCurrentStep - 0-based * @property {() => Record} getStepData - The values gathered so far (a copy) * @property {(data: Record) => void} setStepData - Merge values in and re-render the step * @property {() => boolean} validate - Validate the current step * @property {() => HTMLElement|null} getElement * @property {() => void} destroy - Tear down the current step's controls and remove the wizard */ /** * `F.modal` options; the rest are FormOptions (`onSubmit` and `showSubmitButton` are set by the modal). * @typedef {FormOptions & { * title?: string, * saveText?: string, * closeOnSave?: boolean, * size?: string, * onSave?: (data: Record, form: FormInstance) => void|Promise, * onError?: (error: any, data: Record, form: FormInstance) => void * }} FormModalOptions */ /** * The control object `F.modal` returns. `close()` removes the modal from the page. * @typedef {object} FormModal * @property {() => void} open * @property {() => void} close * @property {() => boolean} isOpen */ /** * `F.crud` configuration. * @typedef {object} FormCrudConfig * @property {FormBlueprint} schema * @property {string} endpoint - REST endpoint (GET list, POST create, PUT/DELETE `/`); responses are `{success, data, message}` * @property {string|HTMLElement} [tableSelector] - Where the table goes * @property {string} [title] - default 'Manage Items' * @property {string} [primaryKey] - default 'id' * @property {string} [displayField] - default 'name' * @property {Record} [headers] * @property {(error: any) => void} [onError] * @property {(message: string) => void} [onSuccess] */ /** * The object `F.crud` returns. Call `init()` to load the data and build the table. * @typedef {object} FormCrud * @property {() => Promise} init * @property {() => Promise} loadData * @property {() => void} createTable * @property {() => void} initializeTableIcons * @property {() => void} bindEvents * @property {() => Promise} create - Open the create modal * @property {(id: string|number) => Promise} edit - Open the edit modal for an item * @property {(id: string|number) => Promise} delete - Confirm, then delete an item * @property {() => Promise} refresh * @property {() => Record[]} getData * @property {() => import('./tables.js').TableInstance|null} getTable * @property {(item: Record) => void} addItem * @property {(id: string|number, updates: Record) => void} updateItem * @property {(id: string|number) => void} removeItem */ /** @typedef {Forma} FormInstance */ /** * Forma Class - Generates forms from Model schemas */ export class Forma { static defaults: { layout: string; columns: number; labelPosition: string; showLabels: boolean; showHelperText: boolean; showHints: boolean; validationMode: string; cssFramework: string; className: string; fieldClassName: string; labelClassName: string; inputClassName: string; errorClassName: string; helperClassName: string; hintClassName: string; cardWrapper: boolean; }; static inputTypes: { string: string; email: string; password: string; number: string; integer: string; float: string; boolean: string; date: string; datetime: string; time: string; url: string; tel: string; color: string; range: string; file: string; hidden: string; signature: string; }; /** * @param {FormBlueprint} schema * @param {Record} [data] - Initial values * @param {FormOptions} [options] */ constructor(schema: FormBlueprint, data?: Record, options?: FormOptions); /** @type {FormBlueprint} */ schema: FormBlueprint; /** @type {Record} */ data: Record; /** @private */ private _startData; /** @type {FormOptions} */ options: FormOptions; /** @type {import('./models.js').Model|null} The model built from the blueprint */ model: import("./models.js").Model | null; /** @type {Record} field name -> error message */ errors: Record; /** @type {Set} Fields that have lost focus at least once */ touched: Set; /** @type {typeof utils} */ utils: typeof utils; /** @type {typeof models} */ models: typeof models; /** * Generate form HTML * @returns {string} */ render(): string; /** * Render form into DOM element * @param {string|HTMLElement} selector - Container (its content is replaced) * @returns {HTMLElement} The container */ renderTo(selector: string | HTMLElement): HTMLElement; /** @type {HTMLFormElement|null} The rendered
*/ formElement: HTMLFormElement | null | undefined; /** * Build the complete form structure * @private */ private _buildForm; /** * Build a form section * @private */ private _buildSection; /** * Build individual field * @private */ private _buildField; /** * Builds an HTML input element based on the provided field definition and options. * * @param {string} fieldName - The name of the field, used as the 'name' attribute for the input. * @param {Object} fieldDef - The field definition containing configuration details such as min, max, pattern, etc. * @param {Object} inputOptions - Options to customize the input element including type, value, required, disabled, placeholder, etc. * * @return {string} A string representing the HTML input element with appropriate attributes and content based on the specified type. * @private */ private _buildInput; /** * The interactive extras a field asks for through formConfig, reduced to * those its type can take, or null: * reveal: true password - show / hide toggle * clear: true single-line text-like inputs and textareas * stepper: true number / integer / float - the - and + buttons * counter: true | n text-like inputs and textareas; true reads the * field's maxLength * * @param {string} type * @param {Object} fieldDef * @return {Object|null} * @private */ private _inputExtras; /** * Joins formConfig.prefix / formConfig.suffix addons to a control's markup. * Used for single-line inputs, selects and textareas; a control with neither * is returned untouched. * * @param {string} controlHtml * @param {Object} formConfig * @return {string} * @private */ private _wrapAddons; /** * Builds an input-group addon (prefix or suffix) for decoration inside a form input. * * Accepts a slot descriptor object from formConfig.prefix / formConfig.suffix: * { icon: 'search' } → renders a Domma icon * { text: '$' } → renders escaped plain text (currency, units, etc.) * { html: 'K' } → renders raw HTML (caller takes responsibility) * '£' → shorthand for { text: '£' } * * @param {Object|undefined} slot The prefix/suffix descriptor, or falsy to skip. * @param {'left'|'right'} side Which side the addon renders on. * @return {string} The addon HTML, or '' if no addon should be rendered. * @private */ private _buildAddon; /** * Constructs the HTML select element based on field definition and current value. * * @param fieldName * @param {Object} fieldDef - The field definition containing options, required status, and other attributes. * @param attrs * @param currentValue * @return {string} A string representing the generated HTML `