remix-validated-form 4.5.7 → 4.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. package/.turbo/turbo-build.log +10 -10
  2. package/browser/ValidatedForm.js +0 -3
  3. package/browser/index.d.ts +1 -0
  4. package/browser/index.js +1 -0
  5. package/browser/internal/hooks.d.ts +2 -3
  6. package/browser/internal/hooks.js +3 -4
  7. package/browser/internal/state/controlledFields.d.ts +1 -0
  8. package/browser/internal/state/controlledFields.js +17 -29
  9. package/browser/internal/state/createFormStore.d.ts +31 -1
  10. package/browser/internal/state/createFormStore.js +175 -8
  11. package/browser/internal/state/fieldArray.d.ts +1 -1
  12. package/browser/internal/state/fieldArray.js +2 -1
  13. package/browser/validation/createValidator.js +2 -4
  14. package/dist/remix-validated-form.cjs.js +4 -4
  15. package/dist/remix-validated-form.cjs.js.map +1 -1
  16. package/dist/remix-validated-form.es.js +478 -138
  17. package/dist/remix-validated-form.es.js.map +1 -1
  18. package/dist/remix-validated-form.umd.js +4 -4
  19. package/dist/remix-validated-form.umd.js.map +1 -1
  20. package/dist/types/index.d.ts +1 -0
  21. package/dist/types/internal/hooks.d.ts +1 -0
  22. package/dist/types/internal/state/arrayUtil.d.ts +12 -0
  23. package/dist/types/internal/state/controlledFields.d.ts +1 -0
  24. package/dist/types/internal/state/createFormStore.d.ts +31 -1
  25. package/dist/types/internal/state/fieldArray.d.ts +28 -0
  26. package/package.json +1 -1
  27. package/src/ValidatedForm.tsx +0 -3
  28. package/src/index.ts +6 -0
  29. package/src/internal/hooks.ts +9 -4
  30. package/src/internal/logic/nestedObjectToPathObject.ts +63 -0
  31. package/src/internal/state/arrayUtil.ts +399 -0
  32. package/src/internal/state/controlledFields.ts +39 -43
  33. package/src/internal/state/createFormStore.ts +286 -9
  34. package/src/internal/state/fieldArray.tsx +155 -0
  35. package/dist/types/internal/state/controlledFieldStore.d.ts +0 -26
  36. package/src/internal/state/controlledFieldStore.ts +0 -112
@@ -1,17 +1,17 @@
1
1
  $ vite build
2
2
  vite v2.9.5 building for production...
3
3
  transforming...
4
- ✓ 561 modules transformed.
4
+ ✓ 562 modules transformed.
5
5
  rendering chunks...
6
- dist/remix-validated-form.cjs.js  52.49 KiB / gzip: 20.02 KiB
7
- dist/remix-validated-form.cjs.js.map 323.70 KiB
8
- dist/remix-validated-form.es.js  120.27 KiB / gzip: 28.59 KiB
9
- dist/remix-validated-form.es.js.map 317.20 KiB
10
- dist/remix-validated-form.umd.js  52.69 KiB / gzip: 20.08 KiB
11
- dist/remix-validated-form.umd.js.map 323.45 KiB
6
+ dist/remix-validated-form.cjs.js  56.46 KiB / gzip: 20.96 KiB
7
+ dist/remix-validated-form.cjs.js.map 351.84 KiB
8
+ dist/remix-validated-form.es.js  129.94 KiB / gzip: 30.15 KiB
9
+ dist/remix-validated-form.es.js.map 345.86 KiB
10
+ No name was provided for external module 'react' in output.globals – guessing 'React'
11
+ No name was provided for external module '@remix-run/react' in output.globals – guessing 'react'
12
+ dist/remix-validated-form.umd.js  56.65 KiB / gzip: 21.04 KiB
13
+ dist/remix-validated-form.umd.js.map 351.78 KiB
12
14
  
13
15
  [vite:dts] Start generate declaration files...
14
- [vite:dts] Declaration files built in 2840ms.
16
+ [vite:dts] Declaration files built in 2937ms.
15
17
  
