@tanstack/form-core 1.26.0 → 1.27.1
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.cjs +45 -51
- package/dist/cjs/FieldApi.cjs.map +1 -1
- package/dist/cjs/FieldApi.d.cts +6 -2
- package/dist/cjs/FormApi.cjs +159 -159
- package/dist/cjs/FormApi.cjs.map +1 -1
- package/dist/cjs/FormApi.d.cts +9 -4
- package/dist/cjs/utils.cjs +1 -1
- package/dist/cjs/utils.cjs.map +1 -1
- package/dist/esm/FieldApi.d.ts +6 -2
- package/dist/esm/FieldApi.js +45 -51
- package/dist/esm/FieldApi.js.map +1 -1
- package/dist/esm/FormApi.d.ts +9 -4
- package/dist/esm/FormApi.js +160 -160
- package/dist/esm/FormApi.js.map +1 -1
- package/dist/esm/utils.js +1 -1
- package/dist/esm/utils.js.map +1 -1
- package/package.json +1 -1
- package/src/FieldApi.ts +8 -3
- package/src/FormApi.ts +16 -6
- package/src/utils.ts +4 -1
package/dist/esm/FormApi.d.ts
CHANGED
|
@@ -308,6 +308,10 @@ export type AnyFormState = FormState<any, any, any, any, any, any, any, any, any
|
|
|
308
308
|
* A type representing the Form API with all generics set to `any` for convenience.
|
|
309
309
|
*/
|
|
310
310
|
export type AnyFormApi = FormApi<any, any, any, any, any, any, any, any, any, any, any, any>;
|
|
311
|
+
/**
|
|
312
|
+
* We cannot use methods and must use arrow functions. Otherwise, our React adapters
|
|
313
|
+
* will break due to loss of the method when using spread.
|
|
314
|
+
*/
|
|
311
315
|
/**
|
|
312
316
|
* A class representing the Form API. It handles the logic and interactions with the form state.
|
|
313
317
|
*
|
|
@@ -343,7 +347,7 @@ export declare class FormApi<in out TFormData, in out TOnMount extends undefined
|
|
|
343
347
|
/**
|
|
344
348
|
* @private
|
|
345
349
|
*/
|
|
346
|
-
|
|
350
|
+
_formId: string;
|
|
347
351
|
/**
|
|
348
352
|
* @private
|
|
349
353
|
*/
|
|
@@ -406,11 +410,12 @@ export declare class FormApi<in out TFormData, in out TOnMount extends undefined
|
|
|
406
410
|
* @private
|
|
407
411
|
*/
|
|
408
412
|
validate: (cause: ValidationCause) => FormErrorMapFromValidator<TFormData, TOnMount, TOnChange, TOnChangeAsync, TOnBlur, TOnBlurAsync, TOnSubmit, TOnSubmitAsync, TOnDynamic, TOnDynamicAsync> | Promise<FormErrorMapFromValidator<TFormData, TOnMount, TOnChange, TOnChangeAsync, TOnBlur, TOnBlurAsync, TOnSubmit, TOnSubmitAsync, TOnDynamic, TOnDynamicAsync>>;
|
|
413
|
+
handleSubmit(): Promise<void>;
|
|
414
|
+
handleSubmit(submitMeta: TSubmitMeta): Promise<void>;
|
|
409
415
|
/**
|
|
410
416
|
* Handles the form submission, performs validation, and calls the appropriate onSubmit or onSubmitInvalid callbacks.
|
|
411
417
|
*/
|
|
412
|
-
|
|
413
|
-
handleSubmit(submitMeta: TSubmitMeta): Promise<void>;
|
|
418
|
+
_handleSubmit: (submitMeta?: TSubmitMeta) => Promise<void>;
|
|
414
419
|
/**
|
|
415
420
|
* Gets the value of the specified field.
|
|
416
421
|
*/
|
|
@@ -468,7 +473,7 @@ export declare class FormApi<in out TFormData, in out TOnMount extends undefined
|
|
|
468
473
|
/**
|
|
469
474
|
* Updates the form's errorMap
|
|
470
475
|
*/
|
|
471
|
-
setErrorMap(errorMap: FormValidationErrorMap<TFormData, UnwrapFormValidateOrFn<TOnMount>, UnwrapFormValidateOrFn<TOnChange>, UnwrapFormAsyncValidateOrFn<TOnChangeAsync>, UnwrapFormValidateOrFn<TOnBlur>, UnwrapFormAsyncValidateOrFn<TOnBlurAsync>, UnwrapFormValidateOrFn<TOnSubmit>, UnwrapFormAsyncValidateOrFn<TOnSubmitAsync>, UnwrapFormValidateOrFn<TOnDynamic>, UnwrapFormAsyncValidateOrFn<TOnDynamicAsync>, UnwrapFormAsyncValidateOrFn<TOnServer>>)
|
|
476
|
+
setErrorMap: (errorMap: FormValidationErrorMap<TFormData, UnwrapFormValidateOrFn<TOnMount>, UnwrapFormValidateOrFn<TOnChange>, UnwrapFormAsyncValidateOrFn<TOnChangeAsync>, UnwrapFormValidateOrFn<TOnBlur>, UnwrapFormAsyncValidateOrFn<TOnBlurAsync>, UnwrapFormValidateOrFn<TOnSubmit>, UnwrapFormAsyncValidateOrFn<TOnSubmitAsync>, UnwrapFormValidateOrFn<TOnDynamic>, UnwrapFormAsyncValidateOrFn<TOnDynamicAsync>, UnwrapFormAsyncValidateOrFn<TOnServer>>) => void;
|
|
472
477
|
/**
|
|
473
478
|
* Returns form and field level errors
|
|
474
479
|
*/
|
package/dist/esm/FormApi.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { batch, Store, Derived } from "@tanstack/store";
|
|
2
2
|
import { throttle } from "@tanstack/pacer";
|
|
3
|
-
import { evaluate, getSyncValidatorArray, determineFormLevelErrorSourceAndValue, getAsyncValidatorArray, getBy, functionalUpdate, setBy, deleteBy, mergeOpts, uuid, isNonEmptyArray
|
|
3
|
+
import { evaluate, getSyncValidatorArray, determineFormLevelErrorSourceAndValue, getAsyncValidatorArray, getBy, functionalUpdate, setBy, deleteBy, mergeOpts, isGlobalFormValidationError, uuid, isNonEmptyArray } from "./utils.js";
|
|
4
4
|
import { defaultValidationLogic } from "./ValidationLogic.js";
|
|
5
5
|
import { standardSchemaValidators, isStandardSchemaValidator } from "./standardSchemaValidator.js";
|
|
6
6
|
import { defaultFieldMeta, metaHelper } from "./metaHelper.js";
|
|
@@ -403,6 +403,121 @@ class FormApi {
|
|
|
403
403
|
}
|
|
404
404
|
return this.validateAsync(cause);
|
|
405
405
|
};
|
|
406
|
+
this._handleSubmit = async (submitMeta) => {
|
|
407
|
+
this.baseStore.setState((old) => ({
|
|
408
|
+
...old,
|
|
409
|
+
// Submission attempts mark the form as not submitted
|
|
410
|
+
isSubmitted: false,
|
|
411
|
+
// Count submission attempts
|
|
412
|
+
submissionAttempts: old.submissionAttempts + 1,
|
|
413
|
+
isSubmitSuccessful: false
|
|
414
|
+
// Reset isSubmitSuccessful at the start of submission
|
|
415
|
+
}));
|
|
416
|
+
batch(() => {
|
|
417
|
+
void Object.values(this.fieldInfo).forEach(
|
|
418
|
+
(field) => {
|
|
419
|
+
if (!field.instance) return;
|
|
420
|
+
if (!field.instance.state.meta.isTouched) {
|
|
421
|
+
field.instance.setMeta((prev) => ({ ...prev, isTouched: true }));
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
);
|
|
425
|
+
});
|
|
426
|
+
const submitMetaArg = submitMeta ?? this.options.onSubmitMeta;
|
|
427
|
+
if (!this.state.canSubmit && !this._devtoolsSubmissionOverride) {
|
|
428
|
+
this.options.onSubmitInvalid?.({
|
|
429
|
+
value: this.state.values,
|
|
430
|
+
formApi: this,
|
|
431
|
+
meta: submitMetaArg
|
|
432
|
+
});
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
this.baseStore.setState((d) => ({ ...d, isSubmitting: true }));
|
|
436
|
+
const done = () => {
|
|
437
|
+
this.baseStore.setState((prev) => ({ ...prev, isSubmitting: false }));
|
|
438
|
+
};
|
|
439
|
+
await this.validateAllFields("submit");
|
|
440
|
+
if (!this.state.isFieldsValid) {
|
|
441
|
+
done();
|
|
442
|
+
this.options.onSubmitInvalid?.({
|
|
443
|
+
value: this.state.values,
|
|
444
|
+
formApi: this,
|
|
445
|
+
meta: submitMetaArg
|
|
446
|
+
});
|
|
447
|
+
formEventClient.emit("form-submission", {
|
|
448
|
+
id: this._formId,
|
|
449
|
+
submissionAttempt: this.state.submissionAttempts,
|
|
450
|
+
successful: false,
|
|
451
|
+
stage: "validateAllFields",
|
|
452
|
+
errors: Object.values(this.state.fieldMeta).map((meta) => meta.errors).flat()
|
|
453
|
+
});
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
456
|
+
await this.validate("submit");
|
|
457
|
+
if (!this.state.isValid) {
|
|
458
|
+
done();
|
|
459
|
+
this.options.onSubmitInvalid?.({
|
|
460
|
+
value: this.state.values,
|
|
461
|
+
formApi: this,
|
|
462
|
+
meta: submitMetaArg
|
|
463
|
+
});
|
|
464
|
+
formEventClient.emit("form-submission", {
|
|
465
|
+
id: this._formId,
|
|
466
|
+
submissionAttempt: this.state.submissionAttempts,
|
|
467
|
+
successful: false,
|
|
468
|
+
stage: "validate",
|
|
469
|
+
errors: this.state.errors
|
|
470
|
+
});
|
|
471
|
+
return;
|
|
472
|
+
}
|
|
473
|
+
batch(() => {
|
|
474
|
+
void Object.values(this.fieldInfo).forEach(
|
|
475
|
+
(field) => {
|
|
476
|
+
field.instance?.options.listeners?.onSubmit?.({
|
|
477
|
+
value: field.instance.state.value,
|
|
478
|
+
fieldApi: field.instance
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
);
|
|
482
|
+
});
|
|
483
|
+
this.options.listeners?.onSubmit?.({ formApi: this, meta: submitMetaArg });
|
|
484
|
+
try {
|
|
485
|
+
await this.options.onSubmit?.({
|
|
486
|
+
value: this.state.values,
|
|
487
|
+
formApi: this,
|
|
488
|
+
meta: submitMetaArg
|
|
489
|
+
});
|
|
490
|
+
batch(() => {
|
|
491
|
+
this.baseStore.setState((prev) => ({
|
|
492
|
+
...prev,
|
|
493
|
+
isSubmitted: true,
|
|
494
|
+
isSubmitSuccessful: true
|
|
495
|
+
// Set isSubmitSuccessful to true on successful submission
|
|
496
|
+
}));
|
|
497
|
+
formEventClient.emit("form-submission", {
|
|
498
|
+
id: this._formId,
|
|
499
|
+
submissionAttempt: this.state.submissionAttempts,
|
|
500
|
+
successful: true
|
|
501
|
+
});
|
|
502
|
+
done();
|
|
503
|
+
});
|
|
504
|
+
} catch (err) {
|
|
505
|
+
this.baseStore.setState((prev) => ({
|
|
506
|
+
...prev,
|
|
507
|
+
isSubmitSuccessful: false
|
|
508
|
+
// Ensure isSubmitSuccessful is false if an error occurs
|
|
509
|
+
}));
|
|
510
|
+
formEventClient.emit("form-submission", {
|
|
511
|
+
id: this._formId,
|
|
512
|
+
submissionAttempt: this.state.submissionAttempts,
|
|
513
|
+
successful: false,
|
|
514
|
+
stage: "inflight",
|
|
515
|
+
onError: err
|
|
516
|
+
});
|
|
517
|
+
done();
|
|
518
|
+
throw err;
|
|
519
|
+
}
|
|
520
|
+
};
|
|
406
521
|
this.getFieldValue = (field) => getBy(this.state.values, field);
|
|
407
522
|
this.getFieldMeta = (field) => {
|
|
408
523
|
return this.state.fieldMeta[field];
|
|
@@ -625,6 +740,48 @@ class FormApi {
|
|
|
625
740
|
};
|
|
626
741
|
});
|
|
627
742
|
};
|
|
743
|
+
this.setErrorMap = (errorMap) => {
|
|
744
|
+
batch(() => {
|
|
745
|
+
Object.entries(errorMap).forEach(([key, value]) => {
|
|
746
|
+
const errorMapKey = key;
|
|
747
|
+
if (isGlobalFormValidationError(value)) {
|
|
748
|
+
const { formError, fieldErrors } = normalizeError(value);
|
|
749
|
+
for (const fieldName of Object.keys(
|
|
750
|
+
this.fieldInfo
|
|
751
|
+
)) {
|
|
752
|
+
const fieldMeta = this.getFieldMeta(fieldName);
|
|
753
|
+
if (!fieldMeta) continue;
|
|
754
|
+
this.setFieldMeta(fieldName, (prev) => ({
|
|
755
|
+
...prev,
|
|
756
|
+
errorMap: {
|
|
757
|
+
...prev.errorMap,
|
|
758
|
+
[errorMapKey]: fieldErrors?.[fieldName]
|
|
759
|
+
},
|
|
760
|
+
errorSourceMap: {
|
|
761
|
+
...prev.errorSourceMap,
|
|
762
|
+
[errorMapKey]: "form"
|
|
763
|
+
}
|
|
764
|
+
}));
|
|
765
|
+
}
|
|
766
|
+
this.baseStore.setState((prev) => ({
|
|
767
|
+
...prev,
|
|
768
|
+
errorMap: {
|
|
769
|
+
...prev.errorMap,
|
|
770
|
+
[errorMapKey]: formError
|
|
771
|
+
}
|
|
772
|
+
}));
|
|
773
|
+
} else {
|
|
774
|
+
this.baseStore.setState((prev) => ({
|
|
775
|
+
...prev,
|
|
776
|
+
errorMap: {
|
|
777
|
+
...prev.errorMap,
|
|
778
|
+
[errorMapKey]: value
|
|
779
|
+
}
|
|
780
|
+
}));
|
|
781
|
+
}
|
|
782
|
+
});
|
|
783
|
+
});
|
|
784
|
+
};
|
|
628
785
|
this.getAllErrors = () => {
|
|
629
786
|
return {
|
|
630
787
|
form: {
|
|
@@ -863,165 +1020,8 @@ class FormApi {
|
|
|
863
1020
|
}
|
|
864
1021
|
return props.validate(props.value);
|
|
865
1022
|
}
|
|
866
|
-
|
|
867
|
-
this.
|
|
868
|
-
...old,
|
|
869
|
-
// Submission attempts mark the form as not submitted
|
|
870
|
-
isSubmitted: false,
|
|
871
|
-
// Count submission attempts
|
|
872
|
-
submissionAttempts: old.submissionAttempts + 1,
|
|
873
|
-
isSubmitSuccessful: false
|
|
874
|
-
// Reset isSubmitSuccessful at the start of submission
|
|
875
|
-
}));
|
|
876
|
-
batch(() => {
|
|
877
|
-
void Object.values(this.fieldInfo).forEach(
|
|
878
|
-
(field) => {
|
|
879
|
-
if (!field.instance) return;
|
|
880
|
-
if (!field.instance.state.meta.isTouched) {
|
|
881
|
-
field.instance.setMeta((prev) => ({ ...prev, isTouched: true }));
|
|
882
|
-
}
|
|
883
|
-
}
|
|
884
|
-
);
|
|
885
|
-
});
|
|
886
|
-
const submitMetaArg = submitMeta ?? this.options.onSubmitMeta;
|
|
887
|
-
if (!this.state.canSubmit && !this._devtoolsSubmissionOverride) {
|
|
888
|
-
this.options.onSubmitInvalid?.({
|
|
889
|
-
value: this.state.values,
|
|
890
|
-
formApi: this,
|
|
891
|
-
meta: submitMetaArg
|
|
892
|
-
});
|
|
893
|
-
return;
|
|
894
|
-
}
|
|
895
|
-
this.baseStore.setState((d) => ({ ...d, isSubmitting: true }));
|
|
896
|
-
const done = () => {
|
|
897
|
-
this.baseStore.setState((prev) => ({ ...prev, isSubmitting: false }));
|
|
898
|
-
};
|
|
899
|
-
await this.validateAllFields("submit");
|
|
900
|
-
if (!this.state.isFieldsValid) {
|
|
901
|
-
done();
|
|
902
|
-
this.options.onSubmitInvalid?.({
|
|
903
|
-
value: this.state.values,
|
|
904
|
-
formApi: this,
|
|
905
|
-
meta: submitMetaArg
|
|
906
|
-
});
|
|
907
|
-
formEventClient.emit("form-submission", {
|
|
908
|
-
id: this._formId,
|
|
909
|
-
submissionAttempt: this.state.submissionAttempts,
|
|
910
|
-
successful: false,
|
|
911
|
-
stage: "validateAllFields",
|
|
912
|
-
errors: Object.values(this.state.fieldMeta).map((meta) => meta.errors).flat()
|
|
913
|
-
});
|
|
914
|
-
return;
|
|
915
|
-
}
|
|
916
|
-
await this.validate("submit");
|
|
917
|
-
if (!this.state.isValid) {
|
|
918
|
-
done();
|
|
919
|
-
this.options.onSubmitInvalid?.({
|
|
920
|
-
value: this.state.values,
|
|
921
|
-
formApi: this,
|
|
922
|
-
meta: submitMetaArg
|
|
923
|
-
});
|
|
924
|
-
formEventClient.emit("form-submission", {
|
|
925
|
-
id: this._formId,
|
|
926
|
-
submissionAttempt: this.state.submissionAttempts,
|
|
927
|
-
successful: false,
|
|
928
|
-
stage: "validate",
|
|
929
|
-
errors: this.state.errors
|
|
930
|
-
});
|
|
931
|
-
return;
|
|
932
|
-
}
|
|
933
|
-
batch(() => {
|
|
934
|
-
void Object.values(this.fieldInfo).forEach(
|
|
935
|
-
(field) => {
|
|
936
|
-
field.instance?.options.listeners?.onSubmit?.({
|
|
937
|
-
value: field.instance.state.value,
|
|
938
|
-
fieldApi: field.instance
|
|
939
|
-
});
|
|
940
|
-
}
|
|
941
|
-
);
|
|
942
|
-
});
|
|
943
|
-
this.options.listeners?.onSubmit?.({ formApi: this, meta: submitMetaArg });
|
|
944
|
-
try {
|
|
945
|
-
await this.options.onSubmit?.({
|
|
946
|
-
value: this.state.values,
|
|
947
|
-
formApi: this,
|
|
948
|
-
meta: submitMetaArg
|
|
949
|
-
});
|
|
950
|
-
batch(() => {
|
|
951
|
-
this.baseStore.setState((prev) => ({
|
|
952
|
-
...prev,
|
|
953
|
-
isSubmitted: true,
|
|
954
|
-
isSubmitSuccessful: true
|
|
955
|
-
// Set isSubmitSuccessful to true on successful submission
|
|
956
|
-
}));
|
|
957
|
-
formEventClient.emit("form-submission", {
|
|
958
|
-
id: this._formId,
|
|
959
|
-
submissionAttempt: this.state.submissionAttempts,
|
|
960
|
-
successful: true
|
|
961
|
-
});
|
|
962
|
-
done();
|
|
963
|
-
});
|
|
964
|
-
} catch (err) {
|
|
965
|
-
this.baseStore.setState((prev) => ({
|
|
966
|
-
...prev,
|
|
967
|
-
isSubmitSuccessful: false
|
|
968
|
-
// Ensure isSubmitSuccessful is false if an error occurs
|
|
969
|
-
}));
|
|
970
|
-
formEventClient.emit("form-submission", {
|
|
971
|
-
id: this._formId,
|
|
972
|
-
submissionAttempt: this.state.submissionAttempts,
|
|
973
|
-
successful: false,
|
|
974
|
-
stage: "inflight",
|
|
975
|
-
onError: err
|
|
976
|
-
});
|
|
977
|
-
done();
|
|
978
|
-
throw err;
|
|
979
|
-
}
|
|
980
|
-
}
|
|
981
|
-
/**
|
|
982
|
-
* Updates the form's errorMap
|
|
983
|
-
*/
|
|
984
|
-
setErrorMap(errorMap) {
|
|
985
|
-
batch(() => {
|
|
986
|
-
Object.entries(errorMap).forEach(([key, value]) => {
|
|
987
|
-
const errorMapKey = key;
|
|
988
|
-
if (isGlobalFormValidationError(value)) {
|
|
989
|
-
const { formError, fieldErrors } = normalizeError(value);
|
|
990
|
-
for (const fieldName of Object.keys(
|
|
991
|
-
this.fieldInfo
|
|
992
|
-
)) {
|
|
993
|
-
const fieldMeta = this.getFieldMeta(fieldName);
|
|
994
|
-
if (!fieldMeta) continue;
|
|
995
|
-
this.setFieldMeta(fieldName, (prev) => ({
|
|
996
|
-
...prev,
|
|
997
|
-
errorMap: {
|
|
998
|
-
...prev.errorMap,
|
|
999
|
-
[errorMapKey]: fieldErrors?.[fieldName]
|
|
1000
|
-
},
|
|
1001
|
-
errorSourceMap: {
|
|
1002
|
-
...prev.errorSourceMap,
|
|
1003
|
-
[errorMapKey]: "form"
|
|
1004
|
-
}
|
|
1005
|
-
}));
|
|
1006
|
-
}
|
|
1007
|
-
this.baseStore.setState((prev) => ({
|
|
1008
|
-
...prev,
|
|
1009
|
-
errorMap: {
|
|
1010
|
-
...prev.errorMap,
|
|
1011
|
-
[errorMapKey]: formError
|
|
1012
|
-
}
|
|
1013
|
-
}));
|
|
1014
|
-
} else {
|
|
1015
|
-
this.baseStore.setState((prev) => ({
|
|
1016
|
-
...prev,
|
|
1017
|
-
errorMap: {
|
|
1018
|
-
...prev.errorMap,
|
|
1019
|
-
[errorMapKey]: value
|
|
1020
|
-
}
|
|
1021
|
-
}));
|
|
1022
|
-
}
|
|
1023
|
-
});
|
|
1024
|
-
});
|
|
1023
|
+
handleSubmit(submitMeta) {
|
|
1024
|
+
return this._handleSubmit(submitMeta);
|
|
1025
1025
|
}
|
|
1026
1026
|
}
|
|
1027
1027
|
function normalizeError(rawError) {
|