remix-validated-form 4.5.2 → 4.6.0-beta.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.
Files changed (38) hide show
  1. package/.turbo/turbo-build.log +8 -9
  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 +1 -0
  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 +177 -14
  11. package/browser/server.d.ts +2 -2
  12. package/browser/server.js +1 -1
  13. package/dist/remix-validated-form.cjs.js +12 -3
  14. package/dist/remix-validated-form.cjs.js.map +1 -1
  15. package/dist/remix-validated-form.es.js +361 -131
  16. package/dist/remix-validated-form.es.js.map +1 -1
  17. package/dist/remix-validated-form.umd.js +12 -3
  18. package/dist/remix-validated-form.umd.js.map +1 -1
  19. package/dist/types/index.d.ts +1 -0
  20. package/dist/types/internal/hooks.d.ts +1 -0
  21. package/dist/types/internal/state/arrayUtil.d.ts +12 -0
  22. package/dist/types/internal/state/controlledFields.d.ts +1 -0
  23. package/dist/types/internal/state/createFormStore.d.ts +31 -1
  24. package/dist/types/internal/state/fieldArray.d.ts +28 -0
  25. package/dist/types/server.d.ts +2 -2
  26. package/package.json +1 -3
  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 +288 -20
  34. package/src/internal/state/fieldArray.tsx +155 -0
  35. package/src/server.ts +1 -1
  36. package/vite.config.ts +1 -1
  37. package/dist/types/internal/state/controlledFieldStore.d.ts +0 -26
  38. package/src/internal/state/controlledFieldStore.ts +0 -112
@@ -1,18 +1,17 @@
1
1
  $ vite build
2
2
  vite v2.9.5 building for production...
3
3
  transforming...
4
- ✓ 319 modules transformed.
4
+ ✓ 483 modules transformed.
5
5
  rendering chunks...
6
- dist/remix-validated-form.cjs.js  45.46 KiB / gzip: 17.05 KiB
7
- dist/remix-validated-form.cjs.js.map 252.62 KiB
8
- dist/remix-validated-form.es.js  101.10 KiB / gzip: 23.71 KiB
9
- dist/remix-validated-form.es.js.map 260.60 KiB
10
- dist/remix-validated-form.umd.js  45.71 KiB / gzip: 17.16 KiB
11
- dist/remix-validated-form.umd.js.map 252.60 KiB
6
+ dist/remix-validated-form.cjs.js  49.71 KiB / gzip: 18.12 KiB
7
+ dist/remix-validated-form.cjs.js.map 281.88 KiB
8
+ dist/remix-validated-form.es.js  109.95 KiB / gzip: 25.38 KiB
9
+ dist/remix-validated-form.es.js.map 290.49 KiB
10
+ dist/remix-validated-form.umd.js  49.91 KiB / gzip: 18.22 KiB
11
+ dist/remix-validated-form.umd.js.map 281.85 KiB
12
12
  
13
13
  [vite:dts] Start generate declaration files...
14
- [vite:dts] Declaration files built in 2049ms.
14
+ [vite:dts] Declaration files built in 2756ms.
15
15
  
16
16
  No name was provided for external module 'react' in output.globals – guessing 'React'
17
17
  No name was provided for external module '@remix-run/react' in output.globals – guessing 'react'
18
- No name was provided for external module '@remix-run/server-runtime' in output.globals – guessing 'serverRuntime'
@@ -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);
@@ -200,6 +198,5 @@ export function ValidatedForm({ validator, onSubmit, children, fetcher, action,
200
198
  if (event.defaultPrevented)
201
199
  return;
202
200
  reset();
203
- resetControlledFields(formId);
204
201
  }, 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));
205
202
  }
@@ -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;
@@ -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,7 +1,9 @@
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
- import { useControlledFieldStore } from "./controlledFieldStore";
6
+ import * as arrayUtil from "./arrayUtil";
5
7
  const noOp = () => { };
