import React, { useState, useRef, useEffect, useCallback } from "react"; import { MoreHorizontal, X } from "lucide-react"; import { clsx } from "clsx"; export interface MoreMenuItem { label: string; icon?: React.ReactNode; onClick: () => void; variant?: "default" | "danger"; disabled?: boolean; } interface MoreMenuProps { items: MoreMenuItem[]; className?: string; } export default function MoreMenu({ items, className }: MoreMenuProps) { const [isOpen, setIsOpen] = useState(false); const [isMobile, setIsMobile] = useState(false); const buttonRef = useRef(null); const menuRef = useRef(null); const sheetRef = useRef(null); const dragStartY = useRef(0); const dragCurrentY = useRef(0); useEffect(() => { const check = () => setIsMobile(window.innerWidth < 640); check(); window.addEventListener("resize", check); return () => window.removeEventListener("resize", check); }, []); useEffect(() => { if (!isOpen || isMobile) return; const handleClickOutside = (e: MouseEvent) => { if ( menuRef.current && !menuRef.current.contains(e.target as Node) && buttonRef.current && !buttonRef.current.contains(e.target as Node) ) { setIsOpen(false); } }; const handleScroll = () => setIsOpen(false); const handleEscape = (e: KeyboardEvent) => { if (e.key === "Escape") setIsOpen(false); }; document.addEventListener("mousedown", handleClickOutside); document.addEventListener("scroll", handleScroll, true); document.addEventListener("keydown", handleEscape); return () => { document.removeEventListener("mousedown", handleClickOutside); document.removeEventListener("scroll", handleScroll, true); document.removeEventListener("keydown", handleEscape); }; }, [isOpen, isMobile]); const handleTouchStart = useCallback((e: React.TouchEvent) => { dragStartY.current = e.touches[0].clientY; if (sheetRef.current) sheetRef.current.style.transition = "none"; }, []); const handleTouchMove = useCallback((e: React.TouchEvent) => { const delta = e.touches[0].clientY - dragStartY.current; dragCurrentY.current = delta; if (delta > 0 && sheetRef.current) { sheetRef.current.style.transform = `translateY(${delta}px)`; } }, []); const handleTouchEnd = useCallback(() => { if (sheetRef.current) { sheetRef.current.style.transition = "transform 0.3s ease"; if (dragCurrentY.current > 100) { sheetRef.current.style.transform = "translateY(100%)"; setTimeout(() => setIsOpen(false), 300); } else { sheetRef.current.style.transform = "translateY(0)"; } } dragCurrentY.current = 0; }, []); if (items.length === 0) return null; return (
{isOpen && isMobile && ( <>
setIsOpen(false)} />
Options
{items.map((item, i) => ( ))}
)} {isOpen && !isMobile && (
{items.map((item, i) => ( ))}
)}
); }