remix-validated-form 4.4.1 → 4.5.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/.turbo/turbo-build.log +8 -5
  2. package/browser/ValidatedForm.js +21 -15
  3. package/browser/hooks.d.ts +1 -1
  4. package/browser/internal/hooks.d.ts +1 -1
  5. package/browser/internal/state/controlledFieldStore.d.ts +23 -21
  6. package/browser/internal/state/controlledFieldStore.js +32 -19
  7. package/browser/internal/state/controlledFields.d.ts +3 -3
  8. package/browser/internal/state/controlledFields.js +19 -21
  9. package/browser/internal/state/createFormStore.d.ts +13 -6
  10. package/browser/internal/state/createFormStore.js +48 -5
  11. package/browser/internal/state/storeHooks.d.ts +1 -3
  12. package/browser/internal/state/storeHooks.js +2 -8
  13. package/browser/internal/state/types.d.ts +1 -0
  14. package/browser/internal/state/types.js +1 -0
  15. package/browser/userFacingFormContext.d.ts +7 -0
  16. package/browser/userFacingFormContext.js +14 -4
  17. package/dist/remix-validated-form.cjs.js +4 -3
  18. package/dist/remix-validated-form.cjs.js.map +1 -0
  19. package/dist/remix-validated-form.es.js +136 -86
  20. package/dist/remix-validated-form.es.js.map +1 -0
  21. package/dist/remix-validated-form.umd.js +4 -3
  22. package/dist/remix-validated-form.umd.js.map +1 -0
  23. package/dist/types/hooks.d.ts +1 -1
  24. package/dist/types/internal/hooks.d.ts +1 -1
  25. package/dist/types/internal/state/controlledFieldStore.d.ts +23 -21
  26. package/dist/types/internal/state/controlledFields.d.ts +3 -3
  27. package/dist/types/internal/state/createFormStore.d.ts +13 -6
  28. package/dist/types/internal/state/storeHooks.d.ts +1 -3
  29. package/dist/types/internal/state/types.d.ts +1 -0
  30. package/package.json +4 -4
  31. package/src/ValidatedForm.tsx +34 -23
  32. package/src/internal/hooks.ts +1 -1
  33. package/src/internal/state/controlledFieldStore.ts +95 -74
  34. package/src/internal/state/controlledFields.ts +38 -26
  35. package/src/internal/state/createFormStore.ts +174 -113
  36. package/src/internal/state/storeHooks.ts +3 -16
  37. package/src/internal/state/types.ts +1 -0
  38. package/src/userFacingFormContext.ts +23 -11
  39. package/dist/types/internal/state/cleanup.d.ts +0 -2
  40. package/dist/types/internal/state/storeFamily.d.ts +0 -9
  41. package/src/internal/state/cleanup.ts +0 -8
  42. package/src/internal/state/storeFamily.ts +0 -24
@@ -1377,14 +1377,18 @@ const createGetInputProps = ({
1377
1377
  return omitBy_1(inputProps, (value) => value === void 0);
1378
1378
  };
1379
1379
  };
1380
+ var isProduction = true;
1380
1381
  var prefix = "Invariant failed";
