formstand 0.15.0 → 0.16.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 +259 -0
- package/dist/{createForm-C9hZffCQ.d.cts → createForm-jlzK2lCG.d.cts} +1 -1
- package/dist/{createForm-C9hZffCQ.d.ts → createForm-jlzK2lCG.d.ts} +1 -1
- package/dist/devtools/index.d.cts +1 -1
- package/dist/devtools/index.d.ts +1 -1
- package/dist/index.cjs +27 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +21 -4
- package/dist/index.d.ts +21 -4
- package/dist/index.js +27 -1
- package/dist/index.js.map +1 -1
- package/package.json +8 -8
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,264 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.16.0 — 2026-09-16
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- **`useFields`: composite fields.** One control backed by several schema
|
|
8
|
+
paths (a coordinate pair behind a DMS input, a range, a date span)
|
|
9
|
+
binds them in one call: `useFields(form, ["latitude", "longitude"])`
|
|
10
|
+
returns the position-typed tuple of full field bindings plus the
|
|
11
|
+
combined view — `error` merged in path order and deduped (a
|
|
12
|
+
cross-field `superRefine` stamping both paths reads once),
|
|
13
|
+
`firstError`, any-of-them `touched`/`dirty`/`isValidating`, and one
|
|
14
|
+
`onBlur` covering every path. The paths tuple must be render-stable;
|
|
15
|
+
React itself throws its hooks-count error otherwise, at the exact
|
|
16
|
+
render that broke the contract.
|
|
17
|
+
- **`field.firstError`.** The first error message or `undefined`, derived
|
|
18
|
+
on `UseFieldReturn` — the display shorthand every consumer (and every
|
|
19
|
+
generated kit adapter) re-derived by hand as `error?.[0]` with an
|
|
20
|
+
empty-array guard. `{field.firstError}` is now the whole helper line;
|
|
21
|
+
the full `field.error` array is unchanged beside it. The CLI keeps
|
|
22
|
+
emitting its own helper for now so generated code stays runnable on
|
|
23
|
+
older formstand; a future major of the generated adapters can lean on
|
|
24
|
+
this instead.
|
|
25
|
+
|
|
26
|
+
### Fixed
|
|
27
|
+
|
|
28
|
+
- **`useVariantField` accepts row-indexed union paths.** A discriminated
|
|
29
|
+
union that is an array's row item was unbindable: `UnionValueAt`
|
|
30
|
+
resolved paths through `keyof` only, so any array-index segment (a
|
|
31
|
+
template `` `pavements.${number}` `` or a literal `"pavements.0"`)
|
|
32
|
+
collapsed the variant-key set to `never` and the `field` argument
|
|
33
|
+
rejected everything. The type machinery now resolves the array element
|
|
34
|
+
through numeric segments, so a row component binds
|
|
35
|
+
`useVariantField(form, `` `pavements.${index}` ``, "designator")` fully
|
|
36
|
+
typed, in both the plain and the `createFormHooks`-bound flavors.
|
|
37
|
+
Runtime behavior was always correct; only the types refused. Found by
|
|
38
|
+
the airfield-maps trial, whose generated TODO pointed at exactly this
|
|
39
|
+
call.
|
|
40
|
+
|
|
41
|
+
## formstand-cli 0.15.0 — 2026-09-16
|
|
42
|
+
|
|
43
|
+
### Changed
|
|
44
|
+
|
|
45
|
+
- **`--form-prop` is single-file only.** With `--layout module` the
|
|
46
|
+
fields are pre-wired to the module's own singleton, so the `form`
|
|
47
|
+
prop was decorative when it told the truth and silently split state
|
|
48
|
+
when it lied (the dev-mode guard existed to catch the lie). The CLI
|
|
49
|
+
now rejects the combination with the fix in the message: the page
|
|
50
|
+
already owns the module's form by importing the exported instance and
|
|
51
|
+
driving it directly. The wizard stops asking the question under the
|
|
52
|
+
module layout, and the playground's command builder disables the
|
|
53
|
+
checkbox. The programmatic `emitModuleForm` surface keeps the option
|
|
54
|
+
for callers who accept the singleton contract knowingly.
|
|
55
|
+
|
|
56
|
+
### Fixed
|
|
57
|
+
|
|
58
|
+
- **The array-row union TODO gives honest advice.** The single-file
|
|
59
|
+
spelling claimed the union was "not supported"; the module spelling
|
|
60
|
+
recommended a `useVariantField` call that did not typecheck before the
|
|
61
|
+
library fix above. Both now describe the working binding and name the
|
|
62
|
+
formstand 0.16+ floor for row-indexed union paths.
|
|
63
|
+
|
|
64
|
+
## formstand-cli 0.14.0 — 2026-09-10
|
|
65
|
+
|
|
66
|
+
### Added
|
|
67
|
+
|
|
68
|
+
- **`component: "textarea"` in the config `fields` block.** The second
|
|
69
|
+
override flavor: a multi-line control for string fields, the binding
|
|
70
|
+
unchanged. Each kit renders its own control (mui `TextField multiline`,
|
|
71
|
+
mantine/chakra/shadcn `Textarea`, antd `Input.TextArea`, plain a native
|
|
72
|
+
`<textarea>`), and it composes with `span` in one entry, so a
|
|
73
|
+
`span: "full"` cover letter can finally look like one. `optionsProp`
|
|
74
|
+
and enum targets are rejected loudly.
|
|
75
|
+
- **JSON Schema defaults and string formats reach the generated
|
|
76
|
+
validator.** A document `default` now emits `.default(value)` in the
|
|
77
|
+
generated zod schema beside the `initialValues` seed it already fed,
|
|
78
|
+
and `format: email`/`uri`/`uuid` emits `z.email()`/`z.url()`/
|
|
79
|
+
`z.uuid()` while the control stays a text field. The emitted schema is
|
|
80
|
+
the runtime source of truth, so it no longer drops contracts the
|
|
81
|
+
source document carried. Note zod's `.default()` makes the input
|
|
82
|
+
optional by design.
|
|
83
|
+
|
|
84
|
+
### Fixed
|
|
85
|
+
|
|
86
|
+
- **An optional union starts empty and its select can clear it.** An
|
|
87
|
+
optional/nullable discriminated union was seeded with its first
|
|
88
|
+
variant, so an untouched form submitted a variant the user never
|
|
89
|
+
chose, and the discriminant select offered no way back. The union now
|
|
90
|
+
starts `undefined`/`null`, and its select reads the discriminant but
|
|
91
|
+
writes the whole union: the empty choice clears it, a tag starts that
|
|
92
|
+
variant blank. mui's select gains an explicit None item; shadcn's
|
|
93
|
+
Radix select cannot hold an empty item, so there the fixed seed alone
|
|
94
|
+
closes the phantom-submit bug. Required unions are unchanged.
|
|
95
|
+
- **Array add buttons read as the action they perform.** "Add
|
|
96
|
+
references" becomes "Add reference": a conservative singularizer on
|
|
97
|
+
the label's last word, with irregulars passing through unchanged.
|
|
98
|
+
- **A union variant with one field no longer wraps it in a fragment.**
|
|
99
|
+
|
|
100
|
+
## formstand-cli 0.13.0 — 2026-09-03
|
|
101
|
+
|
|
102
|
+
### Added
|
|
103
|
+
|
|
104
|
+
- **JSON Schema and OpenAPI inputs.** A `.json` input file is read as a
|
|
105
|
+
bare JSON Schema (2020-12) or an OpenAPI 3.x document: a third
|
|
106
|
+
front-end into the same IR, following type mode's path, so the CLI
|
|
107
|
+
generates the zod schema beside the component (`--schema-out` places
|
|
108
|
+
it; `--layout module` makes it the module's `schema.ts`). The new
|
|
109
|
+
`--schema` flag selects inside the document: a component schema name,
|
|
110
|
+
or a full `#/...` JSON pointer that can reach an operation's request
|
|
111
|
+
body. What translates: `properties`/`required`, `enum`/`const`,
|
|
112
|
+
`oneOf` string consts, discriminated `oneOf` into `useVariantField`
|
|
113
|
+
unions, `allOf` object merging, `$ref` chains, `prefixItems` tuples,
|
|
114
|
+
`format: date`/`date-time` as date fields, primitive `default`s into
|
|
115
|
+
`initialValues`, `description` as helper text, `title` as the label,
|
|
116
|
+
and every nullable dialect (3.1 type arrays, 3.0 `nullable: true`,
|
|
117
|
+
`oneOf` with a null branch). Everything else degrades to the loud TODO
|
|
118
|
+
fallback; Swagger 2.0 and YAML are refused with conversion hints. The
|
|
119
|
+
wizard understands `.json` inputs too, asking `--schema` in place of
|
|
120
|
+
the zod-or-type question.
|
|
121
|
+
|
|
122
|
+
### Fixed
|
|
123
|
+
|
|
124
|
+
- **A PascalCase schema export no longer collides with its module type
|
|
125
|
+
alias.** `export const MySchema` derives the form name MyForm, whose
|
|
126
|
+
schema type alias was also named MySchema, so the module barrel
|
|
127
|
+
exported two different MySchemas (the value from `schema.ts`, the type
|
|
128
|
+
from `types.ts`) and the generated `index.ts` failed to compile with
|
|
129
|
+
TS2308. The alias now yields the colliding name to the user's value
|
|
130
|
+
and becomes `MySchemaType`, both in `types.ts` and in the
|
|
131
|
+
`--form-prop` component that consumes it.
|
|
132
|
+
|
|
133
|
+
## formstand-cli 0.12.0 — 2026-08-18
|
|
134
|
+
|
|
135
|
+
### Added
|
|
136
|
+
|
|
137
|
+
- **`--wizard`: the flag questions asked one at a time.** Which file, zod
|
|
138
|
+
export or TS type, which kit, layout, section chrome, columns, name, and
|
|
139
|
+
where to write — Enter accepts the default on every question, invalid
|
|
140
|
+
answers re-ask, and an existing output target offers `--force` on the
|
|
141
|
+
spot. The interview ends by printing the composed `formstand-gen`
|
|
142
|
+
command before running it, so the run is reproducible without the wizard
|
|
143
|
+
from then on. Strictly opt-in (never a TTY sniff, and the flag runs
|
|
144
|
+
alone), prompts on stderr so stdout output stays pipeable, answers
|
|
145
|
+
pipeable on stdin, and zero new dependencies: numbered readline lists,
|
|
146
|
+
not a prompts library.
|
|
147
|
+
- **Per-field layout placement: `span` in the config `fields` block.** With
|
|
148
|
+
`--columns 2|3`, `"employment.notes": { span: "full" }` gives a field the
|
|
149
|
+
whole row and `{ span: 2 }` two of three columns; it composes with an
|
|
150
|
+
autocomplete override in the same entry. Each backend emits the span in
|
|
151
|
+
the dialect its section grids already use (mui `Grid` size objects, with
|
|
152
|
+
the legacy `item xs/sm` spelling on mui@5/6; mantine `Grid.Col` span
|
|
153
|
+
objects; antd `Col` `xs`/`sm`; shadcn `md:col-span-N`; chakra a `Box`
|
|
154
|
+
with responsive `gridColumn`), so every spelling still collapses to one
|
|
155
|
+
column on a phone. Plain is the one exception, for partial spans only:
|
|
156
|
+
inline styles cannot carry a media query, so a numeric span widens to the
|
|
157
|
+
full row with a comment saying so at the site. A span with no grid to act
|
|
158
|
+
on (a root-level field, array rows, a container, a 1-column form, or a
|
|
159
|
+
module-layout field below a section's direct children) is a loud
|
|
160
|
+
generation-time error, never a silent no-op.
|
|
161
|
+
|
|
162
|
+
### Fixed
|
|
163
|
+
|
|
164
|
+
- **Generated bodies indent under their real container.** Panel and
|
|
165
|
+
collapsible chrome nests the body's container deeper than the section tag
|
|
166
|
+
(Card > CardContent > Grid), but cells were emitted shallower than the
|
|
167
|
+
container holding them and array row stacks sat beside their wrapper
|
|
168
|
+
cells instead of inside them. Both layouts now shift bodies to the
|
|
169
|
+
chrome's true depth, so generated files survive a host Prettier pass
|
|
170
|
+
without reflowing. Cosmetic only; the demos regenerate with a
|
|
171
|
+
whitespace-only diff.
|
|
172
|
+
- **A union riding a grid cell keeps its vertical rhythm.** Inside an
|
|
173
|
+
object section at `--columns 2|3`, a discriminated union's controls are
|
|
174
|
+
bare siblings in one full-row cell and had lost the spacing the 1-column
|
|
175
|
+
chrome provides; mui and mantine now wrap them in the kit's Stack. antd
|
|
176
|
+
needs no wrapper, since each of its controls is already a vertical Flex.
|
|
177
|
+
|
|
178
|
+
## 0.15.2 — 2026-08-11
|
|
179
|
+
|
|
180
|
+
Identical to 0.15.1, republished under a fresh tag. The v0.15.1 tag was
|
|
181
|
+
pushed during the August 6 GitHub Actions outage and GitHub dropped the
|
|
182
|
+
tag event outright, so the Publish workflow never ran and 0.15.1 never
|
|
183
|
+
reached npm. The Publish workflow only fires on tag pushes by design (the
|
|
184
|
+
trusted-publishing trust is bound to that trigger), so the recovery is a
|
|
185
|
+
new version under a new tag. No code changes.
|
|
186
|
+
|
|
187
|
+
## formstand-cli 0.11.1 — 2026-08-11
|
|
188
|
+
|
|
189
|
+
Identical to formstand-cli 0.11.0, republished under a fresh tag for the
|
|
190
|
+
same reason as 0.15.2 above: the cli-v0.11.0 tag event was dropped in the
|
|
191
|
+
August 6 outage and the version never reached npm. No code changes.
|
|
192
|
+
|
|
193
|
+
## 0.15.1 — 2026-08-06
|
|
194
|
+
|
|
195
|
+
### Fixed
|
|
196
|
+
|
|
197
|
+
- **Tuple element paths type as their exact element.** `FieldValue` resolved
|
|
198
|
+
a path like `"pair.0"` into `[string, number]` through the generic array
|
|
199
|
+
arm, so the value typed as `string | number` instead of `string`. The
|
|
200
|
+
bundled field components hid this by asserting their own value type; the
|
|
201
|
+
raw hooks exposed it, and a CLI module tuple section over a mixed tuple
|
|
202
|
+
did not compile because its per-kind prop builders need the exact element.
|
|
203
|
+
Tuple index paths now resolve positionally. Types only, no runtime change.
|
|
204
|
+
|
|
205
|
+
## formstand-cli 0.11.0 — 2026-08-06
|
|
206
|
+
|
|
207
|
+
### Fixed
|
|
208
|
+
|
|
209
|
+
- **Four edge-config compile and layout bugs in the container migration**,
|
|
210
|
+
all one family: a gate that decides imports or exports did not count every
|
|
211
|
+
shape that renders section chrome.
|
|
212
|
+
- A module union section at `--columns 2|3` rendered its vertical `Stack`
|
|
213
|
+
shell while importing for the grid it no longer renders, a TS2304 in
|
|
214
|
+
mantine and mui output. Imports and shell now derive from one visual.
|
|
215
|
+
- A tuple-only root emitted section chrome (`Typography`/`Title`/`Heading`
|
|
216
|
+
headings, and at `--columns 2|3` the `Row`/`Col` or `Grid` pair) without
|
|
217
|
+
importing any of it; the section-import gate now counts tuples.
|
|
218
|
+
- A module tuple section imported the bound field hook from `hooks.ts`,
|
|
219
|
+
which never exported it for a tuple-only root; the export gate now
|
|
220
|
+
mirrors the tuple section's own import rule.
|
|
221
|
+
- A module nested `<fieldset>` lost its full-row span for the CSS-grid
|
|
222
|
+
backends (plain, shadcn, chakra), collapsing a nested section into one
|
|
223
|
+
grid cell; the span now rides the fieldset whenever no cell wrapper
|
|
224
|
+
exists to carry it.
|
|
225
|
+
|
|
226
|
+
The version matrix now generates tuple-only and root-union schemas against
|
|
227
|
+
every kit's real declarations (`cli/matrix/edgeSchemas.ts`), and the CI
|
|
228
|
+
suite grew matching regression tests, so the class cannot ship green again.
|
|
229
|
+
|
|
230
|
+
### Changed
|
|
231
|
+
|
|
232
|
+
- **`--columns` output is responsive.** `--columns 2` now means "two columns
|
|
233
|
+
when there is room, one on a phone" in every backend's own dialect: MUI gets
|
|
234
|
+
an `sx` breakpoint object, chakra a responsive `gridTemplateColumns` style
|
|
235
|
+
prop, mantine `SimpleGrid cols={{ base: 1, sm: N }}`, and the inline-style
|
|
236
|
+
backends (plain, antd) a `repeat(auto-fit, minmax(...))` track whose calc
|
|
237
|
+
caps the column count at N while a 220px floor collapses columns that would
|
|
238
|
+
squeeze inputs below a usable width. shadcn already collapsed via
|
|
239
|
+
`md:grid-cols-N`. Fixed grid tracks were why generated multi-column forms
|
|
240
|
+
were unusable on phones; regenerate to pick the new grids up.
|
|
241
|
+
- **antd sections lay out with `Row`/`Col`** instead of an inline CSS grid:
|
|
242
|
+
the kit's own 24-column idiom, responsive via `xs`/`sm` props, in both
|
|
243
|
+
layouts. Nested sections, arrays, unions, and tuples ride spanning or item
|
|
244
|
+
`Col`s; a union section keeps its vertical shell, since its children are
|
|
245
|
+
conditional fragments that cannot each be a `Col`. This is also the shape
|
|
246
|
+
that makes a per-field span a one-prop change later.
|
|
247
|
+
- **mantine sections lay out with `Grid`/`Grid.Col`** instead of
|
|
248
|
+
`SimpleGrid`: responsive span objects (`span={{ base: 12, sm: 6 }}`), and
|
|
249
|
+
the container that can express a per-field span at all, which `SimpleGrid`
|
|
250
|
+
cannot. Same cell rules as antd: leaves ride item Cols, nested content
|
|
251
|
+
spans, 1-column chrome reads as a `Stack`. The version matrix caught one
|
|
252
|
+
invented prop on the way in (`Grid` has no `gutter` in v9), which is
|
|
253
|
+
exactly the drift it exists to catch.
|
|
254
|
+
- **mui sections lay out with `Grid` containers** instead of `sx` CSS grids
|
|
255
|
+
— the same component the hand-written playground demos use — with the
|
|
256
|
+
spelling the pinned major actually accepts: `size={{ xs: 12, sm: 6 }}` on
|
|
257
|
+
v7/v9, legacy `item xs={12} sm={6}` on v5 **and** v6 (v6's size-prop grid
|
|
258
|
+
is the separate `Grid2` import; the matrix against real v6 declarations is
|
|
259
|
+
what established that). 1-column chrome keeps the historical Stack and
|
|
260
|
+
CardContent output.
|
|
261
|
+
|
|
3
262
|
## 0.15.0 — 2026-08-04
|
|
4
263
|
|
|
5
264
|
### Fixed
|
|
@@ -40,7 +40,7 @@ type FieldPath<T, D extends number = DefaultPathDepth> = FieldPathRec<T, Normali
|
|
|
40
40
|
type FieldPathRec<T, D extends number> = [D] extends [0] ? never : IsArray<T> extends true ? T extends readonly (infer U)[] ? `${number}` | (IsRecord<NonNullable<U>> extends true ? `${number}.${FieldPathRec<NonNullable<U>, Prev[D]>}` : IsArray<NonNullable<U>> extends true ? `${number}.${FieldPathRec<NonNullable<U>, Prev[D]>}` : never) : never : IsRecord<T> extends true ? {
|
|
41
41
|
[K in keyof T & string]: K | (IsRecord<NonNullable<T[K]>> extends true ? `${K}.${FieldPathRec<NonNullable<T[K]>, Prev[D]>}` : IsArray<NonNullable<T[K]>> extends true ? `${K}.${FieldPathRec<NonNullable<T[K]>, Prev[D]>}` : never);
|
|
42
42
|
}[keyof T & string] : never;
|
|
43
|
-
type StepValue<T, K extends string> = T extends readonly (infer U)[] ? K extends `${number}` ? U : never : T extends object ? K extends keyof T ? T[K] : never : never;
|
|
43
|
+
type StepValue<T, K extends string> = T extends readonly (infer U)[] ? K extends keyof T ? T[K] : K extends `${number}` ? U : never : T extends object ? K extends keyof T ? T[K] : never : never;
|
|
44
44
|
type FieldValue<T, P extends string> = [T] extends [NonNullable<T>] ? P extends `${infer Head}.${infer Tail}` ? FieldValue<StepValue<T, Head>, Tail> : StepValue<T, P> : FieldValue<NonNullable<T>, P> | undefined;
|
|
45
45
|
|
|
46
46
|
type ValidationMode = "onChange" | "onBlur" | "onSubmit" | "onTouched" | "all";
|
|
@@ -40,7 +40,7 @@ type FieldPath<T, D extends number = DefaultPathDepth> = FieldPathRec<T, Normali
|
|
|
40
40
|
type FieldPathRec<T, D extends number> = [D] extends [0] ? never : IsArray<T> extends true ? T extends readonly (infer U)[] ? `${number}` | (IsRecord<NonNullable<U>> extends true ? `${number}.${FieldPathRec<NonNullable<U>, Prev[D]>}` : IsArray<NonNullable<U>> extends true ? `${number}.${FieldPathRec<NonNullable<U>, Prev[D]>}` : never) : never : IsRecord<T> extends true ? {
|
|
41
41
|
[K in keyof T & string]: K | (IsRecord<NonNullable<T[K]>> extends true ? `${K}.${FieldPathRec<NonNullable<T[K]>, Prev[D]>}` : IsArray<NonNullable<T[K]>> extends true ? `${K}.${FieldPathRec<NonNullable<T[K]>, Prev[D]>}` : never);
|
|
42
42
|
}[keyof T & string] : never;
|
|
43
|
-
type StepValue<T, K extends string> = T extends readonly (infer U)[] ? K extends `${number}` ? U : never : T extends object ? K extends keyof T ? T[K] : never : never;
|
|
43
|
+
type StepValue<T, K extends string> = T extends readonly (infer U)[] ? K extends keyof T ? T[K] : K extends `${number}` ? U : never : T extends object ? K extends keyof T ? T[K] : never : never;
|
|
44
44
|
type FieldValue<T, P extends string> = [T] extends [NonNullable<T>] ? P extends `${infer Head}.${infer Tail}` ? FieldValue<StepValue<T, Head>, Tail> : StepValue<T, P> : FieldValue<NonNullable<T>, P> | undefined;
|
|
45
45
|
|
|
46
46
|
type ValidationMode = "onChange" | "onBlur" | "onSubmit" | "onTouched" | "all";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
-
import { F as Form } from '../createForm-
|
|
3
|
+
import { F as Form } from '../createForm-jlzK2lCG.cjs';
|
|
4
4
|
import 'zustand/vanilla';
|
|
5
5
|
|
|
6
6
|
type DevtoolsPosition = "bottom-right" | "bottom-left" | "top-right" | "top-left";
|
package/dist/devtools/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
-
import { F as Form } from '../createForm-
|
|
3
|
+
import { F as Form } from '../createForm-jlzK2lCG.js';
|
|
4
4
|
import 'zustand/vanilla';
|
|
5
5
|
|
|
6
6
|
type DevtoolsPosition = "bottom-right" | "bottom-left" | "top-right" | "top-left";
|
package/dist/index.cjs
CHANGED
|
@@ -1390,6 +1390,8 @@ function useField(form, pathArg, options) {
|
|
|
1390
1390
|
initialValue: slice.initialValue,
|
|
1391
1391
|
emptyValue,
|
|
1392
1392
|
error: slice.error,
|
|
1393
|
+
// `?.[0]` covers both the undefined channel and an empty array.
|
|
1394
|
+
firstError: slice.error?.[0],
|
|
1393
1395
|
touched: slice.touched,
|
|
1394
1396
|
dirty: slice.dirty,
|
|
1395
1397
|
isValidating: slice.isValidating,
|
|
@@ -1669,6 +1671,30 @@ function useFormValues(form) {
|
|
|
1669
1671
|
return react$1.useStore(form.store, (state) => state.values);
|
|
1670
1672
|
}
|
|
1671
1673
|
|
|
1674
|
+
// src/react/useFields.ts
|
|
1675
|
+
function useFields(form, paths) {
|
|
1676
|
+
const fields = paths.map(
|
|
1677
|
+
(path) => (
|
|
1678
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks -- a fixed hook count by the stable-arity contract above; each element must be a REAL UseFieldReturn so it spreads into the prop builders
|
|
1679
|
+
useField(form, path)
|
|
1680
|
+
)
|
|
1681
|
+
);
|
|
1682
|
+
const merged = [...new Set(fields.flatMap((field) => field.error ?? []))];
|
|
1683
|
+
return {
|
|
1684
|
+
fields,
|
|
1685
|
+
error: merged.length > 0 ? merged : void 0,
|
|
1686
|
+
firstError: merged[0],
|
|
1687
|
+
touched: fields.some((field) => field.touched),
|
|
1688
|
+
dirty: fields.some((field) => field.dirty),
|
|
1689
|
+
isValidating: fields.some((field) => field.isValidating),
|
|
1690
|
+
onBlur: () => {
|
|
1691
|
+
fields.forEach((field) => {
|
|
1692
|
+
field.onBlur();
|
|
1693
|
+
});
|
|
1694
|
+
}
|
|
1695
|
+
};
|
|
1696
|
+
}
|
|
1697
|
+
|
|
1672
1698
|
// src/react/inputProps.ts
|
|
1673
1699
|
var hasFieldError = (error) => error !== void 0 && error.length > 0;
|
|
1674
1700
|
var ariaInvalid = (field) => hasFieldError(field.error) ? true : void 0;
|
|
@@ -2087,6 +2113,7 @@ exports.shouldValidateOn = shouldValidateOn;
|
|
|
2087
2113
|
exports.textInputProps = textInputProps;
|
|
2088
2114
|
exports.useField = useField;
|
|
2089
2115
|
exports.useFieldArray = useFieldArray;
|
|
2116
|
+
exports.useFields = useFields;
|
|
2090
2117
|
exports.useForm = useForm;
|
|
2091
2118
|
exports.useFormError = useFormError;
|
|
2092
2119
|
exports.useFormSelector = useFormSelector;
|