import { useState, useEffect } from "react"; export default function Modal({ onClose, children, zIndex = "z-[100]", maxWidth = "480px", maxHeight = "88vh", padding = "px-5 pb-8", className = "" }) { const [active, setActive] = useState(false); const [dragY, setDragY] = useState(0); const [isDragging, setIsDragging] = useState(false); const [startY, setStartY] = useState(0); useEffect(() => { // Prevent background scroll when mounted document.body.style.overflow = "hidden"; // Trigger transition shortly after mounting const raf = requestAnimationFrame(() => setActive(true)); return () => { cancelAnimationFrame(raf); document.body.style.overflow = ""; }; }, []); const handleClose = (callback) => { setActive(false); setTimeout(() => { onClose(); if (typeof callback === "function") { callback(); } }, 200); }; const handleDragStart = (clientY) => { setIsDragging(true); setStartY(clientY); }; const handleDragMove = (clientY) => { if (!isDragging) return; const deltaY = clientY - startY; if (deltaY > 0) { setDragY(deltaY); } }; const handleDragEnd = () => { if (!isDragging) return; setIsDragging(false); if (dragY > 100) { handleClose(); } else { setDragY(0); } }; return (
handleClose()} >
e.stopPropagation()} style={{ maxWidth, maxHeight, transform: isDragging ? `translateY(${dragY}px)` : active ? `translateY(${dragY}px)` : "translateY(100%)", transition: isDragging ? "none" : "transform 0.2s ease-out" }} > {/* Grab Handle */}
handleDragStart(e.touches[0].clientY)} onTouchMove={(e) => handleDragMove(e.touches[0].clientY)} onTouchEnd={handleDragEnd} onMouseDown={(e) => handleDragStart(e.clientY)} onMouseMove={(e) => { if (e.buttons === 1) handleDragMove(e.clientY); }} onMouseUp={handleDragEnd} onMouseLeave={handleDragEnd} >
{/* Content */} {typeof children === "function" ? children(handleClose) : children}
); }