diff --git a/src/components/BulkActionBar.tsx b/src/components/BulkActionBar.tsx index 328b175..a0340f5 100644 --- a/src/components/BulkActionBar.tsx +++ b/src/components/BulkActionBar.tsx @@ -1,6 +1,7 @@ import { useEffect, useRef, useState } from "react"; import { useGalleryStore } from "../store"; import { BulkTagPopover } from "./bulk/BulkTagPopover"; +import { useDismissable } from "./menu"; import { Tooltip } from "./Tooltip" type Panel = "tag" | "rating" | "album" | "delete" | null; @@ -28,15 +29,8 @@ export function BulkActionBar() { const [newAlbumName, setNewAlbumName] = useState(""); const barRef = useRef(null); - // Close any open popover when clicking outside the bar. - useEffect(() => { - const onPointerDown = (event: PointerEvent) => { - if (barRef.current?.contains(event.target as Node)) return; - setPanel(null); - }; - window.addEventListener("pointerdown", onPointerDown); - return () => window.removeEventListener("pointerdown", onPointerDown); - }, []); + // Close any open popover when clicking outside the bar or pressing Escape. + useDismissable(barRef, () => setPanel(null), panel !== null); // Reset transient UI whenever the selection empties. useEffect(() => { diff --git a/src/components/ColorFilter.tsx b/src/components/ColorFilter.tsx index 200ed7e..bdb80f5 100644 --- a/src/components/ColorFilter.tsx +++ b/src/components/ColorFilter.tsx @@ -1,6 +1,7 @@ -import { useEffect, useRef, useState } from "react"; +import { useRef, useState } from "react"; import { AnimatePresence, motion } from "framer-motion"; import { useGalleryStore } from "../store"; +import { useDismissable } from "./menu"; import { Tooltip } from "./Tooltip"; type Rgb = [number, number, number]; @@ -45,15 +46,8 @@ export function ColorFilter() { const isActive = colorFilter !== null; const isCustom = isActive && !SWATCHES.some((swatch) => rgbEquals(colorFilter, swatch.rgb)); - // Collapse the panel when clicking elsewhere. - useEffect(() => { - if (!open) return; - const onPointerDown = (event: PointerEvent) => { - if (ref.current && !ref.current.contains(event.target as Node)) setOpen(false); - }; - window.addEventListener("pointerdown", onPointerDown); - return () => window.removeEventListener("pointerdown", onPointerDown); - }, [open]); + // Collapse the panel when clicking elsewhere or pressing Escape. + useDismissable(ref, () => setOpen(false), open); return (