diff --git a/webapp/src/components/LogPreview.tsx b/webapp/src/components/LogPreview.tsx index da74ffe..1015034 100644 --- a/webapp/src/components/LogPreview.tsx +++ b/webapp/src/components/LogPreview.tsx @@ -1,5 +1,6 @@ import { useEffect, useRef, useState } from 'react' import { metricPrimitiveToDisplayString, type LogEntry, type ParamDeclaration } from '../lib/api' +import { useClickOutside } from '../lib/useClickOutside' import { sectionHeaderClassName, sectionHeaderLeftClassName } from './CollapsibleSection' import { HelpIcon } from './HelpIcon' @@ -37,14 +38,7 @@ function ColumnPicker({ const [open, setOpen] = useState(false) const ref = useRef(null) - useEffect(() => { - if (!open) return - function onClickOutside(e: MouseEvent) { - if (ref.current && !ref.current.contains(e.target as Node)) setOpen(false) - } - document.addEventListener('mousedown', onClickOutside) - return () => document.removeEventListener('mousedown', onClickOutside) - }, [open]) + useClickOutside(ref, () => setOpen(false), open) return (
diff --git a/webapp/src/components/RegexHelpIcon.tsx b/webapp/src/components/RegexHelpIcon.tsx index f16ba15..b172edd 100644 --- a/webapp/src/components/RegexHelpIcon.tsx +++ b/webapp/src/components/RegexHelpIcon.tsx @@ -1,4 +1,5 @@ -import { useEffect, useRef, useState } from 'react' +import { useRef, useState } from 'react' +import { useClickOutside } from '../lib/useClickOutside' // Regex syntax cheat sheet, scoped to what the Rust `regex` crate actually supports (used server-side // for message-match/field-match regexes and the filter bar's `regex(...)`) — no PCRE-only lookaround @@ -30,14 +31,7 @@ export function RegexHelpIcon({ label }: { label?: string }) { const open = pinned || hovering const ref = useRef(null) - useEffect(() => { - if (!pinned) return - function onClickOutside(e: MouseEvent) { - if (ref.current && !ref.current.contains(e.target as Node)) setPinned(false) - } - document.addEventListener('mousedown', onClickOutside) - return () => document.removeEventListener('mousedown', onClickOutside) - }, [pinned]) + useClickOutside(ref, () => setPinned(false), pinned) return (
, onOutside: () => void, active: boolean) { + useEffect(() => { + if (!active) return + function onClickOutside(e: MouseEvent) { + if (ref.current && !ref.current.contains(e.target as Node)) onOutside() + } + document.addEventListener('mousedown', onClickOutside) + return () => document.removeEventListener('mousedown', onClickOutside) + }, [active, ref, onOutside]) +}