formstand 0.24.0 → 0.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,81 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.25.0 — 2026-11-12
4
+
5
+ ### Added
6
+
7
+ - `useVariantFieldArray(form, unionPath, field)`, the array twin of
8
+ `useVariantField`: a `z.array` that lives in one variant of a
9
+ discriminated union sits at a path `useFieldArray` cannot take, since
10
+ `FieldPath` on a union offers only the keys every variant shares. The
11
+ hook binds `${unionPath}.${field}` and types the rows from the variants
12
+ that declare the array; `field` is a variant sub-path (`"lights"`,
13
+ `"spec.lights"`, or a row template for an array nested in another
14
+ variant array's rows), and a common-key array stays with plain
15
+ `useFieldArray`, which the variant hook rejects. Same return as
16
+ `useFieldArray`, row ids included. `createFormHooks` gains the bound
17
+ `use{Name}VariantFieldArray`, and the type `VariantArrayFieldPath` is
18
+ exported. formstand-cli emits it for arrays inside union variants.
19
+
20
+ ### Fixed
21
+
22
+ - `createFormHooks(createForm(schema, options), name)` with the form
23
+ created inline collapsed every depth-typed bound hook (`useXField`,
24
+ `useXFieldArray`, `useXIsDirty`) to a `never` path: the inner call
25
+ inferred its depth from the outer call's still-unresolved depth
26
+ parameter, so the depth became the whole budget union. `createForm` and
27
+ `useForm` now take their depth from `pathDepth` alone (`NoInfer` on the
28
+ returned form), so the inline shape types like the const-form shape the
29
+ docs show, and an explicit `pathDepth` still infers its literal.
30
+
31
+ ## formstand-cli 0.26.0 — 2026-11-12
32
+
33
+ ### Added
34
+
35
+ - An **array inside a union variant** generates instead of degrading to a
36
+ TODO, in both layouts and every kit: a `{Stem}Rows` component on
37
+ `useVariantFieldArray(form, unionPath, "lights")` (formstand 0.25), its
38
+ row leaves on the variant path (the Bound components in the single-file
39
+ layout, `useVariantField` on a row template in the module layout), the
40
+ empty item typed from the hook, and deeper lists under those rows bound
41
+ the same way. Under a union row item the row's own index renames to the
42
+ next hole for the extracted component, as nested lists already do. Still
43
+ a TODO: a union inside a variant, a union or tuple inside a variant
44
+ array's rows, and an array every variant shares (a plain path, left to
45
+ `useFieldArray`). Generated output with a variant array needs formstand
46
+ 0.25 or newer.
47
+
48
+ ### Changed
49
+
50
+ - **Arrays of flat object rows generate as tables.** Where every row
51
+ field is a scalar within the path budget and none carries a component
52
+ override, the array renders as a table in both layouts and every kit:
53
+ one column per row field with
54
+ the label in the header (a description becomes the header's title), a
55
+ `{Stem}Row` component per row binding each cell from hoisted `useField`
56
+ hooks (`useVariantField` for an array inside a union variant) and
57
+ naming the control through `aria-label`, a Remove column, and the
58
+ array-level error plus the Add button under the table. antd's
59
+ data-driven `Table` gets one cell component per column. Rows holding a
60
+ container, scalar rows and rows with an overridden field keep the
61
+ cards, and the new `fields: { "<path>": { layout: "cards" } }` opts a
62
+ flat array back out; `layout: "table"` asserts one and errors, naming
63
+ the row field, where it cannot be built.
64
+
65
+ ### Fixed
66
+
67
+ - The single-file layout's TODO for a container that still degrades inside
68
+ a row union's variant printed the hole-stripped naming path
69
+ (`"pavements.sub"`), which names no field a reader can find. It prints
70
+ the row template (`"pavements.${index}.sub"`), as the module layout
71
+ already did.
72
+ - Two usage walks passed the wrong budget when descending into an
73
+ array's rows, so anything two objects deep under a row was skipped: a
74
+ discriminated union there rendered its variant controls without
75
+ importing the prop builders they call, and a described leaf there was
76
+ handed a `description` prop the Bound components did not declare.
77
+ Both walks carry the real budget now.
78
+
3
79
  ## 0.24.0 — 2026-10-23
4
80
 
5
81
  ### Added
package/README.md CHANGED
@@ -123,7 +123,7 @@ PATCH-ready payload of exactly what changed.
123
123
  | Area | What's there | Docs |
124
124
  | --- | --- | --- |
125
125
  | Form instance | `createForm`, `setValue`/`setValues`, `reset`/`resetField`/`adoptValues`, `submit`/`handleSubmit`, `diff`/`dirtyFields`, `snapshot`/`restore`, array ops | [createForm & Form](https://scrumrot.github.io/formstand/documentation/api/) |
126
- | Hooks | `useForm`, `useField`, `useFieldArray`, `useVariantField`, `useFormSelector`, `useFormValues`, the flag hooks, `createFormContext`, `createFormHooks` | [Hooks](https://scrumrot.github.io/formstand/documentation/api/hooks) |
126
+ | Hooks | `useForm`, `useField`, `useFieldArray`, `useVariantField`, `useVariantFieldArray`, `useFormSelector`, `useFormValues`, the flag hooks, `createFormContext`, `createFormHooks` | [Hooks](https://scrumrot.github.io/formstand/documentation/api/hooks) |
127
127
  | Components | `TextField`, `NumberField`, `DateField`, `CheckboxField`, `SelectField`, the prop builders, `useNumberInput`, `focusField`/`focusFirstError` | [Components & bindings](https://scrumrot.github.io/formstand/documentation/api/components) |
128
128
  | Validation | five modes plus `reValidateMode`, field-scoped passes, async refines with race handling, per-field `debounceMs` | [Validation](https://scrumrot.github.io/formstand/documentation/validation) |
129
129
  | Utilities | `parsePath`/`getAtPath`/`setAtPath`, `validateSync`/`validateAsync`, `persistForm`, and the full type list | [Utilities & types](https://scrumrot.github.io/formstand/documentation/api/utilities) |
@@ -202,7 +202,7 @@ type Form<TSchema extends z.ZodType, D extends PathDepth = DefaultPathDepth> = R
202
202
  snapshot: () => FormState<z.input<TSchema>>;
203
203
  restore: (snapshot: FormState<z.input<TSchema>>) => void;
204
204
  }>;
205
- declare const createForm: <TSchema extends z.ZodType, D extends PathDepth = DefaultPathDepth>(schema: TSchema, options: CreateFormOptions<TSchema, D>) => Form<TSchema, D>;
205
+ declare const createForm: <TSchema extends z.ZodType, D extends PathDepth = DefaultPathDepth>(schema: TSchema, options: CreateFormOptions<TSchema, D>) => Form<TSchema, NoInfer<D>>;
206
206
 
207
207
  export { createForm as p, emptyValueForSchema as q, flattenIssues as r, isAsyncRequiredError as s, isClearableSchema as t, shouldValidateOn as u, validateAsync as v, validateSync as w };
208
208
  export type { ArrayFieldPath as A, BoolMap as B, CreateFormOptions as C, DefaultPathDepth as D, ErrorMap as E, Form as F, InvalidSubmitHandler as I, PathDepth as P, ReadonlyStoreApi as R, SettledFieldValidationResult as S, ValidationMode as V, FormState as a, FieldValidationResult as b, FieldPath as c, FieldValue as d, ArrayItemOf as e, FieldSnapshot as f, FieldsValidationResult as g, ResetOptions as h, SettledFieldsValidationResult as i, SettledValidationResult as j, SubmitHandler as k, SubmitOptions as l, SubmitResult as m, ValidationResult as n, ValidationTrigger as o };
@@ -202,7 +202,7 @@ type Form<TSchema extends z.ZodType, D extends PathDepth = DefaultPathDepth> = R
202
202
  snapshot: () => FormState<z.input<TSchema>>;
203
203
  restore: (snapshot: FormState<z.input<TSchema>>) => void;
204
204
  }>;
205
- declare const createForm: <TSchema extends z.ZodType, D extends PathDepth = DefaultPathDepth>(schema: TSchema, options: CreateFormOptions<TSchema, D>) => Form<TSchema, D>;
205
+ declare const createForm: <TSchema extends z.ZodType, D extends PathDepth = DefaultPathDepth>(schema: TSchema, options: CreateFormOptions<TSchema, D>) => Form<TSchema, NoInfer<D>>;
206
206
 
207
207
  export { createForm as p, emptyValueForSchema as q, flattenIssues as r, isAsyncRequiredError as s, isClearableSchema as t, shouldValidateOn as u, validateAsync as v, validateSync as w };
208
208
  export type { ArrayFieldPath as A, BoolMap as B, CreateFormOptions as C, DefaultPathDepth as D, ErrorMap as E, Form as F, InvalidSubmitHandler as I, PathDepth as P, ReadonlyStoreApi as R, SettledFieldValidationResult as S, ValidationMode as V, FormState as a, FieldValidationResult as b, FieldPath as c, FieldValue as d, ArrayItemOf as e, FieldSnapshot as f, FieldsValidationResult as g, ResetOptions as h, SettledFieldsValidationResult as i, SettledValidationResult as j, SubmitHandler as k, SubmitOptions as l, SubmitResult as m, ValidationResult as n, ValidationTrigger as o };
package/dist/index.cjs CHANGED
@@ -2243,6 +2243,14 @@ function useFieldArray(form, pathArg) {
2243
2243
  ]
2244
2244
  );
2245
2245
  }
2246
+
2247
+ // src/react/useVariantFieldArray.ts
2248
+ function useVariantFieldArray(form, unionPath, field) {
2249
+ return useFieldArray(
2250
+ form,
2251
+ `${unionPath}.${field}`
2252
+ );
2253
+ }
2246
2254
  var useFormError = (form) => react$1.useStore(
2247
2255
  form.store,
2248
2256
  shallow.useShallow((state) => state.errors[""])
@@ -2321,6 +2329,11 @@ var createFormHooks = (form, name) => {
2321
2329
  field
2322
2330
  );
2323
2331
  const useBoundFieldArray = (path) => useFieldArray(structural, path);
2332
+ const useBoundVariantFieldArray = (unionPath, field) => useVariantFieldArray(
2333
+ structural,
2334
+ unionPath,
2335
+ field
2336
+ );
2324
2337
  const useBoundSelector = (selector) => useFormSelector(structural, selector);
2325
2338
  const useBoundSelectorShallow = (selector) => useFormSelectorShallow(structural, selector);
2326
2339
  const useBoundError = () => useFormError(structural);
@@ -2340,6 +2353,7 @@ var createFormHooks = (form, name) => {
2340
2353
  [`use${prefix}Field`]: useBoundField,
2341
2354
  [`use${prefix}VariantField`]: useBoundVariantField,
2342
2355
  [`use${prefix}FieldArray`]: useBoundFieldArray,
2356
+ [`use${prefix}VariantFieldArray`]: useBoundVariantFieldArray,
2343
2357
  [`use${prefix}Selector`]: useBoundSelector,
2344
2358
  [`use${prefix}SelectorShallow`]: useBoundSelectorShallow,
2345
2359
  [`use${prefix}Error`]: useBoundError,
@@ -2694,6 +2708,7 @@ exports.useNumberInput = useNumberInput;
2694
2708
  exports.usePersistForm = usePersistForm;
2695
2709
  exports.useSubmitCount = useSubmitCount;
2696
2710
  exports.useVariantField = useVariantField;
2711
+ exports.useVariantFieldArray = useVariantFieldArray;
2697
2712
  exports.validateAsync = validateAsync;
2698
2713
  exports.validateSync = validateSync;
2699
2714
  //# sourceMappingURL=index.cjs.map