@tanstack/form-core 0.10.3 → 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/dist/cjs/FieldApi.d.ts +95 -0
- package/dist/cjs/FormApi.d.ts +118 -0
- package/dist/cjs/index.cjs +926 -0
- package/dist/cjs/index.cjs.map +1 -0
- package/dist/cjs/index.d.cts +5 -0
- package/dist/cjs/index.d.ts +5 -0
- package/dist/cjs/index.js +926 -0
- package/dist/cjs/mergeForm.d.ts +4 -0
- package/dist/cjs/tests/FieldApi.spec.d.ts +1 -0
- package/dist/cjs/tests/FieldApi.test-d.d.ts +1 -0
- package/dist/cjs/tests/FormApi.spec.d.ts +1 -0
- package/dist/cjs/tests/mutateMergeDeep.spec.d.ts +1 -0
- package/dist/cjs/tests/utils.d.ts +1 -0
- package/dist/cjs/tests/utils.spec.d.ts +1 -0
- package/dist/cjs/types.d.ts +14 -0
- package/dist/cjs/utils.d.ts +57 -0
- package/dist/mjs/FieldApi.d.ts +95 -0
- package/dist/mjs/FormApi.d.ts +118 -0
- package/dist/mjs/index.d.mts +5 -0
- package/dist/mjs/index.d.ts +5 -0
- package/dist/mjs/index.js +926 -0
- package/dist/mjs/index.mjs +926 -0
- package/dist/mjs/index.mjs.map +1 -0
- package/dist/mjs/mergeForm.d.ts +4 -0
- package/dist/mjs/tests/FieldApi.spec.d.ts +1 -0
- package/dist/mjs/tests/FieldApi.test-d.d.ts +1 -0
- package/dist/mjs/tests/FormApi.spec.d.ts +1 -0
- package/dist/mjs/tests/mutateMergeDeep.spec.d.ts +1 -0
- package/dist/mjs/tests/utils.d.ts +1 -0
- package/dist/mjs/tests/utils.spec.d.ts +1 -0
- package/dist/mjs/types.d.ts +14 -0
- package/dist/mjs/utils.d.ts +57 -0
- package/package.json +16 -21
- package/src/FieldApi.ts +328 -236
- package/src/FormApi.ts +302 -216
- package/src/index.ts +1 -0
- package/src/mergeForm.ts +42 -0
- package/src/tests/FieldApi.spec.ts +135 -48
- package/src/tests/FieldApi.test-d.ts +10 -6
- package/src/tests/FormApi.spec.ts +171 -62
- package/src/tests/mutateMergeDeep.spec.ts +32 -0
- package/src/tests/utils.ts +1 -1
- package/src/types.ts +11 -2
- package/src/utils.ts +137 -14
- package/build/legacy/FieldApi.cjs +0 -340
- package/build/legacy/FieldApi.cjs.map +0 -1
- package/build/legacy/FieldApi.d.cts +0 -4
- package/build/legacy/FieldApi.d.ts +0 -4
- package/build/legacy/FieldApi.js +0 -315
- package/build/legacy/FieldApi.js.map +0 -1
- package/build/legacy/FormApi.cjs +0 -438
- package/build/legacy/FormApi.cjs.map +0 -1
- package/build/legacy/FormApi.d.cts +0 -4
- package/build/legacy/FormApi.d.ts +0 -4
- package/build/legacy/FormApi.js +0 -419
- package/build/legacy/FormApi.js.map +0 -1
- package/build/legacy/index.cjs +0 -31
- package/build/legacy/index.cjs.map +0 -1
- package/build/legacy/index.d.cts +0 -170
- package/build/legacy/index.d.ts +0 -170
- package/build/legacy/index.js +0 -6
- package/build/legacy/index.js.map +0 -1
- package/build/legacy/types.cjs +0 -19
- package/build/legacy/types.cjs.map +0 -1
- package/build/legacy/types.d.cts +0 -7
- package/build/legacy/types.d.ts +0 -7
- package/build/legacy/types.js +0 -1
- package/build/legacy/types.js.map +0 -1
- package/build/legacy/utils.cjs +0 -132
- package/build/legacy/utils.cjs.map +0 -1
- package/build/legacy/utils.d.cts +0 -37
- package/build/legacy/utils.d.ts +0 -37
- package/build/legacy/utils.js +0 -103
- package/build/legacy/utils.js.map +0 -1
- package/build/modern/FieldApi.cjs +0 -337
- package/build/modern/FieldApi.cjs.map +0 -1
- package/build/modern/FieldApi.d.cts +0 -4
- package/build/modern/FieldApi.d.ts +0 -4
- package/build/modern/FieldApi.js +0 -312
- package/build/modern/FieldApi.js.map +0 -1
- package/build/modern/FormApi.cjs +0 -431
- package/build/modern/FormApi.cjs.map +0 -1
- package/build/modern/FormApi.d.cts +0 -4
- package/build/modern/FormApi.d.ts +0 -4
- package/build/modern/FormApi.js +0 -412
- package/build/modern/FormApi.js.map +0 -1
- package/build/modern/index.cjs +0 -31
- package/build/modern/index.cjs.map +0 -1
- package/build/modern/index.d.cts +0 -170
- package/build/modern/index.d.ts +0 -170
- package/build/modern/index.js +0 -6
- package/build/modern/index.js.map +0 -1
- package/build/modern/types.cjs +0 -19
- package/build/modern/types.cjs.map +0 -1
- package/build/modern/types.d.cts +0 -7
- package/build/modern/types.d.ts +0 -7
- package/build/modern/types.js +0 -1
- package/build/modern/types.js.map +0 -1
- package/build/modern/utils.cjs +0 -132
- package/build/modern/utils.cjs.map +0 -1
- package/build/modern/utils.d.cts +0 -37
- package/build/modern/utils.d.ts +0 -37
- package/build/modern/utils.js +0 -103
- package/build/modern/utils.js.map +0 -1
package/src/FieldApi.ts
CHANGED
|
@@ -1,115 +1,209 @@
|
|
|
1
1
|
import { Store } from '@tanstack/store'
|
|
2
|
-
import type { FormApi
|
|
3
|
-
import type {
|
|
2
|
+
import type { FormApi } from './FormApi'
|
|
3
|
+
import type {
|
|
4
|
+
ValidationCause,
|
|
5
|
+
ValidationError,
|
|
6
|
+
ValidationErrorMap,
|
|
7
|
+
Validator,
|
|
8
|
+
} from './types'
|
|
4
9
|
import type { DeepKeys, DeepValue, Updater } from './utils'
|
|
10
|
+
import { getAsyncValidatorArray, getSyncValidatorArray } from './utils'
|
|
5
11
|
|
|
6
|
-
export type
|
|
7
|
-
|
|
8
|
-
type ValidateFn<
|
|
12
|
+
export type FieldValidateFn<
|
|
9
13
|
TParentData,
|
|
10
14
|
TName extends DeepKeys<TParentData>,
|
|
11
|
-
|
|
15
|
+
TFieldValidator extends
|
|
16
|
+
| Validator<DeepValue<TParentData, TName>, unknown>
|
|
17
|
+
| undefined = undefined,
|
|
18
|
+
TFormValidator extends
|
|
19
|
+
| Validator<TParentData, unknown>
|
|
20
|
+
| undefined = undefined,
|
|
12
21
|
TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,
|
|
13
|
-
> = (
|
|
14
|
-
value: TData
|
|
15
|
-
fieldApi: FieldApi<TParentData, TName,
|
|
16
|
-
) => ValidationError
|
|
22
|
+
> = (props: {
|
|
23
|
+
value: TData
|
|
24
|
+
fieldApi: FieldApi<TParentData, TName, TFieldValidator, TFormValidator, TData>
|
|
25
|
+
}) => ValidationError
|
|
17
26
|
|
|
18
|
-
type
|
|
27
|
+
export type FieldValidateOrFn<
|
|
19
28
|
TParentData,
|
|
20
29
|
TName extends DeepKeys<TParentData>,
|
|
21
|
-
|
|
22
|
-
|
|
30
|
+
TFieldValidator extends
|
|
31
|
+
| Validator<DeepValue<TParentData, TName>, unknown>
|
|
32
|
+
| undefined = undefined,
|
|
33
|
+
TFormValidator extends
|
|
34
|
+
| Validator<TParentData, unknown>
|
|
35
|
+
| undefined = undefined,
|
|
23
36
|
TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,
|
|
24
|
-
> =
|
|
37
|
+
> = TFieldValidator extends Validator<TData, infer TFN>
|
|
25
38
|
?
|
|
26
|
-
|
|
|
27
|
-
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
39
|
+
| TFN
|
|
40
|
+
| FieldValidateFn<
|
|
41
|
+
TParentData,
|
|
42
|
+
TName,
|
|
43
|
+
TFieldValidator,
|
|
44
|
+
TFormValidator,
|
|
45
|
+
TData
|
|
46
|
+
>
|
|
47
|
+
: TFormValidator extends Validator<TParentData, infer FFN>
|
|
48
|
+
?
|
|
49
|
+
| FFN
|
|
50
|
+
| FieldValidateFn<
|
|
51
|
+
TParentData,
|
|
52
|
+
TName,
|
|
53
|
+
TFieldValidator,
|
|
54
|
+
TFormValidator,
|
|
55
|
+
TData
|
|
56
|
+
>
|
|
57
|
+
: FieldValidateFn<
|
|
58
|
+
TParentData,
|
|
59
|
+
TName,
|
|
60
|
+
TFieldValidator,
|
|
61
|
+
TFormValidator,
|
|
62
|
+
TData
|
|
63
|
+
>
|
|
64
|
+
|
|
65
|
+
export type FieldValidateAsyncFn<
|
|
35
66
|
TParentData,
|
|
36
67
|
TName extends DeepKeys<TParentData>,
|
|
37
|
-
|
|
68
|
+
TFieldValidator extends
|
|
69
|
+
| Validator<DeepValue<TParentData, TName>, unknown>
|
|
70
|
+
| undefined = undefined,
|
|
71
|
+
TFormValidator extends
|
|
72
|
+
| Validator<TParentData, unknown>
|
|
73
|
+
| undefined = undefined,
|
|
38
74
|
TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,
|
|
39
|
-
> = (
|
|
40
|
-
value: TData
|
|
41
|
-
fieldApi: FieldApi<TParentData, TName,
|
|
42
|
-
|
|
75
|
+
> = (options: {
|
|
76
|
+
value: TData
|
|
77
|
+
fieldApi: FieldApi<TParentData, TName, TFieldValidator, TFormValidator, TData>
|
|
78
|
+
signal: AbortSignal
|
|
79
|
+
}) => ValidationError | Promise<ValidationError>
|
|
43
80
|
|
|
44
|
-
type
|
|
81
|
+
export type FieldAsyncValidateOrFn<
|
|
45
82
|
TParentData,
|
|
46
83
|
TName extends DeepKeys<TParentData>,
|
|
47
|
-
|
|
48
|
-
|
|
84
|
+
TFieldValidator extends
|
|
85
|
+
| Validator<DeepValue<TParentData, TName>, unknown>
|
|
86
|
+
| undefined = undefined,
|
|
87
|
+
TFormValidator extends
|
|
88
|
+
| Validator<TParentData, unknown>
|
|
89
|
+
| undefined = undefined,
|
|
49
90
|
TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,
|
|
50
|
-
> =
|
|
51
|
-
?
|
|
52
|
-
| Parameters<ReturnType<ValidatorType>['validate']>[1]
|
|
53
|
-
| ValidateAsyncFn<TParentData, TName, ValidatorType, TData>
|
|
54
|
-
: FormValidator extends Validator<TData>
|
|
91
|
+
> = TFieldValidator extends Validator<TData, infer TFN>
|
|
55
92
|
?
|
|
56
|
-
|
|
|
57
|
-
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
93
|
+
| TFN
|
|
94
|
+
| FieldValidateAsyncFn<
|
|
95
|
+
TParentData,
|
|
96
|
+
TName,
|
|
97
|
+
TFieldValidator,
|
|
98
|
+
TFormValidator,
|
|
99
|
+
TData
|
|
100
|
+
>
|
|
101
|
+
: TFormValidator extends Validator<TParentData, infer FFN>
|
|
102
|
+
?
|
|
103
|
+
| FFN
|
|
104
|
+
| FieldValidateAsyncFn<
|
|
105
|
+
TParentData,
|
|
106
|
+
TName,
|
|
107
|
+
TFieldValidator,
|
|
108
|
+
TFormValidator,
|
|
109
|
+
TData
|
|
110
|
+
>
|
|
111
|
+
: FieldValidateAsyncFn<
|
|
112
|
+
TParentData,
|
|
113
|
+
TName,
|
|
114
|
+
TFieldValidator,
|
|
115
|
+
TFormValidator,
|
|
116
|
+
TData
|
|
117
|
+
>
|
|
118
|
+
|
|
119
|
+
export interface FieldValidators<
|
|
61
120
|
TParentData,
|
|
62
121
|
TName extends DeepKeys<TParentData>,
|
|
63
|
-
|
|
64
|
-
|
|
122
|
+
TFieldValidator extends
|
|
123
|
+
| Validator<DeepValue<TParentData, TName>, unknown>
|
|
124
|
+
| undefined = undefined,
|
|
125
|
+
TFormValidator extends
|
|
126
|
+
| Validator<TParentData, unknown>
|
|
127
|
+
| undefined = undefined,
|
|
65
128
|
TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,
|
|
66
129
|
> {
|
|
67
|
-
|
|
68
|
-
index?: TData extends any[] ? number : never
|
|
69
|
-
defaultValue?: TData
|
|
70
|
-
asyncDebounceMs?: number
|
|
71
|
-
asyncAlways?: boolean
|
|
72
|
-
preserveValue?: boolean
|
|
73
|
-
validator?: ValidatorType
|
|
74
|
-
onMount?: (
|
|
75
|
-
formApi: FieldApi<TParentData, TName, ValidatorType, TData>,
|
|
76
|
-
) => void
|
|
77
|
-
onChange?: ValidateOrFn<
|
|
130
|
+
onMount?: FieldValidateOrFn<
|
|
78
131
|
TParentData,
|
|
79
132
|
TName,
|
|
80
|
-
|
|
81
|
-
|
|
133
|
+
TFieldValidator,
|
|
134
|
+
TFormValidator,
|
|
82
135
|
TData
|
|
83
136
|
>
|
|
84
|
-
|
|
137
|
+
onChange?: FieldValidateOrFn<
|
|
85
138
|
TParentData,
|
|
86
139
|
TName,
|
|
87
|
-
|
|
88
|
-
|
|
140
|
+
TFieldValidator,
|
|
141
|
+
TFormValidator,
|
|
142
|
+
TData
|
|
143
|
+
>
|
|
144
|
+
onChangeAsync?: FieldAsyncValidateOrFn<
|
|
145
|
+
TParentData,
|
|
146
|
+
TName,
|
|
147
|
+
TFieldValidator,
|
|
148
|
+
TFormValidator,
|
|
89
149
|
TData
|
|
90
150
|
>
|
|
91
151
|
onChangeAsyncDebounceMs?: number
|
|
92
|
-
onBlur?:
|
|
93
|
-
onBlurAsync?: AsyncValidateOrFn<
|
|
152
|
+
onBlur?: FieldValidateOrFn<
|
|
94
153
|
TParentData,
|
|
95
154
|
TName,
|
|
96
|
-
|
|
97
|
-
|
|
155
|
+
TFieldValidator,
|
|
156
|
+
TFormValidator,
|
|
157
|
+
TData
|
|
158
|
+
>
|
|
159
|
+
onBlurAsync?: FieldAsyncValidateOrFn<
|
|
160
|
+
TParentData,
|
|
161
|
+
TName,
|
|
162
|
+
TFieldValidator,
|
|
163
|
+
TFormValidator,
|
|
98
164
|
TData
|
|
99
165
|
>
|
|
100
166
|
onBlurAsyncDebounceMs?: number
|
|
101
|
-
onSubmit?:
|
|
167
|
+
onSubmit?: FieldValidateOrFn<
|
|
168
|
+
TParentData,
|
|
169
|
+
TName,
|
|
170
|
+
TFieldValidator,
|
|
171
|
+
TFormValidator,
|
|
172
|
+
TData
|
|
173
|
+
>
|
|
174
|
+
onSubmitAsync?: FieldAsyncValidateOrFn<
|
|
102
175
|
TParentData,
|
|
103
176
|
TName,
|
|
104
|
-
|
|
105
|
-
|
|
177
|
+
TFieldValidator,
|
|
178
|
+
TFormValidator,
|
|
106
179
|
TData
|
|
107
180
|
>
|
|
108
|
-
|
|
181
|
+
onSubmitAsyncDebounceMs?: number
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export interface FieldOptions<
|
|
185
|
+
TParentData,
|
|
186
|
+
TName extends DeepKeys<TParentData>,
|
|
187
|
+
TFieldValidator extends
|
|
188
|
+
| Validator<DeepValue<TParentData, TName>, unknown>
|
|
189
|
+
| undefined = undefined,
|
|
190
|
+
TFormValidator extends
|
|
191
|
+
| Validator<TParentData, unknown>
|
|
192
|
+
| undefined = undefined,
|
|
193
|
+
TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,
|
|
194
|
+
> {
|
|
195
|
+
name: TName
|
|
196
|
+
index?: TData extends any[] ? number : never
|
|
197
|
+
defaultValue?: TData
|
|
198
|
+
asyncDebounceMs?: number
|
|
199
|
+
asyncAlways?: boolean
|
|
200
|
+
preserveValue?: boolean
|
|
201
|
+
validatorAdapter?: TFieldValidator
|
|
202
|
+
validators?: FieldValidators<
|
|
109
203
|
TParentData,
|
|
110
204
|
TName,
|
|
111
|
-
|
|
112
|
-
|
|
205
|
+
TFieldValidator,
|
|
206
|
+
TFormValidator,
|
|
113
207
|
TData
|
|
114
208
|
>
|
|
115
209
|
defaultMeta?: Partial<FieldMeta>
|
|
@@ -118,17 +212,21 @@ export interface FieldOptions<
|
|
|
118
212
|
export interface FieldApiOptions<
|
|
119
213
|
TParentData,
|
|
120
214
|
TName extends DeepKeys<TParentData>,
|
|
121
|
-
|
|
122
|
-
|
|
215
|
+
TFieldValidator extends
|
|
216
|
+
| Validator<DeepValue<TParentData, TName>, unknown>
|
|
217
|
+
| undefined = undefined,
|
|
218
|
+
TFormValidator extends
|
|
219
|
+
| Validator<TParentData, unknown>
|
|
220
|
+
| undefined = undefined,
|
|
123
221
|
TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,
|
|
124
222
|
> extends FieldOptions<
|
|
125
223
|
TParentData,
|
|
126
224
|
TName,
|
|
127
|
-
|
|
128
|
-
|
|
225
|
+
TFieldValidator,
|
|
226
|
+
TFormValidator,
|
|
129
227
|
TData
|
|
130
228
|
> {
|
|
131
|
-
form: FormApi<TParentData,
|
|
229
|
+
form: FormApi<TParentData, TFormValidator>
|
|
132
230
|
}
|
|
133
231
|
|
|
134
232
|
export type FieldMeta = {
|
|
@@ -153,14 +251,30 @@ export type ResolveName<TParentData> = unknown extends TParentData
|
|
|
153
251
|
export class FieldApi<
|
|
154
252
|
TParentData,
|
|
155
253
|
TName extends DeepKeys<TParentData>,
|
|
156
|
-
|
|
157
|
-
|
|
254
|
+
TFieldValidator extends
|
|
255
|
+
| Validator<DeepValue<TParentData, TName>, unknown>
|
|
256
|
+
| undefined = undefined,
|
|
257
|
+
TFormValidator extends
|
|
258
|
+
| Validator<TParentData, unknown>
|
|
259
|
+
| undefined = undefined,
|
|
158
260
|
TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,
|
|
159
261
|
> {
|
|
160
262
|
uid: number
|
|
161
|
-
form: FieldApiOptions<
|
|
263
|
+
form: FieldApiOptions<
|
|
264
|
+
TParentData,
|
|
265
|
+
TName,
|
|
266
|
+
TFieldValidator,
|
|
267
|
+
TFormValidator,
|
|
268
|
+
TData
|
|
269
|
+
>['form']
|
|
162
270
|
name!: DeepKeys<TParentData>
|
|
163
|
-
options: FieldApiOptions<
|
|
271
|
+
options: FieldApiOptions<
|
|
272
|
+
TParentData,
|
|
273
|
+
TName,
|
|
274
|
+
TFieldValidator,
|
|
275
|
+
TFormValidator,
|
|
276
|
+
TData
|
|
277
|
+
> = {} as any
|
|
164
278
|
store!: Store<FieldState<TData>>
|
|
165
279
|
state!: FieldState<TData>
|
|
166
280
|
prevState!: FieldState<TData>
|
|
@@ -169,8 +283,8 @@ export class FieldApi<
|
|
|
169
283
|
opts: FieldApiOptions<
|
|
170
284
|
TParentData,
|
|
171
285
|
TName,
|
|
172
|
-
|
|
173
|
-
|
|
286
|
+
TFieldValidator,
|
|
287
|
+
TFormValidator,
|
|
174
288
|
TData
|
|
175
289
|
>,
|
|
176
290
|
) {
|
|
@@ -224,6 +338,29 @@ export class FieldApi<
|
|
|
224
338
|
this.options = opts as never
|
|
225
339
|
}
|
|
226
340
|
|
|
341
|
+
runValidator<
|
|
342
|
+
TValue extends { value: TData; fieldApi: FieldApi<any, any, any, any> },
|
|
343
|
+
TType extends 'validate' | 'validateAsync',
|
|
344
|
+
>(props: {
|
|
345
|
+
validate: TType extends 'validate'
|
|
346
|
+
? FieldValidateOrFn<any, any, any, any>
|
|
347
|
+
: FieldAsyncValidateOrFn<any, any, any, any>
|
|
348
|
+
value: TValue
|
|
349
|
+
type: TType
|
|
350
|
+
}): ReturnType<ReturnType<Validator<any>>[TType]> {
|
|
351
|
+
const adapters = [
|
|
352
|
+
this.form.options.validatorAdapter,
|
|
353
|
+
this.options.validatorAdapter,
|
|
354
|
+
] as const
|
|
355
|
+
for (const adapter of adapters) {
|
|
356
|
+
if (adapter && typeof props.validate !== 'function') {
|
|
357
|
+
return adapter()[props.type](props.value, props.validate) as never
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
return (props.validate as FieldValidateFn<any, any>)(props.value) as never
|
|
362
|
+
}
|
|
363
|
+
|
|
227
364
|
mount = () => {
|
|
228
365
|
const info = this.getInfo()
|
|
229
366
|
info.instances[this.uid] = this as never
|
|
@@ -243,7 +380,25 @@ export class FieldApi<
|
|
|
243
380
|
})
|
|
244
381
|
|
|
245
382
|
this.update(this.options as never)
|
|
246
|
-
this.options.
|
|
383
|
+
const { onMount } = this.options.validators || {}
|
|
384
|
+
|
|
385
|
+
if (onMount) {
|
|
386
|
+
const error = this.runValidator({
|
|
387
|
+
validate: onMount,
|
|
388
|
+
value: {
|
|
389
|
+
value: this.state.value,
|
|
390
|
+
fieldApi: this,
|
|
391
|
+
},
|
|
392
|
+
type: 'validate',
|
|
393
|
+
})
|
|
394
|
+
if (error) {
|
|
395
|
+
this.setMeta((prev) => ({
|
|
396
|
+
...prev,
|
|
397
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
398
|
+
errorMap: { ...prev?.errorMap, onMount: error },
|
|
399
|
+
}))
|
|
400
|
+
}
|
|
401
|
+
}
|
|
247
402
|
|
|
248
403
|
return () => {
|
|
249
404
|
const preserveValue = this.options.preserveValue
|
|
@@ -260,7 +415,13 @@ export class FieldApi<
|
|
|
260
415
|
}
|
|
261
416
|
|
|
262
417
|
update = (
|
|
263
|
-
opts: FieldApiOptions<
|
|
418
|
+
opts: FieldApiOptions<
|
|
419
|
+
TParentData,
|
|
420
|
+
TName,
|
|
421
|
+
TFieldValidator,
|
|
422
|
+
TFormValidator,
|
|
423
|
+
TData
|
|
424
|
+
>,
|
|
264
425
|
) => {
|
|
265
426
|
// Default Value
|
|
266
427
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
@@ -330,52 +491,20 @@ export class FieldApi<
|
|
|
330
491
|
TSubData extends DeepValue<TData, TSubName> = DeepValue<TData, TSubName>,
|
|
331
492
|
>(
|
|
332
493
|
name: TSubName,
|
|
333
|
-
): FieldApi<
|
|
494
|
+
): FieldApi<
|
|
495
|
+
TData,
|
|
496
|
+
TSubName,
|
|
497
|
+
Validator<TSubData, unknown> | undefined,
|
|
498
|
+
Validator<TData, unknown> | undefined,
|
|
499
|
+
TSubData
|
|
500
|
+
> =>
|
|
334
501
|
new FieldApi({
|
|
335
502
|
name: `${this.name}.${name}` as never,
|
|
336
503
|
form: this.form,
|
|
337
504
|
}) as any
|
|
338
505
|
|
|
339
506
|
validateSync = (value = this.state.value, cause: ValidationCause) => {
|
|
340
|
-
const
|
|
341
|
-
|
|
342
|
-
const validates =
|
|
343
|
-
// https://github.com/TanStack/form/issues/490
|
|
344
|
-
cause === 'submit'
|
|
345
|
-
? ([
|
|
346
|
-
{ cause: 'change', validate: onChange },
|
|
347
|
-
{ cause: 'blur', validate: onBlur },
|
|
348
|
-
{ cause: 'submit', validate: onSubmit },
|
|
349
|
-
] as const)
|
|
350
|
-
: cause === 'change'
|
|
351
|
-
? ([{ cause: 'change', validate: onChange }] as const)
|
|
352
|
-
: ([{ cause: 'blur', validate: onBlur }] as const)
|
|
353
|
-
|
|
354
|
-
// Use the validationCount for all field instances to
|
|
355
|
-
// track freshness of the validation
|
|
356
|
-
const validationCount = (this.getInfo().validationCount || 0) + 1
|
|
357
|
-
this.getInfo().validationCount = validationCount
|
|
358
|
-
|
|
359
|
-
const doValidate = (validate: (typeof validates)[number]['validate']) => {
|
|
360
|
-
if (this.options.validator && typeof validate !== 'function') {
|
|
361
|
-
return (this.options.validator as Validator<TData>)().validate(
|
|
362
|
-
value,
|
|
363
|
-
validate,
|
|
364
|
-
)
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
if (this.form.options.validator && typeof validate !== 'function') {
|
|
368
|
-
return (this.form.options.validator as Validator<TData>)().validate(
|
|
369
|
-
value,
|
|
370
|
-
validate,
|
|
371
|
-
)
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
return (validate as ValidateFn<TParentData, TName, ValidatorType, TData>)(
|
|
375
|
-
value,
|
|
376
|
-
this as never,
|
|
377
|
-
)
|
|
378
|
-
}
|
|
507
|
+
const validates = getSyncValidatorArray(cause, this.options)
|
|
379
508
|
|
|
380
509
|
// Needs type cast as eslint errantly believes this is always falsy
|
|
381
510
|
let hasErrored = false as boolean
|
|
@@ -383,7 +512,13 @@ export class FieldApi<
|
|
|
383
512
|
this.form.store.batch(() => {
|
|
384
513
|
for (const validateObj of validates) {
|
|
385
514
|
if (!validateObj.validate) continue
|
|
386
|
-
const error = normalizeError(
|
|
515
|
+
const error = normalizeError(
|
|
516
|
+
this.runValidator({
|
|
517
|
+
validate: validateObj.validate,
|
|
518
|
+
value: { value, fieldApi: this },
|
|
519
|
+
type: 'validate',
|
|
520
|
+
}),
|
|
521
|
+
)
|
|
387
522
|
const errorMapKey = getErrorMapKey(validateObj.cause)
|
|
388
523
|
if (this.state.meta.errorMap[errorMapKey] !== error) {
|
|
389
524
|
this.setMeta((prev) => ({
|
|
@@ -419,130 +554,86 @@ export class FieldApi<
|
|
|
419
554
|
}))
|
|
420
555
|
}
|
|
421
556
|
|
|
422
|
-
// If a sync error is encountered for the errorMapKey (eg. onChange), cancel any async validation
|
|
423
|
-
if (hasErrored) {
|
|
424
|
-
this.cancelValidateAsync()
|
|
425
|
-
}
|
|
426
|
-
|
|
427
557
|
return { hasErrored }
|
|
428
558
|
}
|
|
429
559
|
|
|
430
|
-
__leaseValidateAsync = () => {
|
|
431
|
-
const count = (this.getInfo().validationAsyncCount || 0) + 1
|
|
432
|
-
this.getInfo().validationAsyncCount = count
|
|
433
|
-
return count
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
cancelValidateAsync = () => {
|
|
437
|
-
// Lease a new validation count to ignore any pending validations
|
|
438
|
-
this.__leaseValidateAsync()
|
|
439
|
-
// Cancel any pending validation state
|
|
440
|
-
this.setMeta((prev) => ({
|
|
441
|
-
...prev,
|
|
442
|
-
isValidating: false,
|
|
443
|
-
}))
|
|
444
|
-
}
|
|
445
|
-
|
|
446
560
|
validateAsync = async (value = this.state.value, cause: ValidationCause) => {
|
|
447
|
-
const
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
onSubmitAsync,
|
|
451
|
-
asyncDebounceMs,
|
|
452
|
-
onBlurAsyncDebounceMs,
|
|
453
|
-
onChangeAsyncDebounceMs,
|
|
454
|
-
} = this.options
|
|
455
|
-
|
|
456
|
-
const validate =
|
|
457
|
-
cause === 'change'
|
|
458
|
-
? onChangeAsync
|
|
459
|
-
: cause === 'submit'
|
|
460
|
-
? onSubmitAsync
|
|
461
|
-
: onBlurAsync
|
|
462
|
-
if (!validate) return []
|
|
463
|
-
const debounceMs =
|
|
464
|
-
cause === 'submit'
|
|
465
|
-
? 0
|
|
466
|
-
: (cause === 'change'
|
|
467
|
-
? onChangeAsyncDebounceMs
|
|
468
|
-
: onBlurAsyncDebounceMs) ??
|
|
469
|
-
asyncDebounceMs ??
|
|
470
|
-
0
|
|
471
|
-
|
|
472
|
-
if (this.state.meta.isValidating !== true) {
|
|
561
|
+
const validates = getAsyncValidatorArray(cause, this.options)
|
|
562
|
+
|
|
563
|
+
if (!this.state.meta.isValidating) {
|
|
473
564
|
this.setMeta((prev) => ({ ...prev, isValidating: true }))
|
|
474
565
|
}
|
|
475
566
|
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
const
|
|
481
|
-
validationAsyncCount === this.getInfo().validationAsyncCount
|
|
482
|
-
|
|
483
|
-
if (!this.getInfo().validationPromise) {
|
|
484
|
-
this.getInfo().validationPromise = new Promise((resolve, reject) => {
|
|
485
|
-
this.getInfo().validationResolve = resolve
|
|
486
|
-
this.getInfo().validationReject = reject
|
|
487
|
-
})
|
|
488
|
-
}
|
|
567
|
+
/**
|
|
568
|
+
* We have to use a for loop and generate our promises this way, otherwise it won't be sync
|
|
569
|
+
* when there are no validators needed to be run
|
|
570
|
+
*/
|
|
571
|
+
const promises: Promise<ValidationError | undefined>[] = []
|
|
489
572
|
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
573
|
+
for (const validateObj of validates) {
|
|
574
|
+
if (!validateObj.validate) continue
|
|
575
|
+
const key = getErrorMapKey(validateObj.cause)
|
|
576
|
+
const fieldValidatorMeta = this.getInfo().validationMetaMap[key]
|
|
493
577
|
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
return (this.options.validator as Validator<TData>)().validateAsync(
|
|
497
|
-
value,
|
|
498
|
-
validate,
|
|
499
|
-
)
|
|
500
|
-
}
|
|
578
|
+
fieldValidatorMeta?.lastAbortController.abort()
|
|
579
|
+
const controller = new AbortController()
|
|
501
580
|
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
this.form.options.validator as Validator<TData>
|
|
505
|
-
)().validateAsync(value, validate)
|
|
581
|
+
this.getInfo().validationMetaMap[key] = {
|
|
582
|
+
lastAbortController: controller,
|
|
506
583
|
}
|
|
507
584
|
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
585
|
+
promises.push(
|
|
586
|
+
new Promise<ValidationError | undefined>(async (resolve) => {
|
|
587
|
+
let rawError!: ValidationError | undefined
|
|
588
|
+
try {
|
|
589
|
+
rawError = await new Promise((rawResolve, rawReject) => {
|
|
590
|
+
setTimeout(async () => {
|
|
591
|
+
if (controller.signal.aborted) return rawResolve(undefined)
|
|
592
|
+
try {
|
|
593
|
+
rawResolve(
|
|
594
|
+
await this.runValidator({
|
|
595
|
+
validate: validateObj.validate,
|
|
596
|
+
value: {
|
|
597
|
+
value,
|
|
598
|
+
fieldApi: this,
|
|
599
|
+
signal: controller.signal,
|
|
600
|
+
},
|
|
601
|
+
type: 'validateAsync',
|
|
602
|
+
}),
|
|
603
|
+
)
|
|
604
|
+
} catch (e) {
|
|
605
|
+
rawReject(e)
|
|
606
|
+
}
|
|
607
|
+
}, validateObj.debounceMs)
|
|
608
|
+
})
|
|
609
|
+
} catch (e: unknown) {
|
|
610
|
+
rawError = e as ValidationError
|
|
611
|
+
}
|
|
612
|
+
const error = normalizeError(rawError)
|
|
613
|
+
this.setMeta((prev) => {
|
|
614
|
+
return {
|
|
615
|
+
...prev,
|
|
616
|
+
errorMap: {
|
|
617
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
618
|
+
...prev?.errorMap,
|
|
619
|
+
[getErrorMapKey(cause)]: error,
|
|
620
|
+
},
|
|
621
|
+
}
|
|
622
|
+
})
|
|
623
|
+
|
|
624
|
+
resolve(error)
|
|
625
|
+
}),
|
|
511
626
|
)
|
|
512
627
|
}
|
|
513
628
|
|
|
514
|
-
|
|
515
|
-
if (
|
|
516
|
-
|
|
517
|
-
try {
|
|
518
|
-
const rawError = await doValidate()
|
|
519
|
-
if (checkLatest()) {
|
|
520
|
-
const error = normalizeError(rawError)
|
|
521
|
-
this.setMeta((prev) => ({
|
|
522
|
-
...prev,
|
|
523
|
-
isValidating: false,
|
|
524
|
-
errorMap: {
|
|
525
|
-
...prev.errorMap,
|
|
526
|
-
[getErrorMapKey(cause)]: error,
|
|
527
|
-
},
|
|
528
|
-
}))
|
|
529
|
-
this.getInfo().validationResolve?.([...prevErrors, error])
|
|
530
|
-
}
|
|
531
|
-
} catch (error) {
|
|
532
|
-
if (checkLatest()) {
|
|
533
|
-
this.getInfo().validationReject?.([...prevErrors, error])
|
|
534
|
-
throw error
|
|
535
|
-
}
|
|
536
|
-
} finally {
|
|
537
|
-
if (checkLatest()) {
|
|
538
|
-
this.setMeta((prev) => ({ ...prev, isValidating: false }))
|
|
539
|
-
delete this.getInfo().validationPromise
|
|
540
|
-
}
|
|
541
|
-
}
|
|
629
|
+
let results: ValidationError[] = []
|
|
630
|
+
if (promises.length) {
|
|
631
|
+
results = await Promise.all(promises)
|
|
542
632
|
}
|
|
543
633
|
|
|
544
|
-
|
|
545
|
-
|
|
634
|
+
this.setMeta((prev) => ({ ...prev, isValidating: false }))
|
|
635
|
+
|
|
636
|
+
return results.filter(Boolean)
|
|
546
637
|
}
|
|
547
638
|
|
|
548
639
|
validate = (
|
|
@@ -559,10 +650,8 @@ export class FieldApi<
|
|
|
559
650
|
// Attempt to sync validate first
|
|
560
651
|
const { hasErrored } = this.validateSync(value, cause)
|
|
561
652
|
|
|
562
|
-
if (hasErrored) {
|
|
563
|
-
|
|
564
|
-
return this.state.meta.errors
|
|
565
|
-
}
|
|
653
|
+
if (hasErrored && !this.options.asyncAlways) {
|
|
654
|
+
return this.state.meta.errors
|
|
566
655
|
}
|
|
567
656
|
// No error? Attempt async validation
|
|
568
657
|
return this.validateAsync(value, cause)
|
|
@@ -598,11 +687,14 @@ function getErrorMapKey(cause: ValidationCause) {
|
|
|
598
687
|
switch (cause) {
|
|
599
688
|
case 'submit':
|
|
600
689
|
return 'onSubmit'
|
|
601
|
-
case 'change':
|
|
602
|
-
return 'onChange'
|
|
603
690
|
case 'blur':
|
|
604
691
|
return 'onBlur'
|
|
605
692
|
case 'mount':
|
|
606
693
|
return 'onMount'
|
|
694
|
+
case 'server':
|
|
695
|
+
return 'onServer'
|
|
696
|
+
case 'change':
|
|
697
|
+
default:
|
|
698
|
+
return 'onChange'
|
|
607
699
|
}
|
|
608
700
|
}
|