16
- No name was provided for external module 'react' in output.globals – guessing 'React'
17
- No name was provided for external module '@remix-run/react' in output.globals – guessing 'react'
@@ -7,7 +7,6 @@ import { FORM_ID_FIELD } from "./internal/constants";
7
7
  import { InternalFormContext, } from "./internal/formContext";
8
8
  import { useDefaultValuesFromLoader, useErrorResponseForForm, useHasActiveFormSubmit, useSetFieldErrors, } from "./internal/hooks";
9
9
  import { useMultiValueMap } from "./internal/MultiValueMap";
10
- import { useControlledFieldStore } from "./internal/state/controlledFieldStore";
11
10
  import { useRootFormStore, } from "./internal/state/createFormStore";
12
11
  import { useFormStore } from "./internal/state/storeHooks";
13
12
  import { useSubmitComplete } from "./internal/submissionCallbacks";
@@ -115,7 +114,6 @@ export function ValidatedForm({ validator, onSubmit, children, fetcher, action,
115
114
  const setFieldErrors = useSetFieldErrors(formId);
116
115
  const setFieldError = useFormStore(formId, (state) => state.setFieldError);
117
116
  const reset = useFormStore(formId, (state) => state.reset);
118
- const resetControlledFields = useControlledFieldStore((state) => state.reset);
119
117
  const startSubmit = useFormStore(formId, (state) => state.startSubmit);
120
118
  const endSubmit = useFormStore(formId, (state) => state.endSubmit);
121
119
  const syncFormProps = useFormStore(formId, (state) => state.syncFormProps);
@@ -208,6 +206,5 @@ export function ValidatedForm({ validator, onSubmit, children, fetcher, action,
208
206
  if (event.defaultPrevented)
209
207
  return;
210
208
  reset();
211
- resetControlledFields(formId);
212
209
  }, children: _jsx(InternalFormContext.Provider, { value: contextValue, children: _jsxs(_Fragment, { children: [_jsx(FormResetter, { formRef: formRef, resetAfterSubmit: resetAfterSubmit }, void 0), subaction && (_jsx("input", { type: "hidden", value: subaction, name: "subaction" }, void 0)), id && _jsx("input", { type: "hidden", value: id, name: FORM_ID_FIELD }, void 0), children] }, void 0) }, void 0) }, void 0));
213
210
  }
@@ -4,3 +4,4 @@ export * from "./ValidatedForm";
4
4
  export * from "./validation/types";
5
5
  export * from "./validation/createValidator";
6
6
  export * from "./userFacingFormContext";
7
+ export { FieldArray, useFieldArray, type FieldArrayProps, type FieldArrayHelpers, } from "./internal/state/fieldArray";
package/browser/index.js CHANGED
@@ -4,3 +4,4 @@ export * from "./ValidatedForm";
4
4
  export * from "./validation/types";
5
5
  export * from "./validation/createValidator";
6
6
  export * from "./userFacingFormContext";
