Skip to content

Commit 982492a

Browse files
authored
feat: add tooltip component for links & sponsors data (#7586)
1 parent 7d17d29 commit 982492a

File tree

3 files changed

+136
-32
lines changed

3 files changed

+136
-32
lines changed

src/components/Navigation/Navigation.jsx

+10-8
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { Link as ReactDOMLink, NavLink, useLocation } from 'react-router-dom';
88
import Link from '../Link/Link';
99
import Logo from '../Logo/Logo';
1010
import Dropdown from '../Dropdown/Dropdown';
11+
import Tooltip from '../Tooltip/Tooltip';
1112

1213
// Load Styling
1314
import '@docsearch/css';
@@ -70,14 +71,15 @@ NavigationIcon.propTypes = {
7071
};
7172
function NavigationIcon({ children, to, title }) {
7273
return (
73-
<Link
74-
to={to}
75-
className="inline-flex items-center text-gray-100 dark:text-gray-200 hover:text-blue-200"
76-
title={`webpack on ${title}`}
77-
aria-label={`webpack on ${title}`}
78-
>
79-
{children}
80-
</Link>
74+
<Tooltip content={`webpack on ${title}`}>
75+
<Link
76+
to={to}
77+
className="inline-flex items-center text-gray-100 dark:text-gray-200 hover:text-blue-200"
78+
aria-label={`webpack on ${title}`}
79+
>
80+
{children}
81+
</Link>
82+
</Tooltip>
8183
);
8284
}
8385
const navigationIconProps = {

src/components/Support/Support.jsx

+30-24
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import VisibilitySensor from 'react-visibility-sensor';
77
import Backers from './_supporters.json';
88
import Additional from './AdditionalSupporters';
99
import SmallIcon from '../../assets/icon-square-small-slack.png';
10+
import Tooltip from '../Tooltip/Tooltip';
1011

1112
// Load Styling
1213
import './Support.scss';
@@ -227,34 +228,39 @@ export default class Support extends Component {
227228
</div>
228229

229230
{supporters.map((supporter, index) => (
230-
<a
231+
<Tooltip
231232
key={supporter.slug || index}
232-
className="support__item"
233-
title={`$${formatMoney(supporter.totalDonations / 100)} by ${
233+
content={`$${formatMoney(supporter.totalDonations / 100)} by ${
234234
supporter.name || supporter.slug
235235
} ($${formatMoney(supporter.monthlyDonations / 100)} monthly)`}
236-
target="_blank"
237-
rel="noopener noreferrer nofollow"
238-
href={
239-
supporter.website ||
240-
`https://opencollective.com/${supporter.slug}`
241-
}
242236
>
243-
{
244-
<img
245-
className={`support__${rank}-avatar`}
246-
src={
247-
inView && supporter.avatar ? supporter.avatar : SmallIcon
248-
}
249-
alt={
250-
supporter.name || supporter.slug
251-
? `${supporter.name || supporter.slug}'s avatar`
252-
: 'avatar'
253-
}
254-
onError={this._handleImgError}
255-
/>
256-
}
257-
</a>
237+
<a
238+
className="support__item"
239+
target="_blank"
240+
rel="noopener noreferrer nofollow"
241+
href={
242+
supporter.website ||
243+
`https://opencollective.com/${supporter.slug}`
244+
}
245+
>
246+
{
247+
<img
248+
className={`support__${rank}-avatar`}
249+
src={
250+
inView && supporter.avatar
251+
? supporter.avatar
252+
: SmallIcon
253+
}
254+
alt={
255+
supporter.name || supporter.slug
256+
? `${supporter.name || supporter.slug}'s avatar`
257+
: 'avatar'
258+
}
259+
onError={this._handleImgError}
260+
/>
261+
}
262+
</a>
263+
</Tooltip>
258264
))}
259265

260266
<div className="support__bottom">

src/components/Tooltip/Tooltip.jsx

+96
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import { useState } from 'react';
2+
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 ${className}`}
35+
onMouseEnter={showTooltip}
36+
onMouseLeave={hideTooltip}
37+
onFocus={showTooltip}
38+
onBlur={hideTooltip}
39+
>
40+
{children}
41+
{isVisible && (
42+
<div
43+
className={`
44+
absolute z-[9999]
45+
${positions[position]}
46+
${isDelayed ? 'opacity-100' : 'opacity-0'}
47+
transition-opacity duration-200
48+
pointer-events-none
49+
`}
50+
>
51+
<div
52+
className="text-white text-xs font-medium p-10 rounded-md whitespace-nowrap shadow-xl"
53+
style={{ backgroundColor: bgColor }}
54+
>
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+
)}
81+
</div>
82+
</div>
83+
)}
84+
</div>
85+
);
86+
};
87+
88+
Tooltip.propTypes = {
89+
children: PropTypes.node.isRequired,
90+
content: PropTypes.node.isRequired,
91+
position: PropTypes.oneOf(['top', 'right', 'bottom', 'left']),
92+
delay: PropTypes.number,
93+
className: PropTypes.string,
94+
};
95+
96+
export default Tooltip;

0 commit comments

Comments
 (0)