1
+ import { FormHelperText , type FormHelperTextProps } from "@mui/material"
1
2
import {
2
3
Formik ,
3
4
Form as FormikForm ,
4
5
type FormikConfig ,
5
6
type FormikErrors ,
7
+ type FormikProps ,
6
8
} from "formik"
9
+ import {
10
+ type ReactNode ,
11
+ type FC ,
12
+ useRef ,
13
+ useEffect ,
14
+ type RefObject ,
15
+ } from "react"
7
16
import type { TypedUseMutation } from "@reduxjs/toolkit/query/react"
8
17
18
+ import { getKeyPaths } from "../../utils/general"
9
19
import {
10
20
submitForm ,
11
21
type SubmitFormOptions ,
12
22
type FormValues ,
13
23
} from "../../utils/form"
14
24
15
- const _ = < Values extends FormValues > ( {
25
+ const SCROLL_INTO_VIEW_OPTIONS : ScrollIntoViewOptions = {
26
+ behavior : "smooth" ,
27
+ block : "start" ,
28
+ }
29
+
30
+ type NonFieldErrorsProps = Omit < FormHelperTextProps , "error" | "ref" > & {
31
+ scrollIntoViewOptions ?: ScrollIntoViewOptions
32
+ }
33
+
34
+ const NonFieldErrors : FC < NonFieldErrorsProps > = ( {
35
+ scrollIntoViewOptions = SCROLL_INTO_VIEW_OPTIONS ,
36
+ ...formHelperTextProps
37
+ } ) => {
38
+ const pRef = useRef < HTMLParagraphElement > ( null )
39
+
40
+ useEffect ( ( ) => {
41
+ if ( pRef . current ) pRef . current . scrollIntoView ( scrollIntoViewOptions )
42
+ } , [ scrollIntoViewOptions ] )
43
+
44
+ return < FormHelperText ref = { pRef } error { ...formHelperTextProps } />
45
+ }
46
+
47
+ export type FormErrors < Values > = FormikErrors <
48
+ Omit < Values , "non_field_errors" > & { non_field_errors : string }
49
+ >
50
+
51
+ type _FormikProps < Values > = Omit < FormikProps < Values > , "errors" > & {
52
+ errors : FormErrors < Values >
53
+ }
54
+
55
+ type BaseFormProps < Values > = Omit < FormikConfig < Values > , "children" > & {
56
+ children : ReactNode | ( ( props : _FormikProps < Values > ) => ReactNode )
57
+ scrollIntoViewOptions ?: ScrollIntoViewOptions
58
+ nonFieldErrorsProps ?: Omit < NonFieldErrorsProps , "children" >
59
+ order ?: Array < { name : string ; inputRef : RefObject < HTMLInputElement > } >
60
+ }
61
+
62
+ const BaseForm = < Values extends FormValues > ( {
16
63
children,
64
+ scrollIntoViewOptions = SCROLL_INTO_VIEW_OPTIONS ,
65
+ nonFieldErrorsProps,
66
+ order,
17
67
...otherFormikProps
18
- } : FormikConfig < Values > ) => (
68
+ } : BaseFormProps < Values > ) => (
19
69
< Formik { ...otherFormikProps } >
20
- { formik => (
21
- < FormikForm >
22
- { typeof children === "function" ? children ( formik ) : children }
23
- </ FormikForm >
24
- ) }
70
+ { /* @ts -expect-error */ }
71
+ { ( formik : _FormikProps < Values > ) => {
72
+ let nonFieldErrors : undefined | JSX . Element = undefined
73
+ if ( Object . keys ( formik . errors ) . length ) {
74
+ if ( typeof formik . errors . non_field_errors === "string" ) {
75
+ nonFieldErrors = (
76
+ < NonFieldErrors { ...nonFieldErrorsProps } >
77
+ { formik . errors . non_field_errors }
78
+ </ NonFieldErrors >
79
+ )
80
+ } else if ( order && order . length ) {
81
+ const errorNames = getKeyPaths ( formik . errors )
82
+
83
+ const inputRef = order . find ( ( { name } ) =>
84
+ errorNames . includes ( name ) ,
85
+ ) ?. inputRef
86
+
87
+ if ( inputRef ?. current ) {
88
+ inputRef . current . scrollIntoView ( scrollIntoViewOptions )
89
+ }
90
+ }
91
+ }
92
+
93
+ return (
94
+ < >
95
+ { nonFieldErrors }
96
+ < FormikForm >
97
+ { typeof children === "function" ? children ( formik ) : children }
98
+ </ FormikForm >
99
+ </ >
100
+ )
101
+ } }
25
102
</ Formik >
26
103
)
27
104
28
105
type SubmitFormProps <
29
106
Values extends FormValues ,
30
107
QueryArg extends FormValues ,
31
108
ResultType ,
32
- > = Omit < FormikConfig < Values > , "onSubmit" > & {
109
+ > = Omit < BaseFormProps < Values > , "onSubmit" > & {
33
110
useMutation : TypedUseMutation < ResultType , QueryArg , any >
34
111
} & ( Values extends QueryArg
35
112
? { submitOptions ?: SubmitFormOptions < Values , QueryArg , ResultType > }
@@ -42,15 +119,16 @@ const SubmitForm = <
42
119
> ( {
43
120
useMutation,
44
121
submitOptions,
45
- ...formikProps
122
+ ...baseFormProps
46
123
} : SubmitFormProps < Values , QueryArg , ResultType > ) : JSX . Element => {
47
124
const [ trigger ] = useMutation ( )
48
125
49
126
return (
50
- < _
51
- { ...formikProps }
127
+ < BaseForm
128
+ { ...baseFormProps }
52
129
onSubmit = { submitForm < Values , QueryArg , ResultType > (
53
130
trigger ,
131
+ baseFormProps . initialValues ,
54
132
submitOptions as SubmitFormOptions < Values , QueryArg , ResultType > ,
55
133
) }
56
134
/>
@@ -61,10 +139,10 @@ export type FormProps<
61
139
Values extends FormValues ,
62
140
QueryArg extends FormValues ,
63
141
ResultType ,
64
- > = FormikConfig < Values > | SubmitFormProps < Values , QueryArg , ResultType >
142
+ > = BaseFormProps < Values > | SubmitFormProps < Values , QueryArg , ResultType >
65
143
66
144
const Form : {
67
- < Values extends FormValues > ( props : FormikConfig < Values > ) : JSX . Element
145
+ < Values extends FormValues > ( props : BaseFormProps < Values > ) : JSX . Element
68
146
< Values extends FormValues , QueryArg extends FormValues , ResultType > (
69
147
props : SubmitFormProps < Values , QueryArg , ResultType > ,
70
148
) : JSX . Element
@@ -75,8 +153,7 @@ const Form: {
75
153
> (
76
154
props : FormProps < Values , QueryArg , ResultType > ,
77
155
) : JSX . Element => {
78
- return "onSubmit" in props ? < _ { ...props } /> : SubmitForm ( props )
156
+ return "onSubmit" in props ? < BaseForm { ...props } /> : SubmitForm ( props )
79
157
}
80
158
81
159
export default Form
82
- export { type FormikErrors as FormErrors }
0 commit comments