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.
Files changed (58) hide show
  1. package/.turbo/turbo-build.log +5 -5
  2. package/browser/ValidatedForm.js +20 -35
  3. package/browser/hooks.d.ts +1 -1
  4. package/browser/hooks.js +10 -9
  5. package/browser/internal/hooks.d.ts +20 -9
  6. package/browser/internal/hooks.js +32 -23
  7. package/browser/internal/logic/getRadioChecked.js +1 -1
  8. package/browser/internal/state/cleanup.d.ts +2 -0
  9. package/browser/internal/state/cleanup.js +6 -0
  10. package/browser/internal/state/controlledFieldStore.d.ts +24 -0
  11. package/browser/internal/state/controlledFieldStore.js +57 -0
  12. package/browser/internal/state/controlledFields.d.ts +3 -116
  13. package/browser/internal/state/controlledFields.js +25 -68
  14. package/browser/internal/state/createFormStore.d.ts +40 -0
  15. package/browser/internal/state/createFormStore.js +83 -0
  16. package/browser/internal/state/storeFamily.d.ts +9 -0
  17. package/browser/internal/state/storeFamily.js +18 -0
  18. package/browser/internal/state/storeHooks.d.ts +5 -0
  19. package/browser/internal/state/storeHooks.js +10 -0
  20. package/browser/unreleased/formStateHooks.d.ts +15 -0
  21. package/browser/unreleased/formStateHooks.js +23 -14
  22. package/browser/userFacingFormContext.d.ts +15 -0
  23. package/browser/userFacingFormContext.js +6 -4
  24. package/dist/remix-validated-form.cjs.js +18 -1
  25. package/dist/remix-validated-form.cjs.js.map +1 -0
  26. package/dist/remix-validated-form.es.js +1039 -1729
  27. package/dist/remix-validated-form.es.js.map +1 -0
  28. package/dist/remix-validated-form.umd.js +18 -1
  29. package/dist/remix-validated-form.umd.js.map +1 -0
  30. package/dist/types/hooks.d.ts +1 -1
  31. package/dist/types/internal/hooks.d.ts +20 -9
  32. package/dist/types/internal/state/cleanup.d.ts +2 -0
  33. package/dist/types/internal/state/controlledFieldStore.d.ts +24 -0
  34. package/dist/types/internal/state/controlledFields.d.ts +3 -116
  35. package/dist/types/internal/state/createFormStore.d.ts +40 -0
  36. package/dist/types/internal/state/storeFamily.d.ts +9 -0
  37. package/dist/types/internal/state/storeHooks.d.ts +5 -0
  38. package/dist/types/unreleased/formStateHooks.d.ts +15 -0
  39. package/dist/types/userFacingFormContext.d.ts +15 -0
  40. package/package.json +4 -3
  41. package/src/ValidatedForm.tsx +38 -53
  42. package/src/hooks.ts +15 -18
  43. package/src/internal/hooks.ts +69 -45
  44. package/src/internal/logic/getRadioChecked.ts +1 -1
  45. package/src/internal/state/cleanup.ts +8 -0
  46. package/src/internal/state/controlledFieldStore.ts +91 -0
  47. package/src/internal/state/controlledFields.ts +31 -123
  48. package/src/internal/state/createFormStore.ts +152 -0
  49. package/src/internal/state/storeFamily.ts +24 -0
  50. package/src/internal/state/storeHooks.ts +22 -0
  51. package/src/unreleased/formStateHooks.ts +50 -27
  52. package/src/userFacingFormContext.ts +26 -5
  53. package/dist/types/internal/reset.d.ts +0 -28
  54. package/dist/types/internal/state/atomUtils.d.ts +0 -38
  55. package/dist/types/internal/state.d.ts +0 -343
  56. package/src/internal/reset.ts +0 -26
  57. package/src/internal/state/atomUtils.ts +0 -13
  58. package/src/internal/state.ts +0 -124
@@ -1,14 +1,14 @@
1
1
  $ vite build
2
2
  vite v2.9.5 building for production...
3
3
  transforming...
4
- 383 modules transformed.
4
+ 321 modules transformed.
5
5
  rendering chunks...
6
- dist/remix-validated-form.cjs.js 46.74 KiB / gzip: 18.09 KiB
7
- dist/remix-validated-form.es.js 125.24 KiB / gzip: 27.86 KiB
8
- dist/remix-validated-form.umd.js 47.02 KiB / gzip: 18.26 KiB
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 1713ms.
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'
@@ -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, useFormUpdateAtom, useHasActiveFormSubmit, } from "./internal/hooks";
8
+ import { useDefaultValuesFromLoader, useErrorResponseForForm, useHasActiveFormSubmit, useSetFieldErrors, } from "./internal/hooks";
10
9
  import { useMultiValueMap } from "./internal/MultiValueMap";
