Skip to content

Commit d4ceaf5

Browse files
authored
fix: close tooltip on link redirect (#7640)
1 parent c10f8d0 commit d4ceaf5

File tree

2 files changed

+99
-79
lines changed

2 files changed

+99
-79
lines changed

src/components/Navigation/Navigation.jsx

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// Import External Dependencies
2-
import { useEffect, useState } from 'react';
2+
import { useEffect, useState, useRef } from 'react';
33
import PropTypes from 'prop-types';
44
import { DocSearch } from '@docsearch/react';
55
import { Link as ReactDOMLink, NavLink, useLocation } from 'react-router-dom';
@@ -70,12 +70,21 @@ NavigationIcon.propTypes = {
7070
title: PropTypes.string.isRequired,
7171
};
7272
function NavigationIcon({ children, to, title }) {
73+
const tooltipRef = useRef();
74+
75+
const hideTooltip = () => {
76+
if (tooltipRef.current) {
77+
tooltipRef.current.blur();
78+
}
79+
};
80+
7381
return (
74-
<Tooltip content={`webpack on ${title}`}>
82+
<Tooltip content={`webpack on ${title}`} ref={tooltipRef}>
7583
<Link
7684
to={to}
7785
className="inline-flex items-center text-gray-100 dark:text-gray-200 hover:text-blue-200"
7886
aria-label={`webpack on ${title}`}
87+
onClick={hideTooltip}
7988
>
8089
{children}
8190
</Link>

src/components/Tooltip/Tooltip.jsx

Lines changed: 88 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -1,96 +1,107 @@
1-
import { useState } from 'react';
1+
import { useState, forwardRef } from 'react';
22
import PropTypes from 'prop-types';
3-
const Tooltip = ({
4-
children,
5-
content,
6-
position = 'bottom',
7-
delay = 300,
8-
className = '',
9-
}) => {
10-
const [isVisible, setIsVisible] = useState(false);
11-
const [isDelayed, setIsDelayed] = useState(false);
12-
const showTooltip = () => {
13-
const timer = setTimeout(() => {
14-
setIsDelayed(true);
15-
}, delay);
16-
setIsVisible(true);
17-
return () => clearTimeout(timer);
18-
};
19-
const hideTooltip = () => {
20-
setIsVisible(false);
21-
setIsDelayed(false);
22-
};
23-
// Position classes - increase the margins to create more space
24-
const positions = {
25-
top: 'bottom-full left-1/2 -translate-x-1/2 mb-8',
26-
bottom: 'top-full left-1/2 -translate-x-1/2 mt-10',
27-
left: 'right-full top-1/2 -translate-y-1/2 mr-8',
28-
right: 'left-full top-1/2 -translate-y-1/2 ml-8',
29-
};
30-
// Custom background color for both tooltip and triangle
31-
const bgColor = '#526B78';
32-
return (
33-
<div
34-
className={`relative flex items-center justify-center ${className}`}
35-
onMouseEnter={showTooltip}
36-
onMouseLeave={hideTooltip}
37-
onFocus={showTooltip}
38-
onBlur={hideTooltip}
39-
>
40-
{children}
41-
{isVisible && (
42-
<div
43-
className={`
3+
4+
const Tooltip = forwardRef(
5+
(
6+
{
7+
children,
8+
content,
9+
position = 'bottom',
10+
delay = 300,
11+
className = '',
12+
onHide,
13+
},
14+
ref
15+
) => {
16+
const [isVisible, setIsVisible] = useState(false);
17+
const [isDelayed, setIsDelayed] = useState(false);
18+
const showTooltip = () => {
19+
const timer = setTimeout(() => {
20+
setIsDelayed(true);
21+
}, delay);
22+
setIsVisible(true);
23+
return () => clearTimeout(timer);
24+
};
25+
const hideTooltip = () => {
26+
setIsVisible(false);
27+
setIsDelayed(false);
28+
if (onHide) onHide();
29+
};
30+
// Position classes - increase the margins to create more space
31+
const positions = {
32+
top: 'bottom-full left-1/2 -translate-x-1/2 mb-8',
33+
bottom: 'top-full left-1/2 -translate-x-1/2 mt-10',
34+
left: 'right-full top-1/2 -translate-y-1/2 mr-8',
35+
right: 'left-full top-1/2 -translate-y-1/2 ml-8',
36+
};
37+
// Custom background color for both tooltip and triangle
38+
const bgColor = '#526B78';
39+
return (
40+
<div
41+
ref={ref}
42+
className={`relative flex items-center justify-center ${className}`}
43+
onMouseEnter={showTooltip}
44+
onMouseLeave={hideTooltip}
45+
onBlur={hideTooltip}
46+
>
47+
{children}
48+
{isVisible && (
49+
<div
50+
className={`
4451
absolute z-[9999]
4552
${positions[position]}
4653
${isDelayed ? 'opacity-100' : 'opacity-0'}
4754
transition-opacity duration-200
4855
pointer-events-none
4956
`}
50-
>
51-
<div
52-
className="text-white text-xs font-medium p-10 rounded-md whitespace-nowrap shadow-xl"
53-
style={{ backgroundColor: bgColor }}
5457
>
55-
{content}
56-
{/* Triangle/Arrow - Reduced size */}
57-
{position === 'top' && (
58-
<div
59-
className="absolute top-full left-1/2 -translate-x-1/2 border-solid border-l-[6px] border-r-[6px] border-t-[6px] border-l-transparent border-r-transparent"
60-
style={{ borderTopColor: bgColor }}
61-
></div>
62-
)}
63-
{position === 'bottom' && (
64-
<div
65-
className="absolute -top-[6px] left-1/2 -translate-x-1/2 border-solid border-l-[6px] border-r-[6px] border-b-[6px] border-l-transparent border-r-transparent"
66-
style={{ borderBottomColor: bgColor }}
67-
></div>
68-
)}
69-
{position === 'left' && (
70-
<div
71-
className="absolute top-1/2 -translate-y-1/2 right-[-6px] border-solid border-t-[6px] border-b-[6px] border-l-[6px] border-t-transparent border-b-transparent"
72-
style={{ borderLeftColor: bgColor }}
73-
></div>
74-
)}
75-
{position === 'right' && (
76-
<div
77-
className="absolute top-1/2 -translate-y-1/2 left-[-6px] border-solid border-t-[6px] border-b-[6px] border-r-[6px] border-t-transparent border-b-transparent"
78-
style={{ borderRightColor: bgColor }}
79-
></div>
80-
)}
58+
<div
59+
className="text-white text-xs font-medium p-10 rounded-md whitespace-nowrap shadow-xl"
60+
style={{ backgroundColor: bgColor }}
61+
>
62+
{content}
63+
{/* Triangle/Arrow - Reduced size */}
64+
{position === 'top' && (
65+
<div
66+
className="absolute top-full left-1/2 -translate-x-1/2 border-solid border-l-[6px] border-r-[6px] border-t-[6px] border-l-transparent border-r-transparent"
67+
style={{ borderTopColor: bgColor }}
68+
></div>
69+
)}
70+
{position === 'bottom' && (
71+
<div
72+
className="absolute -top-[6px] left-1/2 -translate-x-1/2 border-solid border-l-[6px] border-r-[6px] border-b-[6px] border-l-transparent border-r-transparent"
73+
style={{ borderBottomColor: bgColor }}
74+
></div>
75+
)}
76+
{position === 'left' && (
77+
<div
78+
className="absolute top-1/2 -translate-y-1/2 right-[-6px] border-solid border-t-[6px] border-b-[6px] border-l-[6px] border-t-transparent border-b-transparent"
79+
style={{ borderLeftColor: bgColor }}
80+
></div>
81+
)}
82+
{position === 'right' && (
83+
<div
84+
className="absolute top-1/2 -translate-y-1/2 left-[-6px] border-solid border-t-[6px] border-b-[6px] border-r-[6px] border-t-transparent border-b-transparent"
85+
style={{ borderRightColor: bgColor }}
86+
></div>
87+
)}
88+
</div>
8189
</div>
82-
</div>
83-
)}
84-
</div>
85-
);
86-
};
90+
)}
91+
</div>
92+
);
93+
}
94+
);
95+
96+
Tooltip.displayName = 'Tooltip';
8797

8898
Tooltip.propTypes = {
8999
children: PropTypes.node.isRequired,
90100
content: PropTypes.node.isRequired,
91101
position: PropTypes.oneOf(['top', 'right', 'bottom', 'left']),
92102
delay: PropTypes.number,
93103
className: PropTypes.string,
104+
onHide: PropTypes.func,
94105
};
95106

96107
export default Tooltip;

0 commit comments

Comments
 (0)