@tanstack/form-core 0.23.1 → 0.23.3

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/src/FieldApi.ts CHANGED
@@ -10,6 +10,9 @@ import type {
10
10
  import type { AsyncValidator, SyncValidator, Updater } from './utils'
11
11
  import type { DeepKeys, DeepValue, NoInfer } from './util-types'
12
12
 
13
+ /**
14
+ * @private
15
+ */
13
16
  export type FieldValidateFn<
14
17
  TParentData,
15
18
  TName extends DeepKeys<TParentData>,
@@ -25,6 +28,9 @@ export type FieldValidateFn<
25
28
  fieldApi: FieldApi<TParentData, TName, TFieldValidator, TFormValidator, TData>
26
29
  }) => ValidationError
27
30
 
31
+ /**
32
+ * @private
33
+ */
28
34
  export type FieldValidateOrFn<
29
35
  TParentData,
30
36
  TName extends DeepKeys<TParentData>,
@@ -63,6 +69,9 @@ export type FieldValidateOrFn<
63
69
  TData
64
70
  >
65
71
 
72
+ /**
73
+ * @private
74
+ */
66
75
  export type FieldValidateAsyncFn<
67
76
  TParentData,
68
77
  TName extends DeepKeys<TParentData>,
@@ -79,6 +88,9 @@ export type FieldValidateAsyncFn<
79
88
  signal: AbortSignal
80
89
  }) => ValidationError | Promise<ValidationError>
81
90
 
91
+ /**
92
+ * @private
93
+ */
82
94
  export type FieldAsyncValidateOrFn<
83
95
  TParentData,
84
96
  TName extends DeepKeys<TParentData>,
@@ -128,6 +140,9 @@ export interface FieldValidators<
128
140
  | undefined = undefined,
129
141
  TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,
