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.
- 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