remix-validated-form 4.3.0 → 4.4.1
Sign up to get free protection for your applications and to get access to all the features.
- package/.turbo/turbo-build.log +15 -9
- package/README.md +1 -0
- package/browser/ValidatedForm.js +16 -30
- package/browser/hooks.d.ts +1 -1
- package/browser/hooks.js +10 -9
- package/browser/internal/hooks.d.ts +20 -9
- package/browser/internal/hooks.js +32 -23
- package/browser/internal/logic/getRadioChecked.js +10 -0
- package/browser/internal/reset.d.ts +0 -0
- package/browser/internal/reset.js +0 -0
- package/browser/internal/state/cleanup.d.ts +2 -0
- package/browser/internal/state/cleanup.js +6 -0
- package/browser/internal/state/controlledFieldStore.d.ts +24 -0
- package/browser/internal/state/controlledFieldStore.js +57 -0
- package/browser/internal/state/controlledFields.d.ts +3 -116
- package/browser/internal/state/controlledFields.js +25 -68
- package/browser/internal/state/createFormStore.d.ts +40 -0
- package/browser/internal/state/createFormStore.js +83 -0
- package/browser/internal/state/storeFamily.d.ts +9 -0
- package/browser/internal/state/storeFamily.js +18 -0
- package/browser/internal/state/storeHooks.d.ts +5 -0
- package/browser/internal/state/storeHooks.js +10 -0
- package/browser/unreleased/formStateHooks.d.ts +15 -0
- package/browser/unreleased/formStateHooks.js +23 -14
- package/browser/userFacingFormContext.d.ts +8 -0
- package/browser/userFacingFormContext.js +5 -4
- package/dist/remix-validated-form.cjs.js +17 -0
- package/dist/remix-validated-form.es.js +2846 -0
- package/dist/remix-validated-form.umd.js +17 -0
- package/{build → dist/types}/ValidatedForm.d.ts +0 -0
- package/{build → dist/types}/hooks.d.ts +1 -1
- package/{build → dist/types}/index.d.ts +0 -0
- package/{build → dist/types}/internal/MultiValueMap.d.ts +0 -0
- package/{build → dist/types}/internal/constants.d.ts +0 -0
- package/{build → dist/types}/internal/flatten.d.ts +0 -0
- package/{build → dist/types}/internal/formContext.d.ts +0 -0
- package/{build → dist/types}/internal/getInputProps.d.ts +0 -0
- package/dist/types/internal/hooks.d.ts +32 -0
- package/{build → dist/types}/internal/hydratable.d.ts +0 -0
- package/{build → dist/types}/internal/logic/getCheckboxChecked.d.ts +0 -0
- package/{build → dist/types}/internal/logic/getRadioChecked.d.ts +0 -0
- package/dist/types/internal/state/cleanup.d.ts +2 -0
- package/dist/types/internal/state/controlledFieldStore.d.ts +24 -0
- package/dist/types/internal/state/controlledFields.d.ts +6 -0
- package/dist/types/internal/state/createFormStore.d.ts +40 -0
- package/dist/types/internal/state/storeFamily.d.ts +9 -0
- package/dist/types/internal/state/storeHooks.d.ts +5 -0
- package/{build → dist/types}/internal/submissionCallbacks.d.ts +0 -0
- package/{build → dist/types}/internal/util.d.ts +0 -0
- package/{build → dist/types}/server.d.ts +0 -0
- package/{build → dist/types}/unreleased/formStateHooks.d.ts +15 -0
- package/{build → dist/types}/userFacingFormContext.d.ts +15 -0
- package/{build → dist/types}/validation/createValidator.d.ts +0 -0
- package/{build → dist/types}/validation/types.d.ts +0 -0
- package/package.json +11 -9
- package/src/ValidatedForm.tsx +25 -47
- package/src/hooks.ts +15 -18
- package/src/internal/getInputProps.test.ts +251 -0
- package/src/internal/hooks.ts +69 -45
- package/src/internal/logic/getRadioChecked.ts +11 -0
- package/src/internal/state/cleanup.ts +8 -0
- package/src/internal/state/controlledFieldStore.ts +91 -0
- package/src/internal/state/controlledFields.ts +31 -123
- package/src/internal/state/createFormStore.ts +152 -0
- package/src/internal/state/storeFamily.ts +24 -0
- package/src/internal/state/storeHooks.ts +22 -0
- package/src/unreleased/formStateHooks.ts +50 -27
- package/src/userFacingFormContext.ts +26 -5
- package/src/validation/validation.test.ts +304 -0
- package/tsconfig.json +4 -1
- package/vite.config.ts +7 -0
- package/.turbo/turbo-test.log +0 -11
- package/browser/components.d.ts +0 -7
- package/browser/components.js +0 -10
- package/browser/internal/SingleTypeMultiValueMap.d.ts +0 -9
- package/browser/internal/SingleTypeMultiValueMap.js +0 -41
- package/browser/internal/customState.d.ts +0 -105
- package/browser/internal/customState.js +0 -46
- package/browser/internal/hooks-valtio.d.ts +0 -18
- package/browser/internal/hooks-valtio.js +0 -110
- package/browser/internal/hooks-zustand.d.ts +0 -16
- package/browser/internal/hooks-zustand.js +0 -100
- package/browser/internal/immerMiddleware.d.ts +0 -6
- package/browser/internal/immerMiddleware.js +0 -7
- package/browser/internal/logic/elementUtils.d.ts +0 -3
- package/browser/internal/logic/elementUtils.js +0 -3
- package/browser/internal/logic/getCheckboxChecked copy.d.ts +0 -1
- package/browser/internal/logic/getCheckboxChecked copy.js +0 -9
- package/browser/internal/logic/setFieldValue.d.ts +0 -1
- package/browser/internal/logic/setFieldValue.js +0 -40
- package/browser/internal/logic/setInputValueInForm.d.ts +0 -1
- package/browser/internal/logic/setInputValueInForm.js +0 -77
- package/browser/internal/setFieldValue.d.ts +0 -20
- package/browser/internal/setFieldValue.js +0 -83
- package/browser/internal/setFormValues.d.ts +0 -2
- package/browser/internal/setFormValues.js +0 -26
- package/browser/internal/state/setFieldValue.d.ts +0 -0
- package/browser/internal/state/setFieldValue.js +0 -1
- package/browser/internal/state-valtio.d.ts +0 -62
- package/browser/internal/state-valtio.js +0 -69
- package/browser/internal/state-zustand.d.ts +0 -47
- package/browser/internal/state-zustand.js +0 -85
- package/browser/internal/test.d.ts +0 -0
- package/browser/internal/test.js +0 -15
- package/browser/internal/useMultiValueMap.d.ts +0 -1
- package/browser/internal/useMultiValueMap.js +0 -11
- package/browser/internal/watch.d.ts +0 -18
- package/browser/internal/watch.js +0 -122
- package/browser/lowLevelHooks.d.ts +0 -0
- package/browser/lowLevelHooks.js +0 -1
- package/browser/test-data/testFormData.d.ts +0 -15
- package/browser/test-data/testFormData.js +0 -46
- package/browser/types.d.ts +0 -1
- package/browser/types.js +0 -1
- package/browser/validation/validation.test.d.ts +0 -1
- package/browser/validation/validation.test.js +0 -274
- package/browser/validation/withYup.d.ts +0 -6
- package/browser/validation/withYup.js +0 -40
- package/browser/validation/withZod.d.ts +0 -6
- package/browser/validation/withZod.js +0 -50
- package/build/ValidatedForm.js +0 -261
- package/build/hooks.js +0 -91
- package/build/index.js +0 -18
- package/build/internal/MultiValueMap.js +0 -48
- package/build/internal/SingleTypeMultiValueMap.d.ts +0 -8
- package/build/internal/SingleTypeMultiValueMap.js +0 -45
- package/build/internal/constants.js +0 -7
- package/build/internal/flatten.js +0 -14
- package/build/internal/formContext.js +0 -5
- package/build/internal/getInputProps.js +0 -58
- package/build/internal/hooks-valtio.d.ts +0 -18
- package/build/internal/hooks-valtio.js +0 -128
- package/build/internal/hooks-zustand.d.ts +0 -16
- package/build/internal/hooks-zustand.js +0 -117
- package/build/internal/hooks.d.ts +0 -21
- package/build/internal/hooks.js +0 -128
- package/build/internal/hydratable.js +0 -17
- package/build/internal/immerMiddleware.d.ts +0 -6
- package/build/internal/immerMiddleware.js +0 -14
- package/build/internal/logic/elementUtils.d.ts +0 -3
- package/build/internal/logic/elementUtils.js +0 -9
- package/build/internal/logic/getCheckboxChecked.js +0 -13
- package/build/internal/logic/getRadioChecked.js +0 -9
- package/build/internal/logic/setFieldValue.d.ts +0 -1
- package/build/internal/logic/setFieldValue.js +0 -47
- package/build/internal/logic/setInputValueInForm.d.ts +0 -1
- package/build/internal/logic/setInputValueInForm.js +0 -84
- package/build/internal/reset.d.ts +0 -28
- package/build/internal/reset.js +0 -19
- package/build/internal/setFormValues.d.ts +0 -2
- package/build/internal/setFormValues.js +0 -33
- package/build/internal/state/atomUtils.d.ts +0 -38
- package/build/internal/state/atomUtils.js +0 -13
- package/build/internal/state/controlledFields.d.ts +0 -119
- package/build/internal/state/controlledFields.js +0 -103
- package/build/internal/state-valtio.d.ts +0 -62
- package/build/internal/state-valtio.js +0 -83
- package/build/internal/state-zustand.d.ts +0 -47
- package/build/internal/state-zustand.js +0 -91
- package/build/internal/state.d.ts +0 -343
- package/build/internal/state.js +0 -71
- package/build/internal/submissionCallbacks.js +0 -17
- package/build/internal/test.d.ts +0 -1
- package/build/internal/test.js +0 -12
- package/build/internal/util.js +0 -41
- package/build/internal/watch.d.ts +0 -20
- package/build/internal/watch.js +0 -126
- package/build/server.js +0 -32
- package/build/types.d.ts +0 -1
- package/build/types.js +0 -2
- package/build/unreleased/formStateHooks.js +0 -59
- package/build/userFacingFormContext.js +0 -30
- package/build/validation/createValidator.js +0 -45
- package/build/validation/types.js +0 -2
- package/src/internal/reset.ts +0 -26
- package/src/internal/state/atomUtils.ts +0 -13
- package/src/internal/state.ts +0 -124
package/.turbo/turbo-build.log
CHANGED
@@ -1,9 +1,15 @@
|
|
1
|
-
[2K[1G[2m$
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
1
|
+
[2K[1G[2m$ vite build[22m
|
2
|
+
[36mvite v2.9.5 [32mbuilding for production...[36m[39m
|
3
|
+
transforming...
|
4
|
+
[32m✓[39m 321 modules transformed.
|
5
|
+
rendering chunks...
|
6
|
+
[90m[37m[2mdist/[22m[90m[39m[36mremix-validated-form.cjs.js [39m [2m43.85 KiB / gzip: 16.69 KiB[22m
|
7
|
+
[90m[37m[2mdist/[22m[90m[39m[36mremix-validated-form.es.js [39m [2m98.54 KiB / gzip: 23.34 KiB[22m
|
8
|
+
[90m[37m[2mdist/[22m[90m[39m[36mremix-validated-form.umd.js [39m [2m44.10 KiB / gzip: 16.81 KiB[22m
|
9
|
+
[32m[39m
|
10
|
+
[32m[36m[vite:dts][39m[32m Start generate declaration files...[39m
|
11
|
+
[32m[36m[vite:dts][39m[32m Declaration files built in 1824ms.[39m
|
12
|
+
[32m[39m
|
13
|
+
No name was provided for external module 'react' in output.globals – guessing 'React'
|
14
|
+
No name was provided for external module '@remix-run/react' in output.globals – guessing 'react'
|
15
|
+
No name was provided for external module '@remix-run/server-runtime' in output.globals – guessing 'serverRuntime'
|
package/README.md
CHANGED
package/browser/ValidatedForm.js
CHANGED
@@ -2,15 +2,13 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Form as RemixForm, useSubmit } from "@remix-run/react";
|
3
3
|
import uniq from "lodash/uniq";
|
4
4
|
import React, { useCallback, useEffect, useMemo, useRef, useState, } from "react";
|
5
|
-
import invariant from "tiny-invariant";
|
6
5
|
import { useIsSubmitting, useIsValid } from "./hooks";
|
7
6
|
import { FORM_ID_FIELD } from "./internal/constants";
|
8
7
|
import { InternalFormContext, } from "./internal/formContext";
|
9
|
-
import { useDefaultValuesFromLoader, useErrorResponseForForm,
|
8
|
+
import { useDefaultValuesFromLoader, useErrorResponseForForm, useHasActiveFormSubmit, useSetFieldErrors, } from "./internal/hooks";
|
10
9
|
import { useMultiValueMap } from "./internal/MultiValueMap";
|
11
|
-
import {
|
12
|
-
import {
|
13
|
-
import { useAwaitValue } from "./internal/state/controlledFields";
|
10
|
+
import { cleanupFormState } from "./internal/state/cleanup";
|
11
|
+
import { useControlledFieldStore, useFormStore, } from "./internal/state/storeHooks";
|
14
12
|
import { useSubmitComplete } from "./internal/submissionCallbacks";
|
15
13
|
import { mergeRefs, useDeepEqualsMemo, useIsomorphicLayoutEffect as useLayoutEffect, } from "./internal/util";
|
16
14
|
const getDataFromForm = (el) => new FormData(el);
|
@@ -113,32 +111,19 @@ export function ValidatedForm({ validator, onSubmit, children, fetcher, action,
|
|
113
111
|
const formRef = useRef(null);
|
114
112
|
const Form = (_a = fetcher === null || fetcher === void 0 ? void 0 : fetcher.Form) !== null && _a !== void 0 ? _a : RemixForm;
|
115
113
|
const submit = useSubmit();
|
116
|
-
const setFieldErrors =
|
117
|
-
const setFieldError =
|
118
|
-
const reset =
|
119
|
-
const
|
120
|
-
const
|
121
|
-
const
|
122
|
-
const
|
123
|
-
const
|
114
|
+
const setFieldErrors = useSetFieldErrors(formId);
|
115
|
+
const setFieldError = useFormStore(formId, (state) => state.setFieldError);
|
116
|
+
const reset = useFormStore(formId, (state) => state.reset);
|
117
|
+
const resetControlledFields = useControlledFieldStore(formId, (state) => state.reset);
|
118
|
+
const startSubmit = useFormStore(formId, (state) => state.startSubmit);
|
119
|
+
const endSubmit = useFormStore(formId, (state) => state.endSubmit);
|
120
|
+
const syncFormProps = useFormStore(formId, (state) => state.syncFormProps);
|
121
|
+
const setHydrated = useFormStore(formId, (state) => state.setHydrated);
|
122
|
+
const setFormElementInState = useFormStore(formId, (state) => state.setFormElement);
|
124
123
|
useEffect(() => {
|
125
|
-
setHydrated(
|
124
|
+
setHydrated();
|
126
125
|
return () => cleanupFormState(formId);
|
127
126
|
}, [formId, setHydrated]);
|
128
|
-
const awaitValue = useAwaitValue(formId);
|
129
|
-
const validateField = useCallback(async (field) => {
|
130
|
-
invariant(formRef.current, "Cannot find reference to form");
|
131
|
-
await awaitValue(field);
|
132
|
-
const { error } = await validator.validateField(getDataFromForm(formRef.current), field);
|
133
|
-
if (error) {
|
134
|
-
setFieldError({ field, error });
|
135
|
-
return error;
|
136
|
-
}
|
137
|
-
else {
|
138
|
-
setFieldError({ field, error: undefined });
|
139
|
-
return null;
|
140
|
-
}
|
141
|
-
}, [awaitValue, setFieldError, validator]);
|
142
127
|
const customFocusHandlers = useMultiValueMap();
|
143
128
|
const registerReceiveFocus = useCallback((fieldName, handler) => {
|
144
129
|
customFocusHandlers().add(fieldName, handler);
|
@@ -152,8 +137,8 @@ export function ValidatedForm({ validator, onSubmit, children, fetcher, action,
|
|
152
137
|
action,
|
153
138
|
defaultValues: (_a = providedDefaultValues !== null && providedDefaultValues !== void 0 ? providedDefaultValues : backendDefaultValues) !== null && _a !== void 0 ? _a : {},
|
154
139
|
subaction,
|
155
|
-
validateField,
|
156
140
|
registerReceiveFocus,
|
141
|
+
validator,
|
157
142
|
});
|
158
143
|
}, [
|
159
144
|
action,
|
@@ -161,8 +146,8 @@ export function ValidatedForm({ validator, onSubmit, children, fetcher, action,
|
|
161
146
|
registerReceiveFocus,
|
162
147
|
subaction,
|
163
148
|
syncFormProps,
|
164
|
-
validateField,
|
165
149
|
backendDefaultValues,
|
150
|
+
validator,
|
166
151
|
]);
|
167
152
|
useEffect(() => {
|
168
153
|
var _a;
|
@@ -231,5 +216,6 @@ export function ValidatedForm({ validator, onSubmit, children, fetcher, action,
|
|
231
216
|
if (event.defaultPrevented)
|
232
217
|
return;
|
233
218
|
reset();
|
219
|
+
resetControlledFields();
|
234
220
|
}, 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));
|
235
221
|
}
|
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) => (fieldName: string, value: unknown) => void;
|
package/browser/hooks.js
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
import { useEffect, useMemo } from "react";
|
2
2
|
import { createGetInputProps, } from "./internal/getInputProps";
|
3
|
-
import { useInternalFormContext, useFieldTouched, useFieldError,
|
4
|
-
import { formPropsAtom, hasBeenSubmittedAtom, isSubmittingAtom, isValidAtom, } from "./internal/state";
|
3
|
+
import { useInternalFormContext, useFieldTouched, useFieldError, useFieldDefaultValue, useClearError, useInternalIsSubmitting, useInternalIsValid, useInternalHasBeenSubmitted, useValidateField, useRegisterReceiveFocus, } from "./internal/hooks";
|
5
4
|
import { useControllableValue, useUpdateControllableValue, } from "./internal/state/controlledFields";
|
6
5
|
/**
|
7
6
|
* Returns whether or not the parent form is currently being submitted.
|
@@ -12,7 +11,7 @@ import { useControllableValue, useUpdateControllableValue, } from "./internal/st
|
|
12
11
|
*/
|
13
12
|
export const useIsSubmitting = (formId) => {
|
14
13
|
const formContext = useInternalFormContext(formId, "useIsSubmitting");
|
15
|
-
return
|
14
|
+
return useInternalIsSubmitting(formContext.formId);
|
16
15
|
};
|
17
16
|
/**
|
18
17
|
* Returns whether or not the current form is valid.
|
@@ -21,7 +20,7 @@ export const useIsSubmitting = (formId) => {
|
|
21
20
|
*/
|
22
21
|
export const useIsValid = (formId) => {
|
23
22
|
const formContext = useInternalFormContext(formId, "useIsValid");
|
24
|
-
return
|
23
|
+
return useInternalIsValid(formContext.formId);
|
25
24
|
};
|
26
25
|
/**
|
27
26
|
* Provides the data and helpers necessary to set up a field.
|
@@ -31,9 +30,11 @@ export const useField = (name, options) => {
|
|
31
30
|
const formContext = useInternalFormContext(providedFormId, "useField");
|
32
31
|
const defaultValue = useFieldDefaultValue(name, formContext);
|
33
32
|
const [touched, setTouched] = useFieldTouched(name, formContext);
|
34
|
-
const
|
35
|
-
const
|
36
|
-
const
|
33
|
+
const error = useFieldError(name, formContext);
|
34
|
+
const clearError = useClearError(formContext);
|
35
|
+
const hasBeenSubmitted = useInternalHasBeenSubmitted(formContext.formId);
|
36
|
+
const validateField = useValidateField(formContext.formId);
|
37
|
+
const registerReceiveFocus = useRegisterReceiveFocus(formContext.formId);
|
37
38
|
useEffect(() => {
|
38
39
|
if (handleReceiveFocus)
|
39
40
|
return registerReceiveFocus(name, handleReceiveFocus);
|
@@ -41,7 +42,7 @@ export const useField = (name, options) => {
|
|
41
42
|
const field = useMemo(() => {
|
42
43
|
const helpers = {
|
43
44
|
error,
|
44
|
-
clearError: () =>
|
45
|
+
clearError: () => clearError(name),
|
45
46
|
validate: () => {
|
46
47
|
validateField(name);
|
47
48
|
},
|
@@ -61,13 +62,13 @@ export const useField = (name, options) => {
|
|
61
62
|
};
|
62
63
|
}, [
|
63
64
|
error,
|
65
|
+
clearError,
|
64
66
|
defaultValue,
|
65
67
|
touched,
|
66
68
|
setTouched,
|
67
69
|
name,
|
68
70
|
hasBeenSubmitted,
|
69
71
|
options === null || options === void 0 ? void 0 : options.validationBehavior,
|
70
|
-
setError,
|
71
72
|
validateField,
|
72
73
|
]);
|
73
74
|
return field;
|
@@ -1,11 +1,7 @@
|
|
1
|
-
import { Atom, WritableAtom } from "jotai";
|
2
|
-
import { useUpdateAtom } from "jotai/utils";
|
3
1
|
import { FieldErrors, ValidationErrorResponseData } from "..";
|
4
2
|
import { InternalFormContextValue } from "./formContext";
|
5
3
|
import { Hydratable } from "./hydratable";
|
6
|
-
|
7
|
-
export declare const useFormAtom: <Value, Update, Result extends void | Promise<void>>(anAtom: WritableAtom<Value, Update, Result>) => [Value extends Promise<infer V> ? V : Value, import("jotai/core/atom").SetAtom<Update, Result>];
|
8
|
-
export declare const useFormAtomValue: <Value>(anAtom: Atom<Value>) => Value extends Promise<infer V> ? V : Value;
|
4
|
+
import { InternalFormId } from "./state/storeFamily";
|
9
5
|
export declare const useInternalFormContext: (formId?: string | symbol | undefined, hookName?: string | undefined) => InternalFormContextValue;
|
10
6
|
export declare function useErrorResponseForForm({ fetcher, subaction, formId, }: InternalFormContextValue): ValidationErrorResponseData | null;
|
11
7
|
export declare const useFieldErrorsForForm: (context: InternalFormContextValue) => Hydratable<FieldErrors | undefined>;
|
@@ -14,8 +10,23 @@ export declare const useDefaultValuesForForm: (context: InternalFormContextValue
|
|
14
10
|
[fieldName: string]: any;
|
15
11
|
}>;
|
16
12
|
export declare const useHasActiveFormSubmit: ({ fetcher, }: InternalFormContextValue) => boolean;
|
17
|
-
export declare const useFieldTouched: (field: string, { formId }: InternalFormContextValue) => [boolean, (
|
18
|
-
export declare const useFieldError: (name: string, context: InternalFormContextValue) =>
|
13
|
+
export declare const useFieldTouched: (field: string, { formId }: InternalFormContextValue) => readonly [boolean, (touched: boolean) => void];
|
14
|
+
export declare const useFieldError: (name: string, context: InternalFormContextValue) => string | undefined;
|
15
|
+
export declare const useClearError: (context: InternalFormContextValue) => (field: string) => void;
|
19
16
|
export declare const useFieldDefaultValue: (name: string, context: InternalFormContextValue) => any;
|
20
|
-
export declare const
|
21
|
-
export declare const
|
17
|
+
export declare const useInternalIsSubmitting: (formId: InternalFormId) => boolean;
|
18
|
+
export declare const useInternalIsValid: (formId: InternalFormId) => boolean;
|
19
|
+
export declare const useInternalHasBeenSubmitted: (formId: InternalFormId) => boolean;
|
20
|
+
export declare const useValidateField: (formId: InternalFormId) => (fieldName: string) => Promise<string | null>;
|
21
|
+
export declare const useValidate: (formId: InternalFormId) => () => Promise<void>;
|
22
|
+
export declare const useRegisterReceiveFocus: (formId: InternalFormId) => (fieldName: string, handler: () => void) => () => void;
|
23
|
+
export declare const useSyncedDefaultValues: (formId: InternalFormId) => {
|
24
|
+
[fieldName: string]: any;
|
25
|
+
};
|
26
|
+
export declare const useSetTouched: ({ formId }: InternalFormContextValue) => (field: string, touched: boolean) => void;
|
27
|
+
export declare const useTouchedFields: (formId: InternalFormId) => import("..").TouchedFields;
|
28
|
+
export declare const useFieldErrors: (formId: InternalFormId) => FieldErrors;
|
29
|
+
export declare const useSetFieldErrors: (formId: InternalFormId) => (errors: FieldErrors) => void;
|
30
|
+
export declare const useResetFormElement: (formId: InternalFormId) => () => void;
|
31
|
+
export declare const useFormActionProp: (formId: InternalFormId) => string | undefined;
|
32
|
+
export declare const useFormSubactionProp: (formId: InternalFormId) => string | undefined;
|
@@ -1,16 +1,11 @@
|
|
1
1
|
import { useActionData, useMatches, useTransition } from "@remix-run/react";
|
2
|
-
import { useAtom } from "jotai";
|
3
|
-
import { useAtomValue, useUpdateAtom } from "jotai/utils";
|
4
2
|
import lodashGet from "lodash/get";
|
5
3
|
import { useCallback, useContext } from "react";
|
6
4
|
import invariant from "tiny-invariant";
|
7
5
|
import { formDefaultValuesKey } from "./constants";
|
8
6
|
import { InternalFormContext } from "./formContext";
|
9
7
|
import { hydratable } from "./hydratable";
|
10
|
-
import {
|
11
|
-
export const useFormUpdateAtom = (atom) => useUpdateAtom(atom, ATOM_SCOPE);
|
12
|
-
export const useFormAtom = (anAtom) => useAtom(anAtom, ATOM_SCOPE);
|
13
|
-
export const useFormAtomValue = (anAtom) => useAtomValue(anAtom, ATOM_SCOPE);
|
8
|
+
import { useFormStore } from "./state/storeHooks";
|
14
9
|
export const useInternalFormContext = (formId, hookName) => {
|
15
10
|
const formContext = useContext(InternalFormContext);
|
16
11
|
if (formId)
|
@@ -39,7 +34,7 @@ export function useErrorResponseForForm({ fetcher, subaction, formId, }) {
|
|
39
34
|
}
|
40
35
|
export const useFieldErrorsForForm = (context) => {
|
41
36
|
const response = useErrorResponseForForm(context);
|
42
|
-
const hydrated =
|
37
|
+
const hydrated = useFormStore(context.formId, (state) => state.isHydrated);
|
43
38
|
return hydratable.from(response === null || response === void 0 ? void 0 : response.fieldErrors, hydrated);
|
44
39
|
};
|
45
40
|
export const useDefaultValuesFromLoader = ({ formId, }) => {
|
@@ -57,7 +52,7 @@ export const useDefaultValuesFromLoader = ({ formId, }) => {
|
|
57
52
|
};
|
58
53
|
export const useDefaultValuesForForm = (context) => {
|
59
54
|
const { formId, defaultValuesProp } = context;
|
60
|
-
const hydrated =
|
55
|
+
const hydrated = useFormStore(formId, (state) => state.isHydrated);
|
61
56
|
const errorResponse = useErrorResponseForForm(context);
|
62
57
|
const defaultValuesFromLoader = useDefaultValuesFromLoader(context);
|
63
58
|
// Typical flow is:
|
@@ -82,27 +77,41 @@ export const useHasActiveFormSubmit = ({ fetcher, }) => {
|
|
82
77
|
: !!transition.submission;
|
83
78
|
return hasActiveSubmission;
|
84
79
|
};
|
85
|
-
export const useFieldTouched = (field, { formId }) =>
|
80
|
+
export const useFieldTouched = (field, { formId }) => {
|
81
|
+
const touched = useFormStore(formId, (state) => state.touchedFields[field]);
|
82
|
+
const setFieldTouched = useFormStore(formId, (state) => state.setTouched);
|
83
|
+
const setTouched = useCallback((touched) => setFieldTouched(field, touched), [field, setFieldTouched]);
|
84
|
+
return [touched, setTouched];
|
85
|
+
};
|
86
86
|
export const useFieldError = (name, context) => {
|
87
87
|
const fieldErrors = useFieldErrorsForForm(context);
|
88
|
-
const
|
89
|
-
return [
|
90
|
-
|
91
|
-
|
92
|
-
|
88
|
+
const state = useFormStore(context.formId, (state) => state.fieldErrors[name]);
|
89
|
+
return fieldErrors.map((fieldErrors) => fieldErrors === null || fieldErrors === void 0 ? void 0 : fieldErrors[name]).hydrateTo(state);
|
90
|
+
};
|
91
|
+
export const useClearError = (context) => {
|
92
|
+
const { formId } = context;
|
93
|
+
return useFormStore(formId, (state) => state.clearFieldError);
|
93
94
|
};
|
94
95
|
export const useFieldDefaultValue = (name, context) => {
|
95
96
|
const defaultValues = useDefaultValuesForForm(context);
|
96
|
-
const
|
97
|
+
const state = useSyncedDefaultValues(context.formId);
|
97
98
|
return defaultValues
|
98
99
|
.map((val) => lodashGet(val, name))
|
99
100
|
.hydrateTo(lodashGet(state, name));
|
100
101
|
};
|
101
|
-
export const
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
export const
|
106
|
-
|
107
|
-
|
108
|
-
};
|
102
|
+
export const useInternalIsSubmitting = (formId) => useFormStore(formId, (state) => state.isSubmitting);
|
103
|
+
export const useInternalIsValid = (formId) => useFormStore(formId, (state) => state.isValid());
|
104
|
+
export const useInternalHasBeenSubmitted = (formId) => useFormStore(formId, (state) => state.hasBeenSubmitted);
|
105
|
+
export const useValidateField = (formId) => useFormStore(formId, (state) => state.validateField);
|
106
|
+
export const useValidate = (formId) => useFormStore(formId, (state) => state.validate);
|
107
|
+
const noOpReceiver = () => () => { };
|
108
|
+
export const useRegisterReceiveFocus = (formId) => useFormStore(formId, (state) => { var _a, _b; return (_b = (_a = state.formProps) === null || _a === void 0 ? void 0 : _a.registerReceiveFocus) !== null && _b !== void 0 ? _b : noOpReceiver; });
|
109
|
+
const defaultDefaultValues = {};
|
110
|
+
export const useSyncedDefaultValues = (formId) => useFormStore(formId, (state) => { var _a, _b; return (_b = (_a = state.formProps) === null || _a === void 0 ? void 0 : _a.defaultValues) !== null && _b !== void 0 ? _b : defaultDefaultValues; });
|
111
|
+
export const useSetTouched = ({ formId }) => useFormStore(formId, (state) => state.setTouched);
|
112
|
+
export const useTouchedFields = (formId) => useFormStore(formId, (state) => state.touchedFields);
|
113
|
+
export const useFieldErrors = (formId) => useFormStore(formId, (state) => state.fieldErrors);
|
114
|
+
export const useSetFieldErrors = (formId) => useFormStore(formId, (state) => state.setFieldErrors);
|
115
|
+
export const useResetFormElement = (formId) => useFormStore(formId, (state) => state.resetFormElement);
|
116
|
+
export const useFormActionProp = (formId) => useFormStore(formId, (state) => { var _a; return (_a = state.formProps) === null || _a === void 0 ? void 0 : _a.action; });
|
117
|
+
export const useFormSubactionProp = (formId) => useFormStore(formId, (state) => { var _a; return (_a = state.formProps) === null || _a === void 0 ? void 0 : _a.subaction; });
|
@@ -3,3 +3,13 @@ export const getRadioChecked = (radioValue = "on", newValue) => {
|
|
3
3
|
return newValue === radioValue;
|
4
4
|
return undefined;
|
5
5
|
};
|
6
|
+
if (import.meta.vitest) {
|
7
|
+
const { it, expect } = import.meta.vitest;
|
8
|
+
it("getRadioChecked", () => {
|
9
|
+
expect(getRadioChecked("on", "on")).toBe(true);
|
10
|
+
expect(getRadioChecked("on", undefined)).toBe(undefined);
|
11
|
+
expect(getRadioChecked("trueValue", undefined)).toBe(undefined);
|
12
|
+
expect(getRadioChecked("trueValue", "bob")).toBe(false);
|
13
|
+
expect(getRadioChecked("trueValue", "trueValue")).toBe(true);
|
14
|
+
});
|
15
|
+
}
|
File without changes
|
File without changes
|
@@ -0,0 +1,24 @@
|
|
1
|
+
export declare type ControlledFieldState = {
|
2
|
+
fields: {
|
3
|
+
[fieldName: string]: {
|
4
|
+
refCount: number;
|
5
|
+
value: unknown;
|
6
|
+
defaultValue?: unknown;
|
7
|
+
hydrated: boolean;
|
8
|
+
valueUpdatePromise: Promise<void> | undefined;
|
9
|
+
resolveValueUpdate: (() => void) | undefined;
|
10
|
+
} | undefined;
|
11
|
+
};
|
12
|
+
register: (fieldName: string) => void;
|
13
|
+
unregister: (fieldName: string) => void;
|
14
|
+
setValue: (fieldName: string, value: unknown) => void;
|
15
|
+
hydrateWithDefault: (fieldName: string, defaultValue: unknown) => void;
|
16
|
+
awaitValueUpdate: (fieldName: string) => Promise<void>;
|
17
|
+
reset: () => void;
|
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;
|
24
|
+
};
|
@@ -0,0 +1,57 @@
|
|
1
|
+
import create from "zustand";
|
2
|
+
import { immer } from "zustand/middleware/immer";
|
3
|
+
import { storeFamily } from "./storeFamily";
|
4
|
+
export const controlledFieldStore = storeFamily(() => create()(immer((set, get, api) => ({
|
5
|
+
fields: {},
|
6
|
+
register: (field) => set((state) => {
|
7
|
+
if (state.fields[field]) {
|
8
|
+
state.fields[field].refCount++;
|
9
|
+
}
|
10
|
+
else {
|
11
|
+
state.fields[field] = {
|
12
|
+
refCount: 1,
|
13
|
+
value: undefined,
|
14
|
+
hydrated: false,
|
15
|
+
valueUpdatePromise: undefined,
|
16
|
+
resolveValueUpdate: undefined,
|
17
|
+
};
|
18
|
+
}
|
19
|
+
}),
|
20
|
+
unregister: (field) => set((state) => {
|
21
|
+
const fieldState = state.fields[field];
|
22
|
+
if (!fieldState)
|
23
|
+
return;
|
24
|
+
fieldState.refCount--;
|
25
|
+
if (fieldState.refCount === 0)
|
26
|
+
delete state.fields[field];
|
27
|
+
}),
|
28
|
+
setValue: (field, value) => set((state) => {
|
29
|
+
const fieldState = state.fields[field];
|
30
|
+
if (!fieldState)
|
31
|
+
return;
|
32
|
+
fieldState.value = value;
|
33
|
+
const promise = new Promise((resolve) => {
|
34
|
+
fieldState.resolveValueUpdate = resolve;
|
35
|
+
});
|
36
|
+
fieldState.valueUpdatePromise = promise;
|
37
|
+
}),
|
38
|
+
hydrateWithDefault: (field, defaultValue) => set((state) => {
|
39
|
+
const fieldState = state.fields[field];
|
40
|
+
if (!fieldState)
|
41
|
+
return;
|
42
|
+
fieldState.value = defaultValue;
|
43
|
+
fieldState.defaultValue = defaultValue;
|
44
|
+
fieldState.hydrated = true;
|
45
|
+
}),
|
46
|
+
awaitValueUpdate: async (field) => {
|
47
|
+
var _a;
|
48
|
+
await ((_a = get().fields[field]) === null || _a === void 0 ? void 0 : _a.valueUpdatePromise);
|
49
|
+
},
|
50
|
+
reset: () => set((state) => {
|
51
|
+
Object.values(state.fields).forEach((field) => {
|
52
|
+
if (!field)
|
53
|
+
return;
|
54
|
+
field.value = field.defaultValue;
|
55
|
+
});
|
56
|
+
}),
|
57
|
+
}))));
|
@@ -1,119 +1,6 @@
|
|
1
|
-
import { PrimitiveAtom } from "jotai";
|
2
1
|
import { InternalFormContextValue } from "../formContext";
|
3
|
-
import {
|
4
|
-
export declare const controlledFieldsAtom: {
|
5
|
-
(param: InternalFormId): import("jotai").Atom<Record<string, PrimitiveAtom<unknown>>> & {
|
6
|
-
write: (get: {
|
7
|
-
<Value>(atom: import("jotai").Atom<Value | Promise<Value>>): Value;
|
8
|
-
<Value_1>(atom: import("jotai").Atom<Promise<Value_1>>): Value_1;
|
9
|
-
<Value_2>(atom: import("jotai").Atom<Value_2>): Value_2 extends Promise<infer V> ? V : Value_2;
|
10
|
-
} & {
|
11
|
-
<Value_3>(atom: import("jotai").Atom<Value_3 | Promise<Value_3>>, options: {
|
12
|
-
unstable_promise: true;
|
13
|
-
}): Value_3 | Promise<Value_3>;
|
14
|
-
<Value_4>(atom: import("jotai").Atom<Promise<Value_4>>, options: {
|
15
|
-
unstable_promise: true;
|
16
|
-
}): Value_4 | Promise<Value_4>;
|
17
|
-
<Value_5>(atom: import("jotai").Atom<Value_5>, options: {
|
18
|
-
unstable_promise: true;
|
19
|
-
}): (Value_5 extends Promise<infer V> ? V : Value_5) | Promise<Value_5 extends Promise<infer V> ? V : Value_5>;
|
20
|
-
}, set: {
|
21
|
-
<Value_6, Result extends void | Promise<void>>(atom: import("jotai").WritableAtom<Value_6, undefined, Result>): Result;
|
22
|
-
<Value_7, Update, Result_1 extends void | Promise<void>>(atom: import("jotai").WritableAtom<Value_7, Update, Result_1>, update: Update): Result_1;
|
23
|
-
}, update: Record<string, PrimitiveAtom<unknown>> | ((prev: Record<string, PrimitiveAtom<unknown>>) => Record<string, PrimitiveAtom<unknown>>)) => void;
|
24
|
-
onMount?: (<S extends (update: Record<string, PrimitiveAtom<unknown>> | ((prev: Record<string, PrimitiveAtom<unknown>>) => Record<string, PrimitiveAtom<unknown>>)) => void>(setAtom: S) => void | (() => void)) | undefined;
|
25
|
-
} & {
|
26
|
-
init: Record<string, PrimitiveAtom<unknown>>;
|
27
|
-
};
|
28
|
-
remove(param: InternalFormId): void;
|
29
|
-
setShouldRemove(shouldRemove: ((createdAt: number, param: InternalFormId) => boolean) | null): void;
|
30
|
-
};
|
31
|
-
export declare const valueUpdatePromiseAtom: {
|
32
|
-
(param: FieldAtomKey): import("jotai").Atom<Promise<void> | undefined> & {
|
33
|
-
write: (get: {
|
34
|
-
<Value>(atom: import("jotai").Atom<Value | Promise<Value>>): Value;
|
35
|
-
<Value_1>(atom: import("jotai").Atom<Promise<Value_1>>): Value_1;
|
36
|
-
<Value_2>(atom: import("jotai").Atom<Value_2>): Value_2 extends Promise<infer V> ? V : Value_2;
|
37
|
-
} & {
|
38
|
-
<Value_3>(atom: import("jotai").Atom<Value_3 | Promise<Value_3>>, options: {
|
39
|
-
unstable_promise: true;
|
40
|
-
}): Value_3 | Promise<Value_3>;
|
41
|
-
<Value_4>(atom: import("jotai").Atom<Promise<Value_4>>, options: {
|
42
|
-
unstable_promise: true;
|
43
|
-
}): Value_4 | Promise<Value_4>;
|
44
|
-
<Value_5>(atom: import("jotai").Atom<Value_5>, options: {
|
45
|
-
unstable_promise: true;
|
46
|
-
}): (Value_5 extends Promise<infer V> ? V : Value_5) | Promise<Value_5 extends Promise<infer V> ? V : Value_5>;
|
47
|
-
}, set: {
|
48
|
-
<Value_6, Result extends void | Promise<void>>(atom: import("jotai").WritableAtom<Value_6, undefined, Result>): Result;
|
49
|
-
<Value_7, Update, Result_1 extends void | Promise<void>>(atom: import("jotai").WritableAtom<Value_7, Update, Result_1>, update: Update): Result_1;
|
50
|
-
}, update: Promise<void> | ((prev: Promise<void> | undefined) => Promise<void> | undefined) | undefined) => void;
|
51
|
-
onMount?: (<S extends (update?: Promise<void> | ((prev: Promise<void> | undefined) => Promise<void> | undefined) | undefined) => void>(setAtom: S) => void | (() => void)) | undefined;
|
52
|
-
} & {
|
53
|
-
init: Promise<void> | undefined;
|
54
|
-
};
|
55
|
-
remove(param: FieldAtomKey): void;
|
56
|
-
setShouldRemove(shouldRemove: ((createdAt: number, param: FieldAtomKey) => boolean) | null): void;
|
57
|
-
};
|
58
|
-
export declare const resolveValueUpdateAtom: {
|
59
|
-
(param: FieldAtomKey): import("jotai").Atom<(() => void) | undefined> & {
|
60
|
-
write: (get: {
|
61
|
-
<Value>(atom: import("jotai").Atom<Value | Promise<Value>>): Value;
|
62
|
-
<Value_1>(atom: import("jotai").Atom<Promise<Value_1>>): Value_1;
|
63
|
-
<Value_2>(atom: import("jotai").Atom<Value_2>): Value_2 extends Promise<infer V> ? V : Value_2;
|
64
|
-
} & {
|
65
|
-
<Value_3>(atom: import("jotai").Atom<Value_3 | Promise<Value_3>>, options: {
|
66
|
-
unstable_promise: true;
|
67
|
-
}): Value_3 | Promise<Value_3>;
|
68
|
-
<Value_4>(atom: import("jotai").Atom<Promise<Value_4>>, options: {
|
69
|
-
unstable_promise: true;
|
70
|
-
}): Value_4 | Promise<Value_4>;
|
71
|
-
<Value_5>(atom: import("jotai").Atom<Value_5>, options: {
|
72
|
-
unstable_promise: true;
|
73
|
-
}): (Value_5 extends Promise<infer V> ? V : Value_5) | Promise<Value_5 extends Promise<infer V> ? V : Value_5>;
|
74
|
-
}, set: {
|
75
|
-
<Value_6, Result extends void | Promise<void>>(atom: import("jotai").WritableAtom<Value_6, undefined, Result>): Result;
|
76
|
-
<Value_7, Update, Result_1 extends void | Promise<void>>(atom: import("jotai").WritableAtom<Value_7, Update, Result_1>, update: Update): Result_1;
|
77
|
-
}, update: (() => void) | ((prev: (() => void) | undefined) => (() => void) | undefined) | undefined) => void;
|
78
|
-
onMount?: (<S extends (update?: (() => void) | ((prev: (() => void) | undefined) => (() => void) | undefined) | undefined) => void>(setAtom: S) => void | (() => void)) | undefined;
|
79
|
-
} & {
|
80
|
-
init: (() => void) | undefined;
|
81
|
-
};
|
82
|
-
remove(param: FieldAtomKey): void;
|
83
|
-
setShouldRemove(shouldRemove: ((createdAt: number, param: FieldAtomKey) => boolean) | null): void;
|
84
|
-
};
|
85
|
-
export declare const setControlledFieldValueAtom: import("jotai").Atom<null> & {
|
86
|
-
write: (get: {
|
87
|
-
<Value>(atom: import("jotai").Atom<Value | Promise<Value>>): Value;
|
88
|
-
<Value_1>(atom: import("jotai").Atom<Promise<Value_1>>): Value_1;
|
89
|
-
<Value_2>(atom: import("jotai").Atom<Value_2>): Value_2 extends Promise<infer V> ? V : Value_2;
|
90
|
-
} & {
|
91
|
-
<Value_3>(atom: import("jotai").Atom<Value_3 | Promise<Value_3>>, options: {
|
92
|
-
unstable_promise: true;
|
93
|
-
}): Value_3 | Promise<Value_3>;
|
94
|
-
<Value_4>(atom: import("jotai").Atom<Promise<Value_4>>, options: {
|
95
|
-
unstable_promise: true;
|
96
|
-
}): Value_4 | Promise<Value_4>;
|
97
|
-
<Value_5>(atom: import("jotai").Atom<Value_5>, options: {
|
98
|
-
unstable_promise: true;
|
99
|
-
}): (Value_5 extends Promise<infer V> ? V : Value_5) | Promise<Value_5 extends Promise<infer V> ? V : Value_5>;
|
100
|
-
}, set: {
|
101
|
-
<Value_6, Result extends void | Promise<void>>(atom: import("jotai").WritableAtom<Value_6, undefined, Result>): Result;
|
102
|
-
<Value_7, Update, Result_1 extends void | Promise<void>>(atom: import("jotai").WritableAtom<Value_7, Update, Result_1>, update: Update): Result_1;
|
103
|
-
}, update: {
|
104
|
-
formId: InternalFormId;
|
105
|
-
field: string;
|
106
|
-
value: unknown;
|
107
|
-
}) => void;
|
108
|
-
onMount?: (<S extends (update: {
|
109
|
-
formId: InternalFormId;
|
110
|
-
field: string;
|
111
|
-
value: unknown;
|
112
|
-
}) => void>(setAtom: S) => void | (() => void)) | undefined;
|
113
|
-
} & {
|
114
|
-
init: null;
|
115
|
-
};
|
2
|
+
import { InternalFormId } from "./storeFamily";
|
116
3
|
export declare const useControlledFieldValue: (context: InternalFormContextValue, field: string) => any;
|
117
4
|
export declare const useControllableValue: (context: InternalFormContextValue, field: string) => readonly [any, (value: unknown) => void];
|
118
|
-
export declare const useUpdateControllableValue: (formId: InternalFormId) => (
|
119
|
-
export declare const useAwaitValue: (formId: InternalFormId) => (
|
5
|
+
export declare const useUpdateControllableValue: (formId: InternalFormId) => (fieldName: string, value: unknown) => void;
|
6
|
+
export declare const useAwaitValue: (formId: InternalFormId) => (fieldName: string) => Promise<void>;
|