refactor: Made modal a reusable component
All checks were successful
Deploy Brew Application / deploy (push) Successful in 11s

This commit is contained in:
2026-06-06 22:36:19 +05:30
parent f95d1f3028
commit 77349f2b90
5 changed files with 272 additions and 481 deletions

101
src/components/Modal.jsx Normal file
View File

@@ -0,0 +1,101 @@
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>
);
}