Skip to content

Commit a5cd7e2

Browse files
committed
Improve hover performance on bar chart
1 parent 91c0b6e commit a5cd7e2

File tree

3 files changed

+25
-29
lines changed

3 files changed

+25
-29
lines changed

apps/webapp/app/components/primitives/charts/ChartBar.tsx

Lines changed: 8 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import {
33
Bar,
44
BarChart,
55
CartesianGrid,
6-
Cell,
76
ReferenceArea,
87
ReferenceLine,
98
XAxis,
@@ -15,9 +14,7 @@ import { ChartTooltip, ChartTooltipContent } from "~/components/primitives/chart
1514
import { useChartContext } from "./ChartContext";
1615
import { ChartBarInvalid, ChartBarLoading, ChartBarNoData } from "./ChartLoading";
1716
import { useHasNoData } from "./ChartRoot";
18-
// Legend is now rendered by ChartRoot outside the chart container
1917
import { ZoomTooltip, useZoomHandlers } from "./ChartZoom";
20-
import { getBarOpacity } from "./hooks/useHighlightState";
2118

2219
//TODO: fix the first and last bars in a stack not having rounded corners
2320

@@ -116,9 +113,8 @@ export function ChartBarRenderer({
116113
onMouseDown={zoomHandlers.onMouseDown}
117114
onMouseMove={(e: any) => {
118115
zoomHandlers.onMouseMove?.(e);
119-
// Update active payload for legend
120116
if (e?.activePayload?.length) {
121-
highlight.setActivePayload(e.activePayload);
117+
highlight.setActivePayload(e.activePayload, e.activeTooltipIndex);
122118
highlight.setTooltipActive(true);
123119
} else {
124120
highlight.setTooltipActive(false);
@@ -189,6 +185,11 @@ export function ChartBarRenderer({
189185
)}
190186

191187
{visibleSeries.map((key, index, array) => {
188+
const dimmed =
189+
!zoom?.isSelecting &&
190+
highlight.activeBarKey !== null &&
191+
highlight.activeBarKey !== key;
192+
192193
return (
193194
<Bar
194195
key={key}
@@ -204,7 +205,7 @@ export function ChartBarRenderer({
204205
] as [number, number, number, number]
205206
}
206207
activeBar={false}
207-
fillOpacity={1}
208+
fillOpacity={dimmed ? 0.2 : 1}
208209
onClick={(data, index, e) => handleBarClick(data, e)}
209210
onMouseEnter={(entry, index) => {
210211
if (entry.tooltipPayload?.[0]) {
@@ -214,20 +215,7 @@ export function ChartBarRenderer({
214215
}}
215216
onMouseLeave={highlight.reset}
216217
isAnimationActive={false}
217-
>
218-
{data.map((_, dataIndex) => {
219-
// Don't dim bars during zoom selection
220-
const opacity = zoom?.isSelecting ? 1 : getBarOpacity(key, dataIndex, highlight);
221-
222-
return (
223-
<Cell
224-
key={`cell-${key}-${dataIndex}`}
225-
fill={config[key]?.color}
226-
fillOpacity={opacity}
227-
/>
228-
);
229-
})}
230-
</Bar>
218+
/>
231219
);
232220
})}
233221

apps/webapp/app/components/primitives/charts/ChartLine.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -142,9 +142,8 @@ export function ChartLineRenderer({
142142
right: 12,
143143
}}
144144
onMouseMove={(e: any) => {
145-
// Update active payload for legend
146145
if (e?.activePayload?.length) {
147-
highlight.setActivePayload(e.activePayload);
146+
highlight.setActivePayload(e.activePayload, e.activeTooltipIndex);
148147
highlight.setTooltipActive(true);
149148
} else {
150149
highlight.setTooltipActive(false);
@@ -191,9 +190,8 @@ export function ChartLineRenderer({
191190
right: 12,
192191
}}
193192
onMouseMove={(e: any) => {
194-
// Update active payload for legend
195193
if (e?.activePayload?.length) {
196-
highlight.setActivePayload(e.activePayload);
194+
highlight.setActivePayload(e.activePayload, e.activeTooltipIndex);
197195
highlight.setTooltipActive(true);
198196
} else {
199197
highlight.setTooltipActive(false);

apps/webapp/app/components/primitives/charts/hooks/useHighlightState.ts

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useCallback, useState } from "react";
1+
import { useCallback, useRef, useState } from "react";
22

33
export type HighlightState = {
44
/** The currently highlighted series key (e.g., "completed", "failed") */
@@ -16,8 +16,8 @@ export type HighlightActions = {
1616
setHoveredBar: (key: string, index: number, payload?: any[]) => void;
1717
/** Set the hovered legend item (highlights all bars of that type) */
1818
setHoveredLegendItem: (key: string) => void;
19-
/** Set the active payload (for tooltip data) */
20-
setActivePayload: (payload: any[] | null) => void;
19+
/** Set the active payload (for tooltip data). Pass tooltipIndex to skip redundant updates. */
20+
setActivePayload: (payload: any[] | null, tooltipIndex?: number | null) => void;
2121
/** Set tooltip active state */
2222
setTooltipActive: (active: boolean) => void;
2323
/** Reset all highlight state */
@@ -39,6 +39,7 @@ const initialState: HighlightState = {
3939
*/
4040
export function useHighlightState(): UseHighlightStateReturn {
4141
const [state, setState] = useState<HighlightState>(initialState);
42+
const activeTooltipIndexRef = useRef<number | null>(null);
4243

4344
const setHoveredBar = useCallback((key: string, index: number, payload?: any[]) => {
4445
setState({
@@ -53,11 +54,19 @@ export function useHighlightState(): UseHighlightStateReturn {
5354
setState((prev) => ({
5455
...prev,
5556
activeBarKey: key,
56-
activeDataPointIndex: null, // null indicates legend hover (all bars of this type)
57+
activeDataPointIndex: null,
5758
}));
5859
}, []);
5960

60-
const setActivePayload = useCallback((payload: any[] | null) => {
61+
const setActivePayload = useCallback((payload: any[] | null, tooltipIndex?: number | null) => {
62+
const idx = tooltipIndex ?? null;
63+
if (idx !== null && idx === activeTooltipIndexRef.current) {
64+
console.log("Tooltip index is the same, skipping update", activeTooltipIndexRef.current);
65+
return;
66+
}
67+
68+
console.log("Tooltip index changed", idx);
69+
activeTooltipIndexRef.current = idx;
6170
setState((prev) => ({
6271
...prev,
6372
activePayload: payload,
@@ -72,6 +81,7 @@ export function useHighlightState(): UseHighlightStateReturn {
7281
}, []);
7382

7483
const reset = useCallback(() => {
84+
activeTooltipIndexRef.current = null;
7585
setState(initialState);
7686
}, []);
7787

0 commit comments

Comments
 (0)