attaform 0.16.3 → 0.16.4
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/dist/chunks/devtools.cjs +1 -1
- package/dist/chunks/devtools.mjs +1 -1
- package/dist/index.cjs +3 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +33 -4
- package/dist/index.d.mts +33 -4
- package/dist/index.d.ts +33 -4
- package/dist/index.mjs +3 -3
- package/dist/nuxt.d.cts +1 -1
- package/dist/nuxt.d.mts +1 -1
- package/dist/nuxt.d.ts +1 -1
- package/dist/shared/{attaform.lFNwBcA3.d.ts → attaform.CCQkY4Ta.d.ts} +1 -1
- package/dist/shared/{attaform.c_NzdRyc.cjs → attaform.CIwZtbGV.cjs} +6 -2
- package/dist/shared/attaform.CIwZtbGV.cjs.map +1 -0
- package/dist/shared/{attaform.Bls_kFR6.d.mts → attaform.CMRmwGDt.d.cts} +1 -1
- package/dist/shared/{attaform._EqYNPYF.d.ts → attaform.CU3JperC.d.cts} +172 -14
- package/dist/shared/{attaform._EqYNPYF.d.cts → attaform.CU3JperC.d.mts} +172 -14
- package/dist/shared/{attaform._EqYNPYF.d.mts → attaform.CU3JperC.d.ts} +172 -14
- package/dist/shared/{attaform.DLnKT7wk.d.cts → attaform.CXMOheyZ.d.mts} +1 -1
- package/dist/shared/{attaform.jrxE_xZw.mjs → attaform.DZRj9s0s.mjs} +5 -3
- package/dist/shared/attaform.DZRj9s0s.mjs.map +1 -0
- package/dist/shared/{attaform.KrNw10aW.cjs → attaform.Dd_pWnmn.cjs} +12 -13
- package/dist/shared/attaform.Dd_pWnmn.cjs.map +1 -0
- package/dist/shared/{attaform.DILbdvfo.mjs → attaform.DyV1O4tI.mjs} +111 -22
- package/dist/shared/attaform.DyV1O4tI.mjs.map +1 -0
- package/dist/shared/{attaform.CFA6y0KF.mjs → attaform.UA19EF3J.mjs} +12 -13
- package/dist/shared/attaform.UA19EF3J.mjs.map +1 -0
- package/dist/shared/{attaform.C9Ph2SMx.cjs → attaform.fegmBJaq.cjs} +111 -21
- package/dist/shared/attaform.fegmBJaq.cjs.map +1 -0
- package/dist/shared/{attaform.XYOMTvuO.mjs → attaform.g7rfuXdz.mjs} +7 -3
- package/dist/shared/attaform.g7rfuXdz.mjs.map +1 -0
- package/dist/shared/{attaform.DGuGGNg9.cjs → attaform.keLBaHB6.cjs} +7 -3
- package/dist/shared/attaform.keLBaHB6.cjs.map +1 -0
- package/dist/zod-v3.cjs +2 -2
- package/dist/zod-v3.d.cts +3 -3
- package/dist/zod-v3.d.mts +3 -3
- package/dist/zod-v3.d.ts +3 -3
- package/dist/zod-v3.mjs +2 -2
- package/dist/zod-v4.cjs +2 -2
- package/dist/zod-v4.d.cts +3 -3
- package/dist/zod-v4.d.mts +3 -3
- package/dist/zod-v4.d.ts +3 -3
- package/dist/zod-v4.mjs +2 -2
- package/dist/zod.cjs +3 -3
- package/dist/zod.d.cts +3 -3
- package/dist/zod.d.mts +3 -3
- package/dist/zod.d.ts +3 -3
- package/dist/zod.mjs +3 -3
- package/package.json +1 -1
- package/dist/shared/attaform.C9Ph2SMx.cjs.map +0 -1
- package/dist/shared/attaform.CFA6y0KF.mjs.map +0 -1
- package/dist/shared/attaform.DGuGGNg9.cjs.map +0 -1
- package/dist/shared/attaform.DILbdvfo.mjs.map +0 -1
- package/dist/shared/attaform.KrNw10aW.cjs.map +0 -1
- package/dist/shared/attaform.XYOMTvuO.mjs.map +0 -1
- package/dist/shared/attaform.c_NzdRyc.cjs.map +0 -1
- package/dist/shared/attaform.jrxE_xZw.mjs.map +0 -1
|
@@ -98,10 +98,15 @@ type Path = readonly Segment[];
|
|
|
98
98
|
* ```ts
|
|
99
99
|
* parseDottedPath('user.address.line1') // ['user', 'address', 'line1']
|
|
100
100
|
* parseDottedPath('items.0.name') // ['items', 0, 'name']
|
|
101
|
-
* parseDottedPath('') // []
|
|
101
|
+
* parseDottedPath('') // [''] (the empty-string key)
|
|
102
102
|
* ```
|
|
103
103
|
*
|
|
104
|
-
*
|
|
104
|
+
* The empty-string input `''` is the **literal empty-key path**, not
|
|
105
|
+
* the root. Use the array form `[]` for root. Form-level errors
|
|
106
|
+
* (root `.refine()`) live at the empty-string path bucket so
|
|
107
|
+
* `errors('')` returns them without sweeping every field error too.
|
|
108
|
+
*
|
|
109
|
+
* Throws `InvalidPathError` for paths with empty INTERNAL segments
|
|
105
110
|
* (`'a..b'`, leading or trailing dots). For keys containing literal
|
|
106
111
|
* dots, pass an array form (`['user.name']`) instead.
|
|
107
112
|
*/
|
|
@@ -465,9 +470,10 @@ type FormKey = string;
|
|
|
465
470
|
/**
|
|
466
471
|
* One validation failure. `path` points at the offending field as a
|
|
467
472
|
* structured array — `['user', 'address', 0, 'line1']` for a nested
|
|
468
|
-
* field, `[]` for a form-level error
|
|
469
|
-
*
|
|
470
|
-
*
|
|
473
|
+
* field, `['']` (the empty-string path) for a form-level error
|
|
474
|
+
* (root `.refine()` messages, `setFormErrors()` entries, server-
|
|
475
|
+
* emitted form banners). `formKey` identifies which form produced
|
|
476
|
+
* the error so a single error list can be routed to multiple forms.
|
|
471
477
|
*
|
|
472
478
|
* Returned by `validate()` / `validateAsync()` / `handleSubmit`'s
|
|
473
479
|
* `onError` callback, and by `parseApiErrors` for server responses.
|
|
@@ -475,7 +481,12 @@ type FormKey = string;
|
|
|
475
481
|
type ValidationError = {
|
|
476
482
|
/** Human-readable message describing the failure. */
|
|
477
483
|
message: string;
|
|
478
|
-
/**
|
|
484
|
+
/**
|
|
485
|
+
* Structured path of the offending field. The empty-string path
|
|
486
|
+
* `['']` is the form-level bucket — the dedicated home for errors
|
|
487
|
+
* that don't belong to any specific field, distinct from the
|
|
488
|
+
* whole-form subtree address `[]`.
|
|
489
|
+
*/
|
|
479
490
|
path: (string | number)[];
|
|
480
491
|
/** Identifies which form produced this error. */
|
|
481
492
|
formKey: FormKey;
|
|
@@ -1394,6 +1405,17 @@ type UseFormConfiguration<Form extends GenericForm, GetValueFormType, Schema ext
|
|
|
1394
1405
|
* coerced.
|
|
1395
1406
|
*/
|
|
1396
1407
|
coerce?: boolean | CoercionRegistry;
|
|
1408
|
+
/**
|
|
1409
|
+
* Per-form override of the `shouldShowErrors` heuristic that drives
|
|
1410
|
+
* `field.showErrors` and `form.meta.showErrors`. Falls back to
|
|
1411
|
+
* `AttaformDefaults.shouldShowErrors`, then to the library default
|
|
1412
|
+
* (`defaultShouldShowErrors`). See `AttaformDefaults.shouldShowErrors`
|
|
1413
|
+
* for the resolution rules and predicate signature.
|
|
1414
|
+
*
|
|
1415
|
+
* Boolean shorthand: `true` → always show *when errors exist*;
|
|
1416
|
+
* `false` → never show.
|
|
1417
|
+
*/
|
|
1418
|
+
shouldShowErrors?: ShouldShowErrorsConfig;
|
|
1397
1419
|
};
|
|
1398
1420
|
/**
|
|
1399
1421
|
* App-level defaults applied to every `useForm` call. Set these once
|
|
@@ -1459,6 +1481,34 @@ type AttaformDefaults = {
|
|
|
1459
1481
|
* authoritative writes whose strict typing is on the caller.
|
|
1460
1482
|
*/
|
|
1461
1483
|
coerce?: boolean | CoercionRegistry;
|
|
1484
|
+
/**
|
|
1485
|
+
* Default for `useForm({ shouldShowErrors })`. Centralised heuristic
|
|
1486
|
+
* that drives `field.showErrors` (and `form.meta.showErrors`) — a
|
|
1487
|
+
* boolean that gates whether a path's errors are *ready* to render.
|
|
1488
|
+
*
|
|
1489
|
+
* Resolution order (per-form wins):
|
|
1490
|
+
*
|
|
1491
|
+
* useForm({ shouldShowErrors }) > AttaformDefaults > library default
|
|
1492
|
+
*
|
|
1493
|
+
* The library default reads "show after the first submit attempt OR
|
|
1494
|
+
* after the field has been interacted with AND changed":
|
|
1495
|
+
*
|
|
1496
|
+
* ```ts
|
|
1497
|
+
* (field, formMeta) =>
|
|
1498
|
+
* formMeta.submitCount > 0 || (field.touched === true && field.dirty)
|
|
1499
|
+
* ```
|
|
1500
|
+
*
|
|
1501
|
+
* Compose with the library default via the public
|
|
1502
|
+
* `defaultShouldShowErrors` export. Boolean shorthand is supported:
|
|
1503
|
+
* `true` → always show *when errors exist*; `false` → never show. The
|
|
1504
|
+
* predicate is invoked only when `errors.length > 0`, so authors
|
|
1505
|
+
* don't re-check inside.
|
|
1506
|
+
*
|
|
1507
|
+
* The predicate's args are `Omit`'d of `showErrors` / `firstError`
|
|
1508
|
+
* to prevent recursive predicates — those are derived FROM this
|
|
1509
|
+
* predicate, so reading them inside would be a self-reference.
|
|
1510
|
+
*/
|
|
1511
|
+
shouldShowErrors?: ShouldShowErrorsConfig;
|
|
1462
1512
|
};
|
|
1463
1513
|
/**
|
|
1464
1514
|
* Callback invoked by `handleSubmit` after the form parses successfully.
|
|
@@ -1473,6 +1523,46 @@ type OnSubmit<Form extends GenericForm> = (form: Form) => void | Promise<void>;
|
|
|
1473
1523
|
* automatic `onInvalidSubmit` UI nudge).
|
|
1474
1524
|
*/
|
|
1475
1525
|
type OnError = (error: ValidationError[]) => void | Promise<void>;
|
|
1526
|
+
/**
|
|
1527
|
+
* Predicate that drives `field.showErrors` (and `form.meta.showErrors`).
|
|
1528
|
+
* Receives the field's reactive state plus the form's reactive meta;
|
|
1529
|
+
* returns `true` to render the field's errors, `false` to keep them
|
|
1530
|
+
* hidden. The framework gates the call on `errors.length > 0`, so
|
|
1531
|
+
* authors don't re-check error presence inside.
|
|
1532
|
+
*
|
|
1533
|
+
* Both arguments are `Omit`'d of `showErrors` / `firstError` — those
|
|
1534
|
+
* are derived FROM this predicate, so reading them inside would be a
|
|
1535
|
+
* self-reference. The omit is enforced at the type level AND at
|
|
1536
|
+
* runtime: the keys literally are not present on the objects passed
|
|
1537
|
+
* in, so `as` casting in TS or vanilla-JS bypass cannot create a
|
|
1538
|
+
* cycle.
|
|
1539
|
+
*
|
|
1540
|
+
* The library default — `defaultShouldShowErrors` — is publicly
|
|
1541
|
+
* exported so a layered predicate can compose with it:
|
|
1542
|
+
*
|
|
1543
|
+
* ```ts
|
|
1544
|
+
* import { defaultShouldShowErrors } from 'attaform'
|
|
1545
|
+
*
|
|
1546
|
+
* useForm({
|
|
1547
|
+
* schema,
|
|
1548
|
+
* shouldShowErrors: (field, formMeta) =>
|
|
1549
|
+
* field.path[0] === 'urgent' || defaultShouldShowErrors(field, formMeta),
|
|
1550
|
+
* })
|
|
1551
|
+
* ```
|
|
1552
|
+
*/
|
|
1553
|
+
type ShouldShowErrors = (field: Omit<FieldState, 'showErrors' | 'firstError'>, formMeta: Omit<FormMeta, 'showErrors' | 'firstError'>) => boolean;
|
|
1554
|
+
/**
|
|
1555
|
+
* Configuration shape for `shouldShowErrors`. A predicate function or
|
|
1556
|
+
* a boolean shorthand:
|
|
1557
|
+
*
|
|
1558
|
+
* - `true` — always show errors (when any exist).
|
|
1559
|
+
* - `false` — never show errors.
|
|
1560
|
+
* - function — custom predicate, see `ShouldShowErrors`.
|
|
1561
|
+
*
|
|
1562
|
+
* Resolved through three tiers (per-form > plugin defaults > library
|
|
1563
|
+
* default).
|
|
1564
|
+
*/
|
|
1565
|
+
type ShouldShowErrorsConfig = ShouldShowErrors | boolean;
|
|
1476
1566
|
/**
|
|
1477
1567
|
* Submit handler returned by `handleSubmit(onSubmit, onError)`. Bind
|
|
1478
1568
|
* it to a `<form>`:
|
|
@@ -2075,6 +2165,51 @@ type FieldState<Value = unknown> = {
|
|
|
2075
2165
|
* green-checkmark / `aria-invalid` UX.
|
|
2076
2166
|
*/
|
|
2077
2167
|
readonly valid: boolean;
|
|
2168
|
+
/**
|
|
2169
|
+
* Centralised "should I render this field's errors right now?"
|
|
2170
|
+
* gate. Wraps `errors.length > 0 && shouldShowErrors(field, formMeta)`
|
|
2171
|
+
* so templates avoid re-spelling the heuristic at every error site:
|
|
2172
|
+
*
|
|
2173
|
+
* ```vue
|
|
2174
|
+
* <span v-if="form.fields.email.showErrors">
|
|
2175
|
+
* {{ form.fields.email.firstError?.message }}
|
|
2176
|
+
* </span>
|
|
2177
|
+
* ```
|
|
2178
|
+
*
|
|
2179
|
+
* The heuristic itself comes from `useForm({ shouldShowErrors })` →
|
|
2180
|
+
* `createAttaform({ defaults: { shouldShowErrors } })` → library
|
|
2181
|
+
* default (`defaultShouldShowErrors` — show after first submit OR
|
|
2182
|
+
* after touched-and-dirty). Override per form, app-wide, or
|
|
2183
|
+
* compose with `defaultShouldShowErrors` for a layered predicate.
|
|
2184
|
+
*
|
|
2185
|
+
* Falls back to `false` whenever there are no errors — the gate
|
|
2186
|
+
* skips the predicate entirely in that case.
|
|
2187
|
+
*
|
|
2188
|
+
* Available on container paths too: `form.fields.users[0].showErrors`
|
|
2189
|
+
* aggregates over the row's descendants (any descendant with a
|
|
2190
|
+
* qualifying error flips the container on).
|
|
2191
|
+
*/
|
|
2192
|
+
readonly showErrors: boolean;
|
|
2193
|
+
/**
|
|
2194
|
+
* The first `ValidationError` at this path in the deterministic
|
|
2195
|
+
* schema-declaration order — equivalent to `errors[0]`, exposed as
|
|
2196
|
+
* a sugar accessor for the common case of "show the highest-priority
|
|
2197
|
+
* error message and ignore the rest":
|
|
2198
|
+
*
|
|
2199
|
+
* ```vue
|
|
2200
|
+
* <span v-if="form.fields.email.showErrors">
|
|
2201
|
+
* {{ form.fields.email.firstError?.message }}
|
|
2202
|
+
* </span>
|
|
2203
|
+
* ```
|
|
2204
|
+
*
|
|
2205
|
+
* `undefined` when no errors exist. Independent of `showErrors` —
|
|
2206
|
+
* the data primitive is always available; the heuristic only
|
|
2207
|
+
* decides when to render it.
|
|
2208
|
+
*
|
|
2209
|
+
* On container paths, the first error in the aggregated subtree
|
|
2210
|
+
* (descendants sorted by `pathOrdinal`).
|
|
2211
|
+
*/
|
|
2212
|
+
readonly firstError: ValidationError | undefined;
|
|
2078
2213
|
readonly path: ReadonlyArray<string | number>;
|
|
2079
2214
|
readonly blank: boolean;
|
|
2080
2215
|
/**
|
|
@@ -2790,12 +2925,15 @@ type UseFormReturnType<Form extends GenericForm, GetValueFormType extends Generi
|
|
|
2790
2925
|
* if (result.ok) form.setFormErrors(result.errors)
|
|
2791
2926
|
* ```
|
|
2792
2927
|
*
|
|
2793
|
-
* Form-level errors
|
|
2794
|
-
*
|
|
2795
|
-
* `form.errors`
|
|
2796
|
-
*
|
|
2797
|
-
*
|
|
2798
|
-
*
|
|
2928
|
+
* Form-level errors land at the empty-string path bucket
|
|
2929
|
+
* (`path: ['']`). They surface in `form.meta.errors` (alongside
|
|
2930
|
+
* field errors), in `form.errors()` / `form.errors([])` (whole-form
|
|
2931
|
+
* subtree aggregates), and — uniquely — in `form.errors('')`,
|
|
2932
|
+
* which returns ONLY the form-level bucket. They're excluded from
|
|
2933
|
+
* the path-keyed `form.errors` drill proxy because no nested-object
|
|
2934
|
+
* key represents the empty-string path. Read them via
|
|
2935
|
+
* `meta.errors.filter(e => e.path.length === 1 && e.path[0] === '')`
|
|
2936
|
+
* if you need a programmatic split.
|
|
2799
2937
|
*/
|
|
2800
2938
|
setFormErrors: (errors: ReadonlyArray<Partial<ValidationError> & {
|
|
2801
2939
|
message: string;
|
|
@@ -2905,6 +3043,26 @@ type UseFormReturnType<Form extends GenericForm, GetValueFormType extends Generi
|
|
|
2905
3043
|
* `options` is forwarded to `Element.scrollIntoView` unchanged.
|
|
2906
3044
|
*/
|
|
2907
3045
|
scrollToFirstError: (options?: ScrollIntoViewOptions) => boolean;
|
|
3046
|
+
/**
|
|
3047
|
+
* Programmatically mark fields as touched — the sticky flag the
|
|
3048
|
+
* standard "show errors after interaction" pattern reads. Closes
|
|
3049
|
+
* the gap when fields are populated without a DOM gesture (post-
|
|
3050
|
+
* import, paste, autofill, server-seeded values you want to
|
|
3051
|
+
* validate immediately).
|
|
3052
|
+
*
|
|
3053
|
+
* ```ts
|
|
3054
|
+
* form.touch('email') // one leaf
|
|
3055
|
+
* form.touch('profile') // every leaf under profile
|
|
3056
|
+
* form.touch(['profile', 'name']) // segment-array form
|
|
3057
|
+
* form.touch() // every leaf in the form
|
|
3058
|
+
* ```
|
|
3059
|
+
*
|
|
3060
|
+
* Pure flag write — does not mutate value, focused, blurred, or
|
|
3061
|
+
* trigger validation. Idempotent: re-calling on an already-touched
|
|
3062
|
+
* field is a no-op. Touched is sticky-true; pair with
|
|
3063
|
+
* `form.reset()` / `form.resetField()` to clear.
|
|
3064
|
+
*/
|
|
3065
|
+
touch: (path?: FlatPath<Form> | (string | number)[]) => void;
|
|
2908
3066
|
/**
|
|
2909
3067
|
* Append `value` to the array at `path`.
|
|
2910
3068
|
*
|
|
@@ -2956,5 +3114,5 @@ type UseFormReturnType<Form extends GenericForm, GetValueFormType extends Generi
|
|
|
2956
3114
|
blankPaths: ComputedRef<ReadonlySet<string>>;
|
|
2957
3115
|
};
|
|
2958
3116
|
|
|
2959
|
-
export {
|
|
2960
|
-
export type {
|
|
3117
|
+
export { ROOT_PATH_KEY as $, ROOT_PATH as _, canonicalizePath as am, isPathPrefix as an, isUnset as ao, parseDottedPath as ap, unset as aq };
|
|
3118
|
+
export type { AttaformDefaults as A, NestedType as B, CoercionRegistry as C, DeepPartial as D, OnInvalidSubmitPolicy as E, FormKey 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, OnSubmit as P, Path as Q, RegisterValue as R, SlimPrimitiveKind as S, PathKey as T, UseFormConfiguration as U, ValidationError as V, PendingValidationStatus as W, PersistConfig as X, PersistConfigOptions as Y, PersistIncludeMode as Z, CoercionEntry as a, ReactiveValidationStatus as a0, RegisterDirective as a1, RegisterFlatPath as a2, RegisterOptions as a3, RegisterSelectModifier as a4, RegisterTextModifier as a5, RegisterTransform as a6, Segment as a7, SetValueCallback as a8, SetValuePayload as a9, SettledValidationStatus as aa, ShouldShowErrorsConfig as ab, SlimRuntimeOf as ac, SubmitHandler as ad, Unset as ae, ValidateOn as af, ValidateOnConfig as ag, ValidationResponse as ah, ValidationResponseWithoutValue as ai, ValueOfUnion as aj, WriteMeta as ak, WriteShape as al, AbstractSchema as b, DefaultValuesShape as c, UseFormReturnType as d, RegisterModelDynamicCustomDirective as e, ShouldShowErrors as f, ApiErrorEnvelope as g, ApiErrorDetails as h, ApiErrorEntry as i, ArrayItem as j, ArrayPath as k, CoercionResult as l, CustomDirectiveRegisterAssignerFn as m, DefaultValuesResponse as n, FieldMetaPayload as o, FieldState as p, FieldStateMap as q, FieldStateMapEntry as r, FlatPath as s, FormErrorRecord as t, FormErrorsSurface as u, FormMeta as v, FormStorage as w, FormStorageKind as x, HistoryConfig as y, IsUnion as z };
|
|
@@ -98,10 +98,15 @@ type Path = readonly Segment[];
|
|
|
98
98
|
* ```ts
|
|
99
99
|
* parseDottedPath('user.address.line1') // ['user', 'address', 'line1']
|
|
100
100
|
* parseDottedPath('items.0.name') // ['items', 0, 'name']
|
|
101
|
-
* parseDottedPath('') // []
|
|
101
|
+
* parseDottedPath('') // [''] (the empty-string key)
|
|
102
102
|
* ```
|
|
103
103
|
*
|
|
104
|
-
*
|
|
104
|
+
* The empty-string input `''` is the **literal empty-key path**, not
|
|
105
|
+
* the root. Use the array form `[]` for root. Form-level errors
|
|
106
|
+
* (root `.refine()`) live at the empty-string path bucket so
|
|
107
|
+
* `errors('')` returns them without sweeping every field error too.
|
|
108
|
+
*
|
|
109
|
+
* Throws `InvalidPathError` for paths with empty INTERNAL segments
|
|
105
110
|
* (`'a..b'`, leading or trailing dots). For keys containing literal
|
|
106
111
|
* dots, pass an array form (`['user.name']`) instead.
|
|
107
112
|
*/
|
|
@@ -465,9 +470,10 @@ type FormKey = string;
|
|
|
465
470
|
/**
|
|
466
471
|
* One validation failure. `path` points at the offending field as a
|
|
467
472
|
* structured array — `['user', 'address', 0, 'line1']` for a nested
|
|
468
|
-
* field, `[]` for a form-level error
|
|
469
|
-
*
|
|
470
|
-
*
|
|
473
|
+
* field, `['']` (the empty-string path) for a form-level error
|
|
474
|
+
* (root `.refine()` messages, `setFormErrors()` entries, server-
|
|
475
|
+
* emitted form banners). `formKey` identifies which form produced
|
|
476
|
+
* the error so a single error list can be routed to multiple forms.
|
|
471
477
|
*
|
|
472
478
|
* Returned by `validate()` / `validateAsync()` / `handleSubmit`'s
|
|
473
479
|
* `onError` callback, and by `parseApiErrors` for server responses.
|
|
@@ -475,7 +481,12 @@ type FormKey = string;
|
|
|
475
481
|
type ValidationError = {
|
|
476
482
|
/** Human-readable message describing the failure. */
|
|
477
483
|
message: string;
|
|
478
|
-
/**
|
|
484
|
+
/**
|
|
485
|
+
* Structured path of the offending field. The empty-string path
|
|
486
|
+
* `['']` is the form-level bucket — the dedicated home for errors
|
|
487
|
+
* that don't belong to any specific field, distinct from the
|
|
488
|
+
* whole-form subtree address `[]`.
|
|
489
|
+
*/
|
|
479
490
|
path: (string | number)[];
|
|
480
491
|
/** Identifies which form produced this error. */
|
|
481
492
|
formKey: FormKey;
|
|
@@ -1394,6 +1405,17 @@ type UseFormConfiguration<Form extends GenericForm, GetValueFormType, Schema ext
|
|
|
1394
1405
|
* coerced.
|
|
1395
1406
|
*/
|
|
1396
1407
|
coerce?: boolean | CoercionRegistry;
|
|
1408
|
+
/**
|
|
1409
|
+
* Per-form override of the `shouldShowErrors` heuristic that drives
|
|
1410
|
+
* `field.showErrors` and `form.meta.showErrors`. Falls back to
|
|
1411
|
+
* `AttaformDefaults.shouldShowErrors`, then to the library default
|
|
1412
|
+
* (`defaultShouldShowErrors`). See `AttaformDefaults.shouldShowErrors`
|
|
1413
|
+
* for the resolution rules and predicate signature.
|
|
1414
|
+
*
|
|
1415
|
+
* Boolean shorthand: `true` → always show *when errors exist*;
|
|
1416
|
+
* `false` → never show.
|
|
1417
|
+
*/
|
|
1418
|
+
shouldShowErrors?: ShouldShowErrorsConfig;
|
|
1397
1419
|
};
|
|
1398
1420
|
/**
|
|
1399
1421
|
* App-level defaults applied to every `useForm` call. Set these once
|
|
@@ -1459,6 +1481,34 @@ type AttaformDefaults = {
|
|
|
1459
1481
|
* authoritative writes whose strict typing is on the caller.
|
|
1460
1482
|
*/
|
|
1461
1483
|
coerce?: boolean | CoercionRegistry;
|
|
1484
|
+
/**
|
|
1485
|
+
* Default for `useForm({ shouldShowErrors })`. Centralised heuristic
|
|
1486
|
+
* that drives `field.showErrors` (and `form.meta.showErrors`) — a
|
|
1487
|
+
* boolean that gates whether a path's errors are *ready* to render.
|
|
1488
|
+
*
|
|
1489
|
+
* Resolution order (per-form wins):
|
|
1490
|
+
*
|
|
1491
|
+
* useForm({ shouldShowErrors }) > AttaformDefaults > library default
|
|
1492
|
+
*
|
|
1493
|
+
* The library default reads "show after the first submit attempt OR
|
|
1494
|
+
* after the field has been interacted with AND changed":
|
|
1495
|
+
*
|
|
1496
|
+
* ```ts
|
|
1497
|
+
* (field, formMeta) =>
|
|
1498
|
+
* formMeta.submitCount > 0 || (field.touched === true && field.dirty)
|
|
1499
|
+
* ```
|
|
1500
|
+
*
|
|
1501
|
+
* Compose with the library default via the public
|
|
1502
|
+
* `defaultShouldShowErrors` export. Boolean shorthand is supported:
|
|
1503
|
+
* `true` → always show *when errors exist*; `false` → never show. The
|
|
1504
|
+
* predicate is invoked only when `errors.length > 0`, so authors
|
|
1505
|
+
* don't re-check inside.
|
|
1506
|
+
*
|
|
1507
|
+
* The predicate's args are `Omit`'d of `showErrors` / `firstError`
|
|
1508
|
+
* to prevent recursive predicates — those are derived FROM this
|
|
1509
|
+
* predicate, so reading them inside would be a self-reference.
|
|
1510
|
+
*/
|
|
1511
|
+
shouldShowErrors?: ShouldShowErrorsConfig;
|
|
1462
1512
|
};
|
|
1463
1513
|
/**
|
|
1464
1514
|
* Callback invoked by `handleSubmit` after the form parses successfully.
|
|
@@ -1473,6 +1523,46 @@ type OnSubmit<Form extends GenericForm> = (form: Form) => void | Promise<void>;
|
|
|
1473
1523
|
* automatic `onInvalidSubmit` UI nudge).
|
|
1474
1524
|
*/
|
|
1475
1525
|
type OnError = (error: ValidationError[]) => void | Promise<void>;
|
|
1526
|
+
/**
|
|
1527
|
+
* Predicate that drives `field.showErrors` (and `form.meta.showErrors`).
|
|
1528
|
+
* Receives the field's reactive state plus the form's reactive meta;
|
|
1529
|
+
* returns `true` to render the field's errors, `false` to keep them
|
|
1530
|
+
* hidden. The framework gates the call on `errors.length > 0`, so
|
|
1531
|
+
* authors don't re-check error presence inside.
|
|
1532
|
+
*
|
|
1533
|
+
* Both arguments are `Omit`'d of `showErrors` / `firstError` — those
|
|
1534
|
+
* are derived FROM this predicate, so reading them inside would be a
|
|
1535
|
+
* self-reference. The omit is enforced at the type level AND at
|
|
1536
|
+
* runtime: the keys literally are not present on the objects passed
|
|
1537
|
+
* in, so `as` casting in TS or vanilla-JS bypass cannot create a
|
|
1538
|
+
* cycle.
|
|
1539
|
+
*
|
|
1540
|
+
* The library default — `defaultShouldShowErrors` — is publicly
|
|
1541
|
+
* exported so a layered predicate can compose with it:
|
|
1542
|
+
*
|
|
1543
|
+
* ```ts
|
|
1544
|
+
* import { defaultShouldShowErrors } from 'attaform'
|
|
1545
|
+
*
|
|
1546
|
+
* useForm({
|
|
1547
|
+
* schema,
|
|
1548
|
+
* shouldShowErrors: (field, formMeta) =>
|
|
1549
|
+
* field.path[0] === 'urgent' || defaultShouldShowErrors(field, formMeta),
|
|
1550
|
+
* })
|
|
1551
|
+
* ```
|
|
1552
|
+
*/
|
|
1553
|
+
type ShouldShowErrors = (field: Omit<FieldState, 'showErrors' | 'firstError'>, formMeta: Omit<FormMeta, 'showErrors' | 'firstError'>) => boolean;
|
|
1554
|
+
/**
|
|
1555
|
+
* Configuration shape for `shouldShowErrors`. A predicate function or
|
|
1556
|
+
* a boolean shorthand:
|
|
1557
|
+
*
|
|
1558
|
+
* - `true` — always show errors (when any exist).
|
|
1559
|
+
* - `false` — never show errors.
|
|
1560
|
+
* - function — custom predicate, see `ShouldShowErrors`.
|
|
1561
|
+
*
|
|
1562
|
+
* Resolved through three tiers (per-form > plugin defaults > library
|
|
1563
|
+
* default).
|
|
1564
|
+
*/
|
|
1565
|
+
type ShouldShowErrorsConfig = ShouldShowErrors | boolean;
|
|
1476
1566
|
/**
|
|
1477
1567
|
* Submit handler returned by `handleSubmit(onSubmit, onError)`. Bind
|
|
1478
1568
|
* it to a `<form>`:
|
|
@@ -2075,6 +2165,51 @@ type FieldState<Value = unknown> = {
|
|
|
2075
2165
|
* green-checkmark / `aria-invalid` UX.
|
|
2076
2166
|
*/
|
|
2077
2167
|
readonly valid: boolean;
|
|
2168
|
+
/**
|
|
2169
|
+
* Centralised "should I render this field's errors right now?"
|
|
2170
|
+
* gate. Wraps `errors.length > 0 && shouldShowErrors(field, formMeta)`
|
|
2171
|
+
* so templates avoid re-spelling the heuristic at every error site:
|
|
2172
|
+
*
|
|
2173
|
+
* ```vue
|
|
2174
|
+
* <span v-if="form.fields.email.showErrors">
|
|
2175
|
+
* {{ form.fields.email.firstError?.message }}
|
|
2176
|
+
* </span>
|
|
2177
|
+
* ```
|
|
2178
|
+
*
|
|
2179
|
+
* The heuristic itself comes from `useForm({ shouldShowErrors })` →
|
|
2180
|
+
* `createAttaform({ defaults: { shouldShowErrors } })` → library
|
|
2181
|
+
* default (`defaultShouldShowErrors` — show after first submit OR
|
|
2182
|
+
* after touched-and-dirty). Override per form, app-wide, or
|
|
2183
|
+
* compose with `defaultShouldShowErrors` for a layered predicate.
|
|
2184
|
+
*
|
|
2185
|
+
* Falls back to `false` whenever there are no errors — the gate
|
|
2186
|
+
* skips the predicate entirely in that case.
|
|
2187
|
+
*
|
|
2188
|
+
* Available on container paths too: `form.fields.users[0].showErrors`
|
|
2189
|
+
* aggregates over the row's descendants (any descendant with a
|
|
2190
|
+
* qualifying error flips the container on).
|
|
2191
|
+
*/
|
|
2192
|
+
readonly showErrors: boolean;
|
|
2193
|
+
/**
|
|
2194
|
+
* The first `ValidationError` at this path in the deterministic
|
|
2195
|
+
* schema-declaration order — equivalent to `errors[0]`, exposed as
|
|
2196
|
+
* a sugar accessor for the common case of "show the highest-priority
|
|
2197
|
+
* error message and ignore the rest":
|
|
2198
|
+
*
|
|
2199
|
+
* ```vue
|
|
2200
|
+
* <span v-if="form.fields.email.showErrors">
|
|
2201
|
+
* {{ form.fields.email.firstError?.message }}
|
|
2202
|
+
* </span>
|
|
2203
|
+
* ```
|
|
2204
|
+
*
|
|
2205
|
+
* `undefined` when no errors exist. Independent of `showErrors` —
|
|
2206
|
+
* the data primitive is always available; the heuristic only
|
|
2207
|
+
* decides when to render it.
|
|
2208
|
+
*
|
|
2209
|
+
* On container paths, the first error in the aggregated subtree
|
|
2210
|
+
* (descendants sorted by `pathOrdinal`).
|
|
2211
|
+
*/
|
|
2212
|
+
readonly firstError: ValidationError | undefined;
|
|
2078
2213
|
readonly path: ReadonlyArray<string | number>;
|
|
2079
2214
|
readonly blank: boolean;
|
|
2080
2215
|
/**
|
|
@@ -2790,12 +2925,15 @@ type UseFormReturnType<Form extends GenericForm, GetValueFormType extends Generi
|
|
|
2790
2925
|
* if (result.ok) form.setFormErrors(result.errors)
|
|
2791
2926
|
* ```
|
|
2792
2927
|
*
|
|
2793
|
-
* Form-level errors
|
|
2794
|
-
*
|
|
2795
|
-
* `form.errors`
|
|
2796
|
-
*
|
|
2797
|
-
*
|
|
2798
|
-
*
|
|
2928
|
+
* Form-level errors land at the empty-string path bucket
|
|
2929
|
+
* (`path: ['']`). They surface in `form.meta.errors` (alongside
|
|
2930
|
+
* field errors), in `form.errors()` / `form.errors([])` (whole-form
|
|
2931
|
+
* subtree aggregates), and — uniquely — in `form.errors('')`,
|
|
2932
|
+
* which returns ONLY the form-level bucket. They're excluded from
|
|
2933
|
+
* the path-keyed `form.errors` drill proxy because no nested-object
|
|
2934
|
+
* key represents the empty-string path. Read them via
|
|
2935
|
+
* `meta.errors.filter(e => e.path.length === 1 && e.path[0] === '')`
|
|
2936
|
+
* if you need a programmatic split.
|
|
2799
2937
|
*/
|
|
2800
2938
|
setFormErrors: (errors: ReadonlyArray<Partial<ValidationError> & {
|
|
2801
2939
|
message: string;
|
|
@@ -2905,6 +3043,26 @@ type UseFormReturnType<Form extends GenericForm, GetValueFormType extends Generi
|
|
|
2905
3043
|
* `options` is forwarded to `Element.scrollIntoView` unchanged.
|
|
2906
3044
|
*/
|
|
2907
3045
|
scrollToFirstError: (options?: ScrollIntoViewOptions) => boolean;
|
|
3046
|
+
/**
|
|
3047
|
+
* Programmatically mark fields as touched — the sticky flag the
|
|
3048
|
+
* standard "show errors after interaction" pattern reads. Closes
|
|
3049
|
+
* the gap when fields are populated without a DOM gesture (post-
|
|
3050
|
+
* import, paste, autofill, server-seeded values you want to
|
|
3051
|
+
* validate immediately).
|
|
3052
|
+
*
|
|
3053
|
+
* ```ts
|
|
3054
|
+
* form.touch('email') // one leaf
|
|
3055
|
+
* form.touch('profile') // every leaf under profile
|
|
3056
|
+
* form.touch(['profile', 'name']) // segment-array form
|
|
3057
|
+
* form.touch() // every leaf in the form
|
|
3058
|
+
* ```
|
|
3059
|
+
*
|
|
3060
|
+
* Pure flag write — does not mutate value, focused, blurred, or
|
|
3061
|
+
* trigger validation. Idempotent: re-calling on an already-touched
|
|
3062
|
+
* field is a no-op. Touched is sticky-true; pair with
|
|
3063
|
+
* `form.reset()` / `form.resetField()` to clear.
|
|
3064
|
+
*/
|
|
3065
|
+
touch: (path?: FlatPath<Form> | (string | number)[]) => void;
|
|
2908
3066
|
/**
|
|
2909
3067
|
* Append `value` to the array at `path`.
|
|
2910
3068
|
*
|
|
@@ -2956,5 +3114,5 @@ type UseFormReturnType<Form extends GenericForm, GetValueFormType extends Generi
|
|
|
2956
3114
|
blankPaths: ComputedRef<ReadonlySet<string>>;
|
|
2957
3115
|
};
|
|
2958
3116
|
|
|
2959
|
-
export {
|
|
2960
|
-
export type {
|
|
3117
|
+
export { ROOT_PATH_KEY as $, ROOT_PATH as _, canonicalizePath as am, isPathPrefix as an, isUnset as ao, parseDottedPath as ap, unset as aq };
|
|
3118
|
+
export type { AttaformDefaults as A, NestedType as B, CoercionRegistry as C, DeepPartial as D, OnInvalidSubmitPolicy as E, FormKey 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, OnSubmit as P, Path as Q, RegisterValue as R, SlimPrimitiveKind as S, PathKey as T, UseFormConfiguration as U, ValidationError as V, PendingValidationStatus as W, PersistConfig as X, PersistConfigOptions as Y, PersistIncludeMode as Z, CoercionEntry as a, ReactiveValidationStatus as a0, RegisterDirective as a1, RegisterFlatPath as a2, RegisterOptions as a3, RegisterSelectModifier as a4, RegisterTextModifier as a5, RegisterTransform as a6, Segment as a7, SetValueCallback as a8, SetValuePayload as a9, SettledValidationStatus as aa, ShouldShowErrorsConfig as ab, SlimRuntimeOf as ac, SubmitHandler as ad, Unset as ae, ValidateOn as af, ValidateOnConfig as ag, ValidationResponse as ah, ValidationResponseWithoutValue as ai, ValueOfUnion as aj, WriteMeta as ak, WriteShape as al, AbstractSchema as b, DefaultValuesShape as c, UseFormReturnType as d, RegisterModelDynamicCustomDirective as e, ShouldShowErrors as f, ApiErrorEnvelope as g, ApiErrorDetails as h, ApiErrorEntry as i, ArrayItem as j, ArrayPath as k, CoercionResult as l, CustomDirectiveRegisterAssignerFn as m, DefaultValuesResponse as n, FieldMetaPayload as o, FieldState as p, FieldStateMap as q, FieldStateMapEntry as r, FlatPath as s, FormErrorRecord as t, FormErrorsSurface as u, FormMeta as v, FormStorage as w, FormStorageKind as x, HistoryConfig as y, IsUnion as z };
|