@paragrav/rhf-utils 0.0.120 → 0.0.124

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 (34) hide show
  1. package/README.md +45 -28
  2. package/dist/esm/errors/flat/context/FlatFieldErrorsContextProvider.mjs +19 -15
  3. package/dist/esm/errors/flat/context/useFlatFieldErrorsContextHasOnlyOrphans.mjs +5 -6
  4. package/dist/esm/errors/flat/context/useFlatFieldErrorsContextOutput.mjs +17 -20
  5. package/dist/esm/exports.mjs +25 -23
  6. package/dist/esm/form/Form.mjs +61 -57
  7. package/dist/esm/form/context/utils/RhfUtilsContextProvider.mjs +22 -23
  8. package/dist/esm/form/utils/useShouldFormBlockNavigation.mjs +9 -0
  9. package/dist/esm/submit/useFormOnSubmitSuccessfulAndReset.mjs +7 -9
  10. package/dist/esm/submit/useFormOnSubmitted.mjs +12 -0
  11. package/dist/esm/submit/useResetFormOnSubmitted.mjs +23 -0
  12. package/dist/esm/submit/useSubmitFormOnChange.mjs +8 -8
  13. package/dist/esm/utils/PassthroughChildren.mjs +7 -0
  14. package/dist/esm/utils/useRefIfValueWasTrue.mjs +14 -0
  15. package/dist/types/client/config/RhfUtilsClientConfig.d.ts +1 -1
  16. package/dist/types/errors/flat/context/FlatFieldErrorsOutputConfig.d.ts +4 -3
  17. package/dist/types/errors/flat/context/useFlatFieldErrorsContext.d.ts +2 -0
  18. package/dist/types/errors/output/consoleErrors.d.ts +1 -2
  19. package/dist/types/errors/output/types.d.ts +0 -7
  20. package/dist/types/exports.d.ts +1 -0
  21. package/dist/types/form/RhfUtilsFormOptions.d.ts +5 -7
  22. package/dist/types/form/UseRhfUtilsFormProps.d.ts +2 -1
  23. package/dist/types/form/utils/useShouldFormBlockNavigation.d.ts +13 -0
  24. package/dist/types/submit/useFormOnSubmitSuccessfulAndReset.d.ts +4 -2
  25. package/dist/types/submit/useFormOnSubmitted.d.ts +6 -0
  26. package/dist/types/submit/useResetFormOnSubmitted.d.ts +23 -0
  27. package/dist/types/submit/useSubmitFormOnChange.d.ts +1 -0
  28. package/dist/types/utils/PassthroughChildren.d.ts +8 -0
  29. package/dist/types/utils/types.d.ts +1 -0
  30. package/dist/types/utils/useRefIfValueWasTrue.d.ts +1 -0
  31. package/package.json +1 -1
  32. package/dist/esm/submit/useFormOnSubmitSuccessful.mjs +0 -11
  33. package/dist/esm/submit/useResetFormOnSubmitError.mjs +0 -18
  34. package/dist/esm/submit/useResetFormOnSubmitSuccessful.mjs +0 -20
package/README.md CHANGED
@@ -18,7 +18,7 @@ To configure, create a file `config.tsx` with desired configuration settings. Th
18
18
 
19
19
  ```tsx
20
20
  export const rhfUtilsClientConfig: RhfUtilsClientConfig = {
21
- // overridable per form instance
21
+ // overridable at individual form level
22
22
  defaults: {
23
23
  // globally-relevant subset of RHF's `UseFormProps` options
24
24
  rhf: {
@@ -43,7 +43,6 @@ export const rhfUtilsClientConfig: RhfUtilsClientConfig = {
43
43
 
44
44
  // optional wrapper to inject your own hooks and components
45
45
  // around all RhfUtilsZodForm instances
46
- // (see further below for more info)
47
46
  FormChildren: (
48
47
  // UseRhfUtilsFormChildrenProps
49
48
  {
@@ -56,7 +55,8 @@ export const rhfUtilsClientConfig: RhfUtilsClientConfig = {
56
55
  children, // RhfUtilsZodForm's Children instance
57
56
  },
58
57
  ) => {
59
- // your navigation blocker (using custom option prop -- see "Extend RhfUtilsFormOptions" section)
58
+ // your own hooks/behaviors
59
+ // (using custom option prop -- see "Extend RhfUtilsFormOptions" section)
60
60
  useMyFormNavigationPrompt(!!options?.enableMyFormNavigationPrompt);
61
61
 
62
62
  return (
@@ -82,15 +82,22 @@ export const rhfUtilsClientConfig: RhfUtilsClientConfig = {
82
82
  return transformServerErrorToFormSubmitErrors(error);
83
83
  },
84
84
 
85
- errors: {
85
+ // form state field errors
86
+ fieldErrors: {
87
+ // callbacks to determine when to output information about field errors
88
+ // provided `FlatFieldErrorsContext` (all, fields, roots, orphans, hasOrphans)
86
89
  output: {
87
- // callback to determine when to output form context errors to console ("debug" or "error")
90
+ // determine when to console ("debug" or "error")
88
91
  console: ({ hasOrphans }) =>
89
- (hasOrphans && { type: 'error' }) || // console error for reporting on prod
90
- (import.meta.env.DEV && { type: 'debug' }), // facilitate debugging on dev
91
-
92
- // callback to determine when to throw an error based on context
93
- throw: ({ hasOrphans }) => import.meta.env.DEV && hasOrphans,
92
+ // console error for reporting on prod
93
+ (hasOrphans && { type: 'error' }) ||
94
+ // facilitate local debugging of form validation
95
+ (import.meta.env.DEV && { type: 'debug' }),
96
+
97
+ // callback to determine when to throw an error based on field errors
98
+ throw: ({ hasOrphans }) =>
99
+ // bring attention to orphans locally
100
+ import.meta.env.DEV && hasOrphans,
94
101
  },
95
102
  },
96
103
  };
@@ -122,7 +129,7 @@ Currently, only `zod` is supported.
122
129
  context, // UseRhfUtilsFormOnSubmitContext (id, ref, rhf context, utils options, strictly-typed FormSubmitError class)
123
130
  event, // SubmitEvent
124
131
  ) => {
125
- await authLoginService(data);
132
+ await loginService(data);
126
133
  props.onSuccess();
127
134
  }}
128
135
  // handle error declaratively (i.e., no throw/catch)
@@ -150,6 +157,7 @@ Currently, only `zod` is supported.
150
157
  <label>
151
158
  Email
152
159
  <input {...field} disabled={isSubmitting} />
160
+ <FormErrorMessageByPath path={field.name} />
153
161
  </label>
154
162
  )}
155
163
  />
@@ -157,14 +165,16 @@ Currently, only `zod` is supported.
157
165
  <button type="submit">Login</button>
158
166
  </>
159
167
  )}
160
- // options/overrides
168
+ // form-specific options/overrides
161
169
  rhf={{
162
170
  mode: 'onBlur',
163
171
  }}
164
172
  utils={{
165
173
  submitOnChange: true,
166
174
  resetValuesOnSubmitError: true,
167
- enableMyFormNavigationPrompt: true, // custom option prop (see "Extend RhfUtilsFormOptions" section)
175
+ // custom option props
176
+ // (see "Extend RhfUtilsFormOptions" section)
177
+ enableMyFormNavigationPrompt: true,
168
178
  }}
169
179
  form={{
170
180
  // class names are merged together with global defaults
@@ -173,7 +183,17 @@ Currently, only `zod` is supported.
173
183
  />
174
184
  ```
