@saas-ui/modals 2.0.0-next.8 → 2.0.0-rc.22
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/CHANGELOG.md +129 -0
- package/dist/index.d.ts +147 -57
- package/dist/index.js +139 -89
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +141 -93
- package/dist/index.mjs.map +1 -1
- package/package.json +6 -4
- package/src/create-modals.tsx +30 -0
- package/src/default-modals.ts +14 -0
- package/src/drawer.tsx +47 -9
- package/src/form.tsx +166 -93
- package/src/index.ts +2 -0
- package/src/menu.tsx +7 -1
- package/src/modal.tsx +43 -8
- package/src/provider.tsx +65 -52
- package/src/types.ts +83 -0
- package/src/create-modals.ts +0 -0
package/src/drawer.tsx
CHANGED
@@ -9,13 +9,29 @@ import {
|
|
9
9
|
DrawerBody,
|
10
10
|
DrawerCloseButton,
|
11
11
|
DrawerProps as ChakraDrawerProps,
|
12
|
+
ModalHeaderProps,
|
13
|
+
ModalContentProps,
|
14
|
+
ModalFooterProps,
|
12
15
|
} from '@chakra-ui/react'
|
16
|
+
import { MaybeRenderProp } from '@chakra-ui/react-utils'
|
17
|
+
import { runIfFn } from '@chakra-ui/utils'
|
13
18
|
|
14
19
|
export interface BaseDrawerProps extends Omit<ChakraDrawerProps, 'children'> {
|
15
20
|
/**
|
16
21
|
* The drawer title
|
17
22
|
*/
|
18
23
|
title: React.ReactNode
|
24
|
+
/**
|
25
|
+
* The modal children
|
26
|
+
*/
|
27
|
+
children: MaybeRenderProp<{
|
28
|
+
isOpen: boolean
|
29
|
+
onClose: () => void
|
30
|
+
}>
|
31
|
+
/**
|
32
|
+
* The modal footer
|
33
|
+
*/
|
34
|
+
footer?: React.ReactNode
|
19
35
|
/**
|
20
36
|
* Hide the close button
|
21
37
|
*/
|
@@ -24,26 +40,45 @@ export interface BaseDrawerProps extends Omit<ChakraDrawerProps, 'children'> {
|
|
24
40
|
* Hide the overflow
|
25
41
|
*/
|
26
42
|
hideOverlay?: boolean
|
27
|
-
|
43
|
+
/**
|
44
|
+
* Props for the modal header
|
45
|
+
*/
|
46
|
+
headerProps?: ModalHeaderProps
|
47
|
+
/**
|
48
|
+
* Props for the modal content
|
49
|
+
*/
|
50
|
+
contentProps?: ModalContentProps
|
51
|
+
/**
|
52
|
+
* Props for the modal footer
|
53
|
+
*/
|
54
|
+
footerProps?: ModalFooterProps
|
28
55
|
}
|
29
56
|
|
30
57
|
export const BaseDrawer: React.FC<BaseDrawerProps> = (props) => {
|
31
58
|
const {
|
32
59
|
title,
|
33
60
|
children,
|
61
|
+
footer,
|
34
62
|
isOpen,
|
35
63
|
onClose,
|
36
64
|
hideCloseButton,
|
37
65
|
hideOverlay,
|
66
|
+
headerProps,
|
67
|
+
contentProps,
|
68
|
+
footerProps,
|
38
69
|
...rest
|
39
70
|
} = props
|
40
71
|
return (
|
41
72
|
<ChakraDrawer isOpen={isOpen} onClose={onClose} {...rest}>
|
42
73
|
{!hideOverlay && <DrawerOverlay />}
|
43
|
-
<DrawerContent>
|
44
|
-
<DrawerHeader>{title}</DrawerHeader>
|
74
|
+
<DrawerContent {...contentProps}>
|
75
|
+
{title && <DrawerHeader {...headerProps}>{title}</DrawerHeader>}
|
45
76
|
{!hideCloseButton && <DrawerCloseButton />}
|
46
|
-
{children
|
77
|
+
{runIfFn(children, {
|
78
|
+
isOpen,
|
79
|
+
onClose,
|
80
|
+
})}
|
81
|
+
{footer && <DrawerFooter {...footerProps}>{footer}</DrawerFooter>}
|
47
82
|
</DrawerContent>
|
48
83
|
</ChakraDrawer>
|
49
84
|
)
|
@@ -57,12 +92,15 @@ export interface DrawerProps extends BaseDrawerProps {
|
|
57
92
|
}
|
58
93
|
|
59
94
|
export const Drawer: React.FC<DrawerProps> = (props) => {
|
60
|
-
const {
|
95
|
+
const { children, isOpen, onClose, ...rest } = props
|
61
96
|
return (
|
62
|
-
<BaseDrawer {...rest}>
|
63
|
-
<DrawerBody>
|
64
|
-
|
65
|
-
|
97
|
+
<BaseDrawer isOpen={isOpen} onClose={onClose} {...rest}>
|
98
|
+
<DrawerBody>
|
99
|
+
{runIfFn(children, {
|
100
|
+
isOpen,
|
101
|
+
onClose,
|
102
|
+
})}
|
103
|
+
</DrawerBody>
|
66
104
|
</BaseDrawer>
|
67
105
|
)
|
68
106
|
}
|
package/src/form.tsx
CHANGED
@@ -1,6 +1,12 @@
|
|
1
1
|
import * as React from 'react'
|
2
2
|
|
3
|
-
import {
|
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 {
|
@@ -10,16 +16,28 @@ import {
|
|
10
16
|
FormProps,
|
11
17
|
FieldValues,
|
12
18
|
FieldResolver,
|
19
|
+
FieldProps,
|
20
|
+
FormType,
|
21
|
+
DefaultFieldOverrides,
|
13
22
|
} from '@saas-ui/forms'
|
14
23
|
|
24
|
+
import { YupFormType } from '@saas-ui/forms/yup'
|
25
|
+
import { ZodFormType } from '@saas-ui/forms/zod'
|
26
|
+
|
15
27
|
import { BaseModal, BaseModalProps } from './modal'
|
16
28
|
|
29
|
+
export type FormDialogFieldOverrides = DefaultFieldOverrides & {
|
30
|
+
cancel?: ButtonProps
|
31
|
+
}
|
32
|
+
|
17
33
|
export interface FormDialogProps<
|
34
|
+
TSchema = any,
|
18
35
|
TFieldValues extends FieldValues = FieldValues,
|
19
|
-
TContext extends object = object
|
36
|
+
TContext extends object = object,
|
37
|
+
TFieldTypes = FieldProps<TFieldValues>
|
20
38
|
> extends Omit<BaseModalProps, 'children'>,
|
21
39
|
Pick<
|
22
|
-
FormProps<TFieldValues, TContext>,
|
40
|
+
FormProps<TSchema, TFieldValues, TContext, TFieldTypes>,
|
23
41
|
| 'schema'
|
24
42
|
| 'defaultValues'
|
25
43
|
| 'values'
|
@@ -35,110 +53,157 @@ export interface FormDialogProps<
|
|
35
53
|
| 'shouldUseNativeValidation'
|
36
54
|
| 'criteriaMode'
|
37
55
|
| 'delayError'
|
56
|
+
| 'resetOptions'
|
57
|
+
| 'children'
|
38
58
|
> {
|
39
59
|
/**
|
40
60
|
* The modal footer, will be wrapped with `ModalFooter`.
|
41
61
|
* Defaults to a cancel and submit button.
|
42
62
|
*/
|
43
63
|
footer?: React.ReactNode
|
44
|
-
/**
|
45
|
-
* The cancel button label
|
46
|
-
* @default "Cancel"
|
47
|
-
*/
|
48
|
-
cancelLabel?: React.ReactNode
|
49
|
-
/**
|
50
|
-
* The submit button label
|
51
|
-
* @default "Submit"
|
52
|
-
*/
|
53
|
-
submitLabel?: React.ReactNode
|
54
|
-
/**
|
55
|
-
* If no children are passed, this will auto render fields based on the supplied schema.
|
56
|
-
*/
|
57
|
-
children?: React.ReactNode
|
58
64
|
/**
|
59
65
|
* A schema field resolver used to auto generate form fields.
|
60
66
|
*/
|
61
67
|
fieldResolver?: FieldResolver
|
68
|
+
/**
|
69
|
+
* Field overrides
|
70
|
+
*/
|
71
|
+
fields?: FormDialogFieldOverrides
|
62
72
|
}
|
73
|
+
|
74
|
+
const useFormProps = (props: FormDialogProps) => {
|
75
|
+
const {
|
76
|
+
schema,
|
77
|
+
resolver,
|
78
|
+
fieldResolver,
|
79
|
+
defaultValues,
|
80
|
+
values,
|
81
|
+
context,
|
82
|
+
onChange,
|
83
|
+
onSubmit,
|
84
|
+
onError,
|
85
|
+
mode,
|
86
|
+
reValidateMode,
|
87
|
+
shouldFocusError = true,
|
88
|
+
shouldUnregister,
|
89
|
+
shouldUseNativeValidation,
|
90
|
+
criteriaMode,
|
91
|
+
delayError = 100,
|
92
|
+
fields,
|
93
|
+
...modalProps
|
94
|
+
} = props
|
95
|
+
|
96
|
+
const formProps = {
|
97
|
+
schema,
|
98
|
+
resolver,
|
99
|
+
defaultValues,
|
100
|
+
values,
|
101
|
+
context,
|
102
|
+
onChange,
|
103
|
+
onSubmit,
|
104
|
+
onError,
|
105
|
+
mode,
|
106
|
+
reValidateMode,
|
107
|
+
shouldFocusError,
|
108
|
+
shouldUnregister,
|
109
|
+
shouldUseNativeValidation,
|
110
|
+
criteriaMode,
|
111
|
+
delayError,
|
112
|
+
fields,
|
113
|
+
}
|
114
|
+
|
115
|
+
return { modalProps, formProps, fields }
|
116
|
+
}
|
117
|
+
|
63
118
|
/**
|
64
|
-
*
|
65
|
-
*
|
66
|
-
* @see Docs https://saas-ui.dev/docs/components/overlay/form-dialog
|
119
|
+
* @todo make this dynamic to support other schema types
|
67
120
|
*/
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
isOpen,
|
98
|
-
onClose,
|
99
|
-
...rest
|
100
|
-
} = props
|
101
|
-
|
102
|
-
const formProps = {
|
103
|
-
ref,
|
104
|
-
schema,
|
105
|
-
resolver,
|
106
|
-
defaultValues,
|
107
|
-
values,
|
108
|
-
context,
|
109
|
-
onChange,
|
110
|
-
onSubmit,
|
111
|
-
onError,
|
112
|
-
mode,
|
113
|
-
reValidateMode,
|
114
|
-
shouldFocusError,
|
115
|
-
shouldUnregister,
|
116
|
-
shouldUseNativeValidation,
|
117
|
-
criteriaMode,
|
118
|
-
delayError,
|
119
|
-
}
|
121
|
+
type MergeDialogProps<T> = T extends YupFormType<
|
122
|
+
infer FieldDefs,
|
123
|
+
infer ExtraProps,
|
124
|
+
infer ExtraOverrides,
|
125
|
+
'yup'
|
126
|
+
>
|
127
|
+
? YupFormType<
|
128
|
+
FieldDefs,
|
129
|
+
ExtraProps & Omit<BaseModalProps, 'children'>,
|
130
|
+
ExtraOverrides & FormDialogFieldOverrides
|
131
|
+
>
|
132
|
+
: T extends ZodFormType<
|
133
|
+
infer FieldDefs,
|
134
|
+
infer ExtraProps,
|
135
|
+
infer ExtraOverrides,
|
136
|
+
'zod'
|
137
|
+
>
|
138
|
+
? ZodFormType<
|
139
|
+
FieldDefs,
|
140
|
+
ExtraProps & Omit<BaseModalProps, 'children'>,
|
141
|
+
ExtraOverrides & FormDialogFieldOverrides
|
142
|
+
>
|
143
|
+
: T extends FormType<infer FieldDefs, infer ExtraProps, infer ExtraOverrides>
|
144
|
+
? FormType<
|
145
|
+
FieldDefs,
|
146
|
+
ExtraProps & Omit<BaseModalProps, 'children'>,
|
147
|
+
ExtraOverrides & FormDialogFieldOverrides
|
148
|
+
>
|
149
|
+
: never
|
120
150
|
|
151
|
+
type IsSchemaType<T, Schema, FieldDefs> = T extends DefaultFormType<FieldDefs>
|
152
|
+
? T extends (
|
153
|
+
props: FormProps<infer TSchema, infer TFieldValues, infer TContext>
|
154
|
+
) => any
|
155
|
+
? Schema extends TSchema
|
156
|
+
? true
|
157
|
+
: false
|
158
|
+
: false
|
159
|
+
: false
|
160
|
+
|
161
|
+
export type DefaultFormType<
|
162
|
+
FieldDefs = any,
|
163
|
+
ExtraProps = object,
|
164
|
+
ExtraOverrides = FormDialogFieldOverrides
|
165
|
+
> = (<
|
166
|
+
TSchema = unknown,
|
167
|
+
TFieldValues extends Record<string, any> = any,
|
168
|
+
TContext extends object = object
|
169
|
+
>(
|
170
|
+
props: any
|
171
|
+
) => React.ReactElement) & {
|
172
|
+
displayName?: string
|
173
|
+
id?: string
|
174
|
+
}
|
175
|
+
|
176
|
+
export function createFormDialog<
|
177
|
+
FieldDefs = any,
|
178
|
+
ExtraProps = object,
|
179
|
+
ExtraOverrides = FormDialogFieldOverrides,
|
180
|
+
TFormType extends DefaultFormType<
|
181
|
+
FieldDefs,
|
182
|
+
ExtraProps,
|
183
|
+
ExtraOverrides
|
184
|
+
> = DefaultFormType<FieldDefs, ExtraProps, ExtraOverrides>
|
185
|
+
>(Form: TFormType) {
|
186
|
+
const Dialog = forwardRef<any, 'div'>((props, ref) => {
|
187
|
+
const { isOpen, onClose, footer, children, ...rest } = props
|
188
|
+
const { modalProps, formProps, fields } = useFormProps(rest)
|
121
189
|
return (
|
122
|
-
<BaseModal isOpen={isOpen} onClose={onClose}
|
123
|
-
<Form
|
124
|
-
{(form) => (
|
190
|
+
<BaseModal {...modalProps} isOpen={isOpen} onClose={onClose}>
|
191
|
+
<Form ref={ref} {...(formProps as any)}>
|
192
|
+
{(form: any) => (
|
125
193
|
<>
|
126
|
-
<ModalBody>
|
127
|
-
{runIfFn(children, form) || (
|
128
|
-
<AutoFields
|
129
|
-
schema={schema}
|
130
|
-
fieldResolver={fieldResolver}
|
131
|
-
focusFirstField
|
132
|
-
/>
|
133
|
-
)}
|
134
|
-
</ModalBody>
|
194
|
+
<ModalBody>{runIfFn(children, form) || <AutoFields />}</ModalBody>
|
135
195
|
|
136
196
|
{footer || (
|
137
197
|
<ModalFooter>
|
138
|
-
<Button
|
139
|
-
|
198
|
+
<Button
|
199
|
+
variant="ghost"
|
200
|
+
mr={3}
|
201
|
+
onClick={onClose}
|
202
|
+
{...fields?.cancel}
|
203
|
+
>
|
204
|
+
{fields?.cancel?.children ?? 'Cancel'}
|
140
205
|
</Button>
|
141
|
-
<SubmitButton
|
206
|
+
<SubmitButton {...fields?.submit} />
|
142
207
|
</ModalFooter>
|
143
208
|
)}
|
144
209
|
</>
|
@@ -146,9 +211,17 @@ export const FormDialog = forwardRef(
|
|
146
211
|
</Form>
|
147
212
|
</BaseModal>
|
148
213
|
)
|
149
|
-
}
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
214
|
+
}) as MergeDialogProps<TFormType>
|
215
|
+
|
216
|
+
Dialog.displayName = `${Form.displayName || Form.name}Dialog`
|
217
|
+
Dialog.id = Form.id
|
218
|
+
|
219
|
+
return Dialog
|
220
|
+
}
|
221
|
+
|
222
|
+
/**
|
223
|
+
* Can be used to quickly request information from people without leaving the current page.
|
224
|
+
*
|
225
|
+
* @see Docs https://saas-ui.dev/docs/components/overlay/form-dialog
|
226
|
+
*/
|
227
|
+
export const FormDialog = createFormDialog(Form)
|
package/src/index.ts
CHANGED
package/src/menu.tsx
CHANGED
@@ -10,6 +10,7 @@ import {
|
|
10
10
|
useMultiStyleConfig,
|
11
11
|
Menu,
|
12
12
|
MenuListProps,
|
13
|
+
useBreakpointValue,
|
13
14
|
} from '@chakra-ui/react'
|
14
15
|
|
15
16
|
import { BaseModal, BaseModalProps } from './modal'
|
@@ -55,7 +56,8 @@ export const MenuDialogList = forwardRef<MenuDialogListProps, 'div'>(
|
|
55
56
|
footer,
|
56
57
|
initialFocusRef,
|
57
58
|
hideCloseButton,
|
58
|
-
motionPreset,
|
59
|
+
motionPreset = 'slideInBottom',
|
60
|
+
isCentered: isCenteredProp,
|
59
61
|
...rest
|
60
62
|
} = props
|
61
63
|
|
@@ -65,6 +67,8 @@ export const MenuDialogList = forwardRef<MenuDialogListProps, 'div'>(
|
|
65
67
|
|
66
68
|
const styles = useMultiStyleConfig('Menu', props)
|
67
69
|
|
70
|
+
const isCentered = useBreakpointValue({ base: true, md: false })
|
71
|
+
|
68
72
|
return (
|
69
73
|
<BaseModal
|
70
74
|
isOpen={isOpen}
|
@@ -73,6 +77,8 @@ export const MenuDialogList = forwardRef<MenuDialogListProps, 'div'>(
|
|
73
77
|
title={title}
|
74
78
|
hideCloseButton={hideCloseButton}
|
75
79
|
motionPreset={motionPreset}
|
80
|
+
isCentered={isCenteredProp ?? isCentered}
|
81
|
+
contentProps={{ mx: 4 }}
|
76
82
|
>
|
77
83
|
{/* We forward the styles again, otherwise the modal styles will be picked up */}
|
78
84
|
<StylesProvider value={styles}>
|
package/src/modal.tsx
CHANGED
@@ -9,13 +9,25 @@ import {
|
|
9
9
|
ModalBody,
|
10
10
|
ModalCloseButton,
|
11
11
|
ModalProps as ChakraModalProps,
|
12
|
+
ModalContentProps,
|
13
|
+
ModalHeaderProps,
|
14
|
+
ModalFooterProps,
|
12
15
|
} from '@chakra-ui/react'
|
16
|
+
import { MaybeRenderProp } from '@chakra-ui/react-utils'
|
17
|
+
import { runIfFn } from '@chakra-ui/utils'
|
13
18
|
|
14
|
-
export interface BaseModalProps extends ChakraModalProps {
|
19
|
+
export interface BaseModalProps extends Omit<ChakraModalProps, 'children'> {
|
15
20
|
/**
|
16
21
|
* The modal title
|
17
22
|
*/
|
18
23
|
title?: React.ReactNode
|
24
|
+
/**
|
25
|
+
* The modal children
|
26
|
+
*/
|
27
|
+
children: MaybeRenderProp<{
|
28
|
+
isOpen: boolean
|
29
|
+
onClose: () => void
|
30
|
+
}>
|
19
31
|
/**
|
20
32
|
* The modal footer
|
21
33
|
*/
|
@@ -28,6 +40,18 @@ export interface BaseModalProps extends ChakraModalProps {
|
|
28
40
|
* Hide the overlay
|
29
41
|
*/
|
30
42
|
hideOverlay?: boolean
|
43
|
+
/**
|
44
|
+
* Props for the modal header
|
45
|
+
*/
|
46
|
+
headerProps?: ModalHeaderProps
|
47
|
+
/**
|
48
|
+
* Props for the modal content
|
49
|
+
*/
|
50
|
+
contentProps?: ModalContentProps
|
51
|
+
/**
|
52
|
+
* Props for the modal footer
|
53
|
+
*/
|
54
|
+
footerProps?: ModalFooterProps
|
31
55
|
}
|
32
56
|
|
33
57
|
export const BaseModal: React.FC<BaseModalProps> = (props) => {
|
@@ -39,26 +63,37 @@ export const BaseModal: React.FC<BaseModalProps> = (props) => {
|
|
39
63
|
onClose,
|
40
64
|
hideCloseButton,
|
41
65
|
hideOverlay,
|
66
|
+
headerProps,
|
67
|
+
contentProps,
|
68
|
+
footerProps,
|
42
69
|
...rest
|
43
70
|
} = props
|
44
71
|
return (
|
45
72
|
<ChakraModal isOpen={isOpen} onClose={onClose} {...rest}>
|
46
73
|
{!hideOverlay && <ModalOverlay />}
|
47
|
-
<ModalContent>
|
48
|
-
{title && <ModalHeader>{title}</ModalHeader>}
|
74
|
+
<ModalContent {...contentProps}>
|
75
|
+
{title && <ModalHeader {...headerProps}>{title}</ModalHeader>}
|
49
76
|
{!hideCloseButton && <ModalCloseButton />}
|
50
|
-
{children
|
51
|
-
|
77
|
+
{runIfFn(children, {
|
78
|
+
isOpen,
|
79
|
+
onClose,
|
80
|
+
})}
|
81
|
+
{footer && <ModalFooter {...footerProps}>{footer}</ModalFooter>}
|
52
82
|
</ModalContent>
|
53
83
|
</ChakraModal>
|
54
84
|
)
|
55
85
|
}
|
56
86
|
|
57
87
|
export const Modal: React.FC<BaseModalProps> = (props) => {
|
58
|
-
const { children, ...rest } = props
|
88
|
+
const { children, isOpen, onClose, ...rest } = props
|
59
89
|
return (
|
60
|
-
<BaseModal {...rest}>
|
61
|
-
<ModalBody>
|
90
|
+
<BaseModal {...rest} isOpen={isOpen} onClose={onClose}>
|
91
|
+
<ModalBody>
|
92
|
+
{runIfFn(children, {
|
93
|
+
isOpen,
|
94
|
+
onClose,
|
95
|
+
})}
|
96
|
+
</ModalBody>
|
62
97
|
</BaseModal>
|
63
98
|
)
|
64
99
|
}
|