Modal.tsx172 lines · main
1'use client'
2
3import React, { forwardRef, useEffect } from 'react'
4
5import { cn } from '../../lib/utils/cn'
6import { Button, ButtonVariantProps } from '../Button/Button'
7import {
8 Dialog,
9 DialogContent,
10 DialogDescription,
11 DialogFooter,
12 DialogHeader,
13 DialogSection,
14 DialogSectionSeparator,
15 DialogTitle,
16 DialogTrigger,
17} from '../shadcn/ui/dialog'
18
19export interface ModalProps extends React.ComponentProps<typeof DialogContent> {
20 Separator?: React.ComponentType
21 Content?: React.ComponentType
22 visible?: boolean
23 /** @deprecated please add the footer directly in component children. This is to prepare for using <DialogFooter/> component */
24 customFooter?: React.ReactNode
25 description?: string
26 /** @deprecated please add the footer directly in component children. This is to prepare for using <DialogFooter/> component */
27 hideFooter?: boolean
28 /** @deprecated please add the footer directly in component children. This is to prepare for using <DialogFooter/> component */
29 alignFooter?: 'right' | 'left'
30 /** @deprecated please add the footer directly in component children. This is to prepare for using <DialogFooter/> component */
31 layout?: 'horizontal' | 'vertical'
32 loading?: boolean
33 onCancel?: any
34 cancelText?: string
35 onConfirm?: any
36 confirmText?: string
37 showCloseButton?: boolean
38 footerBackground?: boolean
39 /** @deprecated please add the footer directly in component children. This is to prepare for using <DialogFooter/> component */
40 variant?: ButtonVariantProps['type']
41 overlayStyle?: React.CSSProperties
42 contentStyle?: React.CSSProperties
43 dialogOverlayProps?: React.ComponentProps<typeof DialogContent>['dialogOverlayProps']
44 /** @deprecated please consider using <Dialog/> and <DialogTrigger/> components */
45 triggerElement?: React.ReactNode
46 /** @deprecated please consider using <Dialog/> and <DialogHeader/> components */
47 header?: React.ReactNode
48 modal?: React.ComponentProps<typeof Dialog>['modal']
49 defaultOpen?: React.ComponentProps<typeof Dialog>['defaultOpen']
50 /**
51 * @deprecated No longer in use
52 */
53 closable?: boolean
54}
55
56interface ModalType extends React.ForwardRefExoticComponent<
57 React.ComponentPropsWithoutRef<typeof DialogContent> & ModalProps
58> {
59 Content: React.ComponentType<{ children: React.ReactNode; className?: string }>
60 Separator: React.ComponentType
61}
62
63/** @deprecated Use `import { Dialog } from "ui"` instead */
64const Modal = forwardRef<
65 React.ElementRef<typeof DialogContent>,
66 React.ComponentPropsWithoutRef<typeof DialogContent> & ModalProps
67>(
68 (
69 {
70 children,
71 customFooter = undefined,
72 description,
73 hideFooter = false,
74 alignFooter = 'left',
75 layout = 'horizontal',
76 loading = false,
77 cancelText = 'Cancel',
78 onConfirm = () => {},
79 onCancel = () => {},
80 confirmText = 'Confirm',
81 showCloseButton = true,
82 footerBackground,
83 variant = 'success',
84 visible = false,
85 size = 'large',
86 style,
87 overlayStyle,
88 contentStyle,
89 triggerElement,
90 header,
91 modal,
92 defaultOpen,
93 ...props
94 },
95 ref
96 ) => {
97 const [open, setOpen] = React.useState(visible ? visible : false)
98
99 useEffect(() => {
100 setOpen(visible)
101 }, [visible])
102
103 const footerContent = customFooter ? (
104 customFooter
105 ) : (
106 <div
107 className="flex w-full space-x-2"
108 style={{
109 width: '100%',
110 justifyContent:
111 layout === 'vertical' ? 'center' : alignFooter === 'right' ? 'flex-end' : 'flex-start',
112 }}
113 >
114 <Button type="default" onClick={onCancel} disabled={loading}>
115 {cancelText}
116 </Button>
117 <Button
118 onClick={onConfirm}
119 disabled={loading}
120 loading={loading}
121 type={variant === 'danger' ? 'danger' : variant === 'warning' ? 'warning' : 'primary'}
122 >
123 {confirmText}
124 </Button>
125 </div>
126 )
127
128 function handleOpenChange(open: boolean) {
129 if (visible !== undefined && !open) {
130 // controlled component behavior
131 onCancel()
132 } else {
133 // un-controlled component behavior
134 setOpen(open)
135 }
136 }
137
138 return (
139 <Dialog open={open} defaultOpen={defaultOpen} onOpenChange={handleOpenChange} modal={modal}>
140 {triggerElement && <DialogTrigger>{triggerElement}</DialogTrigger>}
141 <DialogContent ref={ref} hideClose={!showCloseButton} {...props} size={size}>
142 {header || description ? (
143 <DialogHeader className={cn('border-b')} padding={'small'}>
144 {header && <DialogTitle>{header}</DialogTitle>}
145 {description && <DialogDescription>{description}</DialogDescription>}
146 </DialogHeader>
147 ) : null}
148 {children}
149 {!hideFooter && <DialogFooter padding={'small'}>{footerContent}</DialogFooter>}
150 </DialogContent>
151 </Dialog>
152 )
153 }
154) as ModalType
155
156const Content = forwardRef<
157 React.ElementRef<typeof DialogSection>,
158 React.ComponentPropsWithoutRef<typeof DialogSection>
159>(({ ...props }, ref) => {
160 return <DialogSection ref={ref} {...props} padding="small" className={cn(props.className)} />
161})
162
163const Separator = forwardRef<
164 React.ElementRef<typeof DialogSectionSeparator>,
165 React.ComponentPropsWithoutRef<typeof DialogSectionSeparator>
166>(({ ...props }, ref) => {
167 return <DialogSectionSeparator ref={ref} {...props} />
168})
169
170Modal.Content = Content
171Modal.Separator = Separator
172export default Modal