6
8
  const defaultFormState = {
7
9
  isHydrated: false,
@@ -17,9 +19,9 @@ const defaultFormState = {
17
19
  setFieldError: noOp,
18
20
  setFieldErrors: noOp,
19
21
  clearFieldError: noOp,
22
+ currentDefaultValues: {},
20
23
  reset: () => noOp,
21
24
  syncFormProps: noOp,
22
- setHydrated: noOp,
23
25
  setFormElement: noOp,
24
26
  validateField: async () => null,
25
27
  validate: async () => {
@@ -30,8 +32,32 @@ const defaultFormState = {
30
32
  },
31
33
  resetFormElement: noOp,
32
34
  getValues: () => new FormData(),
35
+ controlledFields: {
36
+ values: {},
37
+ refCounts: {},
38
+ valueUpdatePromises: {},
39
+ valueUpdateResolvers: {},
40
+ register: noOp,
41
+ unregister: noOp,
42
+ setValue: noOp,
43
+ getValue: noOp,
44
+ kickoffValueUpdate: noOp,
45
+ awaitValueUpdate: async () => {
46
+ throw new Error("AwaitValueUpdate called before form was initialized.");
47
+ },
48
+ array: {
49
+ push: noOp,
50
+ swap: noOp,
51
+ move: noOp,
52
+ insert: noOp,
53
+ unshift: noOp,
54
+ remove: noOp,
55
+ pop: noOp,
56
+ replace: noOp,
57
+ },
58
+ },
33
59
  };
34
- const createFormState = (formId, set, get) => ({
60
+ const createFormState = (set, get) => ({
35
61
  // It's not "hydrated" until the form props are synced
36
62
  isHydrated: false,
37
63
  isSubmitting: false,
@@ -39,6 +65,7 @@ const createFormState = (formId, set, get) => ({
39
65
  touchedFields: {},
40
66
  fieldErrors: {},
41
67
  formElement: null,
68
+ currentDefaultValues: {},
42
69
  isValid: () => Object.keys(get().fieldErrors).length === 0,
43
70
  startSubmit: () => set((state) => {
44
71
  state.isSubmitting = true;
@@ -60,17 +87,22 @@ const createFormState = (formId, set, get) => ({
60
87
  delete state.fieldErrors[fieldName];
61
88
  }),
62
89
  reset: () => set((state) => {
90
+ var _a, _b;
63
91
  state.fieldErrors = {};
64
92
  state.touchedFields = {};
65
93
  state.hasBeenSubmitted = false;
94
+ const nextDefaults = (_b = (_a = state.formProps) === null || _a === void 0 ? void 0 : _a.defaultValues) !== null && _b !== void 0 ? _b : {};
95
+ state.controlledFields.values = nextDefaults;
96
+ state.currentDefaultValues = nextDefaults;
66
97
  }),
67
98
  syncFormProps: (props) => set((state) => {
99
+ if (!state.isHydrated) {
100
+ state.controlledFields.values = props.defaultValues;
101
+ state.currentDefaultValues = props.defaultValues;
102
+ }
68
103
  state.formProps = props;
69
104
  state.isHydrated = true;
70
105
  }),
71
- setHydrated: () => set((state) => {
72
- state.isHydrated = true;
73
- }),
74
106
  setFormElement: (formElement) => {
75
107
  // This gets called frequently, so we want to avoid calling set() every time
76
108
  // Or else we wind up with an infinite loop
@@ -88,7 +120,7 @@ const createFormState = (formId, set, get) => ({
88
120
  invariant(formElement, "Cannot find reference to form. This is probably a bug in remix-validated-form.");
89
121
  const validator = (_a = get().formProps) === null || _a === void 0 ? void 0 : _a.validator;
90
122
  invariant(validator, "Cannot validator. This is probably a bug in remix-validated-form.");
91
- await ((_c = (_b = useControlledFieldStore.getState()).awaitValueUpdate) === null || _c === void 0 ? void 0 : _c.call(_b, formId, field));
123
+ await ((_c = (_b = get().controlledFields).awaitValueUpdate) === null || _c === void 0 ? void 0 : _c.call(_b, field));
92
124
  const { error } = await validator.validateField(new FormData(formElement), field);
93
125
  if (error) {
94
126
  get().setFieldError(field, error);
@@ -113,14 +145,145 @@ const createFormState = (formId, set, get) => ({
113
145
  submit: () => {
114
146
  const formElement = get().formElement;
115
147
  invariant(formElement, "Cannot find reference to form. This is probably a bug in remix-validated-form.");
116
- formElement.submit();
117
- },
118
- getValues: () => {
119
- const formElement = get().formElement;
120
- invariant(formElement, "Cannot find reference to form. This is probably a bug in remix-validated-form.");
121
- return new FormData(formElement);
148
+ formElement.requestSubmit();
122
149
  },
150
+ getValues: () => { var _a; return new FormData((_a = get().formElement) !== null && _a !== void 0 ? _a : undefined); },
123
151
  resetFormElement: () => { var _a; return (_a = get().formElement) === null || _a === void 0 ? void 0 : _a.reset(); },
152
+ controlledFields: {
153
+ values: {},
154
+ refCounts: {},
155
+ valueUpdatePromises: {},
156
+ valueUpdateResolvers: {},
157
+ register: (fieldName) => {
158
+ set((state) => {
159
+ var _a;
160
+ const current = (_a = state.controlledFields.refCounts[fieldName]) !== null && _a !== void 0 ? _a : 0;
161
+ state.controlledFields.refCounts[fieldName] = current + 1;
162
+ });
163
+ },
164
+ unregister: (fieldName) => {
165
+ // For this helper in particular, we may run into a case where state is undefined.
166
+ // When the whole form unmounts, the form state may be cleaned up before the fields are.
167
+ if (get() === null || get() === undefined)
168
+ return;
169
+ set((state) => {
170
+ var _a, _b, _c;
171
+ const current = (_a = state.controlledFields.refCounts[fieldName]) !== null && _a !== void 0 ? _a : 0;
172
+ if (current > 1) {
173
+ state.controlledFields.refCounts[fieldName] = current - 1;
174
+ return;
175
+ }
176
+ const isNested = Object.keys(state.controlledFields.refCounts).some((key) => fieldName.startsWith(key) && key !== fieldName);
177
+ // When nested within a field array, we should leave resetting up to the field array
178
+ if (!isNested) {
179
+ lodashSet(state.controlledFields.values, fieldName, lodashGet((_b = state.formProps) === null || _b === void 0 ? void 0 : _b.defaultValues, fieldName));
180
+ lodashSet(state.currentDefaultValues, fieldName, lodashGet((_c = state.formProps) === null || _c === void 0 ? void 0 : _c.defaultValues, fieldName));
181
+ }
182
+ delete state.controlledFields.refCounts[fieldName];
183
+ });
184
+ },
185
+ getValue: (fieldName) => lodashGet(get().controlledFields.values, fieldName),
186
+ setValue: (fieldName, value) => {
187
+ set((state) => {
188
+ lodashSet(state.controlledFields.values, fieldName, value);
189
+ });
190
+ get().controlledFields.kickoffValueUpdate(fieldName);
191
+ },
192
+ kickoffValueUpdate: (fieldName) => {
193
+ const clear = () => set((state) => {
194
+ delete state.controlledFields.valueUpdateResolvers[fieldName];
195
+ delete state.controlledFields.valueUpdatePromises[fieldName];
196
+ });
197
+ set((state) => {
198
+ const promise = new Promise((resolve) => {
199
+ state.controlledFields.valueUpdateResolvers[fieldName] = resolve;
200
+ }).then(clear);
201
+ state.controlledFields.valueUpdatePromises[fieldName] = promise;
202
+ });
203
+ },
204
+ awaitValueUpdate: async (fieldName) => {
205
+ await get().controlledFields.valueUpdatePromises[fieldName];
206
+ },
207
+ array: {
208
+ push: (fieldName, item) => {
209
+ set((state) => {
210
+ arrayUtil
211
+ .getArray(state.controlledFields.values, fieldName)
212
+ .push(item);
213
+ arrayUtil.getArray(state.currentDefaultValues, fieldName).push(item);
214
+ // New item added to the end, no need to update touched or error
215
+ });
216
+ get().controlledFields.kickoffValueUpdate(fieldName);
217
+ },
218
+ swap: (fieldName, indexA, indexB) => {
219
+ set((state) => {
220
+ arrayUtil.swap(arrayUtil.getArray(state.controlledFields.values, fieldName), indexA, indexB);
221
+ arrayUtil.swap(arrayUtil.getArray(state.currentDefaultValues, fieldName), indexA, indexB);
222
+ arrayUtil.mutateAsArray(fieldName, state.touchedFields, (array) => arrayUtil.swap(array, indexA, indexB));
223
+ arrayUtil.mutateAsArray(fieldName, state.fieldErrors, (array) => arrayUtil.swap(array, indexA, indexB));
224
+ });
225
+ get().controlledFields.kickoffValueUpdate(fieldName);
226
+ },
227
+ move: (fieldName, from, to) => {
228
+ set((state) => {
229
+ arrayUtil.move(arrayUtil.getArray(state.controlledFields.values, fieldName), from, to);
230
+ arrayUtil.move(arrayUtil.getArray(state.currentDefaultValues, fieldName), from, to);
231
+ arrayUtil.mutateAsArray(fieldName, state.touchedFields, (array) => arrayUtil.move(array, from, to));
232
+ arrayUtil.mutateAsArray(fieldName, state.fieldErrors, (array) => arrayUtil.move(array, from, to));
233
+ });
234
+ get().controlledFields.kickoffValueUpdate(fieldName);
235
+ },
236
+ insert: (fieldName, index, item) => {
237
+ set((state) => {
238
+ arrayUtil.insert(arrayUtil.getArray(state.controlledFields.values, fieldName), index, item);
239
+ arrayUtil.insert(arrayUtil.getArray(state.currentDefaultValues, fieldName), index, item);
240
+ // Even though this is a new item, we need to push around other items.
241
+ arrayUtil.mutateAsArray(fieldName, state.touchedFields, (array) => arrayUtil.insert(array, index, false));
242
+ arrayUtil.mutateAsArray(fieldName, state.fieldErrors, (array) => arrayUtil.insert(array, index, undefined));
243
+ });
244
+ get().controlledFields.kickoffValueUpdate(fieldName);
245
+ },
246
+ remove: (fieldName, index) => {
247
+ set((state) => {
248
+ arrayUtil.remove(arrayUtil.getArray(state.controlledFields.values, fieldName), index);
249
+ arrayUtil.remove(arrayUtil.getArray(state.currentDefaultValues, fieldName), index);
250
+ arrayUtil.mutateAsArray(fieldName, state.touchedFields, (array) => arrayUtil.remove(array, index));
251
+ arrayUtil.mutateAsArray(fieldName, state.fieldErrors, (array) => arrayUtil.remove(array, index));
252
+ });
253
+ get().controlledFields.kickoffValueUpdate(fieldName);
254
+ },
255
+ pop: (fieldName) => {
256
+ set((state) => {
257
+ arrayUtil.getArray(state.controlledFields.values, fieldName).pop();
258
+ arrayUtil.getArray(state.currentDefaultValues, fieldName).pop();
259
+ arrayUtil.mutateAsArray(fieldName, state.touchedFields, (array) => array.pop());
260
+ arrayUtil.mutateAsArray(fieldName, state.fieldErrors, (array) => array.pop());
261
+ });
262
+ get().controlledFields.kickoffValueUpdate(fieldName);
263
+ },
264
+ unshift: (fieldName, value) => {
265
+ set((state) => {
266
+ arrayUtil
267
+ .getArray(state.controlledFields.values, fieldName)
268
+ .unshift(value);
269
+ arrayUtil
270
+ .getArray(state.currentDefaultValues, fieldName)
271
+ .unshift(value);
272
+ arrayUtil.mutateAsArray(fieldName, state.touchedFields, (array) => array.unshift(false));
273
+ arrayUtil.mutateAsArray(fieldName, state.fieldErrors, (array) => array.unshift(undefined));
274
+ });
275
+ },
276
+ replace: (fieldName, index, item) => {
277
+ set((state) => {
278
+ arrayUtil.replace(arrayUtil.getArray(state.controlledFields.values, fieldName), index, item);
279
+ arrayUtil.replace(arrayUtil.getArray(state.currentDefaultValues, fieldName), index, item);
280
+ arrayUtil.mutateAsArray(fieldName, state.touchedFields, (array) => arrayUtil.replace(array, index, item));
281
+ arrayUtil.mutateAsArray(fieldName, state.fieldErrors, (array) => arrayUtil.replace(array, index, item));
282
+ });
283
+ get().controlledFields.kickoffValueUpdate(fieldName);
284
+ },
285
+ },
286
+ },
124
287
  });
125
288
  export const useRootFormStore = create()(immer((set, get) => ({
126
289
  forms: {},
@@ -137,7 +300,7 @@ export const useRootFormStore = create()(immer((set, get) => ({
137
300
  if (get().forms[formId])
138
301
  return;
139
302
  set((state) => {
140
- state.forms[formId] = createFormState(formId, (setter) => set((state) => setter(state.forms[formId])), () => get().forms[formId]);
303
+ state.forms[formId] = createFormState((setter) => set((state) => setter(state.forms[formId])), () => get().forms[formId]);
141
304
  });
142
305
  },
143
306
  })));
@@ -1,5 +1,5 @@
1
1
  import { FORM_DEFAULTS_FIELD } from "./internal/constants";
2
- import { ValidatorError } from "./validation/types";
2
+ import { ValidatorError, ValidationErrorResponseData } from "./validation/types";
3
3
  /**
4
4
  * Takes the errors from a `Validator` and returns a `Response`.
5
5
  * When you return this from your action, `ValidatedForm` on the frontend will automatically
@@ -14,7 +14,7 @@ import { ValidatorError } from "./validation/types";
14
14
  * if (result.error) return validationError(result.error, result.submittedData);
15
15
  * ```
16
16
  */
17
- export declare function validationError(error: ValidatorError, repopulateFields?: unknown, init?: ResponseInit): Response;
17
+ export declare function validationError(error: ValidatorError, repopulateFields?: unknown, init?: ResponseInit): import("@remix-run/server-runtime").TypedResponse<ValidationErrorResponseData>;
18
18
  export declare type FormDefaults = {
19
19
  [formDefaultsKey: `${typeof FORM_DEFAULTS_FIELD}_${string}`]: any;
20
20
  };
package/browser/server.js CHANGED
@@ -1,4 +1,4 @@
1
- import { json } from "@remix-run/server-runtime";
1
+ import { json } from "remix";
2
2
  import { formDefaultValuesKey, } from "./internal/constants";
3
3
  /**
4
4
  * Takes the errors from a `Validator` and returns a `Response`.