"use client"; import { createPortal } from "react-dom"; import type { ButtonHTMLAttributes, ReactNode } from "react"; import { X } from "lucide-react"; import { cn } from "@/lib/utils"; type ModalSize = "sm" | "md" | "lg" | "xl"; type ModalAction = Omit< ButtonHTMLAttributes, "className" > & { label: ReactNode; icon?: ReactNode; variant?: "primary" | "secondary" | "danger"; }; interface ModalProps { open: boolean; onClose: () => void; children: ReactNode; breadcrumbs?: ReactNode[]; title?: ReactNode; icon?: ReactNode; headerAction?: ReactNode; size?: ModalSize; className?: string; footerInfo?: ReactNode; footerStatus?: ReactNode; primaryAction?: ModalAction; secondaryAction?: ModalAction; cancelAction?: ModalAction | false; } const sizeClassName: Record = { sm: "max-w-md", md: "max-w-xl", lg: "max-w-2xl", xl: "max-w-4xl", }; export function Modal({ open, onClose, children, breadcrumbs, title, icon, headerAction, size = "lg", className, footerInfo, footerStatus, primaryAction, secondaryAction, cancelAction, }: ModalProps) { const hasHeader = breadcrumbs?.length || title || icon; const hasFooter = footerInfo || footerStatus || primaryAction || secondaryAction || cancelAction; const resolvedCancelAction = cancelAction === undefined && primaryAction ? { label: "Cancel", onClick: onClose } : cancelAction; if (!open) return null; return createPortal(
e.stopPropagation()} > {hasHeader && (
{breadcrumbs?.length ? (
{breadcrumbs.map((segment, index) => ( {index > 0 && } {segment} ))}
{headerAction}
) : (
{icon}

{title}

{headerAction}
)}
)}
{children}
{hasFooter && (
{(secondaryAction || footerInfo) && (
{secondaryAction && ( )} {footerInfo}
)}
{footerStatus} {resolvedCancelAction && ( )} {primaryAction && ( )}
)}
, document.body, ); } function ModalActionButton({ action, fallbackVariant, }: { action: ModalAction; fallbackVariant: "primary" | "secondary" | "danger" | "cancel"; }) { const { label, icon, variant = fallbackVariant === "cancel" ? "secondary" : fallbackVariant, ...props } = action; return ( ); }