remix-validated-form 4.6.11 → 5.0.0

Sign up to get free protection for your applications and to get access to all the features.
package/dist/index.d.ts CHANGED
@@ -11,18 +11,18 @@ type ValidationBehaviorOptions = {
11
11
  type HandledProps = "name" | "defaultValue" | "defaultChecked";
12
12
  type Callbacks = "onChange" | "onBlur";
13
13
  type MinimalInputProps = {
14
- onChange?: (...args: any[]) => void;
15
- onBlur?: (...args: any[]) => void;
14
+ onChange?: ((...args: any[]) => void) | undefined;
15
+ onBlur?: ((...args: any[]) => void) | undefined;
16
16
  defaultValue?: any;
17
- defaultChecked?: boolean;
18
- name?: string;
19
- type?: string;
17
+ defaultChecked?: boolean | undefined;
18
+ name?: string | undefined;
19
+ type?: string | undefined;
20
20
  };
21
21
  type GetInputProps = <T extends MinimalInputProps>(props?: Omit<T, HandledProps | Callbacks> & Partial<Pick<T, Callbacks>>) => T;
22
22
 
23
23
  /**
24
24
  * Returns whether or not the parent form is currently being submitted.
25
- * This is different from Remix's `useTransition().submission` in that it
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
- validateField: (unvalidatedData: GenericObject, field: string) => Promise<ValidateFieldResult>;
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 FormProps<DataType> = {
175
+ type SubactionData<DataType, Subaction extends string | undefined> = DataType & {
176
+ subaction: Subaction;
177
+ };
178
+ type DefaultValuesForSubaction<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
  */
@@ -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<DefaultValuesForSubaction<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?: string;
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): [itemDefaults: Item[], helpers: FieldArrayHelpers<any>, error: string | undefined];
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: (itemDefaults: any[], helpers: FieldArrayHelpers, error: string | undefined) => React.ReactNode;
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 const FieldArray: ({ name, children, formId, validationBehavior, }: FieldArrayProps) => JSX.Element;
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, useTransition } from "@remix-run/react";
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
- return array.splice(start, deleteCount);
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
- validateField: async (field) => {
675
- var _a, _b, _c;
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
- if (error) {
692
- get2().setFieldError(field, error);
693
- return error;
694
- } else {
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
- validate: async () => {
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
- const result = await validator.validate(new FormData(formElement));
712
- if (result.error)
713
- get2().setFieldErrors(result.error.fieldErrors);
714
- return result;
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) => insert(array, index, false)
955
+ (array) => insertEmpty(array, index)
868
956
  );
869
957
  mutateAsArray(
870
958
  fieldName,
871
959
  state.fieldErrors,
872
- (array) => insert(array, index, void 0)
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.unshift(false)
1012
+ (array) => insertEmpty(array, 0)
925
1013
  );
926
1014
  mutateAsArray(
927
1015
  fieldName,
928
1016
  state.fieldErrors,
929
- (array) => array.unshift(void 0)
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
- const transition = useTransition();
1062
- const hasActiveSubmission = fetcher ? fetcher.state === "submitting" : !!transition.submission;
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 useValidateField = (formId) => useFormStore(formId, (state) => state.validateField);
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 validateField = useValidateField(formContext.formId);
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
- validateField
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 formDataToValidate = getDataFromForm(target);
1645
+ const formData = getDataFromForm(target);
1557
1646
  if (submitter == null ? void 0 : submitter.name) {
1558
- formDataToValidate.append(submitter.name, submitter.value);
1647
+ formData.append(submitter.name, submitter.value);
1559
1648
  }
1560
- const result = await validator.validate(formDataToValidate);
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(submitter || target, { method: formMethod });
1677
+ fetcher.submit(formData, opts);
1581
1678
  else
1582
- submit(submitter || target, {
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 = useValidateField(formContext.formId);
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 { useMemo as useMemo5 } from "react";
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 = useValidateField(context.formId);
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 arrayValue = useMemo5(() => value != null ? value : [], [value]);
1864
- return [arrayValue, helpers, error];
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
- var FieldArray = ({
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,