|
|
4 months ago | |
|---|---|---|
| .. | ||
| README.md | 5 months ago | |
| form-auto-fields.svelte | 5 months ago | |
| form-error-summary.svelte | 5 months ago | |
| form-reset.svelte | 5 months ago | |
| form-submit.svelte | 5 months ago | |
| form.css | 5 months ago | |
| form.svelte | 4 months ago | |
| index.ts | 5 months ago | |
| types.ts | 5 months ago | |
README.md
Form
Eidos Form wraps Soma Form.Provider and its action parts. Soma owns the form
runtime, SIUM/Standard Schema validation, field registration, dirty/touched
state, error aggregation, reset and first-error focus. Eidos owns only visual
attrs: data-size, data-layout, data-variant on the root and
data-size/data-variant/data-color on actions.
Baseline
- air: no había
Formreflexivo. Air tenía field individuales + un contenedor<form>HTML plano sin auto-rendering ni error aggregation. - soma actual: posee runtime completo (
createFormen$libs/forms), Standard Schema/SIUM validation, registro de fields, dirty/touched, error aggregation, reset, first-error focus.Form.AutoFieldsrenderiza fields reflexivamente desde el schema SIUM. - morfo: declara
Provider+ actions (Submit,Reset,ErrorSummary) + 11 parts de AutoFields para selectores de la receta. Eventos sema:commit-submit(fulfill),signal-invalid(risk),commit-reset(neutral).
API Shape
<script lang="ts">
import { Form } from '$uix/eidos/components/form';
import { Field } from '$uix/eidos/components/field';
import { createForm } from '$libs/forms';
import { object, string, number, pipe, min, max, email, integer, meta } from '$sium/core';
const schema = object({
fullName: pipe(string(), min(2), meta({ label: 'Full name' })),
email: pipe(string(), email(), meta({ label: 'Email' })),
age: pipe(number(), integer(), min(18), max(80), meta({ label: 'Age' }))
});
const form = createForm({
schema,
defaults: { fullName: 'Ada Lovelace', email: 'ada@example.com', age: 36 },
validationBehaviour: 'onChange'
});
</script>
<Form {form} {schema} variant="panel">
<Form.ErrorSummary />
<Field name="fullName" label="Full name" required>
<Field.Label>Full name <Field.RequiredIndicator /></Field.Label>
<Field.Control><Field.Input bind:value={form.values.fullName} /></Field.Control>
<Field.ErrorText>{form.errors.fullName?.[0]}</Field.ErrorText>
</Field>
<Field name="age" label="Age" required>
<Field.Label>Age <Field.RequiredIndicator /></Field.Label>
<Field.Control><input type="number" bind:value={form.values.age} /></Field.Control>
<Field.ErrorText>{form.errors.age?.[0]}</Field.ErrorText>
</Field>
<Form.Reset>Reset</Form.Reset>
<Form.Submit color="fulfill">Save</Form.Submit>
</Form>
Form.AutoFields is the deliberate exception to the thin-provider rule: it is a
reflective renderer over SIUM introspection, not a state holder. It must stay
inside <Form> so it can use the active form context and schema.
Validation timing belongs to Soma / $libs/forms, not Eidos. The supported
values are progressive, onSubmit, onBlur and onChange; onChange is the
real-time validation mode used by the docs demo by default.
The demo starts with valid defaults. This is intentional: onChange validates
immediately and should only show errors after the user edits a field into an
invalid value, unless a demo explicitly documents an initially invalid state.
Morfo / Sema
| Event | Family | Intent | Target | Decision |
|---|---|---|---|---|
commit-submit |
commit |
fulfill |
provider | Valid submit confirms data. |
signal-invalid |
signal |
risk |
provider | Invalid submit warns without committing. |
commit-reset |
commit |
neutral |
provider | Reset is a neutral commit to defaults. |
AutoFields structural parts are declared in formMorfo because their selectors
are part of the recipe. They do not create new semantic events.
Comparativa
| Capability | Radix Form | Ark Field | React Aria | shadcn-svelte | UIX |
|---|---|---|---|---|---|
| Native form submit/reset | yes | yes | yes | yes | yes |
| Field registration | yes | yes | yes | yes | yes |
| Schema validation | native/custom | external | external | external | Standard Schema + SIUM nativo |
| Dirty/touched state | partial | yes | yes | partial | yes |
Error summary aggregada con aria-live |
yes | partial | partial | partial | yes |
| Schema-driven field generation | no | no | no | no | Form.AutoFields reflexivo sobre SIUM |
| Discriminated unions auto-renderizadas | no | no | no | no | yes (AutoFieldsDiscriminated) |
| Array fields auto-renderizadas | no | no | partial | no | yes (AutoFieldsArray) |
| First-error focus on submit fail | yes | no | yes | no | yes |
| Validation timing modes (progressive/onBlur/onChange/onSubmit) | partial | yes | partial | partial | yes |
Decisiones
- El wrapper Eidos es deliberadamente pequeño: si Form necesita
comportamiento nuevo, va a
$libs/forms, Soma Form o Morfo primero; Eidos sólo expone receta visual para parts y estados que ya existen. Form.AutoFieldses la excepción: es un renderer reflexivo sobre introspección SIUM. Debe vivir dentro de<Form>para usar el contexto activo del form y el schema. No es un state holder, es un mapper schema→DOM.- Validation timing pertenece a Soma /
$libs/forms, no a Eidos. Valores soportados:progressive,onSubmit,onBlur,onChange. El demo usaonChange(validación tiempo real). - Demos arrancan con defaults válidos:
onChangevalidaría inmediatamente y mostraría errores antes de que el usuario edite, lo cual es ruido perceptivo. Sólo demos que explícitamente quieran un estado inválido inicial parten así. - Sema sólo en commit-submit / signal-invalid / commit-reset: los cambios per-field son del Field, no del Form. El Form sólo emite en los momentos donde el "todo" cambia (envío, fallo de validación global, reset).
data-invaliden el root es una señal de bajo énfasis: marca el ErrorSummary con borde risk pero NO repinta los fields individuales — esos tienen su propio[data-invalid]desde el Field recipe.
Gaps
| Gap | Disposición | Detalle |
|---|---|---|
apg URL canónica |
descartar | No hay un APG específico para "Form"; APG cubre widgets individuales. El form HTML nativo es la referencia accesibilidad. |
| Action confirmation modal antes de submit destructivo | diferir | Caso aún sin patrón. Cuando aparezca, vive en Dialog/AlertDialog que el consumer monta dentro de Form.Submit. |
| AutoFields i18n (labels desde el schema) | implementar | SIUM meta({ label }) resuelve por idlangref. Falta documentar el patrón y validar con translations:check. |
| Multi-step form / wizard | diferir | Composición sobre Stepper + Form. No es responsabilidad de Form. Sin patrón canónico definido aún. |
| Submission feedback (toast on success) | diferir | El consumer decide; el contrato de Form ya emite commit-submit para que un sema listener arme el toast. |
| Optimistic UI rollback | descartar | Patrón de aplicación; Form no debe asumir transport. |
| Auto-save / draft persistence | descartar | Fuera de scope visual; vive en $libs/forms si fuera necesario. |