diff --git a/web/app/components/base/tooltip/TooltipManager.ts b/web/app/components/base/tooltip/TooltipManager.ts new file mode 100644 index 000000000..e1b7f7ba9 --- /dev/null +++ b/web/app/components/base/tooltip/TooltipManager.ts @@ -0,0 +1,16 @@ +class TooltipManager { + private activeCloser: (() => void) | null = null + + register(closeFn: () => void) { + if (this.activeCloser) + this.activeCloser() + this.activeCloser = closeFn + } + + clear(closeFn: () => void) { + if (this.activeCloser === closeFn) + this.activeCloser = null + } +} + +export const tooltipManager = new TooltipManager() diff --git a/web/app/components/base/tooltip/index.tsx b/web/app/components/base/tooltip/index.tsx index 92a1afe69..eb7ca56cb 100644 --- a/web/app/components/base/tooltip/index.tsx +++ b/web/app/components/base/tooltip/index.tsx @@ -6,6 +6,8 @@ import type { OffsetOptions, Placement } from '@floating-ui/react' import { RiQuestionLine } from '@remixicon/react' import cn from '@/utils/classnames' import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger } from '@/app/components/base/portal-to-follow-elem' +import { tooltipManager } from './TooltipManager' + export type TooltipProps = { position?: Placement triggerMethod?: 'hover' | 'click' @@ -56,22 +58,26 @@ const Tooltip: FC = ({ isHoverTriggerRef.current = isHoverTrigger }, [isHoverTrigger]) + const close = () => setOpen(false) + const handleLeave = (isTrigger: boolean) => { if (isTrigger) setNotHoverTrigger() - else setNotHoverPopup() // give time to move to the popup if (needsDelay) { setTimeout(() => { - if (!isHoverPopupRef.current && !isHoverTriggerRef.current) + if (!isHoverPopupRef.current && !isHoverTriggerRef.current) { setOpen(false) + tooltipManager.clear(close) + } }, 300) } else { setOpen(false) + tooltipManager.clear(close) } } @@ -87,6 +93,7 @@ const Tooltip: FC = ({ onMouseEnter={() => { if (triggerMethod === 'hover') { setHoverTrigger() + tooltipManager.register(close) setOpen(true) } }}