remix-validated-form 4.6.11 → 5.0.0
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/README.md +27 -30
- package/dist/index.cjs.js +178 -52
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +38 -16
- package/dist/index.esm.js +180 -54
- package/dist/index.esm.js.map +1 -1
- package/package.json +6 -5
- package/src/ValidatedForm.tsx +44 -14
- package/src/hooks.ts +5 -7
- package/src/internal/getInputProps.ts +5 -5
- 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/.turbo/turbo-build.log
CHANGED
@@ -7,12 +7,12 @@
|
|
7
7
|
[34mCLI[39m Cleaning output folder
|
8
8
|
[34mESM[39m Build start
|
9
9
|
[34mCJS[39m Build start
|
10
|
-
[
|
11
|
-
[
|
12
|
-
[
|
13
|
-
[
|
14
|
-
[
|
15
|
-
[
|
10
|
+
[32mCJS[39m [1mdist/index.cjs.js [22m[32m63.04 KB[39m
|
11
|
+
[32mCJS[39m [1mdist/index.cjs.js.map [22m[32m130.86 KB[39m
|
12
|
+
[32mCJS[39m ⚡️ Build success in 25ms
|
13
|
+
[32mESM[39m [1mdist/index.esm.js [22m[32m60.30 KB[39m
|
14
|
+
[32mESM[39m [1mdist/index.esm.js.map [22m[32m130.76 KB[39m
|
15
|
+
[32mESM[39m ⚡️ Build success in 25ms
|
16
16
|
[34mDTS[39m Build start
|
17
|
-
[32mDTS[39m ⚡️ Build success in
|
18
|
-
[32mDTS[39m [1mdist/index.d.ts [22m[
|
17
|
+
[32mDTS[39m ⚡️ Build success in 1486ms
|
18
|
+
[32mDTS[39m [1mdist/index.d.ts [22m[32m12.80 KB[39m
|
package/README.md
CHANGED
@@ -2,25 +2,27 @@
|
|
2
2
|
|
3
3
|
A form library built for [Remix](https://remix.run) to make validation easy.
|
4
4
|
|
5
|
+
## Features
|
6
|
+
|
5
7
|
- Client-side, field-by-field and form-level validation
|
6
8
|
- Re-use validation on the server
|
7
9
|
- Set default values for the entire form in one place
|
8
10
|
- Supports nested objects and arrays
|
9
|
-
- Easily detect if a specific form is being
|
11
|
+
- Easily detect if a specific form is being submitted
|
10
12
|
- Validation library agnostic
|
11
13
|
- Can work without JS
|
12
14
|
|
13
|
-
|
15
|
+
## Docs
|
14
16
|
|
15
17
|
The docs are located a [remix-validated-form.io](https://www.remix-validated-form.io).
|
16
18
|
|
17
|
-
|
19
|
+
## Demo
|
18
20
|
|
19
21
|
https://user-images.githubusercontent.com/2811287/145734901-700a5085-a10b-4d89-88e1-5de9142b1e85.mov
|
20
22
|
|
21
23
|
To run `sample-app`:
|
22
24
|
|
23
|
-
```
|
25
|
+
```bash
|
24
26
|
git clone https://github.com/airjp73/remix-validated-form
|
25
27
|
cd ./remix-validated-form
|
26
28
|
yarn install
|
@@ -28,21 +30,20 @@ yarn build
|
|
28
30
|
yarn sample-app
|
29
31
|
```
|
30
32
|
|
31
|
-
|
33
|
+
## Getting started
|
32
34
|
|
33
|
-
|
35
|
+
### Install
|
34
36
|
|
35
|
-
|
37
|
+
#### Base package
|
36
38
|
|
37
39
|
```bash
|
38
40
|
npm install remix-validated-form
|
39
41
|
```
|
40
42
|
|
41
|
-
|
43
|
+
#### Validation library adapter
|
42
44
|
|
43
45
|
There are official adapters available for `zod` and `yup`.
|
44
|
-
If you're using a different library,
|
45
|
-
see the [Validation library support](#validation-library-support) section below.
|
46
|
+
If you're using a different library, see the [Validation library support](#validation-library-support) section below.
|
46
47
|
|
47
48
|
- @remix-validated-form/with-zod
|
48
49
|
- @remix-validated-form/with-yup
|
@@ -53,10 +54,9 @@ npm install @remix-validated-form/with-zod
|
|
53
54
|
|
54
55
|
If you're using zod, you might also find `zod-form-data` helpful.
|
55
56
|
|
56
|
-
|
57
|
+
### Create an input component
|
57
58
|
|
58
|
-
In order to display field errors or do field-by-field validation,
|
59
|
-
it's recommended to incorporate this library into an input component using `useField`.
|
59
|
+
In order to display field errors or do field-by-field validation, it's recommended to incorporate this library into an input component using `useField`.
|
60
60
|
|
61
61
|
```tsx
|
62
62
|
import { useField } from "remix-validated-form";
|
@@ -78,7 +78,7 @@ export const MyInput = ({ name, label }: MyInputProps) => {
|
|
78
78
|
};
|
79
79
|
```
|
80
80
|
|
81
|
-
|
81
|
+
### Create a submit button component
|
82
82
|
|
83
83
|
To best take advantage of the per-form submission detection, we can create a submit button component.
|
84
84
|
|
@@ -102,9 +102,9 @@ export const MySubmitButton = () => {
|
|
102
102
|
};
|
103
103
|
```
|
104
104
|
|
105
|
-
|
105
|
+
### Using the form
|
106
106
|
|
107
|
-
Now that we have our components, making a form is easy
|
107
|
+
Now that we have our components, making a form is easy.
|
108
108
|
|
109
109
|
```tsx
|
110
110
|
import { DataFunctionArgs, json, redirect } from "@remix-run/node";
|
@@ -159,7 +159,7 @@ export default function MyForm() {
|
|
159
159
|
}
|
160
160
|
```
|
161
161
|
|
162
|
-
|
162
|
+
### Nested objects and arrays
|
163
163
|
|
164
164
|
You can use nested objects and arrays by using a period (`.`) or brackets (`[]`) for the field names.
|
165
165
|
|
@@ -186,14 +186,13 @@ export default function MyForm() {
|
|
186
186
|
}
|
187
187
|
```
|
188
188
|
|
189
|
-
|
189
|
+
## Validation Library Support
|
190
190
|
|
191
|
-
There are official adapters available for `zod` and `yup` ,
|
192
|
-
but you can easily support whatever library you want by creating your own adapter.
|
191
|
+
There are official adapters available for `zod` and `yup` , but you can easily support whatever library you want by creating your own adapter.
|
193
192
|
|
194
193
|
And if you create an adapter for a library, feel free to make a PR on this repository 😊
|
195
194
|
|
196
|
-
|
195
|
+
### Creating an adapter
|
197
196
|
|
198
197
|
Any object that conforms to the `Validator` type can be passed into the the `ValidatedForm`'s `validator` prop.
|
199
198
|
|
@@ -215,15 +214,13 @@ type Validator<DataType> = {
|
|
215
214
|
};
|
216
215
|
```
|
217
216
|
|
218
|
-
In order to make an adapter for your validation library of choice,
|
219
|
-
you can create a function that accepts a schema from the validation library and turns it into a validator.
|
217
|
+
In order to make an adapter for your validation library of choice, you can create a function that accepts a schema from the validation library and turns it into a validator.
|
220
218
|
|
221
219
|
Note the use of `createValidator`.
|
222
|
-
It takes care of unflattening the data for nested objects and arrays
|
223
|
-
since the form doesn't know anything about object and arrays and this should be handled by the adapter.
|
220
|
+
It takes care of unflattening the data for nested objects and arrays since the form doesn't know anything about object and arrays and this should be handled by the adapter.
|
224
221
|
For more on this you can check the implementations for `withZod` and `withYup`.
|
225
222
|
|
226
|
-
The out-of-the-box support for `yup` in this library works
|
223
|
+
The out-of-the-box support for `yup` in this library works as the following:
|
227
224
|
|
228
225
|
```ts
|
229
226
|
export const withYup = <Schema extends AnyObjectSchema>(
|
@@ -246,21 +243,21 @@ export const withYup = <Schema extends AnyObjectSchema>(
|
|
246
243
|
});
|
247
244
|
```
|
248
245
|
|
249
|
-
|
246
|
+
## Frequenty Asked Questions
|
250
247
|
|
251
|
-
|
248
|
+
### Why are my fields triggering the native HTML validations before `remix-validated-form` ones?
|
252
249
|
|
253
250
|
This is happening because you or the library you are using is passing the `required` attribute to the fields.
|
254
251
|
This library doesn't take care of eliminating them and it's up to the user how they want to manage the validation errors.
|
255
252
|
If you wan't to disable all native HTML validations you can add `noValidate` to `<ValidatedForm>`.
|
256
253
|
We recommend this approach since the validation will still work even if JS is disabled.
|
257
254
|
|
258
|
-
|
255
|
+
### How do we trigger toast messages on success?
|
259
256
|
|
260
257
|
Problem: how do we trigger a toast message on success if the action redirects away from the form route? The Remix solution is to flash a message in the session and pick this up in a loader function, probably in root.tsx
|
261
258
|
See the [Remix](https://remix.run/docs/en/v1/utils/sessions#sessionflashkey-value) documentation for more information.
|
262
259
|
|
263
|
-
|
260
|
+
### Why is my cancel button triggering form submission?
|
264
261
|
|
265
262
|
Problem: the cancel button has an onClick handler to navigate away from the form route but instead it is submitting the form.
|
266
263
|
A button defaults to `type="submit"` in a form which will submit the form by default. If you want to prevent this you can add `type="reset"` or `type="button"` to the cancel button.
|
package/dist/index.cjs.js
CHANGED
@@ -309,7 +309,9 @@ function sparseSplice(array, start, deleteCount, item) {
|
|
309
309
|
}
|
310
310
|
if (arguments.length === 4)
|
311
311
|
return array.splice(start, deleteCount, item);
|
312
|
-
|
312
|
+
else if (arguments.length === 3)
|
313
|
+
return array.splice(start, deleteCount);
|
314
|
+
return array.splice(start);
|
313
315
|
}
|
314
316
|
var move = (array, from2, to) => {
|
315
317
|
const [item] = sparseSplice(array, from2, 1);
|
@@ -318,6 +320,12 @@ var move = (array, from2, to) => {
|
|
318
320
|
var insert = (array, index, value) => {
|
319
321
|
sparseSplice(array, index, 0, value);
|
320
322
|
};
|
323
|
+
var insertEmpty = (array, index) => {
|
324
|
+
const tail = sparseSplice(array, index);
|
325
|
+
tail.forEach((item, i) => {
|
326
|
+
sparseSplice(array, index + i + 1, 0, item);
|
327
|
+
});
|
328
|
+
};
|
321
329
|
var remove = (array, index) => {
|
322
330
|
sparseSplice(array, index, 1);
|
323
331
|
};
|
@@ -447,6 +455,29 @@ if (void 0) {
|
|
447
455
|
expect(array).toEqual([true, void 0, void 0, true]);
|
448
456
|
});
|
449
457
|
});
|
458
|
+
describe("insertEmpty", () => {
|
459
|
+
it("should insert an empty item at a given index", () => {
|
460
|
+
const array = [1, 2, 3];
|
461
|
+
insertEmpty(array, 1);
|
462
|
+
expect(array).toStrictEqual([1, , 2, 3]);
|
463
|
+
expect(array).not.toStrictEqual([1, void 0, 2, 3]);
|
464
|
+
});
|
465
|
+
it("should work with already sparse arrays", () => {
|
466
|
+
const array = [, , 1, , 2, , 3];
|
467
|
+
insertEmpty(array, 3);
|
468
|
+
expect(array).toStrictEqual([, , 1, , , 2, , 3]);
|
469
|
+
expect(array).not.toStrictEqual([
|
470
|
+
void 0,
|
471
|
+
void 0,
|
472
|
+
1,
|
473
|
+
void 0,
|
474
|
+
void 0,
|
475
|
+
2,
|
476
|
+
void 0,
|
477
|
+
3
|
478
|
+
]);
|
479
|
+
});
|
480
|
+
});
|
450
481
|
describe("remove", () => {
|
451
482
|
it("should remove an item at a given index", () => {
|
452
483
|
const array = [1, 2, 3];
|
@@ -628,10 +659,12 @@ var defaultFormState = {
|
|
628
659
|
reset: () => noOp,
|
629
660
|
syncFormProps: noOp,
|
630
661
|
setFormElement: noOp,
|
631
|
-
validateField: async () => null,
|
632
662
|
validate: async () => {
|
633
663
|
throw new Error("Validate called before form was initialized.");
|
634
664
|
},
|
665
|
+
smartValidate: async () => {
|
666
|
+
throw new Error("Validate called before form was initialized.");
|
667
|
+
},
|
635
668
|
submit: async () => {
|
636
669
|
throw new Error("Submit called before form was initialized.");
|
637
670
|
},
|
@@ -714,8 +747,8 @@ var createFormState = (set, get2) => ({
|
|
714
747
|
state.formElement = formElement;
|
715
748
|
});
|
716
749
|
},
|
717
|
-
|
718
|
-
var _a
|
750
|
+
validate: async () => {
|
751
|
+
var _a;
|
719
752
|
const formElement = get2().formElement;
|
720
753
|
(0, import_tiny_invariant2.default)(
|
721
754
|
formElement,
|
@@ -724,22 +757,14 @@ var createFormState = (set, get2) => ({
|
|
724
757
|
const validator = (_a = get2().formProps) == null ? void 0 : _a.validator;
|
725
758
|
(0, import_tiny_invariant2.default)(
|
726
759
|
validator,
|
727
|
-
"Cannot validator. This is probably a bug in remix-validated-form."
|
728
|
-
);
|
729
|
-
await ((_c = (_b = get2().controlledFields).awaitValueUpdate) == null ? void 0 : _c.call(_b, field));
|
730
|
-
const { error } = await validator.validateField(
|
731
|
-
new FormData(formElement),
|
732
|
-
field
|
760
|
+
"Cannot find validator. This is probably a bug in remix-validated-form."
|
733
761
|
);
|
734
|
-
|
735
|
-
|
736
|
-
|
737
|
-
|
738
|
-
get2().clearFieldError(field);
|
739
|
-
return null;
|
740
|
-
}
|
762
|
+
const result = await validator.validate(new FormData(formElement));
|
763
|
+
if (result.error)
|
764
|
+
get2().setFieldErrors(result.error.fieldErrors);
|
765
|
+
return result;
|
741
766
|
},
|
742
|
-
|
767
|
+
smartValidate: async ({ alwaysIncludeErrorsFromFields = [] } = {}) => {
|
743
768
|
var _a;
|
744
769
|
const formElement = get2().formElement;
|
745
770
|
(0, import_tiny_invariant2.default)(
|
@@ -749,12 +774,75 @@ var createFormState = (set, get2) => ({
|
|
749
774
|
const validator = (_a = get2().formProps) == null ? void 0 : _a.validator;
|
750
775
|
(0, import_tiny_invariant2.default)(
|
751
776
|
validator,
|
752
|
-
"Cannot validator. This is probably a bug in remix-validated-form."
|
777
|
+
"Cannot find validator. This is probably a bug in remix-validated-form."
|
753
778
|
);
|
754
|
-
|
755
|
-
|
756
|
-
|
757
|
-
|
779
|
+
await Promise.all(
|
780
|
+
alwaysIncludeErrorsFromFields.map(
|
781
|
+
(field) => {
|
782
|
+
var _a2, _b;
|
783
|
+
return (_b = (_a2 = get2().controlledFields).awaitValueUpdate) == null ? void 0 : _b.call(_a2, field);
|
784
|
+
}
|
785
|
+
)
|
786
|
+
);
|
787
|
+
const validationResult = await validator.validate(
|
788
|
+
new FormData(formElement)
|
789
|
+
);
|
790
|
+
if (!validationResult.error) {
|
791
|
+
const hadErrors = Object.keys(get2().fieldErrors).length > 0;
|
792
|
+
if (hadErrors)
|
793
|
+
get2().setFieldErrors({});
|
794
|
+
return validationResult;
|
795
|
+
}
|
796
|
+
const {
|
797
|
+
error: { fieldErrors }
|
798
|
+
} = validationResult;
|
799
|
+
const errorFields = /* @__PURE__ */ new Set();
|
800
|
+
const incomingErrors = /* @__PURE__ */ new Set();
|
801
|
+
const prevErrors = /* @__PURE__ */ new Set();
|
802
|
+
Object.keys(fieldErrors).forEach((field) => {
|
803
|
+
errorFields.add(field);
|
804
|
+
incomingErrors.add(field);
|
805
|
+
});
|
806
|
+
Object.keys(get2().fieldErrors).forEach((field) => {
|
807
|
+
errorFields.add(field);
|
808
|
+
prevErrors.add(field);
|
809
|
+
});
|
810
|
+
const fieldsToUpdate = /* @__PURE__ */ new Set();
|
811
|
+
const fieldsToDelete = /* @__PURE__ */ new Set();
|
812
|
+
errorFields.forEach((field) => {
|
813
|
+
if (!incomingErrors.has(field)) {
|
814
|
+
fieldsToDelete.add(field);
|
815
|
+
return;
|
816
|
+
}
|
817
|
+
if (prevErrors.has(field) && incomingErrors.has(field)) {
|
818
|
+
if (fieldErrors[field] !== get2().fieldErrors[field])
|
819
|
+
fieldsToUpdate.add(field);
|
820
|
+
return;
|
821
|
+
}
|
822
|
+
if (alwaysIncludeErrorsFromFields.includes(field)) {
|
823
|
+
fieldsToUpdate.add(field);
|
824
|
+
return;
|
825
|
+
}
|
826
|
+
if (!prevErrors.has(field)) {
|
827
|
+
const fieldTouched = get2().touchedFields[field];
|
828
|
+
const formHasBeenSubmitted = get2().hasBeenSubmitted;
|
829
|
+
if (fieldTouched || formHasBeenSubmitted)
|
830
|
+
fieldsToUpdate.add(field);
|
831
|
+
return;
|
832
|
+
}
|
833
|
+
});
|
834
|
+
if (fieldsToDelete.size === 0 && fieldsToUpdate.size === 0) {
|
835
|
+
return { ...validationResult, error: { fieldErrors: get2().fieldErrors } };
|
836
|
+
}
|
837
|
+
set((state) => {
|
838
|
+
fieldsToDelete.forEach((field) => {
|
839
|
+
delete state.fieldErrors[field];
|
840
|
+
});
|
841
|
+
fieldsToUpdate.forEach((field) => {
|
842
|
+
state.fieldErrors[field] = fieldErrors[field];
|
843
|
+
});
|
844
|
+
});
|
845
|
+
return { ...validationResult, error: { fieldErrors: get2().fieldErrors } };
|
758
846
|
},
|
759
847
|
submit: () => {
|
760
848
|
const formElement = get2().formElement;
|
@@ -907,12 +995,12 @@ var createFormState = (set, get2) => ({
|
|
907
995
|
mutateAsArray(
|
908
996
|
fieldName,
|
909
997
|
state.touchedFields,
|
910
|
-
(array) =>
|
998
|
+
(array) => insertEmpty(array, index)
|
911
999
|
);
|
912
1000
|
mutateAsArray(
|
913
1001
|
fieldName,
|
914
1002
|
state.fieldErrors,
|
915
|
-
(array) =>
|
1003
|
+
(array) => insertEmpty(array, index)
|
916
1004
|
);
|
917
1005
|
});
|
918
1006
|
get2().controlledFields.kickoffValueUpdate(fieldName);
|
@@ -964,12 +1052,12 @@ var createFormState = (set, get2) => ({
|
|
964
1052
|
mutateAsArray(
|
965
1053
|
fieldName,
|
966
1054
|
state.touchedFields,
|
967
|
-
(array) => array
|
1055
|
+
(array) => insertEmpty(array, 0)
|
968
1056
|
);
|
969
1057
|
mutateAsArray(
|
970
1058
|
fieldName,
|
971
1059
|
state.fieldErrors,
|
972
|
-
(array) => array
|
1060
|
+
(array) => insertEmpty(array, 0)
|
973
1061
|
);
|
974
1062
|
});
|
975
1063
|
},
|
@@ -1101,8 +1189,8 @@ var useDefaultValuesForForm = (context) => {
|
|
1101
1189
|
var useHasActiveFormSubmit = ({
|
1102
1190
|
fetcher
|
1103
1191
|
}) => {
|
1104
|
-
|
1105
|
-
const hasActiveSubmission = fetcher ? fetcher.state === "submitting" :
|
1192
|
+
let navigation = (0, import_react2.useNavigation)();
|
1193
|
+
const hasActiveSubmission = fetcher ? fetcher.state === "submitting" : navigation.state === "submitting";
|
1106
1194
|
return hasActiveSubmission;
|
1107
1195
|
};
|
1108
1196
|
var useFieldTouched = (field, { formId }) => {
|
@@ -1135,7 +1223,7 @@ var useFieldDefaultValue = (name, context) => {
|
|
1135
1223
|
var useInternalIsSubmitting = (formId) => useFormStore(formId, (state) => state.isSubmitting);
|
1136
1224
|
var useInternalIsValid = (formId) => useFormStore(formId, (state) => state.isValid());
|
1137
1225
|
var useInternalHasBeenSubmitted = (formId) => useFormStore(formId, (state) => state.hasBeenSubmitted);
|
1138
|
-
var
|
1226
|
+
var useSmartValidate = (formId) => useFormStore(formId, (state) => state.smartValidate);
|
1139
1227
|
var useValidate = (formId) => useFormStore(formId, (state) => state.validate);
|
1140
1228
|
var noOpReceiver = () => () => {
|
1141
1229
|
};
|
@@ -1246,7 +1334,7 @@ var useField = (name, options) => {
|
|
1246
1334
|
const error = useFieldError(name, formContext);
|
1247
1335
|
const clearError = useClearError(formContext);
|
1248
1336
|
const hasBeenSubmitted = useInternalHasBeenSubmitted(formContext.formId);
|
1249
|
-
const
|
1337
|
+
const smartValidate = useSmartValidate(formContext.formId);
|
1250
1338
|
const registerReceiveFocus = useRegisterReceiveFocus(formContext.formId);
|
1251
1339
|
(0, import_react5.useEffect)(() => {
|
1252
1340
|
if (handleReceiveFocus)
|
@@ -1256,9 +1344,7 @@ var useField = (name, options) => {
|
|
1256
1344
|
const helpers = {
|
1257
1345
|
error,
|
1258
1346
|
clearError: () => clearError(name),
|
1259
|
-
validate: () => {
|
1260
|
-
validateField(name);
|
1261
|
-
},
|
1347
|
+
validate: () => smartValidate({ alwaysIncludeErrorsFromFields: [name] }),
|
1262
1348
|
defaultValue,
|
1263
1349
|
touched,
|
1264
1350
|
setTouched
|
@@ -1282,7 +1368,7 @@ var useField = (name, options) => {
|
|
1282
1368
|
name,
|
1283
1369
|
hasBeenSubmitted,
|
1284
1370
|
options == null ? void 0 : options.validationBehavior,
|
1285
|
-
|
1371
|
+
smartValidate
|
1286
1372
|
]);
|
1287
1373
|
return field;
|
1288
1374
|
};
|
@@ -1495,6 +1581,9 @@ function ValidatedForm({
|
|
1495
1581
|
method,
|
1496
1582
|
replace: replace2,
|
1497
1583
|
id,
|
1584
|
+
preventScrollReset,
|
1585
|
+
relative,
|
1586
|
+
encType,
|
1498
1587
|
...rest
|
1499
1588
|
}) {
|
1500
1589
|
var _a;
|
@@ -1587,11 +1676,11 @@ function ValidatedForm({
|
|
1587
1676
|
startSubmit();
|
1588
1677
|
const submitter = nativeEvent.submitter;
|
1589
1678
|
const formMethod = (submitter == null ? void 0 : submitter.formMethod) || method;
|
1590
|
-
const
|
1679
|
+
const formData = getDataFromForm(target);
|
1591
1680
|
if (submitter == null ? void 0 : submitter.name) {
|
1592
|
-
|
1681
|
+
formData.append(submitter.name, submitter.value);
|
1593
1682
|
}
|
1594
|
-
const result = await validator.validate(
|
1683
|
+
const result = await validator.validate(formData);
|
1595
1684
|
if (result.error) {
|
1596
1685
|
setFieldErrors(result.error.fieldErrors);
|
1597
1686
|
endSubmit();
|
@@ -1610,13 +1699,18 @@ function ValidatedForm({
|
|
1610
1699
|
endSubmit();
|
1611
1700
|
return;
|
1612
1701
|
}
|
1702
|
+
const opts = {
|
1703
|
+
method: formMethod,
|
1704
|
+
replace: replace2,
|
1705
|
+
preventScrollReset,
|
1706
|
+
relative,
|
1707
|
+
action,
|
1708
|
+
encType
|
1709
|
+
};
|
1613
1710
|
if (fetcher)
|
1614
|
-
fetcher.submit(
|
1711
|
+
fetcher.submit(formData, opts);
|
1615
1712
|
else
|
1616
|
-
submit(
|
1617
|
-
replace: replace2,
|
1618
|
-
method: formMethod
|
1619
|
-
});
|
1713
|
+
submit(formData, opts);
|
1620
1714
|
}
|
1621
1715
|
};
|
1622
1716
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
@@ -1627,7 +1721,10 @@ function ValidatedForm({
|
|
1627
1721
|
id,
|
1628
1722
|
action,
|
1629
1723
|
method,
|
1724
|
+
encType,
|
1630
1725
|
replace: replace2,
|
1726
|
+
preventScrollReset,
|
1727
|
+
relative,
|
1631
1728
|
onSubmit: (e) => {
|
1632
1729
|
e.preventDefault();
|
1633
1730
|
handleSubmit(
|
@@ -1745,7 +1842,7 @@ var useFormState = (formId) => {
|
|
1745
1842
|
var useFormHelpers = (formId) => {
|
1746
1843
|
const formContext = useInternalFormContext(formId, "useFormHelpers");
|
1747
1844
|
const setTouched = useSetTouched(formContext);
|
1748
|
-
const validateField =
|
1845
|
+
const validateField = useSmartValidate(formContext.formId);
|
1749
1846
|
const validate = useValidate(formContext.formId);
|
1750
1847
|
const clearError = useClearError(formContext);
|
1751
1848
|
const setFieldErrors = useSetFieldErrors(formContext.formId);
|
@@ -1755,7 +1852,13 @@ var useFormHelpers = (formId) => {
|
|
1755
1852
|
return (0, import_react11.useMemo)(
|
1756
1853
|
() => ({
|
1757
1854
|
setTouched,
|
1758
|
-
validateField
|
1855
|
+
validateField: async (fieldName) => {
|
1856
|
+
var _a, _b;
|
1857
|
+
const res = await validateField({
|
1858
|
+
alwaysIncludeErrorsFromFields: [fieldName]
|
1859
|
+
});
|
1860
|
+
return (_b = (_a = res.error) == null ? void 0 : _a.fieldErrors[fieldName]) != null ? _b : null;
|
1861
|
+
},
|
1759
1862
|
clearError,
|
1760
1863
|
validate,
|
1761
1864
|
clearAllErrors: () => setFieldErrors({}),
|
@@ -1828,6 +1931,7 @@ var useFormContext = (formId) => {
|
|
1828
1931
|
};
|
1829
1932
|
|
1830
1933
|
// src/internal/state/fieldArray.tsx
|
1934
|
+
var import_nanoid = require("nanoid");
|
1831
1935
|
var import_react13 = require("react");
|
1832
1936
|
var import_react14 = require("react");
|
1833
1937
|
var import_tiny_invariant4 = __toESM(require("tiny-invariant"));
|
@@ -1836,7 +1940,7 @@ var useInternalFieldArray = (context, field, validationBehavior) => {
|
|
1836
1940
|
const value = useFieldDefaultValue(field, context);
|
1837
1941
|
useRegisterControlledField(context, field);
|
1838
1942
|
const hasBeenSubmitted = useInternalHasBeenSubmitted(context.formId);
|
1839
|
-
const validateField =
|
1943
|
+
const validateField = useSmartValidate(context.formId);
|
1840
1944
|
const error = useFieldError(field, context);
|
1841
1945
|
const resolvedValidationBehavior = {
|
1842
1946
|
initial: "onSubmit",
|
@@ -1846,7 +1950,7 @@ var useInternalFieldArray = (context, field, validationBehavior) => {
|
|
1846
1950
|
const behavior = hasBeenSubmitted ? resolvedValidationBehavior.whenSubmitted : resolvedValidationBehavior.initial;
|
1847
1951
|
const maybeValidate = (0, import_react14.useCallback)(() => {
|
1848
1952
|
if (behavior === "onChange") {
|
1849
|
-
validateField(field);
|
1953
|
+
validateField({ alwaysIncludeErrorsFromFields: [field] });
|
1850
1954
|
}
|
1851
1955
|
}, [behavior, field, validateField]);
|
1852
1956
|
(0, import_tiny_invariant4.default)(
|
@@ -1857,56 +1961,78 @@ var useInternalFieldArray = (context, field, validationBehavior) => {
|
|
1857
1961
|
context.formId,
|
1858
1962
|
(state) => state.controlledFields.array
|
1859
1963
|
);
|
1964
|
+
const arrayValue = (0, import_react13.useMemo)(() => value != null ? value : [], [value]);
|
1965
|
+
const keyRef = (0, import_react13.useRef)([]);
|
1966
|
+
if (keyRef.current.length !== arrayValue.length) {
|
1967
|
+
keyRef.current = arrayValue.map(() => (0, import_nanoid.nanoid)());
|
1968
|
+
}
|
1860
1969
|
const helpers = (0, import_react13.useMemo)(
|
1861
1970
|
() => ({
|
1862
1971
|
push: (item) => {
|
1863
1972
|
arr.push(field, item);
|
1973
|
+
keyRef.current.push((0, import_nanoid.nanoid)());
|
1864
1974
|
maybeValidate();
|
1865
1975
|
},
|
1866
1976
|
swap: (indexA, indexB) => {
|
1867
1977
|
arr.swap(field, indexA, indexB);
|
1978
|
+
swap(keyRef.current, indexA, indexB);
|
1868
1979
|
maybeValidate();
|
1869
1980
|
},
|
1870
1981
|
move: (from2, to) => {
|
1871
1982
|
arr.move(field, from2, to);
|
1983
|
+
move(keyRef.current, from2, to);
|
1872
1984
|
maybeValidate();
|
1873
1985
|
},
|
1874
1986
|
insert: (index, value2) => {
|
1875
1987
|
arr.insert(field, index, value2);
|
1988
|
+
insert(keyRef.current, index, (0, import_nanoid.nanoid)());
|
1876
1989
|
maybeValidate();
|
1877
1990
|
},
|
1878
1991
|
unshift: (value2) => {
|
1879
1992
|
arr.unshift(field, value2);
|
1993
|
+
keyRef.current.unshift((0, import_nanoid.nanoid)());
|
1880
1994
|
maybeValidate();
|
1881
1995
|
},
|
1882
1996
|
remove: (index) => {
|
1883
1997
|
arr.remove(field, index);
|
1998
|
+
remove(keyRef.current, index);
|
1884
1999
|
maybeValidate();
|
1885
2000
|
},
|
1886
2001
|
pop: () => {
|
1887
2002
|
arr.pop(field);
|
2003
|
+
keyRef.current.pop();
|
1888
2004
|
maybeValidate();
|
1889
2005
|
},
|
1890
2006
|
replace: (index, value2) => {
|
1891
2007
|
arr.replace(field, index, value2);
|
2008
|
+
keyRef.current[index] = (0, import_nanoid.nanoid)();
|
1892
2009
|
maybeValidate();
|
1893
2010
|
}
|
1894
2011
|
}),
|
1895
2012
|
[arr, field, maybeValidate]
|
1896
2013
|
);
|
1897
|
-
const
|
1898
|
-
|
2014
|
+
const valueWithKeys = (0, import_react13.useMemo)(() => {
|
2015
|
+
const result = [];
|
2016
|
+
arrayValue.forEach((item, index) => {
|
2017
|
+
result[index] = {
|
2018
|
+
key: keyRef.current[index],
|
2019
|
+
defaultValue: item
|
2020
|
+
};
|
2021
|
+
});
|
2022
|
+
return result;
|
2023
|
+
}, [arrayValue]);
|
2024
|
+
return [valueWithKeys, helpers, error];
|
1899
2025
|
};
|
1900
2026
|
function useFieldArray(name, { formId, validationBehavior } = {}) {
|
1901
2027
|
const context = useInternalFormContext(formId, "FieldArray");
|
1902
2028
|
return useInternalFieldArray(context, name, validationBehavior);
|
1903
2029
|
}
|
1904
|
-
|
2030
|
+
function FieldArray({
|
1905
2031
|
name,
|
1906
2032
|
children,
|
1907
2033
|
formId,
|
1908
2034
|
validationBehavior
|
1909
|
-
})
|
2035
|
+
}) {
|
1910
2036
|
const context = useInternalFormContext(formId, "FieldArray");
|
1911
2037
|
const [value, helpers, error] = useInternalFieldArray(
|
1912
2038
|
context,
|
@@ -1914,7 +2040,7 @@ var FieldArray = ({
|
|
1914
2040
|
validationBehavior
|
1915
2041
|
);
|
1916
2042
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: children(value, helpers, error) });
|
1917
|
-
}
|
2043
|
+
}
|
1918
2044
|
// Annotate the CommonJS export names for ESM import in node:
|
1919
2045
|
0 && (module.exports = {
|
1920
2046
|
FieldArray,
|