175
185
 
176
- `Children` is a component which is rendered as a child of the form. It receives `UseRhfUtilsFormChildrenProps` as props, including type-safe `Controller`.
186
+ `Children` prop takes a component which is rendered as a child of the form (and, if supplied, of `RhfUtilsClientConfig.FormChildren`). It receives `UseRhfUtilsFormChildrenProps` as props, including a type-safe `Controller` component.
187
+
188
+ The component hierarchy is:
189
+
190
+ ```tsx
191
+ <RhfUtilsClientConfig.FormComponent>
192
+ <RhfUtilsClientConfig.FormChildren>
193
+ <RhfUtilsZodForm.Children />
194
+ </RhfUtilsClientConfig.FormChildren>
195
+ </RhfUtilsClientConfig.FormComponent>
196
+ ```
177
197
 
178
198
  If you prefer to define your `Children` component as standalone:
179
199
 
@@ -215,17 +235,14 @@ These options can be set globally and/or per form.
215
235
 
216
236
  ```ts
217
237
  type RhfUtilsFormOptions = {
218
- /** Request submit via listener on form change. */
219
- submitOnChange?: boolean;
220
-
221
- /** Reset form values if submit throw errors. (Use case: optimistic UI toggle form that submits on each change.) */
222
- resetValuesOnSubmitError?: boolean;
223
-
224
238
  /** Stop propagation of submit event. Useful for portals. */
225
239
  stopSubmitPropagation?: boolean;
226
240
 
227
- /** Reset form after successful submit. */
228
- resetFormAfterSubmitSuccessful?: UseResetFormAfterSubmitSuccessfulOptions;
241
+ /** Request submit via listener on form change. */
242
+ submitOnChange?: boolean;
243
+
244
+ /** Reset form after submitted. */
245
+ resetFormOnSubmitted?: UseResetFormOnSubmittedOptions;
229
246
 
230
247
  /** Control dev tool options. (Lazy-loaded when truthy value supplied.) */
231
248
  devTool?: true | Pick<DevtoolUIProps, 'placement' | 'styles'>;
@@ -234,11 +251,11 @@ type RhfUtilsFormOptions = {
234
251
 
235
252
  If you need access to options deeper in component structure, use `useRhfUtilsContext` to receive `RhfUtilsContext` object, which includes `formId`, `formRef`, and `options` settings.
236
253
 
237
- Use `useRhfUtilsContextRequestSubmit` to get `requestSubmit` fn for current form ref in context. This is useful when you need to trigger form submission programatically.
254
+ Use `useRhfUtilsContextRequestSubmit` hook to get `requestSubmit` function for current form ref in context. This is useful when you need to trigger form submission programatically.
238
255
 
239
256
  ## Extend `RhfUtilsFormOptions`
240
257
 
241
- Extend `RhfUtilsFormOptions` with custom options, which get passed to `Children` component in `options` prop.
258
+ Extend `RhfUtilsFormOptions` with custom options, which get passed to `Children` component in `options` prop. These can take any shape, and allow you to override your own functionality at form-level.
242
259
 
243
260
  ```tsx
244
261
  import '@paragrav/rhf-utils';