130
142
  > {
143
+ /**
144
+ * An optional function that takes a param of `formApi` which is a generic type of `TData` and `TParentData`
145
+ */
131
146
  onMount?: FieldValidateOrFn<
132
147
  TParentData,
133
148
  TName,
@@ -135,6 +150,12 @@ export interface FieldValidators<
135
150
  TFormValidator,
136
151
  TData
137
152
  >
153
+ /**
154
+ * An optional property that takes a `ValidateFn` which is a generic of `TData` and `TParentData`.
155
+ * If `validatorAdapter` is passed, this may also accept a property from the respective adapter
156
+ *
157
+ * @example `z.string().min(1)` if `zodAdapter` is passed
158
+ */
138
159
  onChange?: FieldValidateOrFn<
139
160
  TParentData,
140
161
  TName,
@@ -142,6 +163,12 @@ export interface FieldValidators<
142
163
  TFormValidator,
143
164
  TData
144
165
  >
166
+ /**
167
+ * An optional property similar to `onChange` but async validation. If `validatorAdapter`
168
+ * is passed, this may also accept a property from the respective adapter
169
+ *
170
+ * @example `z.string().refine(async (val) => val.length > 3, { message: 'Testing 123' })` if `zodAdapter` is passed
171
+ */
145
172
  onChangeAsync?: FieldAsyncValidateOrFn<
146
173
  TParentData,
147
174
  TName,
@@ -149,8 +176,22 @@ export interface FieldValidators<
149
176
  TFormValidator,
150
177
  TData
151
178
  >
179
+ /**
180
+ * An optional number to represent how long the `onChangeAsync` should wait before running
181
+ *
182
+ * If set to a number larger than 0, will debounce the async validation event by this length of time in milliseconds
183
+ */
152
184
  onChangeAsyncDebounceMs?: number
185
+ /**
186
+ * An optional list of field names that should trigger this field's `onChange` and `onChangeAsync` events when its value changes
187
+ */
153
188
  onChangeListenTo?: DeepKeys<TParentData>[]
189
+ /**
190
+ * An optional function, that when run when subscribing to blur event of input.
191
+ * If `validatorAdapter` is passed, this may also accept a property from the respective adapter
192
+ *
193
+ * @example `z.string().min(1)` if `zodAdapter` is passed
194
+ */
154
195
  onBlur?: FieldValidateOrFn<
155
196
  TParentData,
156
197
  TName,
@@ -158,6 +199,12 @@ export interface FieldValidators<
158
199
  TFormValidator,
159
200
  TData
160
201
  >
202
+ /**
203
+ * An optional property similar to `onBlur` but async validation. If `validatorAdapter`
204
+ * is passed, this may also accept a property from the respective adapter
205
+ *
206
+ * @example `z.string().refine(async (val) => val.length > 3, { message: 'Testing 123' })` if `zodAdapter` is passed
207
+ */
161
208
  onBlurAsync?: FieldAsyncValidateOrFn<
162
209
  TParentData,
163
210
  TName,
@@ -165,8 +212,23 @@ export interface FieldValidators<
165
212
  TFormValidator,
166
213
  TData
167
214
  >
215
+
216
+ /**
217
+ * An optional number to represent how long the `onBlurAsync` should wait before running
218
+ *
219
+ * If set to a number larger than 0, will debounce the async validation event by this length of time in milliseconds
220
+ */
168
221
  onBlurAsyncDebounceMs?: number
222
+ /**
223
+ * An optional list of field names that should trigger this field's `onBlur` and `onBlurAsync` events when its value changes
224
+ */
169
225
  onBlurListenTo?: DeepKeys<TParentData>[]
226
+ /**
227
+ * An optional function, that when run when subscribing to submit event of input.
228
+ * If `validatorAdapter` is passed, this may also accept a property from the respective adapter
229
+ *
230
+ * @example `z.string().min(1)` if `zodAdapter` is passed
231
+ */
170
232
  onSubmit?: FieldValidateOrFn<
171
233
  TParentData,
172
234
  TName,
@@ -174,6 +236,12 @@ export interface FieldValidators<
174
236
  TFormValidator,
175
237
  TData
176
238
  >
239
+ /**
240
+ * An optional property similar to `onSubmit` but async validation. If `validatorAdapter`
241
+ * is passed, this may also accept a property from the respective adapter
242
+ *
243
+ * @example `z.string().refine(async (val) => val.length > 3, { message: 'Testing 123' })` if `zodAdapter` is passed
244
+ */
177
245
  onSubmitAsync?: FieldAsyncValidateOrFn<
178
246
  TParentData,
179
247
  TName,
@@ -183,6 +251,9 @@ export interface FieldValidators<
183
251
  >
184
252
  }
185
253
 
254
+ /**
255
+ * An object type representing the options for a field in a form.
256
+ */
186
257
  export interface FieldOptions<
187
258
  TParentData,
188
259
  TName extends DeepKeys<TParentData>,
@@ -194,12 +265,30 @@ export interface FieldOptions<
194
265
  | undefined = undefined,
195
266
  TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,
196
267
  > {
268
+ /**
269
+ * The field name. The type will be `DeepKeys<TParentData>` to ensure your name is a deep key of the parent dataset.
270
+ */
197
271
  name: TName
272
+ /**
273
+ * An optional default value for the field.
274
+ */
198
275
  defaultValue?: NoInfer<TData>
276
+ /**
277
+ * The default time to debounce async validation if there is not a more specific debounce time passed.
278
+ */
199
279
  asyncDebounceMs?: number
280
+ /**
281
+ * If `true`, always run async validation, even if there are errors emitted during synchronous validation.
282
+ */
200
283
  asyncAlways?: boolean
201
284
  preserveValue?: boolean
285
+ /**
286
+ * A validator provided by an extension, like `yupValidator` from `@tanstack/yup-form-adapter`
287
+ */
202
288
  validatorAdapter?: TFieldValidator
289
+ /**
290
+ * A list of validators to pass to the field
291
+ */
203
292
  validators?: FieldValidators<
204
293
  TParentData,
205
294
  TName,
@@ -207,9 +296,15 @@ export interface FieldOptions<
207
296
  TFormValidator,
208
297
  TData
209
298
  >
299
+ /**
300
+ * An optional object with default metadata for the field.
301
+ */
210
302
  defaultMeta?: Partial<FieldMeta>
211
303
  }
