createWidget
re-export| Package | @formscript/render-dom |
|---|---|
| Specification | 5.1, 7.12, 13, 13.4, 13.5, 13.6, 15.2, 15.3 |
| Depends on | a11y, ast, engine, render, validation |
@formscript/render-dom
The DOM binding of FormScript render intents, per §15.2 and §15.3 of the specification.
@formscript/render computes which widget a field needs; this package turns
that intent into a live HTML form. It is a host-side concern: FormScript emits
intents, never markup (§15.2), so the markup lives here.
Rendering a form
import { renderForm } from '@formscript/render-dom';
const form = renderForm({
container: document.getElementById('live')!,
document: parsed.document,
engine,
});
form.render();
form.submit();
renderForm replaces the content of the container with a <form>, wires every
control to the engine, and re-renders when the engine's state changes. The
engine keeps the values, the validity and the conditions; the renderer only
presents them.
The widgets
| Intent | Widget |
|---|---|
RenderTextInput |
<input type=text> |
RenderNumberInput |
<input type=number> |
RenderChoice |
a radio group, or a <select> above six options (§7.12) |
RenderMultiSelect |
a checkbox group, or a <select multiple> above six options |
RenderDate |
<input type=date>, or datetime-local for a datetime |
RenderToggle |
<input type=checkbox> |
RenderFile |
<input type=file> |
RenderSignature |
a <canvas>, or a text control when the host has no canvas |
RenderLocation |
a pair of number controls and a geolocation button |
RenderGroup |
<fieldset> with a <legend> (§13.6) |
RenderRepeat |
a repeated <fieldset> with add and remove buttons |
RenderProgress |
<progress> |
Every intent of §15.2 has a widget, as Spec-15.2 requires. A field whose declaration selects no intent is not dropped silently: the renderer says so in the form.
The obligations
- A labelled control for every active field (Spec-15.4). A field with no
labelshows its name. A group, a repeat and a radio group carry their label in a<legend>, so the label is never a floating text. - The field state is exposed (Spec-15.4, §13). The control carries
aria-required,aria-invalidandaria-describedby; the hint, the help and the error message are the elements it points at. - A value never loses its label (Spec-15.4). The label is outside the control, so no value is ever rendered without one.
- Focus and the caret survive a state propagation (Spec-15.5). The renderer reconciles by key: an element whose key survives is updated in place, so the person keeps typing in the same control. The caret is restored when the text changes under it.
- A failed submit renders the summary of §13.4: every failing field is listed at the top of the form, the summary takes the focus, each entry links to its field, and the same message is rendered inline at the field. A re-failing submission returns the focus to the summary (§13.5).
Judgement calls
The specification does not say how a browser renderer binds these intents to a DOM. The choices this package makes are recorded here.
- The identity of a control is its path. The
idof a control is the path the engine addresses the field by, joined with dots:household.0.income. A declaredidattribute is the field's identity in the document; the DOM needs one that is unique per repeat instance, so the path is used. - A
choiceabove six options becomes a select (§7.12), a radio group below it. The threshold is the one@formscript/a11yuses for the accessible tree, so the DOM and the accessibility tree agree. - A blank option is offered in a select, and choosing it clears the field
when the field's type is a
string. Anenumcannot hold the empty string (Spec-4.24), so choosing the blank option in anenumleaves the field as it was. - A file field holds the name of the chosen file. The specification gives a file field an unconstrained domain (§5.1) and does not define the shape of the value; the name is the only part a browser exposes without an upload.
- A signature field holds a PNG data URL, which is what a canvas produces. A host with no canvas gets a text control instead.
- A location field holds
{ latitude, longitude }, with either partnullwhen it was not given. - A
whengate removes the field from the form (Spec-8.3). The renderer reads the applicability the engine holds, so a field that comes back keeps its place in the document order.
Public API
12 exported symbols.
displayText
re-exportElementSpec
interface
A keyed description of one element the renderer owns.
FormWidget
interface
The control a field renders as.
KEY_ATTRIBUTE
const
The attribute that carries an element's reconciliation key.
reconcile
function
Reconciles the children of `parent` with `specs`. An element whose key survives is reused and updated in place, so the focus and the caret of the person typi…
RenderedForm
interfacerenderForm
function
Renders a FormScript document as a live form.
RenderFormOptions
interfaceResolvedOption
interface
An option of a choice set, with the label a person reads.
setControlValue
function
Sets a control's value, keeping the caret where it was when the text changed.
WidgetContext
interface
Everything a widget builder needs.