"use client"; import { useRef } from "react"; import { motion, AnimatePresence } from "framer-motion"; type ModalVariant = "sheet" | "dialog"; interface ModalProps { open: boolean; onClose: () => void; children: React.ReactNode; variant?: ModalVariant; /** Used for aria-labelledby; the element with this id should contain the dialog title */ titleId?: string; } const sheet = { backdrop: "fixed inset-0 z-50 flex items-end justify-center bg-black/60 backdrop-blur-sm sm:items-center", content: "relative w-full max-w-sm rounded-t-3xl bg-surface px-5 pb-8 pt-5 shadow-2xl ring-1 ring-border sm:rounded-3xl sm:pb-6", initial: { y: "100%" }, animate: { y: 0 }, exit: { y: "100%" }, transition: { type: "spring" as const, damping: 28, stiffness: 350 }, }; const dialog = { backdrop: "fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm", content: "mx-6 w-full max-w-xs rounded-2xl bg-surface px-6 py-6 shadow-2xl ring-1 ring-border", initial: { scale: 0.9, opacity: 0 }, animate: { scale: 1, opacity: 1 }, exit: { scale: 0.9, opacity: 0 }, transition: { type: "spring" as const, damping: 25, stiffness: 350 }, }; const variants = { sheet, dialog }; export default function Modal({ open, onClose, children, variant = "sheet", titleId, }: ModalProps) { const backdropRef = useRef(null); const v = variants[variant]; return ( {open && ( { if (e.target === backdropRef.current) onClose(); }} > {children} )} ); }