212
304
 
305
+ /**
306
+ * An object type representing the required options for the FieldApi class.
307
+ */
213
308
  export interface FieldApiOptions<
214
309
  TParentData,
215
310
  TName extends DeepKeys<TParentData>,
@@ -230,25 +325,63 @@ export interface FieldApiOptions<
230
325
  form: FormApi<TParentData, TFormValidator>
231
326
  }
232
327
 
328
+ /**
329
+ * An object type representing the metadata of a field in a form.
330
+ */
233
331
  export type FieldMeta = {
332
+ /**
333
+ * A flag indicating whether the field has been touched.
334
+ */
234
335
  isTouched: boolean
336
+ /**
337
+ * A flag that is `true` if the field's value has not been modified by the user. Opposite of `isDirty`.
338
+ */
235
339
  isPristine: boolean
340
+ /**
341
+ * A flag that is `true` if the field's value has been modified by the user. Opposite of `isPristine`.
342
+ */
236
343
  isDirty: boolean
344
+ /**
345
+ * An array of errors related to the touched state of the field.
346
+ */
237
347
  touchedErrors: ValidationError[]
348
+ /**
349
+ * An array of errors related to the field value.
350
+ */
238
351
  errors: ValidationError[]
352
+ /**
353
+ * A map of errors related to the field value.
354
+ */
239
355
  errorMap: ValidationErrorMap
356
+ /**
357
+ * A flag indicating whether the field is currently being validated.
358
+ */
240
359
  isValidating: boolean
241
360
  }
242
361
 
362
+ /**
363
+ * An object type representing the state of a field.
364
+ */
243
365
  export type FieldState<TData> = {
366
+ /**
367
+ * The current value of the field.
368
+ */
244
369
  value: TData
370
+ /**
371
+ * The current metadata of the field.
372
+ */
245
373
  meta: FieldMeta
246
374
  }
247
375
 
248
- export type ResolveName<TParentData> = unknown extends TParentData
249
- ? string
250
- : DeepKeys<TParentData>
251
-
376
+ /**
377
+ * A class representing the API for managing a form field.
378
+ *
379
+ * Normally, you will not need to create a new `FieldApi` instance directly.
380
+ * Instead, you will use a framework hook/function like `useField` or `createField`
381
+ * to create a new instance for you that uses your framework's reactivity model.
382
+ * However, if you need to create a new instance manually, you can do so by calling
383
+ * the `new FieldApi` constructor.
384
+ */
252
385
  export class FieldApi<
253
386
  TParentData,
254
387
  TName extends DeepKeys<TParentData>,
@@ -260,6 +393,9 @@ export class FieldApi<
260
393
  | undefined = undefined,
261
394
  TData extends DeepValue<TParentData, TName> = DeepValue<TParentData, TName>,
