remix-validated-form 4.3.1-beta.0 → 4.4.2
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 +5 -5
- package/browser/ValidatedForm.js +20 -35
- 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 +1 -1
- 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 +15 -0
- package/browser/userFacingFormContext.js +6 -4
- package/dist/remix-validated-form.cjs.js +18 -1
- package/dist/remix-validated-form.cjs.js.map +1 -0
- package/dist/remix-validated-form.es.js +1039 -1729
- package/dist/remix-validated-form.es.js.map +1 -0
- package/dist/remix-validated-form.umd.js +18 -1
- package/dist/remix-validated-form.umd.js.map +1 -0
- package/dist/types/hooks.d.ts +1 -1
- package/dist/types/internal/hooks.d.ts +20 -9
- 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 +3 -116
- 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/dist/types/unreleased/formStateHooks.d.ts +15 -0
- package/dist/types/userFacingFormContext.d.ts +15 -0
- package/package.json +4 -3
- package/src/ValidatedForm.tsx +38 -53
- package/src/hooks.ts +15 -18
- package/src/internal/hooks.ts +69 -45
- package/src/internal/logic/getRadioChecked.ts +1 -1
- 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/dist/types/internal/reset.d.ts +0 -28
- package/dist/types/internal/state/atomUtils.d.ts +0 -38
- package/dist/types/internal/state.d.ts +0 -343
- 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,14 +1,14 @@
|
|
1
1
|
$ vite build
|
2
2
|
vite v2.9.5 building for production...
|
3
3
|
transforming...
|
4
|
-
✓
|
4
|
+
✓ 321 modules transformed.
|
5
5
|
rendering chunks...
|
6
|
-
dist/remix-validated-form.cjs.js
|
7
|
-
dist/remix-validated-form.es.js
|
8
|
-
dist/remix-validated-form.umd.js
|
6
|
+
dist/remix-validated-form.cjs.js 43.88 KiB / gzip: 16.70 KiB
|
7
|
+
dist/remix-validated-form.es.js 98.54 KiB / gzip: 23.35 KiB
|
8
|
+
dist/remix-validated-form.umd.js 44.13 KiB / gzip: 16.84 KiB
|
9
9
|
|
10
10
|
[vite:dts] Start generate declaration files...
|
11
|
-
[vite:dts] Declaration files built in
|
11
|
+
[vite:dts] Declaration files built in 1804ms.
|
12
12
|
|
13
13
|
No name was provided for external module 'react' in output.globals – guessing 'React'
|
14
14
|
No name was provided for external module '@remix-run/react' in output.globals – guessing 'react'
|
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;
|
@@ -208,18 +193,17 @@ export function ValidatedForm({ validator, onSubmit, children, fetcher, action,
|
|
208
193
|
endSubmit();
|
209
194
|
return;
|
210
195
|
}
|
196
|
+
const submitter = e.nativeEvent
|
197
|
+
.submitter;
|
211
198
|
// We deviate from the remix code here a bit because of our async submit.
|
212
199
|
// In remix's `FormImpl`, they use `event.currentTarget` to get the form,
|
213
200
|
// but we already have the form in `formRef.current` so we can just use that.
|
214
201
|
// If we use `event.currentTarget` here, it will break because `currentTarget`
|
215
202
|
// will have changed since the start of the submission.
|
216
203
|
if (fetcher)
|
217
|
-
fetcher.submit(
|
204
|
+
fetcher.submit(submitter || e.currentTarget);
|
218
205
|
else
|
219
|
-
submit(
|
220
|
-
method,
|
221
|
-
replace,
|
222
|
-
});
|
206
|
+
submit(submitter || e.currentTarget, { method, replace });
|
223
207
|
clickedButtonRef.current = null;
|
224
208
|
}
|
225
209
|
};
|
@@ -231,5 +215,6 @@ export function ValidatedForm({ validator, onSubmit, children, fetcher, action,
|
|
231
215
|
if (event.defaultPrevented)
|
232
216
|
return;
|
233
217
|
reset();
|
218
|
+
resetControlledFields();
|
234
219
|
}, 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
220
|
}
|
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; });
|
@@ -5,7 +5,7 @@ export const getRadioChecked = (radioValue = "on", newValue) => {
|
|
5
5
|
};
|
6
6
|
if (import.meta.vitest) {
|
7
7
|
const { it, expect } = import.meta.vitest;
|
8
|
-
it("
|
8
|
+
it("getRadioChecked", () => {
|
9
9
|
expect(getRadioChecked("on", "on")).toBe(true);
|
10
10
|
expect(getRadioChecked("on", undefined)).toBe(undefined);
|
11
11
|
expect(getRadioChecked("trueValue", undefined)).toBe(undefined);
|
@@ -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>;
|