diff --git a/frontend/src/components/AdminDropdown.tsx b/frontend/src/components/AdminDropdown.tsx index 081c096..7eedb55 100644 --- a/frontend/src/components/AdminDropdown.tsx +++ b/frontend/src/components/AdminDropdown.tsx @@ -1,9 +1,48 @@ -import { useEffect, useRef, useState } from "react"; +import { useCallback, useEffect, useRef, useState } from "react"; import { Link } from "react-router-dom"; +const DESKTOP_MQ = "(min-width: 769px)"; +const LEAVE_DELAY_MS = 150; + export default function AdminDropdown() { const [open, setOpen] = useState(false); const dropdownRef = useRef(null); + const isDesktopRef = useRef(false); + const leaveTimerRef = useRef | null>(null); + + // Track desktop breakpoint via matchMedia + useEffect(() => { + const mql = window.matchMedia(DESKTOP_MQ); + isDesktopRef.current = mql.matches; + const onChange = (e: MediaQueryListEvent) => { + isDesktopRef.current = e.matches; + }; + mql.addEventListener("change", onChange); + return () => mql.removeEventListener("change", onChange); + }, []); + + // Clear leave timer on unmount + useEffect(() => { + return () => { + if (leaveTimerRef.current) clearTimeout(leaveTimerRef.current); + }; + }, []); + + const handleMouseEnter = useCallback(() => { + if (leaveTimerRef.current) { + clearTimeout(leaveTimerRef.current); + leaveTimerRef.current = null; + } + if (isDesktopRef.current) setOpen(true); + }, []); + + const handleMouseLeave = useCallback(() => { + if (!isDesktopRef.current) return; + leaveTimerRef.current = setTimeout(() => { + setOpen(false); + leaveTimerRef.current = null; + }, LEAVE_DELAY_MS); + }, []); // Close on outside click useEffect(() => { @@ -31,7 +70,12 @@ export default function AdminDropdown() { }, [open]); return ( -
+