All checks were successful
Deploy Brew Application / deploy (push) Successful in 11s
102 lines
2.9 KiB
JavaScript
102 lines
2.9 KiB
JavaScript
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 (
|
|
<div
|
|
className={`fixed inset-0 bg-[rgba(44,24,16,0.4)] flex items-end justify-center transition-opacity duration-200 ease-in-out ${zIndex} ${active ? "opacity-100" : "opacity-0"}`}
|
|
onClick={() => handleClose()}
|
|
>
|
|
<div
|
|
className={`bg-brew-bg dark:bg-[#150B07] rounded-t-3xl w-full transition-transform duration-200 ease-in-out ${padding} ${className}`}
|
|
onClick={e => 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 */}
|
|
<div
|
|
className="w-full py-4 -mt-2 cursor-grab active:cursor-grabbing flex justify-center select-none"
|
|
onTouchStart={(e) => 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}
|
|
>
|
|
<div className="w-9 h-1 bg-brew-border dark:bg-[#3B2217] rounded" />
|
|
</div>
|
|
|
|
{/* Content */}
|
|
{typeof children === "function" ? children(handleClose) : children}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|