11
- import { resetAtom } from "./internal/reset";
12
- import { cleanupFormState, endSubmitAtom, fieldErrorsAtom, formElementAtom, formPropsAtom, isHydratedAtom, setFieldErrorAtom, startSubmitAtom, } from "./internal/state";
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 = useFormUpdateAtom(fieldErrorsAtom(formId));
117
- const setFieldError = useFormUpdateAtom(setFieldErrorAtom(formId));
118
- const reset = useFormUpdateAtom(resetAtom(formId));
119
- const startSubmit = useFormUpdateAtom(startSubmitAtom(formId));
120
- const endSubmit = useFormUpdateAtom(endSubmitAtom(formId));
121
- const syncFormProps = useFormUpdateAtom(formPropsAtom(formId));
122
- const setHydrated = useFormUpdateAtom(isHydratedAtom(formId));
123
- const setFormElementInState = useFormUpdateAtom(formElementAtom(formId));
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(true);
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(clickedButtonRef.current || formRef.current);
204
+ fetcher.submit(submitter || e.currentTarget);
218
205
  else
219
- submit(clickedButtonRef.current || formRef.current, {
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
  }
@@ -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) => (field: string, value: unknown) => void;
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, useFormAtomValue, useFieldDefaultValue, } from "./internal/hooks";
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 useFormAtomValue(isSubmittingAtom(formContext.formId));
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 useFormAtomValue(isValidAtom(formContext.formId));
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 [error, setError] = useFieldError(name, formContext);
35
- const hasBeenSubmitted = useFormAtomValue(hasBeenSubmittedAtom(formContext.formId));
36
- const { validateField, registerReceiveFocus } = useFormAtomValue(formPropsAtom(formContext.formId));
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: () => setError(undefined),
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
- export declare const useFormUpdateAtom: typeof useUpdateAtom;
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, (update: boolean) => void];
18
- export declare const useFieldError: (name: string, context: InternalFormContextValue) => readonly [string | undefined, (update?: string | undefined) => void];
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 useClearError: ({ formId }: InternalFormContextValue) => (name: string) => void;
21
- export declare const useSetTouched: ({ formId }: InternalFormContextValue) => (name: string, touched: boolean) => void;
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 { ATOM_SCOPE, fieldErrorAtom, fieldTouchedAtom, formPropsAtom, isHydratedAtom, setFieldErrorAtom, setTouchedAtom, } from "./state";
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 = useFormAtomValue(isHydratedAtom(context.formId));
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 = useFormAtomValue(isHydratedAtom(formId));
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 }) => useFormAtom(fieldTouchedAtom({ formId, field }));
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 [state, set] = useFormAtom(fieldErrorAtom({ formId: context.formId, field: name }));
89
- return [
90
- fieldErrors.map((fieldErrors) => fieldErrors === null || fieldErrors === void 0 ? void 0 : fieldErrors[name]).hydrateTo(state),
91
- set,
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 { defaultValues: state } = useFormAtomValue(formPropsAtom(context.formId));
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 useClearError = ({ formId }) => {
102
- const updateError = useFormUpdateAtom(setFieldErrorAtom(formId));
103
- return useCallback((name) => updateError({ field: name, error: undefined }), [updateError]);
104
- };
105
- export const useSetTouched = ({ formId }) => {
106
- const setTouched = useFormUpdateAtom(setTouchedAtom(formId));
107
- return useCallback((name, touched) => setTouched({ field: name, touched }), [setTouched]);
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("add", () => {
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,2 @@
1
+ import { InternalFormId } from "./storeFamily";
2
+ export declare const cleanupFormState: (formId: InternalFormId) => void;
@@ -0,0 +1,6 @@
1
+ import { controlledFieldStore } from "./controlledFieldStore";
2
+ import { formStore } from "./createFormStore";
3
+ export const cleanupFormState = (formId) => {
4
+ formStore.remove(formId);
5
+ controlledFieldStore.remove(formId);
6
+ };
@@ -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 { FieldAtomKey, InternalFormId } from "./atomUtils";
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) => (field: string, value: unknown) => void;
119
- export declare const useAwaitValue: (formId: InternalFormId) => (arg: string) => Promise<void>;
5
+ export declare const useUpdateControllableValue: (formId: InternalFormId) => (fieldName: string, value: unknown) => void;
6
+ export declare const useAwaitValue: (formId: InternalFormId) => (fieldName: string) => Promise<void>;