react-f0rm 0.2.2 → 0.3.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 +527 -33
- package/dist/devtools/index.cjs.js +737 -0
- package/dist/devtools/index.cjs.js.map +1 -0
- package/dist/devtools/index.d.ts +33 -0
- package/dist/devtools/index.mjs +717 -0
- package/dist/devtools/index.mjs.map +1 -0
- package/dist/form-61297bc0.d.ts +578 -0
- package/dist/form-94c70b4b.mjs +378 -0
- package/dist/form-94c70b4b.mjs.map +1 -0
- package/dist/form-b9441d8c.cjs.js +387 -0
- package/dist/form-b9441d8c.cjs.js.map +1 -0
- package/dist/index.cjs.js +1257 -157
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +357 -53
- package/dist/index.mjs +1676 -0
- package/dist/index.mjs.map +1 -0
- package/dist/index.umd.js +1257 -157
- package/dist/index.umd.js.map +1 -1
- package/dist/index.umd.min.js +2 -2
- package/dist/index.umd.min.js.map +1 -1
- package/dist/resolvers/standard-schema.cjs.js +90 -0
- package/dist/resolvers/standard-schema.cjs.js.map +1 -0
- package/dist/resolvers/standard-schema.d.ts +66 -0
- package/dist/resolvers/standard-schema.mjs +86 -0
- package/dist/resolvers/standard-schema.mjs.map +1 -0
- package/dist/resolvers/yup.cjs.js +12 -2
- package/dist/resolvers/yup.cjs.js.map +1 -1
- package/dist/resolvers/yup.d.ts +2 -2
- package/dist/resolvers/yup.mjs +23 -0
- package/dist/resolvers/yup.mjs.map +1 -0
- package/dist/resolvers/zod.cjs.js +14 -1
- package/dist/resolvers/zod.cjs.js.map +1 -1
- package/dist/resolvers/zod.d.ts +2 -2
- package/dist/resolvers/zod.mjs +23 -0
- package/dist/resolvers/zod.mjs.map +1 -0
- package/dist/validate-148fe167.d.ts +22 -0
- package/package.json +34 -8
- package/dist/form-d06e6444.d.ts +0 -201
- package/dist/index.esm.js +0 -593
- package/dist/index.esm.js.map +0 -1
- package/dist/resolvers/yup.esm.js +0 -13
- package/dist/resolvers/yup.esm.js.map +0 -1
- package/dist/resolvers/zod.esm.js +0 -10
- package/dist/resolvers/zod.esm.js.map +0 -1
- package/dist/validate-0f17f86a.d.ts +0 -8
|
@@ -0,0 +1,578 @@
|
|
|
1
|
+
import { EventEmitter } from '@for-fun/event-emitter';
|
|
2
|
+
|
|
3
|
+
type PathSegments = (string | number)[];
|
|
4
|
+
type Name = string | PathSegments;
|
|
5
|
+
type Path = {
|
|
6
|
+
value: PathSegments;
|
|
7
|
+
key: string;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Compile-time field path utilities: `FieldPath<T>` enumerates the valid
|
|
12
|
+
* path strings for a values shape `T` ('a', 'a.b', 'a[0]', 'a.0.b', 'a[b]',
|
|
13
|
+
* ...), and `PathValue<T, P>` resolves the leaf type a path points at.
|
|
14
|
+
* The grammar mirrors the paths accepted at runtime by `normalizePath`.
|
|
15
|
+
*/
|
|
16
|
+
/** `true` only for the `any` type (`0 extends 1 & any`). */
|
|
17
|
+
type IsAny<T> = 0 extends 1 & T ? true : false;
|
|
18
|
+
type Primitive = null | undefined | string | number | boolean | symbol | bigint;
|
|
19
|
+
/** Depth countdown: Prev[9] = 8 ... Prev[1] = 0, Prev[0] = never stops recursion. */
|
|
20
|
+
type Prev = [never, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
|
|
21
|
+
/** Paths are capped at 10 segments to keep instantiation depth bounded. */
|
|
22
|
+
type MaxDepth = 9;
|
|
23
|
+
/**
|
|
24
|
+
* Valid path continuations after a segment: `.k` / `.0` / `[k]` / `[0]`,
|
|
25
|
+
* optionally followed by deeper continuations into the child node.
|
|
26
|
+
*/
|
|
27
|
+
type Continue<T, D extends number> = [D] extends [never] ? never : IsAny<T> extends true ? string : T extends Primitive | Function ? never : T extends readonly (infer U)[] ? `.${number}` | `[${number}]` | `.${number}${Continue<U, Prev[D]>}` | `[${number}]${Continue<U, Prev[D]>}` : {
|
|
28
|
+
[K in Extract<keyof T, string>]: `.${K}` | `[${K}]` | `.${K}${Continue<T[K], Prev[D]>}` | `[${K}]${Continue<T[K], Prev[D]>}`;
|
|
29
|
+
}[Extract<keyof T, string>];
|
|
30
|
+
/**
|
|
31
|
+
* Every valid field path string for a values shape `T`.
|
|
32
|
+
* @example FieldPath<{a: {b: string}}> // 'a' | 'a.b' | 'a[b]'
|
|
33
|
+
*/
|
|
34
|
+
type FieldPath<T> = IsAny<T> extends true ? string : T extends Primitive | Function ? never : T extends readonly (infer U)[] ? `${number}` | `${number}${Continue<U, MaxDepth>}` : {
|
|
35
|
+
[K in Extract<keyof T, string>]: K | `${K}${Continue<T[K], MaxDepth>}`;
|
|
36
|
+
}[Extract<keyof T, string>];
|
|
37
|
+
/** Resolve `T[K]` for one bare segment: array index -> element, object key -> value. */
|
|
38
|
+
type Lookup<T, K extends string> = K extends `${number}` ? T extends readonly (infer U)[] ? U : never : K extends keyof T ? T[K] : never;
|
|
39
|
+
/** One dot-separated chunk: a bare segment plus any `[k]` / `[0]` suffixes. */
|
|
40
|
+
type ChunkValue<T, C extends string> = C extends `${infer Key}[${infer Tail}` ? ChunkSuffix<Lookup<T, Key>, `[${Tail}`> : Lookup<T, C>;
|
|
41
|
+
type ChunkSuffix<T, S extends string> = S extends `[${infer Key}]${infer Rest}` ? Rest extends '' ? Lookup<T, Key> : PathOf<Lookup<T, Key>, Rest> : never;
|
|
42
|
+
/** Resolve the value type the path string `P` points at inside `T`. */
|
|
43
|
+
type PathOf<T, P extends string> = P extends '' ? never : P extends `[${infer Key}]${infer Rest}` ? Rest extends '' ? Lookup<T, Key> : PathOf<Lookup<T, Key>, Rest> : P extends `.${infer Rest}` ? PathOf<T, Rest> : P extends `${infer Chunk}.${infer Rest}` ? PathOf<ChunkValue<T, Chunk>, Rest> : ChunkValue<T, P>;
|
|
44
|
+
/**
|
|
45
|
+
* The value type at path `P` of a values shape `T`.
|
|
46
|
+
* @example PathValue<{a: {b: string}}, 'a.b'> // string
|
|
47
|
+
*/
|
|
48
|
+
type PathValue<T, P extends FieldPath<T>> = PathOf<T, P & string>;
|
|
49
|
+
/**
|
|
50
|
+
* The value type at path `P` of `T`, or `any` when `P` is not a known
|
|
51
|
+
* field path (plain `string` / segment-array calls keep their old behavior).
|
|
52
|
+
*/
|
|
53
|
+
type PathValueOf<T, P> = P extends FieldPath<T> ? PathValue<T, Extract<P, FieldPath<T>>> : any;
|
|
54
|
+
|
|
55
|
+
/** A field error: `type` identifies the error kind ('custom' for plain
|
|
56
|
+
* string errors), `message` is the display text. */
|
|
57
|
+
interface FieldError {
|
|
58
|
+
type: string;
|
|
59
|
+
message: string;
|
|
60
|
+
}
|
|
61
|
+
/** A flattened entry from {@link getErrors}. */
|
|
62
|
+
type FieldErrorEntry = {
|
|
63
|
+
path: string;
|
|
64
|
+
type: string;
|
|
65
|
+
message: string;
|
|
66
|
+
};
|
|
67
|
+
/** When a field is validated:
|
|
68
|
+
* - `'onSubmit'` (default): only on submit
|
|
69
|
+
* - `'onBlur'`: when the field loses focus
|
|
70
|
+
* - `'onChange'`: on every change
|
|
71
|
+
* - `'onTouched'`: on first blur, then on every change
|
|
72
|
+
* - `'all'`: on both change and blur
|
|
73
|
+
*/
|
|
74
|
+
type ValidationMode = 'onSubmit' | 'onBlur' | 'onChange' | 'onTouched' | 'all';
|
|
75
|
+
/** When a field is re-validated after it already has an error:
|
|
76
|
+
* - `'onChange'` (default): on every change
|
|
77
|
+
* - `'onBlur'`: when the field loses focus
|
|
78
|
+
* - `'onSubmit'`: only on submit (no live re-validation)
|
|
79
|
+
*/
|
|
80
|
+
type ReValidateMode = 'onChange' | 'onBlur' | 'onSubmit';
|
|
81
|
+
/** Brand marking a form-level validate result as a structured
|
|
82
|
+
* {@link ValidationOutcome} (parsed values and/or errors) rather than a
|
|
83
|
+
* plain nested error record. Symbols cannot collide with user error
|
|
84
|
+
* records, so detection is an exact `VALIDATION_OUTCOME in result`. */
|
|
85
|
+
declare const VALIDATION_OUTCOME: unique symbol;
|
|
86
|
+
/** Structured form-level validate result: `errors` uses the same nested
|
|
87
|
+
* shape a plain error record uses, `values` is the schema's parsed output
|
|
88
|
+
* (coerce/transform results included). Either side may be omitted. */
|
|
89
|
+
type ValidationOutcome<T> = {
|
|
90
|
+
[VALIDATION_OUTCOME]: true;
|
|
91
|
+
errors?: Record<string, any>;
|
|
92
|
+
values?: T;
|
|
93
|
+
};
|
|
94
|
+
/** What a form-level validate function may return: a plain nested error
|
|
95
|
+
* record (flattened into field errors — the long-standing shape), or a
|
|
96
|
+
* branded {@link ValidationOutcome} whose `values` become the form's
|
|
97
|
+
* parsedValues baseline. */
|
|
98
|
+
type ValidateResult<T> = Record<string, any> | ValidationOutcome<T> | Promise<Record<string, any> | ValidationOutcome<T>>;
|
|
99
|
+
interface Form<T extends Record<string, any> = any> {
|
|
100
|
+
emitter: EventEmitter;
|
|
101
|
+
mode: ValidationMode;
|
|
102
|
+
reValidateMode: ReValidateMode;
|
|
103
|
+
initialValues: T;
|
|
104
|
+
values: Map<string, any>;
|
|
105
|
+
/** Tombstones of unregistered field paths (JSON path keys): reading or
|
|
106
|
+
* merging values must not fall back to initialValues for these paths. */
|
|
107
|
+
deleted: Set<string>;
|
|
108
|
+
/** Every error registered for a field, as a non-empty array (the
|
|
109
|
+
* write-side {@link setErrorByPath} normalizes to this invariant, so
|
|
110
|
+
* readers never need to guard against an empty list). Readers wanting
|
|
111
|
+
* the display error take the first entry ({@link getError}); readers
|
|
112
|
+
* wanting all of them use {@link getFieldErrors}. */
|
|
113
|
+
errors: Map<string, FieldError[]>;
|
|
114
|
+
touched: Set<string>;
|
|
115
|
+
validators: Map<string, () => void>;
|
|
116
|
+
validating: Set<string>;
|
|
117
|
+
/** Parsed values from the last successful schema validation: the
|
|
118
|
+
* schema's complete output tree (coerced/transformed values included).
|
|
119
|
+
* Sits between initialValues and the values Map in {@link getValues}
|
|
120
|
+
* until `reset`/`setInitialValues` clears it. Never affects dirty
|
|
121
|
+
* state — that compares live edits against initialValues only. */
|
|
122
|
+
parsedValues: T | undefined;
|
|
123
|
+
validate?: (values: T) => ValidateResult<T>;
|
|
124
|
+
isSubmitting: boolean;
|
|
125
|
+
submitCount: number;
|
|
126
|
+
isSubmitSuccessful: boolean | undefined;
|
|
127
|
+
/** Form-level disabled flag, OR-ed into every bound field's `disabled`
|
|
128
|
+
* (form flag || the field's own option). Seeded from
|
|
129
|
+
* {@link Options}.disabled at create time and toggled at runtime with
|
|
130
|
+
* {@link setDisabled}, which emits a payload-less 'disabled' event so
|
|
131
|
+
* subscribed fields re-render. */
|
|
132
|
+
disabled: boolean;
|
|
133
|
+
}
|
|
134
|
+
type Options<T extends Record<string, any> = any> = {
|
|
135
|
+
initialValues?: T;
|
|
136
|
+
/** When fields are validated. Defaults to `'onSubmit'`. See
|
|
137
|
+
* {@link ValidationMode}. */
|
|
138
|
+
mode?: ValidationMode;
|
|
139
|
+
/** When a field is re-validated after it already has an error — it only
|
|
140
|
+
* takes effect once the field has an error. Defaults to `'onChange'`. See
|
|
141
|
+
* {@link ReValidateMode}. */
|
|
142
|
+
reValidateMode?: ReValidateMode;
|
|
143
|
+
/**
|
|
144
|
+
* Form-level validator. Returns a record of errors keyed by field path;
|
|
145
|
+
* nested objects are flattened ('a.b' style) and array values contribute
|
|
146
|
+
* every non-empty string they hold as separate errors (zod `flatten()`
|
|
147
|
+
* formErrors style). Schema adapters instead return a branded
|
|
148
|
+
* {@link ValidationOutcome}: `errors` flattens the same way, `values`
|
|
149
|
+
* (the schema's parsed output) becomes the form's parsedValues baseline
|
|
150
|
+
* that {@link getValues} layers over initialValues.
|
|
151
|
+
*/
|
|
152
|
+
validate?: (values: T) => ValidateResult<T>;
|
|
153
|
+
/** Start the form with every bound field disabled — the flag bound
|
|
154
|
+
* fields OR with their own `disabled` option (a field cannot opt out
|
|
155
|
+
* of a disabled form). Toggle later with {@link setDisabled}.
|
|
156
|
+
* Defaults to `false`. */
|
|
157
|
+
disabled?: boolean;
|
|
158
|
+
};
|
|
159
|
+
/**
|
|
160
|
+
* Create form instance
|
|
161
|
+
* @param options
|
|
162
|
+
* @return form instance
|
|
163
|
+
*/
|
|
164
|
+
declare function create<T extends Record<string, any> = any>(options?: Options<T>): Form<T>;
|
|
165
|
+
/**
|
|
166
|
+
* Get form values: the values Map layered over parsedValues (when a schema
|
|
167
|
+
* validation produced them) layered over initialValues.
|
|
168
|
+
*
|
|
169
|
+
* Merged with copy-on-write ownership tracking ({@link setOwned}): every
|
|
170
|
+
* distinct container on a written path is allocated once and shared by all
|
|
171
|
+
* paths through it, instead of re-copying the whole branch for every key.
|
|
172
|
+
* One owned set spans the whole merge, so containers borrowed from the
|
|
173
|
+
* parsedValues tree are copied before mutation exactly like initialValues
|
|
174
|
+
* ones. The result is still a freshly merged tree per call, with untouched
|
|
175
|
+
* branches sharing references with the baseline exactly like chained
|
|
176
|
+
* `set` did -- callers may treat it as their own copy.
|
|
177
|
+
*
|
|
178
|
+
* parsedValues is the schema's complete output tree: once validation
|
|
179
|
+
* succeeds it replaces the initialValues baseline (fields the schema
|
|
180
|
+
* dropped disappear), while live edits in the values Map still win over
|
|
181
|
+
* both. It never affects dirty state — {@link isDirty} and
|
|
182
|
+
* {@link getDirtyFields} compare live edits against initialValues only,
|
|
183
|
+
* because parsing is not a user edit.
|
|
184
|
+
*
|
|
185
|
+
* @param form
|
|
186
|
+
*/
|
|
187
|
+
declare function getValues<T extends Record<string, any> = any>(form: Form<T>): T;
|
|
188
|
+
/**
|
|
189
|
+
* Get field value
|
|
190
|
+
* @param form
|
|
191
|
+
* @param name
|
|
192
|
+
*/
|
|
193
|
+
declare function getValue<T extends Record<string, any> = any, P extends FieldPath<T> | Name = Name>(form: Form<T>, name: P): PathValueOf<T, P>;
|
|
194
|
+
/**
|
|
195
|
+
* Get field value by path
|
|
196
|
+
* @param form
|
|
197
|
+
* @param path
|
|
198
|
+
*/
|
|
199
|
+
declare function getValueByPath({ initialValues, parsedValues, values, deleted }: Form, path: Path): any;
|
|
200
|
+
/** Options accepted by {@link setValue} / {@link setValueByPath}. Every flag
|
|
201
|
+
* defaults to `false`; omitting the options object entirely keeps the plain
|
|
202
|
+
* set-value behavior (no validation, no touched marking). */
|
|
203
|
+
interface SetFieldOptions {
|
|
204
|
+
/** Run the field's registered validator (if any) after the value lands,
|
|
205
|
+
* same as triggering that single field. Defaults to `false`. */
|
|
206
|
+
shouldValidate?: boolean;
|
|
207
|
+
/** Mark the field as touched. Defaults to `false`. */
|
|
208
|
+
shouldTouch?: boolean;
|
|
209
|
+
/** Reserved for a future manual dirty marker. Dirty state is currently
|
|
210
|
+
* derived from comparing values against initialValues, so this flag is
|
|
211
|
+
* accepted but does nothing. Defaults to `false`. */
|
|
212
|
+
shouldDirty?: boolean;
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Set field value
|
|
216
|
+
* @param form
|
|
217
|
+
* @param name
|
|
218
|
+
* @param value
|
|
219
|
+
* @param options
|
|
220
|
+
*/
|
|
221
|
+
declare function setValue<T extends Record<string, any> = any, P extends FieldPath<T> | Name = Name>(form: Form<T>, name: P, value: PathValueOf<T, P>, options?: SetFieldOptions): void;
|
|
222
|
+
/**
|
|
223
|
+
* Set field value
|
|
224
|
+
* @param form
|
|
225
|
+
* @param path
|
|
226
|
+
* @param value
|
|
227
|
+
* @param options
|
|
228
|
+
*/
|
|
229
|
+
declare function setValueByPath(form: Form, path: Path, value: any, options?: SetFieldOptions): void;
|
|
230
|
+
/**
|
|
231
|
+
* Get field error
|
|
232
|
+
* @param form
|
|
233
|
+
* @param name
|
|
234
|
+
* @return FieldError object or undefined
|
|
235
|
+
*/
|
|
236
|
+
declare function getError<T extends Record<string, any> = any, P extends FieldPath<T> | Name = Name>(form: Form<T>, name: P): FieldError | undefined;
|
|
237
|
+
/**
|
|
238
|
+
* Get field error by path
|
|
239
|
+
* @param form
|
|
240
|
+
* @param path
|
|
241
|
+
* @return first FieldError of the field, or undefined
|
|
242
|
+
*/
|
|
243
|
+
declare function getErrorByPath({ errors }: Form, path: Path): FieldError | undefined;
|
|
244
|
+
/**
|
|
245
|
+
* Get all errors of a field
|
|
246
|
+
* @param form
|
|
247
|
+
* @param name
|
|
248
|
+
* @return every error registered for the field (insertion order); an empty
|
|
249
|
+
* array when the field has none
|
|
250
|
+
*/
|
|
251
|
+
declare function getFieldErrors<T extends Record<string, any> = any, P extends FieldPath<T> | Name = Name>(form: Form<T>, name: P): FieldError[];
|
|
252
|
+
/**
|
|
253
|
+
* Get all errors of a field by path
|
|
254
|
+
* @param form
|
|
255
|
+
* @param path
|
|
256
|
+
* @return every error registered for the field (insertion order); an empty
|
|
257
|
+
* array when the field has none
|
|
258
|
+
*/
|
|
259
|
+
declare function getFieldErrorsByPath({ errors }: Form, path: Path): FieldError[];
|
|
260
|
+
/**
|
|
261
|
+
* Get all errors
|
|
262
|
+
* @param form
|
|
263
|
+
* @return array of {path, type, message} entries, in insertion order; path
|
|
264
|
+
* is the user-facing dotted field path ('a.b', 'list.0'), and a
|
|
265
|
+
* field holding several errors contributes one entry per error
|
|
266
|
+
*/
|
|
267
|
+
declare function getErrors({ errors }: Form): FieldErrorEntry[];
|
|
268
|
+
/**
|
|
269
|
+
* Get first error message
|
|
270
|
+
* @param form
|
|
271
|
+
* @return first error's message string, or undefined when there are no errors
|
|
272
|
+
*/
|
|
273
|
+
declare function getFirstError({ errors }: Form): string | undefined;
|
|
274
|
+
/** Snapshot of one field's aggregated state, as {@link getFieldState}
|
|
275
|
+
* returns it. `errors` is the stored array shared with the form — treat it
|
|
276
|
+
* as read-only, like every {@link getFieldErrors} result. */
|
|
277
|
+
interface FieldState<T = any> {
|
|
278
|
+
value: T;
|
|
279
|
+
error: FieldError | undefined;
|
|
280
|
+
errors: FieldError[];
|
|
281
|
+
isDirty: boolean;
|
|
282
|
+
isTouched: boolean;
|
|
283
|
+
isValidating: boolean;
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Get one field's aggregated state: the layered value ({@link getValue}),
|
|
287
|
+
* the first error ({@link getError}) and every error ({@link
|
|
288
|
+
* getFieldErrors}), dirtiness, the touched flag, and whether a validator
|
|
289
|
+
* is in flight. `isDirty` applies the same per-field rule as {@link
|
|
290
|
+
* getDirtyFields}: a live value exists and differs from initialValues at
|
|
291
|
+
* that path (parsedValues never counts — parsing is not an edit).
|
|
292
|
+
*
|
|
293
|
+
* @param form
|
|
294
|
+
* @param name
|
|
295
|
+
*/
|
|
296
|
+
declare function getFieldState<T extends Record<string, any> = any, P extends FieldPath<T> | Name = Name>(form: Form<T>, name: P): FieldState<PathValueOf<T, P>>;
|
|
297
|
+
declare function unsetValidatingByPath({ emitter, validating }: Form, path: Path): void;
|
|
298
|
+
declare function setValidatingByPath({ emitter, validating }: Form, path: Path): void;
|
|
299
|
+
/**
|
|
300
|
+
* Set field error
|
|
301
|
+
* @param form
|
|
302
|
+
* @param name
|
|
303
|
+
* @param error string is normalized to {type: 'custom', message}; a
|
|
304
|
+
* FieldError object is stored as-is; an array holds several errors
|
|
305
|
+
* (falsy items dropped, strings normalized); undefined clears
|
|
306
|
+
*/
|
|
307
|
+
declare function setError<T extends Record<string, any> = any, P extends FieldPath<T> | Name = Name>(form: Form<T>, name: P, error: string | FieldError | (string | FieldError)[] | undefined): void;
|
|
308
|
+
/**
|
|
309
|
+
* Set field error
|
|
310
|
+
* @param form
|
|
311
|
+
* @param path
|
|
312
|
+
* @param error string is normalized to {type: 'custom', message}; a
|
|
313
|
+
* FieldError object is stored as-is; an array holds several errors
|
|
314
|
+
* (falsy items dropped, strings normalized); undefined clears
|
|
315
|
+
*/
|
|
316
|
+
declare function setErrorByPath({ emitter, errors }: Form, path: Path, error: string | FieldError | (string | FieldError)[] | undefined): void;
|
|
317
|
+
/**
|
|
318
|
+
* Clear errors
|
|
319
|
+
* @param form
|
|
320
|
+
* @param name a single path or a list of paths; omit to clear every error
|
|
321
|
+
*/
|
|
322
|
+
declare function clearErrors(form: Form, name?: Name | Name[]): void;
|
|
323
|
+
/**
|
|
324
|
+
* Set field touched state
|
|
325
|
+
* @param form
|
|
326
|
+
* @param name
|
|
327
|
+
*/
|
|
328
|
+
declare function setTouched(form: Form, name: Name): void;
|
|
329
|
+
/**
|
|
330
|
+
* Set field touched state
|
|
331
|
+
* @param form
|
|
332
|
+
* @param path
|
|
333
|
+
*/
|
|
334
|
+
declare function setTouchedByPath({ emitter, touched }: Form, path: Path): void;
|
|
335
|
+
/**
|
|
336
|
+
* Check if field has been touched
|
|
337
|
+
* @param form
|
|
338
|
+
* @param name
|
|
339
|
+
*/
|
|
340
|
+
declare function hasTouched<T extends Record<string, any> = any, P extends FieldPath<T> | Name = Name>(form: Form<T>, name: P): boolean;
|
|
341
|
+
/**
|
|
342
|
+
* Check if field has been touched
|
|
343
|
+
* @param form
|
|
344
|
+
* @param path
|
|
345
|
+
*/
|
|
346
|
+
declare function hasTouchedByPath({ touched }: Form, path: Path): boolean;
|
|
347
|
+
/**
|
|
348
|
+
* Is dirty -- any value differs from initialValues
|
|
349
|
+
* @param form
|
|
350
|
+
*/
|
|
351
|
+
declare function isDirty(form: Form): boolean;
|
|
352
|
+
/**
|
|
353
|
+
* Get dirty fields -- fields whose current value differs from initialValues.
|
|
354
|
+
* Keys are user-facing dotted paths ('a.b', 'a.0.c'), unlike the JSON array
|
|
355
|
+
* keys stored in the values Map.
|
|
356
|
+
* @param form
|
|
357
|
+
* @return object mapping each dirty field's dotted path to true; the same
|
|
358
|
+
* reference is returned until the dirty set actually changes
|
|
359
|
+
*/
|
|
360
|
+
declare function getDirtyFields(form: Form): Record<string, boolean>;
|
|
361
|
+
/**
|
|
362
|
+
* Get touched fields as user-facing dotted paths ('a.b', 'a.0.c'), unlike
|
|
363
|
+
* the JSON array keys stored in the touched Set.
|
|
364
|
+
* @param form
|
|
365
|
+
* @return array of touched fields' dotted paths
|
|
366
|
+
*/
|
|
367
|
+
declare function getTouchedFields({ touched }: Form): string[];
|
|
368
|
+
/**
|
|
369
|
+
* Is touched -- any field has been touched
|
|
370
|
+
* @param form
|
|
371
|
+
*/
|
|
372
|
+
declare function isTouched({ touched }: Form): boolean;
|
|
373
|
+
/**
|
|
374
|
+
* Remove field
|
|
375
|
+
* @param form
|
|
376
|
+
* @param name
|
|
377
|
+
*/
|
|
378
|
+
declare function removeField(form: Form, name: Name): void;
|
|
379
|
+
/**
|
|
380
|
+
* Remove field
|
|
381
|
+
* @param form
|
|
382
|
+
* @param path
|
|
383
|
+
*/
|
|
384
|
+
declare function removeFieldByPath(form: Form, { key, value: segments }: Path): void;
|
|
385
|
+
/**
|
|
386
|
+
* Set form initialValues
|
|
387
|
+
* @param form
|
|
388
|
+
* @param initialValues
|
|
389
|
+
*/
|
|
390
|
+
declare function setInitialValues(form: Form, initialValues: any): void;
|
|
391
|
+
/** Options accepted by {@link reset}. Every flag defaults to `false` —
|
|
392
|
+
* omitting the object (or any flag) keeps the plain full-reset behavior.
|
|
393
|
+
* Names mirror react-hook-form's reset options to ease migration. */
|
|
394
|
+
interface ResetOptions {
|
|
395
|
+
/** Keep the current values of fields that are dirty — differ from the
|
|
396
|
+
* pre-reset initialValues (the same rule {@link getDirtyFields} applies).
|
|
397
|
+
* Clean fields fall back to the new initialValues as usual. */
|
|
398
|
+
keepDirtyValues?: boolean;
|
|
399
|
+
/** Keep the touched set instead of clearing it. */
|
|
400
|
+
keepTouched?: boolean;
|
|
401
|
+
/** Keep field errors instead of clearing them. */
|
|
402
|
+
keepErrors?: boolean;
|
|
403
|
+
/** Keep the submitted flag (`isSubmitSuccessful`) instead of clearing
|
|
404
|
+
* it. */
|
|
405
|
+
keepIsSubmitted?: boolean;
|
|
406
|
+
/** Keep `submitCount` instead of resetting it to 0. */
|
|
407
|
+
keepSubmitCount?: boolean;
|
|
408
|
+
/** Keep `isSubmitting` instead of resetting it to false. */
|
|
409
|
+
keepIsSubmitting?: boolean;
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* Reset form
|
|
413
|
+
* @param form
|
|
414
|
+
* @param initialValues
|
|
415
|
+
* @param options keep-flags to preserve slices of state through the reset
|
|
416
|
+
*/
|
|
417
|
+
declare function reset(form: Form, initialValues?: any, options?: ResetOptions): void;
|
|
418
|
+
/** Options accepted by {@link resetField}. The flags default to `false`;
|
|
419
|
+
* `value` has no default — omitted, the field falls back to initialValues;
|
|
420
|
+
* provided, the explicit value becomes the live value with no fallback at
|
|
421
|
+
* all. Mirrors react-hook-form's resetField options (`value` plays their
|
|
422
|
+
* `defaultValue`'s role) to ease migration. */
|
|
423
|
+
interface ResetFieldOptions {
|
|
424
|
+
/** Keep the field's touched flag instead of clearing it. */
|
|
425
|
+
keepTouched?: boolean;
|
|
426
|
+
/** Keep the field's errors instead of clearing them. */
|
|
427
|
+
keepErrors?: boolean;
|
|
428
|
+
/** Explicit post-reset value for the field — never falls back to
|
|
429
|
+
* initialValues. */
|
|
430
|
+
value?: any;
|
|
431
|
+
}
|
|
432
|
+
/**
|
|
433
|
+
* Reset a single field: drop its live value (reads fall back to the
|
|
434
|
+
* baseline — initialValues, or the schema's parsed output when one
|
|
435
|
+
* exists, in which case the path is removed from parsedValues and the
|
|
436
|
+
* initial value pinned back so the field reads initialValues again),
|
|
437
|
+
* clear its touched flag and errors, and revive the path's removal
|
|
438
|
+
* tombstones — the inverse of {@link removeFieldByPath}. Other fields
|
|
439
|
+
* and the submission flags are untouched; see {@link reset} for the
|
|
440
|
+
* form-wide counterpart.
|
|
441
|
+
*
|
|
442
|
+
* @param form
|
|
443
|
+
* @param name
|
|
444
|
+
* @param options
|
|
445
|
+
*/
|
|
446
|
+
declare function resetField<T extends Record<string, any> = any, P extends FieldPath<T> | Name = Name>(form: Form<T>, name: P, options?: ResetFieldOptions): void;
|
|
447
|
+
/**
|
|
448
|
+
* @param form
|
|
449
|
+
*/
|
|
450
|
+
declare function hasErrors({ errors }: Form): boolean;
|
|
451
|
+
/**
|
|
452
|
+
* Trigger field validation.
|
|
453
|
+
*
|
|
454
|
+
* Without `name` every registered field validator runs. A single `name` —
|
|
455
|
+
* dotted string or segments array — runs only that field's validator, and
|
|
456
|
+
* an array of names runs each one in order. An empty array is a no-op, as
|
|
457
|
+
* is any name with no registered validator. An array argument counts as
|
|
458
|
+
* one segments path only when it mixes in numbers (`['items', 0]`); pure
|
|
459
|
+
* string arrays are name lists, so `['a', 'b']` triggers fields `a` and
|
|
460
|
+
* `b`, not the nested path `a.b`.
|
|
461
|
+
*
|
|
462
|
+
* The returned promise waits for the triggered validation to settle —
|
|
463
|
+
* async validators included — so their errors have already landed in
|
|
464
|
+
* `form.errors` when it resolves. It never rejects: landing errors is the
|
|
465
|
+
* expected outcome here, not a failure. Resolves `true` when the triggered
|
|
466
|
+
* scope is error-free, `false` otherwise. Without `name` the scope is all
|
|
467
|
+
* fields plus the form-level `validate` result (which runs after field
|
|
468
|
+
* validators settle, same pipeline as {@link ensureValidate}); with `name`
|
|
469
|
+
* only those fields' own errors count and form-level `validate` is
|
|
470
|
+
* skipped (RHF semantics).
|
|
471
|
+
*
|
|
472
|
+
* Fire-and-forget callers may ignore the promise: the validator kicks
|
|
473
|
+
* still happen synchronously, matching the pre-promise behavior.
|
|
474
|
+
*
|
|
475
|
+
* @param form
|
|
476
|
+
* @param name field name(s) to trigger, or all fields when omitted
|
|
477
|
+
* @return whether the triggered scope is error-free once validation settles
|
|
478
|
+
*/
|
|
479
|
+
declare function trigger(form: Form, name?: Name | Name[]): Promise<boolean>;
|
|
480
|
+
/**
|
|
481
|
+
* Validate and throw if any field error.
|
|
482
|
+
* @param form
|
|
483
|
+
* @return resolve if no error; reject and stop validate if has an error
|
|
484
|
+
*/
|
|
485
|
+
declare function ensureValidate(form: Form): Promise<void>;
|
|
486
|
+
/**
|
|
487
|
+
* Validate and return if any field error.
|
|
488
|
+
* @param form
|
|
489
|
+
* @return error message string or void
|
|
490
|
+
*/
|
|
491
|
+
declare function validate(form: Form): Promise<void | string>;
|
|
492
|
+
declare function setIsSubmitting(form: Form, value: boolean): void;
|
|
493
|
+
declare function incrementSubmitCount(form: Form): void;
|
|
494
|
+
declare function setSubmitSuccessful(form: Form, value: boolean): void;
|
|
495
|
+
/**
|
|
496
|
+
* Set the form-level disabled flag and emit a payload-less 'disabled'
|
|
497
|
+
* event — subscribed fields (useField and the components built on it)
|
|
498
|
+
* re-render with the merged disabled state: form flag || their own
|
|
499
|
+
* `disabled` option.
|
|
500
|
+
* @param form
|
|
501
|
+
* @param value
|
|
502
|
+
*/
|
|
503
|
+
declare function setDisabled(form: Form, value: boolean): void;
|
|
504
|
+
/** Submit callbacks for {@link handleSubmit}. All optional — a missing
|
|
505
|
+
* callback is simply skipped, matching the <Form> component semantics. */
|
|
506
|
+
interface HandleSubmitOptions<T extends Record<string, any> = any> {
|
|
507
|
+
/** Called after validation passes, before onValidSubmit. */
|
|
508
|
+
onSubmit?: (values: T, e?: any) => void | Promise<void>;
|
|
509
|
+
/** Called after validation passes, following a successful onSubmit. */
|
|
510
|
+
onValidSubmit?: (values: T, e?: any) => void | Promise<void>;
|
|
511
|
+
/**
|
|
512
|
+
* Called when validation fails.
|
|
513
|
+
* @param errors array of {path, type, message} entries in insertion
|
|
514
|
+
* order; path is the dotted field path ('a.b', 'list.0'), type is
|
|
515
|
+
* the error kind ('custom' for plain string errors, 'native' for
|
|
516
|
+
* failed DOM constraint validation), message is the display text
|
|
517
|
+
* @param values current form values
|
|
518
|
+
*/
|
|
519
|
+
onInvalidSubmit?: (errors: FieldErrorEntry[], values: T) => void;
|
|
520
|
+
/**
|
|
521
|
+
* Focus the first error field after a failed submit. Defaults to true —
|
|
522
|
+
* only an explicit `false` disables it. When custom validation fails,
|
|
523
|
+
* a 'focusError' event carrying the first error's path key is emitted
|
|
524
|
+
* on the form (bound fields such as <Field> subscribe and focus their
|
|
525
|
+
* input); when native constraint validation fails, the submitted
|
|
526
|
+
* form's first ':invalid' control is focused directly.
|
|
527
|
+
*/
|
|
528
|
+
shouldFocusError?: boolean;
|
|
529
|
+
}
|
|
530
|
+
/**
|
|
531
|
+
* Create an async submit handler for `form` — the headless counterpart of
|
|
532
|
+
* the <Form> component's onSubmit wiring.
|
|
533
|
+
*
|
|
534
|
+
* Behavior mirrors <Form> exactly: preventDefault when present, then the
|
|
535
|
+
* submit state machine (isSubmitting/submitCount/isSubmitSuccessful) runs
|
|
536
|
+
* around native constraint validation (via `e.currentTarget.checkValidity`,
|
|
537
|
+
* skipped when the target has no checkValidity — e.g. React Native or
|
|
538
|
+
* toolbar-button submits) and custom validators. Failed validation fires
|
|
539
|
+
* onInvalidSubmit with the flattened error entries; a passing submit runs
|
|
540
|
+
* onSubmit then onValidSubmit. Errors thrown by either are swallowed into
|
|
541
|
+
* isSubmitSuccessful=false rather than rejecting the returned promise.
|
|
542
|
+
* Failed validation also focuses the offending field (see
|
|
543
|
+
* {@link HandleSubmitOptions.shouldFocusError}).
|
|
544
|
+
*
|
|
545
|
+
* @param form form instance
|
|
546
|
+
* @param options submit callbacks
|
|
547
|
+
* @return async event handler, callable without an event object
|
|
548
|
+
*/
|
|
549
|
+
declare function handleSubmit<T extends Record<string, any> = any>(form: Form<T>, options?: HandleSubmitOptions<T>): (e?: {
|
|
550
|
+
preventDefault?: () => void;
|
|
551
|
+
currentTarget?: any;
|
|
552
|
+
}) => Promise<void>;
|
|
553
|
+
/** Options accepted by {@link setFocus}. All flags default to `false`. */
|
|
554
|
+
interface SetFocusOptions {
|
|
555
|
+
/** Select the field's text after focusing it. Bound fields call
|
|
556
|
+
* `select()` on their element; elements without one (custom `as`
|
|
557
|
+
* components) just focus. */
|
|
558
|
+
shouldSelect?: boolean;
|
|
559
|
+
}
|
|
560
|
+
/**
|
|
561
|
+
* Programmatically focus a bound field's element (e.g. the <Field>'s
|
|
562
|
+
* input).
|
|
563
|
+
*
|
|
564
|
+
* Rides the same 'focusError' event channel a failed handleSubmit uses to
|
|
565
|
+
* focus the first errored field: the payload is the target's path key,
|
|
566
|
+
* with the focus options as a second, backward-compatible argument (older
|
|
567
|
+
* subscribers declared with a single `key` parameter simply ignore it).
|
|
568
|
+
* Being event-driven, it is a silent no-op when the field is unmounted or
|
|
569
|
+
* nothing subscribes — unknown names never throw.
|
|
570
|
+
*
|
|
571
|
+
* @param form form instance
|
|
572
|
+
* @param name field name (dot path or segments path)
|
|
573
|
+
* @param options focus options
|
|
574
|
+
*/
|
|
575
|
+
declare function setFocus(form: Form, name: Name, options?: SetFocusOptions): void;
|
|
576
|
+
|
|
577
|
+
export { setIsSubmitting as $, clearErrors as A, setTouched as B, setTouchedByPath as C, hasTouched as D, hasTouchedByPath as E, isDirty as G, getDirtyFields as H, getTouchedFields as I, isTouched as J, removeField as K, removeFieldByPath as L, setInitialValues as M, reset as T, resetField as W, hasErrors as X, trigger as Y, ensureValidate as Z, validate as _, incrementSubmitCount as a0, setSubmitSuccessful as a1, setDisabled as a2, handleSubmit as a4, setFocus as a6, create as d, VALIDATION_OUTCOME as g, getValues as j, getValue as k, getValueByPath as l, setValueByPath as m, getError as n, getErrorByPath as o, getFieldErrors as p, getFieldErrorsByPath as q, getErrors as r, setValue as s, getFirstError as t, getFieldState as v, unsetValidatingByPath as w, setValidatingByPath as x, setError as y, setErrorByPath as z };
|
|
578
|
+
export type { FieldPath as F, Name as N, Options as O, PathValueOf as P, ResetOptions as Q, ReValidateMode as R, SetFieldOptions as S, ResetFieldOptions as U, ValidationMode as V, Form as a, HandleSubmitOptions as a3, SetFocusOptions as a5, FieldError as b, Path as c, PathValue as e, FieldErrorEntry as f, ValidationOutcome as h, ValidateResult as i, FieldState as u };
|