262
395
  > {
396
+ /**
397
+ * A reference to the form API instance.
398
+ */
263
399
  form: FieldApiOptions<
264
400
  TParentData,
265
401
  TName,
@@ -267,7 +403,13 @@ export class FieldApi<
267
403
  TFormValidator,
268
404
  TData
269
405
  >['form']
406
+ /**
407
+ * The field name.
408
+ */
270
409
  name!: DeepKeys<TParentData>
410
+ /**
411
+ * The field options.
412
+ */
271
413
  options: FieldApiOptions<
272
414
  TParentData,
273
415
  TName,
@@ -275,10 +417,22 @@ export class FieldApi<
275
417
  TFormValidator,
276
418
  TData
277
419
  > = {} as any
420
+ /**
421
+ * The field state store.
422
+ */
278
423
  store!: Store<FieldState<TData>>
424
+ /**
425
+ * The current field state.
426
+ */
279
427
  state!: FieldState<TData>
428
+ /**
429
+ * @private
430
+ */
280
431
  prevState!: FieldState<TData>
281
432
 
433
+ /**
434
+ * Initializes a new `FieldApi` instance.
435
+ */
282
436
  constructor(
283
437
  opts: FieldApiOptions<
284
438
  TParentData,
@@ -291,6 +445,14 @@ export class FieldApi<
291
445
  this.form = opts.form as never
292
446
  this.name = opts.name as never
293
447
 
448
+ if (
449
+ opts.defaultValue === undefined &&
450
+ this.form._tempDefaultValue !== undefined &&
451
+ this.form._tempDefaultValue.field === this.name
452
+ ) {
453
+ opts.defaultValue = this.form._tempDefaultValue.value as never
454
+ }
455
+
294
456
  if (opts.defaultValue !== undefined) {
295
457
  this.form.setFieldValue(this.name, opts.defaultValue as never)
296
458
  }
@@ -326,6 +488,17 @@ export class FieldApi<
326
488
 
327
489
  this.prevState = state
328
490
  this.state = state
491
+
492
+ // Cleanup the temp value after this "tick"
493
+ // (Everything occurs sync otherwise)
494
+ setTimeout(() => {
495
+ if (
496
+ this.form._tempDefaultValue !== undefined &&
497
+ this.form._tempDefaultValue.field === this.name
498
+ ) {
499
+ this.form._tempDefaultValue = undefined
500
+ }
501
+ }, 0)
329
502
  },
330
503
  },
331
504
  )
@@ -335,6 +508,9 @@ export class FieldApi<
335
508
  this.options = opts as never
336
509
  }
337
510
 
511
+ /**
512
+ * @private
513
+ */
338
514
  runValidator<
339
515
  TValue extends { value: TData; fieldApi: FieldApi<any, any, any, any> },
340
516
  TType extends 'validate' | 'validateAsync',
@@ -361,6 +537,9 @@ export class FieldApi<
361
537
  return (props.validate as FieldValidateFn<any, any>)(props.value) as never
362
538
  }
363
539
 
540
+ /**
541
+ * Mounts the field instance to the form.
542
+ */
364
543
  mount = () => {
365
544
  const info = this.getInfo()
366
545
  info.instance = this as never
@@ -409,6 +588,9 @@ export class FieldApi<
409
588
  }
410
589
  }
411
590
 
591
+ /**
592
+ * Updates the field instance with new options.
593
+ */
412
594
  update = (
413
595
  opts: FieldApiOptions<
414
596
  TParentData,
@@ -435,13 +617,27 @@ export class FieldApi<
435
617
  this.setMeta(this.state.meta)
436
618
  }
437
619
 
620
+ if (
621
+ opts.defaultValue === undefined &&
622
+ this.form._tempDefaultValue !== undefined &&
623
+ this.form._tempDefaultValue.field === this.name
624
+ ) {
625
+ opts.defaultValue = this.form._tempDefaultValue.value as never
626
+ }
627
+
438
628
  this.options = opts as never
439
629
  }
440
630
 
631
+ /**
632
+ * Gets the current field value.
633
+ */
441
634
  getValue = (): TData => {
442
635
  return this.form.getFieldValue(this.name) as TData
443
636
  }
444
637
 
638
+ /**
639
+ * Sets the field value and run the `change` validator.
640
+ */
445
641
  setValue = (
446
642
  updater: Updater<TData>,
447
643
  options?: { touch?: boolean; notify?: boolean },
@@ -450,7 +646,14 @@ export class FieldApi<
450
646
  this.validate('change')
451
647
  }
452
648
 
649
+ /**
650
+ * @private
651
+ */
453
652
  _getMeta = () => this.form.getFieldMeta(this.name)
653
+
654
+ /**
655
+ * Gets the current field metadata.
656
+ */
454
657
  getMeta = () =>
455
658
  this._getMeta() ??
456
659
  ({
@@ -464,37 +667,64 @@ export class FieldApi<
464
667
  ...this.options.defaultMeta,
465
668
  } as FieldMeta)
466
669
 
