@tanstack/form-core 0.10.3 → 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 +91 -118
- 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 +91 -118
- package/build/legacy/FieldApi.js.map +1 -1
- package/build/legacy/FormApi.cjs +125 -118
- 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 +127 -118
- package/build/legacy/FormApi.js.map +1 -1
- package/build/legacy/index.d.cts +160 -73
- package/build/legacy/index.d.ts +160 -73
- 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 +91 -116
- 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 +91 -116
- package/build/modern/FieldApi.js.map +1 -1
- package/build/modern/FormApi.cjs +125 -117
- 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 +127 -117
- package/build/modern/FormApi.js.map +1 -1
- package/build/modern/index.d.cts +160 -73
- package/build/modern/index.d.ts +160 -73
- 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 +308 -231
- package/src/FormApi.ts +259 -209
- package/src/tests/FieldApi.spec.ts +135 -48
- package/src/tests/FieldApi.test-d.ts +10 -6
- package/src/tests/FormApi.spec.ts +170 -62
- 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
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
formApi: FieldApi<TParentData, TName, ValidatorType, TData>,
|
|
76
|
-
) => void
|
|
77
|
-
onChange?: ValidateOrFn<
|
|
124
|
+
onMount?: FieldValidateOrFn<
|
|
125
|
+
TParentData,
|
|
126
|
+
TName,
|
|
127
|
+
TFieldValidator,
|
|
128
|
+
TFormValidator,
|
|
129
|
+
TData
|
|
130
|
+
>
|
|
131
|
+
onChange?: FieldValidateOrFn<
|
|
78
132
|
TParentData,
|
|
79
133
|
TName,
|
|
80
|
-
|
|
81
|
-
|
|
134
|
+
TFieldValidator,
|
|
135
|
+
TFormValidator,
|
|
82
136
|
TData
|
|
83
137
|
>
|
|
84
|
-
onChangeAsync?:
|
|
138
|
+
onChangeAsync?: FieldAsyncValidateOrFn<
|
|
85
139
|
TParentData,
|
|
86
140
|
TName,
|
|
87
|
-
|
|
88
|
-
|
|
141
|
+
TFieldValidator,
|
|
142
|
+
TFormValidator,
|
|
89
143
|
TData
|
|
90
144
|
>
|
|
91
145
|
onChangeAsyncDebounceMs?: number
|
|
92
|
-
onBlur?:
|
|
93
|
-
|
|
146
|
+
onBlur?: FieldValidateOrFn<
|
|
147
|
+
TParentData,
|
|
148
|
+
TName,
|
|
149
|
+
TFieldValidator,
|
|
150
|
+
TFormValidator,
|
|
151
|
+
TData
|
|
152
|
+
>
|
|
153
|
+
onBlurAsync?: FieldAsyncValidateOrFn<
|
|
94
154
|
TParentData,
|
|
95
155
|
TName,
|
|
96
|
-
|
|
97
|
-
|
|
156
|
+
TFieldValidator,
|
|
157
|
+
TFormValidator,
|
|
98
158
|
TData
|
|
99
159
|
>
|
|
100
160
|
onBlurAsyncDebounceMs?: number
|
|
101
|
-
onSubmit?:
|
|
161
|
+
onSubmit?: FieldValidateOrFn<
|
|
162
|
+
TParentData,
|
|
163
|
+
TName,
|
|
164
|
+
TFieldValidator,
|
|
165
|
+
TFormValidator,
|
|
166
|
+
TData
|
|
167
|
+
>
|
|
168
|
+
onSubmitAsync?: FieldAsyncValidateOrFn<
|
|
102
169
|
TParentData,
|
|
103
170
|
TName,
|
|
104
|
-
|
|
105
|
-
|
|
171
|
+
TFieldValidator,
|
|
172
|
+
TFormValidator,
|
|
106
173
|
TData
|
|
107
174
|
>
|
|
108
|
-
|
|
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<
|
|
109
197
|
TParentData,
|
|
110
198
|
TName,
|
|
111
|
-
|
|
112
|
-
|
|
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,52 +485,20 @@ 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
504
|
let hasErrored = false as boolean
|
|
@@ -383,7 +506,13 @@ export class FieldApi<
|
|
|
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) => ({
|
|
@@ -419,130 +548,80 @@ export class FieldApi<
|
|
|
419
548
|
}))
|
|
420
549
|
}
|
|
421
550
|
|
|
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
551
|
return { hasErrored }
|
|
428
552
|
}
|
|
429
553
|
|
|
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
554
|
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) {
|
|
555
|
+
const validates = getAsyncValidatorArray(cause, this.options)
|
|
556
|
+
|
|
557
|
+
if (!this.state.meta.isValidating) {
|
|
473
558
|
this.setMeta((prev) => ({ ...prev, isValidating: true }))
|
|
474
559
|
}
|
|
475
560
|
|
|
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
|
-
}
|
|
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>[] = []
|
|
489
566
|
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
567
|
+
for (const validateObj of validates) {
|
|
568
|
+
if (!validateObj.validate) continue
|
|
569
|
+
const key = getErrorMapKey(validateObj.cause)
|
|
570
|
+
const fieldValidatorMeta = this.getInfo().validationMetaMap[key]
|
|
493
571
|
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
validate,
|
|
499
|
-
)
|
|
500
|
-
}
|
|
572
|
+
fieldValidatorMeta?.lastAbortController.abort()
|
|
573
|
+
// Sorry Safari 12
|
|
574
|
+
// eslint-disable-next-line compat/compat
|
|
575
|
+
const controller = new AbortController()
|
|
501
576
|
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
this.form.options.validator as Validator<TData>
|
|
505
|
-
)().validateAsync(value, validate)
|
|
577
|
+
this.getInfo().validationMetaMap[key] = {
|
|
578
|
+
lastAbortController: controller,
|
|
506
579
|
}
|
|
507
580
|
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
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
|
+
}),
|
|
511
614
|
)
|
|
512
615
|
}
|
|
513
616
|
|
|
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
|
-
}
|
|
617
|
+
let results: ValidationError[] = []
|
|
618
|
+
if (promises.length) {
|
|
619
|
+
results = await Promise.all(promises)
|
|
542
620
|
}
|
|
543
621
|
|
|
544
|
-
|
|
545
|
-
|
|
622
|
+
this.setMeta((prev) => ({ ...prev, isValidating: false }))
|
|
623
|
+
|
|
624
|
+
return results.filter(Boolean)
|
|
546
625
|
}
|
|
547
626
|
|
|
548
627
|
validate = (
|
|
@@ -559,10 +638,8 @@ export class FieldApi<
|
|
|
559
638
|
// Attempt to sync validate first
|
|
560
639
|
const { hasErrored } = this.validateSync(value, cause)
|
|
561
640
|
|
|
562
|
-
if (hasErrored) {
|
|
563
|
-
|
|
564
|
-
return this.state.meta.errors
|
|
565
|
-
}
|
|
641
|
+
if (hasErrored && !this.options.asyncAlways) {
|
|
642
|
+
return this.state.meta.errors
|
|
566
643
|
}
|
|
567
644
|
// No error? Attempt async validation
|
|
568
645
|
return this.validateAsync(value, cause)
|