@paragrav/rhf-utils 0.0.132 → 0.0.134
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 +50 -14
- package/dist/esm/errors/message/FormErrorMessageByPath.mjs +2 -2
- package/dist/esm/errors/message/FormErrorMessageByPath.mjs.map +1 -1
- package/dist/esm/form/Form.mjs +34 -34
- package/dist/esm/form/Form.mjs.map +1 -1
- package/dist/types/client/config/RhfUtilsClientConfig.d.ts +7 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,7 +2,29 @@
|
|
|
2
2
|
|
|
3
3
|
## About
|
|
4
4
|
|
|
5
|
-
Integration and utility library for react-hook-form.
|
|
5
|
+
Integration and utility library for [react-hook-form](https://www.react-hook-form.com/).
|
|
6
|
+
|
|
7
|
+
If you have multiple forms and would like a more declarative API to manage their behavior (via built-in and custom options), including transformation of backend errors, and debugging field errors.
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- built with and for [TypeScript](https://www.typescriptlang.org/)
|
|
12
|
+
- global configuration, such as:
|
|
13
|
+
- injecting your own hooks and UI (`FormChildrenWrapper`)
|
|
14
|
+
- server error transformation (`onSubmitErrorUnknown`)
|
|
15
|
+
- FieldErrors logging/throwing (`RhfUtilsClientConfig.FieldErrors`)
|
|
16
|
+
- extendable options type (`RhfUtilsFormOptions`)
|
|
17
|
+
- form-specific overrides
|
|
18
|
+
- schema-typed Controller component
|
|
19
|
+
- schema-typed FormSubmitError class
|
|
20
|
+
- throw in submit handler to add errors to RHF context and fail submit
|
|
21
|
+
- flatter `FieldErrors` structure (`FlatFieldErrors`)
|
|
22
|
+
- grouped into `all`, `fields`, `roots`, and `orphans`
|
|
23
|
+
- `FormErrorMessageByPath` for displaying error message (with `FormNonFieldErrorMarker`)
|
|
24
|
+
- `zod` support
|
|
25
|
+
- including `z.input` and `z.output` types for transformations
|
|
26
|
+
- safer `FieldValues` type (`SafeFieldValues`)
|
|
27
|
+
- 3.3kB min+gzip core functionality (excluding [peer dependencies](#peer-dependencies))
|
|
6
28
|
|
|
7
29
|
## Install
|
|
8
30
|
|
|
@@ -14,7 +36,7 @@ yarn add @paragrav/rhf-utils # yarn
|
|
|
14
36
|
|
|
15
37
|
## Config
|
|
16
38
|
|
|
17
|
-
To configure, create a file `config.tsx` with desired configuration settings. This is global configuration across all forms.
|
|
39
|
+
To configure, create a file like `config.tsx` with desired configuration settings. This is global configuration across all forms, some of which can be overridden at the form level.
|
|
18
40
|
|
|
19
41
|
```tsx
|
|
20
42
|
export const rhfUtilsClientConfig: RhfUtilsClientConfig = {
|
|
@@ -50,8 +72,8 @@ export const rhfUtilsClientConfig: RhfUtilsClientConfig = {
|
|
|
50
72
|
formRef, // ref
|
|
51
73
|
context, // rhf UseFormReturn
|
|
52
74
|
options, // RhfUtilsFormOptions (with any custom props)
|
|
53
|
-
Controller, //
|
|
54
|
-
FormSubmitError, //
|
|
75
|
+
Controller, // rhf controller (SafeFieldValues-typed; no schema at this level)
|
|
76
|
+
FormSubmitError, // error class (SafeFieldValues-typed; no schema at this level)
|
|
55
77
|
children, // RhfUtilsZodForm's Children instance
|
|
56
78
|
},
|
|
57
79
|
) => {
|
|
@@ -132,7 +154,7 @@ Currently, only `zod` is supported.
|
|
|
132
154
|
// submit handler
|
|
133
155
|
onSubmit={async (
|
|
134
156
|
data, // schema output
|
|
135
|
-
context, // UseRhfUtilsFormOnSubmitContext (id, ref, rhf context, utils options,
|
|
157
|
+
context, // UseRhfUtilsFormOnSubmitContext (id, ref, rhf context, utils options, schema-typed FormSubmitError class)
|
|
136
158
|
event, // SubmitEvent
|
|
137
159
|
) => {
|
|
138
160
|
await loginService(data);
|
|
@@ -153,12 +175,12 @@ Currently, only `zod` is supported.
|
|
|
153
175
|
formRef, // ref
|
|
154
176
|
context, // rhf UseFormReturn
|
|
155
177
|
options, // RhfUtilsFormOptions
|
|
156
|
-
Controller, //
|
|
157
|
-
FormSubmitError, //
|
|
178
|
+
Controller, // schema-typed rhf controller
|
|
179
|
+
FormSubmitError, // schema-typed error class
|
|
158
180
|
}) => (
|
|
159
181
|
<>
|
|
160
182
|
<Controller
|
|
161
|
-
name="email" //
|
|
183
|
+
name="email" // schema-typed field name
|
|
162
184
|
render={({ field, formState: { isSubmitting } }) => (
|
|
163
185
|
<label>
|
|
164
186
|
Email
|
|
@@ -220,16 +242,16 @@ function Children({...}: RhfUtilsUseFormChildrenZodProps<typeof schema>) { }
|
|
|
220
242
|
|
|
221
243
|
## `FormSubmitError`
|
|
222
244
|
|
|
223
|
-
This is an `Error`-based class you can use to throw a structured error in your submit handler. It
|
|
245
|
+
This is an `Error`-based class that you can use to throw a structured error in your submit handler. It uses `FormSubmitErrors` (with an "s") type's structure, which is a flat, simplified version of RHF's `FieldErrors`.
|
|
224
246
|
|
|
225
|
-
It
|
|
247
|
+
It differs from `FlatFieldError` only in that it is narrower. Namely, it is schema-typed, so it allows field names from your schema and `root.${string}` keys. And only `type` (optional) and `message` props for error.
|
|
226
248
|
|
|
227
249
|
Example:
|
|
228
250
|
|
|
229
251
|
```tsx
|
|
230
252
|
<RhfUtilsZodForm
|
|
231
|
-
onSubmit={async ({ FormSubmitError }) => {
|
|
232
|
-
if (isProblem())
|
|
253
|
+
onSubmit={async (data, { FormSubmitError }) => {
|
|
254
|
+
if (isProblem(data))
|
|
233
255
|
throw new FormSubmitError({
|
|
234
256
|
root: { message: 'There was a problem with the form.' },
|
|
235
257
|
'street.address': { message: 'Street address invalid.' },
|
|
@@ -298,6 +320,8 @@ You can configure via `RhfUtilsClientConfig` (example at the top) when form cont
|
|
|
298
320
|
|
|
299
321
|
Use `useFlatFieldErrorsContext()` hook, which returns an object with errors grouped by `all`, `fields`, `roots`, `orphans` records, and `hasErrors` and `hasErrors` and `hasOrphans` booleans.
|
|
300
322
|
|
|
323
|
+
`FlatFieldErrors` type is a flattened, simplified version of RHF's `FieldErrors`. Keys represent flattened, dot-notation field paths.
|
|
324
|
+
|
|
301
325
|
### Orphan Errors
|
|
302
326
|
|
|
303
327
|
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.
|
|
@@ -324,8 +348,6 @@ Orphans are exposed in a few places.
|
|
|
324
348
|
- And `useFlatFieldErrorsContext()` hook, which returns an object with errors grouped by `all`, `fields`, `roots`, `orphans` records, and `hasErrors` and `hasOrphans` boolean.
|
|
325
349
|
- Boolean value from `useFlatFieldErrorsContextHasOnlyOrphans`.
|
|
326
350
|
|
|
327
|
-
`FlatFieldErrors` is a flattened, simplified version of RHF's `FieldErrors`. Keys represent field paths flattened to dot notation.
|
|
328
|
-
|
|
329
351
|
## Form Groups
|
|
330
352
|
|
|
331
353
|
Sometimes you need to group multiple forms together.
|
|
@@ -350,3 +372,17 @@ In children forms, there are two options. You can choose to consider form "busy"
|
|
|
350
372
|
- it is mounted by using `useFormGroupChildIsMountedTracker` hook
|
|
351
373
|
|
|
352
374
|
Both hooks return `boolean` value indicating whether parent form is busy.
|
|
375
|
+
|
|
376
|
+
## Other
|
|
377
|
+
|
|
378
|
+
This library uses `SafeFieldValues` type which uses `unknown` instead of `any`.
|
|
379
|
+
|
|
380
|
+
#### Peer dependencies:
|
|
381
|
+
|
|
382
|
+
- [react](https://www.npmjs.com/package/react)
|
|
383
|
+
- [react-dom](https://www.npmjs.com/package/react-dom)
|
|
384
|
+
- [react-hook-form](https://www.npmjs.com/package/react-hook-form)
|
|
385
|
+
- [zod](https://www.npmjs.com/package/zod)
|
|
386
|
+
- [flat](https://www.npmjs.com/package/flat)
|
|
387
|
+
- [@hookform/resolvers](https://www.npmjs.com/package/@hookform/resolvers)
|
|
388
|
+
- [@hookform/devtools](https://www.npmjs.com/package/@hookform/devtools) (optional)
|
|
@@ -5,8 +5,8 @@ import c from "./FormErrorMessage.mjs";
|
|
|
5
5
|
const f = ({ path: o, Component: s }) => {
|
|
6
6
|
const { errors: t } = g(), r = i(t, o, void 0), m = s ?? c;
|
|
7
7
|
return r != null && r.message ? /* @__PURE__ */ n(a, { children: [
|
|
8
|
-
/* @__PURE__ */ e(
|
|
9
|
-
/* @__PURE__ */ e(
|
|
8
|
+
/* @__PURE__ */ e(m, { message: r.message }),
|
|
9
|
+
/* @__PURE__ */ e(F, { path: o })
|
|
10
10
|
] }) : null;
|
|
11
11
|
}, E = f;
|
|
12
12
|
export {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FormErrorMessageByPath.mjs","sources":["../../../../src/errors/message/FormErrorMessageByPath.tsx"],"sourcesContent":["import type { FieldError } from 'react-hook-form';\nimport { get, useFormState } from 'react-hook-form';\n\nimport FormNonFieldErrorMarker from '../nonfield/FormNonFieldErrorMarker';\n\nimport FormErrorMessage from './FormErrorMessage';\n\ntype Props = {\n /** Field name as flat path. (e.g., \"address.street\") */\n path: string;\n /** Optional component to use to display message. (Defaults to {@link FormErrorMessage}) */\n Component?: React.FC<{ message: string }>;\n};\n\nconst FormErrorMessageByPath: React.FC<Props> = ({ path, Component }) => {\n const { errors } = useFormState();\n\n const error = get(errors, path, undefined) as FieldError | undefined;\n\n const _Component = Component ?? FormErrorMessage;\n\n if (!error?.message) return null;\n\n return (\n <>\n {/* marker (only required for non-root non-field errors) */}\n <FormNonFieldErrorMarker path={path} />\n
|
|
1
|
+
{"version":3,"file":"FormErrorMessageByPath.mjs","sources":["../../../../src/errors/message/FormErrorMessageByPath.tsx"],"sourcesContent":["import type { FieldError } from 'react-hook-form';\nimport { get, useFormState } from 'react-hook-form';\n\nimport FormNonFieldErrorMarker from '../nonfield/FormNonFieldErrorMarker';\n\nimport FormErrorMessage from './FormErrorMessage';\n\ntype Props = {\n /** Field name as flat path. (e.g., \"address.street\") */\n path: string;\n /** Optional component to use to display message. (Defaults to {@link FormErrorMessage}) */\n Component?: React.FC<{ message: string }>;\n};\n\nconst FormErrorMessageByPath: React.FC<Props> = ({ path, Component }) => {\n const { errors } = useFormState();\n\n const error = get(errors, path, undefined) as FieldError | undefined;\n\n const _Component = Component ?? FormErrorMessage;\n\n if (!error?.message) return null;\n\n return (\n <>\n {/* error message */}\n <_Component message={error.message} />\n\n {/* marker (only required for non-root non-field errors) */}\n <FormNonFieldErrorMarker path={path} />\n </>\n );\n};\n\nexport default FormErrorMessageByPath;\n"],"names":["FormErrorMessageByPath","path","Component","errors","useFormState","error","get","_Component","FormErrorMessage","jsxs","Fragment","jsx","FormNonFieldErrorMarker","FormErrorMessageByPath$1"],"mappings":";;;;AAcA,MAAMA,IAA0C,CAAC,EAAE,MAAAC,GAAM,WAAAC,QAAgB;AACjE,QAAA,EAAE,QAAAC,MAAWC,KAEbC,IAAQC,EAAIH,GAAQF,GAAM,MAAS,GAEnCM,IAAaL,KAAaM;AAE5B,SAACH,KAAA,QAAAA,EAAO,UAKR,gBAAAI,EAAAC,GAAA,EAAA,UAAA;AAAA,IAAC,gBAAAC,EAAAJ,GAAA,EAAW,SAASF,EAAM,QAAS,CAAA;AAAA,IAGpC,gBAAAM,EAACC,KAAwB,MAAAX,GAAY;AAAA,EACvC,EAAA,CAAA,IAT0B;AAW9B,GAEAY,IAAeb;"}
|
package/dist/esm/form/Form.mjs
CHANGED
|
@@ -1,87 +1,87 @@
|
|
|
1
|
-
import { jsx as
|
|
1
|
+
import { jsx as c, jsxs as j } from "react/jsx-runtime";
|
|
2
2
|
import "react";
|
|
3
|
-
import { useFormContext as
|
|
3
|
+
import { useFormContext as W } from "react-hook-form";
|
|
4
4
|
import w from "../client/config/useRhfUtilsClientConfigContext.mjs";
|
|
5
5
|
import E from "../devtool/LazyDevTool.mjs";
|
|
6
|
-
import { FormSubmitError as
|
|
6
|
+
import { FormSubmitError as d } from "../submit/error/FormSubmitError.mjs";
|
|
7
7
|
import S from "../submit/error/setCtxErrorsByFormSubmitErrors.mjs";
|
|
8
8
|
import U from "../utils/PassthroughChildren.mjs";
|
|
9
|
-
import
|
|
10
|
-
function
|
|
11
|
-
formId:
|
|
12
|
-
formRef:
|
|
9
|
+
import _ from "./_Controller.mjs";
|
|
10
|
+
function G({
|
|
11
|
+
formId: m,
|
|
12
|
+
formRef: p,
|
|
13
13
|
//
|
|
14
14
|
onSubmit: a,
|
|
15
|
-
onSubmitError:
|
|
16
|
-
onSubmitInvalid:
|
|
15
|
+
onSubmitError: s,
|
|
16
|
+
onSubmitInvalid: x,
|
|
17
17
|
Children: l,
|
|
18
18
|
//
|
|
19
19
|
form: F,
|
|
20
20
|
utils: r
|
|
21
21
|
}) {
|
|
22
|
-
const e =
|
|
22
|
+
const e = W(), t = w(), y = async (o) => {
|
|
23
23
|
r != null && r.stopSubmitPropagation && o.stopPropagation();
|
|
24
24
|
try {
|
|
25
|
-
await e.handleSubmit(f,
|
|
25
|
+
await e.handleSubmit(f, x)(o);
|
|
26
26
|
} catch (n) {
|
|
27
|
-
|
|
27
|
+
P(n, o);
|
|
28
28
|
}
|
|
29
29
|
}, f = (o, n) => a == null ? void 0 : a(
|
|
30
30
|
o,
|
|
31
31
|
{
|
|
32
|
-
formId:
|
|
33
|
-
formRef:
|
|
32
|
+
formId: m,
|
|
33
|
+
formRef: p,
|
|
34
34
|
context: e,
|
|
35
35
|
utils: r,
|
|
36
|
-
FormSubmitError:
|
|
36
|
+
FormSubmitError: d
|
|
37
37
|
},
|
|
38
38
|
n
|
|
39
|
-
),
|
|
40
|
-
var
|
|
41
|
-
const C = o instanceof
|
|
39
|
+
), P = (o, n) => {
|
|
40
|
+
var i;
|
|
41
|
+
const C = o instanceof d ? (
|
|
42
42
|
// consumer can manually throw FormSubmitError (e.g., manual validation)
|
|
43
43
|
o.errors
|
|
44
44
|
) : (
|
|
45
45
|
// if error is not FormSubmitError, consumer can provide global handler
|
|
46
|
-
(
|
|
46
|
+
(i = t == null ? void 0 : t.onSubmitErrorUnknown) == null ? void 0 : i.call(t, o)
|
|
47
47
|
);
|
|
48
|
-
C && S(e, C),
|
|
48
|
+
C && S(e, C), s == null || s(
|
|
49
49
|
o,
|
|
50
50
|
{
|
|
51
|
-
formId:
|
|
52
|
-
formRef:
|
|
51
|
+
formId: m,
|
|
52
|
+
formRef: p,
|
|
53
53
|
context: e,
|
|
54
54
|
utils: r
|
|
55
55
|
},
|
|
56
56
|
n
|
|
57
57
|
);
|
|
58
|
-
},
|
|
59
|
-
formId:
|
|
60
|
-
formRef:
|
|
58
|
+
}, h = {
|
|
59
|
+
formId: m,
|
|
60
|
+
formRef: p,
|
|
61
61
|
context: e,
|
|
62
62
|
utils: r,
|
|
63
|
-
Controller:
|
|
64
|
-
FormSubmitError:
|
|
65
|
-
}, v = (t == null ? void 0 : t.FormComponent) ?? "form",
|
|
66
|
-
return /* @__PURE__ */
|
|
63
|
+
Controller: _,
|
|
64
|
+
FormSubmitError: d
|
|
65
|
+
}, b = h, v = (t == null ? void 0 : t.FormComponent) ?? "form", T = (t == null ? void 0 : t.FormChildrenWrapper) ?? U;
|
|
66
|
+
return /* @__PURE__ */ c(
|
|
67
67
|
v,
|
|
68
68
|
{
|
|
69
69
|
...F,
|
|
70
|
-
ref:
|
|
70
|
+
ref: p,
|
|
71
71
|
onSubmit: (o) => void y(o),
|
|
72
|
-
children: /* @__PURE__ */ T
|
|
73
|
-
(r == null ? void 0 : r.devTool) && /* @__PURE__ */
|
|
72
|
+
children: /* @__PURE__ */ j(T, { ...b, children: [
|
|
73
|
+
(r == null ? void 0 : r.devTool) && /* @__PURE__ */ c(
|
|
74
74
|
E,
|
|
75
75
|
{
|
|
76
76
|
props: typeof r.devTool == "object" ? r.devTool : void 0
|
|
77
77
|
}
|
|
78
78
|
),
|
|
79
|
-
/* @__PURE__ */
|
|
79
|
+
/* @__PURE__ */ c(l, { ...h })
|
|
80
80
|
] })
|
|
81
81
|
}
|
|
82
82
|
);
|
|
83
83
|
}
|
|
84
84
|
export {
|
|
85
|
-
|
|
85
|
+
G as default
|
|
86
86
|
};
|
|
87
87
|
//# sourceMappingURL=Form.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Form.mjs","sources":["../../../src/form/Form.tsx"],"sourcesContent":["import React from 'react';\nimport type { UseFormHandleSubmit } from 'react-hook-form';\nimport { useFormContext } from 'react-hook-form';\n\nimport useRhfUtilsClientConfigMaybeContext from '@/client/config/useRhfUtilsClientConfigContext';\n\nimport LazyDevTool from '@/devtool/LazyDevTool';\n\nimport { FormSubmitError } from '@/submit/error/FormSubmitError';\nimport setCtxErrorsByFormSubmitErrors from '@/submit/error/setCtxErrorsByFormSubmitErrors';\n\nimport PassthroughChildren from '@/utils/PassthroughChildren';\n\nimport _Controller from './_Controller';\nimport type { SafeFieldValues } from './SafeFieldValues';\nimport type { UseRhfUtilsFormChildrenProps } from './UseRhfUtilsFormChildrenProps';\nimport type {\n UseRhfUtilsFormOnSubmitContext,\n UseRhfUtilsFormOnSubmitErrorContext,\n} from './UseRhfUtilsFormOnSubmitContext';\nimport type { UseRhfUtilsFormProps } from './UseRhfUtilsFormProps';\n\nexport type FormChildrenSlots = 'top' | 'bottom';\n\nexport type FormProps<\n TFieldValues extends SafeFieldValues,\n TTransformedValues extends SafeFieldValues,\n> = {\n formId: string;\n formRef: React.RefObject<HTMLFormElement>;\n} & Pick<\n UseRhfUtilsFormProps<TFieldValues, TTransformedValues>,\n | 'onSubmit'\n | 'onSubmitError'\n | 'onSubmitInvalid'\n //\n | 'Children'\n //\n | 'form'\n | 'utils'\n>;\n\nfunction _Form<\n TFieldValues extends SafeFieldValues,\n TTransformedValues extends SafeFieldValues,\n>({\n formId,\n formRef,\n //\n onSubmit,\n onSubmitError,\n onSubmitInvalid,\n Children,\n //\n form,\n utils,\n}: FormProps<TFieldValues, TTransformedValues>) {\n const rhfContext = useFormContext<\n TFieldValues,\n unknown,\n TTransformedValues\n >();\n\n const config = useRhfUtilsClientConfigMaybeContext();\n\n // handlers\n\n const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {\n // cannot stop propagation in handleSubmitValid, b/c it receives a different event object\n if (utils?.stopSubmitPropagation) event.stopPropagation();\n\n try {\n await rhfContext.handleSubmit(handleSubmitValid, onSubmitInvalid)(event);\n } catch (error) {\n handleSubmitError(error, event);\n }\n };\n\n const handleSubmitValid = ((\n data: TTransformedValues,\n event?: React.BaseSyntheticEvent,\n ) => {\n return onSubmit?.(\n data,\n {\n formId,\n formRef,\n context: rhfContext,\n utils,\n FormSubmitError: FormSubmitError<TFieldValues>,\n } satisfies UseRhfUtilsFormOnSubmitContext<\n TFieldValues,\n TTransformedValues\n >,\n event as React.BaseSyntheticEvent<SubmitEvent>, // `as` required due to conditional generic\n );\n }) as Parameters<UseFormHandleSubmit<TFieldValues, TTransformedValues>>[0];\n\n const handleSubmitError = (\n error: unknown,\n event: React.BaseSyntheticEvent,\n ) => {\n // get `FormSubmitErrors`, if possible\n const formSubmitErrors =\n error instanceof FormSubmitError\n ? // consumer can manually throw FormSubmitError (e.g., manual validation)\n (error as FormSubmitError).errors // `as` to avoid `<any>`\n : // if error is not FormSubmitError, consumer can provide global handler\n config?.onSubmitErrorUnknown?.(error);\n\n if (formSubmitErrors)\n setCtxErrorsByFormSubmitErrors(rhfContext, formSubmitErrors);\n\n onSubmitError?.(\n error,\n {\n formId,\n formRef,\n context: rhfContext,\n utils,\n } satisfies UseRhfUtilsFormOnSubmitErrorContext<\n TFieldValues,\n TTransformedValues\n >,\n event as React.BaseSyntheticEvent<SubmitEvent>, // `as` required due to conditional generic\n );\n };\n\n const childrenProps: UseRhfUtilsFormChildrenProps<\n TFieldValues,\n TTransformedValues\n > = {\n formId,\n formRef,\n context: rhfContext,\n utils,\n Controller: _Controller<TFieldValues>,\n FormSubmitError: FormSubmitError<TFieldValues>,\n };\n\n const FormComponent = config?.FormComponent ?? 'form';\n\n const FormChildrenWrapper =\n config?.FormChildrenWrapper ?? PassthroughChildren;\n\n return (\n <FormComponent\n {...form}\n ref={formRef}\n onSubmit={(e) => void handleSubmit(e)}\n >\n <FormChildrenWrapper {...
|
|
1
|
+
{"version":3,"file":"Form.mjs","sources":["../../../src/form/Form.tsx"],"sourcesContent":["import React from 'react';\nimport type { UseFormHandleSubmit } from 'react-hook-form';\nimport { useFormContext } from 'react-hook-form';\n\nimport type { RhfUtilsFormChildrenWrapperProps } from '@/client/config/RhfUtilsClientConfig';\nimport useRhfUtilsClientConfigMaybeContext from '@/client/config/useRhfUtilsClientConfigContext';\n\nimport LazyDevTool from '@/devtool/LazyDevTool';\n\nimport { FormSubmitError } from '@/submit/error/FormSubmitError';\nimport setCtxErrorsByFormSubmitErrors from '@/submit/error/setCtxErrorsByFormSubmitErrors';\n\nimport PassthroughChildren from '@/utils/PassthroughChildren';\n\nimport _Controller from './_Controller';\nimport type { SafeFieldValues } from './SafeFieldValues';\nimport type { UseRhfUtilsFormChildrenProps } from './UseRhfUtilsFormChildrenProps';\nimport type {\n UseRhfUtilsFormOnSubmitContext,\n UseRhfUtilsFormOnSubmitErrorContext,\n} from './UseRhfUtilsFormOnSubmitContext';\nimport type { UseRhfUtilsFormProps } from './UseRhfUtilsFormProps';\n\nexport type FormChildrenSlots = 'top' | 'bottom';\n\nexport type FormProps<\n TFieldValues extends SafeFieldValues,\n TTransformedValues extends SafeFieldValues,\n> = {\n formId: string;\n formRef: React.RefObject<HTMLFormElement>;\n} & Pick<\n UseRhfUtilsFormProps<TFieldValues, TTransformedValues>,\n | 'onSubmit'\n | 'onSubmitError'\n | 'onSubmitInvalid'\n //\n | 'Children'\n //\n | 'form'\n | 'utils'\n>;\n\nfunction _Form<\n TFieldValues extends SafeFieldValues,\n TTransformedValues extends SafeFieldValues,\n>({\n formId,\n formRef,\n //\n onSubmit,\n onSubmitError,\n onSubmitInvalid,\n Children,\n //\n form,\n utils,\n}: FormProps<TFieldValues, TTransformedValues>) {\n const rhfContext = useFormContext<\n TFieldValues,\n unknown,\n TTransformedValues\n >();\n\n const config = useRhfUtilsClientConfigMaybeContext();\n\n // handlers\n\n const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {\n // cannot stop propagation in handleSubmitValid, b/c it receives a different event object\n if (utils?.stopSubmitPropagation) event.stopPropagation();\n\n try {\n await rhfContext.handleSubmit(handleSubmitValid, onSubmitInvalid)(event);\n } catch (error) {\n handleSubmitError(error, event);\n }\n };\n\n const handleSubmitValid = ((\n data: TTransformedValues,\n event?: React.BaseSyntheticEvent,\n ) => {\n return onSubmit?.(\n data,\n {\n formId,\n formRef,\n context: rhfContext,\n utils,\n FormSubmitError: FormSubmitError<TFieldValues>,\n } satisfies UseRhfUtilsFormOnSubmitContext<\n TFieldValues,\n TTransformedValues\n >,\n event as React.BaseSyntheticEvent<SubmitEvent>, // `as` required due to conditional generic\n );\n }) as Parameters<UseFormHandleSubmit<TFieldValues, TTransformedValues>>[0];\n\n const handleSubmitError = (\n error: unknown,\n event: React.BaseSyntheticEvent,\n ) => {\n // get `FormSubmitErrors`, if possible\n const formSubmitErrors =\n error instanceof FormSubmitError\n ? // consumer can manually throw FormSubmitError (e.g., manual validation)\n (error as FormSubmitError).errors // `as` to avoid `<any>`\n : // if error is not FormSubmitError, consumer can provide global handler\n config?.onSubmitErrorUnknown?.(error);\n\n if (formSubmitErrors)\n setCtxErrorsByFormSubmitErrors(rhfContext, formSubmitErrors);\n\n onSubmitError?.(\n error,\n {\n formId,\n formRef,\n context: rhfContext,\n utils,\n } satisfies UseRhfUtilsFormOnSubmitErrorContext<\n TFieldValues,\n TTransformedValues\n >,\n event as React.BaseSyntheticEvent<SubmitEvent>, // `as` required due to conditional generic\n );\n };\n\n const childrenProps: UseRhfUtilsFormChildrenProps<\n TFieldValues,\n TTransformedValues\n > = {\n formId,\n formRef,\n context: rhfContext,\n utils,\n Controller: _Controller<TFieldValues>,\n FormSubmitError: FormSubmitError<TFieldValues>,\n };\n\n // children wrapper is not specific to schema (not possible)\n const childrenWrapperProps =\n childrenProps as RhfUtilsFormChildrenWrapperProps;\n\n const FormComponent = config?.FormComponent ?? 'form';\n\n const FormChildrenWrapper =\n config?.FormChildrenWrapper ?? PassthroughChildren;\n\n return (\n <FormComponent\n {...form}\n ref={formRef}\n onSubmit={(e) => void handleSubmit(e)}\n >\n <FormChildrenWrapper {...childrenWrapperProps}>\n {utils?.devTool && (\n <LazyDevTool\n props={\n typeof utils.devTool === 'object' ? utils.devTool : undefined\n }\n />\n )}\n\n <Children {...childrenProps} />\n </FormChildrenWrapper>\n </FormComponent>\n );\n}\n\nexport default _Form;\n"],"names":["_Form","formId","formRef","onSubmit","onSubmitError","onSubmitInvalid","Children","form","utils","rhfContext","useFormContext","config","useRhfUtilsClientConfigMaybeContext","handleSubmit","event","handleSubmitValid","error","handleSubmitError","data","FormSubmitError","formSubmitErrors","_a","setCtxErrorsByFormSubmitErrors","childrenProps","_Controller","childrenWrapperProps","FormComponent","FormChildrenWrapper","PassthroughChildren","jsx","e","jsxs","LazyDevTool"],"mappings":";;;;;;;;;AA2CA,SAASA,EAGP;AAAA,EACA,QAAAC;AAAA,EACA,SAAAC;AAAA;AAAA,EAEA,UAAAC;AAAA,EACA,eAAAC;AAAA,EACA,iBAAAC;AAAA,EACA,UAAAC;AAAA;AAAA,EAEA,MAAAC;AAAA,EACA,OAAAC;AACF,GAAgD;AAC9C,QAAMC,IAAaC,KAMbC,IAASC,KAITC,IAAe,OAAOC,MAA4C;AAElE,IAAAN,KAAA,QAAAA,EAAO,yBAAuBM,EAAM,gBAAgB;AAEpD,QAAA;AACF,YAAML,EAAW,aAAaM,GAAmBV,CAAe,EAAES,CAAK;AAAA,aAChEE,GAAO;AACd,MAAAC,EAAkBD,GAAOF,CAAK;AAAA,IAChC;AAAA,EAAA,GAGIC,IAAqB,CACzBG,GACAJ,MAEOX,KAAA,gBAAAA;AAAA,IACLe;AAAA,IACA;AAAA,MACE,QAAAjB;AAAA,MACA,SAAAC;AAAA,MACA,SAASO;AAAA,MACT,OAAAD;AAAA,MACA,iBAAAW;AAAA,IACF;AAAA,IAIAL;AAAA,KAIEG,IAAoB,CACxBD,GACAF,MACG;;AAEH,UAAMM,IACJJ,aAAiBG;AAAA;AAAA,MAEZH,EAA0B;AAAA;AAAA;AAAA,OAE3BK,IAAAV,KAAA,gBAAAA,EAAQ,yBAAR,gBAAAU,EAAA,KAAAV,GAA+BK;AAAA;AAEjC,IAAAI,KACFE,EAA+Bb,GAAYW,CAAgB,GAE7DhB,KAAA,QAAAA;AAAA,MACEY;AAAA,MACA;AAAA,QACE,QAAAf;AAAA,QACA,SAAAC;AAAA,QACA,SAASO;AAAA,QACT,OAAAD;AAAA,MACF;AAAA,MAIAM;AAAA;AAAA,EACF,GAGIS,IAGF;AAAA,IACF,QAAAtB;AAAA,IACA,SAAAC;AAAA,IACA,SAASO;AAAA,IACT,OAAAD;AAAA,IACA,YAAYgB;AAAA,IACZ,iBAAAL;AAAA,EAAA,GAIIM,IACJF,GAEIG,KAAgBf,KAAA,gBAAAA,EAAQ,kBAAiB,QAEzCgB,KACJhB,KAAA,gBAAAA,EAAQ,wBAAuBiB;AAG/B,SAAA,gBAAAC;AAAA,IAACH;AAAA,IAAA;AAAA,MACE,GAAGnB;AAAA,MACJ,KAAKL;AAAA,MACL,UAAU,CAAC4B,MAAM,KAAKjB,EAAaiB,CAAC;AAAA,MAEpC,UAAA,gBAAAC,EAACJ,GAAqB,EAAA,GAAGF,GACtB,UAAA;AAAA,SAAAjB,KAAA,gBAAAA,EAAO,YACN,gBAAAqB;AAAA,UAACG;AAAA,UAAA;AAAA,YACC,OACE,OAAOxB,EAAM,WAAY,WAAWA,EAAM,UAAU;AAAA,UAAA;AAAA,QAExD;AAAA,QAGF,gBAAAqB,EAACvB,GAAU,EAAA,GAAGiB,GAAe;AAAA,MAAA,GAC/B;AAAA,IAAA;AAAA,EAAA;AAGN;"}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { UseFormProps } from 'react-hook-form';
|
|
2
2
|
import { FlatFieldErrorsOutputConfig } from '../../errors/flat/context/FlatFieldErrorsOutputConfig';
|
|
3
3
|
import { RhfUtilsFormOptions } from '../../form/RhfUtilsFormOptions';
|
|
4
|
-
import { SafeFieldValues } from '../../form/SafeFieldValues';
|
|
5
4
|
import { UseRhfUtilsFormChildrenProps } from '../../form/UseRhfUtilsFormChildrenProps';
|
|
6
5
|
import { FormSubmitErrors } from '../../submit/error/FormSubmitErrors';
|
|
7
6
|
|
|
@@ -41,11 +40,13 @@ export type RhfUtilsClientConfig = {
|
|
|
41
40
|
/**
|
|
42
41
|
* Wrap all form bodies with your own hooks, components, etc.
|
|
43
42
|
*
|
|
43
|
+
* (NOTE: not schema-typed as not possible at this level)
|
|
44
|
+
*
|
|
44
45
|
* Use cases:
|
|
45
46
|
* - custom hooks/behaviors based on `options`
|
|
46
47
|
* - shared header, footer
|
|
47
48
|
*/
|
|
48
|
-
FormChildrenWrapper?:
|
|
49
|
+
FormChildrenWrapper?: (props: RhfUtilsFormChildrenWrapperProps) => React.ReactNode;
|
|
49
50
|
/**
|
|
50
51
|
* Global submit error handler when/if submit handler throws an error other than {@link FormSubmitError}.
|
|
51
52
|
*
|
|
@@ -64,3 +65,7 @@ export type RhfUtilsClientConfig = {
|
|
|
64
65
|
output?: FlatFieldErrorsOutputConfig;
|
|
65
66
|
};
|
|
66
67
|
};
|
|
68
|
+
/**
|
|
69
|
+
* Non-schema-typed version of `UseRhfUtilsFormChildrenProps` for global config.
|
|
70
|
+
*/
|
|
71
|
+
export type RhfUtilsFormChildrenWrapperProps = React.PropsWithChildren<UseRhfUtilsFormChildrenProps>;
|