remix-validated-form 4.4.3 → 4.5.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 +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 {};
|