@formisch/preact 0.11.0 → 0.12.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 +1 -1
- package/dist/index.d.ts +477 -160
- package/dist/index.js +134 -120
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -56,7 +56,7 @@ export default function LoginPage() {
|
|
|
56
56
|
}
|
|
57
57
|
```
|
|
58
58
|
|
|
59
|
-
In addition, Formisch offers several functions (we call them "methods") that can be used to read and manipulate the form state. These include `focus`, `
|
|
59
|
+
In addition, Formisch offers several functions (we call them "methods") that can be used to read and manipulate the form state. These include `focus`, `getDeepErrorEntries`, `getDeepErrors`, `getDirtyInput`, `getDirtyPaths`, `getErrors`, `getInput`, `handleSubmit`, `insert`, `isDirty`, `isEdited`, `isTouched`, `isValid`, `move`, `pickDirty`, `remove`, `replace`, `reset`, `setErrors`, `setInput`, `submit`, `swap` and `validate`. These methods allow you to control the form programmatically.
|
|
60
60
|
|
|
61
61
|
## Comparison
|
|
62
62
|
|
package/dist/index.d.ts
CHANGED
|
@@ -4,6 +4,177 @@ import { FocusEventHandler, FormHTMLAttributes, GenericEventHandler, InputEventH
|
|
|
4
4
|
|
|
5
5
|
//#region ../../packages/core/dist/index.preact.d.ts
|
|
6
6
|
|
|
7
|
+
//#region src/types/utils/utils.d.ts
|
|
8
|
+
/**
|
|
9
|
+
* Checks if a type is `any`.
|
|
10
|
+
*/
|
|
11
|
+
type IsAny<T> = 0 extends 1 & T ? true : false;
|
|
12
|
+
/**
|
|
13
|
+
* Checks if a type is `never`.
|
|
14
|
+
*/
|
|
15
|
+
type IsNever<T> = [T] extends [never] ? true : false;
|
|
16
|
+
/**
|
|
17
|
+
* Constructs a type that is maybe a promise.
|
|
18
|
+
*/
|
|
19
|
+
type MaybePromise<T> = T | Promise<T>;
|
|
20
|
+
/**
|
|
21
|
+
* Makes all properties deeply optional.
|
|
22
|
+
*/
|
|
23
|
+
type DeepPartial<TValue> = TValue extends Record<PropertyKey, unknown> | readonly unknown[] ? { [TKey in keyof TValue]?: DeepPartial<TValue[TKey]> | undefined } : TValue | undefined;
|
|
24
|
+
/**
|
|
25
|
+
* Makes all value properties optional.
|
|
26
|
+
*
|
|
27
|
+
* Hint: For dynamic arrays, only plain objects and nested arrays have their
|
|
28
|
+
* values made optional. Primitives and class instances are kept as-is to avoid
|
|
29
|
+
* types like `(string | undefined)[]`.
|
|
30
|
+
*/
|
|
31
|
+
type PartialValues<TValue> = TValue extends readonly (infer TItem)[] ? number extends TValue["length"] ? (TItem extends Record<PropertyKey, unknown> | readonly unknown[] ? { [TKey in keyof TItem]: PartialValues<TItem[TKey]> } : TItem)[] : { [TKey in keyof TValue]: PartialValues<TValue[TKey]> } : TValue extends Record<PropertyKey, unknown> ? { [TKey in keyof TValue]: PartialValues<TValue[TKey]> } : TValue | undefined;
|
|
32
|
+
//#endregion
|
|
33
|
+
//#region src/types/path/path.d.ts
|
|
34
|
+
/**
|
|
35
|
+
* Path key type.
|
|
36
|
+
*/
|
|
37
|
+
type PathKey = string | number;
|
|
38
|
+
/**
|
|
39
|
+
* Path type.
|
|
40
|
+
*/
|
|
41
|
+
type Path = readonly PathKey[];
|
|
42
|
+
/**
|
|
43
|
+
* Required path type.
|
|
44
|
+
*/
|
|
45
|
+
type RequiredPath = readonly [PathKey, ...Path];
|
|
46
|
+
/**
|
|
47
|
+
* Extracts the exact keys of a tuple, array or object. Tuples return their
|
|
48
|
+
* literal numeric indices, dynamic arrays return `number`, objects return
|
|
49
|
+
* their `keyof` keys, and any other input returns `never`.
|
|
50
|
+
*/
|
|
51
|
+
type ExactKeysOf<TValue> = IsAny<TValue> extends true ? never : TValue extends readonly unknown[] ? number extends TValue["length"] ? number : { [TKey in keyof TValue]: TKey extends `${infer TIndex extends number}` ? TIndex : never }[number] : TValue extends Record<PropertyKey, unknown> ? keyof TValue & PathKey : never;
|
|
52
|
+
/**
|
|
53
|
+
* Returns the flat object of all indexable properties of `TValue`. For object
|
|
54
|
+
* unions, properties from every member are merged so that any single property
|
|
55
|
+
* is accessible. For primitives and other non-indexable types, the result is
|
|
56
|
+
* `{}`.
|
|
57
|
+
*
|
|
58
|
+
* Hint: This is necessary to make properties accessible across union members.
|
|
59
|
+
* By default, properties that do not exist in all union options are not
|
|
60
|
+
* accessible and result in "any" when accessed.
|
|
61
|
+
*/
|
|
62
|
+
type PropertiesOf<TValue> = { [TKey in ExactKeysOf<TValue>]: TValue extends Record<TKey, infer TItem> ? TItem : never };
|
|
63
|
+
/**
|
|
64
|
+
* Lazily evaluates only the first valid path segment based on the given value.
|
|
65
|
+
*/
|
|
66
|
+
type LazyPath<TValue, TPathToCheck extends Path, TValidPath extends Path = readonly []> = TPathToCheck extends readonly [] ? TValidPath : TPathToCheck extends readonly [infer TFirstKey extends ExactKeysOf<TValue>, ...infer TPathRest extends Path] ? LazyPath<Required<PropertiesOf<TValue>[TFirstKey]>, TPathRest, readonly [...TValidPath, TFirstKey]> : IsNever<ExactKeysOf<TValue>> extends false ? readonly [...TValidPath, ExactKeysOf<TValue>] : TValidPath;
|
|
67
|
+
/**
|
|
68
|
+
* Returns the path if valid, otherwise the first possible valid path based on
|
|
69
|
+
* the given value.
|
|
70
|
+
*/
|
|
71
|
+
type ValidPath<TValue, TPath extends RequiredPath> = TPath extends LazyPath<Required<TValue>, TPath> ? TPath : LazyPath<Required<TValue>, TPath>;
|
|
72
|
+
/**
|
|
73
|
+
* Detects whether the consuming project is configured with
|
|
74
|
+
* `exactOptionalPropertyTypes: true`.
|
|
75
|
+
*
|
|
76
|
+
* Hint: If `false` the built-in `Required<T>` strips `| undefined` from
|
|
77
|
+
* optional properties, so `Required<{ key?: undefined }>['key']` collapses
|
|
78
|
+
* to `never` — under strict mode the same expression yields `undefined`.
|
|
79
|
+
*/
|
|
80
|
+
type IsExactOptionalProps = Required<{
|
|
81
|
+
key?: undefined;
|
|
82
|
+
}>["key"] extends never ? false : true;
|
|
83
|
+
/**
|
|
84
|
+
* Like the built-in `Required<T>`, but preserves `| undefined` in two
|
|
85
|
+
* places where `Required<T>` strips it:
|
|
86
|
+
*
|
|
87
|
+
* 1. Optional property values under `exactOptionalPropertyTypes: false`
|
|
88
|
+
* — without this, input typings for `v.optional`/`v.nullish` schemas
|
|
89
|
+
* narrow incorrectly (issue #15).
|
|
90
|
+
* 2. Array/tuple element types — e.g. `(string | undefined)[]` stays
|
|
91
|
+
* `(string | undefined)[]` instead of becoming `string[]`. Arrays
|
|
92
|
+
* fall through unchanged because they only have a numeric index
|
|
93
|
+
* signature and don't structurally extend `Record<PropertyKey,
|
|
94
|
+
* unknown>` (which requires string keys).
|
|
95
|
+
*/
|
|
96
|
+
type ExactRequired<TValue> = TValue extends Record<PropertyKey, unknown> ? IsExactOptionalProps extends true ? Required<TValue> : { [TKey in keyof Required<TValue>]: TValue[TKey] } : TValue;
|
|
97
|
+
/**
|
|
98
|
+
* Extracts the value type at the given path.
|
|
99
|
+
*/
|
|
100
|
+
type PathValue<TValue, TPath extends Path> = TPath extends readonly [infer TKey, ...infer TRest extends Path] ? TKey extends ExactKeysOf<ExactRequired<TValue>> ? PathValue<PropertiesOf<ExactRequired<TValue>>[TKey], TRest> : unknown : TValue;
|
|
101
|
+
/**
|
|
102
|
+
* Checks whether a value is a dynamic array or contains one anywhere in its
|
|
103
|
+
* shape. A fixed-length tuple is not itself a dynamic array, but it counts when
|
|
104
|
+
* it contains one, so paths can still navigate through tuples to reach nested
|
|
105
|
+
* arrays.
|
|
106
|
+
*
|
|
107
|
+
* Hint: The inner conditionals (`TValue extends readonly unknown[]` and
|
|
108
|
+
* `TValue extends Record<PropertyKey, unknown>`) distribute over union members,
|
|
109
|
+
* so the inner expression returns the union of each member's result (e.g.
|
|
110
|
+
* `true | false` when some members contain arrays and others don't).
|
|
111
|
+
* Downstream code uses `IsOrHasArray<T> extends true`, but
|
|
112
|
+
* `boolean extends true` is `false` — so we collapse the result via
|
|
113
|
+
* `true extends ...`, which is `true` whenever at least one union member
|
|
114
|
+
* contributed `true`.
|
|
115
|
+
*/
|
|
116
|
+
type IsOrHasArray<TValue> = true extends (IsAny<TValue> extends true ? false : TValue extends readonly unknown[] ? number extends TValue["length"] ? true : IsOrHasArray<TValue[number]> : TValue extends Record<PropertyKey, unknown> ? { [TKey in keyof TValue]: IsOrHasArray<TValue[TKey]> }[keyof TValue] : false) ? true : false;
|
|
117
|
+
/**
|
|
118
|
+
* Extracts the exact keys of a tuple, array or object that contain arrays.
|
|
119
|
+
*/
|
|
120
|
+
type ExactKeysOfArrayPath<TValue> = IsAny<TValue> extends true ? never : TValue extends readonly (infer TItem)[] ? number extends TValue["length"] ? IsOrHasArray<TItem> extends true ? number : never : { [TKey in keyof TValue]: TKey extends `${infer TIndex extends number}` ? IsOrHasArray<NonNullable<TValue[TKey]>> extends true ? TIndex : never : never }[number] : TValue extends Record<PropertyKey, unknown> ? { [TKey in keyof TValue]: IsOrHasArray<NonNullable<TValue[TKey]>> extends true ? TKey : never }[keyof TValue] & PathKey : never;
|
|
121
|
+
/**
|
|
122
|
+
* Returns the flat object of indexable properties of `TValue` whose values
|
|
123
|
+
* are or contain arrays. Mirrors `PropertiesOf` but keyed by
|
|
124
|
+
* `ExactKeysOfArrayPath` so the lookup is provably valid for array-path
|
|
125
|
+
* navigation in `LazyArrayPath`.
|
|
126
|
+
*/
|
|
127
|
+
type PropertiesOfArrayPath<TValue> = { [TKey in ExactKeysOfArrayPath<TValue>]: TValue extends Record<TKey, infer TItem> ? TItem : never };
|
|
128
|
+
/**
|
|
129
|
+
* Lazily evaluates only the first valid array path segment based on the given value.
|
|
130
|
+
*/
|
|
131
|
+
type LazyArrayPath<TValue, TPathToCheck extends Path, TValidPath extends Path = readonly []> = TPathToCheck extends readonly [] ? TValue extends readonly unknown[] ? number extends TValue["length"] ? TValidPath : IsNever<ExactKeysOfArrayPath<TValue>> extends false ? readonly [...TValidPath, ExactKeysOfArrayPath<TValue>] : never : readonly [...TValidPath, ExactKeysOfArrayPath<TValue>] : TPathToCheck extends readonly [infer TFirstKey extends ExactKeysOfArrayPath<TValue>, ...infer TPathRest extends Path] ? LazyArrayPath<Required<PropertiesOfArrayPath<TValue>[TFirstKey]>, TPathRest, readonly [...TValidPath, TFirstKey]> : IsNever<ExactKeysOfArrayPath<TValue>> extends false ? readonly [...TValidPath, ExactKeysOfArrayPath<TValue>] : never;
|
|
132
|
+
/**
|
|
133
|
+
* Returns the path if valid, otherwise the first possible valid array path
|
|
134
|
+
* based on the given value.
|
|
135
|
+
*/
|
|
136
|
+
type ValidArrayPath<TValue, TPath extends RequiredPath> = TPath extends LazyArrayPath<Required<TValue>, TPath> ? TPath : LazyArrayPath<Required<TValue>, TPath>;
|
|
137
|
+
/**
|
|
138
|
+
* Recursive helper for `DirtyPath` that prepends `TKey` to each deeper path,
|
|
139
|
+
* or falls through to `never` when the child is not an object.
|
|
140
|
+
*/
|
|
141
|
+
type DeepDirtyPath<TChild, TKey$1 extends PathKey, TDepth extends 0[]> = TChild extends Record<PropertyKey, unknown> ? readonly [TKey$1, ...DirtyPath<TChild, [...TDepth, 0]>] : never;
|
|
142
|
+
/**
|
|
143
|
+
* Returns the union of all `RequiredPath`s that `getDirtyPaths` can emit
|
|
144
|
+
* for a given input type. Object fields contribute their own path and the
|
|
145
|
+
* paths of their descendants; arrays and tuples are atomic and contribute
|
|
146
|
+
* only their own path, because dirty arrays are returned as complete units.
|
|
147
|
+
*
|
|
148
|
+
* Narrowing is exact for the first 5 levels of nesting; deeper paths fall
|
|
149
|
+
* back to `RequiredPath` to keep the result a complete superset of any
|
|
150
|
+
* path the runtime can address.
|
|
151
|
+
*
|
|
152
|
+
* Hint: Arrays and tuples are atomic because they don't structurally
|
|
153
|
+
* extend `Record<PropertyKey, unknown>` and so fall through to `never`
|
|
154
|
+
* via `DeepDirtyPath` — no explicit array check is needed. `TDepth` is
|
|
155
|
+
* a tuple-length counter capped at 5 to bound TypeScript instantiation
|
|
156
|
+
* cost.
|
|
157
|
+
*/
|
|
158
|
+
type DirtyPath<TValue, TDepth extends 0[] = []> = TDepth["length"] extends 5 ? RequiredPath : TValue extends Record<PropertyKey, unknown> ? { [TKey in ExactKeysOf<TValue>]: readonly [TKey] | DeepDirtyPath<NonNullable<PropertiesOf<TValue>[TKey]>, TKey, TDepth> }[ExactKeysOf<TValue>] : never;
|
|
159
|
+
/**
|
|
160
|
+
* Recursive helper for `FieldPath` that prepends `TKey` to each deeper field
|
|
161
|
+
* path, or falls through to `never` when the child is a leaf value.
|
|
162
|
+
*/
|
|
163
|
+
type DeepFieldPath<TChild, TKey$1 extends PathKey, TDepth extends 0[]> = TChild extends readonly unknown[] | Record<PropertyKey, unknown> ? readonly [TKey$1, ...FieldPath<TChild, [...TDepth, 0]>] : never;
|
|
164
|
+
/**
|
|
165
|
+
* Returns the union of all `RequiredPath`s that address a field within the
|
|
166
|
+
* given input type. Object and array fields contribute their own path and the
|
|
167
|
+
* paths of their descendants; unlike `DirtyPath`, arrays are recursed into so
|
|
168
|
+
* that fields at any depth, including array items, can be addressed. Leaf
|
|
169
|
+
* values contribute only their own path (emitted by their parent).
|
|
170
|
+
*
|
|
171
|
+
* Narrowing is exact for the first 5 levels of nesting; deeper paths fall
|
|
172
|
+
* back to `RequiredPath` to keep the result a complete superset of any path
|
|
173
|
+
* the runtime can address. `TDepth` is a tuple-length counter capped at 5 to
|
|
174
|
+
* bound TypeScript instantiation cost.
|
|
175
|
+
*/
|
|
176
|
+
type FieldPath<TValue, TDepth extends 0[] = []> = TDepth["length"] extends 5 ? RequiredPath : TValue extends readonly unknown[] | Record<PropertyKey, unknown> ? { [TKey in ExactKeysOf<TValue>]: readonly [TKey] | DeepFieldPath<NonNullable<PropertiesOf<TValue>[TKey]>, TKey, TDepth> }[ExactKeysOf<TValue>] : never;
|
|
177
|
+
//#endregion
|
|
7
178
|
//#region src/types/schema/schema.d.ts
|
|
8
179
|
/**
|
|
9
180
|
* Schema type.
|
|
@@ -59,6 +230,10 @@ interface InternalBaseStore {
|
|
|
59
230
|
*/
|
|
60
231
|
name: string;
|
|
61
232
|
/**
|
|
233
|
+
* The path to the field.
|
|
234
|
+
*/
|
|
235
|
+
path: Path;
|
|
236
|
+
/**
|
|
62
237
|
* The schema of the field.
|
|
63
238
|
*/
|
|
64
239
|
schema: Schema;
|
|
@@ -86,6 +261,15 @@ interface InternalBaseStore {
|
|
|
86
261
|
*/
|
|
87
262
|
isTouched: Signal<boolean>;
|
|
88
263
|
/**
|
|
264
|
+
* The edited state of the field.
|
|
265
|
+
*
|
|
266
|
+
* Hint: Unlike `isTouched`, which is also set when a field is focused, this
|
|
267
|
+
* is only set when the field's value is changed. Unlike `isDirty`, it stays
|
|
268
|
+
* `true` even if the value is changed back to its initial value. It is only
|
|
269
|
+
* reset when the field is reset.
|
|
270
|
+
*/
|
|
271
|
+
isEdited: Signal<boolean>;
|
|
272
|
+
/**
|
|
89
273
|
* The dirty state of the field.
|
|
90
274
|
*/
|
|
91
275
|
isDirty: Signal<boolean>;
|
|
@@ -99,6 +283,14 @@ interface InternalArrayStore extends InternalBaseStore {
|
|
|
99
283
|
*/
|
|
100
284
|
kind: "array";
|
|
101
285
|
/**
|
|
286
|
+
* Whether the array schema is wrapped in a nullish schema.
|
|
287
|
+
*
|
|
288
|
+
* Hint: This indicates whether a missing input should be represented as the
|
|
289
|
+
* nullish value (`null`/`undefined`) or as a present but empty array
|
|
290
|
+
* (`true`). It keeps resetting consistent with the initial state.
|
|
291
|
+
*/
|
|
292
|
+
isNullish: boolean;
|
|
293
|
+
/**
|
|
102
294
|
* The children of the array field.
|
|
103
295
|
*/
|
|
104
296
|
children: InternalFieldStore[];
|
|
@@ -151,6 +343,14 @@ interface InternalObjectStore extends InternalBaseStore {
|
|
|
151
343
|
*/
|
|
152
344
|
kind: "object";
|
|
153
345
|
/**
|
|
346
|
+
* Whether the object schema is wrapped in a nullish schema.
|
|
347
|
+
*
|
|
348
|
+
* Hint: This indicates whether a missing input should be represented as the
|
|
349
|
+
* nullish value (`null`/`undefined`) or as a present but empty object
|
|
350
|
+
* (`true`). It keeps resetting consistent with the initial state.
|
|
351
|
+
*/
|
|
352
|
+
isNullish: boolean;
|
|
353
|
+
/**
|
|
154
354
|
* The children of the object field.
|
|
155
355
|
*/
|
|
156
356
|
children: Record<string, InternalFieldStore>;
|
|
@@ -214,32 +414,6 @@ type InternalFieldStore = InternalArrayStore | InternalObjectStore | InternalVal
|
|
|
214
414
|
*/
|
|
215
415
|
declare const INTERNAL: "~internal";
|
|
216
416
|
//#endregion
|
|
217
|
-
//#region src/types/utils/utils.d.ts
|
|
218
|
-
/**
|
|
219
|
-
* Checks if a type is `any`.
|
|
220
|
-
*/
|
|
221
|
-
type IsAny<T> = 0 extends 1 & T ? true : false;
|
|
222
|
-
/**
|
|
223
|
-
* Checks if a type is `never`.
|
|
224
|
-
*/
|
|
225
|
-
type IsNever<T> = [T] extends [never] ? true : false;
|
|
226
|
-
/**
|
|
227
|
-
* Constructs a type that is maybe a promise.
|
|
228
|
-
*/
|
|
229
|
-
type MaybePromise<T> = T | Promise<T>;
|
|
230
|
-
/**
|
|
231
|
-
* Makes all properties deeply optional.
|
|
232
|
-
*/
|
|
233
|
-
type DeepPartial<TValue> = TValue extends Record<PropertyKey, unknown> | readonly unknown[] ? { [TKey in keyof TValue]?: DeepPartial<TValue[TKey]> | undefined } : TValue | undefined;
|
|
234
|
-
/**
|
|
235
|
-
* Makes all value properties optional.
|
|
236
|
-
*
|
|
237
|
-
* Hint: For dynamic arrays, only plain objects and nested arrays have their
|
|
238
|
-
* values made optional. Primitives and class instances are kept as-is to avoid
|
|
239
|
-
* types like `(string | undefined)[]`.
|
|
240
|
-
*/
|
|
241
|
-
type PartialValues<TValue> = TValue extends readonly (infer TItem)[] ? number extends TValue["length"] ? (TItem extends Record<PropertyKey, unknown> | readonly unknown[] ? { [TKey in keyof TItem]: PartialValues<TItem[TKey]> } : TItem)[] : { [TKey in keyof TValue]: PartialValues<TValue[TKey]> } : TValue extends Record<PropertyKey, unknown> ? { [TKey in keyof TValue]: PartialValues<TValue[TKey]> } : TValue | undefined;
|
|
242
|
-
//#endregion
|
|
243
417
|
//#region src/types/form/form.d.ts
|
|
244
418
|
/**
|
|
245
419
|
* Validation mode type.
|
|
@@ -323,177 +497,138 @@ type SubmitHandler<TSchema extends FormSchema> = (output: v.InferOutput<TSchema>
|
|
|
323
497
|
*/
|
|
324
498
|
type SubmitEventHandler<TSchema extends FormSchema> = (output: v.InferOutput<TSchema>, event: SubmitEvent) => MaybePromise<unknown>;
|
|
325
499
|
//#endregion
|
|
326
|
-
//#region src/
|
|
327
|
-
/**
|
|
328
|
-
* Path key type.
|
|
329
|
-
*/
|
|
330
|
-
type PathKey = string | number;
|
|
331
|
-
/**
|
|
332
|
-
* Path type.
|
|
333
|
-
*/
|
|
334
|
-
type Path = readonly PathKey[];
|
|
500
|
+
//#region src/array/copyItemState/copyItemState.d.ts
|
|
335
501
|
/**
|
|
336
|
-
*
|
|
502
|
+
* Copies the deeply nested state (signal values) from one field store to
|
|
503
|
+
* another. This includes the `elements`, `errors`, `startInput`, `input`,
|
|
504
|
+
* `isTouched`, `isEdited`, `isDirty`, and for arrays `startItems` and `items`
|
|
505
|
+
* properties. Recursively walks through the field stores and copies all signal
|
|
506
|
+
* values.
|
|
507
|
+
*
|
|
508
|
+
* @param fromInternalFieldStore The source field store to copy from.
|
|
509
|
+
* @param toInternalFieldStore The destination field store to copy to.
|
|
337
510
|
*/
|
|
338
|
-
|
|
511
|
+
//#endregion
|
|
512
|
+
//#region ../../packages/methods/dist/index.preact.d.ts
|
|
513
|
+
//#region src/focus/focus.d.ts
|
|
514
|
+
|
|
339
515
|
/**
|
|
340
|
-
*
|
|
341
|
-
* literal numeric indices, dynamic arrays return `number`, objects return
|
|
342
|
-
* their `keyof` keys, and any other input returns `never`.
|
|
516
|
+
* Focus field config interface.
|
|
343
517
|
*/
|
|
344
|
-
|
|
518
|
+
interface FocusFieldConfig<TSchema extends FormSchema, TFieldPath extends RequiredPath> {
|
|
519
|
+
/**
|
|
520
|
+
* The path to the field to focus.
|
|
521
|
+
*/
|
|
522
|
+
readonly path: ValidPath<v.InferInput<TSchema>, TFieldPath>;
|
|
523
|
+
}
|
|
345
524
|
/**
|
|
346
|
-
*
|
|
347
|
-
*
|
|
348
|
-
*
|
|
349
|
-
* `{}`.
|
|
525
|
+
* Focuses the first focusable input element of a field. This is useful for
|
|
526
|
+
* programmatically setting focus to a specific field, such as after
|
|
527
|
+
* validation errors or user interactions.
|
|
350
528
|
*
|
|
351
|
-
*
|
|
352
|
-
*
|
|
353
|
-
* accessible and result in "any" when accessed.
|
|
529
|
+
* @param form The form store containing the field.
|
|
530
|
+
* @param config The focus field configuration.
|
|
354
531
|
*/
|
|
355
|
-
|
|
532
|
+
declare function focus<TSchema extends FormSchema, TFieldPath extends RequiredPath>(form: BaseFormStore<TSchema>, config: FocusFieldConfig<TSchema, TFieldPath>): void;
|
|
533
|
+
//#endregion
|
|
534
|
+
//#region src/getDeepErrorEntries/getDeepErrorEntries.d.ts
|
|
356
535
|
/**
|
|
357
|
-
*
|
|
536
|
+
* Deep error entry interface.
|
|
358
537
|
*/
|
|
359
|
-
|
|
538
|
+
interface DeepErrorEntry<TValue = unknown> {
|
|
539
|
+
/**
|
|
540
|
+
* The path to the field with errors, or an empty path for form-level errors.
|
|
541
|
+
*/
|
|
542
|
+
readonly path: unknown extends TValue ? Path : readonly [] | FieldPath<TValue>;
|
|
543
|
+
/**
|
|
544
|
+
* The error messages of the field.
|
|
545
|
+
*/
|
|
546
|
+
readonly errors: [string, ...string[]];
|
|
547
|
+
}
|
|
360
548
|
/**
|
|
361
|
-
*
|
|
362
|
-
* the given value.
|
|
549
|
+
* Get form deep error entries config interface.
|
|
363
550
|
*/
|
|
364
|
-
|
|
551
|
+
interface GetFormDeepErrorEntriesConfig {
|
|
552
|
+
/**
|
|
553
|
+
* The path to a field. Leave undefined to get the entries of the entire form.
|
|
554
|
+
*/
|
|
555
|
+
readonly path?: undefined;
|
|
556
|
+
}
|
|
365
557
|
/**
|
|
366
|
-
*
|
|
367
|
-
* `exactOptionalPropertyTypes: true`.
|
|
368
|
-
*
|
|
369
|
-
* Hint: If `false` the built-in `Required<T>` strips `| undefined` from
|
|
370
|
-
* optional properties, so `Required<{ key?: undefined }>['key']` collapses
|
|
371
|
-
* to `never` — under strict mode the same expression yields `undefined`.
|
|
558
|
+
* Get field deep error entries config interface.
|
|
372
559
|
*/
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
560
|
+
interface GetFieldDeepErrorEntriesConfig<TSchema extends FormSchema, TFieldPath extends RequiredPath> {
|
|
561
|
+
/**
|
|
562
|
+
* The path to the field to retrieve the entries from.
|
|
563
|
+
*/
|
|
564
|
+
readonly path: ValidPath<v.InferInput<TSchema>, TFieldPath>;
|
|
565
|
+
}
|
|
376
566
|
/**
|
|
377
|
-
*
|
|
378
|
-
*
|
|
567
|
+
* Retrieves the errors of a specific field or the entire form as a list of
|
|
568
|
+
* entries, each pairing the path to a field with its error messages. This is
|
|
569
|
+
* useful for building custom error summaries that link each message back to
|
|
570
|
+
* its field. Form-level errors are included with an empty path.
|
|
379
571
|
*
|
|
380
|
-
*
|
|
381
|
-
* — without this, input typings for `v.optional`/`v.nullish` schemas
|
|
382
|
-
* narrow incorrectly (issue #15).
|
|
383
|
-
* 2. Array/tuple element types — e.g. `(string | undefined)[]` stays
|
|
384
|
-
* `(string | undefined)[]` instead of becoming `string[]`. Arrays
|
|
385
|
-
* fall through unchanged because they only have a numeric index
|
|
386
|
-
* signature and don't structurally extend `Record<PropertyKey,
|
|
387
|
-
* unknown>` (which requires string keys).
|
|
388
|
-
*/
|
|
389
|
-
type ExactRequired<TValue> = TValue extends Record<PropertyKey, unknown> ? IsExactOptionalProps extends true ? Required<TValue> : { [TKey in keyof Required<TValue>]: TValue[TKey] } : TValue;
|
|
390
|
-
/**
|
|
391
|
-
* Extracts the value type at the given path.
|
|
392
|
-
*/
|
|
393
|
-
type PathValue<TValue, TPath extends Path> = TPath extends readonly [infer TKey, ...infer TRest extends Path] ? TKey extends ExactKeysOf<ExactRequired<TValue>> ? PathValue<PropertiesOf<ExactRequired<TValue>>[TKey], TRest> : unknown : TValue;
|
|
394
|
-
/**
|
|
395
|
-
* Checks whether a value is a dynamic array or contains one anywhere in its
|
|
396
|
-
* shape. A fixed-length tuple is not itself a dynamic array, but it counts when
|
|
397
|
-
* it contains one, so paths can still navigate through tuples to reach nested
|
|
398
|
-
* arrays.
|
|
572
|
+
* @param form The form store to retrieve error entries from.
|
|
399
573
|
*
|
|
400
|
-
*
|
|
401
|
-
* `TValue extends Record<PropertyKey, unknown>`) distribute over union members,
|
|
402
|
-
* so the inner expression returns the union of each member's result (e.g.
|
|
403
|
-
* `true | false` when some members contain arrays and others don't).
|
|
404
|
-
* Downstream code uses `IsOrHasArray<T> extends true`, but
|
|
405
|
-
* `boolean extends true` is `false` — so we collapse the result via
|
|
406
|
-
* `true extends ...`, which is `true` whenever at least one union member
|
|
407
|
-
* contributed `true`.
|
|
574
|
+
* @returns A list of path and error message entries.
|
|
408
575
|
*/
|
|
409
|
-
|
|
410
|
-
/**
|
|
411
|
-
* Extracts the exact keys of a tuple, array or object that contain arrays.
|
|
412
|
-
*/
|
|
413
|
-
type ExactKeysOfArrayPath<TValue> = IsAny<TValue> extends true ? never : TValue extends readonly (infer TItem)[] ? number extends TValue["length"] ? IsOrHasArray<TItem> extends true ? number : never : { [TKey in keyof TValue]: TKey extends `${infer TIndex extends number}` ? IsOrHasArray<NonNullable<TValue[TKey]>> extends true ? TIndex : never : never }[number] : TValue extends Record<PropertyKey, unknown> ? { [TKey in keyof TValue]: IsOrHasArray<NonNullable<TValue[TKey]>> extends true ? TKey : never }[keyof TValue] & PathKey : never;
|
|
414
|
-
/**
|
|
415
|
-
* Returns the flat object of indexable properties of `TValue` whose values
|
|
416
|
-
* are or contain arrays. Mirrors `PropertiesOf` but keyed by
|
|
417
|
-
* `ExactKeysOfArrayPath` so the lookup is provably valid for array-path
|
|
418
|
-
* navigation in `LazyArrayPath`.
|
|
419
|
-
*/
|
|
420
|
-
type PropertiesOfArrayPath<TValue> = { [TKey in ExactKeysOfArrayPath<TValue>]: TValue extends Record<TKey, infer TItem> ? TItem : never };
|
|
421
|
-
/**
|
|
422
|
-
* Lazily evaluates only the first valid array path segment based on the given value.
|
|
423
|
-
*/
|
|
424
|
-
type LazyArrayPath<TValue, TPathToCheck extends Path, TValidPath extends Path = readonly []> = TPathToCheck extends readonly [] ? TValue extends readonly unknown[] ? number extends TValue["length"] ? TValidPath : IsNever<ExactKeysOfArrayPath<TValue>> extends false ? readonly [...TValidPath, ExactKeysOfArrayPath<TValue>] : never : readonly [...TValidPath, ExactKeysOfArrayPath<TValue>] : TPathToCheck extends readonly [infer TFirstKey extends ExactKeysOfArrayPath<TValue>, ...infer TPathRest extends Path] ? LazyArrayPath<Required<PropertiesOfArrayPath<TValue>[TFirstKey]>, TPathRest, readonly [...TValidPath, TFirstKey]> : IsNever<ExactKeysOfArrayPath<TValue>> extends false ? readonly [...TValidPath, ExactKeysOfArrayPath<TValue>] : never;
|
|
576
|
+
declare function getDeepErrorEntries<TSchema extends FormSchema>(form: BaseFormStore<TSchema>): DeepErrorEntry<v.InferInput<TSchema>>[];
|
|
425
577
|
/**
|
|
426
|
-
*
|
|
427
|
-
*
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
/**
|
|
431
|
-
* Recursive helper for `DirtyPath` that prepends `TKey` to each deeper path,
|
|
432
|
-
* or falls through to `never` when the child is not an object.
|
|
433
|
-
*/
|
|
434
|
-
type DeepDirtyPath<TChild, TKey$1 extends PathKey, TDepth extends 0[]> = TChild extends Record<PropertyKey, unknown> ? readonly [TKey$1, ...DirtyPath<TChild, [...TDepth, 0]>] : never;
|
|
435
|
-
/**
|
|
436
|
-
* Returns the union of all `RequiredPath`s that `getDirtyPaths` can emit
|
|
437
|
-
* for a given input type. Object fields contribute their own path and the
|
|
438
|
-
* paths of their descendants; arrays and tuples are atomic and contribute
|
|
439
|
-
* only their own path, because dirty arrays are returned as complete units.
|
|
578
|
+
* Retrieves the errors of a specific field or the entire form as a list of
|
|
579
|
+
* entries, each pairing the path to a field with its error messages. This is
|
|
580
|
+
* useful for building custom error summaries that link each message back to
|
|
581
|
+
* its field. Form-level errors are included with an empty path.
|
|
440
582
|
*
|
|
441
|
-
*
|
|
442
|
-
*
|
|
443
|
-
* path the runtime can address.
|
|
583
|
+
* @param form The form store to retrieve error entries from.
|
|
584
|
+
* @param config The get deep error entries configuration.
|
|
444
585
|
*
|
|
445
|
-
*
|
|
446
|
-
* extend `Record<PropertyKey, unknown>` and so fall through to `never`
|
|
447
|
-
* via `DeepDirtyPath` — no explicit array check is needed. `TDepth` is
|
|
448
|
-
* a tuple-length counter capped at 5 to bound TypeScript instantiation
|
|
449
|
-
* cost.
|
|
586
|
+
* @returns A list of path and error message entries.
|
|
450
587
|
*/
|
|
451
|
-
|
|
588
|
+
declare function getDeepErrorEntries<TSchema extends FormSchema, TFieldPath extends RequiredPath | undefined = undefined>(form: BaseFormStore<TSchema>, config: TFieldPath extends RequiredPath ? GetFieldDeepErrorEntriesConfig<TSchema, TFieldPath> : GetFormDeepErrorEntriesConfig): DeepErrorEntry<v.InferInput<TSchema>>[];
|
|
452
589
|
//#endregion
|
|
453
|
-
//#region src/
|
|
590
|
+
//#region src/getDeepErrors/getDeepErrors.d.ts
|
|
454
591
|
/**
|
|
455
|
-
*
|
|
456
|
-
* another. This includes the `elements`, `errors`, `startInput`, `input`,
|
|
457
|
-
* `isTouched`, `isDirty`, and for arrays `startItems` and `items` properties.
|
|
458
|
-
* Recursively walks through the field stores and copies all signal values.
|
|
459
|
-
*
|
|
460
|
-
* @param fromInternalFieldStore The source field store to copy from.
|
|
461
|
-
* @param toInternalFieldStore The destination field store to copy to.
|
|
592
|
+
* Get form deep errors config interface.
|
|
462
593
|
*/
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
594
|
+
interface GetFormDeepErrorsConfig {
|
|
595
|
+
/**
|
|
596
|
+
* The path to a field. Leave undefined to get the errors of the entire form.
|
|
597
|
+
*/
|
|
598
|
+
readonly path?: undefined;
|
|
599
|
+
}
|
|
467
600
|
/**
|
|
468
|
-
*
|
|
601
|
+
* Get field deep errors config interface.
|
|
469
602
|
*/
|
|
470
|
-
interface
|
|
603
|
+
interface GetFieldDeepErrorsConfig<TSchema extends FormSchema, TFieldPath extends RequiredPath> {
|
|
471
604
|
/**
|
|
472
|
-
* The path to the field to
|
|
605
|
+
* The path to the field to retrieve the errors from.
|
|
473
606
|
*/
|
|
474
607
|
readonly path: ValidPath<v.InferInput<TSchema>, TFieldPath>;
|
|
475
608
|
}
|
|
476
609
|
/**
|
|
477
|
-
*
|
|
478
|
-
*
|
|
479
|
-
* validation errors or
|
|
610
|
+
* Retrieves all error messages of a specific field or the entire form by
|
|
611
|
+
* walking through the field store and all its descendants. This is useful for
|
|
612
|
+
* displaying a summary of all validation errors within a section or the whole
|
|
613
|
+
* form. Form-level errors are included.
|
|
480
614
|
*
|
|
481
|
-
* @param form The form store
|
|
482
|
-
*
|
|
615
|
+
* @param form The form store to retrieve errors from.
|
|
616
|
+
*
|
|
617
|
+
* @returns A non-empty array of error messages, or null if no errors exist.
|
|
483
618
|
*/
|
|
484
|
-
declare function
|
|
485
|
-
//#endregion
|
|
486
|
-
//#region src/getAllErrors/getAllErrors.d.ts
|
|
619
|
+
declare function getDeepErrors<TSchema extends FormSchema>(form: BaseFormStore<TSchema>): [string, ...string[]] | null;
|
|
487
620
|
/**
|
|
488
|
-
* Retrieves all error messages
|
|
489
|
-
* the
|
|
490
|
-
* validation errors
|
|
621
|
+
* Retrieves all error messages of a specific field or the entire form by
|
|
622
|
+
* walking through the field store and all its descendants. This is useful for
|
|
623
|
+
* displaying a summary of all validation errors within a section or the whole
|
|
624
|
+
* form. Form-level errors are included.
|
|
491
625
|
*
|
|
492
626
|
* @param form The form store to retrieve errors from.
|
|
627
|
+
* @param config The get deep errors configuration.
|
|
493
628
|
*
|
|
494
629
|
* @returns A non-empty array of error messages, or null if no errors exist.
|
|
495
630
|
*/
|
|
496
|
-
declare function
|
|
631
|
+
declare function getDeepErrors<TSchema extends FormSchema, TFieldPath extends RequiredPath | undefined = undefined>(form: BaseFormStore<TSchema>, config: TFieldPath extends RequiredPath ? GetFieldDeepErrorsConfig<TSchema, TFieldPath> : GetFormDeepErrorsConfig): [string, ...string[]] | null;
|
|
497
632
|
//#endregion
|
|
498
633
|
//#region src/getDirtyInput/getDirtyInput.d.ts
|
|
499
634
|
/**
|
|
@@ -713,6 +848,172 @@ interface InsertConfig<TSchema extends FormSchema, TFieldArrayPath extends Requi
|
|
|
713
848
|
*/
|
|
714
849
|
declare function insert<TSchema extends FormSchema, TFieldArrayPath extends RequiredPath>(form: BaseFormStore<TSchema>, config: InsertConfig<TSchema, TFieldArrayPath>): void;
|
|
715
850
|
//#endregion
|
|
851
|
+
//#region src/isDirty/isDirty.d.ts
|
|
852
|
+
/**
|
|
853
|
+
* Is form dirty config interface.
|
|
854
|
+
*/
|
|
855
|
+
interface IsFormDirtyConfig {
|
|
856
|
+
/**
|
|
857
|
+
* The path to a field. Leave undefined to check the entire form.
|
|
858
|
+
*/
|
|
859
|
+
readonly path?: undefined;
|
|
860
|
+
}
|
|
861
|
+
/**
|
|
862
|
+
* Is field dirty config interface.
|
|
863
|
+
*/
|
|
864
|
+
interface IsFieldDirtyConfig<TSchema extends FormSchema, TFieldPath extends RequiredPath> {
|
|
865
|
+
/**
|
|
866
|
+
* The path to the field to check for dirtiness.
|
|
867
|
+
*/
|
|
868
|
+
readonly path: ValidPath<v.InferInput<TSchema>, TFieldPath>;
|
|
869
|
+
}
|
|
870
|
+
/**
|
|
871
|
+
* Checks whether a specific field or the entire form is dirty by walking
|
|
872
|
+
* through the field store and all its descendants. A field is dirty when its
|
|
873
|
+
* input differs from its initial value.
|
|
874
|
+
*
|
|
875
|
+
* @param form The form store to check for dirtiness.
|
|
876
|
+
*
|
|
877
|
+
* @returns Whether the field or form is dirty.
|
|
878
|
+
*/
|
|
879
|
+
declare function isDirty<TSchema extends FormSchema>(form: BaseFormStore<TSchema>): boolean;
|
|
880
|
+
/**
|
|
881
|
+
* Checks whether a specific field or the entire form is dirty by walking
|
|
882
|
+
* through the field store and all its descendants. A field is dirty when its
|
|
883
|
+
* input differs from its initial value.
|
|
884
|
+
*
|
|
885
|
+
* @param form The form store to check for dirtiness.
|
|
886
|
+
* @param config The is dirty configuration.
|
|
887
|
+
*
|
|
888
|
+
* @returns Whether the field or form is dirty.
|
|
889
|
+
*/
|
|
890
|
+
declare function isDirty<TSchema extends FormSchema, TFieldPath extends RequiredPath | undefined = undefined>(form: BaseFormStore<TSchema>, config: TFieldPath extends RequiredPath ? IsFieldDirtyConfig<TSchema, TFieldPath> : IsFormDirtyConfig): boolean;
|
|
891
|
+
//#endregion
|
|
892
|
+
//#region src/isEdited/isEdited.d.ts
|
|
893
|
+
/**
|
|
894
|
+
* Is form edited config interface.
|
|
895
|
+
*/
|
|
896
|
+
interface IsFormEditedConfig {
|
|
897
|
+
/**
|
|
898
|
+
* The path to a field. Leave undefined to check the entire form.
|
|
899
|
+
*/
|
|
900
|
+
readonly path?: undefined;
|
|
901
|
+
}
|
|
902
|
+
/**
|
|
903
|
+
* Is field edited config interface.
|
|
904
|
+
*/
|
|
905
|
+
interface IsFieldEditedConfig<TSchema extends FormSchema, TFieldPath extends RequiredPath> {
|
|
906
|
+
/**
|
|
907
|
+
* The path to the field to check for being edited.
|
|
908
|
+
*/
|
|
909
|
+
readonly path: ValidPath<v.InferInput<TSchema>, TFieldPath>;
|
|
910
|
+
}
|
|
911
|
+
/**
|
|
912
|
+
* Checks whether a specific field or the entire form is edited by walking
|
|
913
|
+
* through the field store and all its descendants. A field is edited once its
|
|
914
|
+
* value has been changed by the user.
|
|
915
|
+
*
|
|
916
|
+
* @param form The form store to check for being edited.
|
|
917
|
+
*
|
|
918
|
+
* @returns Whether the field or form is edited.
|
|
919
|
+
*/
|
|
920
|
+
declare function isEdited<TSchema extends FormSchema>(form: BaseFormStore<TSchema>): boolean;
|
|
921
|
+
/**
|
|
922
|
+
* Checks whether a specific field or the entire form is edited by walking
|
|
923
|
+
* through the field store and all its descendants. A field is edited once its
|
|
924
|
+
* value has been changed by the user.
|
|
925
|
+
*
|
|
926
|
+
* @param form The form store to check for being edited.
|
|
927
|
+
* @param config The is edited configuration.
|
|
928
|
+
*
|
|
929
|
+
* @returns Whether the field or form is edited.
|
|
930
|
+
*/
|
|
931
|
+
declare function isEdited<TSchema extends FormSchema, TFieldPath extends RequiredPath | undefined = undefined>(form: BaseFormStore<TSchema>, config: TFieldPath extends RequiredPath ? IsFieldEditedConfig<TSchema, TFieldPath> : IsFormEditedConfig): boolean;
|
|
932
|
+
//#endregion
|
|
933
|
+
//#region src/isTouched/isTouched.d.ts
|
|
934
|
+
/**
|
|
935
|
+
* Is form touched config interface.
|
|
936
|
+
*/
|
|
937
|
+
interface IsFormTouchedConfig {
|
|
938
|
+
/**
|
|
939
|
+
* The path to a field. Leave undefined to check the entire form.
|
|
940
|
+
*/
|
|
941
|
+
readonly path?: undefined;
|
|
942
|
+
}
|
|
943
|
+
/**
|
|
944
|
+
* Is field touched config interface.
|
|
945
|
+
*/
|
|
946
|
+
interface IsFieldTouchedConfig<TSchema extends FormSchema, TFieldPath extends RequiredPath> {
|
|
947
|
+
/**
|
|
948
|
+
* The path to the field to check for being touched.
|
|
949
|
+
*/
|
|
950
|
+
readonly path: ValidPath<v.InferInput<TSchema>, TFieldPath>;
|
|
951
|
+
}
|
|
952
|
+
/**
|
|
953
|
+
* Checks whether a specific field or the entire form is touched by walking
|
|
954
|
+
* through the field store and all its descendants. A field is touched once it
|
|
955
|
+
* has received and lost focus.
|
|
956
|
+
*
|
|
957
|
+
* @param form The form store to check for being touched.
|
|
958
|
+
*
|
|
959
|
+
* @returns Whether the field or form is touched.
|
|
960
|
+
*/
|
|
961
|
+
declare function isTouched<TSchema extends FormSchema>(form: BaseFormStore<TSchema>): boolean;
|
|
962
|
+
/**
|
|
963
|
+
* Checks whether a specific field or the entire form is touched by walking
|
|
964
|
+
* through the field store and all its descendants. A field is touched once it
|
|
965
|
+
* has received and lost focus.
|
|
966
|
+
*
|
|
967
|
+
* @param form The form store to check for being touched.
|
|
968
|
+
* @param config The is touched configuration.
|
|
969
|
+
*
|
|
970
|
+
* @returns Whether the field or form is touched.
|
|
971
|
+
*/
|
|
972
|
+
declare function isTouched<TSchema extends FormSchema, TFieldPath extends RequiredPath | undefined = undefined>(form: BaseFormStore<TSchema>, config: TFieldPath extends RequiredPath ? IsFieldTouchedConfig<TSchema, TFieldPath> : IsFormTouchedConfig): boolean;
|
|
973
|
+
//#endregion
|
|
974
|
+
//#region src/isValid/isValid.d.ts
|
|
975
|
+
/**
|
|
976
|
+
* Is form valid config interface.
|
|
977
|
+
*/
|
|
978
|
+
interface IsFormValidConfig {
|
|
979
|
+
/**
|
|
980
|
+
* The path to a field. Leave undefined to check the entire form.
|
|
981
|
+
*/
|
|
982
|
+
readonly path?: undefined;
|
|
983
|
+
}
|
|
984
|
+
/**
|
|
985
|
+
* Is field valid config interface.
|
|
986
|
+
*/
|
|
987
|
+
interface IsFieldValidConfig<TSchema extends FormSchema, TFieldPath extends RequiredPath> {
|
|
988
|
+
/**
|
|
989
|
+
* The path to the field to check for validity.
|
|
990
|
+
*/
|
|
991
|
+
readonly path: ValidPath<v.InferInput<TSchema>, TFieldPath>;
|
|
992
|
+
}
|
|
993
|
+
/**
|
|
994
|
+
* Checks whether a specific field or the entire form is valid by walking
|
|
995
|
+
* through the field store and all its descendants. A field is valid when
|
|
996
|
+
* neither it nor any of its descendants contains an error. Form-level errors
|
|
997
|
+
* are included.
|
|
998
|
+
*
|
|
999
|
+
* @param form The form store to check for validity.
|
|
1000
|
+
*
|
|
1001
|
+
* @returns Whether the field or form is valid.
|
|
1002
|
+
*/
|
|
1003
|
+
declare function isValid<TSchema extends FormSchema>(form: BaseFormStore<TSchema>): boolean;
|
|
1004
|
+
/**
|
|
1005
|
+
* Checks whether a specific field or the entire form is valid by walking
|
|
1006
|
+
* through the field store and all its descendants. A field is valid when
|
|
1007
|
+
* neither it nor any of its descendants contains an error. Form-level errors
|
|
1008
|
+
* are included.
|
|
1009
|
+
*
|
|
1010
|
+
* @param form The form store to check for validity.
|
|
1011
|
+
* @param config The is valid configuration.
|
|
1012
|
+
*
|
|
1013
|
+
* @returns Whether the field or form is valid.
|
|
1014
|
+
*/
|
|
1015
|
+
declare function isValid<TSchema extends FormSchema, TFieldPath extends RequiredPath | undefined = undefined>(form: BaseFormStore<TSchema>, config: TFieldPath extends RequiredPath ? IsFieldValidConfig<TSchema, TFieldPath> : IsFormValidConfig): boolean;
|
|
1016
|
+
//#endregion
|
|
716
1017
|
//#region src/move/move.d.ts
|
|
717
1018
|
/**
|
|
718
1019
|
* Move array field config interface.
|
|
@@ -828,6 +1129,10 @@ interface ResetBaseConfig {
|
|
|
828
1129
|
*/
|
|
829
1130
|
readonly keepTouched?: boolean | undefined;
|
|
830
1131
|
/**
|
|
1132
|
+
* Whether to keep the edited state during reset. Defaults to false.
|
|
1133
|
+
*/
|
|
1134
|
+
readonly keepEdited?: boolean | undefined;
|
|
1135
|
+
/**
|
|
831
1136
|
* Whether to keep the error messages during reset. Defaults to false.
|
|
832
1137
|
*/
|
|
833
1138
|
readonly keepErrors?: boolean | undefined;
|
|
@@ -1078,6 +1383,10 @@ interface FieldStore<TSchema extends FormSchema = FormSchema, TFieldPath extends
|
|
|
1078
1383
|
*/
|
|
1079
1384
|
readonly isTouched: ReadonlySignal<boolean>;
|
|
1080
1385
|
/**
|
|
1386
|
+
* Whether the field value has been edited.
|
|
1387
|
+
*/
|
|
1388
|
+
readonly isEdited: ReadonlySignal<boolean>;
|
|
1389
|
+
/**
|
|
1081
1390
|
* Whether the field input differs from its initial value.
|
|
1082
1391
|
*/
|
|
1083
1392
|
readonly isDirty: ReadonlySignal<boolean>;
|
|
@@ -1115,6 +1424,10 @@ interface FieldArrayStore<TSchema extends FormSchema = FormSchema, TFieldArrayPa
|
|
|
1115
1424
|
*/
|
|
1116
1425
|
readonly isTouched: ReadonlySignal<boolean>;
|
|
1117
1426
|
/**
|
|
1427
|
+
* Whether the field array value has been edited.
|
|
1428
|
+
*/
|
|
1429
|
+
readonly isEdited: ReadonlySignal<boolean>;
|
|
1430
|
+
/**
|
|
1118
1431
|
* Whether the field array input differs from its initial value.
|
|
1119
1432
|
*/
|
|
1120
1433
|
readonly isDirty: ReadonlySignal<boolean>;
|
|
@@ -1146,6 +1459,10 @@ interface FormStore<TSchema extends FormSchema = FormSchema> extends BaseFormSto
|
|
|
1146
1459
|
*/
|
|
1147
1460
|
readonly isTouched: ReadonlySignal<boolean>;
|
|
1148
1461
|
/**
|
|
1462
|
+
* Whether any field in the form has been edited.
|
|
1463
|
+
*/
|
|
1464
|
+
readonly isEdited: ReadonlySignal<boolean>;
|
|
1465
|
+
/**
|
|
1149
1466
|
* Whether any field in the form differs from its initial value.
|
|
1150
1467
|
*/
|
|
1151
1468
|
readonly isDirty: ReadonlySignal<boolean>;
|
|
@@ -1157,7 +1474,7 @@ interface FormStore<TSchema extends FormSchema = FormSchema> extends BaseFormSto
|
|
|
1157
1474
|
* The current error messages of the form.
|
|
1158
1475
|
*
|
|
1159
1476
|
* Hint: This property only contains validation errors at the root level
|
|
1160
|
-
* of the form. To get all errors from all fields, use `
|
|
1477
|
+
* of the form. To get all errors from all fields, use `getDeepErrors`.
|
|
1161
1478
|
*/
|
|
1162
1479
|
readonly errors: ReadonlySignal<[string, ...string[]] | null>;
|
|
1163
1480
|
}
|
|
@@ -1304,4 +1621,4 @@ declare function useFieldArray<TSchema extends FormSchema, TFieldArrayPath exten
|
|
|
1304
1621
|
*/
|
|
1305
1622
|
declare function useForm<TSchema extends FormSchema>(config: FormConfig<TSchema>): FormStore<TSchema>;
|
|
1306
1623
|
//#endregion
|
|
1307
|
-
export { type DeepPartial, Field, FieldArray, FieldArrayProps, FieldArrayStore, type FieldElement, FieldElementProps, FieldProps, FieldStore, FocusFieldConfig, Form, type FormConfig, FormProps, type FormSchema, FormStore, GetFieldDirtyInputConfig, GetFieldDirtyPathsConfig, GetFieldErrorsConfig, GetFieldInputConfig, GetFormDirtyInputConfig, GetFormDirtyPathsConfig, GetFormErrorsConfig, GetFormInputConfig, InsertConfig, MoveConfig, type PartialValues, type PathValue, PickDirtyConfig, RemoveConfig, ReplaceConfig, type RequiredPath, ResetFieldConfig, ResetFormConfig, type Schema, SetFieldErrorsConfig, SetFieldInputConfig, SetFormErrorsConfig, SetFormInputConfig, type SubmitEventHandler, type SubmitHandler, SwapConfig, UseFieldArrayConfig, UseFieldConfig, type ValidArrayPath, type ValidPath, ValidateFormConfig, type ValidationMode, focus,
|
|
1624
|
+
export { DeepErrorEntry, type DeepPartial, Field, FieldArray, FieldArrayProps, FieldArrayStore, type FieldElement, FieldElementProps, FieldProps, FieldStore, FocusFieldConfig, Form, type FormConfig, FormProps, type FormSchema, FormStore, GetFieldDeepErrorEntriesConfig, GetFieldDeepErrorsConfig, GetFieldDirtyInputConfig, GetFieldDirtyPathsConfig, GetFieldErrorsConfig, GetFieldInputConfig, GetFormDeepErrorEntriesConfig, GetFormDeepErrorsConfig, GetFormDirtyInputConfig, GetFormDirtyPathsConfig, GetFormErrorsConfig, GetFormInputConfig, InsertConfig, IsFieldDirtyConfig, IsFieldEditedConfig, IsFieldTouchedConfig, IsFieldValidConfig, IsFormDirtyConfig, IsFormEditedConfig, IsFormTouchedConfig, IsFormValidConfig, MoveConfig, type PartialValues, type PathValue, PickDirtyConfig, RemoveConfig, ReplaceConfig, type RequiredPath, ResetFieldConfig, ResetFormConfig, type Schema, SetFieldErrorsConfig, SetFieldInputConfig, SetFormErrorsConfig, SetFormInputConfig, type SubmitEventHandler, type SubmitHandler, SwapConfig, UseFieldArrayConfig, UseFieldConfig, type ValidArrayPath, type ValidPath, ValidateFormConfig, type ValidationMode, focus, getDeepErrorEntries, getDeepErrors, getDirtyInput, getDirtyPaths, getErrors, getInput, handleSubmit, insert, isDirty, isEdited, isTouched, isValid, move, pickDirty, remove, replace, reset, setErrors, setInput, submit, swap, useField, useFieldArray, useForm, validate };
|
package/dist/index.js
CHANGED
|
@@ -37,11 +37,13 @@ function initializeFieldStore(internalFieldStore, schema, initialInput, path, nu
|
|
|
37
37
|
else {
|
|
38
38
|
internalFieldStore.schema = schema;
|
|
39
39
|
internalFieldStore.name = JSON.stringify(path);
|
|
40
|
+
internalFieldStore.path = path;
|
|
40
41
|
const initialElements = [];
|
|
41
42
|
internalFieldStore.initialElements = initialElements;
|
|
42
43
|
internalFieldStore.elements = initialElements;
|
|
43
44
|
internalFieldStore.errors = createSignal(null);
|
|
44
45
|
internalFieldStore.isTouched = createSignal(false);
|
|
46
|
+
internalFieldStore.isEdited = createSignal(false);
|
|
45
47
|
internalFieldStore.isDirty = createSignal(false);
|
|
46
48
|
if (schema.type === "array" || schema.type === "loose_tuple" || schema.type === "strict_tuple" || schema.type === "tuple") {
|
|
47
49
|
if (internalFieldStore.kind && internalFieldStore.kind !== "array") throw new Error(`Store initialized as "${internalFieldStore.kind}" cannot be reinitialized as "array"`);
|
|
@@ -51,16 +53,13 @@ function initializeFieldStore(internalFieldStore, schema, initialInput, path, nu
|
|
|
51
53
|
if (schema.type === "array") {
|
|
52
54
|
if (initialInput) for (let index = 0; index < initialInput.length; index++) {
|
|
53
55
|
internalFieldStore.children[index] = {};
|
|
54
|
-
|
|
55
|
-
initializeFieldStore(internalFieldStore.children[index], schema.item, initialInput[index], path);
|
|
56
|
-
path.pop();
|
|
56
|
+
initializeFieldStore(internalFieldStore.children[index], schema.item, initialInput[index], [...path, index]);
|
|
57
57
|
}
|
|
58
58
|
} else for (let index = 0; index < schema.items.length; index++) {
|
|
59
59
|
internalFieldStore.children[index] = {};
|
|
60
|
-
|
|
61
|
-
initializeFieldStore(internalFieldStore.children[index], schema.items[index], initialInput?.[index], path);
|
|
62
|
-
path.pop();
|
|
60
|
+
initializeFieldStore(internalFieldStore.children[index], schema.items[index], initialInput?.[index], [...path, index]);
|
|
63
61
|
}
|
|
62
|
+
internalFieldStore.isNullish = nullish;
|
|
64
63
|
const arrayInput = nullish && initialInput == null ? initialInput : true;
|
|
65
64
|
internalFieldStore.initialInput = createSignal(arrayInput);
|
|
66
65
|
internalFieldStore.startInput = createSignal(arrayInput);
|
|
@@ -77,10 +76,9 @@ function initializeFieldStore(internalFieldStore, schema, initialInput, path, nu
|
|
|
77
76
|
internalFieldStore.children ??= {};
|
|
78
77
|
for (const key in schema.entries) {
|
|
79
78
|
internalFieldStore.children[key] ??= {};
|
|
80
|
-
|
|
81
|
-
initializeFieldStore(internalFieldStore.children[key], schema.entries[key], initialInput?.[key], path);
|
|
82
|
-
path.pop();
|
|
79
|
+
initializeFieldStore(internalFieldStore.children[key], schema.entries[key], initialInput?.[key], [...path, key]);
|
|
83
80
|
}
|
|
81
|
+
internalFieldStore.isNullish = nullish;
|
|
84
82
|
const objectInput = nullish && initialInput == null ? initialInput : true;
|
|
85
83
|
internalFieldStore.initialInput = createSignal(objectInput);
|
|
86
84
|
internalFieldStore.startInput = createSignal(objectInput);
|
|
@@ -100,8 +98,9 @@ function initializeFieldStore(internalFieldStore, schema, initialInput, path, nu
|
|
|
100
98
|
/**
|
|
101
99
|
* Copies the deeply nested state (signal values) from one field store to
|
|
102
100
|
* another. This includes the `elements`, `errors`, `startInput`, `input`,
|
|
103
|
-
* `isTouched`, `isDirty`, and for arrays `startItems` and `items`
|
|
104
|
-
* Recursively walks through the field stores and copies all signal
|
|
101
|
+
* `isTouched`, `isEdited`, `isDirty`, and for arrays `startItems` and `items`
|
|
102
|
+
* properties. Recursively walks through the field stores and copies all signal
|
|
103
|
+
* values.
|
|
105
104
|
*
|
|
106
105
|
* @param fromInternalFieldStore The source field store to copy from.
|
|
107
106
|
* @param toInternalFieldStore The destination field store to copy to.
|
|
@@ -114,19 +113,16 @@ function copyItemState(fromInternalFieldStore, toInternalFieldStore) {
|
|
|
114
113
|
toInternalFieldStore.startInput.value = fromInternalFieldStore.startInput.value;
|
|
115
114
|
toInternalFieldStore.input.value = fromInternalFieldStore.input.value;
|
|
116
115
|
toInternalFieldStore.isTouched.value = fromInternalFieldStore.isTouched.value;
|
|
116
|
+
toInternalFieldStore.isEdited.value = fromInternalFieldStore.isEdited.value;
|
|
117
117
|
toInternalFieldStore.isDirty.value = fromInternalFieldStore.isDirty.value;
|
|
118
118
|
if (fromInternalFieldStore.kind === "array" && toInternalFieldStore.kind === "array") {
|
|
119
119
|
const fromItems = fromInternalFieldStore.items.value;
|
|
120
120
|
toInternalFieldStore.startItems.value = fromInternalFieldStore.startItems.value;
|
|
121
121
|
toInternalFieldStore.items.value = fromItems;
|
|
122
|
-
let path;
|
|
123
122
|
for (let index = 0; index < fromItems.length; index++) {
|
|
124
123
|
if (!toInternalFieldStore.children[index]) {
|
|
125
|
-
path ??= JSON.parse(toInternalFieldStore.name);
|
|
126
124
|
toInternalFieldStore.children[index] = {};
|
|
127
|
-
|
|
128
|
-
initializeFieldStore(toInternalFieldStore.children[index], toInternalFieldStore.schema.item, void 0, path);
|
|
129
|
-
path.pop();
|
|
125
|
+
initializeFieldStore(toInternalFieldStore.children[index], toInternalFieldStore.schema.item, void 0, [...toInternalFieldStore.path, index]);
|
|
130
126
|
}
|
|
131
127
|
copyItemState(fromInternalFieldStore.children[index], toInternalFieldStore.children[index]);
|
|
132
128
|
}
|
|
@@ -136,10 +132,10 @@ function copyItemState(fromInternalFieldStore, toInternalFieldStore) {
|
|
|
136
132
|
}
|
|
137
133
|
/**
|
|
138
134
|
* Resets the state of a field store (signal values) deeply nested. Sets
|
|
139
|
-
* `elements` to empty array, `errors` to `null`, `isTouched
|
|
140
|
-
* `false`, and `startInput`, `input`, `startItems`, and `items` to
|
|
141
|
-
* input value. Keeps the `initialInput` and `initialItems` state
|
|
142
|
-
* form reset functionality.
|
|
135
|
+
* `elements` to empty array, `errors` to `null`, `isTouched`, `isEdited` and
|
|
136
|
+
* `isDirty` to `false`, and `startInput`, `input`, `startItems`, and `items` to
|
|
137
|
+
* the new input value. Keeps the `initialInput` and `initialItems` state
|
|
138
|
+
* unchanged for form reset functionality.
|
|
143
139
|
*
|
|
144
140
|
* @param internalFieldStore The field store to reset.
|
|
145
141
|
* @param input The new input value (can be any type including array or object).
|
|
@@ -154,30 +150,32 @@ function resetItemState(internalFieldStore, input, keepStart = false) {
|
|
|
154
150
|
internalFieldStore.elements = elements;
|
|
155
151
|
internalFieldStore.errors.value = null;
|
|
156
152
|
internalFieldStore.isTouched.value = false;
|
|
153
|
+
internalFieldStore.isEdited.value = false;
|
|
157
154
|
internalFieldStore.isDirty.value = false;
|
|
158
155
|
if (internalFieldStore.kind === "array" || internalFieldStore.kind === "object") {
|
|
159
|
-
const objectInput = input == null ? input : true;
|
|
156
|
+
const objectInput = internalFieldStore.isNullish && input == null ? input : true;
|
|
160
157
|
if (!keepStart) internalFieldStore.startInput.value = objectInput;
|
|
161
158
|
internalFieldStore.input.value = objectInput;
|
|
162
|
-
if (internalFieldStore.kind === "array")
|
|
163
|
-
const
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
159
|
+
if (internalFieldStore.kind === "array") {
|
|
160
|
+
const isTuple = internalFieldStore.schema.type !== "array";
|
|
161
|
+
if (input || isTuple) {
|
|
162
|
+
const length = isTuple ? internalFieldStore.children.length : input.length;
|
|
163
|
+
const newItems = Array.from({ length }, createId);
|
|
164
|
+
if (!keepStart) internalFieldStore.startItems.value = newItems;
|
|
165
|
+
internalFieldStore.items.value = newItems;
|
|
166
|
+
for (let index = 0; index < length; index++) {
|
|
167
|
+
const itemInput = input?.[index];
|
|
168
|
+
if (internalFieldStore.children[index]) resetItemState(internalFieldStore.children[index], itemInput, keepStart);
|
|
169
|
+
else {
|
|
170
|
+
internalFieldStore.children[index] = {};
|
|
171
|
+
initializeFieldStore(internalFieldStore.children[index], internalFieldStore.schema.item, itemInput, [...internalFieldStore.path, index]);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
} else {
|
|
175
|
+
if (!keepStart) internalFieldStore.startItems.value = [];
|
|
176
|
+
internalFieldStore.items.value = [];
|
|
175
177
|
}
|
|
176
|
-
} else
|
|
177
|
-
if (!keepStart) internalFieldStore.startItems.value = [];
|
|
178
|
-
internalFieldStore.items.value = [];
|
|
179
|
-
}
|
|
180
|
-
else for (const key in internalFieldStore.children) resetItemState(internalFieldStore.children[key], input?.[key], keepStart);
|
|
178
|
+
} else for (const key in internalFieldStore.children) resetItemState(internalFieldStore.children[key], input?.[key], keepStart);
|
|
181
179
|
} else {
|
|
182
180
|
if (!keepStart) internalFieldStore.startInput.value = input;
|
|
183
181
|
internalFieldStore.input.value = input;
|
|
@@ -187,8 +185,8 @@ function resetItemState(internalFieldStore, input, keepStart = false) {
|
|
|
187
185
|
/**
|
|
188
186
|
* Swaps the deeply nested state (signal values) between two field stores. This
|
|
189
187
|
* includes the `elements`, `errors`, `startInput`, `input`, `isTouched`,
|
|
190
|
-
* `isDirty`, and for arrays `startItems` and `items` properties.
|
|
191
|
-
* walks through the field stores and swaps all signal values.
|
|
188
|
+
* `isEdited`, `isDirty`, and for arrays `startItems` and `items` properties.
|
|
189
|
+
* Recursively walks through the field stores and swaps all signal values.
|
|
192
190
|
*
|
|
193
191
|
* @param firstInternalFieldStore The first field store to swap.
|
|
194
192
|
* @param secondInternalFieldStore The second field store to swap.
|
|
@@ -211,6 +209,9 @@ function swapItemState(firstInternalFieldStore, secondInternalFieldStore) {
|
|
|
211
209
|
const tempIsTouched = firstInternalFieldStore.isTouched.value;
|
|
212
210
|
firstInternalFieldStore.isTouched.value = secondInternalFieldStore.isTouched.value;
|
|
213
211
|
secondInternalFieldStore.isTouched.value = tempIsTouched;
|
|
212
|
+
const tempIsEdited = firstInternalFieldStore.isEdited.value;
|
|
213
|
+
firstInternalFieldStore.isEdited.value = secondInternalFieldStore.isEdited.value;
|
|
214
|
+
secondInternalFieldStore.isEdited.value = tempIsEdited;
|
|
214
215
|
const tempIsDirty = firstInternalFieldStore.isDirty.value;
|
|
215
216
|
firstInternalFieldStore.isDirty.value = secondInternalFieldStore.isDirty.value;
|
|
216
217
|
secondInternalFieldStore.isDirty.value = tempIsDirty;
|
|
@@ -223,22 +224,14 @@ function swapItemState(firstInternalFieldStore, secondInternalFieldStore) {
|
|
|
223
224
|
firstInternalFieldStore.items.value = secondItems;
|
|
224
225
|
secondInternalFieldStore.items.value = firstItems;
|
|
225
226
|
const maxLength = Math.max(firstItems.length, secondItems.length);
|
|
226
|
-
let firstPath;
|
|
227
|
-
let secondPath;
|
|
228
227
|
for (let index = 0; index < maxLength; index++) {
|
|
229
228
|
if (!firstInternalFieldStore.children[index]) {
|
|
230
|
-
firstPath ??= JSON.parse(firstInternalFieldStore.name);
|
|
231
229
|
firstInternalFieldStore.children[index] = {};
|
|
232
|
-
|
|
233
|
-
initializeFieldStore(firstInternalFieldStore.children[index], firstInternalFieldStore.schema.item, void 0, firstPath);
|
|
234
|
-
firstPath.pop();
|
|
230
|
+
initializeFieldStore(firstInternalFieldStore.children[index], firstInternalFieldStore.schema.item, void 0, [...firstInternalFieldStore.path, index]);
|
|
235
231
|
}
|
|
236
232
|
if (!secondInternalFieldStore.children[index]) {
|
|
237
|
-
secondPath ??= JSON.parse(secondInternalFieldStore.name);
|
|
238
233
|
secondInternalFieldStore.children[index] = {};
|
|
239
|
-
|
|
240
|
-
initializeFieldStore(secondInternalFieldStore.children[index], secondInternalFieldStore.schema.item, void 0, secondPath);
|
|
241
|
-
secondPath.pop();
|
|
234
|
+
initializeFieldStore(secondInternalFieldStore.children[index], secondInternalFieldStore.schema.item, void 0, [...secondInternalFieldStore.path, index]);
|
|
242
235
|
}
|
|
243
236
|
swapItemState(firstInternalFieldStore.children[index], secondInternalFieldStore.children[index]);
|
|
244
237
|
}
|
|
@@ -269,6 +262,29 @@ function focusFieldElement(internalFieldStore) {
|
|
|
269
262
|
return false;
|
|
270
263
|
}
|
|
271
264
|
/**
|
|
265
|
+
* Walks through the field store and all nested children, calling the callback
|
|
266
|
+
* for each field store in depth-first order. The callback may return `true` to
|
|
267
|
+
* stop the walk early, in which case `walkFieldStore` returns `true` as well.
|
|
268
|
+
*
|
|
269
|
+
* The walk reads array `items` reactively, so a reactive caller subscribes to
|
|
270
|
+
* structural changes naturally. Imperative callers that must not subscribe
|
|
271
|
+
* (e.g. when invoked inside an effect) should wrap the call in `untrack`.
|
|
272
|
+
*
|
|
273
|
+
* @param internalFieldStore The field store to walk.
|
|
274
|
+
* @param callback The callback to invoke for each field store. Return `true` to stop the walk early.
|
|
275
|
+
*
|
|
276
|
+
* @returns Whether the walk was stopped early by the callback.
|
|
277
|
+
*/
|
|
278
|
+
function walkFieldStore(internalFieldStore, callback) {
|
|
279
|
+
if (callback(internalFieldStore)) return true;
|
|
280
|
+
if (internalFieldStore.kind === "array") {
|
|
281
|
+
for (let index = 0; index < internalFieldStore.items.value.length; index++) if (walkFieldStore(internalFieldStore.children[index], callback)) return true;
|
|
282
|
+
} else if (internalFieldStore.kind === "object") {
|
|
283
|
+
for (const key in internalFieldStore.children) if (walkFieldStore(internalFieldStore.children[key], callback)) return true;
|
|
284
|
+
}
|
|
285
|
+
return false;
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
272
288
|
* Returns whether the specified boolean property is true for the field store
|
|
273
289
|
* or any of its nested children. Recursively checks arrays and objects.
|
|
274
290
|
*
|
|
@@ -279,16 +295,7 @@ function focusFieldElement(internalFieldStore) {
|
|
|
279
295
|
*/
|
|
280
296
|
/* @__NO_SIDE_EFFECTS__ */
|
|
281
297
|
function getFieldBool(internalFieldStore, type) {
|
|
282
|
-
|
|
283
|
-
if (internalFieldStore.kind === "array") {
|
|
284
|
-
for (let index = 0; index < internalFieldStore.items.value.length; index++) if (/* @__PURE__ */ getFieldBool(internalFieldStore.children[index], type)) return true;
|
|
285
|
-
return false;
|
|
286
|
-
}
|
|
287
|
-
if (internalFieldStore.kind == "object") {
|
|
288
|
-
for (const key in internalFieldStore.children) if (/* @__PURE__ */ getFieldBool(internalFieldStore.children[key], type)) return true;
|
|
289
|
-
return false;
|
|
290
|
-
}
|
|
291
|
-
return false;
|
|
298
|
+
return walkFieldStore(internalFieldStore, (internalFieldStore$1) => Boolean(internalFieldStore$1[type].value));
|
|
292
299
|
}
|
|
293
300
|
/**
|
|
294
301
|
* Returns only the dirty input of the field store. Arrays are treated as
|
|
@@ -406,9 +413,11 @@ function getFieldStore(internalFormStore, path) {
|
|
|
406
413
|
*/
|
|
407
414
|
function setFieldBool(internalFieldStore, type, bool) {
|
|
408
415
|
batch(() => {
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
416
|
+
untrack(() => {
|
|
417
|
+
walkFieldStore(internalFieldStore, (internalFieldStore$1) => {
|
|
418
|
+
internalFieldStore$1[type].value = bool;
|
|
419
|
+
});
|
|
420
|
+
});
|
|
412
421
|
});
|
|
413
422
|
}
|
|
414
423
|
/**
|
|
@@ -420,19 +429,17 @@ function setFieldBool(internalFieldStore, type, bool) {
|
|
|
420
429
|
*/
|
|
421
430
|
function setNestedInput(internalFieldStore, input) {
|
|
422
431
|
internalFieldStore.isTouched.value = true;
|
|
432
|
+
internalFieldStore.isEdited.value = true;
|
|
423
433
|
if (internalFieldStore.kind === "array") {
|
|
424
434
|
const arrayInput = input ?? [];
|
|
425
435
|
const items = internalFieldStore.items.value;
|
|
426
436
|
const length = internalFieldStore.schema.type === "array" ? arrayInput.length : internalFieldStore.children.length;
|
|
427
437
|
if (length < items.length) internalFieldStore.items.value = items.slice(0, length);
|
|
428
438
|
else if (length > items.length) {
|
|
429
|
-
const path = JSON.parse(internalFieldStore.name);
|
|
430
439
|
for (let index = items.length; index < length; index++) if (internalFieldStore.children[index]) resetItemState(internalFieldStore.children[index], arrayInput[index], true);
|
|
431
440
|
else {
|
|
432
441
|
internalFieldStore.children[index] = {};
|
|
433
|
-
|
|
434
|
-
initializeFieldStore(internalFieldStore.children[index], internalFieldStore.schema.item, arrayInput[index], path);
|
|
435
|
-
path.pop();
|
|
442
|
+
initializeFieldStore(internalFieldStore.children[index], internalFieldStore.schema.item, arrayInput[index], [...internalFieldStore.path, index]);
|
|
436
443
|
}
|
|
437
444
|
internalFieldStore.items.value = [...items, ...Array.from({ length: length - items.length }, createId)];
|
|
438
445
|
}
|
|
@@ -483,14 +490,9 @@ function setInitialFieldInput(internalFieldStore, initialInput) {
|
|
|
483
490
|
internalFieldStore.initialInput.value = initialInput == null ? initialInput : true;
|
|
484
491
|
const initialArrayInput = initialInput ?? [];
|
|
485
492
|
const length = internalFieldStore.schema.type === "array" ? initialArrayInput.length : internalFieldStore.children.length;
|
|
486
|
-
if (length > internalFieldStore.children.length) {
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
internalFieldStore.children[index] = {};
|
|
490
|
-
path.push(index);
|
|
491
|
-
initializeFieldStore(internalFieldStore.children[index], internalFieldStore.schema.item, initialArrayInput[index], path);
|
|
492
|
-
path.pop();
|
|
493
|
-
}
|
|
493
|
+
if (length > internalFieldStore.children.length) for (let index = internalFieldStore.children.length; index < length; index++) {
|
|
494
|
+
internalFieldStore.children[index] = {};
|
|
495
|
+
initializeFieldStore(internalFieldStore.children[index], internalFieldStore.schema.item, initialArrayInput[index], [...internalFieldStore.path, index]);
|
|
494
496
|
}
|
|
495
497
|
internalFieldStore.initialItems.value = Array.from({ length }, createId);
|
|
496
498
|
for (let index = 0; index < internalFieldStore.children.length; index++) setInitialFieldInput(internalFieldStore.children[index], initialArrayInput[index]);
|
|
@@ -501,18 +503,6 @@ function setInitialFieldInput(internalFieldStore, initialInput) {
|
|
|
501
503
|
});
|
|
502
504
|
}
|
|
503
505
|
/**
|
|
504
|
-
* Walks through the field store and all nested children, calling the callback
|
|
505
|
-
* for each field store in depth-first order.
|
|
506
|
-
*
|
|
507
|
-
* @param internalFieldStore The field store to walk.
|
|
508
|
-
* @param callback The callback to invoke for each field store.
|
|
509
|
-
*/
|
|
510
|
-
function walkFieldStore(internalFieldStore, callback) {
|
|
511
|
-
callback(internalFieldStore);
|
|
512
|
-
if (internalFieldStore.kind === "array") for (let index = 0; index < untrack(() => internalFieldStore.items.value).length; index++) walkFieldStore(internalFieldStore.children[index], callback);
|
|
513
|
-
else if (internalFieldStore.kind === "object") for (const key in internalFieldStore.children) walkFieldStore(internalFieldStore.children[key], callback);
|
|
514
|
-
}
|
|
515
|
-
/**
|
|
516
506
|
* Creates a new internal form store from the provided configuration.
|
|
517
507
|
* Initializes the field store hierarchy, sets validation modes, and
|
|
518
508
|
* creates form state signals.
|
|
@@ -571,13 +561,15 @@ async function validateFormInput(internalFormStore, config) {
|
|
|
571
561
|
}
|
|
572
562
|
let shouldFocus = config?.shouldFocus ?? false;
|
|
573
563
|
batch(() => {
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
564
|
+
untrack(() => {
|
|
565
|
+
walkFieldStore(internalFormStore, (internalFieldStore) => {
|
|
566
|
+
if (internalFieldStore.path.length === 0) internalFieldStore.errors.value = rootErrors ?? null;
|
|
567
|
+
else {
|
|
568
|
+
const fieldErrors = nestedErrors?.[internalFieldStore.name] ?? null;
|
|
569
|
+
internalFieldStore.errors.value = fieldErrors;
|
|
570
|
+
if (shouldFocus && fieldErrors && focusFieldElement(internalFieldStore)) shouldFocus = false;
|
|
571
|
+
}
|
|
572
|
+
});
|
|
581
573
|
});
|
|
582
574
|
internalFormStore.validators--;
|
|
583
575
|
internalFormStore.isValidating.value = internalFormStore.validators > 0;
|
|
@@ -621,25 +613,27 @@ const INTERNAL = "~internal";
|
|
|
621
613
|
function focus(form, config) {
|
|
622
614
|
focusFieldElement(getFieldStore(form[INTERNAL], config.path));
|
|
623
615
|
}
|
|
624
|
-
/**
|
|
625
|
-
* Retrieves all error messages from all fields in the form by walking through
|
|
626
|
-
* the entire field store tree. This is useful for displaying a summary of all
|
|
627
|
-
* validation errors across the form.
|
|
628
|
-
*
|
|
629
|
-
* @param form The form store to retrieve errors from.
|
|
630
|
-
*
|
|
631
|
-
* @returns A non-empty array of error messages, or null if no errors exist.
|
|
632
|
-
*/
|
|
633
616
|
/* @__NO_SIDE_EFFECTS__ */
|
|
634
|
-
function
|
|
635
|
-
|
|
636
|
-
walkFieldStore(form[INTERNAL], (internalFieldStore) => {
|
|
637
|
-
if (internalFieldStore.kind === "array") internalFieldStore.items.value;
|
|
617
|
+
function getDeepErrorEntries(form, config) {
|
|
618
|
+
const entries = [];
|
|
619
|
+
walkFieldStore(config?.path ? getFieldStore(form[INTERNAL], config.path) : form[INTERNAL], (internalFieldStore) => {
|
|
638
620
|
const errors = internalFieldStore.errors.value;
|
|
639
|
-
if (errors)
|
|
640
|
-
|
|
621
|
+
if (errors) entries.push({
|
|
622
|
+
path: internalFieldStore.path,
|
|
623
|
+
errors
|
|
624
|
+
});
|
|
641
625
|
});
|
|
642
|
-
return
|
|
626
|
+
return entries;
|
|
627
|
+
}
|
|
628
|
+
/* @__NO_SIDE_EFFECTS__ */
|
|
629
|
+
function getDeepErrors(form, config) {
|
|
630
|
+
let deepErrors = null;
|
|
631
|
+
walkFieldStore(config?.path ? getFieldStore(form[INTERNAL], config.path) : form[INTERNAL], (internalFieldStore) => {
|
|
632
|
+
const errors = internalFieldStore.errors.value;
|
|
633
|
+
if (errors) if (deepErrors) deepErrors.push(...errors);
|
|
634
|
+
else deepErrors = [...errors];
|
|
635
|
+
});
|
|
636
|
+
return deepErrors;
|
|
643
637
|
}
|
|
644
638
|
/* @__NO_SIDE_EFFECTS__ */
|
|
645
639
|
function getDirtyInput(form, config) {
|
|
@@ -647,9 +641,8 @@ function getDirtyInput(form, config) {
|
|
|
647
641
|
}
|
|
648
642
|
/* @__NO_SIDE_EFFECTS__ */
|
|
649
643
|
function getDirtyPaths(form, config) {
|
|
650
|
-
config?.path ? getFieldStore(form[INTERNAL], config.path) : form[INTERNAL];
|
|
651
644
|
const paths = [];
|
|
652
|
-
config?.path
|
|
645
|
+
config?.path ? getFieldStore(form[INTERNAL], config.path) : form[INTERNAL];
|
|
653
646
|
return paths;
|
|
654
647
|
}
|
|
655
648
|
/* @__NO_SIDE_EFFECTS__ */
|
|
@@ -700,25 +693,38 @@ function insert(form, config) {
|
|
|
700
693
|
internalArrayStore.items.value = newItems;
|
|
701
694
|
for (let index = items.length; index > insertIndex; index--) {
|
|
702
695
|
if (!internalArrayStore.children[index]) {
|
|
703
|
-
const path = JSON.parse(internalArrayStore.name);
|
|
704
696
|
internalArrayStore.children[index] = {};
|
|
705
|
-
|
|
706
|
-
initializeFieldStore(internalArrayStore.children[index], internalArrayStore.schema.item, void 0, path);
|
|
697
|
+
initializeFieldStore(internalArrayStore.children[index], internalArrayStore.schema.item, void 0, [...internalArrayStore.path, index]);
|
|
707
698
|
}
|
|
708
699
|
copyItemState(internalArrayStore.children[index - 1], internalArrayStore.children[index]);
|
|
709
700
|
}
|
|
710
701
|
if (!internalArrayStore.children[insertIndex]) {
|
|
711
|
-
const path = JSON.parse(internalArrayStore.name);
|
|
712
702
|
internalArrayStore.children[insertIndex] = {};
|
|
713
|
-
|
|
714
|
-
initializeFieldStore(internalArrayStore.children[insertIndex], internalArrayStore.schema.item, config.initialInput, path);
|
|
703
|
+
initializeFieldStore(internalArrayStore.children[insertIndex], internalArrayStore.schema.item, config.initialInput, [...internalArrayStore.path, insertIndex]);
|
|
715
704
|
} else resetItemState(internalArrayStore.children[insertIndex], config.initialInput);
|
|
716
705
|
internalArrayStore.input.value = true;
|
|
717
706
|
internalArrayStore.isTouched.value = true;
|
|
707
|
+
internalArrayStore.isEdited.value = true;
|
|
718
708
|
internalArrayStore.isDirty.value = true;
|
|
719
709
|
validateIfRequired(internalFormStore, internalArrayStore, "input");
|
|
720
710
|
});
|
|
721
711
|
}
|
|
712
|
+
/* @__NO_SIDE_EFFECTS__ */
|
|
713
|
+
function isDirty(form, config) {
|
|
714
|
+
return getFieldBool(config?.path ? getFieldStore(form[INTERNAL], config.path) : form[INTERNAL], "isDirty");
|
|
715
|
+
}
|
|
716
|
+
/* @__NO_SIDE_EFFECTS__ */
|
|
717
|
+
function isEdited(form, config) {
|
|
718
|
+
return getFieldBool(config?.path ? getFieldStore(form[INTERNAL], config.path) : form[INTERNAL], "isEdited");
|
|
719
|
+
}
|
|
720
|
+
/* @__NO_SIDE_EFFECTS__ */
|
|
721
|
+
function isTouched(form, config) {
|
|
722
|
+
return getFieldBool(config?.path ? getFieldStore(form[INTERNAL], config.path) : form[INTERNAL], "isTouched");
|
|
723
|
+
}
|
|
724
|
+
/* @__NO_SIDE_EFFECTS__ */
|
|
725
|
+
function isValid(form, config) {
|
|
726
|
+
return !getFieldBool(config?.path ? getFieldStore(form[INTERNAL], config.path) : form[INTERNAL], "errors");
|
|
727
|
+
}
|
|
722
728
|
/**
|
|
723
729
|
* Moves an item from one index to another within a field array. All items
|
|
724
730
|
* between the source and destination indices are shifted accordingly.
|
|
@@ -741,6 +747,7 @@ function move(form, config) {
|
|
|
741
747
|
else for (let index = config.from; index > config.to; index--) copyItemState(internalArrayStore.children[index - 1], internalArrayStore.children[index]);
|
|
742
748
|
copyItemState(tempInternalFieldStore, internalArrayStore.children[config.to]);
|
|
743
749
|
internalArrayStore.isTouched.value = true;
|
|
750
|
+
internalArrayStore.isEdited.value = true;
|
|
744
751
|
internalArrayStore.isDirty.value = internalArrayStore.startItems.value.join() !== newItems.join();
|
|
745
752
|
validateIfRequired(internalFormStore, internalArrayStore, "input");
|
|
746
753
|
});
|
|
@@ -804,6 +811,7 @@ function remove(form, config) {
|
|
|
804
811
|
internalArrayStore.items.value = newItems;
|
|
805
812
|
for (let index = config.at; index < items.length - 1; index++) copyItemState(internalArrayStore.children[index + 1], internalArrayStore.children[index]);
|
|
806
813
|
internalArrayStore.isTouched.value = true;
|
|
814
|
+
internalArrayStore.isEdited.value = true;
|
|
807
815
|
internalArrayStore.isDirty.value = internalArrayStore.startItems.value.join() !== newItems.join();
|
|
808
816
|
validateIfRequired(internalFormStore, internalArrayStore, "input");
|
|
809
817
|
});
|
|
@@ -824,6 +832,7 @@ function replace(form, config) {
|
|
|
824
832
|
internalArrayStore.items.value = newItems;
|
|
825
833
|
resetItemState(internalArrayStore.children[config.at], config.initialInput);
|
|
826
834
|
internalArrayStore.isTouched.value = true;
|
|
835
|
+
internalArrayStore.isEdited.value = true;
|
|
827
836
|
internalArrayStore.isDirty.value = true;
|
|
828
837
|
validateIfRequired(internalFormStore, internalArrayStore, "input");
|
|
829
838
|
});
|
|
@@ -838,6 +847,7 @@ function reset(form, config) {
|
|
|
838
847
|
internalFieldStore$1.elements = internalFieldStore$1.initialElements;
|
|
839
848
|
if (!config?.keepErrors) internalFieldStore$1.errors.value = null;
|
|
840
849
|
if (!config?.keepTouched) internalFieldStore$1.isTouched.value = false;
|
|
850
|
+
if (!config?.keepEdited) internalFieldStore$1.isEdited.value = false;
|
|
841
851
|
internalFieldStore$1.startInput.value = internalFieldStore$1.initialInput.value;
|
|
842
852
|
if (!config?.keepInput) internalFieldStore$1.input.value = internalFieldStore$1.initialInput.value;
|
|
843
853
|
if (internalFieldStore$1.kind === "array") {
|
|
@@ -896,6 +906,7 @@ function swap(form, config) {
|
|
|
896
906
|
internalArrayStore.items.value = newItems;
|
|
897
907
|
swapItemState(internalArrayStore.children[config.at], internalArrayStore.children[config.and]);
|
|
898
908
|
internalArrayStore.isTouched.value = true;
|
|
909
|
+
internalArrayStore.isEdited.value = true;
|
|
899
910
|
internalArrayStore.isDirty.value = internalArrayStore.startItems.value.join() !== newItems.join();
|
|
900
911
|
validateIfRequired(internalFormStore, internalArrayStore, "input");
|
|
901
912
|
});
|
|
@@ -964,6 +975,7 @@ function useField(form, config) {
|
|
|
964
975
|
input: computed(() => getFieldInput(internalFieldStore.value)),
|
|
965
976
|
errors: computed(() => internalFieldStore.value.errors.value),
|
|
966
977
|
isTouched: computed(() => getFieldBool(internalFieldStore.value, "isTouched")),
|
|
978
|
+
isEdited: computed(() => getFieldBool(internalFieldStore.value, "isEdited")),
|
|
967
979
|
isDirty: computed(() => getFieldBool(internalFieldStore.value, "isDirty")),
|
|
968
980
|
isValid: computed(() => !getFieldBool(internalFieldStore.value, "errors")),
|
|
969
981
|
onInput(value) {
|
|
@@ -1007,6 +1019,7 @@ function useFieldArray(form, config) {
|
|
|
1007
1019
|
items: computed(() => internalFieldStore.value.items.value),
|
|
1008
1020
|
errors: computed(() => internalFieldStore.value.errors.value),
|
|
1009
1021
|
isTouched: computed(() => getFieldBool(internalFieldStore.value, "isTouched")),
|
|
1022
|
+
isEdited: computed(() => getFieldBool(internalFieldStore.value, "isEdited")),
|
|
1010
1023
|
isDirty: computed(() => getFieldBool(internalFieldStore.value, "isDirty")),
|
|
1011
1024
|
isValid: computed(() => !getFieldBool(internalFieldStore.value, "errors"))
|
|
1012
1025
|
}), []);
|
|
@@ -1024,6 +1037,7 @@ function useForm(config) {
|
|
|
1024
1037
|
isSubmitted: internalFormStore.isSubmitted,
|
|
1025
1038
|
isValidating: internalFormStore.isValidating,
|
|
1026
1039
|
isTouched: computed(() => getFieldBool(internalFormStore, "isTouched")),
|
|
1040
|
+
isEdited: computed(() => getFieldBool(internalFormStore, "isEdited")),
|
|
1027
1041
|
isDirty: computed(() => getFieldBool(internalFormStore, "isDirty")),
|
|
1028
1042
|
isValid: computed(() => !getFieldBool(internalFormStore, "errors")),
|
|
1029
1043
|
errors: internalFormStore.errors
|
|
@@ -1083,4 +1097,4 @@ function Form({ of, onSubmit, ...other }) {
|
|
|
1083
1097
|
}
|
|
1084
1098
|
|
|
1085
1099
|
//#endregion
|
|
1086
|
-
export { Field, FieldArray, Form, focus,
|
|
1100
|
+
export { Field, FieldArray, Form, focus, getDeepErrorEntries, getDeepErrors, getDirtyInput, getDirtyPaths, getErrors, getInput, handleSubmit, insert, isDirty, isEdited, isTouched, isValid, move, pickDirty, remove, replace, reset, setErrors, setInput, submit, swap, useField, useFieldArray, useForm, validate };
|
package/package.json
CHANGED