@tanstack/react-form 0.0.1 → 0.0.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/build/cjs/formContext.js.map +1 -1
- package/build/cjs/index.js +7 -0
- package/build/cjs/index.js.map +1 -1
- package/build/esm/index.js +1 -0
- package/build/esm/index.js.map +1 -1
- package/build/stats-html.html +1 -1
- package/build/stats-react.json +116 -110
- package/build/types/form-core/src/FieldApi.d.ts +9 -11
- package/build/types/form-core/src/FormApi.d.ts +7 -3
- package/build/types/index.d.ts +1 -0
- package/build/types/react-form/src/formContext.d.ts +2 -2
- package/build/types/react-form/src/index.d.ts +1 -0
- package/build/umd/index.development.js +167 -74
- package/build/umd/index.development.js.map +1 -1
- package/build/umd/index.production.js +5 -4
- package/build/umd/index.production.js.map +1 -1
- package/package.json +2 -2
- package/src/formContext.ts +2 -2
- package/src/index.ts +1 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { DeepKeys, DeepValue, RequiredByKey, Updater } from './utils';
|
|
2
2
|
import type { FormApi, ValidationError } from './FormApi';
|
|
3
3
|
import { Store } from '@tanstack/store';
|
|
4
|
-
declare type
|
|
4
|
+
export declare type ValidationCause = 'change' | 'blur' | 'submit';
|
|
5
5
|
export declare type FieldOptions<TData, TFormData> = {
|
|
6
6
|
name: unknown extends TFormData ? string : DeepKeys<TFormData>;
|
|
7
7
|
defaultValue?: TData;
|
|
@@ -9,14 +9,10 @@ export declare type FieldOptions<TData, TFormData> = {
|
|
|
9
9
|
validate?: (value: TData, fieldApi: FieldApi<TData, TFormData>) => ValidationError;
|
|
10
10
|
validateAsync?: (value: TData, fieldApi: FieldApi<TData, TFormData>) => ValidationError | Promise<ValidationError>;
|
|
11
11
|
validatePristine?: boolean;
|
|
12
|
-
validateOn?:
|
|
13
|
-
validateAsyncOn?:
|
|
12
|
+
validateOn?: ValidationCause;
|
|
13
|
+
validateAsyncOn?: ValidationCause;
|
|
14
14
|
validateAsyncDebounceMs?: number;
|
|
15
|
-
filterValue?: (value: TData) => TData;
|
|
16
15
|
defaultMeta?: Partial<FieldMeta>;
|
|
17
|
-
change?: boolean;
|
|
18
|
-
blur?: boolean;
|
|
19
|
-
submit?: boolean;
|
|
20
16
|
};
|
|
21
17
|
export declare type FieldMeta = {
|
|
22
18
|
isTouched: boolean;
|
|
@@ -44,7 +40,7 @@ export declare class FieldApi<TData, TFormData> {
|
|
|
44
40
|
name: DeepKeys<TFormData>;
|
|
45
41
|
store: Store<FieldState<TData>>;
|
|
46
42
|
state: FieldState<TData>;
|
|
47
|
-
options: RequiredByKey<FieldOptions<TData, TFormData>, 'validateOn' | 'validateAsyncOn'>;
|
|
43
|
+
options: RequiredByKey<FieldOptions<TData, TFormData>, 'validatePristine' | 'validateOn' | 'validateAsyncOn' | 'validateAsyncDebounceMs'>;
|
|
48
44
|
constructor(opts: FieldApiOptions<TData, TFormData>);
|
|
49
45
|
mount: () => () => void;
|
|
50
46
|
update: (opts: FieldApiOptions<TData, TFormData>) => void;
|
|
@@ -61,9 +57,11 @@ export declare class FieldApi<TData, TFormData> {
|
|
|
61
57
|
removeValue: (index: number) => void;
|
|
62
58
|
swapValues: (aIndex: number, bIndex: number) => void;
|
|
63
59
|
getSubField: <TName extends DeepKeys<TData>>(name: TName) => FieldApi<DeepValue<TData, TName>, TFormData>;
|
|
64
|
-
|
|
65
|
-
|
|
60
|
+
validateSync: (value?: TData) => Promise<void>;
|
|
61
|
+
cancelValidateAsync: () => void;
|
|
62
|
+
validateAsync: (value?: TData) => Promise<ValidationError>;
|
|
63
|
+
shouldValidate: (isAsync: boolean, cause?: ValidationCause) => boolean;
|
|
64
|
+
validate: (cause?: ValidationCause, value?: TData) => Promise<ValidationError>;
|
|
66
65
|
getChangeProps: <T extends ChangeProps<any>>(props?: T) => ChangeProps<TData> & Omit<T, keyof ChangeProps<TData>>;
|
|
67
66
|
getInputProps: <T extends InputProps>(props?: T) => InputProps & Omit<T, keyof InputProps>;
|
|
68
67
|
}
|
|
69
|
-
export {};
|
|
@@ -1,21 +1,25 @@
|
|
|
1
1
|
import type { FormEvent } from 'react';
|
|
2
2
|
import { Store } from '@tanstack/store';
|
|
3
3
|
import type { DeepKeys, DeepValue, Updater } from './utils';
|
|
4
|
-
import type { FieldApi, FieldMeta } from './FieldApi';
|
|
4
|
+
import type { FieldApi, FieldMeta, ValidationCause } from './FieldApi';
|
|
5
5
|
export declare type FormOptions<TData> = {
|
|
6
6
|
defaultValues?: TData;
|
|
7
7
|
defaultState?: Partial<FormState<TData>>;
|
|
8
|
-
onSubmit?: (values: TData, formApi: FormApi<TData>) =>
|
|
8
|
+
onSubmit?: (values: TData, formApi: FormApi<TData>) => void;
|
|
9
9
|
onInvalidSubmit?: (values: TData, formApi: FormApi<TData>) => void;
|
|
10
10
|
validate?: (values: TData, formApi: FormApi<TData>) => Promise<any>;
|
|
11
11
|
debugForm?: boolean;
|
|
12
|
-
|
|
12
|
+
defaultValidatePristine?: boolean;
|
|
13
|
+
defaultValidateOn?: ValidationCause;
|
|
14
|
+
defaultValidateAsyncOn?: ValidationCause;
|
|
15
|
+
defaultValidateAsyncDebounceMs?: number;
|
|
13
16
|
};
|
|
14
17
|
export declare type FieldInfo<TFormData> = {
|
|
15
18
|
instances: Record<string, FieldApi<any, TFormData>>;
|
|
16
19
|
} & ValidationMeta;
|
|
17
20
|
export declare type ValidationMeta = {
|
|
18
21
|
validationCount?: number;
|
|
22
|
+
validationAsyncCount?: number;
|
|
19
23
|
validationPromise?: Promise<ValidationError>;
|
|
20
24
|
validationResolve?: (error: ValidationError) => void;
|
|
21
25
|
validationReject?: (error: unknown) => void;
|
package/build/types/index.d.ts
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
* @license MIT
|
|
10
10
|
*/
|
|
11
11
|
import { DeepKeys, FieldApi, DeepValue, FieldOptions, FormApi, FormState, FormOptions } from '@tanstack/form-core';
|
|
12
|
+
export * from '@tanstack/form-core';
|
|
12
13
|
import { NoInfer } from '@tanstack/react-store';
|
|
13
14
|
import React from 'react';
|
|
14
15
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { FormApi } from '@tanstack/form-core';
|
|
1
|
+
import type { FormApi } from '@tanstack/form-core';
|
|
2
2
|
import * as React from 'react';
|
|
3
|
-
export declare const formContext: React.Context<FormApi<any
|
|
3
|
+
export declare const formContext: React.Context<FormApi<any> | null>;
|
|
4
4
|
export declare function useFormContext(customFormApi?: FormApi<any>): FormApi<any>;
|
|
@@ -37,23 +37,6 @@
|
|
|
37
37
|
var React__namespace = /*#__PURE__*/_interopNamespace(React);
|
|
38
38
|
var React__default = /*#__PURE__*/_interopDefaultLegacy(React);
|
|
39
39
|
|
|
40
|
-
function _extends() {
|
|
41
|
-
_extends = Object.assign ? Object.assign.bind() : function (target) {
|
|
42
|
-
for (var i = 1; i < arguments.length; i++) {
|
|
43
|
-
var source = arguments[i];
|
|
44
|
-
|
|
45
|
-
for (var key in source) {
|
|
46
|
-
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
|
47
|
-
target[key] = source[key];
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
return target;
|
|
53
|
-
};
|
|
54
|
-
return _extends.apply(this, arguments);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
40
|
/**
|
|
58
41
|
* store
|
|
59
42
|
*
|
|
@@ -498,7 +481,7 @@
|
|
|
498
481
|
|
|
499
482
|
var _updateStore = /*#__PURE__*/_classPrivateFieldLooseKey("updateStore");
|
|
500
483
|
|
|
501
|
-
var
|
|
484
|
+
var _leaseValidateAsync = /*#__PURE__*/_classPrivateFieldLooseKey("leaseValidateAsync");
|
|
502
485
|
|
|
503
486
|
class FieldApi {
|
|
504
487
|
constructor(_opts) {
|
|
@@ -545,10 +528,13 @@
|
|
|
545
528
|
});
|
|
546
529
|
|
|
547
530
|
this.update = opts => {
|
|
531
|
+
var _this$form$options$de, _this$form$options$de2, _this$form$options$de3, _this$form$options$de4;
|
|
532
|
+
|
|
548
533
|
this.options = {
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
534
|
+
validatePristine: (_this$form$options$de = this.form.options.defaultValidatePristine) != null ? _this$form$options$de : false,
|
|
535
|
+
validateOn: (_this$form$options$de2 = this.form.options.defaultValidateOn) != null ? _this$form$options$de2 : 'change',
|
|
536
|
+
validateAsyncOn: (_this$form$options$de3 = this.form.options.defaultValidateAsyncOn) != null ? _this$form$options$de3 : 'blur',
|
|
537
|
+
validateAsyncDebounceMs: (_this$form$options$de4 = this.form.options.defaultValidateAsyncDebounceMs) != null ? _this$form$options$de4 : 0,
|
|
552
538
|
...opts
|
|
553
539
|
}; // Default Value
|
|
554
540
|
|
|
@@ -586,48 +572,91 @@
|
|
|
586
572
|
form: this.form
|
|
587
573
|
});
|
|
588
574
|
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
575
|
+
this.validateSync = async (value = this.state.value) => {
|
|
576
|
+
const {
|
|
577
|
+
validate
|
|
578
|
+
} = this.options;
|
|
579
|
+
|
|
580
|
+
if (!validate) {
|
|
581
|
+
return;
|
|
582
|
+
} // Use the validationCount for all field instances to
|
|
583
|
+
// track freshness of the validation
|
|
584
|
+
|
|
595
585
|
|
|
586
|
+
const validationCount = (this.getInfo().validationCount || 0) + 1;
|
|
587
|
+
this.getInfo().validationCount = validationCount;
|
|
588
|
+
const error = normalizeError(validate(value, this));
|
|
589
|
+
|
|
590
|
+
if (this.state.meta.error !== error) {
|
|
596
591
|
this.setMeta(prev => ({ ...prev,
|
|
597
|
-
|
|
598
|
-
}));
|
|
599
|
-
|
|
592
|
+
error
|
|
593
|
+
}));
|
|
594
|
+
} // If a sync error is encountered, cancel any async validation
|
|
600
595
|
|
|
601
|
-
const validationCount = (this.getInfo().validationCount || 0) + 1;
|
|
602
|
-
this.getInfo().validationCount = validationCount;
|
|
603
596
|
|
|
604
|
-
|
|
597
|
+
if (this.state.meta.error) {
|
|
598
|
+
this.cancelValidateAsync();
|
|
599
|
+
}
|
|
600
|
+
};
|
|
605
601
|
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
602
|
+
Object.defineProperty(this, _leaseValidateAsync, {
|
|
603
|
+
writable: true,
|
|
604
|
+
value: () => {
|
|
605
|
+
const count = (this.getInfo().validationAsyncCount || 0) + 1;
|
|
606
|
+
this.getInfo().validationAsyncCount = count;
|
|
607
|
+
return count;
|
|
608
|
+
}
|
|
609
|
+
});
|
|
612
610
|
|
|
613
|
-
|
|
614
|
-
|
|
611
|
+
this.cancelValidateAsync = () => {
|
|
612
|
+
// Lease a new validation count to ignore any pending validations
|
|
613
|
+
_classPrivateFieldLooseBase(this, _leaseValidateAsync)[_leaseValidateAsync](); // Cancel any pending validation state
|
|
615
614
|
|
|
616
|
-
if (checkLatest()) {
|
|
617
|
-
var _this$getInfo$validat, _this$getInfo;
|
|
618
615
|
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
}
|
|
616
|
+
this.setMeta(prev => ({ ...prev,
|
|
617
|
+
isValidating: false
|
|
618
|
+
}));
|
|
619
|
+
};
|
|
624
620
|
|
|
625
|
-
|
|
626
|
-
|
|
621
|
+
this.validateAsync = async (value = this.state.value) => {
|
|
622
|
+
const {
|
|
623
|
+
validateAsync,
|
|
624
|
+
validateAsyncDebounceMs
|
|
625
|
+
} = this.options;
|
|
626
|
+
|
|
627
|
+
if (!validateAsync) {
|
|
628
|
+
return;
|
|
629
|
+
}
|
|
627
630
|
|
|
628
|
-
|
|
629
|
-
|
|
631
|
+
if (this.state.meta.isValidating !== true) this.setMeta(prev => ({ ...prev,
|
|
632
|
+
isValidating: true
|
|
633
|
+
})); // Use the validationCount for all field instances to
|
|
634
|
+
// track freshness of the validation
|
|
635
|
+
|
|
636
|
+
const validationAsyncCount = _classPrivateFieldLooseBase(this, _leaseValidateAsync)[_leaseValidateAsync]();
|
|
637
|
+
|
|
638
|
+
const checkLatest = () => validationAsyncCount === this.getInfo().validationAsyncCount;
|
|
639
|
+
|
|
640
|
+
if (!this.getInfo().validationPromise) {
|
|
641
|
+
this.getInfo().validationPromise = new Promise((resolve, reject) => {
|
|
642
|
+
this.getInfo().validationResolve = resolve;
|
|
643
|
+
this.getInfo().validationReject = reject;
|
|
644
|
+
});
|
|
645
|
+
}
|
|
630
646
|
|
|
647
|
+
if (validateAsyncDebounceMs > 0) {
|
|
648
|
+
await new Promise(r => setTimeout(r, validateAsyncDebounceMs));
|
|
649
|
+
} // Only kick off validation if this validation is the latest attempt
|
|
650
|
+
|
|
651
|
+
|
|
652
|
+
if (checkLatest()) {
|
|
653
|
+
try {
|
|
654
|
+
const rawError = await validateAsync(value, this);
|
|
655
|
+
|
|
656
|
+
if (checkLatest()) {
|
|
657
|
+
var _this$getInfo$validat, _this$getInfo;
|
|
658
|
+
|
|
659
|
+
const error = normalizeError(rawError);
|
|
631
660
|
this.setMeta(prev => ({ ...prev,
|
|
632
661
|
isValidating: false,
|
|
633
662
|
error
|
|
@@ -649,14 +678,45 @@
|
|
|
649
678
|
delete this.getInfo().validationPromise;
|
|
650
679
|
}
|
|
651
680
|
}
|
|
681
|
+
} // Always return the latest validation promise to the caller
|
|
652
682
|
|
|
653
|
-
return this.getInfo().validationPromise;
|
|
654
|
-
}
|
|
655
|
-
});
|
|
656
683
|
|
|
657
|
-
|
|
684
|
+
return this.getInfo().validationPromise;
|
|
685
|
+
};
|
|
658
686
|
|
|
659
|
-
this.
|
|
687
|
+
this.shouldValidate = (isAsync, cause) => {
|
|
688
|
+
const {
|
|
689
|
+
validateOn,
|
|
690
|
+
validateAsyncOn
|
|
691
|
+
} = this.options;
|
|
692
|
+
const level = getValidationCauseLevel(cause); // Must meet *at least* the validation level to validate,
|
|
693
|
+
// e.g. if validateOn is 'change' and validateCause is 'blur',
|
|
694
|
+
// the field will still validate
|
|
695
|
+
|
|
696
|
+
return Object.keys(validateCauseLevels).some(d => isAsync ? validateAsyncOn : validateOn === d && level >= validateCauseLevels[d]);
|
|
697
|
+
};
|
|
698
|
+
|
|
699
|
+
this.validate = async (cause, value) => {
|
|
700
|
+
// If the field is pristine and validatePristine is false, do not validate
|
|
701
|
+
if (!this.options.validatePristine && !this.state.meta.isTouched) return; // Attempt to sync validate first
|
|
702
|
+
|
|
703
|
+
if (this.shouldValidate(false, cause)) {
|
|
704
|
+
this.validateSync(value);
|
|
705
|
+
} // If there is an error, return it, do not attempt async validation
|
|
706
|
+
|
|
707
|
+
|
|
708
|
+
if (this.state.meta.error) {
|
|
709
|
+
return this.state.meta.error;
|
|
710
|
+
} // No error? Attempt async validation
|
|
711
|
+
|
|
712
|
+
|
|
713
|
+
if (this.shouldValidate(true, cause)) {
|
|
714
|
+
return this.validateAsync(value);
|
|
715
|
+
} // If there is no sync error or async validation attempt, there is no error
|
|
716
|
+
|
|
717
|
+
|
|
718
|
+
return undefined;
|
|
719
|
+
};
|
|
660
720
|
|
|
661
721
|
this.getChangeProps = (props = {}) => {
|
|
662
722
|
return { ...props,
|
|
@@ -669,14 +729,7 @@
|
|
|
669
729
|
this.setMeta(prev => ({ ...prev,
|
|
670
730
|
isTouched: true
|
|
671
731
|
}));
|
|
672
|
-
|
|
673
|
-
validateOn
|
|
674
|
-
} = this.options;
|
|
675
|
-
|
|
676
|
-
if (validateOn === 'blur' || validateOn.split('-')[0] === 'blur') {
|
|
677
|
-
this.validate();
|
|
678
|
-
}
|
|
679
|
-
|
|
732
|
+
this.validate('blur');
|
|
680
733
|
props.onBlur == null ? void 0 : props.onBlur(e);
|
|
681
734
|
}
|
|
682
735
|
};
|
|
@@ -715,17 +768,13 @@
|
|
|
715
768
|
onUpdate: next => {
|
|
716
769
|
next.meta.touchedError = next.meta.isTouched ? next.meta.error : undefined; // Do not validate pristine fields
|
|
717
770
|
|
|
718
|
-
|
|
771
|
+
const prevState = this.state;
|
|
772
|
+
this.state = next;
|
|
719
773
|
|
|
720
|
-
if (
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
} catch (err) {
|
|
724
|
-
console.error('An error occurred during validation', err);
|
|
725
|
-
}
|
|
774
|
+
if (next.value !== prevState.value) {
|
|
775
|
+
console.log('change');
|
|
776
|
+
this.validate('change', next.value);
|
|
726
777
|
}
|
|
727
|
-
|
|
728
|
-
this.state = next;
|
|
729
778
|
}
|
|
730
779
|
});
|
|
731
780
|
this.state = this.store.state;
|
|
@@ -733,6 +782,44 @@
|
|
|
733
782
|
}
|
|
734
783
|
|
|
735
784
|
}
|
|
785
|
+
const validateCauseLevels = {
|
|
786
|
+
change: 0,
|
|
787
|
+
blur: 1,
|
|
788
|
+
submit: 2
|
|
789
|
+
};
|
|
790
|
+
|
|
791
|
+
function getValidationCauseLevel(cause) {
|
|
792
|
+
return !cause ? 3 : validateCauseLevels[cause];
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
function normalizeError(rawError) {
|
|
796
|
+
if (rawError) {
|
|
797
|
+
if (typeof rawError !== 'string') {
|
|
798
|
+
return 'Invalid Form Values';
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
return rawError;
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
return undefined;
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
function _extends() {
|
|
808
|
+
_extends = Object.assign ? Object.assign.bind() : function (target) {
|
|
809
|
+
for (var i = 1; i < arguments.length; i++) {
|
|
810
|
+
var source = arguments[i];
|
|
811
|
+
|
|
812
|
+
for (var key in source) {
|
|
813
|
+
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
|
814
|
+
target[key] = source[key];
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
return target;
|
|
820
|
+
};
|
|
821
|
+
return _extends.apply(this, arguments);
|
|
822
|
+
}
|
|
736
823
|
|
|
737
824
|
/**
|
|
738
825
|
* react-store
|
|
@@ -895,9 +982,15 @@
|
|
|
895
982
|
}
|
|
896
983
|
|
|
897
984
|
exports.Field = Field;
|
|
985
|
+
exports.FieldApi = FieldApi;
|
|
986
|
+
exports.FormApi = FormApi;
|
|
898
987
|
exports.createFieldComponent = createFieldComponent;
|
|
899
988
|
exports.createFormComponent = createFormComponent;
|
|
900
989
|
exports.createUseField = createUseField;
|
|
990
|
+
exports.functionalUpdate = functionalUpdate;
|
|
991
|
+
exports.getBy = getBy;
|
|
992
|
+
exports.getDefaultFormState = getDefaultFormState;
|
|
993
|
+
exports.setBy = setBy;
|
|
901
994
|
exports.useField = useField;
|
|
902
995
|
exports.useForm = useForm;
|
|
903
996
|
|