import { RefObject, useEffect, useRef } from "react"; /** * Shared outside-click-to-close effect. * * Seven components hand-rolled this same `mousedown` listener (AdvancedSearch, * GroupBy, QuickFilter, Setting, ColumnChooser, ExportPopup, CreateScreenPopup), * differing only in an exemption predicate. Each copy also carried a dead * `removeEventListener` in the else-branch, which never had a matching listener * to remove because the handler identity changed every render. * * `onOutside` and `isExempt` are read through refs, so a caller may pass inline * closures without the listener being re-bound on every render. */ export function useClickOutside( ref: RefObject, enabled: boolean, onOutside: (event: MouseEvent) => void, isExempt?: (target: HTMLElement, event: MouseEvent) => boolean, ) { const onOutsideRef = useRef(onOutside); const isExemptRef = useRef(isExempt); useEffect(() => { onOutsideRef.current = onOutside; isExemptRef.current = isExempt; }); useEffect(() => { if (!enabled) return; const handleClickOutside = (event: MouseEvent) => { const target = event.target as HTMLElement; if (isExemptRef.current?.(target, event)) return; if (ref.current && !ref.current.contains(target)) { onOutsideRef.current(event); } }; document.addEventListener("mousedown", handleClickOutside); return () => document.removeEventListener("mousedown", handleClickOutside); }, [ref, enabled]); } export default useClickOutside;