formstand 0.4.1 → 0.6.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,123 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.6.0 — 2026-07-09
4
+
5
+ ### Added
6
+
7
+ - `useIsDirty(form, path?)` and `useIsValid(form, path?)`: an optional
8
+ typed path scopes the flag to a subtree with the library's usual prefix
9
+ semantics (`"shipping"` covers `shipping.city`; for validity the path's
10
+ own key counts, so array-level errors match their array's path).
11
+ Subscriptions stay boolean-only — the component re-renders when the flag
12
+ flips, not on every keystroke like `useField(...).dirty`. Omitting the
13
+ path keeps the whole-form behavior; schema-less `FieldFormApi`-style
14
+ forms take plain string paths.
15
+
16
+ ## formstand-cli 0.2.2 — 2026-07-09
17
+
18
+ - Generated array hooks drop the explicit item type
19
+ (`useFieldArray(form, "items")`): formstand ≥ 0.5 infers it from the
20
+ schema through the path — and rejects the old explicit spelling on typed
21
+ forms. On formstand 0.4 the generated code still compiles, with untyped
22
+ items.
23
+ - Generated shadcn output defines one `ariaInvalid` helper (used by the
24
+ inlined adapters and the select trigger) instead of repeating the ternary
25
+ four times, and the generated `FieldError` computes the message once —
26
+ cosmetic; runtime behavior unchanged. Internally the MUI and shadcn
27
+ backends now share their emitted snippets (error helper, `BoundFieldProps`,
28
+ the leaf switch), so the generators can't drift.
29
+
30
+ ## 0.5.0 — 2026-07-09
31
+
32
+ ### Breaking
33
+
34
+ - `useFieldArray` infers the item type from a `Form<TSchema>` and a typed
35
+ path — `useFieldArray(form, "users")` (and template paths like
36
+ `` `albums.${index}.tracks` ``) needs no type argument, and `push`/`insert`
37
+ are typed against the schema's item. Consequently the old explicit
38
+ `useFieldArray<TItem>(form, path)` spelling is a **compile error on typed
39
+ forms** (drop the generic); it remains the way to bind schema-less
40
+ `FieldFormApi` forms, where there is nothing to infer from. A non-array
41
+ path types the items as `never`; a typo'd path is rejected against the
42
+ full `FieldPath` union, like `useField`. Path selectors return
43
+ `UseFieldArrayReturn<unknown>` (dynamic paths carry no type), also like
44
+ `useField`.
45
+
46
+ ### Fixed (site only)
47
+
48
+ - The deployed playground bundled two copies of React (the formstand alias
49
+ reaches outside the examples package, so its imports resolved the repo
50
+ root's copy) — every tab crashed at startup with a null hooks
51
+ dispatcher. The examples build now dedupes react/zustand/zod and CI
52
+ asserts the bundle holds exactly one React.
53
+
54
+ ### Docs & examples (no package changes)
55
+
56
+ - The playground demos bind shadcn's `Input`/`Textarea` with the library's
57
+ own exported `textInputProps`/`numberInputProps` — the shadcn adapter now
58
+ covers only the Radix dialect, and the "field has an error" predicate
59
+ lives once in `examples/src/fieldErrors.ts` (shared by both adapters).
60
+ - The copy-in shadcn kit is trimmed to what the demos render (Badge/Button
61
+ variants, dead peer-disabled label classes) and every component exports a
62
+ `Readonly` props type.
63
+
64
+ ## formstand-cli 0.2.1 — 2026-07-09
65
+
66
+ ### Fixed
67
+
68
+ - Leaf-free schemas (no scalar fields anywhere, e.g. an object of empty
69
+ objects) no longer generate non-compiling output in the `mui` and
70
+ `shadcn` backends — the emitted `BoundFieldProps` type referenced
71
+ `FieldFormApi` without importing it. All three backends now have a
72
+ leaf-free typecheck test.
73
+ - The shadcn backend's generated output is additionally typechecked in CI
74
+ against the repo's real Radix-based components (not just the structural
75
+ stub), so a shadcn/Radix prop-contract change fails our suite instead of
76
+ the consumer's build.
77
+
78
+ ### Docs & examples (no library package changes)
79
+
80
+ - Playground: the page chrome stylesheet is unlayered again — cascade
81
+ layers can't be transpiled, so pre-15.4 WebKit (the es2019 target's
82
+ audience) was losing every tab's styling; its selectors are class-scoped,
83
+ so the shadcn utilities are unaffected. The layer-order statement now
84
+ pins Tailwind's canonical `components` slot below `utilities`.
85
+ - shadcn kit: sliders take an `aria-label` routed to the Radix Thumb (the
86
+ Root is a `<span>`, so `htmlFor`/`id` never named it); radio-group items
87
+ show error styling via `group-aria-invalid` (Radix doesn't propagate the
88
+ Root's `aria-invalid` to items); the Team demo's rows are memoized with
89
+ stable handlers so keystrokes in one row stop re-rendering the rest.
90
+ - Publish workflow: `--provenance` is explicit again (fail-closed — the
91
+ automatic path silently skips attestation when conditions aren't met) and
92
+ the Node pin is `>=24.8`, the first line whose bundled npm meets trusted
93
+ publishing's 11.5.1 floor. CI asserts the root and examples lockfiles
94
+ agree on the deduped UI packages, so the smoke test can't silently
95
+ certify a different build than the deployed playground.
96
+
97
+ ## formstand-cli 0.2.0 — 2026-07-08
98
+
99
+ ### Added
100
+
101
+ - `--ui shadcn`: generates forms against [shadcn/ui](https://ui.shadcn.com/)
102
+ conventions — components imported from the app's `@/components/ui/*` alias
103
+ (what `npx shadcn add` scaffolds), an inlined adapter speaking the Radix
104
+ dialect (`onCheckedChange`/`onValueChange`, dropdown-close as the blur
105
+ trigger), and `aria-invalid` error styling with a message line. Generated
106
+ output is typechecked in CI against the library source and a structural
107
+ stub of the shadcn components, like the other backends.
108
+ - `emitShadcnForm` joins the programmatic API.
109
+
110
+ ### Docs & examples (no library package changes)
111
+
112
+ - Four shadcn/ui playground demos (Signup, Checkout, Settings, Team) plus
113
+ the formstand→shadcn adapter pattern they showcase —
114
+ `examples/src/shadcn/shadcnAdapter.ts` bridges `useField` to both native
115
+ inputs and the Radix-based widgets (Checkbox, Switch, Select, Slider,
116
+ RadioGroup).
117
+ - The playground carries its own copy-in shadcn component kit under a
118
+ scoped Tailwind 4 setup (no preflight; the plain-CSS and MUI demos are
119
+ untouched).
120
+
3
121
  ## 0.4.1 — 2026-07-08
4
122
 
5
123
  ### Fixed
package/dist/index.cjs CHANGED
@@ -995,17 +995,26 @@ var useFormError = (form) => react$1.useStore(
995
995
  form.store,
996
996
  shallow.useShallow((state) => state.errors[""])
997
997
  );
998
- var useIsDirty = (form) => react$1.useStore(
999
- form.store,
1000
- (state) => isFieldDirty(state.values, state.initialValues)
1001
- );
1002
- var useIsValid = (form) => react$1.useStore(form.store, (state) => {
1003
- for (const k in state.errors) {
1004
- const errs = state.errors[k];
1005
- if (errs !== void 0 && errs.length > 0) return false;
1006
- }
1007
- return true;
1008
- });
998
+ function useIsDirty(form, path) {
999
+ return react$1.useStore(
1000
+ form.store,
1001
+ (state) => path === void 0 || path === "" ? isFieldDirty(state.values, state.initialValues) : isFieldDirty(
1002
+ getAtPath(state.values, path),
1003
+ getAtPath(state.initialValues, path)
1004
+ )
1005
+ );
1006
+ }
1007
+ function useIsValid(form, path) {
1008
+ return react$1.useStore(form.store, (state) => {
1009
+ for (const k in state.errors) {
1010
+ const errs = state.errors[k];
1011
+ if (errs !== void 0 && errs.length > 0 && (path === void 0 || path === "" || isPathOrChild(k, path))) {
1012
+ return false;
1013
+ }
1014
+ }
1015
+ return true;
1016
+ });
1017
+ }
1009
1018
  var useIsSubmitting = (form) => react$1.useStore(form.store, (state) => state.isSubmitting);
1010
1019
  var useSubmitCount = (form) => react$1.useStore(form.store, (state) => state.submitCount);
1011
1020
  function useField(form, pathArg, options) {
@@ -1429,7 +1438,7 @@ var reconcileIds = (prev, nextItems) => {
1429
1438
  counter: prev.counter + Math.max(0, unmatched - leftover.length)
1430
1439
  };
1431
1440
  };
1432
- var useFieldArray = (form, pathArg) => {
1441
+ function useFieldArray(form, pathArg) {
1433
1442
  const path = react$1.useStore(
1434
1443
  form.store,
1435
1444
  (state) => typeof pathArg === "function" ? pathArg(state) : pathArg
@@ -1494,7 +1503,7 @@ var useFieldArray = (form, pathArg) => {
1494
1503
  }),
1495
1504
  [fields, items, error, push, remove, insert, move, swap]
1496
1505
  );
1497
- };
1506
+ }
1498
1507
 
1499
1508
  exports.CheckboxField = CheckboxField;
1500
1509
  exports.NumberField = NumberField;