Extract useClickOutside hook, dedupe popover close-on-outside-click logic
RegexHelpIcon and LogPreview's ColumnPicker each reimplemented the same mousedown-outside-ref-closes-popover effect; share it via a small hook. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TeyhRfMYjMditek6geDRoQ
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
a47f771087
commit
c0174963d8
@@ -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<HTMLDivElement>(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 (
|
||||
<div ref={ref} className="relative">
|
||||
|
||||
@@ -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<HTMLDivElement>(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 (
|
||||
<div
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import { useEffect, type RefObject } from 'react'
|
||||
|
||||
// Shared by popover-style UI (RegexHelpIcon, LogPreview's ColumnPicker) that closes itself when a
|
||||
// mousedown lands outside its container ref, while `active`.
|
||||
export function useClickOutside(ref: RefObject<HTMLElement | null>, 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])
|
||||
}
|
||||
Reference in New Issue
Block a user