diff --git a/web/chat/tooltip-provider.js b/web/chat/tooltip-provider.js index 2f4339a36..d311f239a 100644 --- a/web/chat/tooltip-provider.js +++ b/web/chat/tooltip-provider.js @@ -1,129 +1,140 @@ // @flow import invariant from 'invariant'; import * as React from 'react'; import type { TooltipPositionStyle } from './tooltip-utils'; const onMouseLeaveSourceDisappearTimeoutMs = 200; +const onMouseLeaveTooltipDisappearTimeoutMs = 100; export type RenderTooltipParams = { +newNode: React.Node, +tooltipPositionStyle: TooltipPositionStyle, }; export type RenderTooltipResult = { +onMouseLeaveCallback: () => mixed, +clearTooltip: () => mixed, }; type TooltipContextType = { +renderTooltip: (params: RenderTooltipParams) => RenderTooltipResult, +clearTooltip: () => mixed, }; const TooltipContext: React.Context = React.createContext( { renderTooltip: () => ({ onMouseLeaveCallback: () => {}, clearTooltip: () => {}, }), clearTooltip: () => {}, }, ); type Props = { +children: React.Node, }; function TooltipProvider(props: Props): React.Node { const { children } = props; // eslint-disable-next-line no-unused-vars const tooltipSymbol = React.useRef(null); // eslint-disable-next-line no-unused-vars const tooltipCancelTimer = React.useRef(null); // eslint-disable-next-line no-unused-vars const [tooltipNode, setTooltipNode] = React.useState(null); const [ // eslint-disable-next-line no-unused-vars tooltipPosition, // eslint-disable-next-line no-unused-vars setTooltipPosition, ] = React.useState(null); const clearTooltip = React.useCallback((tooltipToClose: symbol) => { if (tooltipSymbol.current !== tooltipToClose) { return; } tooltipCancelTimer.current = null; setTooltipNode(null); setTooltipPosition(null); tooltipSymbol.current = null; }, []); const clearCurrentTooltip = React.useCallback(() => { if (tooltipSymbol.current) { clearTooltip(tooltipSymbol.current); } }, [clearTooltip]); const renderTooltip = React.useCallback( ({ newNode, tooltipPositionStyle: newTooltipPosition, }: RenderTooltipParams): RenderTooltipResult => { setTooltipNode(newNode); setTooltipPosition(newTooltipPosition); const newNodeSymbol = Symbol(); tooltipSymbol.current = newNodeSymbol; if (tooltipCancelTimer.current) { clearTimeout(tooltipCancelTimer.current); } return { onMouseLeaveCallback: () => { const newTimer = setTimeout( () => clearTooltip(newNodeSymbol), onMouseLeaveSourceDisappearTimeoutMs, ); tooltipCancelTimer.current = newTimer; }, clearTooltip: () => clearTooltip(newNodeSymbol), }; }, [clearTooltip], ); // eslint-disable-next-line no-unused-vars - const onMouseEnterTooltip = React.useCallback(() => {}, []); + const onMouseEnterTooltip = React.useCallback(() => { + if (tooltipSymbol.current) { + clearTimeout(tooltipCancelTimer.current); + } + }, []); // eslint-disable-next-line no-unused-vars - const onMouseLeaveTooltip = React.useCallback(() => {}, []); + const onMouseLeaveTooltip = React.useCallback(() => { + const timer = setTimeout( + clearCurrentTooltip, + onMouseLeaveTooltipDisappearTimeoutMs, + ); + tooltipCancelTimer.current = timer; + }, [clearCurrentTooltip]); const tooltip = React.useMemo(() => {}, []); const value = React.useMemo( () => ({ renderTooltip, clearTooltip: clearCurrentTooltip, }), [renderTooltip, clearCurrentTooltip], ); return ( {children} {tooltip} ); } function useTooltipContext(): TooltipContextType { const context = React.useContext(TooltipContext); invariant(context, 'TooltipContext not found'); return context; } export { TooltipProvider, useTooltipContext };