@@ -257,7 +274,7 @@ declare module '@paragrav/rhf-utils' {
257
274
 
258
275
  You can configure via `RhfUtilsClientConfig` (example at the top) when form context errors are outputted -- i.e., via console and/or thrown error.
259
276
 
260
- Use `useFlatFieldErrorsContext()` hook, which returns an object with errors grouped by `all`, `fields`, `root`, and `orphans`.
277
+ Use `useFlatFieldErrorsContext()` hook, which returns an object with errors grouped by `all`, `fields`, `roots`, `orphans` records, and `hasErrors` and `hasErrors` and `hasOrphans` booleans.
261
278
 
262
279
  ### Orphans
263
280
 
@@ -269,7 +286,7 @@ The criteria for an orphan is any form context error that meets all of the follo
269
286
  - has no `ref` -- RHF includes `ref` to the associated input on each error object (when applicable)
270
287
  - has no marker in DOM (e.g., `FormNonFieldErrorMarker`)
271
288
 
272
- To get accurate orphan analysis, you must either use `FormNonFieldErrorMarker` in any error you are displaying to user which is not "root" and doesn't have a `ref`. (There is no harm in using this consistently across all errors, even those expected to have a ref.)
289
+ To get accurate orphan analysis, you must either use `FormNonFieldErrorMarker` in any error you are displaying to user that is not "root" and doesn't have a `ref`. (There is no harm in using this consistently across all errors, even those expected to have a ref.)
273
290
 
274
291
  Alternatively, you can use `FormErrorMessageByPath` to display error message to user:
275
292
 
@@ -277,12 +294,12 @@ Alternatively, you can use `FormErrorMessageByPath` to display error message to
277
294
  <FormErrorMessageByPath path="street.address" />
278
295
  ```
279
296
 
280
- Example use case: Field array with minimum items. (When there are no items, the error is not associated with a field and is displayed separately.)
297
+ Example use case: Field array with minimum items. When there are no items, the error is not associated with a field and is displayed separately.
281
298
 
282
299
  Orphans are exposed in a few places.
283
300
 
284
301
  - In errors outputted via console. (Configurable via `RhfUtilsClientConfig['errors']['output']`.)
285
- - And `useFlatFieldErrorsContext()` hook, which returns an object with errors grouped by `all`, `fields`, `root`, and `orphans`.
302
+ - And `useFlatFieldErrorsContext()` hook, which returns an object with errors grouped by `all`, `fields`, `roots`, `orphans` records, and `hasErrors` and `hasOrphans` boolean.
286
303
  - Boolean value from `useFlatFieldErrorsContextHasOnlyOrphans`.
287
304
 
288
305
  `FlatFieldErrors` is a flattened, simplified version of RHF's `FieldErrors`. Keys represent field paths flattened to dot notation.
@@ -1,23 +1,27 @@
1
- import { jsx as s } from "react/jsx-runtime";
1
+ import { jsx as a } from "react/jsx-runtime";
2
2
  import "react";
3
- import { useFormState as i } from "react-hook-form";
4
- import l from "../../getRefdFromFlatFieldErrors.mjs";
5
- import F from "../../orphan/getOrphansFromFlatFieldErrors.mjs";
6
- import a from "../../root/getRootsFromFlatFieldErrors.mjs";
7
- import n from "../getFlatFieldErrors.mjs";
8
- import { _FormErrorsFlatContextProvider as p } from "./useFlatFieldErrorsContext.mjs";
9
- const h = ({
3
+ import { useFormState as l } from "react-hook-form";
4
+ import F from "../../getRefdFromFlatFieldErrors.mjs";
5
+ import e from "../../../utils/isEmptyObject.mjs";
6
+ import p from "../../orphan/getOrphansFromFlatFieldErrors.mjs";
7
+ import n from "../../root/getRootsFromFlatFieldErrors.mjs";
8
+ import d from "../getFlatFieldErrors.mjs";
9
+ import { _FormErrorsFlatContextProvider as f } from "./useFlatFieldErrorsContext.mjs";
10
+ const C = ({
10
11
  formRef: o,
11
- children: t
12
+ children: m
12
13
  }) => {
13
- const { errors: e } = i(), r = n(e), m = {
14
+ const { errors: t } = l(), r = d(t), s = o.current ? p(r, o.current) : {}, i = {
14
15
  all: r,
15
- fields: l(r),
16
- roots: a(r),
17
- orphans: o.current ? F(r, o.current) : {}
16
+ fields: F(r),
17
+ roots: n(r),
18
+ orphans: s,
19
+ // computed
20
+ hasErrors: !e(t),
21
+ hasOrphans: !e(s)
18
22
  };
19
- return /* @__PURE__ */ s(p, { value: m, children: t });
23
+ return /* @__PURE__ */ a(f, { value: i, children: m });
20
24
  };
21
25
  export {
22
- h as default
26
+ C as default
23
27
  };
@@ -1,10 +1,9 @@
1
- import t from "react";
2
- import s from "../../../utils/isEmptyObject.mjs";
1
+ import s from "react";
3
2
  import e from "./useFlatFieldErrorsContext.mjs";
4
- const n = () => {
3
+ const t = () => {
5
4
  const r = e();
6
- return t.useMemo(() => s(r.orphans) ? !1 : Object.keys(r.orphans).length === Object.keys(r.all).length, [r]);
7
- }, O = n;
5
+ return s.useMemo(() => !r.hasErrors || !r.hasOrphans ? !1 : Object.keys(r.orphans).length === Object.keys(r.all).length, [r]);
6
+ }, l = t;
8
7
  export {
9
- O as default
8
+ l as default
10
9
  };
@@ -1,28 +1,25 @@
1
- import h from "react";
2
- import { useFormContext as E } from "react-hook-form";
3
- import l from "../../../utils/isEmptyObject.mjs";
4
- import F from "../../output/consoleErrors.mjs";
5
- import x from "./useFlatFieldErrorsContext.mjs";
6
- const n = (r) => {
7
- const u = E(), t = x();
8
- h.useEffect(() => {
9
- var m, a;
10
- if (l(t.all))
1
+ import u from "react";
2
+ import { useFormContext as p } from "react-hook-form";
3
+ import E from "../../output/consoleErrors.mjs";
4
+ import F from "./useFlatFieldErrorsContext.mjs";
5
+ const C = (r) => {
6
+ const l = p(), t = F();
7
+ u.useEffect(() => {
8
+ var o, m;
9
+ if (!t.hasErrors)
11
10
  return;
12
- const o = {
13
- hasOrphans: !l(t.orphans)
14
- }, e = (m = r == null ? void 0 : r.console) == null ? void 0 : m.call(r, o);
15
- e && F(
16
- e.message ?? p,
17
- u.getValues(),
11
+ const e = (o = r == null ? void 0 : r.console) == null ? void 0 : o.call(r, t);
12
+ e && E(
13
+ e.message ?? a,
14
+ l.getValues(),
18
15
  t,
19
16
  e.type
20
17
  );
21
- const s = (a = r == null ? void 0 : r.throw) == null ? void 0 : a.call(r, o);
18
+ const s = (m = r == null ? void 0 : r.throw) == null ? void 0 : m.call(r, t);
22
19
  if (s)
23
- throw new Error(typeof s == "string" ? s : p);
20
+ throw new Error(typeof s == "string" ? s : a);
24
21
  }, [t]);
25
- }, p = "Form errors";
22
+ }, a = "Form errors";
26
23
  export {
27
- n as default
24
+ C as default
28
25
  };
@@ -1,5 +1,5 @@
1
1
  import { default as e } from "./client/zod/context/RhfUtilsClientForZodContextProvider.mjs";
2
- import { default as s } from "./client/zod/context/RhfUtilsZodForm.mjs";
2
+ import { default as a } from "./client/zod/context/RhfUtilsZodForm.mjs";
3
3
  import { default as u } from "./client/zod/context/useRhfUtilsZodForm.mjs";
4
4
  import { default as m } from "./errors/flat/context/useFlatFieldErrorsContext.mjs";
5
5
  import { default as d } from "./errors/flat/context/useFlatFieldErrorsContextHasOnlyOrphans.mjs";
@@ -7,44 +7,46 @@ import { default as x } from "./errors/message/FormErrorMessageByPath.mjs";
7
7
  import { default as F } from "./errors/nonfield/FormNonFieldErrorMarker.mjs";
8
8
  import { default as C } from "./errors/root/RootErrorsListFromFlatFieldErrorsContext.mjs";
9
9
  import { getOnSubmitTrpcClientErrorHandler as E } from "./errors/trpc/getOnSubmitTrpcClientErrorHandler.mjs";
10
- import { trpcClientErrorToFormSubmitErrorsSchemaTransformer as R } from "./errors/trpc/trpcClientErrorToFormSubmitErrorsSchemaTransformer.mjs";
11
- import { default as b } from "./form/context/utils/useRhfUtilsContext.mjs";
10
+ import { trpcClientErrorToFormSubmitErrorsSchemaTransformer as c } from "./errors/trpc/trpcClientErrorToFormSubmitErrorsSchemaTransformer.mjs";
11
+ import { default as R } from "./form/context/utils/useRhfUtilsContext.mjs";
12
12
  import { default as y } from "./form/context/utils/useRhfUtilsContextRequestSubmit.mjs";
13
- import { default as B } from "./form/context/group/FormGroupContextProvider.mjs";
14
- import { default as P } from "./form/context/group/useFormGroupChildIsMountedTracker.mjs";
15
- import { default as U } from "./form/context/group/useFormGroupChildIsSubmittingTracker.mjs";
13
+ import { default as T } from "./form/context/group/FormGroupContextProvider.mjs";
14
+ import { default as I } from "./form/context/group/useFormGroupChildIsMountedTracker.mjs";
15
+ import { default as g } from "./form/context/group/useFormGroupChildIsSubmittingTracker.mjs";
16
16
  import { default as O } from "./form/context/group/useFormGroupChildTracker.mjs";
17
- import { default as Z } from "./form/context/group/useFormGroupIsAnyBusy.mjs";
18
- import { default as v } from "./form/context/group/useFormGroupIsChildBusy.mjs";
19
- import { default as A } from "./form/context/group/useFormGroupIsParentBusy.mjs";
20
- import { default as L } from "./form/context/group/useFormGroupParentTracker.mjs";
17
+ import { default as M } from "./form/context/group/useFormGroupIsAnyBusy.mjs";
18
+ import { default as q } from "./form/context/group/useFormGroupIsChildBusy.mjs";
19
+ import { default as N } from "./form/context/group/useFormGroupIsParentBusy.mjs";
20
+ import { default as D } from "./form/context/group/useFormGroupParentTracker.mjs";
21
21
  import { default as j } from "./form/context/group/useFormOrParentIsBusy.mjs";
22
22
  import { default as z } from "./form/utils/getSubmitterButtonData.mjs";
23
23
  import { default as K } from "./form/utils/useFormRequestSubmit.mjs";
24
- import { FormSubmitError as V } from "./submit/error/FormSubmitError.mjs";
24
+ import { default as V } from "./form/utils/useShouldFormBlockNavigation.mjs";
25
+ import { FormSubmitError as X } from "./submit/error/FormSubmitError.mjs";
25
26
  export {
26
27
  x as FormErrorMessageByPath,
27
- B as FormGroupContextProvider,
28
+ T as FormGroupContextProvider,
28
29
  F as FormNonFieldErrorMarker,
29
- V as FormSubmitError,
30
+ X as FormSubmitError,
30
31
  e as RhfUtilsClientForZodContextProvider,
31
- s as RhfUtilsZodForm,
32
+ a as RhfUtilsZodForm,
32
33
  C as RootErrorsListFromFlatFieldErrorsContext,
33
34
  E as getOnSubmitTrpcClientErrorHandler,
34
35
  z as getSubmitterButtonData,
35
- R as trpcClientErrorToFormSubmitErrorsSchemaTransformer,
36
+ c as trpcClientErrorToFormSubmitErrorsSchemaTransformer,
36
37
  m as useFlatFieldErrorsContext,
37
38
  d as useFlatFieldErrorsContextHasOnlyOrphans,
38
- P as useFormGroupChildIsMountedTracker,
39
- U as useFormGroupChildIsSubmittingTracker,
39
+ I as useFormGroupChildIsMountedTracker,
40
+ g as useFormGroupChildIsSubmittingTracker,
40
41
  O as useFormGroupChildTracker,
41
- Z as useFormGroupIsAnyBusy,
42
- v as useFormGroupIsChildBusy,
43
- A as useFormGroupIsParentBusy,
44
- L as useFormGroupParentTracker,
42
+ M as useFormGroupIsAnyBusy,
43
+ q as useFormGroupIsChildBusy,
44
+ N as useFormGroupIsParentBusy,
45
+ D as useFormGroupParentTracker,
45
46
  j as useFormOrParentIsBusy,
46
47
  K as useFormRequestSubmit,
47
- b as useRhfUtilsContext,
48
+ R as useRhfUtilsContext,
48
49
  y as useRhfUtilsContextRequestSubmit,
49
- u as useRhfUtilsZodForm
50
+ u as useRhfUtilsZodForm,
51
+ V as useShouldFormBlockNavigation
50
52
  };
@@ -1,81 +1,85 @@
1
- import { jsx as a, jsxs as v, Fragment as E } from "react/jsx-runtime";
1
+ import { jsx as h, jsxs as T } from "react/jsx-runtime";
2
2
  import "react";
3
- import { useFormContext as P } from "react-hook-form";
3
+ import { useFormContext as j } from "react-hook-form";
4
4
  import w from "../client/config/useRhfUtilsClientConfigContext.mjs";
5
- import S from "../devtool/LazyDevTool.mjs";
6
- import { FormSubmitError as h } from "../submit/error/FormSubmitError.mjs";
7
- import U from "../submit/error/setCtxErrorsByFormSubmitErrors.mjs";
5
+ import E from "../devtool/LazyDevTool.mjs";
6
+ import { FormSubmitError as s } from "../submit/error/FormSubmitError.mjs";
7
+ import S from "../submit/error/setCtxErrorsByFormSubmitErrors.mjs";
8
+ import U from "../utils/PassthroughChildren.mjs";
8
9
  import _ from "./_Controller.mjs";
9
10
  function G({
10
- formId: n,
11
- formRef: p,
11
+ formId: p,
12
+ formRef: m,
12
13
  //
13
- onSubmit: c,
14
+ onSubmit: a,
14
15
  onSubmitError: d,
15
- onSubmitInvalid: F,
16
- Children: i,
16
+ onSubmitInvalid: i,
17
+ Children: l,
17
18
  //
18
- form: l,
19
- utils: o
19
+ form: F,
20
+ utils: r
20
21
  }) {
21
- const e = P(), r = w(), y = async (t) => {
22
- o != null && o.stopSubmitPropagation && t.stopPropagation();
22
+ const n = j(), t = w(), y = async (o) => {
23
+ r != null && r.stopSubmitPropagation && o.stopPropagation();
23
24
  try {
24
- await e.handleSubmit(f, F)(t);
25
- } catch (m) {
26
- b(m, t);
25
+ await n.handleSubmit(f, i)(o);
26
+ } catch (e) {
27
+ b(e, o);
27
28
  }
28
- }, f = (t, m) => c == null ? void 0 : c(
29
- t,
29
+ }, f = (o, e) => a == null ? void 0 : a(
30
+ o,
30
31
  {
31
- formId: n,
32
- formRef: p,
33
- context: e,
34
- utils: o,
35
- FormSubmitError: h
32
+ formId: p,
33
+ formRef: m,
34
+ context: n,
35
+ utils: r,
36
+ FormSubmitError: s
36
37
  },
37
- m
38
- ), b = (t, m) => {
38
+ e
39
+ ), b = (o, e) => {
39
40
  var x;
40
- const C = t instanceof h ? (
41
+ const C = o instanceof s ? (
41
42
  // consumer can manually throw FormSubmitError (e.g., manual validation)
42
- t.errors
43
+ o.errors
43
44
  ) : (
44
45
  // if error is not FormSubmitError, consumer can provide global handler
45
- (x = r == null ? void 0 : r.onSubmitUnknownError) == null ? void 0 : x.call(r, t)
46
+ (x = t == null ? void 0 : t.onSubmitUnknownError) == null ? void 0 : x.call(t, o)
46
47
  );
47
- C && U(e, C), d == null || d(
48
- t,
48
+ C && S(n, C), d == null || d(
49
+ o,
49
50
  {
50
- formId: n,
51
- formRef: p,
52
- context: e,
53
- utils: o
51
+ formId: p,
52
+ formRef: m,
53
+ context: n,
54
+ utils: r
54
55
  },
55
- m
56
+ e
56
57
  );
57
- }, s = {
58
- formId: n,
59
- formRef: p,
60
- context: e,
61
- utils: o,
58
+ }, c = {
59
+ formId: p,
60
+ formRef: m,
61
+ context: n,
62
+ utils: r,
62
63
  Controller: _,
63
- FormSubmitError: h
64
- }, T = (r == null ? void 0 : r.FormComponent) ?? "form", j = (r == null ? void 0 : r.FormChildren) ?? k;
65
- return /* @__PURE__ */ a(T, { ...l, ref: p, onSubmit: y, children: /* @__PURE__ */ v(j, { ...s, children: [
66
- (o == null ? void 0 : o.devTool) && /* @__PURE__ */ a(
67
- S,
68
- {
69
- props: typeof o.devTool == "object" ? o.devTool : void 0
70
- }
71
- ),
72
- /* @__PURE__ */ a(i, { ...s })
73
- ] }) });
74
- }
75
- function k({
76
- children: n
77
- }) {
78
- return /* @__PURE__ */ a(E, { children: n });
64
+ FormSubmitError: s
65
+ }, v = (t == null ? void 0 : t.FormComponent) ?? "form", P = (t == null ? void 0 : t.FormChildren) ?? U;
66
+ return /* @__PURE__ */ h(
67
+ v,
68
+ {
69
+ ...F,
70
+ ref: m,
71
+ onSubmit: (o) => void y(o),
72
+ children: /* @__PURE__ */ T(P, { ...c, children: [
73
+ (r == null ? void 0 : r.devTool) && /* @__PURE__ */ h(
74
+ E,
75
+ {
76
+ props: typeof r.devTool == "object" ? r.devTool : void 0
77
+ }
78
+ ),
79
+ /* @__PURE__ */ h(l, { ...c })
80
+ ] })
81
+ }
82
+ );
79
83
  }
80
84
  export {
81
85
  G as default
@@ -1,30 +1,29 @@
1
- import { jsx as l } from "react/jsx-runtime";
2
- import C from "../../../client/config/useRhfUtilsClientConfigContext.mjs";
3
- import F from "../../../errors/flat/context/useFlatFieldErrorsContextOutput.mjs";
4
- import x from "../../../submit/useFormOnSubmitSuccessfulAndReset.mjs";
5
- import O from "../../../submit/useResetFormOnSubmitError.mjs";
6
- import b from "../../../submit/useResetFormOnSubmitSuccessful.mjs";
7
- import h from "../../../submit/useSubmitFormOnChange.mjs";
1
+ import { jsx as F } from "react/jsx-runtime";
2
+ import O from "../../../client/config/useRhfUtilsClientConfigContext.mjs";
3
+ import f from "../../../errors/flat/context/useFlatFieldErrorsContextOutput.mjs";
4
+ import h from "../../../submit/useFormOnSubmitSuccessfulAndReset.mjs";
5
+ import i from "../../../submit/useResetFormOnSubmitted.mjs";
6
+ import l from "../../../submit/useSubmitFormOnChange.mjs";
8
7
  import { _RhfUtilsContextProvider as d } from "./useRhfUtilsContext.mjs";
9
- const M = ({
10
- formId: a,
11
- formRef: t,
12
- onSubmitSuccessfulAndReset: e,
8
+ const b = ({
9
+ formId: C,
10
+ formRef: e,
11
+ onSubmitSuccessfulAndReset: t,
13
12
  options: r,
14
- children: f
13
+ children: x
15
14
  }) => {
16
- var u;
17
- const m = C();
18
- return F((u = m == null ? void 0 : m.errors) == null ? void 0 : u.output), h(t, {
15
+ var a;
16
+ const m = O();
17
+ return f((a = m == null ? void 0 : m.fieldErrors) == null ? void 0 : a.output), l(e, {
19
18
  disabled: !(r != null && r.submitOnChange)
20
- }), O({
21
- disabled: !(r != null && r.resetValuesOnSubmitError)
22
- }), b(
23
- (r == null ? void 0 : r.resetFormAfterSubmitSuccessful) || !!e
24
- ), x(() => {
25
- e == null || e();
26
- }), /* @__PURE__ */ l(d, { value: { formId: a, formRef: t, options: r }, children: f });
19
+ }), i(
20
+ (r == null ? void 0 : r.resetFormOnSubmitted) ?? (t && {
21
+ onSuccess: !!t
22
+ })
23
+ ), h(() => {
24
+ t == null || t();
25
+ }), /* @__PURE__ */ F(d, { value: { formId: C, formRef: e, options: r }, children: x });
27
26
  };
28
27
  export {
29
- M as default
28
+ b as default
30
29
  };
@@ -0,0 +1,9 @@
1
+ const s = (o, { isDirty: a, isSubmitting: t, isValid: l }) => (
2
+ // enabled
3
+ o && // dirty
4
+ a && // allow redirecting in submit handler
5
+ !(t && l)
6
+ ), u = s;
7
+ export {
8
+ u as default
9
+ };
@@ -1,13 +1,11 @@
1
- import r from "react";
2
- import { useFormState as f } from "react-hook-form";
3
- import c from "./useFormOnSubmitSuccessful.mjs";
1
+ import f from "react";
2
+ import { useFormState as i } from "react-hook-form";
3
+ import { useRefIfValueWasTrue as m } from "../utils/useRefIfValueWasTrue.mjs";
4
4
  const n = (t) => {
5
- const { isSubmitSuccessful: u, isDirty: s } = f(), e = r.useRef(!1);
6
- c(() => {
7
- e.current = !0;
8
- }), r.useEffect(() => {
9
- t && e.current && !u && !s && (t(), e.current = !1);
10
- }, [u, s]);
5
+ const { isDirty: s, isSubmitSuccessful: e } = i(), [u, r] = m(e);
6
+ f.useEffect(() => {
7
+ t && (s || !e && !u || (t(), r()));
8
+ }, [e, s]);
11
9
  };
12
10
  export {
13
11
  n as default
@@ -0,0 +1,12 @@
1
+ import u from "react";
2
+ import { useFormState as o } from "react-hook-form";
3
+ import { useRefIfValueWasTrue as m } from "../utils/useRefIfValueWasTrue.mjs";
4
+ const c = (e, f) => {
5
+ const { isSubmitSuccessful: i, isSubmitting: t } = o(), [r, s] = m(t);
6
+ u.useEffect(() => {
7
+ e && (t || r && (s(), e(i)));
8
+ }, [t]);
9
+ };
10
+ export {
11
+ c as default
12
+ };
@@ -0,0 +1,23 @@
1
+ import { useFormContext as m } from "react-hook-form";
2
+ import n from "./useFormOnSubmitted.mjs";
3
+ const S = (e) => {
4
+ const { reset: r, getValues: u } = m();
5
+ n((s) => {
6
+ if (!e)
7
+ return;
8
+ const t = e[s ? "onSuccess" : "onError"];
9
+ if (!t)
10
+ return;
11
+ const o = t === "current" ? u() : void 0;
12
+ r(o, {
13
+ // keep submit stuff for tracking relevant state (e.g., `useFormOnSubmitSuccessful`)
14
+ keepIsSubmitSuccessful: !0,
15
+ keepIsSubmitted: !0,
16
+ keepSubmitCount: !0,
17
+ keepErrors: !0
18
+ });
19
+ });
20
+ };
21
+ export {
22
+ S as default
23
+ };
@@ -1,15 +1,15 @@
1
- import m from "react";
2
- import a from "../form/utils/useFormRequestSubmit.mjs";
3
- const o = (t, e) => {
4
- const u = a(t);
5
- m.useEffect(() => {
6
- if (e != null && e.disabled)
1
+ import c from "react";
2
+ import m from "../form/utils/useFormRequestSubmit.mjs";
3
+ const d = (t, e) => {
4
+ const u = !(e != null && e.disabled), a = m(t);
5
+ c.useEffect(() => {
6
+ if (!u)
7
7
  return;
8
8
  const r = t.current;
9
9
  if (!r)
10
10
  throw new Error();
11
11
  const n = () => {
12
- u();
12
+ a();
13
13
  };
14
14
  return r.addEventListener("change", n), () => {
15
15
  r.removeEventListener("change", n);
@@ -17,5 +17,5 @@ const o = (t, e) => {
17
17
  }, []);
18
18
  };
19
19
  export {
20
- o as default
20
+ d as default
21
21
  };
@@ -0,0 +1,7 @@
1
+ import { jsx as t, Fragment as o } from "react/jsx-runtime";
2
+ const a = ({
3
+ children: r
4
+ }) => /* @__PURE__ */ t(o, { children: r });
5
+ export {
6
+ a as default
7
+ };
@@ -0,0 +1,14 @@
1
+ import t from "react";
2
+ const u = (r) => {
3
+ const e = t.useRef(!1);
4
+ t.useEffect(() => {
5
+ r && (e.current = !0);
6
+ }, [r]);
7
+ const s = () => {
8
+ e.current = !1;
9
+ };
10
+ return [e.current, s];
11
+ };
12
+ export {
13
+ u as useRefIfValueWasTrue
14
+ };
@@ -57,7 +57,7 @@ export type RhfUtilsClientConfig = {
57
57
  * - if `undefined`: do nothing.
58
58
  */
59
59
  onSubmitUnknownError?: (error: unknown) => FormSubmitErrors | undefined;
60
- errors?: {
60
+ fieldErrors?: {
61
61
  /**
62
62
  * Configure how/if errors should be outputted.
63
63
  */
@@ -1,4 +1,5 @@
1
- import { RhfUtilsErrorsOutputConfigContext, RhfUtilsErrorsOutputConsoleConfig } from '../../output/types';
1
+ import { RhfUtilsErrorsOutputConsoleConfig } from '../../output/types';
2
+ import { FlatFieldErrorsContext } from './useFlatFieldErrorsContext';
2
3
 
3
4
  export type FlatFieldErrorsOutputConfig = {
4
5
  /**
@@ -8,12 +9,12 @@ export type FlatFieldErrorsOutputConfig = {
8
9
  * - console.debug errors in development environment.
9
10
  * - console.error certain errors in production environment.
10
11
  */
11
- console?: (context: RhfUtilsErrorsOutputConfigContext) => RhfUtilsErrorsOutputConsoleConfig | null | false | undefined;
12
+ console?: (context: FlatFieldErrorsContext) => RhfUtilsErrorsOutputConsoleConfig | null | false | undefined;
12
13
  /**
13
14
  * Configure how/if errors should be thrown.
14
15
  *
15
16
  * Example use case:
16
17
  * - bring attention to certain errors in development environment.
17
18
  */
18
- throw?: (context: RhfUtilsErrorsOutputConfigContext) => true | string | false | undefined;
19
+ throw?: (context: FlatFieldErrorsContext) => true | string | false | undefined;
19
20
  };
@@ -5,6 +5,8 @@ export type FlatFieldErrorsContext = {
5
5
  fields: FlatFieldErrors;
6
6
  roots: FlatFieldErrors;
7
7
  orphans: FlatFieldErrors;
8
+ hasErrors: boolean;
9
+ hasOrphans: boolean;
8
10
  };
9
11
  declare const _FormErrorsFlatContextProvider: import('react').Provider<FlatFieldErrorsContext | undefined>, useFlatFieldErrorsContext: () => FlatFieldErrorsContext;
10
12
  export default useFlatFieldErrorsContext;
@@ -1,8 +1,7 @@
1
- import { FieldErrors } from 'react-hook-form';
2
1
  import { SafeFieldValues } from '../../form/SafeFieldValues';
3
2
 
4
3
  /**
5
4
  * Output form values and errors to console.
6
5
  */
7
- declare const consoleErrors: (message: string, values: SafeFieldValues, errors: FieldErrors, type?: 'debug' | 'error') => void;
6
+ declare const consoleErrors: (message: string, values: SafeFieldValues, errors: Record<string, unknown>, type?: 'debug' | 'error') => void;
8
7
  export default consoleErrors;
@@ -5,10 +5,3 @@ export type RhfUtilsErrorsOutputConsoleConfig = {
5
5
  type?: 'debug' | 'error';
6
6
  message?: string;
7
7
  };
8
- /**
9
- * Contextual data to determine whether to output error.
10
- */
11
- export type RhfUtilsErrorsOutputConfigContext = {
12
- isDevelopment?: boolean;
13
- hasOrphans?: boolean;
14
- };
@@ -28,4 +28,5 @@ export { default as useFormGroupParentTracker } from './form/context/group/useFo
28
28
  export { default as useFormOrParentIsBusy } from './form/context/group/useFormOrParentIsBusy';
29
29
  export { default as getSubmitterButtonData } from './form/utils/getSubmitterButtonData';
30
30
  export { default as useFormRequestSubmit } from './form/utils/useFormRequestSubmit';
31
+ export { default as useShouldFormBlockNavigation } from './form/utils/useShouldFormBlockNavigation';
31
32
  export { FormSubmitError } from './submit/error/FormSubmitError';
@@ -1,5 +1,5 @@
1
1
  import { DevtoolUIProps } from '@hookform/devtools/dist/devToolUI';
2
- import { UseResetFormAfterSubmitSuccessfulOptions } from '../submit/useResetFormOnSubmitSuccessful';
2
+ import { UseResetFormOnSubmittedOptions } from '../submit/useResetFormOnSubmitted';
3
3
  import { Register } from '../register';
4
4
 
5
5
  /**
@@ -8,14 +8,12 @@ import { Register } from '../register';
8
8
  * Can be extended using {@link Register.RhfUtilsFormOptions}.
9
9
  */
10
10
  export type RhfUtilsFormOptions = {
11
- /** Request submit via listener on form change. */
12
- submitOnChange?: boolean;
13
- /** Reset form values if submit throw errors. (Use case: optimistic UI toggle form that submits on each change.) */
14
- resetValuesOnSubmitError?: boolean;
15
11
  /** Stop propagation of submit event. Useful for portals. */
16
12
  stopSubmitPropagation?: boolean;
17
- /** Reset form after successful submit. */
18
- resetFormAfterSubmitSuccessful?: UseResetFormAfterSubmitSuccessfulOptions;
13
+ /** Request submit via listener on form change. */
14
+ submitOnChange?: boolean;
15
+ /** Reset form after submit. */
16
+ resetFormOnSubmitted?: UseResetFormOnSubmittedOptions;
19
17
  /**
20
18
  * Control dev tool options.
21
19
  * (Lazy-loaded when truthy value supplied.
@@ -1,3 +1,4 @@
1
+ import { MaybePromise } from '@trpc/server/unstable-core-do-not-import';
1
2
  import { default as React } from 'react';
2
3
  import { SubmitErrorHandler, UseFormProps } from 'react-hook-form';
3
4
  import { RhfUtilsFormOptions } from './RhfUtilsFormOptions';
@@ -7,7 +8,7 @@ import { UseRhfUtilsFormOnSubmitContext, UseRhfUtilsFormOnSubmitErrorContext } f
7
8
 
8
9
  export type UseRhfUtilsFormProps<TFieldValues extends SafeFieldValues, TTransformedValues extends SafeFieldValues> = {
9
10
  defaultValues?: UseFormProps<TFieldValues>['defaultValues'];
10
- onSubmit?: (values: TTransformedValues, context: UseRhfUtilsFormOnSubmitContext<TFieldValues, TTransformedValues>, event: React.BaseSyntheticEvent<SubmitEvent>) => unknown | Promise<unknown>;
11
+ onSubmit?: (values: TTransformedValues, context: UseRhfUtilsFormOnSubmitContext<TFieldValues, TTransformedValues>, event: React.BaseSyntheticEvent<SubmitEvent>) => MaybePromise<unknown>;
11
12
  /** Submit error handler. (Only for errors not of instance `FormError`, which are handled internally.) */
12
13
  onSubmitError?: (error: unknown, context: UseRhfUtilsFormOnSubmitErrorContext<TFieldValues, TTransformedValues>, event: React.BaseSyntheticEvent<SubmitEvent>) => void;
13
14
  /** Submit handler when form is invalid. */
@@ -0,0 +1,13 @@
1
+ import { FieldValues, FormState } from 'react-hook-form';
2
+
3
+ /**
4
+ * Returns `boolean` value when the form should block navigation.
5
+ *
6
+ * Evaluates to `true` when `enabled` and `isDirty` except when `isSubmitting` and `isValid`,
7
+ * because we don't want to block navigation when the form is submitting and valid.
8
+ *
9
+ * NOTE: If you need to block navigation during a potentially prolonged submission request,
10
+ * you will need to provide your own behaviors above and beyond this hook.
11
+ */
12
+ declare const useShouldFormBlockNavigation: (enabled: boolean, { isDirty, isSubmitting, isValid }: FormState<FieldValues>) => boolean;
13
+ export default useShouldFormBlockNavigation;
@@ -1,3 +1,5 @@
1
+ import { MaybePromise } from '@trpc/server/unstable-core-do-not-import';
2
+
1
3
  /**
2
4
  * Calls a callback after a successful submit and after a form reset.
3
5
  *
@@ -7,7 +9,7 @@
7
9
  * Using RHF and following its rules, we are not able to get the state `isSubmitSuccessful=true` and `isDirty=false`.
8
10
  * So, this has to keep track of previous state.
9
11
  *
10
- * SubmitSuccessfulAndReset = was `isSubmitSuccessful` and now `!isSubmitSuccessful && !isDirty`
12
+ * SubmitSuccessfulAndReset = was `isSubmitSuccessful` and now `!isDirty`
11
13
  */
12
- declare const useFormOnSubmitSuccessfulAndReset: (callback?: () => void) => void;
14
+ declare const useFormOnSubmitSuccessfulAndReset: (callback?: () => MaybePromise<unknown>) => void;
13
15
  export default useFormOnSubmitSuccessfulAndReset;
@@ -0,0 +1,6 @@
1
+ import { MaybePromise } from '@trpc/server/unstable-core-do-not-import';
2
+
3
+ declare const useFormOnSubmitted: (callback?: (success: boolean) => MaybePromise<unknown>, options?: {
4
+ successful?: boolean;
5
+ }) => void;
6
+ export default useFormOnSubmitted;
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Configuration for hook.
3
+ * - `undefined`: disabled
4
+ */
5
+ export type UseResetFormOnSubmittedOptions = undefined | {
6
+ /**
7
+ * Reset values on submit successful.
8
+ * - `true`: reset to default values
9
+ * - `'current'`: reset to current values
10
+ */
11
+ onSuccess?: true | 'current';
12
+ /**
13
+ * Reset values on submit error. (reset to default values)
14
+ */
15
+ onError?: true;
16
+ };
17
+ /**
18
+ * Handle resetting after successful submit with no server validation errors.
19
+ * See `Rules` section: https://react-hook-form.com/api/useform/reset/
20
+ * Specifically: "- It's recommended to reset inside useEffect after submission."
21
+ */
22
+ declare const useResetFormOnSubmitted: (options: UseResetFormOnSubmittedOptions) => void;
23
+ export default useResetFormOnSubmitted;
@@ -1,6 +1,7 @@
1
1
  import { default as React } from 'react';
2
2
 
3
3
  declare const useSubmitFormOnChange: (formRef: React.RefObject<HTMLFormElement>, options?: {
4
+ /** Disable-able so that default behavior is enabled for sake of simplicity of usage. */
4
5
  disabled?: boolean;
5
6
  }) => void;
6
7
  export default useSubmitFormOnChange;
@@ -0,0 +1,8 @@
1
+ /// <reference types="react" />
2
+ /**
3
+ * Component for simply passing through children.
4
+ *
5
+ * (Useful for conditional wrapping of children.)
6
+ */
7
+ declare const PassthroughChildren: React.FC<React.PropsWithChildren>;
8
+ export default PassthroughChildren;
@@ -0,0 +1 @@
1
+ export type MaybePromise<T> = T | Promise<T>;
@@ -0,0 +1 @@
1
+ export declare const useRefIfValueWasTrue: (value: boolean) => readonly [boolean, () => void];
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@paragrav/rhf-utils",
3
3
  "author": "paragrav.dev",
4
4
  "license": "MIT",
5
- "version": "0.0.120",
5
+ "version": "0.0.124",
6
6
  "description": "Integration utilities for react-hook-form.",
7
7
  "type": "module",
8
8
  "sideEffects": false,
@@ -1,11 +0,0 @@
1
- import s from "react";
2
- import { useFormState as u } from "react-hook-form";
3
- const r = (t) => {
4
- const { isSubmitSuccessful: e } = u();
5
- s.useEffect(() => {
6
- t && e && t();
7
- }, [t, e]);
8
- };
9
- export {
10
- r as default
11
- };
@@ -1,18 +0,0 @@
1
- import f from "react";
2
- import { useFormContext as n, useFormState as c } from "react-hook-form";
3
- const a = (t) => {
4
- const e = !!(t != null && t.disabled), { reset: r } = n(), { isSubmitting: u, isSubmitSuccessful: s, isSubmitted: i, submitCount: m } = c();
5
- f.useEffect(() => {
6
- e || u || s || i && m !== 0 && r();
7
- }, [
8
- e,
9
- u,
10
- s,
11
- i,
12
- m,
13
- r
14
- ]);
15
- };
16
- export {
17
- a as default
18
- };
@@ -1,20 +0,0 @@
1
- import { useFormContext as a } from "react-hook-form";
2
- import l from "./useFormOnSubmitSuccessful.mjs";
3
- const f = (e) => {
4
- const { reset: u, getValues: t } = a();
5
- l(() => {
6
- if (!e)
7
- return;
8
- const r = e === !0 || e.values === void 0 ? void 0 : e.values.resetToCurrentValues ? t() : e.values, s = e === !0 ? (
9
- // default
10
- void 0
11
- ) : (
12
- // custom settings
13
- e.state
14
- );
15
- u(r, s);
16
- });
17
- };
18
- export {
19
- f as default
20
- };