remix-validated-form 4.6.12 → 5.0.1
Sign up to get free protection for your applications and to get access to all the features.
- package/.turbo/turbo-build.log +8 -8
- package/dist/index.cjs.js +178 -52
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +34 -12
- package/dist/index.esm.js +180 -54
- package/dist/index.esm.js.map +1 -1
- package/package.json +6 -5
- package/src/ValidatedForm.tsx +46 -16
- package/src/hooks.ts +5 -7
- package/src/internal/hooks.ts +5 -5
- package/src/internal/state/arrayUtil.ts +41 -3
- package/src/internal/state/createFormStore.ts +104 -27
- package/src/internal/state/fieldArray.tsx +56 -14
- package/src/server.ts +3 -0
- package/src/unreleased/formStateHooks.ts +8 -3
- package/src/validation/types.ts +4 -1
- package/tsconfig.json +1 -1
- package/src/validation/validation.test.ts +0 -330
package/dist/index.d.ts
CHANGED
@@ -22,7 +22,7 @@ type GetInputProps = <T extends MinimalInputProps>(props?: Omit<T, HandledProps
|
|
22
22
|
|
23
23
|
/**
|
24
24
|
* Returns whether or not the parent form is currently being submitted.
|
25
|
-
* This is different from Remix's `
|
25
|
+
* This is different from Remix's `useNavigation()` in that it
|
26
26
|
* is aware of what form it's in and when _that_ form is being submitted.
|
27
27
|
*
|
28
28
|
* @param formId
|
@@ -87,6 +87,8 @@ declare const useField: (name: string, options?: {
|
|
87
87
|
declare const useControlField: <T>(name: string, formId?: string) => readonly [T, (value: T) => void];
|
88
88
|
declare const useUpdateControlledField: (formId?: string) => (field: string, value: unknown) => void;
|
89
89
|
|
90
|
+
declare const FORM_DEFAULTS_FIELD: "__rvfInternalFormDefaults";
|
91
|
+
|
90
92
|
type FieldErrors = Record<string, string>;
|
91
93
|
type TouchedFields = Record<string, boolean>;
|
92
94
|
type GenericObject = {
|
@@ -130,7 +132,10 @@ type ValidateFieldResult = {
|
|
130
132
|
*/
|
131
133
|
type Validator<DataType> = {
|
132
134
|
validate: (unvalidatedData: GenericObject) => Promise<ValidationResult<DataType>>;
|
133
|
-
|
135
|
+
/**
|
136
|
+
* @deprecated Will be removed in a future version of remix-validated-form
|
137
|
+
*/
|
138
|
+
validateField?: (unvalidatedData: GenericObject, field: string) => Promise<ValidateFieldResult>;
|
134
139
|
};
|
135
140
|
type Valid<DataType> = {
|
136
141
|
data: DataType;
|
@@ -164,9 +169,14 @@ declare function validationError(error: ValidatorError, repopulateFields?: unkno
|
|
164
169
|
type FormDefaults = {
|
165
170
|
[formDefaultsKey: `${typeof FORM_DEFAULTS_FIELD}_${string}`]: any;
|
166
171
|
};
|
172
|
+
type internal_FORM_DEFAULTS_FIELD = typeof FORM_DEFAULTS_FIELD;
|
167
173
|
declare const setFormDefaults: <DataType = any>(formId: string, defaultValues: Partial<DataType>) => FormDefaults;
|
168
174
|
|
169
|
-
type
|
175
|
+
type SubactionData<DataType, Subaction extends string | undefined> = DataType & {
|
176
|
+
subaction: Subaction;
|
177
|
+
};
|
178
|
+
type DataForSubaction<DataType, Subaction extends string | undefined> = Subaction extends string ? SubactionData<DataType, Subaction> extends undefined ? DataType : SubactionData<DataType, Subaction> : DataType;
|
179
|
+
type FormProps<DataType, Subaction extends string | undefined> = {
|
170
180
|
/**
|
171
181
|
* A `Validator` object that describes how to validate the form.
|
172
182
|
*/
|
@@ -175,7 +185,7 @@ type FormProps<DataType> = {
|
|
175
185
|
* A submit callback that gets called when the form is submitted
|
176
186
|
* after all validations have been run.
|
177
187
|
*/
|
178
|
-
onSubmit?: (data: DataType, event: React.FormEvent<HTMLFormElement>) => void | Promise<void>;
|
188
|
+
onSubmit?: (data: DataForSubaction<DataType, Subaction>, event: React.FormEvent<HTMLFormElement>) => void | Promise<void>;
|
179
189
|
/**
|
180
190
|
* Allows you to provide a `fetcher` from Remix's `useFetcher` hook.
|
181
191
|
* The form will use the fetcher for loading states, action data, etc
|
@@ -186,7 +196,7 @@ type FormProps<DataType> = {
|
|
186
196
|
* Accepts an object of default values for the form
|
187
197
|
* that will automatically be propagated to the form fields via `useField`.
|
188
198
|
*/
|
189
|
-
defaultValues?: Partial<DataType
|
199
|
+
defaultValues?: Partial<DataForSubaction<DataType, Subaction>>;
|
190
200
|
/**
|
191
201
|
* A ref to the form element.
|
192
202
|
*/
|
@@ -196,7 +206,7 @@ type FormProps<DataType> = {
|
|
196
206
|
* Setting a value here will cause the form to be submitted with an extra `subaction` value.
|
197
207
|
* This can be useful when there are multiple forms on the screen handled by the same action.
|
198
208
|
*/
|
199
|
-
subaction?:
|
209
|
+
subaction?: Subaction;
|
200
210
|
/**
|
201
211
|
* Reset the form to the default values after the form has been successfully submitted.
|
202
212
|
* This is useful if you want to submit the same form multiple times,
|
@@ -212,7 +222,7 @@ type FormProps<DataType> = {
|
|
212
222
|
/**
|
213
223
|
* The primary form component of `remix-validated-form`.
|
214
224
|
*/
|
215
|
-
declare function ValidatedForm<DataType>({ validator, onSubmit, children, fetcher, action, defaultValues: unMemoizedDefaults, formRef: formRefProp, onReset, subaction, resetAfterSubmit, disableFocusOnError, method, replace, id, ...rest }: FormProps<DataType>): JSX.Element;
|
225
|
+
declare function ValidatedForm<DataType, Subaction extends string | undefined>({ validator, onSubmit, children, fetcher, action, defaultValues: unMemoizedDefaults, formRef: formRefProp, onReset, subaction, resetAfterSubmit, disableFocusOnError, method, replace, id, preventScrollReset, relative, encType, ...rest }: FormProps<DataType, Subaction>): JSX.Element;
|
216
226
|
|
217
227
|
/**
|
218
228
|
* Used to create a validator for a form.
|
@@ -311,6 +321,18 @@ type FieldArrayValidationBehaviorOptions = {
|
|
311
321
|
initial: FieldArrayValidationBehavior;
|
312
322
|
whenSubmitted: FieldArrayValidationBehavior;
|
313
323
|
};
|
324
|
+
type FieldArrayItem<T> = {
|
325
|
+
/**
|
326
|
+
* The default value of the item.
|
327
|
+
* This does not update as the field is changed by the user.
|
328
|
+
*/
|
329
|
+
defaultValue: T;
|
330
|
+
/**
|
331
|
+
* A unique key for the item.
|
332
|
+
* Use this as the key prop when rendering the item.
|
333
|
+
*/
|
334
|
+
key: string;
|
335
|
+
};
|
314
336
|
type FieldArrayHelpers<Item = any> = {
|
315
337
|
push: (item: Item) => void;
|
316
338
|
swap: (indexA: number, indexB: number) => void;
|
@@ -325,13 +347,13 @@ type UseFieldArrayOptions = {
|
|
325
347
|
formId?: string;
|
326
348
|
validationBehavior?: Partial<FieldArrayValidationBehaviorOptions>;
|
327
349
|
};
|
328
|
-
declare function useFieldArray<Item = any>(name: string, { formId, validationBehavior }?: UseFieldArrayOptions): [
|
329
|
-
type FieldArrayProps = {
|
350
|
+
declare function useFieldArray<Item = any>(name: string, { formId, validationBehavior }?: UseFieldArrayOptions): [items: FieldArrayItem<Item>[], helpers: FieldArrayHelpers<any>, error: string | undefined];
|
351
|
+
type FieldArrayProps<Item> = {
|
330
352
|
name: string;
|
331
|
-
children: (
|
353
|
+
children: (items: FieldArrayItem<Item>[], helpers: FieldArrayHelpers<Item>, error: string | undefined) => React.ReactNode;
|
332
354
|
formId?: string;
|
333
355
|
validationBehavior?: FieldArrayValidationBehaviorOptions;
|
334
356
|
};
|
335
|
-
declare
|
357
|
+
declare function FieldArray<Item = any>({ name, children, formId, validationBehavior, }: FieldArrayProps<Item>): JSX.Element;
|
336
358
|
|
337
|
-
export { BaseResult, CreateValidatorArg, ErrorResult, FieldArray, FieldArrayHelpers, FieldArrayProps, FieldErrors, FieldProps, FormContextValue, FormDefaults, FormProps, GenericObject, Invalid, SuccessResult, TouchedFields, Valid, ValidateFieldResult, ValidatedForm, ValidationErrorResponseData, ValidationResult, Validator, ValidatorData, ValidatorError, createValidator, setFormDefaults, useControlField, useField, useFieldArray, useFormContext, useIsSubmitting, useIsValid, useUpdateControlledField, validationError };
|
359
|
+
export { BaseResult, CreateValidatorArg, ErrorResult, FieldArray, FieldArrayHelpers, FieldArrayProps, FieldErrors, FieldProps, FormContextValue, FormDefaults, FormProps, GenericObject, Invalid, SuccessResult, TouchedFields, Valid, ValidateFieldResult, ValidatedForm, ValidationErrorResponseData, ValidationResult, Validator, ValidatorData, ValidatorError, createValidator, internal_FORM_DEFAULTS_FIELD, setFormDefaults, useControlField, useField, useFieldArray, useFormContext, useIsSubmitting, useIsValid, useUpdateControlledField, validationError };
|
package/dist/index.esm.js
CHANGED
@@ -85,7 +85,7 @@ var createGetInputProps = ({
|
|
85
85
|
};
|
86
86
|
|
87
87
|
// src/internal/hooks.ts
|
88
|
-
import { useActionData, useMatches,
|
88
|
+
import { useActionData, useMatches, useNavigation } from "@remix-run/react";
|
89
89
|
import { useCallback, useContext } from "react";
|
90
90
|
|
91
91
|
// ../set-get/src/stringToPathArray.ts
|
@@ -266,7 +266,9 @@ function sparseSplice(array, start, deleteCount, item) {
|
|
266
266
|
}
|
267
267
|
if (arguments.length === 4)
|
268
268
|
return array.splice(start, deleteCount, item);
|
269
|
-
|
269
|
+
else if (arguments.length === 3)
|
270
|
+
return array.splice(start, deleteCount);
|
271
|
+
return array.splice(start);
|
270
272
|
}
|
271
273
|
var move = (array, from2, to) => {
|
272
274
|
const [item] = sparseSplice(array, from2, 1);
|
@@ -275,6 +277,12 @@ var move = (array, from2, to) => {
|
|
275
277
|
var insert = (array, index, value) => {
|
276
278
|
sparseSplice(array, index, 0, value);
|
277
279
|
};
|
280
|
+
var insertEmpty = (array, index) => {
|
281
|
+
const tail = sparseSplice(array, index);
|
282
|
+
tail.forEach((item, i) => {
|
283
|
+
sparseSplice(array, index + i + 1, 0, item);
|
284
|
+
});
|
285
|
+
};
|
278
286
|
var remove = (array, index) => {
|
279
287
|
sparseSplice(array, index, 1);
|
280
288
|
};
|
@@ -404,6 +412,29 @@ if (void 0) {
|
|
404
412
|
expect(array).toEqual([true, void 0, void 0, true]);
|
405
413
|
});
|
406
414
|
});
|
415
|
+
describe("insertEmpty", () => {
|
416
|
+
it("should insert an empty item at a given index", () => {
|
417
|
+
const array = [1, 2, 3];
|
418
|
+
insertEmpty(array, 1);
|
419
|
+
expect(array).toStrictEqual([1, , 2, 3]);
|
420
|
+
expect(array).not.toStrictEqual([1, void 0, 2, 3]);
|
421
|
+
});
|
422
|
+
it("should work with already sparse arrays", () => {
|
423
|
+
const array = [, , 1, , 2, , 3];
|
424
|
+
insertEmpty(array, 3);
|
425
|
+
expect(array).toStrictEqual([, , 1, , , 2, , 3]);
|
426
|
+
expect(array).not.toStrictEqual([
|
427
|
+
void 0,
|
428
|
+
void 0,
|
429
|
+
1,
|
430
|
+
void 0,
|
431
|
+
void 0,
|
432
|
+
2,
|
433
|
+
void 0,
|
434
|
+
3
|
435
|
+
]);
|
436
|
+
});
|
437
|
+
});
|
407
438
|
describe("remove", () => {
|
408
439
|
it("should remove an item at a given index", () => {
|
409
440
|
const array = [1, 2, 3];
|
@@ -585,10 +616,12 @@ var defaultFormState = {
|
|
585
616
|
reset: () => noOp,
|
586
617
|
syncFormProps: noOp,
|
587
618
|
setFormElement: noOp,
|
588
|
-
validateField: async () => null,
|
589
619
|
validate: async () => {
|
590
620
|
throw new Error("Validate called before form was initialized.");
|
591
621
|
},
|
622
|
+
smartValidate: async () => {
|
623
|
+
throw new Error("Validate called before form was initialized.");
|
624
|
+
},
|
592
625
|
submit: async () => {
|
593
626
|
throw new Error("Submit called before form was initialized.");
|
594
627
|
},
|
@@ -671,8 +704,8 @@ var createFormState = (set, get2) => ({
|
|
671
704
|
state.formElement = formElement;
|
672
705
|
});
|
673
706
|
},
|
674
|
-
|
675
|
-
var _a
|
707
|
+
validate: async () => {
|
708
|
+
var _a;
|
676
709
|
const formElement = get2().formElement;
|
677
710
|
invariant2(
|
678
711
|
formElement,
|
@@ -681,22 +714,14 @@ var createFormState = (set, get2) => ({
|
|
681
714
|
const validator = (_a = get2().formProps) == null ? void 0 : _a.validator;
|
682
715
|
invariant2(
|
683
716
|
validator,
|
684
|
-
"Cannot validator. This is probably a bug in remix-validated-form."
|
685
|
-
);
|
686
|
-
await ((_c = (_b = get2().controlledFields).awaitValueUpdate) == null ? void 0 : _c.call(_b, field));
|
687
|
-
const { error } = await validator.validateField(
|
688
|
-
new FormData(formElement),
|
689
|
-
field
|
717
|
+
"Cannot find validator. This is probably a bug in remix-validated-form."
|
690
718
|
);
|
691
|
-
|
692
|
-
|
693
|
-
|
694
|
-
|
695
|
-
get2().clearFieldError(field);
|
696
|
-
return null;
|
697
|
-
}
|
719
|
+
const result = await validator.validate(new FormData(formElement));
|
720
|
+
if (result.error)
|
721
|
+
get2().setFieldErrors(result.error.fieldErrors);
|
722
|
+
return result;
|
698
723
|
},
|
699
|
-
|
724
|
+
smartValidate: async ({ alwaysIncludeErrorsFromFields = [] } = {}) => {
|
700
725
|
var _a;
|
701
726
|
const formElement = get2().formElement;
|
702
727
|
invariant2(
|
@@ -706,12 +731,75 @@ var createFormState = (set, get2) => ({
|
|
706
731
|
const validator = (_a = get2().formProps) == null ? void 0 : _a.validator;
|
707
732
|
invariant2(
|
708
733
|
validator,
|
709
|
-
"Cannot validator. This is probably a bug in remix-validated-form."
|
734
|
+
"Cannot find validator. This is probably a bug in remix-validated-form."
|
710
735
|
);
|
711
|
-
|
712
|
-
|
713
|
-
|
714
|
-
|
736
|
+
await Promise.all(
|
737
|
+
alwaysIncludeErrorsFromFields.map(
|
738
|
+
(field) => {
|
739
|
+
var _a2, _b;
|
740
|
+
return (_b = (_a2 = get2().controlledFields).awaitValueUpdate) == null ? void 0 : _b.call(_a2, field);
|
741
|
+
}
|
742
|
+
)
|
743
|
+
);
|
744
|
+
const validationResult = await validator.validate(
|
745
|
+
new FormData(formElement)
|
746
|
+
);
|
747
|
+
if (!validationResult.error) {
|
748
|
+
const hadErrors = Object.keys(get2().fieldErrors).length > 0;
|
749
|
+
if (hadErrors)
|
750
|
+
get2().setFieldErrors({});
|
751
|
+
return validationResult;
|
752
|
+
}
|
753
|
+
const {
|
754
|
+
error: { fieldErrors }
|
755
|
+
} = validationResult;
|
756
|
+
const errorFields = /* @__PURE__ */ new Set();
|
757
|
+
const incomingErrors = /* @__PURE__ */ new Set();
|
758
|
+
const prevErrors = /* @__PURE__ */ new Set();
|
759
|
+
Object.keys(fieldErrors).forEach((field) => {
|
760
|
+
errorFields.add(field);
|
761
|
+
incomingErrors.add(field);
|
762
|
+
});
|
763
|
+
Object.keys(get2().fieldErrors).forEach((field) => {
|
764
|
+
errorFields.add(field);
|
765
|
+
prevErrors.add(field);
|
766
|
+
});
|
767
|
+
const fieldsToUpdate = /* @__PURE__ */ new Set();
|
768
|
+
const fieldsToDelete = /* @__PURE__ */ new Set();
|
769
|
+
errorFields.forEach((field) => {
|
770
|
+
if (!incomingErrors.has(field)) {
|
771
|
+
fieldsToDelete.add(field);
|
772
|
+
return;
|
773
|
+
}
|
774
|
+
if (prevErrors.has(field) && incomingErrors.has(field)) {
|
775
|
+
if (fieldErrors[field] !== get2().fieldErrors[field])
|
776
|
+
fieldsToUpdate.add(field);
|
777
|
+
return;
|
778
|
+
}
|
779
|
+
if (alwaysIncludeErrorsFromFields.includes(field)) {
|
780
|
+
fieldsToUpdate.add(field);
|
781
|
+
return;
|
782
|
+
}
|
783
|
+
if (!prevErrors.has(field)) {
|
784
|
+
const fieldTouched = get2().touchedFields[field];
|
785
|
+
const formHasBeenSubmitted = get2().hasBeenSubmitted;
|
786
|
+
if (fieldTouched || formHasBeenSubmitted)
|
787
|
+
fieldsToUpdate.add(field);
|
788
|
+
return;
|
789
|
+
}
|
790
|
+
});
|
791
|
+
if (fieldsToDelete.size === 0 && fieldsToUpdate.size === 0) {
|
792
|
+
return { ...validationResult, error: { fieldErrors: get2().fieldErrors } };
|
793
|
+
}
|
794
|
+
set((state) => {
|
795
|
+
fieldsToDelete.forEach((field) => {
|
796
|
+
delete state.fieldErrors[field];
|
797
|
+
});
|
798
|
+
fieldsToUpdate.forEach((field) => {
|
799
|
+
state.fieldErrors[field] = fieldErrors[field];
|
800
|
+
});
|
801
|
+
});
|
802
|
+
return { ...validationResult, error: { fieldErrors: get2().fieldErrors } };
|
715
803
|
},
|
716
804
|
submit: () => {
|
717
805
|
const formElement = get2().formElement;
|
@@ -864,12 +952,12 @@ var createFormState = (set, get2) => ({
|
|
864
952
|
mutateAsArray(
|
865
953
|
fieldName,
|
866
954
|
state.touchedFields,
|
867
|
-
(array) =>
|
955
|
+
(array) => insertEmpty(array, index)
|
868
956
|
);
|
869
957
|
mutateAsArray(
|
870
958
|
fieldName,
|
871
959
|
state.fieldErrors,
|
872
|
-
(array) =>
|
960
|
+
(array) => insertEmpty(array, index)
|
873
961
|
);
|
874
962
|
});
|
875
963
|
get2().controlledFields.kickoffValueUpdate(fieldName);
|
@@ -921,12 +1009,12 @@ var createFormState = (set, get2) => ({
|
|
921
1009
|
mutateAsArray(
|
922
1010
|
fieldName,
|
923
1011
|
state.touchedFields,
|
924
|
-
(array) => array
|
1012
|
+
(array) => insertEmpty(array, 0)
|
925
1013
|
);
|
926
1014
|
mutateAsArray(
|
927
1015
|
fieldName,
|
928
1016
|
state.fieldErrors,
|
929
|
-
(array) => array
|
1017
|
+
(array) => insertEmpty(array, 0)
|
930
1018
|
);
|
931
1019
|
});
|
932
1020
|
},
|
@@ -1058,8 +1146,8 @@ var useDefaultValuesForForm = (context) => {
|
|
1058
1146
|
var useHasActiveFormSubmit = ({
|
1059
1147
|
fetcher
|
1060
1148
|
}) => {
|
1061
|
-
|
1062
|
-
const hasActiveSubmission = fetcher ? fetcher.state === "submitting" :
|
1149
|
+
let navigation = useNavigation();
|
1150
|
+
const hasActiveSubmission = fetcher ? fetcher.state === "submitting" : navigation.state === "submitting";
|
1063
1151
|
return hasActiveSubmission;
|
1064
1152
|
};
|
1065
1153
|
var useFieldTouched = (field, { formId }) => {
|
@@ -1092,7 +1180,7 @@ var useFieldDefaultValue = (name, context) => {
|
|
1092
1180
|
var useInternalIsSubmitting = (formId) => useFormStore(formId, (state) => state.isSubmitting);
|
1093
1181
|
var useInternalIsValid = (formId) => useFormStore(formId, (state) => state.isValid());
|
1094
1182
|
var useInternalHasBeenSubmitted = (formId) => useFormStore(formId, (state) => state.hasBeenSubmitted);
|
1095
|
-
var
|
1183
|
+
var useSmartValidate = (formId) => useFormStore(formId, (state) => state.smartValidate);
|
1096
1184
|
var useValidate = (formId) => useFormStore(formId, (state) => state.validate);
|
1097
1185
|
var noOpReceiver = () => () => {
|
1098
1186
|
};
|
@@ -1203,7 +1291,7 @@ var useField = (name, options) => {
|
|
1203
1291
|
const error = useFieldError(name, formContext);
|
1204
1292
|
const clearError = useClearError(formContext);
|
1205
1293
|
const hasBeenSubmitted = useInternalHasBeenSubmitted(formContext.formId);
|
1206
|
-
const
|
1294
|
+
const smartValidate = useSmartValidate(formContext.formId);
|
1207
1295
|
const registerReceiveFocus = useRegisterReceiveFocus(formContext.formId);
|
1208
1296
|
useEffect2(() => {
|
1209
1297
|
if (handleReceiveFocus)
|
@@ -1213,9 +1301,7 @@ var useField = (name, options) => {
|
|
1213
1301
|
const helpers = {
|
1214
1302
|
error,
|
1215
1303
|
clearError: () => clearError(name),
|
1216
|
-
validate: () => {
|
1217
|
-
validateField(name);
|
1218
|
-
},
|
1304
|
+
validate: () => smartValidate({ alwaysIncludeErrorsFromFields: [name] }),
|
1219
1305
|
defaultValue,
|
1220
1306
|
touched,
|
1221
1307
|
setTouched
|
@@ -1239,7 +1325,7 @@ var useField = (name, options) => {
|
|
1239
1325
|
name,
|
1240
1326
|
hasBeenSubmitted,
|
1241
1327
|
options == null ? void 0 : options.validationBehavior,
|
1242
|
-
|
1328
|
+
smartValidate
|
1243
1329
|
]);
|
1244
1330
|
return field;
|
1245
1331
|
};
|
@@ -1461,6 +1547,9 @@ function ValidatedForm({
|
|
1461
1547
|
method,
|
1462
1548
|
replace: replace2,
|
1463
1549
|
id,
|
1550
|
+
preventScrollReset,
|
1551
|
+
relative,
|
1552
|
+
encType,
|
1464
1553
|
...rest
|
1465
1554
|
}) {
|
1466
1555
|
var _a;
|
@@ -1553,11 +1642,11 @@ function ValidatedForm({
|
|
1553
1642
|
startSubmit();
|
1554
1643
|
const submitter = nativeEvent.submitter;
|
1555
1644
|
const formMethod = (submitter == null ? void 0 : submitter.formMethod) || method;
|
1556
|
-
const
|
1645
|
+
const formData = getDataFromForm(target);
|
1557
1646
|
if (submitter == null ? void 0 : submitter.name) {
|
1558
|
-
|
1647
|
+
formData.append(submitter.name, submitter.value);
|
1559
1648
|
}
|
1560
|
-
const result = await validator.validate(
|
1649
|
+
const result = await validator.validate(formData);
|
1561
1650
|
if (result.error) {
|
1562
1651
|
setFieldErrors(result.error.fieldErrors);
|
1563
1652
|
endSubmit();
|
@@ -1576,13 +1665,18 @@ function ValidatedForm({
|
|
1576
1665
|
endSubmit();
|
1577
1666
|
return;
|
1578
1667
|
}
|
1668
|
+
const opts = {
|
1669
|
+
method: formMethod,
|
1670
|
+
replace: replace2,
|
1671
|
+
preventScrollReset,
|
1672
|
+
relative,
|
1673
|
+
action,
|
1674
|
+
encType
|
1675
|
+
};
|
1579
1676
|
if (fetcher)
|
1580
|
-
fetcher.submit(
|
1677
|
+
fetcher.submit(formData, opts);
|
1581
1678
|
else
|
1582
|
-
submit(
|
1583
|
-
replace: replace2,
|
1584
|
-
method: formMethod
|
1585
|
-
});
|
1679
|
+
submit(formData, opts);
|
1586
1680
|
}
|
1587
1681
|
};
|
1588
1682
|
return /* @__PURE__ */ jsx(
|
@@ -1593,7 +1687,10 @@ function ValidatedForm({
|
|
1593
1687
|
id,
|
1594
1688
|
action,
|
1595
1689
|
method,
|
1690
|
+
encType,
|
1596
1691
|
replace: replace2,
|
1692
|
+
preventScrollReset,
|
1693
|
+
relative,
|
1597
1694
|
onSubmit: (e) => {
|
1598
1695
|
e.preventDefault();
|
1599
1696
|
handleSubmit(
|
@@ -1711,7 +1808,7 @@ var useFormState = (formId) => {
|
|
1711
1808
|
var useFormHelpers = (formId) => {
|
1712
1809
|
const formContext = useInternalFormContext(formId, "useFormHelpers");
|
1713
1810
|
const setTouched = useSetTouched(formContext);
|
1714
|
-
const validateField =
|
1811
|
+
const validateField = useSmartValidate(formContext.formId);
|
1715
1812
|
const validate = useValidate(formContext.formId);
|
1716
1813
|
const clearError = useClearError(formContext);
|
1717
1814
|
const setFieldErrors = useSetFieldErrors(formContext.formId);
|
@@ -1721,7 +1818,13 @@ var useFormHelpers = (formId) => {
|
|
1721
1818
|
return useMemo3(
|
1722
1819
|
() => ({
|
1723
1820
|
setTouched,
|
1724
|
-
validateField
|
1821
|
+
validateField: async (fieldName) => {
|
1822
|
+
var _a, _b;
|
1823
|
+
const res = await validateField({
|
1824
|
+
alwaysIncludeErrorsFromFields: [fieldName]
|
1825
|
+
});
|
1826
|
+
return (_b = (_a = res.error) == null ? void 0 : _a.fieldErrors[fieldName]) != null ? _b : null;
|
1827
|
+
},
|
1725
1828
|
clearError,
|
1726
1829
|
validate,
|
1727
1830
|
clearAllErrors: () => setFieldErrors({}),
|
@@ -1794,7 +1897,8 @@ var useFormContext = (formId) => {
|
|
1794
1897
|
};
|
1795
1898
|
|
1796
1899
|
// src/internal/state/fieldArray.tsx
|
1797
|
-
import {
|
1900
|
+
import { nanoid } from "nanoid";
|
1901
|
+
import { useMemo as useMemo5, useRef as useRef5 } from "react";
|
1798
1902
|
import { useCallback as useCallback6 } from "react";
|
1799
1903
|
import invariant4 from "tiny-invariant";
|
1800
1904
|
import { Fragment as Fragment2, jsx as jsx2 } from "react/jsx-runtime";
|
@@ -1802,7 +1906,7 @@ var useInternalFieldArray = (context, field, validationBehavior) => {
|
|
1802
1906
|
const value = useFieldDefaultValue(field, context);
|
1803
1907
|
useRegisterControlledField(context, field);
|
1804
1908
|
const hasBeenSubmitted = useInternalHasBeenSubmitted(context.formId);
|
1805
|
-
const validateField =
|
1909
|
+
const validateField = useSmartValidate(context.formId);
|
1806
1910
|
const error = useFieldError(field, context);
|
1807
1911
|
const resolvedValidationBehavior = {
|
1808
1912
|
initial: "onSubmit",
|
@@ -1812,7 +1916,7 @@ var useInternalFieldArray = (context, field, validationBehavior) => {
|
|
1812
1916
|
const behavior = hasBeenSubmitted ? resolvedValidationBehavior.whenSubmitted : resolvedValidationBehavior.initial;
|
1813
1917
|
const maybeValidate = useCallback6(() => {
|
1814
1918
|
if (behavior === "onChange") {
|
1815
|
-
validateField(field);
|
1919
|
+
validateField({ alwaysIncludeErrorsFromFields: [field] });
|
1816
1920
|
}
|
1817
1921
|
}, [behavior, field, validateField]);
|
1818
1922
|
invariant4(
|
@@ -1823,56 +1927,78 @@ var useInternalFieldArray = (context, field, validationBehavior) => {
|
|
1823
1927
|
context.formId,
|
1824
1928
|
(state) => state.controlledFields.array
|
1825
1929
|
);
|
1930
|
+
const arrayValue = useMemo5(() => value != null ? value : [], [value]);
|
1931
|
+
const keyRef = useRef5([]);
|
1932
|
+
if (keyRef.current.length !== arrayValue.length) {
|
1933
|
+
keyRef.current = arrayValue.map(() => nanoid());
|
1934
|
+
}
|
1826
1935
|
const helpers = useMemo5(
|
1827
1936
|
() => ({
|
1828
1937
|
push: (item) => {
|
1829
1938
|
arr.push(field, item);
|
1939
|
+
keyRef.current.push(nanoid());
|
1830
1940
|
maybeValidate();
|
1831
1941
|
},
|
1832
1942
|
swap: (indexA, indexB) => {
|
1833
1943
|
arr.swap(field, indexA, indexB);
|
1944
|
+
swap(keyRef.current, indexA, indexB);
|
1834
1945
|
maybeValidate();
|
1835
1946
|
},
|
1836
1947
|
move: (from2, to) => {
|
1837
1948
|
arr.move(field, from2, to);
|
1949
|
+
move(keyRef.current, from2, to);
|
1838
1950
|
maybeValidate();
|
1839
1951
|
},
|
1840
1952
|
insert: (index, value2) => {
|
1841
1953
|
arr.insert(field, index, value2);
|
1954
|
+
insert(keyRef.current, index, nanoid());
|
1842
1955
|
maybeValidate();
|
1843
1956
|
},
|
1844
1957
|
unshift: (value2) => {
|
1845
1958
|
arr.unshift(field, value2);
|
1959
|
+
keyRef.current.unshift(nanoid());
|
1846
1960
|
maybeValidate();
|
1847
1961
|
},
|
1848
1962
|
remove: (index) => {
|
1849
1963
|
arr.remove(field, index);
|
1964
|
+
remove(keyRef.current, index);
|
1850
1965
|
maybeValidate();
|
1851
1966
|
},
|
1852
1967
|
pop: () => {
|
1853
1968
|
arr.pop(field);
|
1969
|
+
keyRef.current.pop();
|
1854
1970
|
maybeValidate();
|
1855
1971
|
},
|
1856
1972
|
replace: (index, value2) => {
|
1857
1973
|
arr.replace(field, index, value2);
|
1974
|
+
keyRef.current[index] = nanoid();
|
1858
1975
|
maybeValidate();
|
1859
1976
|
}
|
1860
1977
|
}),
|
1861
1978
|
[arr, field, maybeValidate]
|
1862
1979
|
);
|
1863
|
-
const
|
1864
|
-
|
1980
|
+
const valueWithKeys = useMemo5(() => {
|
1981
|
+
const result = [];
|
1982
|
+
arrayValue.forEach((item, index) => {
|
1983
|
+
result[index] = {
|
1984
|
+
key: keyRef.current[index],
|
1985
|
+
defaultValue: item
|
1986
|
+
};
|
1987
|
+
});
|
1988
|
+
return result;
|
1989
|
+
}, [arrayValue]);
|
1990
|
+
return [valueWithKeys, helpers, error];
|
1865
1991
|
};
|
1866
1992
|
function useFieldArray(name, { formId, validationBehavior } = {}) {
|
1867
1993
|
const context = useInternalFormContext(formId, "FieldArray");
|
1868
1994
|
return useInternalFieldArray(context, name, validationBehavior);
|
1869
1995
|
}
|
1870
|
-
|
1996
|
+
function FieldArray({
|
1871
1997
|
name,
|
1872
1998
|
children,
|
1873
1999
|
formId,
|
1874
2000
|
validationBehavior
|
1875
|
-
})
|
2001
|
+
}) {
|
1876
2002
|
const context = useInternalFormContext(formId, "FieldArray");
|
1877
2003
|
const [value, helpers, error] = useInternalFieldArray(
|
1878
2004
|
context,
|
@@ -1880,7 +2006,7 @@ var FieldArray = ({
|
|
1880
2006
|
validationBehavior
|
1881
2007
|
);
|
1882
2008
|
return /* @__PURE__ */ jsx2(Fragment2, { children: children(value, helpers, error) });
|
1883
|
-
}
|
2009
|
+
}
|
1884
2010
|
export {
|
1885
2011
|
FieldArray,
|
1886
2012
|
ValidatedForm,
|