670
+ /**
671
+ * Sets the field metadata.
672
+ */
467
673
  setMeta = (updater: Updater<FieldMeta>) =>
468
674
  this.form.setFieldMeta(this.name, updater)
469
675
 
676
+ /**
677
+ * Gets the field information object.
678
+ */
470
679
  getInfo = () => this.form.getFieldInfo(this.name)
471
680
 
681
+ /**
682
+ * Pushes a new value to the field.
683
+ */
472
684
  pushValue = (
473
685
  value: TData extends any[] ? TData[number] : never,
474
686
  opts?: { touch?: boolean },
475
687
  ) => this.form.pushFieldValue(this.name, value as any, opts)
476
688
 
689
+ /**
690
+ * Inserts a value at the specified index, shifting the subsequent values to the right.
691
+ */
477
692
  insertValue = (
478
693
  index: number,
479
694
  value: TData extends any[] ? TData[number] : never,
480
695
  opts?: { touch?: boolean },
481
696
  ) => this.form.insertFieldValue(this.name, index, value as any, opts)
482
697
 
698
+ /**
699
+ * Replaces a value at the specified index.
700
+ */
483
701
  replaceValue = (
484
702
  index: number,
485
703
  value: TData extends any[] ? TData[number] : never,
486
704
  opts?: { touch?: boolean },
487
705
  ) => this.form.replaceFieldValue(this.name, index, value as any, opts)
488
706
 
707
+ /**
708
+ * Removes a value at the specified index.
709
+ */
489
710
  removeValue = (index: number, opts?: { touch: boolean }) =>
490
711
  this.form.removeFieldValue(this.name, index, opts)
491
712
 
713
+ /**
714
+ * Swaps the values at the specified indices.
715
+ */
492
716
  swapValues = (aIndex: number, bIndex: number, opts?: { touch?: boolean }) =>
493
717
  this.form.swapFieldValues(this.name, aIndex, bIndex, opts)
494
718
 
719
+ /**
720
+ * Moves the value at the first specified index to the second specified index.
721
+ */
495
722
  moveValue = (aIndex: number, bIndex: number, opts?: { touch?: boolean }) =>
496
723
  this.form.moveFieldValues(this.name, aIndex, bIndex, opts)
497
724
 
725
+ /**
726
+ * @private
727
+ */
498
728
  getLinkedFields = (cause: ValidationCause) => {
499
729
  const fields = Object.values(this.form.fieldInfo) as FieldInfo<
500
730
  any,
@@ -520,6 +750,9 @@ export class FieldApi<
520
750
  return linkedFields
521
751
  }
522
752
 
753
+ /**
754
+ * @private
755
+ */
523
756
  validateSync = (cause: ValidationCause) => {
524
757
  const validates = getSyncValidatorArray(cause, this.options)
525
758
 
@@ -597,6 +830,9 @@ export class FieldApi<
597
830
  return { hasErrored }
598
831
  }
599
832
 
833
+ /**
834
+ * @private
835
+ */
600
836
  validateAsync = async (cause: ValidationCause) => {
601
837
  const validates = getAsyncValidatorArray(cause, this.options)
602
838
 
@@ -717,6 +953,9 @@ export class FieldApi<
717
953
  return results.filter(Boolean)
718
954
  }
719
955
 
956
+ /**
957
+ * Validates the field value.
958
+ */
720
959
  validate = (
721
960
  cause: ValidationCause,
722
961
  ): ValidationError[] | Promise<ValidationError[]> => {
@@ -737,10 +976,16 @@ export class FieldApi<
737
976
  return this.validateAsync(cause)
738
977
  }
739
978
 
979
+ /**
980
+ * Handles the change event.
981
+ */
740
982
  handleChange = (updater: Updater<TData>) => {
741
983
  this.setValue(updater, { touch: true })
742
984
  }
743
985
 
986
+ /**
987
+ * Handles the blur event.
988
+ */
744
989
  handleBlur = () => {
745
990
  const prevTouched = this.state.meta.isTouched
746
991
  if (!prevTouched) {