Skip to content

Commit e0423a0

Browse files
committed
refactor(chart): extract common chart mark interaction logic
This change creates a reusable useChartMarkInteraction hook that consolidates chart hover state management, mark state calculation, and motion handling across all chart components: - Extracts logic from useChartMarkTooltip and shared utility functions - Provides unified getMarkState, getMarkMotion, and showMark helpers - Updates bar, pie, heatmap, scatter, network, and sankey charts to use the new hook - Reduces code duplication across chart components while standardizing hover/fade behavior
1 parent daa7eb9 commit e0423a0

8 files changed

Lines changed: 244 additions & 189 deletions

File tree

agent-html/components/chart/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,9 @@ Use visx primitives by responsibility:
179179
- `components/ui/chart.tsx` owns mark identity helpers. Discrete charts use
180180
`getChartMarkKey`, `getChartMarkPresence`, and `getChartMarkOpacity` instead
181181
of hand-built hover keys or local opacity branches.
182+
- `useChartMarkInteraction` is the default discrete-mark interaction protocol:
183+
it owns mark key generation, mark state, mark motion props, hover writes, and
184+
tooltip binding.
182185
- `BarChart`, `BarHChart`, `PieChart`, `HeatmapChart`, `NetworkChart`, and
183186
`SankeyChart` consume the shared hover protocol for highlighted and faded
184187
marks.

agent-html/components/chart/bar-chart.tsx

Lines changed: 13 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,14 @@ import {
1717
ChartTooltipContent,
1818
ChartXAxisGrid,
1919
ChartYAxisGrid,
20-
chartMotion,
2120
createBandScale,
2221
createCartesianLayout,
2322
createLinearScale,
2423
getChartCssVariable,
25-
getChartMarkKey,
26-
getChartMarkOpacity,
27-
getChartMarkPresence,
2824
getFiniteValues,
2925
getValue,
3026
isFiniteNumber,
31-
useChartMarkTooltip,
27+
useChartMarkInteraction,
3228
} from "../ui/chart"
3329
import { RoughRect } from "@/lib/rough-svg"
3430

@@ -262,14 +258,14 @@ function BarChartCore<T>({
262258
const {
263259
currentTooltipData: tooltip,
264260
followTooltip,
261+
getMarkKey,
262+
getMarkMotion,
265263
hideTooltip,
266-
hover,
267-
setHover,
268-
showTooltip: showMarkTooltip,
264+
showMark,
269265
tooltipLeft,
270266
tooltipOpen,
271267
tooltipTop,
272-
} = useChartMarkTooltip<TooltipState<T>, "bar">()
268+
} = useChartMarkInteraction<TooltipState<T>, "bar">()
273269
const seriesKey = React.useMemo(() => Object.keys(config)[0] ?? "value", [config])
274270
const rows = React.useMemo(() => Array.from(data), [data])
275271

@@ -362,27 +358,23 @@ function BarChartCore<T>({
362358
}
363359

364360
const rect = model.getBarRect(datum, value)
365-
const key = getChartMarkKey("bar", category)
366-
const presence = getChartMarkPresence({
367-
hover,
368-
key,
369-
})
370-
const opacity = getChartMarkOpacity({ presence })
361+
const key = getMarkKey("bar", category)
362+
const markMotion = getMarkMotion({ key })
371363
const showTooltip = (
372364
event: React.PointerEvent<SVGRectElement>
373365
) => {
374-
setHover({ key, type: "bar" })
375-
showMarkTooltip(event, {
376-
datum,
366+
showMark({
367+
data: { datum },
368+
event,
369+
key,
370+
type: "bar",
377371
})
378372
}
379373

380374
return (
381375
<g key={category}>
382376
<ChartMotionGroup
383-
animate={{ opacity }}
384-
initial={false}
385-
transition={chartMotion.hover}
377+
{...markMotion}
386378
>
387379
{renderer === "rough" ? (
388380
<RoughRect

agent-html/components/chart/heatmap-chart.tsx

Lines changed: 17 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,10 @@ import {
1414
ChartTooltip,
1515
ChartTooltipContent,
1616
ChartTooltipPanel,
17-
chartMotion,
1817
getChartCssVariable,
19-
getChartMarkKey,
20-
getChartMarkOpacity,
21-
getChartMarkPresence,
2218
getValue,
2319
isFiniteNumber,
24-
useChartMarkTooltip,
20+
useChartMarkInteraction,
2521
} from "../ui/chart"
2622
import { RoughCircle } from "@/lib/rough-svg"
2723

@@ -133,14 +129,14 @@ export function HeatmapChart<T>({
133129
const {
134130
currentTooltipData: tooltip,
135131
followTooltip,
132+
getMarkKey,
133+
getMarkMotion,
136134
hideTooltip,
137-
hover,
138-
setHover,
139-
showTooltip,
135+
showMark,
140136
tooltipLeft,
141137
tooltipOpen,
142138
tooltipTop,
143-
} = useChartMarkTooltip<TooltipState<T>, "cell">()
139+
} = useChartMarkInteraction<TooltipState<T>, "cell">()
144140
const columns = React.useMemo(
145141
() =>
146142
createHeatmapColumns({
@@ -241,26 +237,20 @@ export function HeatmapChart<T>({
241237
<>
242238
{heatmap.flatMap((column) =>
243239
column.map((cell) => {
244-
const key = getChartMarkKey(
240+
const key = getMarkKey(
245241
"cell",
246242
cell.datum.key,
247243
cell.bin.key
248244
)
249-
const presence = getChartMarkPresence({
250-
hover,
251-
key,
252-
})
253-
const opacity = getChartMarkOpacity({
245+
const markMotion = getMarkMotion({
254246
baseOpacity: cell.opacity,
255-
presence,
247+
key,
256248
})
257249

258250
return (
259251
<g key={key}>
260252
<ChartMotionGroup
261-
animate={{ opacity }}
262-
initial={false}
263-
transition={chartMotion.hover}
253+
{...markMotion}
264254
>
265255
{renderer === "rough" ? (
266256
<RoughCircle
@@ -290,10 +280,14 @@ export function HeatmapChart<T>({
290280
cx={cell.cx}
291281
cy={cell.cy}
292282
onPointerEnter={(event) => {
293-
setHover({ key, type: "cell" })
294-
showTooltip(event, {
295-
bin: cell.bin,
296-
column: cell.datum,
283+
showMark({
284+
data: {
285+
bin: cell.bin,
286+
column: cell.datum,
287+
},
288+
event,
289+
key,
290+
type: "cell",
297291
})
298292
}}
299293
onPointerLeave={hideTooltip}

0 commit comments

Comments
 (0)