@paragrav/rhf-utils 0.0.110 → 0.0.112

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -34,7 +34,7 @@ export const rhfUtilsClientConfig: RhfUtilsClientConfig = {
34
34
  FormComponent: Form.Root,
35
35
 
36
36
  // transform backend error
37
- // use `getOnSubmitTrpcClientErrorHandler` for TRPC backends
37
+ // use `getOnSubmitTrpcClientErrorHandler` HOF for TRPC backends
38
38
  onSubmitError: (
39
39
  error, // unknown
40
40
  ) => {
@@ -117,7 +117,7 @@ Currently, only `zod` is supported.
117
117
  }) => {
118
118
  onError(error);
119
119
  }}
120
- // children/fields
120
+ // fields
121
121
  Children={({
122
122
  // UseRhfUtilsFormChildrenProps
123
123
  formId, // unique id string
@@ -130,13 +130,10 @@ Currently, only `zod` is supported.
130
130
  <Controller
131
131
  name="email" // strongly-typed field name
132
132
  render={({ field, formState: { isSubmitting } }) => (
133
- <input
134
- autoFocus
135
- label="Email"
136
- autoComplete="email"
137
- disabled={isSubmitting}
138
- {...field}
139
- />
133
+ <label>
134
+ Email
135
+ <input {...field} disabled={isSubmitting} />
136
+ </label>
140
137
  )}
141
138
  />
142
139
 
@@ -151,7 +148,9 @@ Currently, only `zod` is supported.
151
148
  If you prefer to define your `Children` component as standalone:
152
149
 
153
150
  ```tsx
154
- const Children: RhfUtilsUseFormChildrenZodFC<typeof schema> = () => {};
151
+ const Children: RhfUtilsUseFormChildrenZodFC<typeof schema> = ({ ... }) => { };
152
+
153
+ function Children({...}: RhfUtilsUseFormChildrenZodProps<typeof schema>) { }
155
154
  ```
156
155
 
157
156
  ## `RhfUtilsFormOptions`
@@ -201,3 +200,59 @@ declare module '@paragrav/rhf-utils' {
201
200
  }
202
201
  }
203
202
  ```
203
+
204
+ ## Errors
205
+
206
+ You can configure via `RhfUtilsClientConfig` (example at the top) when form context errors are outputted -- i.e., via console and/or thrown error.
207
+
208
+ ### Orphans
209
+
210
+ The concept of orphan errors is any error that is not being shown to user. An example would be a stray field in a schema that is prohibiting users from successfully submitting a form.
211
+
212
+ The criteria for an orphan is any form context error that meets all of the following criteria:
213
+
214
+ - not a root error -- e.g., `root` or `root.${string}`
215
+ - has no `ref` -- RHF includes `ref` to the associated input on each error object (when applicable)
216
+ - has no marker in DOM (e.g., `FormNonFieldErrorMarker`)
217
+
218
+ 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.)
219
+
220
+ Alternatively, you can use `FormErrorMessageByPath` to display error message to user:
221
+
222
+ ```tsx
223
+ <FormErrorMessageByPath path="street.address" />
224
+ ```
225
+
226
+ 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.)
227
+
228
+ Orphans are optionally exposed in a few places.
229
+
230
+ - In errors outputted via console.
231
+ - And `useFlatFieldErrorsContext()` hook, which returns an object with divided into `all`, `root`, and `orphans` errors.
232
+
233
+ `FlatFieldErrors` is a flattened, simplified version of RHF's `FieldErrors`. Keys represent field paths flattened to dot notation.
234
+
235
+ ## Form Groups
236
+
237
+ Sometimes you need to group multiple forms together.
238
+
239
+ Use case: disable parent form when any child is "busy"; disable all children when parent is "busy".
240
+
241
+ Use `FormGroupContextProvider` to wrap your children and parent forms.
242
+
243
+ ```tsx
244
+ <FormGroupContextProvider>
245
+ <ChildForm1 />
246
+ <ChildForm2 />
247
+ <ParentForm />
248
+ </FormGroupContextProvider>
249
+ ```
250
+
251
+ In your parent form, use the hook `useFormGroupParentTracker`, which returns `boolean` value indicating whether any child forms is busy.
252
+
253
+ In children forms, there are two options. You can choose to consider form "busy" when:
254
+
255
+ - `isSubmitting` is `true` by using `useFormGroupChildIsSubmittingTracker` hook
256
+ - it is mounted by using `useFormGroupChildIsMountedTracker` hook
257
+
258
+ Both hooks return `boolean` value indicating whether parent form is busy.
@@ -2,13 +2,13 @@ import { jsxs as n, Fragment as a, jsx as e } from "react/jsx-runtime";
2
2
  import { useFormState as g, get as i } from "react-hook-form";
3
3
  import F from "../nonfield/FormNonFieldErrorMarker.mjs";
4
4
  import c from "./FormErrorMessage.mjs";
5
- const p = ({ path: o, ...s }) => {
6
- const { errors: m } = g(), r = i(m, o, void 0), t = s.Component ?? c;
7
- return /* @__PURE__ */ n(a, { children: [
5
+ const f = ({ path: o, Component: s }) => {
6
+ const { errors: t } = g(), r = i(t, o, void 0), m = s ?? c;
7
+ return r != null && r.message ? /* @__PURE__ */ n(a, { children: [
8
8
  /* @__PURE__ */ e(F, { path: o }),
9
- (r == null ? void 0 : r.message) && /* @__PURE__ */ e(t, { message: r.message })
10
- ] });
11
- }, l = p;
9
+ /* @__PURE__ */ e(m, { message: r.message })
10
+ ] }) : null;
11
+ }, E = f;
12
12
  export {
13
- l as default
13
+ E as default
14
14
  };
@@ -1,7 +1,6 @@
1
1
  import r from "./useFormGroupChildTracker.mjs";
2
- const o = () => {
3
- r(!0);
4
- }, u = o;
2
+ import o from "./useFormGroupIsParentBusy.mjs";
3
+ const e = () => (r(!0), o()), t = e;
5
4
  export {
6
- u as default
5
+ t as default
7
6
  };
@@ -1,9 +1,10 @@
1
1
  import { useFormState as o } from "react-hook-form";
2
2
  import t from "./useFormGroupChildTracker.mjs";
3
- const i = () => {
3
+ import u from "./useFormGroupIsParentBusy.mjs";
4
+ const m = () => {
4
5
  const { isSubmitting: r } = o();
5
- t(r);
6
- }, u = i;
6
+ return t(r), u();
7
+ }, n = m;
7
8
  export {
8
- u as default
9
+ n as default
9
10
  };
@@ -2,7 +2,7 @@ import { _useFormGroupContextMaybe as o } from "./index.mjs";
2
2
  const s = () => {
3
3
  const r = o();
4
4
  return !!(r != null && r.parent.isBusy);
5
- }, t = s;
5
+ };
6
6
  export {
7
- t as default
7
+ s as default
8
8
  };
@@ -1,12 +1,13 @@
1
1
  import s from "react";
2
2
  import t from "../../useFormIsBusy.mjs";
3
3
  import { _useFormGroupContext as e } from "./index.mjs";
4
- const u = () => {
4
+ import u from "./useFormGroupIsChildBusy.mjs";
5
+ const m = () => {
5
6
  const r = t(), { setIsBusy: o } = e().parent;
6
- s.useEffect(() => {
7
+ return s.useEffect(() => {
7
8
  o(r);
8
- }, [o, r]);
9
- }, n = u;
9
+ }, [o, r]), u();
10
+ }, f = m;
10
11
  export {
11
- n as default
12
+ f as default
12
13
  };
@@ -8,9 +8,15 @@ import { FlatFieldErrors } from './types';
8
8
  * @param errors {@link FieldErrors} object.
9
9
  * @returns FlatFieldErrors object.
10
10
  *
11
- * e.g.,
11
+ * @example
12
12
  * ```
13
- * [ "address.street", { "address.street": { type: "too_short", ref: { value: "123" }, message: "Required." } } ]
13
+ * {
14
+ * "address.street": {
15
+ * type: "too_short",
16
+ * ref: { value: "123" },
17
+ * message: "Required." }
18
+ * }
19
+ * }
14
20
  * ```
15
21
  */
16
22
  declare const getFlatFieldErrors: (errors: FieldErrors) => FlatFieldErrors;
@@ -5,6 +5,10 @@ type Props = {
5
5
  };
6
6
  /**
7
7
  * Empty, hidden marker to indicate a non-field/non-root error is being displayed to user.
8
+ *
9
+ * Example use case:
10
+ * Field array with minimum items.
11
+ * (When there are no items, the error is not associated with a field and is displayed separately.)
8
12
  */
9
13
  declare const FormNonFieldErrorMarker: React.FC<Props>;
10
14
  export default FormNonFieldErrorMarker;
@@ -1,5 +1,5 @@
1
1
  /**
2
2
  * Mark "child" form as "busy" when mounted.
3
3
  */
4
- declare const useFormGroupChildIsMountedTracker: () => void;
4
+ declare const useFormGroupChildIsMountedTracker: () => boolean;
5
5
  export default useFormGroupChildIsMountedTracker;
@@ -1,5 +1,5 @@
1
1
  /**
2
2
  * Mark "child" form as "busy" when submitting.
3
3
  */
4
- declare const useFormGroupChildIsSubmittingTracker: () => void;
4
+ declare const useFormGroupChildIsSubmittingTracker: () => boolean;
5
5
  export default useFormGroupChildIsSubmittingTracker;
@@ -3,5 +3,5 @@
3
3
  *
4
4
  * Requires usage of provider.
5
5
  */
6
- declare const useFormGroupParentTracker: () => void;
6
+ declare const useFormGroupParentTracker: () => boolean;
7
7
  export default useFormGroupParentTracker;
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.110",
5
+ "version": "0.0.112",
6
6
  "description": "Integration utilities for react-hook-form.",
7
7
  "type": "module",
8
8
  "sideEffects": false,