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