Skip to content

Commit f1887ca

Browse files
authored
fix: improve Tooltip stability and integration with DropdownMenu
1 parent 2095f06 commit f1887ca

File tree

2 files changed

+96
-71
lines changed

2 files changed

+96
-71
lines changed

src/components/Tooltip.tsx

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,22 @@ export function Tooltip({
2424
className = '',
2525
}: TooltipProps) {
2626
const [isOpen, setIsOpen] = React.useState(false)
27+
const isMounted = React.useRef(true)
28+
29+
React.useEffect(() => {
30+
isMounted.current = true
31+
return () => {
32+
isMounted.current = false
33+
}
34+
}, [])
2735

2836
const { refs, floatingStyles, context } = useFloating({
2937
open: isOpen,
30-
onOpenChange: setIsOpen,
38+
onOpenChange: (open) => {
39+
if (isMounted.current) {
40+
setIsOpen(open)
41+
}
42+
},
3143
placement,
3244
middleware: [offset(5), flip(), shift()],
3345
whileElementsMounted: autoUpdate,

src/components/npm-stats/ChartControls.tsx

Lines changed: 83 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -73,12 +73,14 @@ export function ChartControls({
7373
{/* Time Range */}
7474
<DropdownMenu>
7575
<Tooltip content="Select time range">
76-
<DropdownMenuTrigger asChild>
77-
<button className={twMerge(dropdownButtonStyles.base)}>
78-
{timeRanges.find((r) => r.value === range)?.label}
79-
<EllipsisVertical className="w-3 h-3" />
80-
</button>
81-
</DropdownMenuTrigger>
76+
<span>
77+
<DropdownMenuTrigger asChild>
78+
<button className={twMerge(dropdownButtonStyles.base)}>
79+
{timeRanges.find((r) => r.value === range)?.label}
80+
<EllipsisVertical className="w-3 h-3" />
81+
</button>
82+
</DropdownMenuTrigger>
83+
</span>
8284
</Tooltip>
8385
<DropdownMenuContent className="min-w-[200px] bg-white dark:bg-gray-800 rounded-lg shadow-lg p-2 z-50">
8486
<div className="flex justify-between items-center mb-2">
@@ -103,17 +105,19 @@ export function ChartControls({
103105
{/* Binning Interval */}
104106
<DropdownMenu>
105107
<Tooltip content="Select binning interval">
106-
<DropdownMenuTrigger asChild>
107-
<button
108-
className={twMerge(
109-
dropdownButtonStyles.base,
110-
binType !== 'weekly' && dropdownButtonStyles.active,
111-
)}
112-
>
113-
{binningOptions.find((b) => b.value === binType)?.label}
114-
<EllipsisVertical className="w-3 h-3" />
115-
</button>
116-
</DropdownMenuTrigger>
108+
<span>
109+
<DropdownMenuTrigger asChild>
110+
<button
111+
className={twMerge(
112+
dropdownButtonStyles.base,
113+
binType !== 'weekly' && dropdownButtonStyles.active,
114+
)}
115+
>
116+
{binningOptions.find((b) => b.value === binType)?.label}
117+
<EllipsisVertical className="w-3 h-3" />
118+
</button>
119+
</DropdownMenuTrigger>
120+
</span>
117121
</Tooltip>
118122
<DropdownMenuContent className="min-w-[200px] bg-white dark:bg-gray-800 rounded-lg shadow-lg p-2 z-50">
119123
<div className="flex justify-between items-center mb-2">
@@ -142,17 +146,19 @@ export function ChartControls({
142146
{/* Y-Axis Transform */}
143147
<DropdownMenu>
144148
<Tooltip content="Transform the Y-axis to show relative changes between packages. 'None' shows actual download numbers, while 'Normalize Y' shows percentage changes relative to the first data point.">
145-
<DropdownMenuTrigger asChild>
146-
<button
147-
className={twMerge(
148-
dropdownButtonStyles.base,
149-
transform !== 'none' && dropdownButtonStyles.active,
150-
)}
151-
>
152-
{transformOptions.find((opt) => opt.value === transform)?.label}
153-
<EllipsisVertical className="w-3 h-3" />
154-
</button>
155-
</DropdownMenuTrigger>
149+
<span>
150+
<DropdownMenuTrigger asChild>
151+
<button
152+
className={twMerge(
153+
dropdownButtonStyles.base,
154+
transform !== 'none' && dropdownButtonStyles.active,
155+
)}
156+
>
157+
{transformOptions.find((opt) => opt.value === transform)?.label}
158+
<EllipsisVertical className="w-3 h-3" />
159+
</button>
160+
</DropdownMenuTrigger>
161+
</span>
156162
</Tooltip>
157163
<DropdownMenuContent className="min-w-[200px] bg-white dark:bg-gray-800 rounded-lg shadow-lg p-2 z-50">
158164
<div className="flex justify-between items-center mb-2">
@@ -178,19 +184,21 @@ export function ChartControls({
178184
{showFacets && (
179185
<DropdownMenu>
180186
<Tooltip content="Split the visualization horizontally by package">
181-
<DropdownMenuTrigger asChild>
182-
<button
183-
className={twMerge(
184-
dropdownButtonStyles.base,
185-
facetX && dropdownButtonStyles.active,
186-
)}
187-
>
188-
{facetX
189-
? `Facet X by ${facetOptions.find((opt) => opt.value === facetX)?.label}`
190-
: 'No Facet X'}
191-
<EllipsisVertical className="w-3 h-3" />
192-
</button>
193-
</DropdownMenuTrigger>
187+
<span>
188+
<DropdownMenuTrigger asChild>
189+
<button
190+
className={twMerge(
191+
dropdownButtonStyles.base,
192+
facetX && dropdownButtonStyles.active,
193+
)}
194+
>
195+
{facetX
196+
? `Facet X by ${facetOptions.find((opt) => opt.value === facetX)?.label}`
197+
: 'No Facet X'}
198+
<EllipsisVertical className="w-3 h-3" />
199+
</button>
200+
</DropdownMenuTrigger>
201+
</span>
194202
</Tooltip>
195203
<DropdownMenuContent className="min-w-[200px] bg-white dark:bg-gray-800 rounded-lg shadow-lg p-2 z-50">
196204
<div className="flex justify-between items-center mb-2">
@@ -227,19 +235,21 @@ export function ChartControls({
227235
{showFacets && (
228236
<DropdownMenu>
229237
<Tooltip content="Split the visualization vertically by package">
230-
<DropdownMenuTrigger asChild>
231-
<button
232-
className={twMerge(
233-
dropdownButtonStyles.base,
234-
facetY && dropdownButtonStyles.active,
235-
)}
236-
>
237-
{facetY
238-
? `Facet Y by ${facetOptions.find((opt) => opt.value === facetY)?.label}`
239-
: 'No Facet Y'}
240-
<EllipsisVertical className="w-3 h-3" />
241-
</button>
242-
</DropdownMenuTrigger>
238+
<span>
239+
<DropdownMenuTrigger asChild>
240+
<button
241+
className={twMerge(
242+
dropdownButtonStyles.base,
243+
facetY && dropdownButtonStyles.active,
244+
)}
245+
>
246+
{facetY
247+
? `Facet Y by ${facetOptions.find((opt) => opt.value === facetY)?.label}`
248+
: 'No Facet Y'}
249+
<EllipsisVertical className="w-3 h-3" />
250+
</button>
251+
</DropdownMenuTrigger>
252+
</span>
243253
</Tooltip>
244254
<DropdownMenuContent className="min-w-[200px] bg-white dark:bg-gray-800 rounded-lg shadow-lg p-2 z-50">
245255
<div className="flex justify-between items-center mb-2">
@@ -281,22 +291,25 @@ export function ChartControls({
281291
: 'Control how data is displayed'
282292
}
283293
>
284-
<DropdownMenuTrigger asChild>
285-
<button
286-
className={twMerge(
287-
dropdownButtonStyles.base,
288-
showDataMode !== 'all' && dropdownButtonStyles.active,
289-
transform === 'normalize-y' && 'opacity-50 cursor-not-allowed',
290-
)}
291-
disabled={transform === 'normalize-y'}
292-
>
293-
{
294-
showDataModeOptions.find((opt) => opt.value === showDataMode)
295-
?.label
296-
}
297-
<EllipsisVertical className="w-3 h-3" />
298-
</button>
299-
</DropdownMenuTrigger>
294+
<span>
295+
<DropdownMenuTrigger asChild>
296+
<button
297+
className={twMerge(
298+
dropdownButtonStyles.base,
299+
showDataMode !== 'all' && dropdownButtonStyles.active,
300+
transform === 'normalize-y' &&
301+
'opacity-50 cursor-not-allowed',
302+
)}
303+
disabled={transform === 'normalize-y'}
304+
>
305+
{
306+
showDataModeOptions.find((opt) => opt.value === showDataMode)
307+
?.label
308+
}
309+
<EllipsisVertical className="w-3 h-3" />
310+
</button>
311+
</DropdownMenuTrigger>
312+
</span>
300313
</Tooltip>
301314
<DropdownMenuContent className="min-w-[200px] bg-white dark:bg-gray-800 rounded-lg shadow-lg p-2 z-50">
302315
<div className="flex justify-between items-center mb-2">

0 commit comments

Comments
 (0)