1381
1382
  function invariant(condition, message) {
1382
1383
  if (condition) {
1383
1384
  return;
1384
1385
  }
1385
- {
1386
+ if (isProduction) {
1386
1387
  throw new Error(prefix);
1387
1388
  }
1389
+ var provided = typeof message === "function" ? message() : message;
1390
+ var value = provided ? prefix + ": " + provided : prefix;
1391
+ throw new Error(value);
1388
1392
  }
1389
1393
  const FORM_ID_FIELD = "__rvfInternalFormId";
1390
1394
  const FORM_DEFAULTS_FIELD = "__rvfInternalFormDefaults";
@@ -1903,27 +1907,16 @@ const immerImpl = (initializer) => (set2, get2, store) => {
1903
1907
  return initializer(store.setState, get2, store);
1904
1908
  };
1905
1909
  const immer = immerImpl;
1906
- const storeFamily = (create2) => {
1907
- const stores = /* @__PURE__ */ new Map();
1908
- const family = (formId) => {
1909
- if (stores.has(formId))
1910
- return stores.get(formId);
1911
- const store = create2(formId);
1912
- stores.set(formId, store);
1913
- return store;
1914
- };
1915
- family.remove = (formId) => {
1916
- stores.delete(formId);
1917
- };
1918
- return family;
1919
- };
1920
- const controlledFieldStore = storeFamily(() => create$1()(immer((set2, get2, api) => ({
1921
- fields: {},
1922
- register: (field) => set2((state) => {
1923
- if (state.fields[field]) {
1924
- state.fields[field].refCount++;
1910
+ const useControlledFieldStore = create$1()(immer((set2, get2) => ({
1911
+ forms: {},
1912
+ register: (formId, field) => set2((state) => {
1913
+ if (!state.forms[formId]) {
1914
+ state.forms[formId] = {};
1915
+ }
1916
+ if (state.forms[formId][field]) {
1917
+ state.forms[formId][field].refCount++;
1925
1918
  } else {
1926
- state.fields[field] = {
1919
+ state.forms[formId][field] = {
1927
1920
  refCount: 1,
1928
1921
  value: void 0,
1929
1922
  hydrated: false,
@@ -1932,16 +1925,23 @@ const controlledFieldStore = storeFamily(() => create$1()(immer((set2, get2, api
1932
1925
  };
1933
1926
  }
1934
1927
  }),
1935
- unregister: (field) => set2((state) => {
1936
- const fieldState = state.fields[field];
1928
+ unregister: (formId, field) => set2((state) => {
1929
+ var _a;
1930
+ const formState = (_a = state.forms) == null ? void 0 : _a[formId];
1931
+ const fieldState = formState == null ? void 0 : formState[field];
1937
1932
  if (!fieldState)
1938
1933
  return;
1939
1934
  fieldState.refCount--;
1940
1935
  if (fieldState.refCount === 0)
1941
- delete state.fields[field];
1936
+ delete formState[field];
1942
1937
  }),
1943
- setValue: (field, value) => set2((state) => {
1944
- const fieldState = state.fields[field];
1938
+ getField: (formId, field) => {
1939
+ var _a, _b;
1940
+ return (_b = (_a = get2().forms) == null ? void 0 : _a[formId]) == null ? void 0 : _b[field];
1941
+ },
1942
+ setValue: (formId, field, value) => set2((state) => {
1943
+ var _a, _b;
1944
+ const fieldState = (_b = (_a = state.forms) == null ? void 0 : _a[formId]) == null ? void 0 : _b[field];
1945
1945
  if (!fieldState)
1946
1946
  return;
1947
1947
  fieldState.value = value;
@@ -1950,27 +1950,56 @@ const controlledFieldStore = storeFamily(() => create$1()(immer((set2, get2, api
1950
1950
  });
1951
1951
  fieldState.valueUpdatePromise = promise;
1952
1952
  }),
1953
- hydrateWithDefault: (field, defaultValue) => set2((state) => {
1954
- const fieldState = state.fields[field];
1953
+ hydrateWithDefault: (formId, field, defaultValue) => set2((state) => {
1954
+ var _a;
1955
+ const fieldState = (_a = state.forms[formId]) == null ? void 0 : _a[field];
1955
1956
  if (!fieldState)
1956
1957
  return;
1957
1958
  fieldState.value = defaultValue;
1958
1959
  fieldState.defaultValue = defaultValue;
1959
1960
  fieldState.hydrated = true;
1960
1961
  }),
1961
- awaitValueUpdate: async (field) => {
1962
+ awaitValueUpdate: async (formId, field) => {
1962
1963
  var _a;
1963
- await ((_a = get2().fields[field]) == null ? void 0 : _a.valueUpdatePromise);
1964
+ await ((_a = get2().getField(formId, field)) == null ? void 0 : _a.valueUpdatePromise);
1964
1965
  },
1965
- reset: () => set2((state) => {
1966
- Object.values(state.fields).forEach((field) => {
1966
+ reset: (formId) => set2((state) => {
1967
+ const formState = state.forms[formId];
1968
+ if (!formState)
1969
+ return;
1970
+ Object.values(formState).forEach((field) => {
1967
1971
  if (!field)
1968
1972
  return;
1969
1973
  field.value = field.defaultValue;
1970
1974
  });
1971
1975
  })
1972
- }))));
1973
- const formStore = storeFamily((formId) => create$1()(immer((set2, get2, api) => ({
1976
+ })));
1977
+ const noOp = () => {
1978
+ };
1979
+ const defaultFormState = {
1980
+ isHydrated: false,
1981
+ isSubmitting: false,
1982
+ hasBeenSubmitted: false,
1983
+ touchedFields: {},
1984
+ fieldErrors: {},
1985
+ formElement: null,
1986
+ isValid: () => true,
1987
+ startSubmit: noOp,
1988
+ endSubmit: noOp,
1989
+ setTouched: noOp,
1990
+ setFieldError: noOp,
1991
+ setFieldErrors: noOp,
1992
+ clearFieldError: noOp,
1993
+ reset: () => noOp,
1994
+ syncFormProps: noOp,
1995
+ setHydrated: noOp,
1996
+ setFormElement: noOp,
1997
+ validateField: async () => null,
1998
+ validate: async () => {
1999
+ },
2000
+ resetFormElement: noOp
2001
+ };
2002
+ const createFormState = (formId, set2, get2) => ({
1974
2003
  isHydrated: false,
1975
2004
  isSubmitting: false,
1976
2005
  hasBeenSubmitted: false,
@@ -2004,6 +2033,7 @@ const formStore = storeFamily((formId) => create$1()(immer((set2, get2, api) =>
2004
2033
  }),
2005
2034
  syncFormProps: (props) => set2((state) => {
2006
2035
  state.formProps = props;
2036
+ state.isHydrated = true;
2007
2037
  }),
2008
2038
  setHydrated: () => set2((state) => {
2009
2039
  state.isHydrated = true;
@@ -2018,10 +2048,10 @@ const formStore = storeFamily((formId) => create$1()(immer((set2, get2, api) =>
2018
2048
  validateField: async (field) => {
2019
2049
  var _a, _b, _c;
2020
2050
  const formElement = get2().formElement;
2021
- invariant(formElement);
2051
+ invariant(formElement, "Cannot find reference to form. This is probably a bug in remix-validated-form.");
2022
2052
  const validator = (_a = get2().formProps) == null ? void 0 : _a.validator;
2023
- invariant(validator);
2024
- await ((_c = (_b = controlledFieldStore(formId).getState()).awaitValueUpdate) == null ? void 0 : _c.call(_b, field));
2053
+ invariant(validator, "Cannot validator. This is probably a bug in remix-validated-form.");
2054
+ await ((_c = (_b = useControlledFieldStore.getState()).awaitValueUpdate) == null ? void 0 : _c.call(_b, formId, field));
2025
2055
  const { error } = await validator.validateField(new FormData(formElement), field);
2026
2056
  if (error) {
2027
2057
  get2().setFieldError(field, error);
@@ -2034,9 +2064,9 @@ const formStore = storeFamily((formId) => create$1()(immer((set2, get2, api) =>
2034
2064
  validate: async () => {
2035
2065
  var _a;
2036
2066
  const formElement = get2().formElement;
2037
- invariant(formElement);
2067
+ invariant(formElement, "Cannot find reference to form. This is probably a bug in remix-validated-form.");
2038
2068
  const validator = (_a = get2().formProps) == null ? void 0 : _a.validator;
2039
- invariant(validator);
2069
+ invariant(validator, "Cannot validator. This is probably a bug in remix-validated-form.");
2040
2070
  const { error } = await validator.validate(new FormData(formElement));
2041
2071
  if (error)
2042
2072
  get2().setFieldErrors(error.fieldErrors);
@@ -2045,14 +2075,28 @@ const formStore = storeFamily((formId) => create$1()(immer((set2, get2, api) =>
2045
2075
  var _a;
2046
2076
  return (_a = get2().formElement) == null ? void 0 : _a.reset();
2047
2077
  }
2048
- }))));
2078
+ });
2079
+ const useRootFormStore = create$1()(immer((set2, get2) => ({
2080
+ forms: {},
2081
+ form: (formId) => {
2082
+ var _a;
2083
+ return (_a = get2().forms[formId]) != null ? _a : defaultFormState;
2084
+ },
2085
+ cleanupForm: (formId) => {
2086
+ set2((state) => {
2087
+ delete state.forms[formId];
2088
+ });
2089
+ },
2090
+ registerForm: (formId) => {
2091
+ if (get2().forms[formId])
2092
+ return;
2093
+ set2((state) => {
2094
+ state.forms[formId] = createFormState(formId, (setter) => set2((state2) => setter(state2.forms[formId])), () => get2().forms[formId]);
2095
+ });
2096
+ }
2097
+ })));
2049
2098
  const useFormStore = (formId, selector) => {
2050
- const useStore2 = formStore(formId);
2051
- return useStore2(selector);
2052
- };
2053
- const useControlledFieldStore = (formId, selector) => {
2054
- const useStore2 = controlledFieldStore(formId);
2055
- return useStore2(selector);
2099
+ return useRootFormStore((state) => selector(state.form(formId)));
2056
2100
  };
2057
2101
  const useInternalFormContext = (formId, hookName) => {
2058
2102
  const formContext = useContext(InternalFormContext);
@@ -2106,7 +2150,7 @@ const useDefaultValuesForForm = (context) => {
2106
2150
  if (hydrated)
2107
2151
  return hydratable.hydratedData();
2108
2152
  if (errorResponse == null ? void 0 : errorResponse.repopulateFields) {
2109
- invariant(typeof errorResponse.repopulateFields === "object");
2153
+ invariant(typeof errorResponse.repopulateFields === "object", "repopulateFields returned something other than an object");
2110
2154
  return hydratable.serverData(errorResponse.repopulateFields);
2111
2155
  }
2112
2156
  if (defaultValuesProp)
@@ -2170,24 +2214,23 @@ const useFormSubactionProp = (formId) => useFormStore(formId, (state) => {
2170
2214
  return (_a = state.formProps) == null ? void 0 : _a.subaction;
2171
2215
  });
2172
2216
  const useControlledFieldValue = (context, field) => {
2173
- const useValueStore = controlledFieldStore(context.formId);
2174
- const value = useValueStore((state) => {
2217
+ const value = useControlledFieldStore((state) => {
2175
2218
  var _a;
2176
- return (_a = state.fields[field]) == null ? void 0 : _a.value;
2219
+ return (_a = state.getField(context.formId, field)) == null ? void 0 : _a.value;
2177
2220
  });
2178
- const useFormStore2 = formStore(context.formId);
2179
- const isFormHydrated = useFormStore2((state) => state.isHydrated);
2221
+ const isFormHydrated = useFormStore(context.formId, (state) => state.isHydrated);
2180
2222
  const defaultValue = useFieldDefaultValue(field, context);
2181
- const isFieldHydrated = useValueStore((state) => {
2223
+ const isFieldHydrated = useControlledFieldStore((state) => {
2182
2224
  var _a, _b;
2183
- return (_b = (_a = state.fields[field]) == null ? void 0 : _a.hydrated) != null ? _b : false;
2225
+ return (_b = (_a = state.getField(context.formId, field)) == null ? void 0 : _a.hydrated) != null ? _b : false;
2184
2226
  });
2185
- const hydrateWithDefault = useValueStore((state) => state.hydrateWithDefault);
2227
+ const hydrateWithDefault = useControlledFieldStore((state) => state.hydrateWithDefault);
2186
2228
  useEffect(() => {
2187
2229
  if (isFormHydrated && !isFieldHydrated) {
2188
- hydrateWithDefault(field, defaultValue);
2230
+ hydrateWithDefault(context.formId, field, defaultValue);
2189
2231
  }
2190
2232
  }, [
2233
+ context.formId,
2191
2234
  defaultValue,
2192
2235
  field,
2193
2236
  hydrateWithDefault,
@@ -2197,28 +2240,27 @@ const useControlledFieldValue = (context, field) => {
2197
2240
  return isFieldHydrated ? value : defaultValue;
2198
2241
  };
2199
2242
  const useControllableValue = (context, field) => {
2200
- const useValueStore = controlledFieldStore(context.formId);
2201
- const resolveUpdate = useValueStore((state) => {
2243
+ const resolveUpdate = useControlledFieldStore((state) => {
2202
2244
  var _a;
2203
- return (_a = state.fields[field]) == null ? void 0 : _a.resolveValueUpdate;
2245
+ return (_a = state.getField(context.formId, field)) == null ? void 0 : _a.resolveValueUpdate;
2204
2246
  });
2205
2247
  useEffect(() => {
2206
2248
  resolveUpdate == null ? void 0 : resolveUpdate();
2207
2249
  }, [resolveUpdate]);
2208
- const register = useValueStore((state) => state.register);
2209
- const unregister = useValueStore((state) => state.unregister);
2250
+ const register = useControlledFieldStore((state) => state.register);
2251
+ const unregister = useControlledFieldStore((state) => state.unregister);
2210
2252
  useEffect(() => {
2211
- register(field);
2212
- return () => unregister(field);
2253
+ register(context.formId, field);
2254
+ return () => unregister(context.formId, field);
2213
2255
  }, [context.formId, field, register, unregister]);
2214
- const setControlledFieldValue = useValueStore((state) => state.setValue);
2215
- const setValue = useCallback((value2) => setControlledFieldValue(field, value2), [field, setControlledFieldValue]);
2256
+ const setControlledFieldValue = useControlledFieldStore((state) => state.setValue);
2257
+ const setValue = useCallback((value2) => setControlledFieldValue(context.formId, field, value2), [context.formId, field, setControlledFieldValue]);
2216
2258
  const value = useControlledFieldValue(context, field);
2217
2259
  return [value, setValue];
2218
2260
  };
2219
2261
  const useUpdateControllableValue = (formId) => {
2220
- const useValueStore = controlledFieldStore(formId);
2221
- return useValueStore((state) => state.setValue);
2262
+ const setValue = useControlledFieldStore((state) => state.setValue);
2263
+ return useCallback((field, value) => setValue(formId, field, value), [formId, setValue]);
2222
2264
  };
2223
2265
  const useIsSubmitting = (formId) => {
2224
2266
  const formContext = useInternalFormContext(formId, "useIsSubmitting");
@@ -2437,10 +2479,6 @@ const useMultiValueMap = () => {
2437
2479
  return ref.current;
2438
2480
  }, []);
2439
2481
  };
2440
- const cleanupFormState = (formId) => {
2441
- formStore.remove(formId);
2442
- controlledFieldStore.remove(formId);
2443
- };
2444
2482
  function useSubmitComplete(isSubmitting, callback) {
2445
2483
  const isPending = useRef(false);
2446
2484
  useEffect(() => {
@@ -2604,16 +2642,13 @@ function ValidatedForm(_a) {
2604
2642
  const setFieldErrors = useSetFieldErrors(formId);
2605
2643
  const setFieldError = useFormStore(formId, (state) => state.setFieldError);
2606
2644
  const reset = useFormStore(formId, (state) => state.reset);
2607
- const resetControlledFields = useControlledFieldStore(formId, (state) => state.reset);
2645
+ const resetControlledFields = useControlledFieldStore((state) => state.reset);
2608
2646
  const startSubmit = useFormStore(formId, (state) => state.startSubmit);
2609
2647
  const endSubmit = useFormStore(formId, (state) => state.endSubmit);
2610
2648
  const syncFormProps = useFormStore(formId, (state) => state.syncFormProps);
2611
- const setHydrated = useFormStore(formId, (state) => state.setHydrated);
2612
2649
  const setFormElementInState = useFormStore(formId, (state) => state.setFormElement);
2613
- useEffect(() => {
2614
- setHydrated();
2615
- return () => cleanupFormState(formId);
2616
- }, [formId, setHydrated]);
2650
+ const cleanupForm = useRootFormStore((state) => state.cleanupForm);
2651
+ const registerForm = useRootFormStore((state) => state.registerForm);
2617
2652
  const customFocusHandlers = useMultiValueMap();
2618
2653
  const registerReceiveFocus = useCallback((fieldName, handler) => {
2619
2654
  customFocusHandlers().add(fieldName, handler);
@@ -2621,6 +2656,10 @@ function ValidatedForm(_a) {
2621
2656
  customFocusHandlers().remove(fieldName, handler);
2622
2657
  };
2623
2658
  }, [customFocusHandlers]);
2659
+ useIsomorphicLayoutEffect(() => {
2660
+ registerForm(formId);
2661
+ return () => cleanupForm(formId);
2662
+ }, [cleanupForm, formId, registerForm]);
2624
2663
  useIsomorphicLayoutEffect(() => {
2625
2664
  var _a3;
2626
2665
  syncFormProps({
@@ -2639,6 +2678,9 @@ function ValidatedForm(_a) {
2639
2678
  backendDefaultValues,
2640
2679
  validator
2641
2680
  ]);
2681
+ useIsomorphicLayoutEffect(() => {
2682
+ setFormElementInState(formRef.current);
2683
+ }, [setFormElementInState]);
2642
2684
  useEffect(() => {
2643
2685
  var _a3;
2644
2686
  setFieldErrors((_a3 = backendError == null ? void 0 : backendError.fieldErrors) != null ? _a3 : {});
@@ -2680,18 +2722,16 @@ function ValidatedForm(_a) {
2680
2722
  endSubmit();
2681
2723
  return;
2682
2724
  }
2725
+ const submitter = e2.nativeEvent.submitter;
2683
2726
  if (fetcher)
2684
- fetcher.submit(clickedButtonRef.current || formRef.current);
2727
+ fetcher.submit(submitter || e2.currentTarget);
2685
2728
  else
2686
- submit(clickedButtonRef.current || formRef.current, {
2687
- method,
2688
- replace
2689
- });
2729
+ submit(submitter || e2.currentTarget, { method, replace });
2690
2730
  clickedButtonRef.current = null;
2691
2731
  }
2692
2732
  };
2693
2733
  return /* @__PURE__ */ React.createElement(Form$1, __spreadProps(__spreadValues({
2694
- ref: mergeRefs([formRef, formRefProp, setFormElementInState])
2734
+ ref: mergeRefs([formRef, formRefProp])
2695
2735
  }, rest), {
2696
2736
  id,
2697
2737
  action,
@@ -2706,7 +2746,7 @@ function ValidatedForm(_a) {
2706
2746
  if (event.defaultPrevented)
2707
2747
  return;
2708
2748
  reset();
2709
- resetControlledFields();
2749
+ resetControlledFields(formId);
2710
2750
  }
2711
2751
  }), /* @__PURE__ */ React.createElement(InternalFormContext.Provider, {
2712
2752
  value: contextValue
@@ -2833,7 +2873,7 @@ const useFormContext = (formId) => {
2833
2873
  internalClearError(name);
2834
2874
  });
2835
2875
  }, [internalClearError]);
2836
- return __spreadProps(__spreadValues({}, state), {
2876
+ return useMemo(() => __spreadProps(__spreadValues({}, state), {
2837
2877
  setFieldTouched: setTouched,
2838
2878
  validateField,
2839
2879
  clearError,
@@ -2841,6 +2881,16 @@ const useFormContext = (formId) => {
2841
2881
  clearAllErrors,
2842
2882
  validate,
2843
2883
  reset
2844
- });
2884
+ }), [
2885
+ clearAllErrors,
2886
+ clearError,
2887
+ registerReceiveFocus,
2888
+ reset,
2889
+ setTouched,
2890
+ state,
2891
+ validate,
2892
+ validateField
2893
+ ]);
2845
2894
  };
2846
2895
  export { ValidatedForm, createValidator, setFormDefaults, useControlField, useField, useFormContext, useIsSubmitting, useIsValid, useUpdateControlledField, validationError };
2896
+ //# sourceMappingURL=remix-validated-form.es.js.map