mirror of
https://github.com/supabase/supabase.git
synced 2026-10-18 07:39:07 +03:00
176 lines
5.4 KiB
TypeScript
176 lines
5.4 KiB
TypeScript
'use client'
|
|
|
|
import React, { forwardRef, useEffect } from 'react'
|
|
import styleHandler from '../../lib/theme/styleHandler'
|
|
import { cn } from '../../lib/utils/cn'
|
|
import { Button, ButtonVariantProps } from '../Button/Button'
|
|
import {
|
|
Dialog,
|
|
DialogContent,
|
|
DialogFooter,
|
|
DialogHeader,
|
|
DialogSection,
|
|
DialogSectionSeparator,
|
|
DialogTitle,
|
|
DialogTrigger,
|
|
DialogDescription,
|
|
DialogClose,
|
|
} from '../shadcn/ui/dialog'
|
|
|
|
export interface ModalProps extends React.ComponentProps<typeof DialogContent> {
|
|
Separator?: React.ComponentType
|
|
Content?: React.ComponentType
|
|
visible?: boolean
|
|
// @deprecated please add the footer directly in component children. This is to prepare for using <DialogFooter/> component
|
|
customFooter?: React.ReactNode
|
|
description?: string
|
|
// @deprecated please add the footer directly in component children. This is to prepare for using <DialogFooter/> component
|
|
hideFooter?: boolean
|
|
// @deprecated please add the footer directly in component children. This is to prepare for using <DialogFooter/> component
|
|
alignFooter?: 'right' | 'left'
|
|
// @deprecated please add the footer directly in component children. This is to prepare for using <DialogFooter/> component
|
|
layout?: 'horizontal' | 'vertical'
|
|
loading?: boolean
|
|
onCancel?: any
|
|
cancelText?: string
|
|
onConfirm?: any
|
|
confirmText?: string
|
|
showCloseButton?: boolean
|
|
footerBackground?: boolean
|
|
// @deprecated please add the footer directly in component children. This is to prepare for using <DialogFooter/> component
|
|
variant?: ButtonVariantProps['type']
|
|
overlayStyle?: React.CSSProperties
|
|
contentStyle?: React.CSSProperties
|
|
dialogOverlayProps?: React.ComponentProps<typeof DialogContent>['dialogOverlayProps']
|
|
// @deprecated please consider using <Dialog/> and <DialogTrigger/> components
|
|
triggerElement?: React.ReactNode
|
|
// @deprecated please consider using <Dialog/> and <DialogHeader/> components
|
|
header?: React.ReactNode
|
|
modal?: React.ComponentProps<typeof Dialog>['modal']
|
|
defaultOpen?: React.ComponentProps<typeof Dialog>['defaultOpen']
|
|
/**
|
|
* @deprecated No longer in use
|
|
*/
|
|
closable?: boolean
|
|
}
|
|
|
|
interface ModalType
|
|
extends React.ForwardRefExoticComponent<
|
|
React.ComponentPropsWithoutRef<typeof DialogContent> & ModalProps
|
|
> {
|
|
Content: React.ComponentType<{ children: React.ReactNode; className?: string }>
|
|
Separator: React.ComponentType
|
|
}
|
|
|
|
// @deprecated use <Dialog/> instead
|
|
const Modal = forwardRef<
|
|
React.ElementRef<typeof DialogContent>,
|
|
React.ComponentPropsWithoutRef<typeof DialogContent> & ModalProps
|
|
>(
|
|
(
|
|
{
|
|
children,
|
|
customFooter = undefined,
|
|
description,
|
|
hideFooter = false,
|
|
alignFooter = 'left',
|
|
layout = 'horizontal',
|
|
loading = false,
|
|
cancelText = 'Cancel',
|
|
onConfirm = () => {},
|
|
onCancel = () => {},
|
|
confirmText = 'Confirm',
|
|
showCloseButton = true,
|
|
footerBackground,
|
|
variant = 'success',
|
|
visible = false,
|
|
size = 'large',
|
|
style,
|
|
overlayStyle,
|
|
contentStyle,
|
|
triggerElement,
|
|
header,
|
|
modal,
|
|
defaultOpen,
|
|
...props
|
|
},
|
|
ref
|
|
) => {
|
|
const [open, setOpen] = React.useState(visible ? visible : false)
|
|
const __styles = styleHandler('modal')
|
|
|
|
useEffect(() => {
|
|
setOpen(visible)
|
|
}, [visible])
|
|
|
|
const footerContent = customFooter ? (
|
|
customFooter
|
|
) : (
|
|
<div
|
|
className="flex w-full space-x-2"
|
|
style={{
|
|
width: '100%',
|
|
justifyContent:
|
|
layout === 'vertical' ? 'center' : alignFooter === 'right' ? 'flex-end' : 'flex-start',
|
|
}}
|
|
>
|
|
<Button type="default" onClick={onCancel} disabled={loading}>
|
|
{cancelText}
|
|
</Button>
|
|
<Button
|
|
onClick={onConfirm}
|
|
disabled={loading}
|
|
loading={loading}
|
|
type={variant === 'danger' ? 'danger' : 'primary'}
|
|
>
|
|
{confirmText}
|
|
</Button>
|
|
</div>
|
|
)
|
|
|
|
function handleOpenChange(open: boolean) {
|
|
if (visible !== undefined && !open) {
|
|
// controlled component behavior
|
|
onCancel()
|
|
} else {
|
|
// un-controlled component behavior
|
|
setOpen(open)
|
|
}
|
|
}
|
|
|
|
return (
|
|
<Dialog open={open} defaultOpen={defaultOpen} onOpenChange={handleOpenChange} modal={modal}>
|
|
{triggerElement && <DialogTrigger>{triggerElement}</DialogTrigger>}
|
|
<DialogContent ref={ref} hideClose={!showCloseButton} {...props} size={size}>
|
|
{header || description ? (
|
|
<DialogHeader className={cn('border-b')} padding={'small'}>
|
|
{header && <DialogTitle>{header}</DialogTitle>}
|
|
{description && <DialogDescription>{description}</DialogDescription>}
|
|
</DialogHeader>
|
|
) : null}
|
|
{children}
|
|
{!hideFooter && <DialogFooter padding={'small'}>{footerContent}</DialogFooter>}
|
|
</DialogContent>
|
|
</Dialog>
|
|
)
|
|
}
|
|
) as ModalType
|
|
|
|
const Content = forwardRef<
|
|
React.ElementRef<typeof DialogSection>,
|
|
React.ComponentPropsWithoutRef<typeof DialogSection>
|
|
>(({ ...props }, ref) => {
|
|
return <DialogSection ref={ref} {...props} padding="small" className={cn(props.className)} />
|
|
})
|
|
|
|
const Separator = forwardRef<
|
|
React.ElementRef<typeof DialogSectionSeparator>,
|
|
React.ComponentPropsWithoutRef<typeof DialogSectionSeparator>
|
|
>(({ ...props }, ref) => {
|
|
return <DialogSectionSeparator ref={ref} {...props} />
|
|
})
|
|
|
|
Modal.Content = Content
|
|
Modal.Separator = Separator
|
|
export default Modal
|