remix-validated-form 4.6.12 → 5.0.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/.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,
|