@saas-ui/modals 2.0.0-next.13 → 2.0.0-next.15

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/src/form.tsx CHANGED
@@ -1,6 +1,12 @@
1
1
  import * as React from 'react'
2
2
 
3
- import { ModalBody, ModalFooter, Button, forwardRef } from '@chakra-ui/react'
3
+ import {
4
+ ModalBody,
5
+ ModalFooter,
6
+ Button,
7
+ forwardRef,
8
+ ButtonProps,
9
+ } from '@chakra-ui/react'
4
10
  import { runIfFn } from '@saas-ui/react-utils'
5
11
 
6
12
  import {
@@ -11,10 +17,24 @@ import {
11
17
  FieldValues,
12
18
  FieldResolver,
13
19
  FieldProps,
20
+ FormType,
21
+ DefaultFieldOverrides,
22
+ WithFields,
14
23
  } from '@saas-ui/forms'
15
24
 
25
+ import {
26
+ Form as YupForm,
27
+ AnyObjectSchema,
28
+ YupFormType,
29
+ } from '@saas-ui/forms/yup'
30
+ import { Form as ZodForm, ZodFormType } from '@saas-ui/forms/zod'
31
+
16
32
  import { BaseModal, BaseModalProps } from './modal'
17
33
 
34
+ export type FormDialogFieldOverrides = DefaultFieldOverrides & {
35
+ cancel?: ButtonProps
36
+ }
37
+
18
38
  export interface FormDialogProps<
19
39
  TFieldValues extends FieldValues = FieldValues,
20
40
  TContext extends object = object,
@@ -46,100 +66,149 @@ export interface FormDialogProps<
46
66
  * Defaults to a cancel and submit button.
47
67
  */
48
68
  footer?: React.ReactNode
49
- /**
50
- * The cancel button label
51
- * @default "Cancel"
52
- */
53
- cancelLabel?: React.ReactNode
54
- /**
55
- * The submit button label
56
- * @default "Submit"
57
- */
58
- submitLabel?: React.ReactNode
59
69
  /**
60
70
  * A schema field resolver used to auto generate form fields.
61
71
  */
62
72
  fieldResolver?: FieldResolver
73
+ /**
74
+ * Field overrides
75
+ */
76
+ fields?: FormDialogFieldOverrides
63
77
  }
78
+
79
+ const useFormProps = (props: FormDialogProps) => {
80
+ const {
81
+ schema,
82
+ resolver,
83
+ fieldResolver,
84
+ defaultValues,
85
+ values,
86
+ context,
87
+ onChange,
88
+ onSubmit,
89
+ onError,
90
+ mode,
91
+ reValidateMode,
92
+ shouldFocusError = true,
93
+ shouldUnregister,
94
+ shouldUseNativeValidation,
95
+ criteriaMode,
96
+ delayError = 100,
97
+ fields,
98
+ ...modalProps
99
+ } = props
100
+
101
+ const formProps = {
102
+ schema,
103
+ resolver,
104
+ defaultValues,
105
+ values,
106
+ context,
107
+ onChange,
108
+ onSubmit,
109
+ onError,
110
+ mode,
111
+ reValidateMode,
112
+ shouldFocusError,
113
+ shouldUnregister,
114
+ shouldUseNativeValidation,
115
+ criteriaMode,
116
+ delayError,
117
+ fields,
118
+ }
119
+
120
+ return { modalProps, formProps, fields }
121
+ }
122
+
64
123
  /**
65
- * Can be used to quickly request information from people without leaving the current page.
66
- *
67
- * @see Docs https://saas-ui.dev/docs/components/overlay/form-dialog
124
+ * Todo make this dynamic to support other schema types
68
125
  */
