remix-validated-form 4.4.3 → 4.5.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 +13 -10
- package/.turbo/turbo-dev.log +0 -95
- package/browser/ValidatedForm.js +17 -10
- package/browser/hooks.d.ts +1 -1
- package/browser/internal/hooks.d.ts +4 -2
- package/browser/internal/state/controlledFieldStore.d.ts +23 -21
- package/browser/internal/state/controlledFieldStore.js +32 -19
- package/browser/internal/state/controlledFields.d.ts +3 -3
- package/browser/internal/state/controlledFields.js +19 -21
- package/browser/internal/state/createFormStore.d.ts +13 -6
- package/browser/internal/state/createFormStore.js +48 -5
- package/browser/internal/state/storeHooks.d.ts +1 -3
- package/browser/internal/state/storeHooks.js +2 -8
- package/browser/internal/state/types.d.ts +1 -0
- package/browser/internal/state/types.js +1 -0
- package/dist/remix-validated-form.cjs.js +3 -3
- package/dist/remix-validated-form.cjs.js.map +1 -1
- package/dist/remix-validated-form.es.js +121 -79
- package/dist/remix-validated-form.es.js.map +1 -1
- package/dist/remix-validated-form.umd.js +3 -3
- package/dist/remix-validated-form.umd.js.map +1 -1
- package/dist/types/hooks.d.ts +1 -1
- package/dist/types/internal/hooks.d.ts +1 -1
- package/dist/types/internal/state/controlledFieldStore.d.ts +23 -21
- package/dist/types/internal/state/controlledFields.d.ts +3 -3
- package/dist/types/internal/state/createFormStore.d.ts +13 -6
- package/dist/types/internal/state/storeHooks.d.ts +1 -3
- package/dist/types/internal/state/types.d.ts +1 -0
- package/package.json +4 -4
- package/src/ValidatedForm.tsx +21 -17
- package/src/internal/hooks.ts +1 -1
- package/src/internal/state/controlledFieldStore.ts +95 -74
- package/src/internal/state/controlledFields.ts +38 -26
- package/src/internal/state/createFormStore.ts +174 -113
- package/src/internal/state/storeHooks.ts +3 -16
- package/src/internal/state/types.ts +1 -0
- package/dist/types/internal/state/cleanup.d.ts +0 -2
- package/dist/types/internal/state/storeFamily.d.ts +0 -9
- package/src/internal/state/cleanup.ts +0 -8
- package/src/internal/state/storeFamily.ts +0 -24
package/.turbo/turbo-build.log
CHANGED
@@ -1,15 +1,18 @@
|
|
1
|
-
$ vite build
|
2
|
-
|
1
|
+
[2K[1G[2m$ vite build[22m
|
2
|
+
[36mvite v2.9.5 [32mbuilding for production...[36m[39m
|
3
3
|
transforming...
|
4
|
-
|
4
|
+
[32m✓[39m 319 modules transformed.
|
5
5
|
rendering chunks...
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
[
|
11
|
-
[
|
12
|
-
|
6
|
+
[90m[37m[2mdist/[22m[90m[39m[36mremix-validated-form.cjs.js [39m [2m45.13 KiB / gzip: 17.03 KiB[22m
|
7
|
+
[90m[37m[2mdist/[22m[90m[39m[90mremix-validated-form.cjs.js.map[39m [2m250.93 KiB[22m
|
8
|
+
[90m[37m[2mdist/[22m[90m[39m[36mremix-validated-form.es.js [39m [2m100.56 KiB / gzip: 23.71 KiB[22m
|
9
|
+
[90m[37m[2mdist/[22m[90m[39m[90mremix-validated-form.es.js.map[39m [2m258.79 KiB[22m
|
10
|
+
[90m[37m[2mdist/[22m[90m[39m[36mremix-validated-form.umd.js [39m [2m45.38 KiB / gzip: 17.15 KiB[22m
|
11
|
+
[90m[37m[2mdist/[22m[90m[39m[90mremix-validated-form.umd.js.map[39m [2m250.91 KiB[22m
|
12
|
+
[32m[39m
|
13
|
+
[32m[36m[vite:dts][39m[32m Start generate declaration files...[39m
|
14
|
+
[32m[36m[vite:dts][39m[32m Declaration files built in 1725ms.[39m
|
15
|
+
[32m[39m
|
13
16
|
No name was provided for external module 'react' in output.globals – guessing 'React'
|
14
17
|
No name was provided for external module '@remix-run/react' in output.globals – guessing 'react'
|
15
18
|
No name was provided for external module '@remix-run/server-runtime' in output.globals – guessing 'serverRuntime'
|
package/.turbo/turbo-dev.log
CHANGED
@@ -1,95 +0,0 @@
|
|
1
|
-
$ tsc --module ESNext --outDir ./browser --watch
|
2
|
-
c10:51:22 PM - Starting compilation in watch mode...
|
3
|
-
|
4
|
-
|
5
|
-
10:51:23 PM - Found 0 errors. Watching for file changes.
|
6
|
-
c10:58:01 PM - File change detected. Starting incremental compilation...
|
7
|
-
|
8
|
-
src/internal/hooks.ts(149,23): error TS2304: Cannot find name 'formId'.
|
9
|
-
src/internal/hooks.ts(151,41): error TS2304: Cannot find name 'defaultDefaultValues'.
|
10
|
-
src/unreleased/formStateHooks.ts(18,3): error TS2724: '"../internal/hooks"' has no exported member named 'useSyncedDefaultValues'. Did you mean 'useFieldDefaultValue'?
|
11
|
-
|
12
|
-
10:58:02 PM - Found 3 errors. Watching for file changes.
|
13
|
-
c10:58:40 PM - File change detected. Starting incremental compilation...
|
14
|
-
|
15
|
-
src/internal/hooks.ts(156,23): error TS2304: Cannot find name 'formId'.
|
16
|
-
|
17
|
-
10:58:40 PM - Found 1 error. Watching for file changes.
|
18
|
-
c10:59:28 PM - File change detected. Starting incremental compilation...
|
19
|
-
|
20
|
-
|
21
|
-
10:59:28 PM - Found 0 errors. Watching for file changes.
|
22
|
-
c11:00:16 PM - File change detected. Starting incremental compilation...
|
23
|
-
|
24
|
-
src/internal/hooks.ts(160,4): error TS1005: ',' expected.
|
25
|
-
src/internal/hooks.ts(161,1): error TS1128: Declaration or statement expected.
|
26
|
-
|
27
|
-
11:00:16 PM - Found 2 errors. Watching for file changes.
|
28
|
-
c11:01:07 PM - File change detected. Starting incremental compilation...
|
29
|
-
|
30
|
-
|
31
|
-
11:01:07 PM - Found 0 errors. Watching for file changes.
|
32
|
-
c11:01:30 PM - File change detected. Starting incremental compilation...
|
33
|
-
|
34
|
-
|
35
|
-
11:01:30 PM - Found 0 errors. Watching for file changes.
|
36
|
-
c11:01:50 PM - File change detected. Starting incremental compilation...
|
37
|
-
|
38
|
-
|
39
|
-
11:01:50 PM - Found 0 errors. Watching for file changes.
|
40
|
-
c11:02:06 PM - File change detected. Starting incremental compilation...
|
41
|
-
|
42
|
-
|
43
|
-
11:02:06 PM - Found 0 errors. Watching for file changes.
|
44
|
-
c11:04:31 PM - File change detected. Starting incremental compilation...
|
45
|
-
|
46
|
-
|
47
|
-
11:04:31 PM - Found 0 errors. Watching for file changes.
|
48
|
-
c11:05:28 PM - File change detected. Starting incremental compilation...
|
49
|
-
|
50
|
-
|
51
|
-
11:05:28 PM - Found 0 errors. Watching for file changes.
|
52
|
-
c11:05:52 PM - File change detected. Starting incremental compilation...
|
53
|
-
|
54
|
-
|
55
|
-
11:05:52 PM - Found 0 errors. Watching for file changes.
|
56
|
-
c11:06:11 PM - File change detected. Starting incremental compilation...
|
57
|
-
|
58
|
-
|
59
|
-
11:06:11 PM - Found 0 errors. Watching for file changes.
|
60
|
-
c11:06:33 PM - File change detected. Starting incremental compilation...
|
61
|
-
|
62
|
-
|
63
|
-
11:06:33 PM - Found 0 errors. Watching for file changes.
|
64
|
-
c11:06:48 PM - File change detected. Starting incremental compilation...
|
65
|
-
|
66
|
-
|
67
|
-
11:06:48 PM - Found 0 errors. Watching for file changes.
|
68
|
-
c11:08:57 PM - File change detected. Starting incremental compilation...
|
69
|
-
|
70
|
-
src/internal/hooks.ts(114,25): error TS2304: Cannot find name 'useCorrectDefaultValues'.
|
71
|
-
src/unreleased/formStateHooks.ts(19,3): error TS2305: Module '"../internal/hooks"' has no exported member 'useCorrectDefaultValues'.
|
72
|
-
|
73
|
-
11:08:57 PM - Found 2 errors. Watching for file changes.
|
74
|
-
c11:09:06 PM - File change detected. Starting incremental compilation...
|
75
|
-
|
76
|
-
src/unreleased/formStateHooks.ts(19,3): error TS2305: Module '"../internal/hooks"' has no exported member 'useCorrectDefaultValues'.
|
77
|
-
|
78
|
-
11:09:06 PM - Found 1 error. Watching for file changes.
|
79
|
-
c11:09:19 PM - File change detected. Starting incremental compilation...
|
80
|
-
|
81
|
-
src/unreleased/formStateHooks.ts(48,49): error TS2345: Argument of type 'string | symbol' is not assignable to parameter of type 'InternalFormContextValue'.
|
82
|
-
Type 'string' is not assignable to type 'InternalFormContextValue'.
|
83
|
-
|
84
|
-
11:09:19 PM - Found 1 error. Watching for file changes.
|
85
|
-
c11:09:34 PM - File change detected. Starting incremental compilation...
|
86
|
-
|
87
|
-
src/unreleased/formStateHooks.ts(48,49): error TS2345: Argument of type 'string | symbol' is not assignable to parameter of type 'InternalFormContextValue'.
|
88
|
-
Type 'string' is not assignable to type 'InternalFormContextValue'.
|
89
|
-
|
90
|
-
11:09:34 PM - Found 1 error. Watching for file changes.
|
91
|
-
c11:09:38 PM - File change detected. Starting incremental compilation...
|
92
|
-
|
93
|
-
|
94
|
-
11:09:38 PM - Found 0 errors. Watching for file changes.
|
95
|
-
c11:11:35 PM - File change detected. Starting incremental compilat
|
package/browser/ValidatedForm.js
CHANGED
@@ -7,8 +7,9 @@ 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 {
|
11
|
-
import {
|
10
|
+
import { useControlledFieldStore } from "./internal/state/controlledFieldStore";
|
11
|
+
import { useRootFormStore, } from "./internal/state/createFormStore";
|
12
|
+
import { useFormStore } from "./internal/state/storeHooks";
|
12
13
|
import { useSubmitComplete } from "./internal/submissionCallbacks";
|
13
14
|
import { mergeRefs, useDeepEqualsMemo, useIsomorphicLayoutEffect as useLayoutEffect, } from "./internal/util";
|
14
15
|
const getDataFromForm = (el) => new FormData(el);
|
@@ -114,16 +115,13 @@ export function ValidatedForm({ validator, onSubmit, children, fetcher, action,
|
|
114
115
|
const setFieldErrors = useSetFieldErrors(formId);
|
115
116
|
const setFieldError = useFormStore(formId, (state) => state.setFieldError);
|
116
117
|
const reset = useFormStore(formId, (state) => state.reset);
|
117
|
-
const resetControlledFields = useControlledFieldStore(
|
118
|
+
const resetControlledFields = useControlledFieldStore((state) => state.reset);
|
118
119
|
const startSubmit = useFormStore(formId, (state) => state.startSubmit);
|
119
120
|
const endSubmit = useFormStore(formId, (state) => state.endSubmit);
|
120
121
|
const syncFormProps = useFormStore(formId, (state) => state.syncFormProps);
|
121
|
-
const setHydrated = useFormStore(formId, (state) => state.setHydrated);
|
122
122
|
const setFormElementInState = useFormStore(formId, (state) => state.setFormElement);
|
123
|
-
|
124
|
-
|
125
|
-
return () => cleanupFormState(formId);
|
126
|
-
}, [formId, setHydrated]);
|
123
|
+
const cleanupForm = useRootFormStore((state) => state.cleanupForm);
|
124
|
+
const registerForm = useRootFormStore((state) => state.registerForm);
|
127
125
|
const customFocusHandlers = useMultiValueMap();
|
128
126
|
const registerReceiveFocus = useCallback((fieldName, handler) => {
|
129
127
|
customFocusHandlers().add(fieldName, handler);
|
@@ -131,6 +129,12 @@ export function ValidatedForm({ validator, onSubmit, children, fetcher, action,
|
|
131
129
|
customFocusHandlers().remove(fieldName, handler);
|
132
130
|
};
|
133
131
|
}, [customFocusHandlers]);
|
132
|
+
// TODO: all these hooks running at startup cause extra, unnecessary renders
|
133
|
+
// There must be a nice way to avoid this.
|
134
|
+
useLayoutEffect(() => {
|
135
|
+
registerForm(formId);
|
136
|
+
return () => cleanupForm(formId);
|
137
|
+
}, [cleanupForm, formId, registerForm]);
|
134
138
|
useLayoutEffect(() => {
|
135
139
|
var _a;
|
136
140
|
syncFormProps({
|
@@ -149,6 +153,9 @@ export function ValidatedForm({ validator, onSubmit, children, fetcher, action,
|
|
149
153
|
backendDefaultValues,
|
150
154
|
validator,
|
151
155
|
]);
|
156
|
+
useLayoutEffect(() => {
|
157
|
+
setFormElementInState(formRef.current);
|
158
|
+
}, [setFormElementInState]);
|
152
159
|
useEffect(() => {
|
153
160
|
var _a;
|
154
161
|
setFieldErrors((_a = backendError === null || backendError === void 0 ? void 0 : backendError.fieldErrors) !== null && _a !== void 0 ? _a : {});
|
@@ -207,7 +214,7 @@ export function ValidatedForm({ validator, onSubmit, children, fetcher, action,
|
|
207
214
|
clickedButtonRef.current = null;
|
208
215
|
}
|
209
216
|
};
|
210
|
-
return (_jsx(Form, { ref: mergeRefs([formRef, formRefProp
|
217
|
+
return (_jsx(Form, { ref: mergeRefs([formRef, formRefProp]), ...rest, id: id, action: action, method: method, replace: replace, onSubmit: (e) => {
|
211
218
|
e.preventDefault();
|
212
219
|
handleSubmit(e);
|
213
220
|
}, onReset: (event) => {
|
@@ -215,6 +222,6 @@ export function ValidatedForm({ validator, onSubmit, children, fetcher, action,
|
|
215
222
|
if (event.defaultPrevented)
|
216
223
|
return;
|
217
224
|
reset();
|
218
|
-
resetControlledFields();
|
225
|
+
resetControlledFields(formId);
|
219
226
|
}, children: _jsxs(InternalFormContext.Provider, { value: contextValue, 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));
|
220
227
|
}
|
package/browser/hooks.d.ts
CHANGED
@@ -64,4 +64,4 @@ export declare const useField: (name: string, options?: {
|
|
64
64
|
formId?: string | undefined;
|
65
65
|
} | undefined) => FieldProps;
|
66
66
|
export declare const useControlField: <T>(name: string, formId?: string | undefined) => readonly [T, (value: T) => void];
|
67
|
-
export declare const useUpdateControlledField: (formId?: string | undefined) => (
|
67
|
+
export declare const useUpdateControlledField: (formId?: string | undefined) => (field: string, value: unknown) => void;
|
@@ -1,7 +1,7 @@
|
|
1
1
|
import { FieldErrors, ValidationErrorResponseData } from "..";
|
2
2
|
import { InternalFormContextValue } from "./formContext";
|
3
3
|
import { Hydratable } from "./hydratable";
|
4
|
-
import { InternalFormId } from "./state/
|
4
|
+
import { InternalFormId } from "./state/types";
|
5
5
|
export declare const useInternalFormContext: (formId?: string | symbol | undefined, hookName?: string | undefined) => InternalFormContextValue;
|
6
6
|
export declare function useErrorResponseForForm({ fetcher, subaction, formId, }: InternalFormContextValue): ValidationErrorResponseData | null;
|
7
7
|
export declare const useFieldErrorsForForm: (context: InternalFormContextValue) => Hydratable<FieldErrors | undefined>;
|
@@ -20,7 +20,9 @@ export declare const useInternalHasBeenSubmitted: (formId: InternalFormId) => bo
|
|
20
20
|
export declare const useValidateField: (formId: InternalFormId) => (fieldName: string) => Promise<string | null>;
|
21
21
|
export declare const useValidate: (formId: InternalFormId) => () => Promise<void>;
|
22
22
|
export declare const useRegisterReceiveFocus: (formId: InternalFormId) => (fieldName: string, handler: () => void) => () => void;
|
23
|
-
export declare const useSyncedDefaultValues: (formId: InternalFormId) => {
|
23
|
+
export declare const useSyncedDefaultValues: (formId: InternalFormId) => {
|
24
|
+
[fieldName: string]: any;
|
25
|
+
};
|
24
26
|
export declare const useSetTouched: ({ formId }: InternalFormContextValue) => (field: string, touched: boolean) => void;
|
25
27
|
export declare const useTouchedFields: (formId: InternalFormId) => import("..").TouchedFields;
|
26
28
|
export declare const useFieldErrors: (formId: InternalFormId) => FieldErrors;
|
@@ -1,24 +1,26 @@
|
|
1
|
+
import { InternalFormId } from "./types";
|
2
|
+
export declare type FieldState = {
|
3
|
+
refCount: number;
|
4
|
+
value: unknown;
|
5
|
+
defaultValue?: unknown;
|
6
|
+
hydrated: boolean;
|
7
|
+
valueUpdatePromise: Promise<void> | undefined;
|
8
|
+
resolveValueUpdate: (() => void) | undefined;
|
9
|
+
};
|
1
10
|
export declare type ControlledFieldState = {
|
2
|
-
|
3
|
-
[
|
4
|
-
|
5
|
-
|
6
|
-
defaultValue?: unknown;
|
7
|
-
hydrated: boolean;
|
8
|
-
valueUpdatePromise: Promise<void> | undefined;
|
9
|
-
resolveValueUpdate: (() => void) | undefined;
|
10
|
-
} | undefined;
|
11
|
+
forms: {
|
12
|
+
[formId: InternalFormId]: {
|
13
|
+
[fieldName: string]: FieldState | undefined;
|
14
|
+
};
|
11
15
|
};
|
12
|
-
register: (fieldName: string) => void;
|
13
|
-
unregister: (fieldName: string) => void;
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
export declare const controlledFieldStore: {
|
20
|
-
(formId: import("./storeFamily").InternalFormId): import("zustand").UseBoundStore<Omit<import("zustand").StoreApi<ControlledFieldState>, "setState"> & {
|
21
|
-
setState(nextStateOrUpdater: ControlledFieldState | Partial<ControlledFieldState> | ((state: import("immer/dist/internal").WritableDraft<ControlledFieldState>) => void), shouldReplace?: boolean | undefined): void;
|
22
|
-
}>;
|
23
|
-
remove(formId: import("./storeFamily").InternalFormId): void;
|
16
|
+
register: (formId: InternalFormId, fieldName: string) => void;
|
17
|
+
unregister: (formId: InternalFormId, fieldName: string) => void;
|
18
|
+
getField: (formId: InternalFormId, fieldName: string) => FieldState | undefined;
|
19
|
+
setValue: (formId: InternalFormId, fieldName: string, value: unknown) => void;
|
20
|
+
hydrateWithDefault: (formId: InternalFormId, fieldName: string, defaultValue: unknown) => void;
|
21
|
+
awaitValueUpdate: (formId: InternalFormId, fieldName: string) => Promise<void>;
|
22
|
+
reset: (formId: InternalFormId) => void;
|
24
23
|
};
|
24
|
+
export declare const useControlledFieldStore: import("zustand").UseBoundStore<Omit<import("zustand").StoreApi<ControlledFieldState>, "setState"> & {
|
25
|
+
setState(nextStateOrUpdater: ControlledFieldState | Partial<ControlledFieldState> | ((state: import("immer/dist/internal").WritableDraft<ControlledFieldState>) => void), shouldReplace?: boolean | undefined): void;
|
26
|
+
}>;
|
@@ -1,14 +1,16 @@
|
|
1
1
|
import create from "zustand";
|
2
2
|
import { immer } from "zustand/middleware/immer";
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
3
|
+
export const useControlledFieldStore = create()(immer((set, get) => ({
|
4
|
+
forms: {},
|
5
|
+
register: (formId, field) => set((state) => {
|
6
|
+
if (!state.forms[formId]) {
|
7
|
+
state.forms[formId] = {};
|
8
|
+
}
|
9
|
+
if (state.forms[formId][field]) {
|
10
|
+
state.forms[formId][field].refCount++;
|
9
11
|
}
|
10
12
|
else {
|
11
|
-
state.
|
13
|
+
state.forms[formId][field] = {
|
12
14
|
refCount: 1,
|
13
15
|
value: undefined,
|
14
16
|
hydrated: false,
|
@@ -17,16 +19,23 @@ export const controlledFieldStore = storeFamily(() => create()(immer((set, get,
|
|
17
19
|
};
|
18
20
|
}
|
19
21
|
}),
|
20
|
-
unregister: (field) => set((state) => {
|
21
|
-
|
22
|
+
unregister: (formId, field) => set((state) => {
|
23
|
+
var _a;
|
24
|
+
const formState = (_a = state.forms) === null || _a === void 0 ? void 0 : _a[formId];
|
25
|
+
const fieldState = formState === null || formState === void 0 ? void 0 : formState[field];
|
22
26
|
if (!fieldState)
|
23
27
|
return;
|
24
28
|
fieldState.refCount--;
|
25
29
|
if (fieldState.refCount === 0)
|
26
|
-
delete
|
30
|
+
delete formState[field];
|
27
31
|
}),
|
28
|
-
|
29
|
-
|
32
|
+
getField: (formId, field) => {
|
33
|
+
var _a, _b;
|
34
|
+
return (_b = (_a = get().forms) === null || _a === void 0 ? void 0 : _a[formId]) === null || _b === void 0 ? void 0 : _b[field];
|
35
|
+
},
|
36
|
+
setValue: (formId, field, value) => set((state) => {
|
37
|
+
var _a, _b;
|
38
|
+
const fieldState = (_b = (_a = state.forms) === null || _a === void 0 ? void 0 : _a[formId]) === null || _b === void 0 ? void 0 : _b[field];
|
30
39
|
if (!fieldState)
|
31
40
|
return;
|
32
41
|
fieldState.value = value;
|
@@ -35,23 +44,27 @@ export const controlledFieldStore = storeFamily(() => create()(immer((set, get,
|
|
35
44
|
});
|
36
45
|
fieldState.valueUpdatePromise = promise;
|
37
46
|
}),
|
38
|
-
hydrateWithDefault: (field, defaultValue) => set((state) => {
|
39
|
-
|
47
|
+
hydrateWithDefault: (formId, field, defaultValue) => set((state) => {
|
48
|
+
var _a;
|
49
|
+
const fieldState = (_a = state.forms[formId]) === null || _a === void 0 ? void 0 : _a[field];
|
40
50
|
if (!fieldState)
|
41
51
|
return;
|
42
52
|
fieldState.value = defaultValue;
|
43
53
|
fieldState.defaultValue = defaultValue;
|
44
54
|
fieldState.hydrated = true;
|
45
55
|
}),
|
46
|
-
awaitValueUpdate: async (field) => {
|
56
|
+
awaitValueUpdate: async (formId, field) => {
|
47
57
|
var _a;
|
48
|
-
await ((_a = get().
|
58
|
+
await ((_a = get().getField(formId, field)) === null || _a === void 0 ? void 0 : _a.valueUpdatePromise);
|
49
59
|
},
|
50
|
-
reset: () => set((state) => {
|
51
|
-
|
60
|
+
reset: (formId) => set((state) => {
|
61
|
+
const formState = state.forms[formId];
|
62
|
+
if (!formState)
|
63
|
+
return;
|
64
|
+
Object.values(formState).forEach((field) => {
|
52
65
|
if (!field)
|
53
66
|
return;
|
54
67
|
field.value = field.defaultValue;
|
55
68
|
});
|
56
69
|
}),
|
57
|
-
})))
|
70
|
+
})));
|
@@ -1,6 +1,6 @@
|
|
1
1
|
import { InternalFormContextValue } from "../formContext";
|
2
|
-
import { InternalFormId } from "./
|
2
|
+
import { InternalFormId } from "./types";
|
3
3
|
export declare const useControlledFieldValue: (context: InternalFormContextValue, field: string) => any;
|
4
4
|
export declare const useControllableValue: (context: InternalFormContextValue, field: string) => readonly [any, (value: unknown) => void];
|
5
|
-
export declare const useUpdateControllableValue: (formId: InternalFormId) => (
|
6
|
-
export declare const useAwaitValue: (formId: InternalFormId) => (
|
5
|
+
export declare const useUpdateControllableValue: (formId: InternalFormId) => (field: string, value: unknown) => void;
|
6
|
+
export declare const useAwaitValue: (formId: InternalFormId) => (field: string) => Promise<void>;
|
@@ -1,20 +1,19 @@
|
|
1
1
|
import { useCallback, useEffect } from "react";
|
2
2
|
import { useFieldDefaultValue } from "../hooks";
|
3
|
-
import {
|
4
|
-
import {
|
3
|
+
import { useControlledFieldStore } from "./controlledFieldStore";
|
4
|
+
import { useFormStore } from "./storeHooks";
|
5
5
|
export const useControlledFieldValue = (context, field) => {
|
6
|
-
const
|
7
|
-
const
|
8
|
-
const useFormStore = formStore(context.formId);
|
9
|
-
const isFormHydrated = useFormStore((state) => state.isHydrated);
|
6
|
+
const value = useControlledFieldStore((state) => { var _a; return (_a = state.getField(context.formId, field)) === null || _a === void 0 ? void 0 : _a.value; });
|
7
|
+
const isFormHydrated = useFormStore(context.formId, (state) => state.isHydrated);
|
10
8
|
const defaultValue = useFieldDefaultValue(field, context);
|
11
|
-
const isFieldHydrated =
|
12
|
-
const hydrateWithDefault =
|
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);
|
13
11
|
useEffect(() => {
|
14
12
|
if (isFormHydrated && !isFieldHydrated) {
|
15
|
-
hydrateWithDefault(field, defaultValue);
|
13
|
+
hydrateWithDefault(context.formId, field, defaultValue);
|
16
14
|
}
|
17
15
|
}, [
|
16
|
+
context.formId,
|
18
17
|
defaultValue,
|
19
18
|
field,
|
20
19
|
hydrateWithDefault,
|
@@ -24,27 +23,26 @@ export const useControlledFieldValue = (context, field) => {
|
|
24
23
|
return isFieldHydrated ? value : defaultValue;
|
25
24
|
};
|
26
25
|
export const useControllableValue = (context, field) => {
|
27
|
-
const
|
28
|
-
const resolveUpdate = useValueStore((state) => { var _a; return (_a = state.fields[field]) === null || _a === void 0 ? void 0 : _a.resolveValueUpdate; });
|
26
|
+
const resolveUpdate = useControlledFieldStore((state) => { var _a; return (_a = state.getField(context.formId, field)) === null || _a === void 0 ? void 0 : _a.resolveValueUpdate; });
|
29
27
|
useEffect(() => {
|
30
28
|
resolveUpdate === null || resolveUpdate === void 0 ? void 0 : resolveUpdate();
|
31
29
|
}, [resolveUpdate]);
|
32
|
-
const register =
|
33
|
-
const unregister =
|
30
|
+
const register = useControlledFieldStore((state) => state.register);
|
31
|
+
const unregister = useControlledFieldStore((state) => state.unregister);
|
34
32
|
useEffect(() => {
|
35
|
-
register(field);
|
36
|
-
return () => unregister(field);
|
33
|
+
register(context.formId, field);
|
34
|
+
return () => unregister(context.formId, field);
|
37
35
|
}, [context.formId, field, register, unregister]);
|
38
|
-
const setControlledFieldValue =
|
39
|
-
const setValue = useCallback((value) => setControlledFieldValue(field, value), [field, setControlledFieldValue]);
|
36
|
+
const setControlledFieldValue = useControlledFieldStore((state) => state.setValue);
|
37
|
+
const setValue = useCallback((value) => setControlledFieldValue(context.formId, field, value), [context.formId, field, setControlledFieldValue]);
|
40
38
|
const value = useControlledFieldValue(context, field);
|
41
39
|
return [value, setValue];
|
42
40
|
};
|
43
41
|
export const useUpdateControllableValue = (formId) => {
|
44
|
-
const
|
45
|
-
return
|
42
|
+
const setValue = useControlledFieldStore((state) => state.setValue);
|
43
|
+
return useCallback((field, value) => setValue(formId, field, value), [formId, setValue]);
|
46
44
|
};
|
47
45
|
export const useAwaitValue = (formId) => {
|
48
|
-
const
|
49
|
-
return
|
46
|
+
const awaitValue = useControlledFieldStore((state) => state.awaitValueUpdate);
|
47
|
+
return useCallback((field) => awaitValue(formId, field), [awaitValue, formId]);
|
50
48
|
};
|
@@ -1,4 +1,6 @@
|
|
1
|
+
import { WritableDraft } from "immer/dist/internal";
|
1
2
|
import { FieldErrors, TouchedFields, Validator } from "../../validation/types";
|
3
|
+
import { InternalFormId } from "./types";
|
2
4
|
export declare type SyncedFormProps = {
|
3
5
|
formId?: string;
|
4
6
|
action?: string;
|
@@ -9,6 +11,14 @@ export declare type SyncedFormProps = {
|
|
9
11
|
registerReceiveFocus: (fieldName: string, handler: () => void) => () => void;
|
10
12
|
validator: Validator<unknown>;
|
11
13
|
};
|
14
|
+
export declare type FormStoreState = {
|
15
|
+
forms: {
|
16
|
+
[formId: InternalFormId]: FormState;
|
17
|
+
};
|
18
|
+
form: (formId: InternalFormId) => FormState;
|
19
|
+
registerForm: (formId: InternalFormId) => void;
|
20
|
+
cleanupForm: (formId: InternalFormId) => void;
|
21
|
+
};
|
12
22
|
export declare type FormState = {
|
13
23
|
isHydrated: boolean;
|
14
24
|
isSubmitting: boolean;
|
@@ -32,9 +42,6 @@ export declare type FormState = {
|
|
32
42
|
validate: () => Promise<void>;
|
33
43
|
resetFormElement: () => void;
|
34
44
|
};
|
35
|
-
export declare const
|
36
|
-
(
|
37
|
-
|
38
|
-
}>;
|
39
|
-
remove(formId: import("./storeFamily").InternalFormId): void;
|
40
|
-
};
|
45
|
+
export declare const useRootFormStore: import("zustand").UseBoundStore<Omit<import("zustand").StoreApi<FormStoreState>, "setState"> & {
|
46
|
+
setState(nextStateOrUpdater: FormStoreState | Partial<FormStoreState> | ((state: WritableDraft<FormStoreState>) => void), shouldReplace?: boolean | undefined): void;
|
47
|
+
}>;
|
@@ -1,9 +1,32 @@
|
|
1
1
|
import invariant from "tiny-invariant";
|
2
2
|
import create from "zustand";
|
3
3
|
import { immer } from "zustand/middleware/immer";
|
4
|
-
import {
|
5
|
-
|
6
|
-
|
4
|
+
import { useControlledFieldStore } from "./controlledFieldStore";
|
5
|
+
const noOp = () => { };
|
6
|
+
const defaultFormState = {
|
7
|
+
isHydrated: false,
|
8
|
+
isSubmitting: false,
|
9
|
+
hasBeenSubmitted: false,
|
10
|
+
touchedFields: {},
|
11
|
+
fieldErrors: {},
|
12
|
+
formElement: null,
|
13
|
+
isValid: () => true,
|
14
|
+
startSubmit: noOp,
|
15
|
+
endSubmit: noOp,
|
16
|
+
setTouched: noOp,
|
17
|
+
setFieldError: noOp,
|
18
|
+
setFieldErrors: noOp,
|
19
|
+
clearFieldError: noOp,
|
20
|
+
reset: () => noOp,
|
21
|
+
syncFormProps: noOp,
|
22
|
+
setHydrated: noOp,
|
23
|
+
setFormElement: noOp,
|
24
|
+
validateField: async () => null,
|
25
|
+
validate: async () => { },
|
26
|
+
resetFormElement: noOp,
|
27
|
+
};
|
28
|
+
const createFormState = (formId, set, get) => ({
|
29
|
+
// It's not "hydrated" until the form props are synced
|
7
30
|
isHydrated: false,
|
8
31
|
isSubmitting: false,
|
9
32
|
hasBeenSubmitted: false,
|
@@ -37,6 +60,7 @@ export const formStore = storeFamily((formId) => create()(immer((set, get, api)
|
|
37
60
|
}),
|
38
61
|
syncFormProps: (props) => set((state) => {
|
39
62
|
state.formProps = props;
|
63
|
+
state.isHydrated = true;
|
40
64
|
}),
|
41
65
|
setHydrated: () => set((state) => {
|
42
66
|
state.isHydrated = true;
|
@@ -58,7 +82,7 @@ export const formStore = storeFamily((formId) => create()(immer((set, get, api)
|
|
58
82
|
invariant(formElement, "Cannot find reference to form. This is probably a bug in remix-validated-form.");
|
59
83
|
const validator = (_a = get().formProps) === null || _a === void 0 ? void 0 : _a.validator;
|
60
84
|
invariant(validator, "Cannot validator. This is probably a bug in remix-validated-form.");
|
61
|
-
await ((_c = (_b =
|
85
|
+
await ((_c = (_b = useControlledFieldStore.getState()).awaitValueUpdate) === null || _c === void 0 ? void 0 : _c.call(_b, formId, field));
|
62
86
|
const { error } = await validator.validateField(new FormData(formElement), field);
|
63
87
|
if (error) {
|
64
88
|
get().setFieldError(field, error);
|
@@ -80,4 +104,23 @@ export const formStore = storeFamily((formId) => create()(immer((set, get, api)
|
|
80
104
|
get().setFieldErrors(error.fieldErrors);
|
81
105
|
},
|
82
106
|
resetFormElement: () => { var _a; return (_a = get().formElement) === null || _a === void 0 ? void 0 : _a.reset(); },
|
83
|
-
})
|
107
|
+
});
|
108
|
+
export const useRootFormStore = create()(immer((set, get) => ({
|
109
|
+
forms: {},
|
110
|
+
form: (formId) => {
|
111
|
+
var _a;
|
112
|
+
return (_a = get().forms[formId]) !== null && _a !== void 0 ? _a : defaultFormState;
|
113
|
+
},
|
114
|
+
cleanupForm: (formId) => {
|
115
|
+
set((state) => {
|
116
|
+
delete state.forms[formId];
|
117
|
+
});
|
118
|
+
},
|
119
|
+
registerForm: (formId) => {
|
120
|
+
if (get().forms[formId])
|
121
|
+
return;
|
122
|
+
set((state) => {
|
123
|
+
state.forms[formId] = createFormState(formId, (setter) => set((state) => setter(state.forms[formId])), () => get().forms[formId]);
|
124
|
+
});
|
125
|
+
},
|
126
|
+
})));
|
@@ -1,5 +1,3 @@
|
|
1
|
-
import { ControlledFieldState } from "./controlledFieldStore";
|
2
1
|
import { FormState } from "./createFormStore";
|
3
|
-
import { InternalFormId } from "./
|
2
|
+
import { InternalFormId } from "./types";
|
4
3
|
export declare const useFormStore: <T>(formId: InternalFormId, selector: (state: FormState) => T) => T;
|
5
|
-
export declare const useControlledFieldStore: <T>(formId: InternalFormId, selector: (state: ControlledFieldState) => T) => T;
|
@@ -1,10 +1,4 @@
|
|
1
|
-
import {
|
2
|
-
import { formStore } from "./createFormStore";
|
1
|
+
import { useRootFormStore } from "./createFormStore";
|
3
2
|
export const useFormStore = (formId, selector) => {
|
4
|
-
|
5
|
-
return useStore(selector);
|
6
|
-
};
|
7
|
-
export const useControlledFieldStore = (formId, selector) => {
|
8
|
-
const useStore = controlledFieldStore(formId);
|
9
|
-
return useStore(selector);
|
3
|
+
return useRootFormStore((state) => selector(state.form(formId)));
|
10
4
|
};
|
@@ -0,0 +1 @@
|
|
1
|
+
export declare type InternalFormId = string | symbol;
|
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|