@@ -5,11 +5,14 @@ import styled from "@emotion/styled";
55// Context for passing hover state and trigger ref from wrapper to tooltip
66interface TooltipContextValue {
77 isHovered : boolean ;
8+ setIsHovered : ( value : boolean ) => void ;
89 triggerRef : React . RefObject < HTMLElement > | null ;
910}
1011
1112const TooltipContext = createContext < TooltipContextValue > ( {
1213 isHovered : false ,
14+ // eslint-disable-next-line @typescript-eslint/no-empty-function
15+ setIsHovered : ( ) => { } ,
1316 triggerRef : null ,
1417} ) ;
1518
@@ -22,14 +25,30 @@ interface TooltipWrapperProps {
2225export const TooltipWrapper : React . FC < TooltipWrapperProps > = ( { inline = false , children } ) => {
2326 const [ isHovered , setIsHovered ] = useState ( false ) ;
2427 const triggerRef = useRef < HTMLSpanElement > ( null ) ;
28+ const leaveTimerRef = useRef < NodeJS . Timeout | null > ( null ) ;
29+
30+ const handleMouseEnter = ( ) => {
31+ if ( leaveTimerRef . current ) {
32+ clearTimeout ( leaveTimerRef . current ) ;
33+ leaveTimerRef . current = null ;
34+ }
35+ setIsHovered ( true ) ;
36+ } ;
37+
38+ const handleMouseLeave = ( ) => {
39+ // Delay hiding to allow moving mouse to tooltip
40+ leaveTimerRef . current = setTimeout ( ( ) => {
41+ setIsHovered ( false ) ;
42+ } , 100 ) ;
43+ } ;
2544
2645 return (
27- < TooltipContext . Provider value = { { isHovered, triggerRef } } >
46+ < TooltipContext . Provider value = { { isHovered, setIsHovered , triggerRef } } >
2847 < StyledWrapper
2948 ref = { triggerRef }
3049 inline = { inline }
31- onMouseEnter = { ( ) => setIsHovered ( true ) }
32- onMouseLeave = { ( ) => setIsHovered ( false ) }
50+ onMouseEnter = { handleMouseEnter }
51+ onMouseLeave = { handleMouseLeave }
3352 >
3453 { children }
3554 </ StyledWrapper >
@@ -49,6 +68,7 @@ interface TooltipProps {
4968 position ?: "top" | "bottom" ;
5069 children : React . ReactNode ;
5170 className ?: string ;
71+ interactive ?: boolean ;
5272}
5373
5474export const Tooltip : React . FC < TooltipProps > = ( {
@@ -57,9 +77,11 @@ export const Tooltip: React.FC<TooltipProps> = ({
5777 position = "top" ,
5878 children,
5979 className = "tooltip" ,
80+ interactive = false ,
6081} ) => {
61- const { isHovered, triggerRef } = useContext ( TooltipContext ) ;
82+ const { isHovered, setIsHovered , triggerRef } = useContext ( TooltipContext ) ;
6283 const tooltipRef = useRef < HTMLDivElement > ( null ) ;
84+ const leaveTimerRef = useRef < NodeJS . Timeout | null > ( null ) ;
6385 const [ tooltipState , setTooltipState ] = useState < {
6486 style : React . CSSProperties ;
6587 arrowStyle : React . CSSProperties ;
@@ -170,6 +192,22 @@ export const Tooltip: React.FC<TooltipProps> = ({
170192 return ( ) => cancelAnimationFrame ( rafId ) ;
171193 } , [ isHovered , align , position , triggerRef ] ) ;
172194
195+ const handleTooltipMouseEnter = ( ) => {
196+ if ( interactive ) {
197+ if ( leaveTimerRef . current ) {
198+ clearTimeout ( leaveTimerRef . current ) ;
199+ leaveTimerRef . current = null ;
200+ }
201+ setIsHovered ( true ) ;
202+ }
203+ } ;
204+
205+ const handleTooltipMouseLeave = ( ) => {
206+ if ( interactive ) {
207+ setIsHovered ( false ) ;
208+ }
209+ } ;
210+
173211 if ( ! isHovered ) {
174212 return null ;
175213 }
@@ -187,6 +225,9 @@ export const Tooltip: React.FC<TooltipProps> = ({
187225 } }
188226 width = { width }
189227 className = { className }
228+ interactive = { interactive }
229+ onMouseEnter = { handleTooltipMouseEnter }
230+ onMouseLeave = { handleTooltipMouseLeave }
190231 >
191232 { children }
192233 < Arrow style = { tooltipState . arrowStyle } />
@@ -195,7 +236,7 @@ export const Tooltip: React.FC<TooltipProps> = ({
195236 ) ;
196237} ;
197238
198- const StyledTooltip = styled . div < { width : string } > `
239+ const StyledTooltip = styled . div < { width : string ; interactive : boolean } > `
199240 background-color: #2d2d30;
200241 color: #cccccc;
201242 text-align: left;
@@ -209,8 +250,18 @@ const StyledTooltip = styled.div<{ width: string }>`
209250 font-family: var(--font-primary);
210251 border: 1px solid #464647;
211252 box-shadow: 0 2px 8px rgba(0, 0, 0, 0.4);
212- pointer-events: none;
253+ pointer-events: ${ ( props ) => ( props . interactive ? "auto" : " none" ) } ;
213254 /* No default visibility/opacity - controlled via inline styles */
255+
256+ a {
257+ color: #4ec9b0;
258+ text-decoration: underline;
259+ cursor: pointer;
260+
261+ &:hover {
262+ color: #6fd9c0;
263+ }
264+ }
214265` ;
215266
216267const Arrow = styled . div `
0 commit comments