remix-validated-form 4.5.2 → 4.6.0-beta.0
Sign up to get free protection for your applications and to get access to all the features.
- package/.turbo/turbo-build.log +8 -9
- package/browser/ValidatedForm.js +0 -3
- package/browser/index.d.ts +1 -0
- package/browser/index.js +1 -0
- package/browser/internal/hooks.d.ts +1 -0
- package/browser/internal/hooks.js +3 -4
- package/browser/internal/state/controlledFields.d.ts +1 -0
- package/browser/internal/state/controlledFields.js +17 -29
- package/browser/internal/state/createFormStore.d.ts +31 -1
- package/browser/internal/state/createFormStore.js +177 -14
- package/browser/server.d.ts +2 -2
- package/browser/server.js +1 -1
- package/dist/remix-validated-form.cjs.js +12 -3
- package/dist/remix-validated-form.cjs.js.map +1 -1
- package/dist/remix-validated-form.es.js +361 -131
- package/dist/remix-validated-form.es.js.map +1 -1
- package/dist/remix-validated-form.umd.js +12 -3
- package/dist/remix-validated-form.umd.js.map +1 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/internal/hooks.d.ts +1 -0
- package/dist/types/internal/state/arrayUtil.d.ts +12 -0
- package/dist/types/internal/state/controlledFields.d.ts +1 -0
- package/dist/types/internal/state/createFormStore.d.ts +31 -1
- package/dist/types/internal/state/fieldArray.d.ts +28 -0
- package/dist/types/server.d.ts +2 -2
- package/package.json +1 -3
- package/src/ValidatedForm.tsx +0 -3
- package/src/index.ts +6 -0
- package/src/internal/hooks.ts +9 -4
- package/src/internal/logic/nestedObjectToPathObject.ts +63 -0
- package/src/internal/state/arrayUtil.ts +399 -0
- package/src/internal/state/controlledFields.ts +39 -43
- package/src/internal/state/createFormStore.ts +288 -20
- package/src/internal/state/fieldArray.tsx +155 -0
- package/src/server.ts +1 -1
- package/vite.config.ts +1 -1
- package/dist/types/internal/state/controlledFieldStore.d.ts +0 -26
- package/src/internal/state/controlledFieldStore.ts +0 -112
package/.turbo/turbo-build.log
CHANGED
@@ -1,18 +1,17 @@
|
|
1
1
|
[2K[1G[2m$ vite build[22m
|
2
2
|
[36mvite v2.9.5 [32mbuilding for production...[36m[39m
|
3
3
|
transforming...
|
4
|
-
[32m✓[39m
|
4
|
+
[32m✓[39m 483 modules transformed.
|
5
5
|
rendering chunks...
|
6
|
-
[90m[37m[2mdist/[22m[90m[39m[36mremix-validated-form.cjs.js [39m [
|
7
|
-
[90m[37m[2mdist/[22m[90m[39m[90mremix-validated-form.cjs.js.map[39m [
|
8
|
-
[90m[37m[2mdist/[22m[90m[39m[36mremix-validated-form.es.js [39m [
|
9
|
-
[90m[37m[2mdist/[22m[90m[39m[90mremix-validated-form.es.js.map[39m [
|
10
|
-
[90m[37m[2mdist/[22m[90m[39m[36mremix-validated-form.umd.js [39m [
|
11
|
-
[90m[37m[2mdist/[22m[90m[39m[90mremix-validated-form.umd.js.map[39m [
|
6
|
+
[90m[37m[2mdist/[22m[90m[39m[36mremix-validated-form.cjs.js [39m [2m49.71 KiB / gzip: 18.12 KiB[22m
|
7
|
+
[90m[37m[2mdist/[22m[90m[39m[90mremix-validated-form.cjs.js.map[39m [2m281.88 KiB[22m
|
8
|
+
[90m[37m[2mdist/[22m[90m[39m[36mremix-validated-form.es.js [39m [2m109.95 KiB / gzip: 25.38 KiB[22m
|
9
|
+
[90m[37m[2mdist/[22m[90m[39m[90mremix-validated-form.es.js.map[39m [2m290.49 KiB[22m
|
10
|
+
[90m[37m[2mdist/[22m[90m[39m[36mremix-validated-form.umd.js [39m [2m49.91 KiB / gzip: 18.22 KiB[22m
|
11
|
+
[90m[37m[2mdist/[22m[90m[39m[90mremix-validated-form.umd.js.map[39m [2m281.85 KiB[22m
|
12
12
|
[32m[39m
|
13
13
|
[32m[36m[vite:dts][39m[32m Start generate declaration files...[39m
|
14
|
-
[32m[36m[vite:dts][39m[32m Declaration files built in
|
14
|
+
[32m[36m[vite:dts][39m[32m Declaration files built in 2756ms.[39m
|
15
15
|
[32m[39m
|
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'
|
package/browser/ValidatedForm.js
CHANGED
@@ -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
|
}
|
package/browser/index.d.ts
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, type FieldArrayProps, type FieldArrayHelpers, } from "./internal/state/fieldArray";
|
package/browser/index.js
CHANGED
@@ -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 =
|
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 =
|
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
|
-
|
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
|
26
|
-
const resolveUpdate =
|
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 =
|
31
|
-
const 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(
|
34
|
-
return () => unregister(
|
18
|
+
register(field);
|
19
|
+
return () => unregister(field);
|
35
20
|
}, [context.formId, field, register, unregister]);
|
36
|
-
|
37
|
-
|
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 =
|
43
|
-
return useCallback((field, value) => 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 =
|
47
|
-
return useCallback((field) => awaitValue(
|
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
|
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 = (
|
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 =
|
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.
|
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(
|
303
|
+
state.forms[formId] = createFormState((setter) => set((state) => setter(state.forms[formId])), () => get().forms[formId]);
|
141
304
|
});
|
142
305
|
},
|
143
306
|
})));
|
package/browser/server.d.ts
CHANGED
@@ -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):
|
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