| import * as Dialog from '@radix-ui/react-dialog' |
| import clsx from 'clsx' |
| import { X } from 'lucide-react' |
|
|
| const sizes = { |
| sm: 'max-w-lg', |
| md: 'max-w-2xl', |
| lg: 'max-w-5xl', |
| } |
|
|
| export default function Modal({ |
| open, |
| onOpenChange, |
| title, |
| description, |
| size = 'md', |
| children, |
| footer, |
| className, |
| }) { |
| return ( |
| <Dialog.Root open={open} onOpenChange={onOpenChange}> |
| <Dialog.Portal> |
| <Dialog.Overlay className="fixed inset-0 z-50 bg-black/45" /> |
| <Dialog.Content |
| className={clsx( |
| 'fixed left-1/2 top-1/2 z-50 w-[calc(100%-1.5rem)] max-h-[calc(100vh-1.5rem)] -translate-x-1/2 -translate-y-1/2 overflow-hidden rounded-lg border border-border bg-card shadow-panel md:w-full', |
| sizes[size], |
| className, |
| )} |
| > |
| <div className="border-b border-border px-5 py-4 sm:px-6"> |
| <div className="flex items-start justify-between gap-4"> |
| <div> |
| <Dialog.Title className="font-display text-xl font-semibold text-foreground"> |
| {title} |
| </Dialog.Title> |
| {description ? ( |
| <Dialog.Description className="mt-1 max-w-2xl text-sm leading-6 text-muted-foreground"> |
| {description} |
| </Dialog.Description> |
| ) : null} |
| </div> |
| <Dialog.Close |
| className="rounded-lg p-2 text-muted-foreground transition hover:bg-secondary hover:text-foreground" |
| aria-label="Close dialog" |
| > |
| <X className="h-4 w-4" /> |
| </Dialog.Close> |
| </div> |
| </div> |
| <div className="max-h-[calc(100vh-12rem)] overflow-y-auto px-5 py-5 sm:px-6">{children}</div> |
| {footer ? <div className="border-t border-border px-5 py-4 sm:px-6">{footer}</div> : null} |
| </Dialog.Content> |
| </Dialog.Portal> |
| </Dialog.Root> |
| ) |
| } |
|
|