import { CloseOutlined } from "@ant-design/icons"; import { Button } from "antd"; import { useCallback, useEffect, useRef, useState } from "react"; import { useAppSelector } from "redux/hooks"; interface ProBottomSheetProps { isOpen: boolean; onClose: () => void; title?: string; children: React.ReactNode; height?: string | number; showHandle?: boolean; showCloseButton?: boolean; backdrop?: boolean; snapPoints?: string[]; initialSnap?: number; className?: string; themeName?: "light" | "dark"; } export function ProBottomSheet({ isOpen, onClose, title, children, height = "50vh", showHandle = true, showCloseButton = true, backdrop = true, snapPoints = ["50vh"], initialSnap = 0, className = "", }: ProBottomSheetProps) { const [currentSnap, setCurrentSnap] = useState(initialSnap); const [isDragging, setIsDragging] = useState(false); const startPosRef = useRef(0); const sheetRef = useRef(null); const { themeName } = useAppSelector((state) => state.theme); // Handle body scroll locking useEffect(() => { if (isOpen) { document.body.style.overflow = "hidden"; } else { document.body.style.overflow = ""; } return () => { document.body.style.overflow = ""; }; }, [isOpen]); // Event handlers const startDrag = useCallback((clientY: number) => { setIsDragging(true); startPosRef.current = clientY; }, []); const handleDrag = useCallback( (clientY: number) => { if (!isDragging || !sheetRef.current) return; const deltaY = clientY - startPosRef.current; sheetRef.current.style.transform = `translateY(${Math.max(0, deltaY)}px)`; }, [isDragging] ); const endDrag = useCallback( (clientY: number) => { if (!isDragging) return; setIsDragging(false); const deltaY = clientY - startPosRef.current; const threshold = 50; // Reduced threshold for better responsiveness if (deltaY > threshold) { // Swipe down if (currentSnap > 0) { setCurrentSnap(currentSnap - 1); } else { onClose(); } } else if (deltaY < -threshold && currentSnap < snapPoints.length - 1) { // Swipe up setCurrentSnap(currentSnap + 1); } // Reset transform if (sheetRef.current) { sheetRef.current.style.transform = "translateY(0)"; } }, [isDragging, currentSnap, snapPoints.length, onClose] ); // Touch event handlers const handleTouchStart = useCallback( (e: React.TouchEvent) => { startDrag(e.touches[0].clientY); }, [startDrag] ); const handleTouchMove = useCallback( (e: React.TouchEvent) => { handleDrag(e.touches[0].clientY); }, [handleDrag] ); const handleTouchEnd = useCallback( (e: React.TouchEvent) => { endDrag(e.changedTouches[0].clientY); }, [endDrag] ); // Mouse event handlers const handleMouseDown = useCallback( (e: React.MouseEvent) => { startDrag(e.clientY); }, [startDrag] ); const handleMouseMove = useCallback( (e: MouseEvent) => { handleDrag(e.clientY); }, [handleDrag] ); const handleMouseUp = useCallback( (e: MouseEvent) => { endDrag(e.clientY); }, [endDrag] ); // Mouse event listeners useEffect(() => { if (isDragging) { document.addEventListener("mousemove", handleMouseMove); document.addEventListener("mouseup", handleMouseUp); } return () => { document.removeEventListener("mousemove", handleMouseMove); document.removeEventListener("mouseup", handleMouseUp); }; }, [isDragging, handleMouseMove, handleMouseUp]); // Enhanced styles with better dark themeName support const backdropStyle: React.CSSProperties = { position: "fixed", top: 0, left: 0, right: 0, bottom: 0, backgroundColor: themeName === "dark" ? "rgba(0, 0, 0, 0.7)" : "rgba(0, 0, 0, 0.5)", backdropFilter: "blur(4px)", zIndex: 1000, opacity: isOpen ? 1 : 0, visibility: isOpen ? "visible" : "hidden", transition: "opacity 0.3s ease, visibility 0.3s ease", pointerEvents: isOpen ? "auto" : "none", }; const sheetStyle: React.CSSProperties = { position: "fixed", left: 0, right: 0, bottom: 0, height: snapPoints[currentSnap] || height, backgroundColor: themeName === "dark" ? "#0a0a0a" : "#ffffff", borderTopLeftRadius: 20, borderTopRightRadius: 20, boxShadow: themeName === "dark" ? "0 -8px 32px rgba(0, 0, 0, 0.6)" : "0 -4px 20px rgba(0, 0, 0, 0.15)", zIndex: 1001, transform: isOpen ? "translateY(0)" : "translateY(100%)", transition: isDragging ? "none" : "transform 0.3s cubic-bezier(0.2, 0, 0, 1), height 0.3s ease", display: "flex", flexDirection: "column", maxHeight: "95vh", border: `1px solid ${themeName === "dark" ? "#363636" : "#e5e7eb"}`, borderBottom: "none", backdropFilter: themeName === "dark" ? "blur(12px)" : "none", width: "100vw", }; const handleStyle: React.CSSProperties = { width: 40, height: 4, backgroundColor: themeName === "dark" ? "#525252" : "#d1d5db", borderRadius: 2, margin: "12px auto 8px", cursor: "grab", transition: "background-color 0.3s ease", }; const headerStyle: React.CSSProperties = { padding: "16px 20px", borderBottom: `1px solid ${themeName === "dark" ? "#363636" : "#e5e7eb"}`, display: "flex", alignItems: "center", justifyContent: "space-between", minHeight: 60, backgroundColor: themeName === "dark" ? "rgba(42, 42, 42, 0.8)" : "transparent", backdropFilter: themeName === "dark" ? "blur(8px)" : "none", }; const contentStyle: React.CSSProperties = { flex: 1, overflow: "auto", padding: "20px", backgroundColor: themeName === "dark" ? "#0a0a0a" : "#ffffff", color: themeName === "dark" ? "#ffffff" : "#000000", }; const closeButtonStyle: React.CSSProperties = { color: themeName === "dark" ? "#ffffff" : "#4D5154", display: "flex", alignItems: "center", justifyContent: "center", padding: 8, borderRadius: "50%", backgroundColor: themeName === "dark" ? "rgba(54, 54, 54, 0.8)" : "#EDEEEE", border: `1px solid ${themeName === "dark" ? "#424242" : "transparent"}`, transition: "all 0.3s ease", cursor: "pointer", }; const titleStyle: React.CSSProperties = { fontSize: 18, fontWeight: 600, color: themeName === "dark" ? "#ffffff" : "#1f2937", margin: 0, textShadow: themeName === "dark" ? "0 1px 2px rgba(0, 0, 0, 0.3)" : "none", transition: "color 0.3s ease", }; return ( <> {backdrop &&
}
{showHandle && (
{ if (handleStyle) { handleStyle.backgroundColor = themeName === "dark" ? "#6b6b6b" : "#9ca3af"; } }} onMouseLeave={() => { if (handleStyle) { handleStyle.backgroundColor = themeName === "dark" ? "#525252" : "#d1d5db"; } }} /> )} {(title || showCloseButton) && (

{title}

{showCloseButton && (
)}
{children}
); }