@formisch/react 0.6.0 → 0.7.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 +476 -159
- package/dist/index.js +140 -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
|
@@ -3,6 +3,177 @@ import { ChangeEventHandler, FocusEventHandler, FormEvent, FormHTMLAttributes, R
|
|
|
3
3
|
|
|
4
4
|
//#region ../../packages/core/dist/index.react.d.ts
|
|
5
5
|
|
|
6
|
+
//#region src/types/utils/utils.d.ts
|
|
7
|
+
/**
|
|
8
|
+
* Checks if a type is `any`.
|
|
9
|
+
*/
|
|
10
|
+
type IsAny<T> = 0 extends 1 & T ? true : false;
|
|
11
|
+
/**
|
|
12
|
+
* Checks if a type is `never`.
|
|
13
|
+
*/
|
|
14
|
+
type IsNever<T> = [T] extends [never] ? true : false;
|
|
15
|
+
/**
|
|
16
|
+
* Constructs a type that is maybe a promise.
|
|
17
|
+
*/
|
|
18
|
+
type MaybePromise<T> = T | Promise<T>;
|
|
19
|
+
/**
|
|
20
|
+
* Makes all properties deeply optional.
|
|
21
|
+
*/
|
|
22
|
+
type DeepPartial<TValue> = TValue extends Record<PropertyKey, unknown> | readonly unknown[] ? { [TKey in keyof TValue]?: DeepPartial<TValue[TKey]> | undefined } : TValue | undefined;
|
|
23
|
+
/**
|
|
24
|
+
* Makes all value properties optional.
|
|
25
|
+
*
|
|
26
|
+
* Hint: For dynamic arrays, only plain objects and nested arrays have their
|
|
27
|
+
* values made optional. Primitives and class instances are kept as-is to avoid
|
|
28
|
+
* types like `(string | undefined)[]`.
|
|
29
|
+
*/
|
|
30
|
+
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;
|
|
31
|
+
//#endregion
|
|
32
|
+
//#region src/types/path/path.d.ts
|
|
33
|
+
/**
|
|
34
|
+
* Path key type.
|
|
35
|
+
*/
|
|
36
|
+
type PathKey = string | number;
|
|
37
|
+
/**
|
|
38
|
+
* Path type.
|
|
39
|
+
*/
|
|
40
|
+
type Path = readonly PathKey[];
|
|
41
|
+
/**
|
|
42
|
+
* Required path type.
|
|
43
|
+
*/
|
|
44
|
+
type RequiredPath = readonly [PathKey, ...Path];
|
|
45
|
+
/**
|
|
46
|
+
* Extracts the exact keys of a tuple, array or object. Tuples return their
|
|
47
|
+
* literal numeric indices, dynamic arrays return `number`, objects return
|
|
48
|
+
* their `keyof` keys, and any other input returns `never`.
|
|
49
|
+
*/
|
|
50
|
+
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;
|
|
51
|
+
/**
|
|
52
|
+
* Returns the flat object of all indexable properties of `TValue`. For object
|
|
53
|
+
* unions, properties from every member are merged so that any single property
|
|
54
|
+
* is accessible. For primitives and other non-indexable types, the result is
|
|
55
|
+
* `{}`.
|
|
56
|
+
*
|
|
57
|
+
* Hint: This is necessary to make properties accessible across union members.
|
|
58
|
+
* By default, properties that do not exist in all union options are not
|
|
59
|
+
* accessible and result in "any" when accessed.
|
|
60
|
+
*/
|
|
61
|
+
type PropertiesOf<TValue> = { [TKey in ExactKeysOf<TValue>]: TValue extends Record<TKey, infer TItem> ? TItem : never };
|
|
62
|
+
/**
|
|
63
|
+
* Lazily evaluates only the first valid path segment based on the given value.
|
|
64
|
+
*/
|
|
65
|
+
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;
|
|
66
|
+
/**
|
|
67
|
+
* Returns the path if valid, otherwise the first possible valid path based on
|
|
68
|
+
* the given value.
|
|
69
|
+
*/
|
|
70
|
+
type ValidPath<TValue, TPath extends RequiredPath> = TPath extends LazyPath<Required<TValue>, TPath> ? TPath : LazyPath<Required<TValue>, TPath>;
|
|
71
|
+
/**
|
|
72
|
+
* Detects whether the consuming project is configured with
|
|
73
|
+
* `exactOptionalPropertyTypes: true`.
|
|
74
|
+
*
|
|
75
|
+
* Hint: If `false` the built-in `Required<T>` strips `| undefined` from
|
|
76
|
+
* optional properties, so `Required<{ key?: undefined }>['key']` collapses
|
|
77
|
+
* to `never` — under strict mode the same expression yields `undefined`.
|
|
78
|
+
*/
|
|
79
|
+
type IsExactOptionalProps = Required<{
|
|
80
|
+
key?: undefined;
|
|
81
|
+
}>["key"] extends never ? false : true;
|
|
82
|
+
/**
|
|
83
|
+
* Like the built-in `Required<T>`, but preserves `| undefined` in two
|
|
84
|
+
* places where `Required<T>` strips it:
|
|
85
|
+
*
|
|
86
|
+
* 1. Optional property values under `exactOptionalPropertyTypes: false`
|
|
87
|
+
* — without this, input typings for `v.optional`/`v.nullish` schemas
|
|
88
|
+
* narrow incorrectly (issue #15).
|
|
89
|
+
* 2. Array/tuple element types — e.g. `(string | undefined)[]` stays
|
|
90
|
+
* `(string | undefined)[]` instead of becoming `string[]`. Arrays
|
|
91
|
+
* fall through unchanged because they only have a numeric index
|
|
92
|
+
* signature and don't structurally extend `Record<PropertyKey,
|
|
93
|
+
* unknown>` (which requires string keys).
|
|
94
|
+
*/
|
|
95
|
+
type ExactRequired<TValue> = TValue extends Record<PropertyKey, unknown> ? IsExactOptionalProps extends true ? Required<TValue> : { [TKey in keyof Required<TValue>]: TValue[TKey] } : TValue;
|
|
96
|
+
/**
|
|
97
|
+
* Extracts the value type at the given path.
|
|
98
|
+
*/
|
|
99
|
+
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;
|
|
100
|
+
/**
|
|
101
|
+
* Checks whether a value is a dynamic array or contains one anywhere in its
|
|
102
|
+
* shape. A fixed-length tuple is not itself a dynamic array, but it counts when
|
|
103
|
+
* it contains one, so paths can still navigate through tuples to reach nested
|
|
104
|
+
* arrays.
|
|
105
|
+
*
|
|
106
|
+
* Hint: The inner conditionals (`TValue extends readonly unknown[]` and
|
|
107
|
+
* `TValue extends Record<PropertyKey, unknown>`) distribute over union members,
|
|
108
|
+
* so the inner expression returns the union of each member's result (e.g.
|
|
109
|
+
* `true | false` when some members contain arrays and others don't).
|
|
110
|
+
* Downstream code uses `IsOrHasArray<T> extends true`, but
|
|
111
|
+
* `boolean extends true` is `false` — so we collapse the result via
|
|
112
|
+
* `true extends ...`, which is `true` whenever at least one union member
|
|
113
|
+
* contributed `true`.
|
|
114
|
+
*/
|
|
115
|
+
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;
|
|
116
|
+
/**
|
|
117
|
+
* Extracts the exact keys of a tuple, array or object that contain arrays.
|
|
118
|
+
*/
|
|
119
|
+
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;
|
|
120
|
+
/**
|
|
121
|
+
* Returns the flat object of indexable properties of `TValue` whose values
|
|
122
|
+
* are or contain arrays. Mirrors `PropertiesOf` but keyed by
|
|
123
|
+
* `ExactKeysOfArrayPath` so the lookup is provably valid for array-path
|
|
124
|
+
* navigation in `LazyArrayPath`.
|
|
125
|
+
*/
|
|
126
|
+
type PropertiesOfArrayPath<TValue> = { [TKey in ExactKeysOfArrayPath<TValue>]: TValue extends Record<TKey, infer TItem> ? TItem : never };
|
|
127
|
+
/**
|
|
128
|
+
* Lazily evaluates only the first valid array path segment based on the given value.
|
|
129
|
+
*/
|
|
130
|
+
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;
|
|
131
|
+
/**
|
|
132
|
+
* Returns the path if valid, otherwise the first possible valid array path
|
|
133
|
+
* based on the given value.
|
|
134
|
+
*/
|
|
135
|
+
type ValidArrayPath<TValue, TPath extends RequiredPath> = TPath extends LazyArrayPath<Required<TValue>, TPath> ? TPath : LazyArrayPath<Required<TValue>, TPath>;
|
|
136
|
+
/**
|
|
137
|
+
* Recursive helper for `DirtyPath` that prepends `TKey` to each deeper path,
|
|
138
|
+
* or falls through to `never` when the child is not an object.
|
|
139
|
+
*/
|
|
140
|
+
type DeepDirtyPath<TChild, TKey$1 extends PathKey, TDepth extends 0[]> = TChild extends Record<PropertyKey, unknown> ? readonly [TKey$1, ...DirtyPath<TChild, [...TDepth, 0]>] : never;
|
|
141
|
+
/**
|
|
142
|
+
* Returns the union of all `RequiredPath`s that `getDirtyPaths` can emit
|
|
143
|
+
* for a given input type. Object fields contribute their own path and the
|
|
144
|
+
* paths of their descendants; arrays and tuples are atomic and contribute
|
|
145
|
+
* only their own path, because dirty arrays are returned as complete units.
|
|
146
|
+
*
|
|
147
|
+
* Narrowing is exact for the first 5 levels of nesting; deeper paths fall
|
|
148
|
+
* back to `RequiredPath` to keep the result a complete superset of any
|
|
149
|
+
* path the runtime can address.
|
|
150
|
+
*
|
|
151
|
+
* Hint: Arrays and tuples are atomic because they don't structurally
|
|
152
|
+
* extend `Record<PropertyKey, unknown>` and so fall through to `never`
|
|
153
|
+
* via `DeepDirtyPath` — no explicit array check is needed. `TDepth` is
|
|
154
|
+
* a tuple-length counter capped at 5 to bound TypeScript instantiation
|
|
155
|
+
* cost.
|
|
156
|
+
*/
|
|
157
|
+
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;
|
|
158
|
+
/**
|
|
159
|
+
* Recursive helper for `FieldPath` that prepends `TKey` to each deeper field
|
|
160
|
+
* path, or falls through to `never` when the child is a leaf value.
|
|
161
|
+
*/
|
|
162
|
+
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;
|
|
163
|
+
/**
|
|
164
|
+
* Returns the union of all `RequiredPath`s that address a field within the
|
|
165
|
+
* given input type. Object and array fields contribute their own path and the
|
|
166
|
+
* paths of their descendants; unlike `DirtyPath`, arrays are recursed into so
|
|
167
|
+
* that fields at any depth, including array items, can be addressed. Leaf
|
|
168
|
+
* values contribute only their own path (emitted by their parent).
|
|
169
|
+
*
|
|
170
|
+
* Narrowing is exact for the first 5 levels of nesting; deeper paths fall
|
|
171
|
+
* back to `RequiredPath` to keep the result a complete superset of any path
|
|
172
|
+
* the runtime can address. `TDepth` is a tuple-length counter capped at 5 to
|
|
173
|
+
* bound TypeScript instantiation cost.
|
|
174
|
+
*/
|
|
175
|
+
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;
|
|
176
|
+
//#endregion
|
|
6
177
|
//#region src/types/schema/schema.d.ts
|
|
7
178
|
/**
|
|
8
179
|
* Schema type.
|
|
@@ -67,6 +238,10 @@ interface InternalBaseStore {
|
|
|
67
238
|
*/
|
|
68
239
|
name: string;
|
|
69
240
|
/**
|
|
241
|
+
* The path to the field.
|
|
242
|
+
*/
|
|
243
|
+
path: Path;
|
|
244
|
+
/**
|
|
70
245
|
* The schema of the field.
|
|
71
246
|
*/
|
|
72
247
|
schema: Schema;
|
|
@@ -94,6 +269,15 @@ interface InternalBaseStore {
|
|
|
94
269
|
*/
|
|
95
270
|
isTouched: Signal<boolean>;
|
|
96
271
|
/**
|
|
272
|
+
* The edited state of the field.
|
|
273
|
+
*
|
|
274
|
+
* Hint: Unlike `isTouched`, which is also set when a field is focused, this
|
|
275
|
+
* is only set when the field's value is changed. Unlike `isDirty`, it stays
|
|
276
|
+
* `true` even if the value is changed back to its initial value. It is only
|
|
277
|
+
* reset when the field is reset.
|
|
278
|
+
*/
|
|
279
|
+
isEdited: Signal<boolean>;
|
|
280
|
+
/**
|
|
97
281
|
* The dirty state of the field.
|
|
98
282
|
*/
|
|
99
283
|
isDirty: Signal<boolean>;
|
|
@@ -107,6 +291,14 @@ interface InternalArrayStore extends InternalBaseStore {
|
|
|
107
291
|
*/
|
|
108
292
|
kind: "array";
|
|
109
293
|
/**
|
|
294
|
+
* Whether the array schema is wrapped in a nullish schema.
|
|
295
|
+
*
|
|
296
|
+
* Hint: This indicates whether a missing input should be represented as the
|
|
297
|
+
* nullish value (`null`/`undefined`) or as a present but empty array
|
|
298
|
+
* (`true`). It keeps resetting consistent with the initial state.
|
|
299
|
+
*/
|
|
300
|
+
isNullish: boolean;
|
|
301
|
+
/**
|
|
110
302
|
* The children of the array field.
|
|
111
303
|
*/
|
|
112
304
|
children: InternalFieldStore[];
|
|
@@ -159,6 +351,14 @@ interface InternalObjectStore extends InternalBaseStore {
|
|
|
159
351
|
*/
|
|
160
352
|
kind: "object";
|
|
161
353
|
/**
|
|
354
|
+
* Whether the object schema is wrapped in a nullish schema.
|
|
355
|
+
*
|
|
356
|
+
* Hint: This indicates whether a missing input should be represented as the
|
|
357
|
+
* nullish value (`null`/`undefined`) or as a present but empty object
|
|
358
|
+
* (`true`). It keeps resetting consistent with the initial state.
|
|
359
|
+
*/
|
|
360
|
+
isNullish: boolean;
|
|
361
|
+
/**
|
|
162
362
|
* The children of the object field.
|
|
163
363
|
*/
|
|
164
364
|
children: Record<string, InternalFieldStore>;
|
|
@@ -216,32 +416,6 @@ interface InternalValueStore extends InternalBaseStore {
|
|
|
216
416
|
*/
|
|
217
417
|
type InternalFieldStore = InternalArrayStore | InternalObjectStore | InternalValueStore;
|
|
218
418
|
//#endregion
|
|
219
|
-
//#region src/types/utils/utils.d.ts
|
|
220
|
-
/**
|
|
221
|
-
* Checks if a type is `any`.
|
|
222
|
-
*/
|
|
223
|
-
type IsAny<T> = 0 extends 1 & T ? true : false;
|
|
224
|
-
/**
|
|
225
|
-
* Checks if a type is `never`.
|
|
226
|
-
*/
|
|
227
|
-
type IsNever<T> = [T] extends [never] ? true : false;
|
|
228
|
-
/**
|
|
229
|
-
* Constructs a type that is maybe a promise.
|
|
230
|
-
*/
|
|
231
|
-
type MaybePromise<T> = T | Promise<T>;
|
|
232
|
-
/**
|
|
233
|
-
* Makes all properties deeply optional.
|
|
234
|
-
*/
|
|
235
|
-
type DeepPartial<TValue> = TValue extends Record<PropertyKey, unknown> | readonly unknown[] ? { [TKey in keyof TValue]?: DeepPartial<TValue[TKey]> | undefined } : TValue | undefined;
|
|
236
|
-
/**
|
|
237
|
-
* Makes all value properties optional.
|
|
238
|
-
*
|
|
239
|
-
* Hint: For dynamic arrays, only plain objects and nested arrays have their
|
|
240
|
-
* values made optional. Primitives and class instances are kept as-is to avoid
|
|
241
|
-
* types like `(string | undefined)[]`.
|
|
242
|
-
*/
|
|
243
|
-
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;
|
|
244
|
-
//#endregion
|
|
245
419
|
//#region src/values.d.ts
|
|
246
420
|
/**
|
|
247
421
|
* Internal symbol constant.
|
|
@@ -333,176 +507,137 @@ type SubmitHandler<TSchema extends FormSchema> = (output: v.InferOutput<TSchema>
|
|
|
333
507
|
*/
|
|
334
508
|
type SubmitEventHandler<TSchema extends FormSchema> = (output: v.InferOutput<TSchema>, event: FormEvent<HTMLFormElement>) => MaybePromise<unknown>;
|
|
335
509
|
//#endregion
|
|
336
|
-
//#region src/
|
|
337
|
-
/**
|
|
338
|
-
* Path key type.
|
|
339
|
-
*/
|
|
340
|
-
type PathKey = string | number;
|
|
341
|
-
/**
|
|
342
|
-
* Path type.
|
|
343
|
-
*/
|
|
344
|
-
type Path = readonly PathKey[];
|
|
510
|
+
//#region src/array/copyItemState/copyItemState.d.ts
|
|
345
511
|
/**
|
|
346
|
-
*
|
|
512
|
+
* Copies the deeply nested state (signal values) from one field store to
|
|
513
|
+
* another. This includes the `elements`, `errors`, `startInput`, `input`,
|
|
514
|
+
* `isTouched`, `isEdited`, `isDirty`, and for arrays `startItems` and `items`
|
|
515
|
+
* properties. Recursively walks through the field stores and copies all signal
|
|
516
|
+
* values.
|
|
517
|
+
*
|
|
518
|
+
* @param fromInternalFieldStore The source field store to copy from.
|
|
519
|
+
* @param toInternalFieldStore The destination field store to copy to.
|
|
347
520
|
*/
|
|
348
|
-
|
|
521
|
+
//#endregion
|
|
522
|
+
//#region ../../packages/methods/dist/index.react.d.ts
|
|
523
|
+
//#region src/focus/focus.d.ts
|
|
349
524
|
/**
|
|
350
|
-
*
|
|
351
|
-
* literal numeric indices, dynamic arrays return `number`, objects return
|
|
352
|
-
* their `keyof` keys, and any other input returns `never`.
|
|
525
|
+
* Focus field config interface.
|
|
353
526
|
*/
|
|
354
|
-
|
|
527
|
+
interface FocusFieldConfig<TSchema extends FormSchema, TFieldPath extends RequiredPath> {
|
|
528
|
+
/**
|
|
529
|
+
* The path to the field to focus.
|
|
530
|
+
*/
|
|
531
|
+
readonly path: ValidPath<v.InferInput<TSchema>, TFieldPath>;
|
|
532
|
+
}
|
|
355
533
|
/**
|
|
356
|
-
*
|
|
357
|
-
*
|
|
358
|
-
*
|
|
359
|
-
* `{}`.
|
|
534
|
+
* Focuses the first focusable input element of a field. This is useful for
|
|
535
|
+
* programmatically setting focus to a specific field, such as after
|
|
536
|
+
* validation errors or user interactions.
|
|
360
537
|
*
|
|
361
|
-
*
|
|
362
|
-
*
|
|
363
|
-
* accessible and result in "any" when accessed.
|
|
538
|
+
* @param form The form store containing the field.
|
|
539
|
+
* @param config The focus field configuration.
|
|
364
540
|
*/
|
|
365
|
-
|
|
541
|
+
declare function focus<TSchema extends FormSchema, TFieldPath extends RequiredPath>(form: BaseFormStore<TSchema>, config: FocusFieldConfig<TSchema, TFieldPath>): void;
|
|
542
|
+
//#endregion
|
|
543
|
+
//#region src/getDeepErrorEntries/getDeepErrorEntries.d.ts
|
|
366
544
|
/**
|
|
367
|
-
*
|
|
545
|
+
* Deep error entry interface.
|
|
368
546
|
*/
|
|
369
|
-
|
|
547
|
+
interface DeepErrorEntry<TValue = unknown> {
|
|
548
|
+
/**
|
|
549
|
+
* The path to the field with errors, or an empty path for form-level errors.
|
|
550
|
+
*/
|
|
551
|
+
readonly path: unknown extends TValue ? Path : readonly [] | FieldPath<TValue>;
|
|
552
|
+
/**
|
|
553
|
+
* The error messages of the field.
|
|
554
|
+
*/
|
|
555
|
+
readonly errors: [string, ...string[]];
|
|
556
|
+
}
|
|
370
557
|
/**
|
|
371
|
-
*
|
|
372
|
-
* the given value.
|
|
558
|
+
* Get form deep error entries config interface.
|
|
373
559
|
*/
|
|
374
|
-
|
|
560
|
+
interface GetFormDeepErrorEntriesConfig {
|
|
561
|
+
/**
|
|
562
|
+
* The path to a field. Leave undefined to get the entries of the entire form.
|
|
563
|
+
*/
|
|
564
|
+
readonly path?: undefined;
|
|
565
|
+
}
|
|
375
566
|
/**
|
|
376
|
-
*
|
|
377
|
-
* `exactOptionalPropertyTypes: true`.
|
|
378
|
-
*
|
|
379
|
-
* Hint: If `false` the built-in `Required<T>` strips `| undefined` from
|
|
380
|
-
* optional properties, so `Required<{ key?: undefined }>['key']` collapses
|
|
381
|
-
* to `never` — under strict mode the same expression yields `undefined`.
|
|
567
|
+
* Get field deep error entries config interface.
|
|
382
568
|
*/
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
569
|
+
interface GetFieldDeepErrorEntriesConfig<TSchema extends FormSchema, TFieldPath extends RequiredPath> {
|
|
570
|
+
/**
|
|
571
|
+
* The path to the field to retrieve the entries from.
|
|
572
|
+
*/
|
|
573
|
+
readonly path: ValidPath<v.InferInput<TSchema>, TFieldPath>;
|
|
574
|
+
}
|
|
386
575
|
/**
|
|
387
|
-
*
|
|
388
|
-
*
|
|
576
|
+
* Retrieves the errors of a specific field or the entire form as a list of
|
|
577
|
+
* entries, each pairing the path to a field with its error messages. This is
|
|
578
|
+
* useful for building custom error summaries that link each message back to
|
|
579
|
+
* its field. Form-level errors are included with an empty path.
|
|
389
580
|
*
|
|
390
|
-
*
|
|
391
|
-
* — without this, input typings for `v.optional`/`v.nullish` schemas
|
|
392
|
-
* narrow incorrectly (issue #15).
|
|
393
|
-
* 2. Array/tuple element types — e.g. `(string | undefined)[]` stays
|
|
394
|
-
* `(string | undefined)[]` instead of becoming `string[]`. Arrays
|
|
395
|
-
* fall through unchanged because they only have a numeric index
|
|
396
|
-
* signature and don't structurally extend `Record<PropertyKey,
|
|
397
|
-
* unknown>` (which requires string keys).
|
|
398
|
-
*/
|
|
399
|
-
type ExactRequired<TValue> = TValue extends Record<PropertyKey, unknown> ? IsExactOptionalProps extends true ? Required<TValue> : { [TKey in keyof Required<TValue>]: TValue[TKey] } : TValue;
|
|
400
|
-
/**
|
|
401
|
-
* Extracts the value type at the given path.
|
|
402
|
-
*/
|
|
403
|
-
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;
|
|
404
|
-
/**
|
|
405
|
-
* Checks whether a value is a dynamic array or contains one anywhere in its
|
|
406
|
-
* shape. A fixed-length tuple is not itself a dynamic array, but it counts when
|
|
407
|
-
* it contains one, so paths can still navigate through tuples to reach nested
|
|
408
|
-
* arrays.
|
|
581
|
+
* @param form The form store to retrieve error entries from.
|
|
409
582
|
*
|
|
410
|
-
*
|
|
411
|
-
* `TValue extends Record<PropertyKey, unknown>`) distribute over union members,
|
|
412
|
-
* so the inner expression returns the union of each member's result (e.g.
|
|
413
|
-
* `true | false` when some members contain arrays and others don't).
|
|
414
|
-
* Downstream code uses `IsOrHasArray<T> extends true`, but
|
|
415
|
-
* `boolean extends true` is `false` — so we collapse the result via
|
|
416
|
-
* `true extends ...`, which is `true` whenever at least one union member
|
|
417
|
-
* contributed `true`.
|
|
583
|
+
* @returns A list of path and error message entries.
|
|
418
584
|
*/
|
|
419
|
-
|
|
420
|
-
/**
|
|
421
|
-
* Extracts the exact keys of a tuple, array or object that contain arrays.
|
|
422
|
-
*/
|
|
423
|
-
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;
|
|
424
|
-
/**
|
|
425
|
-
* Returns the flat object of indexable properties of `TValue` whose values
|
|
426
|
-
* are or contain arrays. Mirrors `PropertiesOf` but keyed by
|
|
427
|
-
* `ExactKeysOfArrayPath` so the lookup is provably valid for array-path
|
|
428
|
-
* navigation in `LazyArrayPath`.
|
|
429
|
-
*/
|
|
430
|
-
type PropertiesOfArrayPath<TValue> = { [TKey in ExactKeysOfArrayPath<TValue>]: TValue extends Record<TKey, infer TItem> ? TItem : never };
|
|
431
|
-
/**
|
|
432
|
-
* Lazily evaluates only the first valid array path segment based on the given value.
|
|
433
|
-
*/
|
|
434
|
-
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;
|
|
585
|
+
declare function getDeepErrorEntries<TSchema extends FormSchema>(form: BaseFormStore<TSchema>): DeepErrorEntry<v.InferInput<TSchema>>[];
|
|
435
586
|
/**
|
|
436
|
-
*
|
|
437
|
-
*
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
/**
|
|
441
|
-
* Recursive helper for `DirtyPath` that prepends `TKey` to each deeper path,
|
|
442
|
-
* or falls through to `never` when the child is not an object.
|
|
443
|
-
*/
|
|
444
|
-
type DeepDirtyPath<TChild, TKey$1 extends PathKey, TDepth extends 0[]> = TChild extends Record<PropertyKey, unknown> ? readonly [TKey$1, ...DirtyPath<TChild, [...TDepth, 0]>] : never;
|
|
445
|
-
/**
|
|
446
|
-
* Returns the union of all `RequiredPath`s that `getDirtyPaths` can emit
|
|
447
|
-
* for a given input type. Object fields contribute their own path and the
|
|
448
|
-
* paths of their descendants; arrays and tuples are atomic and contribute
|
|
449
|
-
* only their own path, because dirty arrays are returned as complete units.
|
|
587
|
+
* Retrieves the errors of a specific field or the entire form as a list of
|
|
588
|
+
* entries, each pairing the path to a field with its error messages. This is
|
|
589
|
+
* useful for building custom error summaries that link each message back to
|
|
590
|
+
* its field. Form-level errors are included with an empty path.
|
|
450
591
|
*
|
|
451
|
-
*
|
|
452
|
-
*
|
|
453
|
-
* path the runtime can address.
|
|
592
|
+
* @param form The form store to retrieve error entries from.
|
|
593
|
+
* @param config The get deep error entries configuration.
|
|
454
594
|
*
|
|
455
|
-
*
|
|
456
|
-
* extend `Record<PropertyKey, unknown>` and so fall through to `never`
|
|
457
|
-
* via `DeepDirtyPath` — no explicit array check is needed. `TDepth` is
|
|
458
|
-
* a tuple-length counter capped at 5 to bound TypeScript instantiation
|
|
459
|
-
* cost.
|
|
595
|
+
* @returns A list of path and error message entries.
|
|
460
596
|
*/
|
|
461
|
-
|
|
597
|
+
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>>[];
|
|
462
598
|
//#endregion
|
|
463
|
-
//#region src/
|
|
599
|
+
//#region src/getDeepErrors/getDeepErrors.d.ts
|
|
464
600
|
/**
|
|
465
|
-
*
|
|
466
|
-
* another. This includes the `elements`, `errors`, `startInput`, `input`,
|
|
467
|
-
* `isTouched`, `isDirty`, and for arrays `startItems` and `items` properties.
|
|
468
|
-
* Recursively walks through the field stores and copies all signal values.
|
|
469
|
-
*
|
|
470
|
-
* @param fromInternalFieldStore The source field store to copy from.
|
|
471
|
-
* @param toInternalFieldStore The destination field store to copy to.
|
|
601
|
+
* Get form deep errors config interface.
|
|
472
602
|
*/
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
603
|
+
interface GetFormDeepErrorsConfig {
|
|
604
|
+
/**
|
|
605
|
+
* The path to a field. Leave undefined to get the errors of the entire form.
|
|
606
|
+
*/
|
|
607
|
+
readonly path?: undefined;
|
|
608
|
+
}
|
|
476
609
|
/**
|
|
477
|
-
*
|
|
610
|
+
* Get field deep errors config interface.
|
|
478
611
|
*/
|
|
479
|
-
interface
|
|
612
|
+
interface GetFieldDeepErrorsConfig<TSchema extends FormSchema, TFieldPath extends RequiredPath> {
|
|
480
613
|
/**
|
|
481
|
-
* The path to the field to
|
|
614
|
+
* The path to the field to retrieve the errors from.
|
|
482
615
|
*/
|
|
483
616
|
readonly path: ValidPath<v.InferInput<TSchema>, TFieldPath>;
|
|
484
617
|
}
|
|
485
618
|
/**
|
|
486
|
-
*
|
|
487
|
-
*
|
|
488
|
-
* validation errors or
|
|
619
|
+
* Retrieves all error messages of a specific field or the entire form by
|
|
620
|
+
* walking through the field store and all its descendants. This is useful for
|
|
621
|
+
* displaying a summary of all validation errors within a section or the whole
|
|
622
|
+
* form. Form-level errors are included.
|
|
489
623
|
*
|
|
490
|
-
* @param form The form store
|
|
491
|
-
*
|
|
624
|
+
* @param form The form store to retrieve errors from.
|
|
625
|
+
*
|
|
626
|
+
* @returns A non-empty array of error messages, or null if no errors exist.
|
|
492
627
|
*/
|
|
493
|
-
declare function
|
|
494
|
-
//#endregion
|
|
495
|
-
//#region src/getAllErrors/getAllErrors.d.ts
|
|
628
|
+
declare function getDeepErrors<TSchema extends FormSchema>(form: BaseFormStore<TSchema>): [string, ...string[]] | null;
|
|
496
629
|
/**
|
|
497
|
-
* Retrieves all error messages
|
|
498
|
-
* the
|
|
499
|
-
* validation errors
|
|
630
|
+
* Retrieves all error messages of a specific field or the entire form by
|
|
631
|
+
* walking through the field store and all its descendants. This is useful for
|
|
632
|
+
* displaying a summary of all validation errors within a section or the whole
|
|
633
|
+
* form. Form-level errors are included.
|
|
500
634
|
*
|
|
501
635
|
* @param form The form store to retrieve errors from.
|
|
636
|
+
* @param config The get deep errors configuration.
|
|
502
637
|
*
|
|
503
638
|
* @returns A non-empty array of error messages, or null if no errors exist.
|
|
504
639
|
*/
|
|
505
|
-
declare function
|
|
640
|
+
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;
|
|
506
641
|
//#endregion
|
|
507
642
|
//#region src/getDirtyInput/getDirtyInput.d.ts
|
|
508
643
|
/**
|
|
@@ -722,6 +857,172 @@ interface InsertConfig<TSchema extends FormSchema, TFieldArrayPath extends Requi
|
|
|
722
857
|
*/
|
|
723
858
|
declare function insert<TSchema extends FormSchema, TFieldArrayPath extends RequiredPath>(form: BaseFormStore<TSchema>, config: InsertConfig<TSchema, TFieldArrayPath>): void;
|
|
724
859
|
//#endregion
|
|
860
|
+
//#region src/isDirty/isDirty.d.ts
|
|
861
|
+
/**
|
|
862
|
+
* Is form dirty config interface.
|
|
863
|
+
*/
|
|
864
|
+
interface IsFormDirtyConfig {
|
|
865
|
+
/**
|
|
866
|
+
* The path to a field. Leave undefined to check the entire form.
|
|
867
|
+
*/
|
|
868
|
+
readonly path?: undefined;
|
|
869
|
+
}
|
|
870
|
+
/**
|
|
871
|
+
* Is field dirty config interface.
|
|
872
|
+
*/
|
|
873
|
+
interface IsFieldDirtyConfig<TSchema extends FormSchema, TFieldPath extends RequiredPath> {
|
|
874
|
+
/**
|
|
875
|
+
* The path to the field to check for dirtiness.
|
|
876
|
+
*/
|
|
877
|
+
readonly path: ValidPath<v.InferInput<TSchema>, TFieldPath>;
|
|
878
|
+
}
|
|
879
|
+
/**
|
|
880
|
+
* Checks whether a specific field or the entire form is dirty by walking
|
|
881
|
+
* through the field store and all its descendants. A field is dirty when its
|
|
882
|
+
* input differs from its initial value.
|
|
883
|
+
*
|
|
884
|
+
* @param form The form store to check for dirtiness.
|
|
885
|
+
*
|
|
886
|
+
* @returns Whether the field or form is dirty.
|
|
887
|
+
*/
|
|
888
|
+
declare function isDirty<TSchema extends FormSchema>(form: BaseFormStore<TSchema>): boolean;
|
|
889
|
+
/**
|
|
890
|
+
* Checks whether a specific field or the entire form is dirty by walking
|
|
891
|
+
* through the field store and all its descendants. A field is dirty when its
|
|
892
|
+
* input differs from its initial value.
|
|
893
|
+
*
|
|
894
|
+
* @param form The form store to check for dirtiness.
|
|
895
|
+
* @param config The is dirty configuration.
|
|
896
|
+
*
|
|
897
|
+
* @returns Whether the field or form is dirty.
|
|
898
|
+
*/
|
|
899
|
+
declare function isDirty<TSchema extends FormSchema, TFieldPath extends RequiredPath | undefined = undefined>(form: BaseFormStore<TSchema>, config: TFieldPath extends RequiredPath ? IsFieldDirtyConfig<TSchema, TFieldPath> : IsFormDirtyConfig): boolean;
|
|
900
|
+
//#endregion
|
|
901
|
+
//#region src/isEdited/isEdited.d.ts
|
|
902
|
+
/**
|
|
903
|
+
* Is form edited config interface.
|
|
904
|
+
*/
|
|
905
|
+
interface IsFormEditedConfig {
|
|
906
|
+
/**
|
|
907
|
+
* The path to a field. Leave undefined to check the entire form.
|
|
908
|
+
*/
|
|
909
|
+
readonly path?: undefined;
|
|
910
|
+
}
|
|
911
|
+
/**
|
|
912
|
+
* Is field edited config interface.
|
|
913
|
+
*/
|
|
914
|
+
interface IsFieldEditedConfig<TSchema extends FormSchema, TFieldPath extends RequiredPath> {
|
|
915
|
+
/**
|
|
916
|
+
* The path to the field to check for being edited.
|
|
917
|
+
*/
|
|
918
|
+
readonly path: ValidPath<v.InferInput<TSchema>, TFieldPath>;
|
|
919
|
+
}
|
|
920
|
+
/**
|
|
921
|
+
* Checks whether a specific field or the entire form is edited by walking
|
|
922
|
+
* through the field store and all its descendants. A field is edited once its
|
|
923
|
+
* value has been changed by the user.
|
|
924
|
+
*
|
|
925
|
+
* @param form The form store to check for being edited.
|
|
926
|
+
*
|
|
927
|
+
* @returns Whether the field or form is edited.
|
|
928
|
+
*/
|
|
929
|
+
declare function isEdited<TSchema extends FormSchema>(form: BaseFormStore<TSchema>): boolean;
|
|
930
|
+
/**
|
|
931
|
+
* Checks whether a specific field or the entire form is edited by walking
|
|
932
|
+
* through the field store and all its descendants. A field is edited once its
|
|
933
|
+
* value has been changed by the user.
|
|
934
|
+
*
|
|
935
|
+
* @param form The form store to check for being edited.
|
|
936
|
+
* @param config The is edited configuration.
|
|
937
|
+
*
|
|
938
|
+
* @returns Whether the field or form is edited.
|
|
939
|
+
*/
|
|
940
|
+
declare function isEdited<TSchema extends FormSchema, TFieldPath extends RequiredPath | undefined = undefined>(form: BaseFormStore<TSchema>, config: TFieldPath extends RequiredPath ? IsFieldEditedConfig<TSchema, TFieldPath> : IsFormEditedConfig): boolean;
|
|
941
|
+
//#endregion
|
|
942
|
+
//#region src/isTouched/isTouched.d.ts
|
|
943
|
+
/**
|
|
944
|
+
* Is form touched config interface.
|
|
945
|
+
*/
|
|
946
|
+
interface IsFormTouchedConfig {
|
|
947
|
+
/**
|
|
948
|
+
* The path to a field. Leave undefined to check the entire form.
|
|
949
|
+
*/
|
|
950
|
+
readonly path?: undefined;
|
|
951
|
+
}
|
|
952
|
+
/**
|
|
953
|
+
* Is field touched config interface.
|
|
954
|
+
*/
|
|
955
|
+
interface IsFieldTouchedConfig<TSchema extends FormSchema, TFieldPath extends RequiredPath> {
|
|
956
|
+
/**
|
|
957
|
+
* The path to the field to check for being touched.
|
|
958
|
+
*/
|
|
959
|
+
readonly path: ValidPath<v.InferInput<TSchema>, TFieldPath>;
|
|
960
|
+
}
|
|
961
|
+
/**
|
|
962
|
+
* Checks whether a specific field or the entire form is touched by walking
|
|
963
|
+
* through the field store and all its descendants. A field is touched once it
|
|
964
|
+
* has received and lost focus.
|
|
965
|
+
*
|
|
966
|
+
* @param form The form store to check for being touched.
|
|
967
|
+
*
|
|
968
|
+
* @returns Whether the field or form is touched.
|
|
969
|
+
*/
|
|
970
|
+
declare function isTouched<TSchema extends FormSchema>(form: BaseFormStore<TSchema>): boolean;
|
|
971
|
+
/**
|
|
972
|
+
* Checks whether a specific field or the entire form is touched by walking
|
|
973
|
+
* through the field store and all its descendants. A field is touched once it
|
|
974
|
+
* has received and lost focus.
|
|
975
|
+
*
|
|
976
|
+
* @param form The form store to check for being touched.
|
|
977
|
+
* @param config The is touched configuration.
|
|
978
|
+
*
|
|
979
|
+
* @returns Whether the field or form is touched.
|
|
980
|
+
*/
|
|
981
|
+
declare function isTouched<TSchema extends FormSchema, TFieldPath extends RequiredPath | undefined = undefined>(form: BaseFormStore<TSchema>, config: TFieldPath extends RequiredPath ? IsFieldTouchedConfig<TSchema, TFieldPath> : IsFormTouchedConfig): boolean;
|
|
982
|
+
//#endregion
|
|
983
|
+
//#region src/isValid/isValid.d.ts
|
|
984
|
+
/**
|
|
985
|
+
* Is form valid config interface.
|
|
986
|
+
*/
|
|
987
|
+
interface IsFormValidConfig {
|
|
988
|
+
/**
|
|
989
|
+
* The path to a field. Leave undefined to check the entire form.
|
|
990
|
+
*/
|
|
991
|
+
readonly path?: undefined;
|
|
992
|
+
}
|
|
993
|
+
/**
|
|
994
|
+
* Is field valid config interface.
|
|
995
|
+
*/
|
|
996
|
+
interface IsFieldValidConfig<TSchema extends FormSchema, TFieldPath extends RequiredPath> {
|
|
997
|
+
/**
|
|
998
|
+
* The path to the field to check for validity.
|
|
999
|
+
*/
|
|
1000
|
+
readonly path: ValidPath<v.InferInput<TSchema>, TFieldPath>;
|
|
1001
|
+
}
|
|
1002
|
+
/**
|
|
1003
|
+
* Checks whether a specific field or the entire form is valid by walking
|
|
1004
|
+
* through the field store and all its descendants. A field is valid when
|
|
1005
|
+
* neither it nor any of its descendants contains an error. Form-level errors
|
|
1006
|
+
* are included.
|
|
1007
|
+
*
|
|
1008
|
+
* @param form The form store to check for validity.
|
|
1009
|
+
*
|
|
1010
|
+
* @returns Whether the field or form is valid.
|
|
1011
|
+
*/
|
|
1012
|
+
declare function isValid<TSchema extends FormSchema>(form: BaseFormStore<TSchema>): boolean;
|
|
1013
|
+
/**
|
|
1014
|
+
* Checks whether a specific field or the entire form is valid by walking
|
|
1015
|
+
* through the field store and all its descendants. A field is valid when
|
|
1016
|
+
* neither it nor any of its descendants contains an error. Form-level errors
|
|
1017
|
+
* are included.
|
|
1018
|
+
*
|
|
1019
|
+
* @param form The form store to check for validity.
|
|
1020
|
+
* @param config The is valid configuration.
|
|
1021
|
+
*
|
|
1022
|
+
* @returns Whether the field or form is valid.
|
|
1023
|
+
*/
|
|
1024
|
+
declare function isValid<TSchema extends FormSchema, TFieldPath extends RequiredPath | undefined = undefined>(form: BaseFormStore<TSchema>, config: TFieldPath extends RequiredPath ? IsFieldValidConfig<TSchema, TFieldPath> : IsFormValidConfig): boolean;
|
|
1025
|
+
//#endregion
|
|
725
1026
|
//#region src/move/move.d.ts
|
|
726
1027
|
/**
|
|
727
1028
|
* Move array field config interface.
|
|
@@ -837,6 +1138,10 @@ interface ResetBaseConfig {
|
|
|
837
1138
|
*/
|
|
838
1139
|
readonly keepTouched?: boolean | undefined;
|
|
839
1140
|
/**
|
|
1141
|
+
* Whether to keep the edited state during reset. Defaults to false.
|
|
1142
|
+
*/
|
|
1143
|
+
readonly keepEdited?: boolean | undefined;
|
|
1144
|
+
/**
|
|
840
1145
|
* Whether to keep the error messages during reset. Defaults to false.
|
|
841
1146
|
*/
|
|
842
1147
|
readonly keepErrors?: boolean | undefined;
|
|
@@ -1085,6 +1390,10 @@ interface FieldStore<TSchema extends FormSchema = FormSchema, TFieldPath extends
|
|
|
1085
1390
|
*/
|
|
1086
1391
|
readonly isTouched: boolean;
|
|
1087
1392
|
/**
|
|
1393
|
+
* Whether the field value has been edited.
|
|
1394
|
+
*/
|
|
1395
|
+
readonly isEdited: boolean;
|
|
1396
|
+
/**
|
|
1088
1397
|
* Whether the field input differs from its initial value.
|
|
1089
1398
|
*/
|
|
1090
1399
|
readonly isDirty: boolean;
|
|
@@ -1122,6 +1431,10 @@ interface FieldArrayStore<TSchema extends FormSchema = FormSchema, TFieldArrayPa
|
|
|
1122
1431
|
*/
|
|
1123
1432
|
readonly isTouched: boolean;
|
|
1124
1433
|
/**
|
|
1434
|
+
* Whether the field array value has been edited.
|
|
1435
|
+
*/
|
|
1436
|
+
readonly isEdited: boolean;
|
|
1437
|
+
/**
|
|
1125
1438
|
* Whether the field array input differs from its initial value.
|
|
1126
1439
|
*/
|
|
1127
1440
|
readonly isDirty: boolean;
|
|
@@ -1153,6 +1466,10 @@ interface FormStore<TSchema extends FormSchema = FormSchema> extends BaseFormSto
|
|
|
1153
1466
|
*/
|
|
1154
1467
|
readonly isTouched: boolean;
|
|
1155
1468
|
/**
|
|
1469
|
+
* Whether any field in the form has been edited.
|
|
1470
|
+
*/
|
|
1471
|
+
readonly isEdited: boolean;
|
|
1472
|
+
/**
|
|
1156
1473
|
* Whether any field in the form differs from its initial value.
|
|
1157
1474
|
*/
|
|
1158
1475
|
readonly isDirty: boolean;
|
|
@@ -1164,7 +1481,7 @@ interface FormStore<TSchema extends FormSchema = FormSchema> extends BaseFormSto
|
|
|
1164
1481
|
* The current error messages of the form.
|
|
1165
1482
|
*
|
|
1166
1483
|
* Hint: This property only contains validation errors at the root level
|
|
1167
|
-
* of the form. To get all errors from all fields, use `
|
|
1484
|
+
* of the form. To get all errors from all fields, use `getDeepErrors`.
|
|
1168
1485
|
*/
|
|
1169
1486
|
readonly errors: [string, ...string[]] | null;
|
|
1170
1487
|
}
|
|
@@ -1311,4 +1628,4 @@ declare function useFieldArray<TSchema extends FormSchema, TFieldArrayPath exten
|
|
|
1311
1628
|
*/
|
|
1312
1629
|
declare function useForm<TSchema extends FormSchema>(config: FormConfig<TSchema>): FormStore<TSchema>;
|
|
1313
1630
|
//#endregion
|
|
1314
|
-
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, type SetFieldInputConfig, SetFormErrorsConfig, type SetFormInputConfig, type SubmitEventHandler, type SubmitHandler, SwapConfig, UseFieldArrayConfig, UseFieldConfig, type ValidArrayPath, type ValidPath, ValidateFormConfig, type ValidationMode, focus,
|
|
1631
|
+
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, type SetFieldInputConfig, SetFormErrorsConfig, type 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
|
@@ -123,11 +123,13 @@ function initializeFieldStore(internalFieldStore, schema, initialInput, path, nu
|
|
|
123
123
|
else {
|
|
124
124
|
internalFieldStore.schema = schema;
|
|
125
125
|
internalFieldStore.name = JSON.stringify(path);
|
|
126
|
+
internalFieldStore.path = path;
|
|
126
127
|
const initialElements = [];
|
|
127
128
|
internalFieldStore.initialElements = initialElements;
|
|
128
129
|
internalFieldStore.elements = initialElements;
|
|
129
130
|
internalFieldStore.errors = /* @__PURE__ */ createSignal(null);
|
|
130
131
|
internalFieldStore.isTouched = /* @__PURE__ */ createSignal(false);
|
|
132
|
+
internalFieldStore.isEdited = /* @__PURE__ */ createSignal(false);
|
|
131
133
|
internalFieldStore.isDirty = /* @__PURE__ */ createSignal(false);
|
|
132
134
|
if (schema.type === "array" || schema.type === "loose_tuple" || schema.type === "strict_tuple" || schema.type === "tuple") {
|
|
133
135
|
if (internalFieldStore.kind && internalFieldStore.kind !== "array") throw new Error(`Store initialized as "${internalFieldStore.kind}" cannot be reinitialized as "array"`);
|
|
@@ -137,16 +139,13 @@ function initializeFieldStore(internalFieldStore, schema, initialInput, path, nu
|
|
|
137
139
|
if (schema.type === "array") {
|
|
138
140
|
if (initialInput) for (let index = 0; index < initialInput.length; index++) {
|
|
139
141
|
internalFieldStore.children[index] = {};
|
|
140
|
-
|
|
141
|
-
initializeFieldStore(internalFieldStore.children[index], schema.item, initialInput[index], path);
|
|
142
|
-
path.pop();
|
|
142
|
+
initializeFieldStore(internalFieldStore.children[index], schema.item, initialInput[index], [...path, index]);
|
|
143
143
|
}
|
|
144
144
|
} else for (let index = 0; index < schema.items.length; index++) {
|
|
145
145
|
internalFieldStore.children[index] = {};
|
|
146
|
-
|
|
147
|
-
initializeFieldStore(internalFieldStore.children[index], schema.items[index], initialInput?.[index], path);
|
|
148
|
-
path.pop();
|
|
146
|
+
initializeFieldStore(internalFieldStore.children[index], schema.items[index], initialInput?.[index], [...path, index]);
|
|
149
147
|
}
|
|
148
|
+
internalFieldStore.isNullish = nullish;
|
|
150
149
|
const arrayInput = nullish && initialInput == null ? initialInput : true;
|
|
151
150
|
internalFieldStore.initialInput = /* @__PURE__ */ createSignal(arrayInput);
|
|
152
151
|
internalFieldStore.startInput = /* @__PURE__ */ createSignal(arrayInput);
|
|
@@ -163,10 +162,9 @@ function initializeFieldStore(internalFieldStore, schema, initialInput, path, nu
|
|
|
163
162
|
internalFieldStore.children ??= {};
|
|
164
163
|
for (const key in schema.entries) {
|
|
165
164
|
internalFieldStore.children[key] ??= {};
|
|
166
|
-
|
|
167
|
-
initializeFieldStore(internalFieldStore.children[key], schema.entries[key], initialInput?.[key], path);
|
|
168
|
-
path.pop();
|
|
165
|
+
initializeFieldStore(internalFieldStore.children[key], schema.entries[key], initialInput?.[key], [...path, key]);
|
|
169
166
|
}
|
|
167
|
+
internalFieldStore.isNullish = nullish;
|
|
170
168
|
const objectInput = nullish && initialInput == null ? initialInput : true;
|
|
171
169
|
internalFieldStore.initialInput = /* @__PURE__ */ createSignal(objectInput);
|
|
172
170
|
internalFieldStore.startInput = /* @__PURE__ */ createSignal(objectInput);
|
|
@@ -186,8 +184,9 @@ function initializeFieldStore(internalFieldStore, schema, initialInput, path, nu
|
|
|
186
184
|
/**
|
|
187
185
|
* Copies the deeply nested state (signal values) from one field store to
|
|
188
186
|
* another. This includes the `elements`, `errors`, `startInput`, `input`,
|
|
189
|
-
* `isTouched`, `isDirty`, and for arrays `startItems` and `items`
|
|
190
|
-
* Recursively walks through the field stores and copies all signal
|
|
187
|
+
* `isTouched`, `isEdited`, `isDirty`, and for arrays `startItems` and `items`
|
|
188
|
+
* properties. Recursively walks through the field stores and copies all signal
|
|
189
|
+
* values.
|
|
191
190
|
*
|
|
192
191
|
* @param fromInternalFieldStore The source field store to copy from.
|
|
193
192
|
* @param toInternalFieldStore The destination field store to copy to.
|
|
@@ -200,19 +199,16 @@ function copyItemState(fromInternalFieldStore, toInternalFieldStore) {
|
|
|
200
199
|
toInternalFieldStore.startInput.value = fromInternalFieldStore.startInput.value;
|
|
201
200
|
toInternalFieldStore.input.value = fromInternalFieldStore.input.value;
|
|
202
201
|
toInternalFieldStore.isTouched.value = fromInternalFieldStore.isTouched.value;
|
|
202
|
+
toInternalFieldStore.isEdited.value = fromInternalFieldStore.isEdited.value;
|
|
203
203
|
toInternalFieldStore.isDirty.value = fromInternalFieldStore.isDirty.value;
|
|
204
204
|
if (fromInternalFieldStore.kind === "array" && toInternalFieldStore.kind === "array") {
|
|
205
205
|
const fromItems = fromInternalFieldStore.items.value;
|
|
206
206
|
toInternalFieldStore.startItems.value = fromInternalFieldStore.startItems.value;
|
|
207
207
|
toInternalFieldStore.items.value = fromItems;
|
|
208
|
-
let path;
|
|
209
208
|
for (let index = 0; index < fromItems.length; index++) {
|
|
210
209
|
if (!toInternalFieldStore.children[index]) {
|
|
211
|
-
path ??= JSON.parse(toInternalFieldStore.name);
|
|
212
210
|
toInternalFieldStore.children[index] = {};
|
|
213
|
-
|
|
214
|
-
initializeFieldStore(toInternalFieldStore.children[index], toInternalFieldStore.schema.item, void 0, path);
|
|
215
|
-
path.pop();
|
|
211
|
+
initializeFieldStore(toInternalFieldStore.children[index], toInternalFieldStore.schema.item, void 0, [...toInternalFieldStore.path, index]);
|
|
216
212
|
}
|
|
217
213
|
copyItemState(fromInternalFieldStore.children[index], toInternalFieldStore.children[index]);
|
|
218
214
|
}
|
|
@@ -222,10 +218,10 @@ function copyItemState(fromInternalFieldStore, toInternalFieldStore) {
|
|
|
222
218
|
}
|
|
223
219
|
/**
|
|
224
220
|
* Resets the state of a field store (signal values) deeply nested. Sets
|
|
225
|
-
* `elements` to empty array, `errors` to `null`, `isTouched
|
|
226
|
-
* `false`, and `startInput`, `input`, `startItems`, and `items` to
|
|
227
|
-
* input value. Keeps the `initialInput` and `initialItems` state
|
|
228
|
-
* form reset functionality.
|
|
221
|
+
* `elements` to empty array, `errors` to `null`, `isTouched`, `isEdited` and
|
|
222
|
+
* `isDirty` to `false`, and `startInput`, `input`, `startItems`, and `items` to
|
|
223
|
+
* the new input value. Keeps the `initialInput` and `initialItems` state
|
|
224
|
+
* unchanged for form reset functionality.
|
|
229
225
|
*
|
|
230
226
|
* @param internalFieldStore The field store to reset.
|
|
231
227
|
* @param input The new input value (can be any type including array or object).
|
|
@@ -240,30 +236,32 @@ function resetItemState(internalFieldStore, input, keepStart = false) {
|
|
|
240
236
|
internalFieldStore.elements = elements;
|
|
241
237
|
internalFieldStore.errors.value = null;
|
|
242
238
|
internalFieldStore.isTouched.value = false;
|
|
239
|
+
internalFieldStore.isEdited.value = false;
|
|
243
240
|
internalFieldStore.isDirty.value = false;
|
|
244
241
|
if (internalFieldStore.kind === "array" || internalFieldStore.kind === "object") {
|
|
245
|
-
const objectInput = input == null ? input : true;
|
|
242
|
+
const objectInput = internalFieldStore.isNullish && input == null ? input : true;
|
|
246
243
|
if (!keepStart) internalFieldStore.startInput.value = objectInput;
|
|
247
244
|
internalFieldStore.input.value = objectInput;
|
|
248
|
-
if (internalFieldStore.kind === "array")
|
|
249
|
-
const
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
245
|
+
if (internalFieldStore.kind === "array") {
|
|
246
|
+
const isTuple = internalFieldStore.schema.type !== "array";
|
|
247
|
+
if (input || isTuple) {
|
|
248
|
+
const length = isTuple ? internalFieldStore.children.length : input.length;
|
|
249
|
+
const newItems = Array.from({ length }, createId);
|
|
250
|
+
if (!keepStart) internalFieldStore.startItems.value = newItems;
|
|
251
|
+
internalFieldStore.items.value = newItems;
|
|
252
|
+
for (let index = 0; index < length; index++) {
|
|
253
|
+
const itemInput = input?.[index];
|
|
254
|
+
if (internalFieldStore.children[index]) resetItemState(internalFieldStore.children[index], itemInput, keepStart);
|
|
255
|
+
else {
|
|
256
|
+
internalFieldStore.children[index] = {};
|
|
257
|
+
initializeFieldStore(internalFieldStore.children[index], internalFieldStore.schema.item, itemInput, [...internalFieldStore.path, index]);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
} else {
|
|
261
|
+
if (!keepStart) internalFieldStore.startItems.value = [];
|
|
262
|
+
internalFieldStore.items.value = [];
|
|
261
263
|
}
|
|
262
|
-
} else
|
|
263
|
-
if (!keepStart) internalFieldStore.startItems.value = [];
|
|
264
|
-
internalFieldStore.items.value = [];
|
|
265
|
-
}
|
|
266
|
-
else for (const key in internalFieldStore.children) resetItemState(internalFieldStore.children[key], input?.[key], keepStart);
|
|
264
|
+
} else for (const key in internalFieldStore.children) resetItemState(internalFieldStore.children[key], input?.[key], keepStart);
|
|
267
265
|
} else {
|
|
268
266
|
if (!keepStart) internalFieldStore.startInput.value = input;
|
|
269
267
|
internalFieldStore.input.value = input;
|
|
@@ -273,8 +271,8 @@ function resetItemState(internalFieldStore, input, keepStart = false) {
|
|
|
273
271
|
/**
|
|
274
272
|
* Swaps the deeply nested state (signal values) between two field stores. This
|
|
275
273
|
* includes the `elements`, `errors`, `startInput`, `input`, `isTouched`,
|
|
276
|
-
* `isDirty`, and for arrays `startItems` and `items` properties.
|
|
277
|
-
* walks through the field stores and swaps all signal values.
|
|
274
|
+
* `isEdited`, `isDirty`, and for arrays `startItems` and `items` properties.
|
|
275
|
+
* Recursively walks through the field stores and swaps all signal values.
|
|
278
276
|
*
|
|
279
277
|
* @param firstInternalFieldStore The first field store to swap.
|
|
280
278
|
* @param secondInternalFieldStore The second field store to swap.
|
|
@@ -297,6 +295,9 @@ function swapItemState(firstInternalFieldStore, secondInternalFieldStore) {
|
|
|
297
295
|
const tempIsTouched = firstInternalFieldStore.isTouched.value;
|
|
298
296
|
firstInternalFieldStore.isTouched.value = secondInternalFieldStore.isTouched.value;
|
|
299
297
|
secondInternalFieldStore.isTouched.value = tempIsTouched;
|
|
298
|
+
const tempIsEdited = firstInternalFieldStore.isEdited.value;
|
|
299
|
+
firstInternalFieldStore.isEdited.value = secondInternalFieldStore.isEdited.value;
|
|
300
|
+
secondInternalFieldStore.isEdited.value = tempIsEdited;
|
|
300
301
|
const tempIsDirty = firstInternalFieldStore.isDirty.value;
|
|
301
302
|
firstInternalFieldStore.isDirty.value = secondInternalFieldStore.isDirty.value;
|
|
302
303
|
secondInternalFieldStore.isDirty.value = tempIsDirty;
|
|
@@ -309,22 +310,14 @@ function swapItemState(firstInternalFieldStore, secondInternalFieldStore) {
|
|
|
309
310
|
firstInternalFieldStore.items.value = secondItems;
|
|
310
311
|
secondInternalFieldStore.items.value = firstItems;
|
|
311
312
|
const maxLength = Math.max(firstItems.length, secondItems.length);
|
|
312
|
-
let firstPath;
|
|
313
|
-
let secondPath;
|
|
314
313
|
for (let index = 0; index < maxLength; index++) {
|
|
315
314
|
if (!firstInternalFieldStore.children[index]) {
|
|
316
|
-
firstPath ??= JSON.parse(firstInternalFieldStore.name);
|
|
317
315
|
firstInternalFieldStore.children[index] = {};
|
|
318
|
-
|
|
319
|
-
initializeFieldStore(firstInternalFieldStore.children[index], firstInternalFieldStore.schema.item, void 0, firstPath);
|
|
320
|
-
firstPath.pop();
|
|
316
|
+
initializeFieldStore(firstInternalFieldStore.children[index], firstInternalFieldStore.schema.item, void 0, [...firstInternalFieldStore.path, index]);
|
|
321
317
|
}
|
|
322
318
|
if (!secondInternalFieldStore.children[index]) {
|
|
323
|
-
secondPath ??= JSON.parse(secondInternalFieldStore.name);
|
|
324
319
|
secondInternalFieldStore.children[index] = {};
|
|
325
|
-
|
|
326
|
-
initializeFieldStore(secondInternalFieldStore.children[index], secondInternalFieldStore.schema.item, void 0, secondPath);
|
|
327
|
-
secondPath.pop();
|
|
320
|
+
initializeFieldStore(secondInternalFieldStore.children[index], secondInternalFieldStore.schema.item, void 0, [...secondInternalFieldStore.path, index]);
|
|
328
321
|
}
|
|
329
322
|
swapItemState(firstInternalFieldStore.children[index], secondInternalFieldStore.children[index]);
|
|
330
323
|
}
|
|
@@ -355,6 +348,29 @@ function focusFieldElement(internalFieldStore) {
|
|
|
355
348
|
return false;
|
|
356
349
|
}
|
|
357
350
|
/**
|
|
351
|
+
* Walks through the field store and all nested children, calling the callback
|
|
352
|
+
* for each field store in depth-first order. The callback may return `true` to
|
|
353
|
+
* stop the walk early, in which case `walkFieldStore` returns `true` as well.
|
|
354
|
+
*
|
|
355
|
+
* The walk reads array `items` reactively, so a reactive caller subscribes to
|
|
356
|
+
* structural changes naturally. Imperative callers that must not subscribe
|
|
357
|
+
* (e.g. when invoked inside an effect) should wrap the call in `untrack`.
|
|
358
|
+
*
|
|
359
|
+
* @param internalFieldStore The field store to walk.
|
|
360
|
+
* @param callback The callback to invoke for each field store. Return `true` to stop the walk early.
|
|
361
|
+
*
|
|
362
|
+
* @returns Whether the walk was stopped early by the callback.
|
|
363
|
+
*/
|
|
364
|
+
function walkFieldStore(internalFieldStore, callback) {
|
|
365
|
+
if (callback(internalFieldStore)) return true;
|
|
366
|
+
if (internalFieldStore.kind === "array") {
|
|
367
|
+
for (let index = 0; index < internalFieldStore.items.value.length; index++) if (walkFieldStore(internalFieldStore.children[index], callback)) return true;
|
|
368
|
+
} else if (internalFieldStore.kind === "object") {
|
|
369
|
+
for (const key in internalFieldStore.children) if (walkFieldStore(internalFieldStore.children[key], callback)) return true;
|
|
370
|
+
}
|
|
371
|
+
return false;
|
|
372
|
+
}
|
|
373
|
+
/**
|
|
358
374
|
* Returns whether the specified boolean property is true for the field store
|
|
359
375
|
* or any of its nested children. Recursively checks arrays and objects.
|
|
360
376
|
*
|
|
@@ -365,16 +381,7 @@ function focusFieldElement(internalFieldStore) {
|
|
|
365
381
|
*/
|
|
366
382
|
/* @__NO_SIDE_EFFECTS__ */
|
|
367
383
|
function getFieldBool(internalFieldStore, type) {
|
|
368
|
-
|
|
369
|
-
if (internalFieldStore.kind === "array") {
|
|
370
|
-
for (let index = 0; index < internalFieldStore.items.value.length; index++) if (/* @__PURE__ */ getFieldBool(internalFieldStore.children[index], type)) return true;
|
|
371
|
-
return false;
|
|
372
|
-
}
|
|
373
|
-
if (internalFieldStore.kind == "object") {
|
|
374
|
-
for (const key in internalFieldStore.children) if (/* @__PURE__ */ getFieldBool(internalFieldStore.children[key], type)) return true;
|
|
375
|
-
return false;
|
|
376
|
-
}
|
|
377
|
-
return false;
|
|
384
|
+
return walkFieldStore(internalFieldStore, (internalFieldStore$1) => Boolean(internalFieldStore$1[type].value));
|
|
378
385
|
}
|
|
379
386
|
/**
|
|
380
387
|
* Returns only the dirty input of the field store. Arrays are treated as
|
|
@@ -492,9 +499,11 @@ function getFieldStore(internalFormStore, path) {
|
|
|
492
499
|
*/
|
|
493
500
|
function setFieldBool(internalFieldStore, type, bool) {
|
|
494
501
|
batch(() => {
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
502
|
+
untrack(() => {
|
|
503
|
+
walkFieldStore(internalFieldStore, (internalFieldStore$1) => {
|
|
504
|
+
internalFieldStore$1[type].value = bool;
|
|
505
|
+
});
|
|
506
|
+
});
|
|
498
507
|
});
|
|
499
508
|
}
|
|
500
509
|
/**
|
|
@@ -506,19 +515,17 @@ function setFieldBool(internalFieldStore, type, bool) {
|
|
|
506
515
|
*/
|
|
507
516
|
function setNestedInput(internalFieldStore, input) {
|
|
508
517
|
internalFieldStore.isTouched.value = true;
|
|
518
|
+
internalFieldStore.isEdited.value = true;
|
|
509
519
|
if (internalFieldStore.kind === "array") {
|
|
510
520
|
const arrayInput = input ?? [];
|
|
511
521
|
const items = internalFieldStore.items.value;
|
|
512
522
|
const length = internalFieldStore.schema.type === "array" ? arrayInput.length : internalFieldStore.children.length;
|
|
513
523
|
if (length < items.length) internalFieldStore.items.value = items.slice(0, length);
|
|
514
524
|
else if (length > items.length) {
|
|
515
|
-
const path = JSON.parse(internalFieldStore.name);
|
|
516
525
|
for (let index = items.length; index < length; index++) if (internalFieldStore.children[index]) resetItemState(internalFieldStore.children[index], arrayInput[index], true);
|
|
517
526
|
else {
|
|
518
527
|
internalFieldStore.children[index] = {};
|
|
519
|
-
|
|
520
|
-
initializeFieldStore(internalFieldStore.children[index], internalFieldStore.schema.item, arrayInput[index], path);
|
|
521
|
-
path.pop();
|
|
528
|
+
initializeFieldStore(internalFieldStore.children[index], internalFieldStore.schema.item, arrayInput[index], [...internalFieldStore.path, index]);
|
|
522
529
|
}
|
|
523
530
|
internalFieldStore.items.value = [...items, ...Array.from({ length: length - items.length }, createId)];
|
|
524
531
|
}
|
|
@@ -569,14 +576,9 @@ function setInitialFieldInput(internalFieldStore, initialInput) {
|
|
|
569
576
|
internalFieldStore.initialInput.value = initialInput == null ? initialInput : true;
|
|
570
577
|
const initialArrayInput = initialInput ?? [];
|
|
571
578
|
const length = internalFieldStore.schema.type === "array" ? initialArrayInput.length : internalFieldStore.children.length;
|
|
572
|
-
if (length > internalFieldStore.children.length) {
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
internalFieldStore.children[index] = {};
|
|
576
|
-
path.push(index);
|
|
577
|
-
initializeFieldStore(internalFieldStore.children[index], internalFieldStore.schema.item, initialArrayInput[index], path);
|
|
578
|
-
path.pop();
|
|
579
|
-
}
|
|
579
|
+
if (length > internalFieldStore.children.length) for (let index = internalFieldStore.children.length; index < length; index++) {
|
|
580
|
+
internalFieldStore.children[index] = {};
|
|
581
|
+
initializeFieldStore(internalFieldStore.children[index], internalFieldStore.schema.item, initialArrayInput[index], [...internalFieldStore.path, index]);
|
|
580
582
|
}
|
|
581
583
|
internalFieldStore.initialItems.value = Array.from({ length }, createId);
|
|
582
584
|
for (let index = 0; index < internalFieldStore.children.length; index++) setInitialFieldInput(internalFieldStore.children[index], initialArrayInput[index]);
|
|
@@ -587,18 +589,6 @@ function setInitialFieldInput(internalFieldStore, initialInput) {
|
|
|
587
589
|
});
|
|
588
590
|
}
|
|
589
591
|
/**
|
|
590
|
-
* Walks through the field store and all nested children, calling the callback
|
|
591
|
-
* for each field store in depth-first order.
|
|
592
|
-
*
|
|
593
|
-
* @param internalFieldStore The field store to walk.
|
|
594
|
-
* @param callback The callback to invoke for each field store.
|
|
595
|
-
*/
|
|
596
|
-
function walkFieldStore(internalFieldStore, callback) {
|
|
597
|
-
callback(internalFieldStore);
|
|
598
|
-
if (internalFieldStore.kind === "array") for (let index = 0; index < untrack(() => internalFieldStore.items.value).length; index++) walkFieldStore(internalFieldStore.children[index], callback);
|
|
599
|
-
else if (internalFieldStore.kind === "object") for (const key in internalFieldStore.children) walkFieldStore(internalFieldStore.children[key], callback);
|
|
600
|
-
}
|
|
601
|
-
/**
|
|
602
592
|
* Creates a new internal form store from the provided configuration.
|
|
603
593
|
* Initializes the field store hierarchy, sets validation modes, and
|
|
604
594
|
* creates form state signals.
|
|
@@ -657,13 +647,15 @@ async function validateFormInput(internalFormStore, config) {
|
|
|
657
647
|
}
|
|
658
648
|
let shouldFocus = config?.shouldFocus ?? false;
|
|
659
649
|
batch(() => {
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
650
|
+
untrack(() => {
|
|
651
|
+
walkFieldStore(internalFormStore, (internalFieldStore) => {
|
|
652
|
+
if (internalFieldStore.path.length === 0) internalFieldStore.errors.value = rootErrors ?? null;
|
|
653
|
+
else {
|
|
654
|
+
const fieldErrors = nestedErrors?.[internalFieldStore.name] ?? null;
|
|
655
|
+
internalFieldStore.errors.value = fieldErrors;
|
|
656
|
+
if (shouldFocus && fieldErrors && focusFieldElement(internalFieldStore)) shouldFocus = false;
|
|
657
|
+
}
|
|
658
|
+
});
|
|
667
659
|
});
|
|
668
660
|
internalFormStore.validators--;
|
|
669
661
|
internalFormStore.isValidating.value = internalFormStore.validators > 0;
|
|
@@ -707,25 +699,27 @@ const INTERNAL = "~internal";
|
|
|
707
699
|
function focus(form, config) {
|
|
708
700
|
focusFieldElement(getFieldStore(form[INTERNAL], config.path));
|
|
709
701
|
}
|
|
710
|
-
/**
|
|
711
|
-
* Retrieves all error messages from all fields in the form by walking through
|
|
712
|
-
* the entire field store tree. This is useful for displaying a summary of all
|
|
713
|
-
* validation errors across the form.
|
|
714
|
-
*
|
|
715
|
-
* @param form The form store to retrieve errors from.
|
|
716
|
-
*
|
|
717
|
-
* @returns A non-empty array of error messages, or null if no errors exist.
|
|
718
|
-
*/
|
|
719
702
|
/* @__NO_SIDE_EFFECTS__ */
|
|
720
|
-
function
|
|
721
|
-
|
|
722
|
-
walkFieldStore(form[INTERNAL], (internalFieldStore) => {
|
|
723
|
-
if (internalFieldStore.kind === "array") internalFieldStore.items.value;
|
|
703
|
+
function getDeepErrorEntries(form, config) {
|
|
704
|
+
const entries = [];
|
|
705
|
+
walkFieldStore(config?.path ? getFieldStore(form[INTERNAL], config.path) : form[INTERNAL], (internalFieldStore) => {
|
|
724
706
|
const errors = internalFieldStore.errors.value;
|
|
725
|
-
if (errors)
|
|
726
|
-
|
|
707
|
+
if (errors) entries.push({
|
|
708
|
+
path: internalFieldStore.path,
|
|
709
|
+
errors
|
|
710
|
+
});
|
|
727
711
|
});
|
|
728
|
-
return
|
|
712
|
+
return entries;
|
|
713
|
+
}
|
|
714
|
+
/* @__NO_SIDE_EFFECTS__ */
|
|
715
|
+
function getDeepErrors(form, config) {
|
|
716
|
+
let deepErrors = null;
|
|
717
|
+
walkFieldStore(config?.path ? getFieldStore(form[INTERNAL], config.path) : form[INTERNAL], (internalFieldStore) => {
|
|
718
|
+
const errors = internalFieldStore.errors.value;
|
|
719
|
+
if (errors) if (deepErrors) deepErrors.push(...errors);
|
|
720
|
+
else deepErrors = [...errors];
|
|
721
|
+
});
|
|
722
|
+
return deepErrors;
|
|
729
723
|
}
|
|
730
724
|
/* @__NO_SIDE_EFFECTS__ */
|
|
731
725
|
function getDirtyInput(form, config) {
|
|
@@ -733,9 +727,8 @@ function getDirtyInput(form, config) {
|
|
|
733
727
|
}
|
|
734
728
|
/* @__NO_SIDE_EFFECTS__ */
|
|
735
729
|
function getDirtyPaths(form, config) {
|
|
736
|
-
config?.path ? getFieldStore(form[INTERNAL], config.path) : form[INTERNAL];
|
|
737
730
|
const paths = [];
|
|
738
|
-
config?.path
|
|
731
|
+
config?.path ? getFieldStore(form[INTERNAL], config.path) : form[INTERNAL];
|
|
739
732
|
return paths;
|
|
740
733
|
}
|
|
741
734
|
/* @__NO_SIDE_EFFECTS__ */
|
|
@@ -790,25 +783,38 @@ function insert(form, config) {
|
|
|
790
783
|
internalArrayStore.items.value = newItems;
|
|
791
784
|
for (let index = items.length; index > insertIndex; index--) {
|
|
792
785
|
if (!internalArrayStore.children[index]) {
|
|
793
|
-
const path = JSON.parse(internalArrayStore.name);
|
|
794
786
|
internalArrayStore.children[index] = {};
|
|
795
|
-
|
|
796
|
-
initializeFieldStore(internalArrayStore.children[index], internalArrayStore.schema.item, void 0, path);
|
|
787
|
+
initializeFieldStore(internalArrayStore.children[index], internalArrayStore.schema.item, void 0, [...internalArrayStore.path, index]);
|
|
797
788
|
}
|
|
798
789
|
copyItemState(internalArrayStore.children[index - 1], internalArrayStore.children[index]);
|
|
799
790
|
}
|
|
800
791
|
if (!internalArrayStore.children[insertIndex]) {
|
|
801
|
-
const path = JSON.parse(internalArrayStore.name);
|
|
802
792
|
internalArrayStore.children[insertIndex] = {};
|
|
803
|
-
|
|
804
|
-
initializeFieldStore(internalArrayStore.children[insertIndex], internalArrayStore.schema.item, config.initialInput, path);
|
|
793
|
+
initializeFieldStore(internalArrayStore.children[insertIndex], internalArrayStore.schema.item, config.initialInput, [...internalArrayStore.path, insertIndex]);
|
|
805
794
|
} else resetItemState(internalArrayStore.children[insertIndex], config.initialInput);
|
|
806
795
|
internalArrayStore.input.value = true;
|
|
807
796
|
internalArrayStore.isTouched.value = true;
|
|
797
|
+
internalArrayStore.isEdited.value = true;
|
|
808
798
|
internalArrayStore.isDirty.value = true;
|
|
809
799
|
validateIfRequired(internalFormStore, internalArrayStore, "input");
|
|
810
800
|
});
|
|
811
801
|
}
|
|
802
|
+
/* @__NO_SIDE_EFFECTS__ */
|
|
803
|
+
function isDirty(form, config) {
|
|
804
|
+
return getFieldBool(config?.path ? getFieldStore(form[INTERNAL], config.path) : form[INTERNAL], "isDirty");
|
|
805
|
+
}
|
|
806
|
+
/* @__NO_SIDE_EFFECTS__ */
|
|
807
|
+
function isEdited(form, config) {
|
|
808
|
+
return getFieldBool(config?.path ? getFieldStore(form[INTERNAL], config.path) : form[INTERNAL], "isEdited");
|
|
809
|
+
}
|
|
810
|
+
/* @__NO_SIDE_EFFECTS__ */
|
|
811
|
+
function isTouched(form, config) {
|
|
812
|
+
return getFieldBool(config?.path ? getFieldStore(form[INTERNAL], config.path) : form[INTERNAL], "isTouched");
|
|
813
|
+
}
|
|
814
|
+
/* @__NO_SIDE_EFFECTS__ */
|
|
815
|
+
function isValid(form, config) {
|
|
816
|
+
return !getFieldBool(config?.path ? getFieldStore(form[INTERNAL], config.path) : form[INTERNAL], "errors");
|
|
817
|
+
}
|
|
812
818
|
/**
|
|
813
819
|
* Moves an item from one index to another within a field array. All items
|
|
814
820
|
* between the source and destination indices are shifted accordingly.
|
|
@@ -831,6 +837,7 @@ function move(form, config) {
|
|
|
831
837
|
else for (let index = config.from; index > config.to; index--) copyItemState(internalArrayStore.children[index - 1], internalArrayStore.children[index]);
|
|
832
838
|
copyItemState(tempInternalFieldStore, internalArrayStore.children[config.to]);
|
|
833
839
|
internalArrayStore.isTouched.value = true;
|
|
840
|
+
internalArrayStore.isEdited.value = true;
|
|
834
841
|
internalArrayStore.isDirty.value = internalArrayStore.startItems.value.join() !== newItems.join();
|
|
835
842
|
validateIfRequired(internalFormStore, internalArrayStore, "input");
|
|
836
843
|
});
|
|
@@ -894,6 +901,7 @@ function remove(form, config) {
|
|
|
894
901
|
internalArrayStore.items.value = newItems;
|
|
895
902
|
for (let index = config.at; index < items.length - 1; index++) copyItemState(internalArrayStore.children[index + 1], internalArrayStore.children[index]);
|
|
896
903
|
internalArrayStore.isTouched.value = true;
|
|
904
|
+
internalArrayStore.isEdited.value = true;
|
|
897
905
|
internalArrayStore.isDirty.value = internalArrayStore.startItems.value.join() !== newItems.join();
|
|
898
906
|
validateIfRequired(internalFormStore, internalArrayStore, "input");
|
|
899
907
|
});
|
|
@@ -914,6 +922,7 @@ function replace(form, config) {
|
|
|
914
922
|
internalArrayStore.items.value = newItems;
|
|
915
923
|
resetItemState(internalArrayStore.children[config.at], config.initialInput);
|
|
916
924
|
internalArrayStore.isTouched.value = true;
|
|
925
|
+
internalArrayStore.isEdited.value = true;
|
|
917
926
|
internalArrayStore.isDirty.value = true;
|
|
918
927
|
validateIfRequired(internalFormStore, internalArrayStore, "input");
|
|
919
928
|
});
|
|
@@ -928,6 +937,7 @@ function reset(form, config) {
|
|
|
928
937
|
internalFieldStore$1.elements = internalFieldStore$1.initialElements;
|
|
929
938
|
if (!config?.keepErrors) internalFieldStore$1.errors.value = null;
|
|
930
939
|
if (!config?.keepTouched) internalFieldStore$1.isTouched.value = false;
|
|
940
|
+
if (!config?.keepEdited) internalFieldStore$1.isEdited.value = false;
|
|
931
941
|
internalFieldStore$1.startInput.value = internalFieldStore$1.initialInput.value;
|
|
932
942
|
if (!config?.keepInput) internalFieldStore$1.input.value = internalFieldStore$1.initialInput.value;
|
|
933
943
|
if (internalFieldStore$1.kind === "array") {
|
|
@@ -988,6 +998,7 @@ function swap(form, config) {
|
|
|
988
998
|
internalArrayStore.items.value = newItems;
|
|
989
999
|
swapItemState(internalArrayStore.children[config.at], internalArrayStore.children[config.and]);
|
|
990
1000
|
internalArrayStore.isTouched.value = true;
|
|
1001
|
+
internalArrayStore.isEdited.value = true;
|
|
991
1002
|
internalArrayStore.isDirty.value = internalArrayStore.startItems.value.join() !== newItems.join();
|
|
992
1003
|
validateIfRequired(internalFormStore, internalArrayStore, "input");
|
|
993
1004
|
});
|
|
@@ -1057,6 +1068,9 @@ function useField(form, config) {
|
|
|
1057
1068
|
get isTouched() {
|
|
1058
1069
|
return getFieldBool(internalFieldStore, "isTouched");
|
|
1059
1070
|
},
|
|
1071
|
+
get isEdited() {
|
|
1072
|
+
return getFieldBool(internalFieldStore, "isEdited");
|
|
1073
|
+
},
|
|
1060
1074
|
get isDirty() {
|
|
1061
1075
|
return getFieldBool(internalFieldStore, "isDirty");
|
|
1062
1076
|
},
|
|
@@ -1108,6 +1122,9 @@ function useFieldArray(form, config) {
|
|
|
1108
1122
|
get isTouched() {
|
|
1109
1123
|
return getFieldBool(internalFieldStore, "isTouched");
|
|
1110
1124
|
},
|
|
1125
|
+
get isEdited() {
|
|
1126
|
+
return getFieldBool(internalFieldStore, "isEdited");
|
|
1127
|
+
},
|
|
1111
1128
|
get isDirty() {
|
|
1112
1129
|
return getFieldBool(internalFieldStore, "isDirty");
|
|
1113
1130
|
},
|
|
@@ -1140,6 +1157,9 @@ function useForm(config) {
|
|
|
1140
1157
|
get isTouched() {
|
|
1141
1158
|
return getFieldBool(internalFormStore, "isTouched");
|
|
1142
1159
|
},
|
|
1160
|
+
get isEdited() {
|
|
1161
|
+
return getFieldBool(internalFormStore, "isEdited");
|
|
1162
|
+
},
|
|
1143
1163
|
get isDirty() {
|
|
1144
1164
|
return getFieldBool(internalFormStore, "isDirty");
|
|
1145
1165
|
},
|
|
@@ -1200,4 +1220,4 @@ function Form({ of, onSubmit, ...other }) {
|
|
|
1200
1220
|
}
|
|
1201
1221
|
|
|
1202
1222
|
//#endregion
|
|
1203
|
-
export { Field, FieldArray, Form, focus,
|
|
1223
|
+
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