import * as React from "react"; import { Dialog, DialogContent as BaseDialogContent, } from "@/components/ui/dialog"; import { cn } from "@/lib/utils"; interface DisclosureOptions { isOpen?: boolean; } type OpenChangeHandler = (open?: boolean) => void; export function useDisclosure(options: DisclosureOptions = {}) { const [isOpen, setIsOpen] = React.useState(Boolean(options.isOpen)); const onOpen = React.useCallback(() => { setIsOpen(true); }, []); const onClose = React.useCallback(() => { setIsOpen(false); }, []); const onOpenChange = React.useCallback((open) => { setIsOpen((prev) => (typeof open === "boolean" ? open : !prev)); }, []); return { isOpen, onClose, onOpen, onOpenChange, }; } interface ModalContextValue { classNames?: Record; onClose: () => void; scrollBehavior?: "inside" | "outside"; size?: ModalSize; } const ModalContext = React.createContext(null); function useModalContext() { return React.useContext(ModalContext); } type ModalSize = "sm" | "md" | "lg" | "xl" | "2xl" | "4xl" | "full"; function mapSize(size: ModalSize | undefined) { if (size === "sm") { return "max-w-md"; } if (size === "lg") { return "max-w-2xl"; } if (size === "xl") { return "max-w-3xl"; } if (size === "2xl") { return "max-w-5xl"; } if (size === "4xl") { return "max-w-6xl"; } if (size === "full") { return "w-[95vw] max-w-[95vw] h-[95vh] max-h-[95vh]"; } return "max-w-lg"; } export interface ModalProps { backdrop?: "blur" | "opaque" | "transparent"; children: React.ReactNode; className?: string; classNames?: Record; isDismissable?: boolean; isOpen?: boolean; onClose?: () => void; onOpenChange?: (open: boolean) => void; placement?: "center" | "top" | "bottom"; scrollBehavior?: "inside" | "outside"; size?: ModalSize; } export function Modal({ children, classNames, isOpen = false, onClose, onOpenChange, scrollBehavior, size, }: ModalProps) { const handleOpenChange = (open: boolean) => { onOpenChange?.(open); if (!open) { onClose?.(); } }; const contextValue = { classNames, onClose: () => { handleOpenChange(false); }, scrollBehavior, size, }; return ( {children} ); } interface ModalContentProps extends Omit, "children"> { children: React.ReactNode | ((onClose: () => void) => React.ReactNode); scrollBehavior?: "inside" | "outside"; size?: ModalSize; } export function ModalContent({ children, className, scrollBehavior, size, ...props }: ModalContentProps) { const context = useModalContext(); const resolvedScrollBehavior = scrollBehavior ?? context?.scrollBehavior; const resolvedSize = size ?? context?.size; const renderedChildren = typeof children === "function" ? children(() => context?.onClose()) : children; return ( [data-slot=modal-body]]:min-h-0 [&>[data-slot=modal-body]]:flex-1 [&>[data-slot=modal-body]]:overflow-y-auto" : "", className, )} showCloseButton={false} {...props} > {renderedChildren} ); } export function ModalHeader({ className, ...props }: React.ComponentProps<"div">) { const context = useModalContext(); return (
); } export function ModalBody({ className, ...props }: React.ComponentProps<"div">) { const context = useModalContext(); return (
); } export function ModalFooter({ className, ...props }: React.ComponentProps<"div">) { const context = useModalContext(); return (
); }