69
- export const FormDialog = forwardRef(
70
- <
71
- TFieldValues extends FieldValues = FieldValues,
72
- TContext extends object = object
73
- >(
74
- props: FormDialogProps<TFieldValues, TContext>,
75
- ref: React.ForwardedRef<HTMLFormElement>
76
- ) => {
77
- const {
78
- children,
79
- schema,
80
- resolver,
81
- fieldResolver,
82
- defaultValues,
83
- values,
84
- context,
85
- onChange,
86
- onSubmit,
87
- onError,
88
- mode,
89
- reValidateMode,
90
- shouldFocusError = true,
91
- shouldUnregister,
92
- shouldUseNativeValidation,
93
- criteriaMode,
94
- delayError = 100,
95
- cancelLabel = 'Cancel',
96
- submitLabel = 'Submit',
97
- footer,
98
- isOpen,
99
- onClose,
100
- ...rest
101
- } = props
102
-
103
- const formProps = {
104
- ref,
105
- schema,
106
- resolver,
107
- defaultValues,
108
- values,
109
- context,
110
- onChange,
111
- onSubmit,
112
- onError,
113
- mode,
114
- reValidateMode,
115
- shouldFocusError,
116
- shouldUnregister,
117
- shouldUseNativeValidation,
118
- criteriaMode,
119
- delayError,
120
- }
126
+ type MergeDialogProps<T> = T extends YupFormType<
127
+ infer FieldDefs,
128
+ infer ExtraProps,
129
+ infer ExtraOverrides,
130
+ 'yup'
131
+ >
132
+ ? YupFormType<
133
+ FieldDefs,
134
+ ExtraProps & Omit<BaseModalProps, 'children'>,
135
+ ExtraOverrides & FormDialogFieldOverrides
136
+ >
137
+ : T extends ZodFormType<
138
+ infer FieldDefs,
139
+ infer ExtraProps,
140
+ infer ExtraOverrides,
141
+ 'zod'
142
+ >
143
+ ? ZodFormType<
144
+ FieldDefs,
145
+ ExtraProps & Omit<BaseModalProps, 'children'>,
146
+ ExtraOverrides & FormDialogFieldOverrides
147
+ >
148
+ : T extends FormType<infer FieldDefs, infer ExtraProps, infer ExtraOverrides>
149
+ ? FormType<
150
+ FieldDefs,
151
+ ExtraProps & Omit<BaseModalProps, 'children'>,
152
+ ExtraOverrides & FormDialogFieldOverrides
153
+ >
154
+ : never
155
+
156
+ type IsSchemaType<T, Schema, FieldDefs> = T extends DefaultFormType<FieldDefs>
157
+ ? T extends (
158
+ props: FormProps<infer TFieldValues, infer TContext, infer TSchema>
159
+ ) => any
160
+ ? Schema extends TSchema
161
+ ? true
162
+ : false
163
+ : false
164
+ : false
121
165
 
166
+ export type DefaultFormType<
167
+ FieldDefs = any,
168
+ ExtraProps = object,
169
+ ExtraOverrides = FormDialogFieldOverrides
170
+ > = (<
171
+ TFieldValues extends Record<string, any> = any,
172
+ TContext extends object = object,
173
+ TSchema = unknown
174
+ >(
175
+ props: any
176
+ ) => React.ReactElement) & {
177
+ displayName?: string
178
+ id?: string
179
+ }
180
+
181
+ export function createFormDialog<
182
+ FieldDefs = any,
183
+ ExtraProps = object,
184
+ ExtraOverrides = FormDialogFieldOverrides,
185
+ TFormType extends DefaultFormType<
186
+ FieldDefs,
187
+ ExtraProps,
188
+ ExtraOverrides
189
+ > = DefaultFormType<FieldDefs, ExtraProps, ExtraOverrides>
190
+ >(Form: TFormType) {
191
+ const Dialog = forwardRef<any, 'div'>((props, ref) => {
192
+ const { isOpen, onClose, footer, children, ...rest } = props
193
+ const { modalProps, formProps, fields } = useFormProps(rest)
122
194
  return (
123
- <BaseModal isOpen={isOpen} onClose={onClose} {...rest}>
124
- <Form {...formProps} ref={ref}>
125
- {(form) => (
195
+ <BaseModal {...modalProps} isOpen={isOpen} onClose={onClose}>
196
+ <Form ref={ref} {...(formProps as any)}>
197
+ {(form: any) => (
126
198
  <>
127
- <ModalBody>
128
- {runIfFn(children, form) || (
129
- <AutoFields
130
- schema={schema}
131
- fieldResolver={fieldResolver}
132
- focusFirstField
133
- />
134
- )}
135
- </ModalBody>
199
+ <ModalBody>{runIfFn(children, form) || <AutoFields />}</ModalBody>
136
200
 
137
201
  {footer || (
138
202
  <ModalFooter>
139
- <Button variant="ghost" mr={3} onClick={onClose}>
140
- {cancelLabel}
203
+ <Button
204
+ variant="ghost"
205
+ mr={3}
206
+ onClick={onClose}
207
+ {...fields?.cancel}
208
+ >
209
+ {fields?.cancel?.children ?? 'Cancel'}
141
210
  </Button>
142
- <SubmitButton>{submitLabel}</SubmitButton>
211
+ <SubmitButton {...fields?.submit} />
143
212
  </ModalFooter>
144
213
  )}
145
214
  </>
@@ -147,9 +216,16 @@ export const FormDialog = forwardRef(
147
216
  </Form>
148
217
  </BaseModal>
149
218
  )
150
- }
151
- ) as <TFieldValues extends FieldValues>(
152
- props: FormDialogProps<TFieldValues> & {
153
- ref?: React.ForwardedRef<HTMLFormElement>
154
- }
155
- ) => React.ReactElement
219
+ }) as MergeDialogProps<TFormType>
220
+
221
+ Dialog.displayName = `${Form.displayName || Form.name}Dialog`
222
+
223
+ return Dialog
224
+ }
225
+
226
+ /**
227
+ * Can be used to quickly request information from people without leaving the current page.
228
+ *
229
+ * @see Docs https://saas-ui.dev/docs/components/overlay/form-dialog
230
+ */
231
+ export const FormDialog = createFormDialog(Form)