attaform 0.18.1 → 0.19.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/README.md +3 -0
- package/dist/chunks/devtools.cjs +1 -1
- package/dist/chunks/devtools.mjs +1 -1
- package/dist/chunks/indexeddb.cjs +1 -1
- package/dist/chunks/indexeddb.mjs +1 -1
- package/dist/chunks/local-storage.cjs +1 -1
- package/dist/chunks/local-storage.mjs +1 -1
- package/dist/chunks/session-storage.cjs +1 -1
- package/dist/chunks/session-storage.mjs +1 -1
- package/dist/index.cjs +4 -4
- package/dist/index.d.cts +68 -75
- package/dist/index.d.mts +68 -75
- package/dist/index.d.ts +68 -75
- package/dist/index.mjs +5 -5
- package/dist/nuxt.d.cts +1 -1
- package/dist/nuxt.d.mts +1 -1
- package/dist/nuxt.d.ts +1 -1
- package/dist/runtime/plugins/attaform.cjs +2 -2
- package/dist/runtime/plugins/attaform.mjs +2 -2
- package/dist/shared/{attaform.DsC3rZHG.mjs → attaform.BTi-PsHr.mjs} +544 -134
- package/dist/shared/attaform.BTi-PsHr.mjs.map +1 -0
- package/dist/shared/{attaform.iTqxvl-P.d.mts → attaform.BTpuvGec.d.ts} +46 -13
- package/dist/shared/{attaform.BqK_L4gK.cjs → attaform.BqEfHpVB.cjs} +119 -1
- package/dist/shared/attaform.BqEfHpVB.cjs.map +1 -0
- package/dist/shared/{attaform.DK9aj0N8.d.ts → attaform.BtBmfLQN.d.mts} +46 -13
- package/dist/shared/{attaform.Dj9mwbaV.d.mts → attaform.C0uGZQ4M.d.cts} +365 -86
- package/dist/shared/{attaform.Dj9mwbaV.d.ts → attaform.C0uGZQ4M.d.mts} +365 -86
- package/dist/shared/{attaform.Dj9mwbaV.d.cts → attaform.C0uGZQ4M.d.ts} +365 -86
- package/dist/shared/{attaform.II89Pcf4.cjs → attaform.C1msmO2v.cjs} +544 -134
- package/dist/shared/attaform.C1msmO2v.cjs.map +1 -0
- package/dist/shared/{attaform.tts_OM7j.d.cts → attaform.CBjmobqk.d.cts} +1 -1
- package/dist/shared/{attaform.2b7M2mww.d.mts → attaform.CJ-e9gYI.d.ts} +1 -1
- package/dist/shared/{attaform.tsNFcEW7.d.ts → attaform.CRNA0vrd.d.mts} +1 -1
- package/dist/shared/{attaform.BDdFdjeX.mjs → attaform.Cghpuav8.mjs} +3 -3
- package/dist/shared/{attaform.BDdFdjeX.mjs.map → attaform.Cghpuav8.mjs.map} +1 -1
- package/dist/shared/{attaform.CtNUB9nf.mjs → attaform.CiMqJHDm.mjs} +3 -3
- package/dist/shared/{attaform.CtNUB9nf.mjs.map → attaform.CiMqJHDm.mjs.map} +1 -1
- package/dist/shared/{attaform.5UhpSVFI.cjs → attaform.CoxJ8Qm8.cjs} +2 -2
- package/dist/shared/{attaform.5UhpSVFI.cjs.map → attaform.CoxJ8Qm8.cjs.map} +1 -1
- package/dist/shared/{attaform.Xhg0AYNa.mjs → attaform.CrpjyXdO.mjs} +120 -2
- package/dist/shared/attaform.CrpjyXdO.mjs.map +1 -0
- package/dist/shared/{attaform.DF8wo-ry.d.ts → attaform.D4I63aBV.d.ts} +1 -1
- package/dist/shared/{attaform.DVLB6CAn.d.mts → attaform.DXYHL99q.d.mts} +1 -1
- package/dist/shared/{attaform.Dlk1jMuv.cjs → attaform.JBx8cfMA.cjs} +3 -3
- package/dist/shared/{attaform.Dlk1jMuv.cjs.map → attaform.JBx8cfMA.cjs.map} +1 -1
- package/dist/shared/{attaform.DUHru0OF.cjs → attaform.OznWyOPy.cjs} +3 -3
- package/dist/shared/{attaform.DUHru0OF.cjs.map → attaform.OznWyOPy.cjs.map} +1 -1
- package/dist/shared/{attaform.M33WKVV4.d.cts → attaform.QvygsFGh.d.cts} +1 -1
- package/dist/shared/{attaform.Xt0A3QUd.mjs → attaform.a3uBo-gw.mjs} +3 -3
- package/dist/shared/{attaform.Xt0A3QUd.mjs.map → attaform.a3uBo-gw.mjs.map} +1 -1
- package/dist/shared/{attaform.DoSuaKMd.d.cts → attaform.ePUcKxId.d.cts} +46 -13
- package/dist/zod-v3.cjs +3 -3
- package/dist/zod-v3.d.cts +4 -4
- package/dist/zod-v3.d.mts +4 -4
- package/dist/zod-v3.d.ts +4 -4
- package/dist/zod-v3.mjs +3 -3
- package/dist/zod-v4.cjs +3 -3
- package/dist/zod-v4.d.cts +4 -4
- package/dist/zod-v4.d.mts +4 -4
- package/dist/zod-v4.d.ts +4 -4
- package/dist/zod-v4.mjs +3 -3
- package/dist/zod.cjs +4 -4
- package/dist/zod.d.cts +6 -6
- package/dist/zod.d.mts +6 -6
- package/dist/zod.d.ts +6 -6
- package/dist/zod.mjs +5 -5
- package/package.json +5 -5
- package/dist/shared/attaform.BqK_L4gK.cjs.map +0 -1
- package/dist/shared/attaform.DsC3rZHG.mjs.map +0 -1
- package/dist/shared/attaform.II89Pcf4.cjs.map +0 -1
- package/dist/shared/attaform.Xhg0AYNa.mjs.map +0 -1
|
@@ -319,6 +319,26 @@ type ArrayPath<Form, P extends FlatPath<Form> = FlatPath<Form>> = P extends stri
|
|
|
319
319
|
* constrain `Path extends ArrayPath<Form>` so this is always well-defined.
|
|
320
320
|
*/
|
|
321
321
|
type ArrayItem<Form, Path extends ArrayPath<Form>> = NestedType<Form, Path> extends ReadonlyArray<infer Item> ? Item : never;
|
|
322
|
+
/**
|
|
323
|
+
* Companion to `ArrayPath`: filter `FlatPath<Form>` down to the subset
|
|
324
|
+
* of paths whose resolved leaf is a record (an object with an open
|
|
325
|
+
* string-keyed index signature, e.g. `z.record(z.string(), V)`). A
|
|
326
|
+
* fixed-shape object (`z.object({ ... })`) is excluded — its keys are
|
|
327
|
+
* statically known, so it has no `string` index signature.
|
|
328
|
+
*
|
|
329
|
+
* `string extends keyof T` is the index-signature probe: it holds for
|
|
330
|
+
* `Record<string, V>` (`keyof` is `string`) and fails for a fixed object
|
|
331
|
+
* (`keyof` is the literal key union). The leading array guard keeps
|
|
332
|
+
* arrays (which also satisfy the object check) out of the record set.
|
|
333
|
+
*/
|
|
334
|
+
type RecordPath<Form, P extends FlatPath<Form> = FlatPath<Form>> = P extends string ? NestedType<Form, P> extends readonly unknown[] ? never : NestedType<Form, P> extends Record<string, unknown> ? string extends keyof NestedType<Form, P> ? P : never : never : never;
|
|
335
|
+
/**
|
|
336
|
+
* Value type of the record addressed by `Path` — the `V` in a
|
|
337
|
+
* `Record<string, V>`. Callers constrain `Path extends RecordPath<Form>`,
|
|
338
|
+
* so the leaf is always an open string-keyed record and this is
|
|
339
|
+
* well-defined.
|
|
340
|
+
*/
|
|
341
|
+
type RecordValue<Form, Path extends RecordPath<Form>> = NestedType<Form, Path> extends Record<string, infer Value> ? Value : never;
|
|
322
342
|
/**
|
|
323
343
|
* Widens primitive-literal leaves to their primitive supertype to
|
|
324
344
|
* match the runtime "slim-primitive write contract."
|
|
@@ -1308,20 +1328,24 @@ type WriteMeta = {
|
|
|
1308
1328
|
*/
|
|
1309
1329
|
readonly skipDiscriminatorReshape?: boolean;
|
|
1310
1330
|
/**
|
|
1311
|
-
*
|
|
1312
|
-
*
|
|
1313
|
-
*
|
|
1314
|
-
*
|
|
1315
|
-
*
|
|
1316
|
-
*
|
|
1331
|
+
* Records an array structural mutation precisely enough to replay the
|
|
1332
|
+
* exact index permutation it produced, set by `field-arrays.ts`
|
|
1333
|
+
* helpers. `setValueAtPath` uses it to surgically clear variant memory
|
|
1334
|
+
* for the indices the operation invalidated. Without this hint, a raw
|
|
1335
|
+
* whole-array `setValue(arrayPath, [...])` clears all memory under the
|
|
1336
|
+
* array (the runtime can't tell which indices stayed put). Internal —
|
|
1337
|
+
* don't set from consumer code.
|
|
1317
1338
|
*/
|
|
1318
1339
|
readonly arrayOp?: {
|
|
1319
|
-
readonly kind: '
|
|
1340
|
+
readonly kind: 'insert';
|
|
1341
|
+
readonly index: number;
|
|
1342
|
+
} | {
|
|
1343
|
+
readonly kind: 'remove';
|
|
1320
1344
|
readonly index: number;
|
|
1321
1345
|
} | {
|
|
1322
|
-
readonly kind: '
|
|
1323
|
-
readonly
|
|
1324
|
-
readonly
|
|
1346
|
+
readonly kind: 'move';
|
|
1347
|
+
readonly from: number;
|
|
1348
|
+
readonly to: number;
|
|
1325
1349
|
} | {
|
|
1326
1350
|
readonly kind: 'swap';
|
|
1327
1351
|
readonly a: number;
|
|
@@ -1736,16 +1760,14 @@ type UseFormConfiguration<Form extends GenericForm, GetValueFormType, Schema ext
|
|
|
1736
1760
|
*/
|
|
1737
1761
|
coerce?: boolean | CoercionRegistry;
|
|
1738
1762
|
/**
|
|
1739
|
-
* Per-form override of the `
|
|
1740
|
-
* `field.
|
|
1741
|
-
* `AttaformDefaults.
|
|
1742
|
-
* (`
|
|
1743
|
-
* for the resolution rules and
|
|
1744
|
-
*
|
|
1745
|
-
* Boolean shorthand: `true` → always show *when errors exist*;
|
|
1746
|
-
* `false` → never show.
|
|
1763
|
+
* Per-form override of the `getDisplayState` heuristic that drives
|
|
1764
|
+
* `field.displayState` and the `show*` booleans (and their `form.meta`
|
|
1765
|
+
* rollups). Falls back to `AttaformDefaults.getDisplayState`, then to
|
|
1766
|
+
* the library default (`defaultDisplayState`). See
|
|
1767
|
+
* `AttaformDefaults.getDisplayState` for the resolution rules and
|
|
1768
|
+
* predicate signature.
|
|
1747
1769
|
*/
|
|
1748
|
-
|
|
1770
|
+
getDisplayState?: GetDisplayState;
|
|
1749
1771
|
/**
|
|
1750
1772
|
* Recursion ceiling for schema walks that descend through recursive
|
|
1751
1773
|
* schemas (Zod's `z.lazy(...)` today). Default `64`. Per-form value
|
|
@@ -1818,6 +1840,20 @@ type UseFormConfiguration<Form extends GenericForm, GetValueFormType, Schema ext
|
|
|
1818
1840
|
* channel would be solo by construction.
|
|
1819
1841
|
*/
|
|
1820
1842
|
multiTab?: boolean;
|
|
1843
|
+
/**
|
|
1844
|
+
* Whether `v-register` automatically manages aria attributes
|
|
1845
|
+
* (`aria-invalid`, `aria-busy`, `aria-required`, `aria-describedby`)
|
|
1846
|
+
* from the field's display state. **Defaults to `true`.**
|
|
1847
|
+
*
|
|
1848
|
+
* **Resolution order (per-register override > per-form > global > library):**
|
|
1849
|
+
*
|
|
1850
|
+
* register(path, { autoAria }) > useForm({ autoAria }) > AttaformDefaults.autoAria > library default (`true`)
|
|
1851
|
+
*
|
|
1852
|
+
* Set `false` to leave all aria wiring to your own markup form-wide.
|
|
1853
|
+
* Any aria attribute you author yourself is always left untouched,
|
|
1854
|
+
* independent of this flag.
|
|
1855
|
+
*/
|
|
1856
|
+
autoAria?: boolean;
|
|
1821
1857
|
/**
|
|
1822
1858
|
* @internal
|
|
1823
1859
|
* SSR prefetch mark — set by the `attaform/vite` compile-time
|
|
@@ -1899,33 +1935,41 @@ type AttaformDefaults = {
|
|
|
1899
1935
|
*/
|
|
1900
1936
|
coerce?: boolean | CoercionRegistry;
|
|
1901
1937
|
/**
|
|
1902
|
-
* Default for `useForm({
|
|
1903
|
-
* that
|
|
1904
|
-
*
|
|
1938
|
+
* Default for `useForm({ getDisplayState })`. The centralised
|
|
1939
|
+
* heuristic that resolves every path's `field.displayState` — and thus
|
|
1940
|
+
* the `show*` booleans and their `form.meta` rollups — to one of
|
|
1941
|
+
* `'idle' | 'pending' | 'error' | 'success'`.
|
|
1905
1942
|
*
|
|
1906
1943
|
* Resolution order (per-form wins):
|
|
1907
1944
|
*
|
|
1908
|
-
* useForm({
|
|
1945
|
+
* useForm({ getDisplayState }) > AttaformDefaults > library default
|
|
1909
1946
|
*
|
|
1910
|
-
* The library default
|
|
1911
|
-
*
|
|
1947
|
+
* The library default opens one timing gate, then resolves by
|
|
1948
|
+
* precedence: gate closed → `'idle'`; a run in flight → `'pending'`;
|
|
1949
|
+
* an own-path error → `'error'`; otherwise `valid` → `'success'`, else
|
|
1950
|
+
* `'idle'`. The gate opens after the first submit attempt OR once the
|
|
1951
|
+
* field is touched and not currently focused:
|
|
1912
1952
|
*
|
|
1913
1953
|
* ```ts
|
|
1914
|
-
* (field, formMeta) =>
|
|
1915
|
-
*
|
|
1954
|
+
* (field, formMeta) => {
|
|
1955
|
+
* const gateOpen =
|
|
1956
|
+
* formMeta.submissionAttempts > 0 ||
|
|
1957
|
+
* (field.touched === true && field.focused !== true)
|
|
1958
|
+
* if (!gateOpen) return 'idle'
|
|
1959
|
+
* if (field.validating === true) return 'pending'
|
|
1960
|
+
* // ...own-path error → 'error'; valid → 'success'; else 'idle'
|
|
1961
|
+
* }
|
|
1916
1962
|
* ```
|
|
1917
1963
|
*
|
|
1918
|
-
* Compose with the library default via the public
|
|
1919
|
-
*
|
|
1920
|
-
*
|
|
1921
|
-
* predicate is invoked only when `errors.length > 0`, so authors
|
|
1922
|
-
* don't re-check inside.
|
|
1964
|
+
* Compose with the library default via the public `defaultDisplayState`
|
|
1965
|
+
* export. The predicate runs on every field-state read, so it owns the
|
|
1966
|
+
* idle / pending / error / success decision outright.
|
|
1923
1967
|
*
|
|
1924
|
-
* The predicate's args are `Omit`'d of `
|
|
1925
|
-
*
|
|
1926
|
-
*
|
|
1968
|
+
* The predicate's args are `Omit`'d of the derived `displayState` /
|
|
1969
|
+
* `show*` / `firstError` keys (see `FieldStateDerivedKey`) to prevent
|
|
1970
|
+
* a self-referential predicate.
|
|
1927
1971
|
*/
|
|
1928
|
-
|
|
1972
|
+
getDisplayState?: GetDisplayState;
|
|
1929
1973
|
/**
|
|
1930
1974
|
* Default for `useForm({ maxRecursionDepth })`. Recursion ceiling
|
|
1931
1975
|
* for schema walks that descend through recursive schemas (Zod's
|
|
@@ -2025,6 +2069,21 @@ type AttaformDefaults = {
|
|
|
2025
2069
|
* the multi-tab-sync recipe's Security section for the threat model.
|
|
2026
2070
|
*/
|
|
2027
2071
|
multiTab?: boolean;
|
|
2072
|
+
/**
|
|
2073
|
+
* App-wide default for `useForm({ autoAria })`. Library default is
|
|
2074
|
+
* `true`: `v-register` keeps `aria-invalid` / `aria-busy` /
|
|
2075
|
+
* `aria-required` / `aria-describedby` in sync with each field's
|
|
2076
|
+
* display state out of the box.
|
|
2077
|
+
*
|
|
2078
|
+
* **Resolution order (per-form wins):**
|
|
2079
|
+
*
|
|
2080
|
+
* useForm({ autoAria }) > AttaformDefaults.autoAria > library default (`true`)
|
|
2081
|
+
*
|
|
2082
|
+
* Set `false` once at the plugin level to make every form manage its
|
|
2083
|
+
* own aria markup. Authored aria attributes are always preserved
|
|
2084
|
+
* regardless of this setting.
|
|
2085
|
+
*/
|
|
2086
|
+
autoAria?: boolean;
|
|
2028
2087
|
};
|
|
2029
2088
|
/**
|
|
2030
2089
|
* Callback invoked by `handleSubmit` after the form parses successfully.
|
|
@@ -2040,45 +2099,59 @@ type OnSubmit<Form extends GenericForm> = (form: Form) => void | Promise<void>;
|
|
|
2040
2099
|
*/
|
|
2041
2100
|
type OnError = (error: ValidationError[]) => void | Promise<void>;
|
|
2042
2101
|
/**
|
|
2043
|
-
*
|
|
2044
|
-
*
|
|
2045
|
-
*
|
|
2046
|
-
*
|
|
2047
|
-
*
|
|
2048
|
-
*
|
|
2049
|
-
*
|
|
2050
|
-
*
|
|
2051
|
-
*
|
|
2052
|
-
*
|
|
2053
|
-
*
|
|
2054
|
-
*
|
|
2055
|
-
*
|
|
2056
|
-
*
|
|
2057
|
-
*
|
|
2102
|
+
* The display-state verdict at a path: the single signal a UI needs to
|
|
2103
|
+
* decide what (if anything) to surface about validation right now.
|
|
2104
|
+
* Rolled up at containers and at the form root (`form.meta.displayState`).
|
|
2105
|
+
*
|
|
2106
|
+
* - `'idle'` — nothing to surface. Either pre-interaction (the timing
|
|
2107
|
+
* gate hasn't opened) or gate-open with no verdict worth showing.
|
|
2108
|
+
* - `'pending'` — a validation run is in flight at this path; the prior
|
|
2109
|
+
* verdict is stale. Drive a spinner / "Checking…" affordance.
|
|
2110
|
+
* - `'error'` — a blocking error the timing gate has cleared for display.
|
|
2111
|
+
* - `'success'` — validation passed and the gate has cleared a positive
|
|
2112
|
+
* confirmation (the green-check pattern).
|
|
2113
|
+
*
|
|
2114
|
+
* The four `show*` booleans on `FieldState` are sugar over this enum
|
|
2115
|
+
* (`showErrors === (displayState === 'error')`, and so on), so they can
|
|
2116
|
+
* never contradict it.
|
|
2117
|
+
*/
|
|
2118
|
+
type DisplayState = 'idle' | 'pending' | 'error' | 'success';
|
|
2119
|
+
/**
|
|
2120
|
+
* Keys on `FieldState` layered on FROM the display-state predicate
|
|
2121
|
+
* (plus `firstError`, computed alongside them). `Omit`'d from the
|
|
2122
|
+
* predicate's arguments so a predicate cannot read its own output and
|
|
2123
|
+
* form a cycle — enforced at the type level AND at runtime: the base
|
|
2124
|
+
* objects passed in literally lack these keys, so an `as` cast in TS
|
|
2125
|
+
* or a vanilla-JS caller still can't reach them. `FieldStateBase` /
|
|
2126
|
+
* `FormMetaBase` (field-state-api.ts) omit the same set in lockstep.
|
|
2127
|
+
*/
|
|
2128
|
+
type FieldStateDerivedKey = 'displayState' | 'showErrors' | 'showPending' | 'showSuccess' | 'showIdle' | 'firstError';
|
|
2129
|
+
/**
|
|
2130
|
+
* Predicate that resolves a path's `displayState`. Receives the field's
|
|
2131
|
+
* reactive state plus the form's reactive meta (both minus the derived
|
|
2132
|
+
* `displayState` / `show*` / `firstError` keys — see `FieldStateDerivedKey`)
|
|
2133
|
+
* and returns the single enum verdict; the `show*` booleans derive from
|
|
2134
|
+
* the result. Runs unconditionally on every field-state read, so the
|
|
2135
|
+
* idle / pending / error / success decision lives in exactly one place
|
|
2136
|
+
* and the whole app's validation-display behavior flows from it.
|
|
2137
|
+
*
|
|
2138
|
+
* The library default — `defaultDisplayState` — is publicly exported so
|
|
2139
|
+
* a layered predicate can compose with it:
|
|
2058
2140
|
*
|
|
2059
2141
|
* ```ts
|
|
2060
|
-
* import {
|
|
2142
|
+
* import { defaultDisplayState } from 'attaform'
|
|
2061
2143
|
*
|
|
2062
2144
|
* useForm({
|
|
2063
2145
|
* schema,
|
|
2064
|
-
*
|
|
2065
|
-
*
|
|
2146
|
+
* // Defer to the default everywhere, but never show a success check on `username`.
|
|
2147
|
+
* getDisplayState: (field, formMeta) => {
|
|
2148
|
+
* const state = defaultDisplayState(field, formMeta)
|
|
2149
|
+
* return field.path[0] === 'username' && state === 'success' ? 'idle' : state
|
|
2150
|
+
* },
|
|
2066
2151
|
* })
|
|
2067
2152
|
* ```
|
|
2068
2153
|
*/
|
|
2069
|
-
type
|
|
2070
|
-
/**
|
|
2071
|
-
* Configuration shape for `shouldShowErrors`. A predicate function or
|
|
2072
|
-
* a boolean shorthand:
|
|
2073
|
-
*
|
|
2074
|
-
* - `true` — always show errors (when any exist).
|
|
2075
|
-
* - `false` — never show errors.
|
|
2076
|
-
* - function — custom predicate, see `ShouldShowErrors`.
|
|
2077
|
-
*
|
|
2078
|
-
* Resolved through three tiers (per-form > plugin defaults > library
|
|
2079
|
-
* default).
|
|
2080
|
-
*/
|
|
2081
|
-
type ShouldShowErrorsConfig = ShouldShowErrors | boolean;
|
|
2154
|
+
type GetDisplayState = (field: Omit<FieldState, FieldStateDerivedKey>, formMeta: Omit<FormMeta, FieldStateDerivedKey>) => DisplayState;
|
|
2082
2155
|
/**
|
|
2083
2156
|
* Submit handler returned by `handleSubmit(onSubmit, onError)`. Bind
|
|
2084
2157
|
* it to a `<form>`:
|
|
@@ -2330,6 +2403,22 @@ type RegisterOptions = {
|
|
|
2330
2403
|
* instead — see the "Custom assigners" section in the API docs.
|
|
2331
2404
|
*/
|
|
2332
2405
|
transforms?: ReadonlyArray<RegisterTransform>;
|
|
2406
|
+
/**
|
|
2407
|
+
* Per-binding override for automatic aria management, the narrowest
|
|
2408
|
+
* tier of the `autoAria` cascade. By default the directive keeps
|
|
2409
|
+
* `aria-invalid` / `aria-busy` / `aria-required` / `aria-describedby`
|
|
2410
|
+
* in sync with the field's display state. Pass `autoAria: false` to
|
|
2411
|
+
* leave every aria attribute on this element to you (the directive
|
|
2412
|
+
* still manages value binding and registration), or `autoAria: true`
|
|
2413
|
+
* to re-enable management on one binding even when the form set
|
|
2414
|
+
* `useForm({ autoAria: false })`.
|
|
2415
|
+
*
|
|
2416
|
+
* Overrides `useForm({ autoAria })` and
|
|
2417
|
+
* `createAttaform({ defaults: { autoAria } })`. Writing an aria
|
|
2418
|
+
* attribute yourself also locks the directive out of that one
|
|
2419
|
+
* attribute, regardless of this flag.
|
|
2420
|
+
*/
|
|
2421
|
+
autoAria?: boolean;
|
|
2333
2422
|
};
|
|
2334
2423
|
/**
|
|
2335
2424
|
* The object returned by `form.register(path)`. Pass it to a native
|
|
@@ -2530,6 +2619,16 @@ type RegisterValue<Value = unknown> = Readonly<{
|
|
|
2530
2619
|
* @internal
|
|
2531
2620
|
*/
|
|
2532
2621
|
markBlank: () => boolean;
|
|
2622
|
+
/**
|
|
2623
|
+
* Flip this field's sticky `interacted` flag — the signal that the
|
|
2624
|
+
* user has issued at least one value edit here (an insert or a
|
|
2625
|
+
* delete). Called by the directive's input / change listeners on
|
|
2626
|
+
* every genuine user input; never by hydration or programmatic
|
|
2627
|
+
* writes. Idempotent (the store skips the write once set). Don't
|
|
2628
|
+
* call from consumer code.
|
|
2629
|
+
* @internal
|
|
2630
|
+
*/
|
|
2631
|
+
markInteracted: () => void;
|
|
2533
2632
|
/**
|
|
2534
2633
|
* `true` when the schema's slim primitive set at this path includes
|
|
2535
2634
|
* `'undefined'` — i.e. the leaf was declared `.optional()` (or as
|
|
@@ -2561,6 +2660,42 @@ type RegisterValue<Value = unknown> = Readonly<{
|
|
|
2561
2660
|
* @internal
|
|
2562
2661
|
*/
|
|
2563
2662
|
acceptsString: boolean;
|
|
2663
|
+
/**
|
|
2664
|
+
* The field's aria satellite ids, mirroring `FieldState.aria`. The
|
|
2665
|
+
* directive points `aria-describedby` at `errorId` while the field
|
|
2666
|
+
* is in its error state. Optional so hand-rolled `RegisterValue`
|
|
2667
|
+
* mocks don't have to declare it; the directive skips aria wiring
|
|
2668
|
+
* when absent.
|
|
2669
|
+
* @internal
|
|
2670
|
+
*/
|
|
2671
|
+
aria?: {
|
|
2672
|
+
readonly errorId: string;
|
|
2673
|
+
readonly descriptionId: string;
|
|
2674
|
+
};
|
|
2675
|
+
/**
|
|
2676
|
+
* Whether the schema marks this path required, from
|
|
2677
|
+
* `schema.isRequiredAtPath(segments)`. Drives `aria-required`.
|
|
2678
|
+
* Optional for the same mock-tolerance reason as `aria`.
|
|
2679
|
+
* @internal
|
|
2680
|
+
*/
|
|
2681
|
+
isRequired?: boolean;
|
|
2682
|
+
/**
|
|
2683
|
+
* Whether the directive should auto-manage aria attributes for this
|
|
2684
|
+
* binding. Resolves the per-register `autoAria` override against the
|
|
2685
|
+
* form-level value: `options.autoAria ?? formAutoAria`. The directive
|
|
2686
|
+
* treats an absent value as off.
|
|
2687
|
+
* @internal
|
|
2688
|
+
*/
|
|
2689
|
+
ariaEnabled?: boolean;
|
|
2690
|
+
/**
|
|
2691
|
+
* The gated display-state verdict for this path, reusing the same
|
|
2692
|
+
* field-state identity as `form.fields`. The directive watches it to
|
|
2693
|
+
* keep `aria-invalid` / `aria-busy` / `aria-describedby` in lockstep
|
|
2694
|
+
* with the visible error state, even on async ticks with no parent
|
|
2695
|
+
* re-render. Optional; the directive skips aria wiring when absent.
|
|
2696
|
+
* @internal
|
|
2697
|
+
*/
|
|
2698
|
+
ariaDisplayState?: Readonly<Ref<DisplayState>>;
|
|
2564
2699
|
}>;
|
|
2565
2700
|
/**
|
|
2566
2701
|
* Custom assigner installed on an element via the directive's
|
|
@@ -2876,6 +3011,32 @@ type FieldState<Value = unknown> = {
|
|
|
2876
3011
|
readonly focused: boolean | null;
|
|
2877
3012
|
readonly blurred: boolean | null;
|
|
2878
3013
|
readonly touched: boolean;
|
|
3014
|
+
/**
|
|
3015
|
+
* `true` once the user has issued at least one value edit on this
|
|
3016
|
+
* field through `v-register` (an insert or a delete), sticky
|
|
3017
|
+
* thereafter and preserved across disconnects. Distinct from
|
|
3018
|
+
* `dirty`: typing `"a"` then deleting it back to empty leaves the
|
|
3019
|
+
* field net-unchanged (`dirty: false`) yet `interacted: true`.
|
|
3020
|
+
* Distinct from `touched`: tabbing through a field without editing
|
|
3021
|
+
* flips `touched` but never `interacted`. Set only by user input,
|
|
3022
|
+
* never by hydration or programmatic `setValue`; cleared by
|
|
3023
|
+
* `form.reset()` / `form.resetField(path)`. Containers roll it up as
|
|
3024
|
+
* a disjunction (any descendant interacted).
|
|
3025
|
+
*/
|
|
3026
|
+
readonly interacted: boolean;
|
|
3027
|
+
/**
|
|
3028
|
+
* `true` once the user has blurred this field after editing it: the
|
|
3029
|
+
* first time they edit a value and then leave. Sticky thereafter and
|
|
3030
|
+
* preserved across disconnects; a tab-through with no edit never sets
|
|
3031
|
+
* it (`interacted` is still false at that blur). It composes
|
|
3032
|
+
* `interacted` with the departure and drives the default display gate,
|
|
3033
|
+
* so errors reveal once the user finishes a pass and leaves, then stay
|
|
3034
|
+
* visible through a re-focus to be fixed live. Set only by user
|
|
3035
|
+
* input/blur, never by hydration or programmatic writes; cleared by
|
|
3036
|
+
* `form.reset()` / `form.resetField(path)`. Containers roll it up as a
|
|
3037
|
+
* disjunction.
|
|
3038
|
+
*/
|
|
3039
|
+
readonly blurredAfterInteraction: boolean;
|
|
2879
3040
|
readonly connected: boolean;
|
|
2880
3041
|
/**
|
|
2881
3042
|
* The first DOM element bound to this path via `v-register`, or
|
|
@@ -2932,9 +3093,29 @@ type FieldState<Value = unknown> = {
|
|
|
2932
3093
|
*/
|
|
2933
3094
|
readonly valid: boolean;
|
|
2934
3095
|
/**
|
|
2935
|
-
*
|
|
2936
|
-
*
|
|
2937
|
-
*
|
|
3096
|
+
* The single display-state verdict at this path: `'idle'`,
|
|
3097
|
+
* `'pending'`, `'error'`, or `'success'`. The source of truth the
|
|
3098
|
+
* four `show*` booleans below derive from. Bind it directly when one
|
|
3099
|
+
* branch over the set reads cleaner than four flags:
|
|
3100
|
+
*
|
|
3101
|
+
* ```vue
|
|
3102
|
+
* <FieldStatusIcon :state="form.fields.email.displayState" />
|
|
3103
|
+
* ```
|
|
3104
|
+
*
|
|
3105
|
+
* Resolved by the `getDisplayState` heuristic:
|
|
3106
|
+
* `useForm({ getDisplayState })` →
|
|
3107
|
+
* `createAttaform({ defaults: { getDisplayState } })` → library
|
|
3108
|
+
* default (`defaultDisplayState`). Override per form, app-wide, or
|
|
3109
|
+
* compose with `defaultDisplayState` for a layered predicate.
|
|
3110
|
+
*
|
|
3111
|
+
* Available on container paths too: `form.fields.users[0].displayState`
|
|
3112
|
+
* rolls up over the row's descendants.
|
|
3113
|
+
*/
|
|
3114
|
+
readonly displayState: DisplayState;
|
|
3115
|
+
/**
|
|
3116
|
+
* `displayState === 'error'`. The centralised "render this field's
|
|
3117
|
+
* errors right now?" gate, so templates avoid re-spelling the
|
|
3118
|
+
* heuristic at every error site:
|
|
2938
3119
|
*
|
|
2939
3120
|
* ```vue
|
|
2940
3121
|
* <span v-if="form.fields.email.showErrors">
|
|
@@ -2942,20 +3123,29 @@ type FieldState<Value = unknown> = {
|
|
|
2942
3123
|
* </span>
|
|
2943
3124
|
* ```
|
|
2944
3125
|
*
|
|
2945
|
-
*
|
|
2946
|
-
*
|
|
2947
|
-
*
|
|
2948
|
-
* after touched-and-dirty). Override per form, app-wide, or
|
|
2949
|
-
* compose with `defaultShouldShowErrors` for a layered predicate.
|
|
2950
|
-
*
|
|
2951
|
-
* Falls back to `false` whenever there are no errors — the gate
|
|
2952
|
-
* skips the predicate entirely in that case.
|
|
2953
|
-
*
|
|
2954
|
-
* Available on container paths too: `form.fields.users[0].showErrors`
|
|
2955
|
-
* aggregates over the row's descendants (any descendant with a
|
|
2956
|
-
* qualifying error flips the container on).
|
|
3126
|
+
* Kept plural to match `errors` / `firstError`. On container paths it
|
|
3127
|
+
* rolls up over descendants (any descendant resolving to `'error'`
|
|
3128
|
+
* flips the container on).
|
|
2957
3129
|
*/
|
|
2958
3130
|
readonly showErrors: boolean;
|
|
3131
|
+
/**
|
|
3132
|
+
* `displayState === 'pending'`. A per-field validation run is in
|
|
3133
|
+
* flight at this path and the prior verdict is stale; drive a spinner
|
|
3134
|
+
* or a "Checking…" affordance.
|
|
3135
|
+
*/
|
|
3136
|
+
readonly showPending: boolean;
|
|
3137
|
+
/**
|
|
3138
|
+
* `displayState === 'success'`. Validation has passed and the timing
|
|
3139
|
+
* gate has cleared a positive confirmation; drive the green-check
|
|
3140
|
+
* pattern.
|
|
3141
|
+
*/
|
|
3142
|
+
readonly showSuccess: boolean;
|
|
3143
|
+
/**
|
|
3144
|
+
* `displayState === 'idle'`. Nothing to surface yet — pre-interaction,
|
|
3145
|
+
* or gate-open with no verdict worth showing. Read it to suppress
|
|
3146
|
+
* helper text the moment any other signal takes over.
|
|
3147
|
+
*/
|
|
3148
|
+
readonly showIdle: boolean;
|
|
2959
3149
|
/**
|
|
2960
3150
|
* The first `ValidationError` at this path in the deterministic
|
|
2961
3151
|
* schema-declaration order — equivalent to `errors[0]`, exposed as
|
|
@@ -2977,6 +3167,53 @@ type FieldState<Value = unknown> = {
|
|
|
2977
3167
|
*/
|
|
2978
3168
|
readonly firstError: ValidationError | undefined;
|
|
2979
3169
|
readonly path: ReadonlyArray<string | number>;
|
|
3170
|
+
/**
|
|
3171
|
+
* Stable, SSR-safe DOM id for this field, unique across every mount
|
|
3172
|
+
* on the page. Derived from the form's key and this path, folded with
|
|
3173
|
+
* the form's per-mount `instanceId` so two simultaneous mounts of the
|
|
3174
|
+
* same keyed form never collide. Bind it to wire a label and its
|
|
3175
|
+
* input without inventing your own id:
|
|
3176
|
+
*
|
|
3177
|
+
* ```vue
|
|
3178
|
+
* <label :for="form.fields.email.id">Email</label>
|
|
3179
|
+
* <input :id="form.fields.email.id" v-register="form.register('email')" />
|
|
3180
|
+
* ```
|
|
3181
|
+
*
|
|
3182
|
+
* Treat as identity, not state: stable for the path across the form's
|
|
3183
|
+
* lifetime, opaque, not meant to be parsed.
|
|
3184
|
+
*/
|
|
3185
|
+
readonly id: string;
|
|
3186
|
+
/**
|
|
3187
|
+
* Satellite ids derived from {@link id} for the elements that
|
|
3188
|
+
* describe this field. Wire them to an error node and a description
|
|
3189
|
+
* node so assistive tech announces them with the input. The
|
|
3190
|
+
* `v-register` directive points `aria-describedby` at `errorId`
|
|
3191
|
+
* automatically while the field is in its error state; you render the
|
|
3192
|
+
* matching element and id it:
|
|
3193
|
+
*
|
|
3194
|
+
* ```vue
|
|
3195
|
+
* <input v-register="form.register('email')" />
|
|
3196
|
+
* <span :id="form.fields.email.aria.errorId" v-if="form.fields.email.showErrors">
|
|
3197
|
+
* {{ form.fields.email.firstError?.message }}
|
|
3198
|
+
* </span>
|
|
3199
|
+
* ```
|
|
3200
|
+
*
|
|
3201
|
+
* `descriptionId` is for opt-in help text; chain it into your own
|
|
3202
|
+
* `aria-describedby` when you render a persistent description element.
|
|
3203
|
+
*/
|
|
3204
|
+
readonly aria: {
|
|
3205
|
+
readonly errorId: string;
|
|
3206
|
+
readonly descriptionId: string;
|
|
3207
|
+
};
|
|
3208
|
+
/**
|
|
3209
|
+
* Stable identity for this field as an element of its parent array,
|
|
3210
|
+
* suitable as a Vue `:key` when iterating array elements. An allocated
|
|
3211
|
+
* token (not derived from the element's value) that follows the
|
|
3212
|
+
* element across inserts, removals, moves, and swaps, so a row keeps
|
|
3213
|
+
* its component instance across a reorder. Empty for fields that are
|
|
3214
|
+
* not array elements. Treat as opaque identity, not state.
|
|
3215
|
+
*/
|
|
3216
|
+
readonly key: string;
|
|
2980
3217
|
readonly blank: boolean;
|
|
2981
3218
|
/**
|
|
2982
3219
|
* Presentational label for this field. Resolves through the
|
|
@@ -3422,8 +3659,8 @@ type FormMeta<F = unknown> = FieldState<F> & {
|
|
|
3422
3659
|
*
|
|
3423
3660
|
* Pure introspection counter — useful for "this form has been
|
|
3424
3661
|
* visited and left" UX (analytics, prior-step badges, layered
|
|
3425
|
-
* `
|
|
3426
|
-
* default `
|
|
3662
|
+
* `getDisplayState` predicates) but does NOT drive the library's
|
|
3663
|
+
* default `getDisplayState` heuristic. The reveal-on-submit story
|
|
3427
3664
|
* runs entirely through `submissionAttempts`, which
|
|
3428
3665
|
* `wizard.handleSubmit` bumps on the active form at intermediate
|
|
3429
3666
|
* steps and on every form at the final step.
|
|
@@ -4217,6 +4454,48 @@ type UseFormReturnType<Form extends GenericForm, GetValueFormType extends Generi
|
|
|
4217
4454
|
move: <Path extends ArrayPath<Form>>(path: Path, from: number, to: number) => void;
|
|
4218
4455
|
/** Replace the element at `index` with `value`. No-op when out of range. */
|
|
4219
4456
|
replace: <Path extends ArrayPath<Form>>(path: Path, index: number, value: ArrayItem<Form, Path>) => void;
|
|
4457
|
+
/**
|
|
4458
|
+
* Read-only, reactive view of the array at `path` as one `FieldState`
|
|
4459
|
+
* per element, in index order. Each entry carries its element `key`,
|
|
4460
|
+
* an allocated identity token, so a `v-for` keyed by it keeps a row's
|
|
4461
|
+
* component instance across an insert, removal, move, or swap:
|
|
4462
|
+
*
|
|
4463
|
+
* ```vue
|
|
4464
|
+
* <div v-for="(row, i) in form.list('contacts')" :key="row.key">
|
|
4465
|
+
* <input v-register="form.register(`contacts.${i}.name`)" />
|
|
4466
|
+
* <p v-if="row.showErrors">{{ row.firstError?.message }}</p>
|
|
4467
|
+
* </div>
|
|
4468
|
+
* ```
|
|
4469
|
+
*
|
|
4470
|
+
* Entries are the same field states `form.fields` exposes, so reads
|
|
4471
|
+
* stay live. `form.fields(path)` remains the single aggregated
|
|
4472
|
+
* container for the whole array; `list` is the per-element view.
|
|
4473
|
+
* For a record, reach for `record`, which keys each entry by its own
|
|
4474
|
+
* key.
|
|
4475
|
+
*/
|
|
4476
|
+
list: <Path extends ArrayPath<Form>>(path: Path) => readonly FieldState<ArrayItem<Form, Path>>[];
|
|
4477
|
+
/**
|
|
4478
|
+
* Read-only, reactive view of the record at `path` as one `FieldState`
|
|
4479
|
+
* per entry, keyed by the entry's own key. Where `list` hands back an
|
|
4480
|
+
* ordered array for an array path, `record` hands back a keyed object
|
|
4481
|
+
* for a record path, so you iterate it by key:
|
|
4482
|
+
*
|
|
4483
|
+
* ```vue
|
|
4484
|
+
* <div v-for="(field, key) in form.record('scoresByTeam')" :key="key">
|
|
4485
|
+
* <label>{{ key }}</label>
|
|
4486
|
+
* <input v-register="form.register(`scoresByTeam.${key}`)" />
|
|
4487
|
+
* <p v-if="field.showErrors">{{ field.firstError?.message }}</p>
|
|
4488
|
+
* </div>
|
|
4489
|
+
* ```
|
|
4490
|
+
*
|
|
4491
|
+
* Entries are the same field states `form.fields` exposes, so reads
|
|
4492
|
+
* stay live, and the keyed shape mirrors the record's own keys: an
|
|
4493
|
+
* entry appears once you write its key (`form.setValue`) and drops
|
|
4494
|
+
* when the key leaves. `form.fields(path)` remains the single
|
|
4495
|
+
* aggregated container for the whole record; `record` is the
|
|
4496
|
+
* per-entry view.
|
|
4497
|
+
*/
|
|
4498
|
+
record: <Path extends RecordPath<Form>>(path: Path) => Readonly<Record<string, FieldState<RecordValue<Form, Path>>>>;
|
|
4220
4499
|
/**
|
|
4221
4500
|
* Read-only view of the form's blank path set. Reactive — Vue 3.5
|
|
4222
4501
|
* tracks `.has()` / `for..of` / size accesses, so consumers can drive
|
|
@@ -4244,5 +4523,5 @@ type UseFormReturnType<Form extends GenericForm, GetValueFormType extends Generi
|
|
|
4244
4523
|
blankPaths: ComputedRef<BlankPathsView>;
|
|
4245
4524
|
};
|
|
4246
4525
|
|
|
4247
|
-
export {
|
|
4248
|
-
export type {
|
|
4526
|
+
export { ROOT_PATH_KEY as $, ROOT_PATH as _, canonicalizePath as as, isPathPrefix as at, isUnset as au, parseDottedPath as av, unset as aw };
|
|
4527
|
+
export type { AbstractSchema as A, NestedType as B, CoercionEntry as C, DeepPartial as D, ErrorsProxyShape as E, FieldMetaPayload as F, GenericForm as G, HandleSubmit as H, IsTuple as I, JoinSegments as J, KeyofUnion as K, LiftedValueShape as L, MetaTrackerValue as M, NestedReadType as N, OnError as O, OnInvalidSubmitPolicy as P, OnSubmit as Q, PartialFlatPath as R, Path as S, PathKey as T, PendingValidationStatus as U, PersistConfig as V, PersistConfigOptions as W, PersistIncludeMode as X, PersistOptInRegistry as Y, Primitive as Z, ApiErrorDetails as a, ReactiveValidationStatus as a0, RegisterDirective as a1, RegisterFlatPath as a2, RegisterModelDynamicCustomDirective as a3, RegisterOptions as a4, RegisterSelectModifier as a5, RegisterTextModifier as a6, RegisterTransform as a7, RegisterValue as a8, SchemaFactoryOptions as a9, Segment as aa, SetValueCallback as ab, SetValuePayload as ac, SettledValidationStatus as ad, SlimPrimitiveKind as ae, SlimRuntimeOf as af, SubmitHandler as ag, Unset as ah, UseFormConfiguration as ai, UseFormReturnType as aj, ValidateOn as ak, ValidateOnConfig as al, ValidationError as am, ValidationResponse as an, ValidationResponseWithoutValue as ao, ValueOfUnion as ap, WriteMeta as aq, WriteShape as ar, ApiErrorEntry as b, ApiErrorEnvelope as c, ArrayItem as d, ArrayPath as e, AttaformDefaults as f, CoercionRegistry as g, CoercionResult as h, CustomDirectiveRegisterAssignerFn as i, DefaultValuesInput as j, DefaultValuesResponse as k, DefaultValuesShape as l, DisplayState as m, FieldState as n, FieldStateMap as o, FieldStateMapEntry as p, FlatPath as q, FormErrorRecord as r, FormErrorsSurface as s, FormKey as t, FormMeta as u, FormStorage as v, FormStorageKind as w, GetDisplayState as x, HistoryConfig as y, IsUnion as z };
|