7
+ export { FieldArray, useFieldArray, } from "./internal/state/fieldArray";
@@ -13,6 +13,7 @@ export declare const useHasActiveFormSubmit: ({ fetcher, }: InternalFormContextV
13
13
  export declare const useFieldTouched: (field: string, { formId }: InternalFormContextValue) => readonly [boolean, (touched: boolean) => void];
14
14
  export declare const useFieldError: (name: string, context: InternalFormContextValue) => string | undefined;
15
15
  export declare const useClearError: (context: InternalFormContextValue) => (field: string) => void;
16
+ export declare const useCurrentDefaultValueForField: (formId: InternalFormId, field: string) => any;
16
17
  export declare const useFieldDefaultValue: (name: string, context: InternalFormContextValue) => any;
17
18
  export declare const useInternalIsSubmitting: (formId: InternalFormId) => boolean;
18
19
  export declare const useInternalIsValid: (formId: InternalFormId) => boolean;
@@ -20,9 +21,7 @@ export declare const useInternalHasBeenSubmitted: (formId: InternalFormId) => bo
20
21
  export declare const useValidateField: (formId: InternalFormId) => (fieldName: string) => Promise<string | null>;
21
22
  export declare const useValidate: (formId: InternalFormId) => () => Promise<import("..").ValidationResult<unknown>>;
22
23
  export declare const useRegisterReceiveFocus: (formId: InternalFormId) => (fieldName: string, handler: () => void) => () => void;
23
- export declare const useSyncedDefaultValues: (formId: InternalFormId) => {
24
- [fieldName: string]: any;
25
- };
24
+ export declare const useSyncedDefaultValues: (formId: InternalFormId) => {};
26
25
  export declare const useSetTouched: ({ formId }: InternalFormContextValue) => (field: string, touched: boolean) => void;
27
26
  export declare const useTouchedFields: (formId: InternalFormId) => import("..").TouchedFields;
28
27
  export declare const useFieldErrors: (formId: InternalFormId) => FieldErrors;
@@ -92,12 +92,11 @@ export const useClearError = (context) => {
92
92
  const { formId } = context;
93
93
  return useFormStore(formId, (state) => state.clearFieldError);
94
94
  };
95
+ export const useCurrentDefaultValueForField = (formId, field) => useFormStore(formId, (state) => lodashGet(state.currentDefaultValues, field));
95
96
  export const useFieldDefaultValue = (name, context) => {
96
97
  const defaultValues = useDefaultValuesForForm(context);
97
- const state = useSyncedDefaultValues(context.formId);
98
- return defaultValues
99
- .map((val) => lodashGet(val, name))
100
- .hydrateTo(lodashGet(state, name));
98
+ const state = useCurrentDefaultValueForField(context.formId, name);
99
+ return defaultValues.map((val) => lodashGet(val, name)).hydrateTo(state);
101
100
  };
102
101
  export const useInternalIsSubmitting = (formId) => useFormStore(formId, (state) => state.isSubmitting);
103
102
  export const useInternalIsValid = (formId) => useFormStore(formId, (state) => state.isValid());
@@ -1,6 +1,7 @@
1
1
  import { InternalFormContextValue } from "../formContext";
2
2
  import { InternalFormId } from "./types";
3
3
  export declare const useControlledFieldValue: (context: InternalFormContextValue, field: string) => any;
4
+ export declare const useRegisterControlledField: (context: InternalFormContextValue, field: string) => void;
4
5
  export declare const useControllableValue: (context: InternalFormContextValue, field: string) => readonly [any, (value: unknown) => void];
5
6
  export declare const useUpdateControllableValue: (formId: InternalFormId) => (field: string, value: unknown) => void;
6
7
  export declare const useAwaitValue: (formId: InternalFormId) => (field: string) => Promise<void>;
@@ -1,48 +1,36 @@
1
1
  import { useCallback, useEffect } from "react";
2
2
  import { useFieldDefaultValue } from "../hooks";
3
- import { useControlledFieldStore } from "./controlledFieldStore";
4
3
  import { useFormStore } from "./storeHooks";
5
4
  export const useControlledFieldValue = (context, field) => {
6
- const value = useControlledFieldStore((state) => { var _a; return (_a = state.getField(context.formId, field)) === null || _a === void 0 ? void 0 : _a.value; });
5
+ const value = useFormStore(context.formId, (state) => state.controlledFields.getValue(field));
7
6
  const isFormHydrated = useFormStore(context.formId, (state) => state.isHydrated);
8
7
  const defaultValue = useFieldDefaultValue(field, context);
9
- const isFieldHydrated = useControlledFieldStore((state) => { var _a, _b; return (_b = (_a = state.getField(context.formId, field)) === null || _a === void 0 ? void 0 : _a.hydrated) !== null && _b !== void 0 ? _b : false; });
10
- const hydrateWithDefault = useControlledFieldStore((state) => state.hydrateWithDefault);
11
- useEffect(() => {
12
- if (isFormHydrated && !isFieldHydrated) {
13
- hydrateWithDefault(context.formId, field, defaultValue);
14
- }
15
- }, [
16
- context.formId,
17
- defaultValue,
18
- field,
19
- hydrateWithDefault,
20
- isFieldHydrated,
21
- isFormHydrated,
22
- ]);
23
- return isFieldHydrated ? value : defaultValue;
8
+ return isFormHydrated ? value : defaultValue;
24
9
  };
25
- export const useControllableValue = (context, field) => {
26
- const resolveUpdate = useControlledFieldStore((state) => { var _a; return (_a = state.getField(context.formId, field)) === null || _a === void 0 ? void 0 : _a.resolveValueUpdate; });
10
+ export const useRegisterControlledField = (context, field) => {
11
+ const resolveUpdate = useFormStore(context.formId, (state) => state.controlledFields.valueUpdateResolvers[field]);
27
12
  useEffect(() => {
28
13
  resolveUpdate === null || resolveUpdate === void 0 ? void 0 : resolveUpdate();
29
14
  }, [resolveUpdate]);
30
- const register = useControlledFieldStore((state) => state.register);
31
- const unregister = useControlledFieldStore((state) => state.unregister);
15
+ const register = useFormStore(context.formId, (state) => state.controlledFields.register);
16
+ const unregister = useFormStore(context.formId, (state) => state.controlledFields.unregister);
32
17
  useEffect(() => {
33
- register(context.formId, field);
34
- return () => unregister(context.formId, field);
18
+ register(field);
19
+ return () => unregister(field);
35
20
  }, [context.formId, field, register, unregister]);
36
- const setControlledFieldValue = useControlledFieldStore((state) => state.setValue);
37
- const setValue = useCallback((value) => setControlledFieldValue(context.formId, field, value), [context.formId, field, setControlledFieldValue]);
21
+ };
22
+ export const useControllableValue = (context, field) => {
23
+ useRegisterControlledField(context, field);
24
+ const setControlledFieldValue = useFormStore(context.formId, (state) => state.controlledFields.setValue);
25
+ const setValue = useCallback((value) => setControlledFieldValue(field, value), [field, setControlledFieldValue]);
38
26
  const value = useControlledFieldValue(context, field);
39
27
  return [value, setValue];
40
28
  };
41
29
  export const useUpdateControllableValue = (formId) => {
42
- const setValue = useControlledFieldStore((state) => state.setValue);
43
- return useCallback((field, value) => setValue(formId, field, value), [formId, setValue]);
30
+ const setValue = useFormStore(formId, (state) => state.controlledFields.setValue);
31
+ return useCallback((field, value) => setValue(field, value), [setValue]);
44
32
  };
45
33
  export const useAwaitValue = (formId) => {
46
- const awaitValue = useControlledFieldStore((state) => state.awaitValueUpdate);
47
- return useCallback((field) => awaitValue(formId, field), [awaitValue, formId]);
34
+ const awaitValue = useFormStore(formId, (state) => state.controlledFields.awaitValueUpdate);
35
+ return useCallback((field) => awaitValue(field), [awaitValue]);
48
36
  };
@@ -27,6 +27,7 @@ export declare type FormState = {
27
27
  touchedFields: TouchedFields;
28
28
  formProps?: SyncedFormProps;
29
29
  formElement: HTMLFormElement | null;
30
+ currentDefaultValues: Record<string, any>;
30
31
  isValid: () => boolean;
31
32
  startSubmit: () => void;
32
33
  endSubmit: () => void;
@@ -36,13 +37,42 @@ export declare type FormState = {
36
37
  clearFieldError: (field: string) => void;
37
38
  reset: () => void;
38
39
  syncFormProps: (props: SyncedFormProps) => void;
39
- setHydrated: () => void;
40
40
  setFormElement: (formElement: HTMLFormElement | null) => void;
41
41
  validateField: (fieldName: string) => Promise<string | null>;
42
42
  validate: () => Promise<ValidationResult<unknown>>;
43
43
  resetFormElement: () => void;
44
44
  submit: () => void;
45
45
  getValues: () => FormData;
46
+ controlledFields: {
47
+ values: {
48
+ [fieldName: string]: any;
49
+ };
50
+ refCounts: {
51
+ [fieldName: string]: number;
52
+ };
53
+ valueUpdatePromises: {
54
+ [fieldName: string]: Promise<void>;
55
+ };
56
+ valueUpdateResolvers: {
57
+ [fieldName: string]: () => void;
58
+ };
59
+ register: (fieldName: string) => void;
60
+ unregister: (fieldName: string) => void;
61
+ setValue: (fieldName: string, value: unknown) => void;
62
+ kickoffValueUpdate: (fieldName: string) => void;
63
+ getValue: (fieldName: string) => unknown;
64
+ awaitValueUpdate: (fieldName: string) => Promise<void>;
65
+ array: {
66
+ push: (fieldName: string, value: unknown) => void;
67
+ swap: (fieldName: string, indexA: number, indexB: number) => void;
68
+ move: (fieldName: string, fromIndex: number, toIndex: number) => void;
69
+ insert: (fieldName: string, index: number, value: unknown) => void;
70
+ unshift: (fieldName: string, value: unknown) => void;
71
+ remove: (fieldName: string, index: number) => void;
72
+ pop: (fieldName: string) => void;
73
+ replace: (fieldName: string, index: number, value: unknown) => void;
74
+ };
75
+ };
46
76
  };
47
77
  export declare const useRootFormStore: import("zustand").UseBoundStore<Omit<import("zustand").StoreApi<FormStoreState>, "setState"> & {
48
78
  setState(nextStateOrUpdater: FormStoreState | Partial<FormStoreState> | ((state: WritableDraft<FormStoreState>) => void), shouldReplace?: boolean | undefined): void;
@@ -1,8 +1,10 @@
1
+ import lodashGet from "lodash/get";
2
+ import lodashSet from "lodash/set";
1
3
  import invariant from "tiny-invariant";
2
4
  import create from "zustand";
3
5
  import { immer } from "zustand/middleware/immer";
4
6
  import { requestSubmit } from "../logic/requestSubmit";
5
- import { useControlledFieldStore } from "./controlledFieldStore";
7
+ import * as arrayUtil from "./arrayUtil";
6
8
  const noOp = () => { };
7
9
  const defaultFormState = {
8
10
  isHydrated: false,
@@ -18,9 +20,9 @@ const defaultFormState = {
18
20
  setFieldError: noOp,
19
21
  setFieldErrors: noOp,
20
22
  clearFieldError: noOp,
23
+ currentDefaultValues: {},
21
24
  reset: () => noOp,
22
25
  syncFormProps: noOp,
23
- setHydrated: noOp,
24
26
  setFormElement: noOp,
25
27
  validateField: async () => null,
26
28
  validate: async () => {
@@ -31,8 +33,32 @@ const defaultFormState = {
31
33
  },
32
34
  resetFormElement: noOp,
33
35
  getValues: () => new FormData(),
36
+ controlledFields: {
37
+ values: {},
38
+ refCounts: {},
39
+ valueUpdatePromises: {},
40
+ valueUpdateResolvers: {},
41
+ register: noOp,
42
+ unregister: noOp,
43
+ setValue: noOp,
44
+ getValue: noOp,
45
+ kickoffValueUpdate: noOp,
46
+ awaitValueUpdate: async () => {
47
+ throw new Error("AwaitValueUpdate called before form was initialized.");
48
+ },
49
+ array: {
50
+ push: noOp,
51
+ swap: noOp,
52
+ move: noOp,
53
+ insert: noOp,
54
+ unshift: noOp,
55
+ remove: noOp,
56
+ pop: noOp,
57
+ replace: noOp,
58
+ },
59
+ },
34
60
  };
35
- const createFormState = (formId, set, get) => ({
61
+ const createFormState = (set, get) => ({
36
62
  // It's not "hydrated" until the form props are synced
37
63
  isHydrated: false,
38
64
  isSubmitting: false,
@@ -40,6 +66,7 @@ const createFormState = (formId, set, get) => ({
40
66
  touchedFields: {},
41
67
  fieldErrors: {},
42
68
  formElement: null,
69
+ currentDefaultValues: {},
43
70
  isValid: () => Object.keys(get().fieldErrors).length === 0,
44
71
  startSubmit: () => set((state) => {
45
72
  state.isSubmitting = true;
@@ -61,17 +88,22 @@ const createFormState = (formId, set, get) => ({
61
88
  delete state.fieldErrors[fieldName];
62
89
  }),
63
90
  reset: () => set((state) => {
91
+ var _a, _b;
64
92
  state.fieldErrors = {};
65
93
  state.touchedFields = {};
66
94
  state.hasBeenSubmitted = false;
95
+ const nextDefaults = (_b = (_a = state.formProps) === null || _a === void 0 ? void 0 : _a.defaultValues) !== null && _b !== void 0 ? _b : {};
96
+ state.controlledFields.values = nextDefaults;
97
+ state.currentDefaultValues = nextDefaults;
67
98
  }),
68
99
  syncFormProps: (props) => set((state) => {
100
+ if (!state.isHydrated) {
101
+ state.controlledFields.values = props.defaultValues;
102
+ state.currentDefaultValues = props.defaultValues;
103
+ }
69
104
  state.formProps = props;
70
105
  state.isHydrated = true;
71
106
  }),
72
- setHydrated: () => set((state) => {
73
- state.isHydrated = true;
74
- }),
75
107
  setFormElement: (formElement) => {
76
108
  // This gets called frequently, so we want to avoid calling set() every time
77
109
  // Or else we wind up with an infinite loop
@@ -89,7 +121,7 @@ const createFormState = (formId, set, get) => ({
89
121
  invariant(formElement, "Cannot find reference to form. This is probably a bug in remix-validated-form.");
90
122
  const validator = (_a = get().formProps) === null || _a === void 0 ? void 0 : _a.validator;
91
123
  invariant(validator, "Cannot validator. This is probably a bug in remix-validated-form.");
92
- await ((_c = (_b = useControlledFieldStore.getState()).awaitValueUpdate) === null || _c === void 0 ? void 0 : _c.call(_b, formId, field));
124
+ await ((_c = (_b = get().controlledFields).awaitValueUpdate) === null || _c === void 0 ? void 0 : _c.call(_b, field));
93
125
  const { error } = await validator.validateField(new FormData(formElement), field);
94
126
  if (error) {
95
127
  get().setFieldError(field, error);
@@ -118,6 +150,141 @@ const createFormState = (formId, set, get) => ({
118
150
  },
119
151
  getValues: () => { var _a; return new FormData((_a = get().formElement) !== null && _a !== void 0 ? _a : undefined); },
120
152
  resetFormElement: () => { var _a; return (_a = get().formElement) === null || _a === void 0 ? void 0 : _a.reset(); },
153
+ controlledFields: {
154
+ values: {},
155
+ refCounts: {},
156
+ valueUpdatePromises: {},
157
+ valueUpdateResolvers: {},
158
+ register: (fieldName) => {
159
+ set((state) => {
160
+ var _a;
161
+ const current = (_a = state.controlledFields.refCounts[fieldName]) !== null && _a !== void 0 ? _a : 0;
162
+ state.controlledFields.refCounts[fieldName] = current + 1;
163
+ });
164
+ },
165
+ unregister: (fieldName) => {
166
+ // For this helper in particular, we may run into a case where state is undefined.
167
+ // When the whole form unmounts, the form state may be cleaned up before the fields are.
168
+ if (get() === null || get() === undefined)
169
+ return;
170
+ set((state) => {
171
+ var _a, _b, _c;
172
+ const current = (_a = state.controlledFields.refCounts[fieldName]) !== null && _a !== void 0 ? _a : 0;
173
+ if (current > 1) {
174
+ state.controlledFields.refCounts[fieldName] = current - 1;
175
+ return;
176
+ }
177
+ const isNested = Object.keys(state.controlledFields.refCounts).some((key) => fieldName.startsWith(key) && key !== fieldName);
178
+ // When nested within a field array, we should leave resetting up to the field array
179
+ if (!isNested) {
180
+ lodashSet(state.controlledFields.values, fieldName, lodashGet((_b = state.formProps) === null || _b === void 0 ? void 0 : _b.defaultValues, fieldName));
181
+ lodashSet(state.currentDefaultValues, fieldName, lodashGet((_c = state.formProps) === null || _c === void 0 ? void 0 : _c.defaultValues, fieldName));
182
+ }
183
+ delete state.controlledFields.refCounts[fieldName];
184
+ });
185
+ },
186
+ getValue: (fieldName) => lodashGet(get().controlledFields.values, fieldName),
187
+ setValue: (fieldName, value) => {
188
+ set((state) => {
189
+ lodashSet(state.controlledFields.values, fieldName, value);
190
+ });
191
+ get().controlledFields.kickoffValueUpdate(fieldName);
192
+ },
193
+ kickoffValueUpdate: (fieldName) => {
194
+ const clear = () => set((state) => {
195
+ delete state.controlledFields.valueUpdateResolvers[fieldName];
196
+ delete state.controlledFields.valueUpdatePromises[fieldName];
197
+ });
198
+ set((state) => {
199
+ const promise = new Promise((resolve) => {
200
+ state.controlledFields.valueUpdateResolvers[fieldName] = resolve;
201
+ }).then(clear);
202
+ state.controlledFields.valueUpdatePromises[fieldName] = promise;
203
+ });
204
+ },
205
+ awaitValueUpdate: async (fieldName) => {
206
+ await get().controlledFields.valueUpdatePromises[fieldName];
207
+ },
208
+ array: {
209
+ push: (fieldName, item) => {
210
+ set((state) => {
211
+ arrayUtil
212
+ .getArray(state.controlledFields.values, fieldName)
213
+ .push(item);
214
+ arrayUtil.getArray(state.currentDefaultValues, fieldName).push(item);
215
+ // New item added to the end, no need to update touched or error
216
+ });
217
+ get().controlledFields.kickoffValueUpdate(fieldName);
218
+ },
219
+ swap: (fieldName, indexA, indexB) => {
220
+ set((state) => {
221
+ arrayUtil.swap(arrayUtil.getArray(state.controlledFields.values, fieldName), indexA, indexB);
222
+ arrayUtil.swap(arrayUtil.getArray(state.currentDefaultValues, fieldName), indexA, indexB);
223
+ arrayUtil.mutateAsArray(fieldName, state.touchedFields, (array) => arrayUtil.swap(array, indexA, indexB));
224
+ arrayUtil.mutateAsArray(fieldName, state.fieldErrors, (array) => arrayUtil.swap(array, indexA, indexB));
225
+ });
226
+ get().controlledFields.kickoffValueUpdate(fieldName);
227
+ },
228
+ move: (fieldName, from, to) => {
229
+ set((state) => {
230
+ arrayUtil.move(arrayUtil.getArray(state.controlledFields.values, fieldName), from, to);
231
+ arrayUtil.move(arrayUtil.getArray(state.currentDefaultValues, fieldName), from, to);
232
+ arrayUtil.mutateAsArray(fieldName, state.touchedFields, (array) => arrayUtil.move(array, from, to));
233
+ arrayUtil.mutateAsArray(fieldName, state.fieldErrors, (array) => arrayUtil.move(array, from, to));
234
+ });
235
+ get().controlledFields.kickoffValueUpdate(fieldName);
236
+ },
237
+ insert: (fieldName, index, item) => {
238
+ set((state) => {
239
+ arrayUtil.insert(arrayUtil.getArray(state.controlledFields.values, fieldName), index, item);
240
+ arrayUtil.insert(arrayUtil.getArray(state.currentDefaultValues, fieldName), index, item);
241
+ // Even though this is a new item, we need to push around other items.
242
+ arrayUtil.mutateAsArray(fieldName, state.touchedFields, (array) => arrayUtil.insert(array, index, false));
243
+ arrayUtil.mutateAsArray(fieldName, state.fieldErrors, (array) => arrayUtil.insert(array, index, undefined));
244
+ });
245
+ get().controlledFields.kickoffValueUpdate(fieldName);
246
+ },
247
+ remove: (fieldName, index) => {
248
+ set((state) => {
249
+ arrayUtil.remove(arrayUtil.getArray(state.controlledFields.values, fieldName), index);
250
+ arrayUtil.remove(arrayUtil.getArray(state.currentDefaultValues, fieldName), index);
251
+ arrayUtil.mutateAsArray(fieldName, state.touchedFields, (array) => arrayUtil.remove(array, index));
252
+ arrayUtil.mutateAsArray(fieldName, state.fieldErrors, (array) => arrayUtil.remove(array, index));
253
+ });
254
+ get().controlledFields.kickoffValueUpdate(fieldName);
255
+ },
256
+ pop: (fieldName) => {
257
+ set((state) => {
258
+ arrayUtil.getArray(state.controlledFields.values, fieldName).pop();
259
+ arrayUtil.getArray(state.currentDefaultValues, fieldName).pop();
260
+ arrayUtil.mutateAsArray(fieldName, state.touchedFields, (array) => array.pop());
261
+ arrayUtil.mutateAsArray(fieldName, state.fieldErrors, (array) => array.pop());
262
+ });
263
+ get().controlledFields.kickoffValueUpdate(fieldName);
264
+ },
265
+ unshift: (fieldName, value) => {
266
+ set((state) => {
267
+ arrayUtil
268
+ .getArray(state.controlledFields.values, fieldName)
269
+ .unshift(value);
270
+ arrayUtil
271
+ .getArray(state.currentDefaultValues, fieldName)
272
+ .unshift(value);
273
+ arrayUtil.mutateAsArray(fieldName, state.touchedFields, (array) => array.unshift(false));
274
+ arrayUtil.mutateAsArray(fieldName, state.fieldErrors, (array) => array.unshift(undefined));
275
+ });
276
+ },
277
+ replace: (fieldName, index, item) => {
278
+ set((state) => {
279
+ arrayUtil.replace(arrayUtil.getArray(state.controlledFields.values, fieldName), index, item);
280
+ arrayUtil.replace(arrayUtil.getArray(state.currentDefaultValues, fieldName), index, item);
281
+ arrayUtil.mutateAsArray(fieldName, state.touchedFields, (array) => arrayUtil.replace(array, index, item));
282
+ arrayUtil.mutateAsArray(fieldName, state.fieldErrors, (array) => arrayUtil.replace(array, index, item));
283
+ });
284
+ get().controlledFields.kickoffValueUpdate(fieldName);
285
+ },
286
+ },
287
+ },
121
288
  });
122
289
  export const useRootFormStore = create()(immer((set, get) => ({
123
290
  forms: {},
@@ -134,7 +301,7 @@ export const useRootFormStore = create()(immer((set, get) => ({
134
301
  if (get().forms[formId])
135
302
  return;
136
303
  set((state) => {
137
- state.forms[formId] = createFormState(formId, (setter) => set((state) => setter(state.forms[formId])), () => get().forms[formId]);
304
+ state.forms[formId] = createFormState((setter) => set((state) => setter(state.forms[formId])), () => get().forms[formId]);
138
305
  });
139
306
  },
140
307
  })));
@@ -25,4 +25,4 @@ export declare type FieldArrayProps = {
25
25
  formId?: string;
26
26
  validationBehavior?: FieldArrayValidationBehaviorOptions;
27
27
  };
28
- export declare const FieldArray: ({ name, children, formId, validationBehavior, }: FieldArrayProps) => React.ReactNode;
28
+ export declare const FieldArray: ({ name, children, formId, validationBehavior, }: FieldArrayProps) => JSX.Element;
@@ -1,3 +1,4 @@
1
+ import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
1
2
  import { useMemo } from "react";
2
3
  import { useCallback } from "react";
3
4
  import invariant from "tiny-invariant";
@@ -69,5 +70,5 @@ export function useFieldArray(name, { formId, validationBehavior } = {}) {
69
70
  export const FieldArray = ({ name, children, formId, validationBehavior, }) => {
70
71
  const context = useInternalFormContext(formId, "FieldArray");
71
72
  const [value, helpers, error] = useInternalFieldArray(context, name, validationBehavior);
72
- return children(value, helpers, error);
73
+ return _jsx(_Fragment, { children: children(value, helpers, error) }, void 0);
73
74
  };
@@ -1,3 +1,4 @@
1
+ import omit from "lodash/omit";
1
2
  import { FORM_ID_FIELD } from "../internal/constants";
2
3
  import { objectFromPathEntries } from "../internal/flatten";
3
4
  const preprocessFormData = (data) => {
@@ -7,10 +8,7 @@ const preprocessFormData = (data) => {
7
8
  return objectFromPathEntries([...data.entries()]);
8
9
  return objectFromPathEntries(Object.entries(data));
9
10
  };
10
- const omitInternalFields = (data) => {
11
- const { [FORM_ID_FIELD]: _, ...rest } = data;
12
- return rest;
13
- };
11
+ const omitInternalFields = (data) => omit(data, FORM_ID_FIELD);
14
12
  /**
15
13
  * Used to create a validator for a form.
16
14
  * It provides built-in handling for unflattening nested objects and