From 17e2a27627ae07cb6ccdcbceadfd42c76be38f1d Mon Sep 17 00:00:00 2001 From: Levi Kipke Date: Fri, 24 Apr 2026 13:46:31 +0100 Subject: [PATCH 1/8] feat(chart): dim non-hovered series rows in tooltip When hovering a series on a bar or line chart, the tooltip now dims rows for other series to 50% opacity, matching the emphasis dimming that ECharts already applies to the chart elements themselves. --- .../src/components/chart/TimeseriesChart.tsx | 32 +++++++++++++++---- 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/packages/kumo/src/components/chart/TimeseriesChart.tsx b/packages/kumo/src/components/chart/TimeseriesChart.tsx index edf31be631..7f3245a876 100644 --- a/packages/kumo/src/components/chart/TimeseriesChart.tsx +++ b/packages/kumo/src/components/chart/TimeseriesChart.tsx @@ -140,6 +140,9 @@ export function TimeseriesChart({ ariaDescription, }: TimeseriesChartProps) { const chartRef = useRef(null); + // Tracks which series the user is hovering over so the tooltip formatter + // can dim the rows of non-hovered series (mirrors the bar emphasis dimming). + const hoveredSeriesRef = useRef(null); const incompleteBefore = incomplete?.before; const incompleteAfter = incomplete?.after; @@ -262,6 +265,8 @@ export function TimeseriesChart({ ? `
${echarts.format.encodeHTML(formatTimestamp(ts))}
` : ""; + const hovered = hoveredSeriesRef.current; + const rows = filteredParams .map((param: any) => { const value = param?.value?.[1]; @@ -270,9 +275,13 @@ export function TimeseriesChart({ ? echarts.format.encodeHTML(String(formatFn(value))) : echarts.format.encodeHTML(String(value)); - return `${param.marker} ${echarts.format.encodeHTML(param.seriesName)}: ${formattedValue}`; + const isDimmed = + hovered != null && param.seriesName !== hovered; + const style = isDimmed ? ' style="opacity:0.5"' : ""; + + return `${param.marker} ${echarts.format.encodeHTML(param.seriesName)}: ${formattedValue}`; }) - .join("
"); + .join(""); return `${header}${rows}`; }, @@ -340,15 +349,26 @@ export function TimeseriesChart({ ]); const events = useMemo>(() => { - if (!onTimeRangeChange) return {}; + const handlers: Partial = {}; - return { - brushend: (params) => { + if (onTimeRangeChange) { + handlers.brushend = (params) => { const range = params.areas[0].coordRange; onTimeRangeChange(range[0], range[1]); chartRef.current?.dispatchAction({ type: "brush", areas: [] }); - }, + }; + } + + // Track which series is hovered so the tooltip formatter can dim + // non-hovered rows, matching the emphasis dimming on the chart. + handlers.mouseover = (params) => { + hoveredSeriesRef.current = params.seriesName ?? null; }; + handlers.mouseout = () => { + hoveredSeriesRef.current = null; + }; + + return handlers; }, [onTimeRangeChange]); // Activate the lineX brush cursor when a time-range callback is provided, From c8d05364cf2a27a239f2e64497009ccbf9b64288 Mon Sep 17 00:00:00 2001 From: Levi Kipke Date: Fri, 24 Apr 2026 14:01:05 +0100 Subject: [PATCH 2/8] changeset --- .changeset/two-numbers-throw.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/two-numbers-throw.md diff --git a/.changeset/two-numbers-throw.md b/.changeset/two-numbers-throw.md new file mode 100644 index 0000000000..ee1112ea6b --- /dev/null +++ b/.changeset/two-numbers-throw.md @@ -0,0 +1,5 @@ +--- +"@cloudflare/kumo": minor +--- + +feat(chart): dim non-hovered series rows in tooltip From 615ea09c25c15626c2d19d7c55e2971c7aa3ab0d Mon Sep 17 00:00:00 2001 From: Levi Kipke Date: Tue, 28 Apr 2026 18:02:35 +0100 Subject: [PATCH 3/8] fix(chart): use updateAxisPointer for reliable tooltip dimming Address review feedback: - Replace mouseover/mouseout with updateAxisPointer + globalout for reliable hover tracking with axis-triggered tooltips - Move tooltip value formatter to a ref to avoid stale closure in the useMemo'd dangerousHtmlFormatter - Dispatch highlight/downplay actions to keep chart emphasis in sync - Add updateAxisPointer event type to ChartEvents interface --- packages/kumo/src/components/chart/EChart.tsx | 20 +++++++++ .../src/components/chart/TimeseriesChart.tsx | 43 +++++++++++++++---- 2 files changed, 54 insertions(+), 9 deletions(-) diff --git a/packages/kumo/src/components/chart/EChart.tsx b/packages/kumo/src/components/chart/EChart.tsx index 29b69ddc87..4f05f6462b 100644 --- a/packages/kumo/src/components/chart/EChart.tsx +++ b/packages/kumo/src/components/chart/EChart.tsx @@ -107,6 +107,26 @@ export interface ChartEvents { axisareaselected: (params: any) => void; + /** + * Fired whenever the axis pointer moves to a new position. + * Includes the series index closest to the pointer, which is useful + * for determining which series the user is interacting with when + * the tooltip uses `trigger: "axis"`. + */ + updateAxisPointer: (params: { + seriesDataByAxis: Array<{ + axisDim: string; + axisIndex: number; + value: number; + seriesDataIndices: Array<{ + seriesIndex: number; + dataIndex: number; + }>; + }>; + dataIndex?: number; + seriesIndex?: number; + }) => void; + // Brush / selection events brush: (params: any) => void; brushselected: (params: any) => void; diff --git a/packages/kumo/src/components/chart/TimeseriesChart.tsx b/packages/kumo/src/components/chart/TimeseriesChart.tsx index 7f3245a876..b0a29c0959 100644 --- a/packages/kumo/src/components/chart/TimeseriesChart.tsx +++ b/packages/kumo/src/components/chart/TimeseriesChart.tsx @@ -141,8 +141,15 @@ export function TimeseriesChart({ }: TimeseriesChartProps) { const chartRef = useRef(null); // Tracks which series the user is hovering over so the tooltip formatter - // can dim the rows of non-hovered series (mirrors the bar emphasis dimming). + // can dim the rows of non-hovered series (mirrors the emphasis dimming). + // Updated via the updateAxisPointer event, which fires reliably for + // axis-triggered tooltips regardless of chart type. const hoveredSeriesRef = useRef(null); + // Stable ref to the tooltip value formatter so the dangerousHtmlFormatter + // closure (captured by useMemo) always calls the latest function without + // needing to be a useMemo dependency. + const tooltipValueFormatRef = useRef(tooltipValueFormat ?? yAxisTickLabelFormat); + tooltipValueFormatRef.current = tooltipValueFormat ?? yAxisTickLabelFormat; const incompleteBefore = incomplete?.before; const incompleteAfter = incomplete?.after; @@ -266,11 +273,11 @@ export function TimeseriesChart({ : ""; const hovered = hoveredSeriesRef.current; + const formatFn = tooltipValueFormatRef.current; const rows = filteredParams .map((param: any) => { const value = param?.value?.[1]; - const formatFn = tooltipValueFormat ?? yAxisTickLabelFormat; const formattedValue = formatFn ? echarts.format.encodeHTML(String(formatFn(value))) : echarts.format.encodeHTML(String(value)); @@ -336,10 +343,8 @@ export function TimeseriesChart({ xAxisTickCount, xAxisTickFormat, yAxisTickFormat, - yAxisTickLabelFormat, yAxisName, yAxisTickCount, - tooltipValueFormat, incompleteBefore, incompleteAfter, type, @@ -359,13 +364,33 @@ export function TimeseriesChart({ }; } - // Track which series is hovered so the tooltip formatter can dim - // non-hovered rows, matching the emphasis dimming on the chart. - handlers.mouseover = (params) => { - hoveredSeriesRef.current = params.seriesName ?? null; + // Use updateAxisPointer to track which series is closest to the + // pointer. This fires reliably for axis-triggered tooltips (unlike + // mouseover which requires hovering directly on a graphical element). + // We dispatch highlight/downplay actions so the chart emphasis and + // the tooltip dimming stay in sync. + handlers.updateAxisPointer = (params) => { + const chart = chartRef.current; + if (!chart) return; + + const seriesIndex = params.seriesIndex; + if (seriesIndex != null) { + const option = chart.getOption() as EChartsOption; + const series = (option.series as SeriesOption[])?.[seriesIndex]; + const name = + series && "name" in series ? (series.name as string) : null; + + if (name && name !== hoveredSeriesRef.current) { + hoveredSeriesRef.current = name; + chart.dispatchAction({ type: "highlight", seriesName: name }); + } + } }; - handlers.mouseout = () => { + + // Clear hover state when the pointer leaves the chart entirely. + handlers.globalout = () => { hoveredSeriesRef.current = null; + chartRef.current?.dispatchAction({ type: "downplay" }); }; return handlers; From 0ddc823cca60d7133d707af781ce5a9e31cdb5c5 Mon Sep 17 00:00:00 2001 From: Levi Kipke Date: Tue, 28 Apr 2026 18:04:09 +0100 Subject: [PATCH 4/8] fix(chart): use mouseover for bar charts, updateAxisPointer for lines Bar charts have distinct hoverable segments so mouseover/mouseout work reliably. Line charts with showSymbol:false lack hover targets, so updateAxisPointer (which provides the nearest seriesIndex) is used instead. Both share globalout to clear state when leaving the chart. --- .../src/components/chart/TimeseriesChart.tsx | 60 ++++++++++++------- 1 file changed, 38 insertions(+), 22 deletions(-) diff --git a/packages/kumo/src/components/chart/TimeseriesChart.tsx b/packages/kumo/src/components/chart/TimeseriesChart.tsx index b0a29c0959..d8fc80b0ef 100644 --- a/packages/kumo/src/components/chart/TimeseriesChart.tsx +++ b/packages/kumo/src/components/chart/TimeseriesChart.tsx @@ -364,28 +364,44 @@ export function TimeseriesChart({ }; } - // Use updateAxisPointer to track which series is closest to the - // pointer. This fires reliably for axis-triggered tooltips (unlike - // mouseover which requires hovering directly on a graphical element). - // We dispatch highlight/downplay actions so the chart emphasis and - // the tooltip dimming stay in sync. - handlers.updateAxisPointer = (params) => { - const chart = chartRef.current; - if (!chart) return; - - const seriesIndex = params.seriesIndex; - if (seriesIndex != null) { - const option = chart.getOption() as EChartsOption; - const series = (option.series as SeriesOption[])?.[seriesIndex]; - const name = - series && "name" in series ? (series.name as string) : null; - - if (name && name !== hoveredSeriesRef.current) { - hoveredSeriesRef.current = name; - chart.dispatchAction({ type: "highlight", seriesName: name }); + // --- Hover tracking for tooltip row dimming --- + // Bar and line charts need different strategies because their hover + // targets differ: + // + // Bar charts: each stacked segment is a distinct graphical element, + // so mouseover/mouseout fire reliably on individual segments. + // updateAxisPointer does NOT provide a useful seriesIndex for bars + // because the axis pointer covers the entire stacked column. + // + // Line charts: with showSymbol: false there are no hoverable elements, + // so mouseover rarely fires. updateAxisPointer provides seriesIndex + // indicating the nearest line, which is exactly what we need. + if (type === "bar") { + handlers.mouseover = (params) => { + hoveredSeriesRef.current = params.seriesName ?? null; + }; + handlers.mouseout = () => { + hoveredSeriesRef.current = null; + }; + } else { + handlers.updateAxisPointer = (params) => { + const chart = chartRef.current; + if (!chart) return; + + const seriesIndex = params.seriesIndex; + if (seriesIndex != null) { + const option = chart.getOption() as EChartsOption; + const series = (option.series as SeriesOption[])?.[seriesIndex]; + const name = + series && "name" in series ? (series.name as string) : null; + + if (name && name !== hoveredSeriesRef.current) { + hoveredSeriesRef.current = name; + chart.dispatchAction({ type: "highlight", seriesName: name }); + } } - } - }; + }; + } // Clear hover state when the pointer leaves the chart entirely. handlers.globalout = () => { @@ -394,7 +410,7 @@ export function TimeseriesChart({ }; return handlers; - }, [onTimeRangeChange]); + }, [onTimeRangeChange, type]); // Activate the lineX brush cursor when a time-range callback is provided, // and deactivate it on cleanup so the cursor resets when the prop is removed. From ef6b6455099aae13bca2ebba2bf8362a416f3620 Mon Sep 17 00:00:00 2001 From: Levi Kipke Date: Tue, 28 Apr 2026 18:13:36 +0100 Subject: [PATCH 5/8] fix(chart): scope tooltip dimming to bar charts only Remove line chart hover tracking (ZRender mousemove, updateAxisPointer) since line series with showSymbol: false lack reliable hover targets, causing the tooltip dimming to be out of sync with the chart emphasis. Bar chart dimming via mouseover/mouseout works reliably since each stacked segment is a distinct hoverable element. --- .../src/components/chart/TimeseriesChart.tsx | 51 +++++-------------- 1 file changed, 12 insertions(+), 39 deletions(-) diff --git a/packages/kumo/src/components/chart/TimeseriesChart.tsx b/packages/kumo/src/components/chart/TimeseriesChart.tsx index d8fc80b0ef..73233da4e9 100644 --- a/packages/kumo/src/components/chart/TimeseriesChart.tsx +++ b/packages/kumo/src/components/chart/TimeseriesChart.tsx @@ -140,10 +140,10 @@ export function TimeseriesChart({ ariaDescription, }: TimeseriesChartProps) { const chartRef = useRef(null); - // Tracks which series the user is hovering over so the tooltip formatter - // can dim the rows of non-hovered series (mirrors the emphasis dimming). - // Updated via the updateAxisPointer event, which fires reliably for - // axis-triggered tooltips regardless of chart type. + // Tracks which bar series the user is hovering over so the tooltip + // formatter can dim the rows of non-hovered series, matching the + // emphasis dimming on the bars. Only used for bar charts — line charts + // with showSymbol: false lack hover targets for mouseover events. const hoveredSeriesRef = useRef(null); // Stable ref to the tooltip value formatter so the dangerousHtmlFormatter // closure (captured by useMemo) always calls the latest function without @@ -364,18 +364,12 @@ export function TimeseriesChart({ }; } - // --- Hover tracking for tooltip row dimming --- - // Bar and line charts need different strategies because their hover - // targets differ: - // - // Bar charts: each stacked segment is a distinct graphical element, - // so mouseover/mouseout fire reliably on individual segments. - // updateAxisPointer does NOT provide a useful seriesIndex for bars - // because the axis pointer covers the entire stacked column. - // - // Line charts: with showSymbol: false there are no hoverable elements, - // so mouseover rarely fires. updateAxisPointer provides seriesIndex - // indicating the nearest line, which is exactly what we need. + // Track which series is hovered so the tooltip formatter can dim + // non-hovered rows, matching the emphasis dimming on the bars. + // Only enabled for bar charts — each stacked segment is a distinct + // graphical element so mouseover/mouseout fire reliably. Line charts + // with showSymbol: false lack hover targets, so tooltip dimming is + // not supported for them. if (type === "bar") { handlers.mouseover = (params) => { hoveredSeriesRef.current = params.seriesName ?? null; @@ -383,32 +377,11 @@ export function TimeseriesChart({ handlers.mouseout = () => { hoveredSeriesRef.current = null; }; - } else { - handlers.updateAxisPointer = (params) => { - const chart = chartRef.current; - if (!chart) return; - - const seriesIndex = params.seriesIndex; - if (seriesIndex != null) { - const option = chart.getOption() as EChartsOption; - const series = (option.series as SeriesOption[])?.[seriesIndex]; - const name = - series && "name" in series ? (series.name as string) : null; - - if (name && name !== hoveredSeriesRef.current) { - hoveredSeriesRef.current = name; - chart.dispatchAction({ type: "highlight", seriesName: name }); - } - } + handlers.globalout = () => { + hoveredSeriesRef.current = null; }; } - // Clear hover state when the pointer leaves the chart entirely. - handlers.globalout = () => { - hoveredSeriesRef.current = null; - chartRef.current?.dispatchAction({ type: "downplay" }); - }; - return handlers; }, [onTimeRangeChange, type]); From 76451cc9ac68cb4925a4d34d79487df1e4b01492 Mon Sep 17 00:00:00 2001 From: Levi Kipke Date: Tue, 28 Apr 2026 18:31:35 +0100 Subject: [PATCH 6/8] feat(chart): add tooltipCss and tooltipEnterable props Expose ECharts' extraCssText and enterable tooltip options as TimeseriesChart props. When tooltipEnterable is true, the tooltip is positioned close to the cursor so users can mouse into it to scroll or interact with the content. Includes a docs demo with 8 series and a scrollable tooltip. --- .../src/components/demos/Chart/ChartDemo.tsx | 40 +++++++++++++++++++ .../src/pages/charts/timeseries.mdx | 17 ++++++++ .../src/components/chart/TimeseriesChart.tsx | 31 ++++++++++++++ 3 files changed, 88 insertions(+) diff --git a/packages/kumo-docs-astro/src/components/demos/Chart/ChartDemo.tsx b/packages/kumo-docs-astro/src/components/demos/Chart/ChartDemo.tsx index 156ba3336f..bbe1b79edf 100644 --- a/packages/kumo-docs-astro/src/components/demos/Chart/ChartDemo.tsx +++ b/packages/kumo-docs-astro/src/components/demos/Chart/ChartDemo.tsx @@ -450,6 +450,46 @@ export function BarChartDemo() { ); } +/** Bar chart with many series and a scrollable tooltip via `tooltipCss`. */ +export function TooltipCssDemo() { + const isDarkMode = useIsDarkMode(); + + const colors = [ + ChartPalette.categorical(0, isDarkMode), + ChartPalette.categorical(1, isDarkMode), + ChartPalette.categorical(2, isDarkMode), + ChartPalette.categorical(3, isDarkMode), + ChartPalette.categorical(4, isDarkMode), + ChartPalette.categorical(5, isDarkMode), + ChartPalette.categorical(6, isDarkMode), + ChartPalette.categorical(7, isDarkMode), + ]; + + const data = useMemo( + () => + Array.from({ length: 8 }, (_, i) => ({ + name: `Series ${String.fromCharCode(65 + i)}`, + data: buildSeriesData(i, 20, 3_600_000, 0.3 + i * 0.1), + color: colors[i], + })), + [isDarkMode], + ); + + return ( + r.toFixed(2)} + tooltipCss="max-height:180px;overflow-y:auto;" + tooltipEnterable + /> + ); +} + /** * Timeseries chart in loading state, showing the animated sine-wave skeleton. * Loads for 5 seconds then reveals the real chart. A button restarts the cycle. diff --git a/packages/kumo-docs-astro/src/pages/charts/timeseries.mdx b/packages/kumo-docs-astro/src/pages/charts/timeseries.mdx index 515538ab0d..25f5245010 100644 --- a/packages/kumo-docs-astro/src/pages/charts/timeseries.mdx +++ b/packages/kumo-docs-astro/src/pages/charts/timeseries.mdx @@ -15,6 +15,7 @@ import { IncompleteDataChartDemo, LoadingChartDemo, TimeRangeSelectionChartDemo, + TooltipCssDemo, } from "~/components/demos/Chart/ChartDemo";

@@ -108,6 +109,22 @@ import { +### Tooltip CSS + +

+ Use the tooltipCss prop to append custom CSS to the tooltip + container. This is useful for constraining tooltip height when there are many + series, or adding scroll behavior. The string is passed directly to ECharts' + extraCssText option. +

+ + + + + + + + ### Loading State

diff --git a/packages/kumo/src/components/chart/TimeseriesChart.tsx b/packages/kumo/src/components/chart/TimeseriesChart.tsx index 73233da4e9..fdec6a06c4 100644 --- a/packages/kumo/src/components/chart/TimeseriesChart.tsx +++ b/packages/kumo/src/components/chart/TimeseriesChart.tsx @@ -87,6 +87,26 @@ export interface TimeseriesChartProps { * @see https://echarts.apache.org/handbook/en/best-practices/aria/ */ ariaDescription?: string; + /** + * Extra CSS text appended to the tooltip's inline `style` attribute. + * Useful for constraining tooltip size or adding scroll behavior when + * there are many series. + * + * @example + * ```tsx + * tooltipCss="max-height:200px;overflow-y:auto;" + * ``` + */ + tooltipCss?: string; + /** + * When `true`, the tooltip stays visible when the user moves their cursor + * into it, allowing interaction with scrollable or selectable content. + * Pair with `tooltipCss` to create scrollable tooltips for charts with + * many series. + * + * @default false + */ + tooltipEnterable?: boolean; } /** @@ -138,6 +158,8 @@ export function TimeseriesChart({ gradient, loading, ariaDescription, + tooltipCss, + tooltipEnterable, }: TimeseriesChartProps) { const chartRef = useRef(null); // Tracks which bar series the user is hovering over so the tooltip @@ -247,6 +269,13 @@ export function TimeseriesChart({ trigger: "axis" as const, appendTo: "body", axisPointer: { type: "shadow" as const }, + ...(tooltipCss && { extraCssText: tooltipCss }), + ...(tooltipEnterable && { + enterable: true, + // Position the tooltip close to the cursor so the user can + // easily move into it (e.g. to scroll overflow content). + position: (point: number[]) => [point[0] + 10, point[1] - 10], + }), dangerousHtmlFormatter: (params) => { const items = Array.isArray(params) ? params : [params]; @@ -351,6 +380,8 @@ export function TimeseriesChart({ gradient, echarts, ariaDescription, + tooltipCss, + tooltipEnterable, ]); const events = useMemo>(() => { From 335f826e0f77dfefc4ec1a4ef48fa0b43a46edc1 Mon Sep 17 00:00:00 2001 From: Levi Kipke Date: Fri, 1 May 2026 10:34:25 +0100 Subject: [PATCH 7/8] chore: remove unused updateAxisPointer from ChartEvents MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This event type was added during development but is no longer used — bar chart tooltip dimming uses mouseover/mouseout instead. --- packages/kumo/src/components/chart/EChart.tsx | 20 ------------------- 1 file changed, 20 deletions(-) diff --git a/packages/kumo/src/components/chart/EChart.tsx b/packages/kumo/src/components/chart/EChart.tsx index 4f05f6462b..29b69ddc87 100644 --- a/packages/kumo/src/components/chart/EChart.tsx +++ b/packages/kumo/src/components/chart/EChart.tsx @@ -107,26 +107,6 @@ export interface ChartEvents { axisareaselected: (params: any) => void; - /** - * Fired whenever the axis pointer moves to a new position. - * Includes the series index closest to the pointer, which is useful - * for determining which series the user is interacting with when - * the tooltip uses `trigger: "axis"`. - */ - updateAxisPointer: (params: { - seriesDataByAxis: Array<{ - axisDim: string; - axisIndex: number; - value: number; - seriesDataIndices: Array<{ - seriesIndex: number; - dataIndex: number; - }>; - }>; - dataIndex?: number; - seriesIndex?: number; - }) => void; - // Brush / selection events brush: (params: any) => void; brushselected: (params: any) => void; From 0b739234c7f04c5c24f728c8d47fe229727a5bf5 Mon Sep 17 00:00:00 2001 From: Levi Kipke Date: Fri, 1 May 2026 11:05:27 +0100 Subject: [PATCH 8/8] feat(chart): enable tooltip dimming for both bar and line charts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tooltip row dimming now works for all chart types via mouseover/mouseout. When ECharts triggers emphasis on a series element, the tooltip dims non-hovered rows to match. The hit area for line charts is unchanged (tied to the 2px line stroke) — improving that is a separate concern. --- .../src/components/chart/TimeseriesChart.tsx | 33 ++++++++----------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/packages/kumo/src/components/chart/TimeseriesChart.tsx b/packages/kumo/src/components/chart/TimeseriesChart.tsx index fdec6a06c4..54f4b70474 100644 --- a/packages/kumo/src/components/chart/TimeseriesChart.tsx +++ b/packages/kumo/src/components/chart/TimeseriesChart.tsx @@ -162,10 +162,9 @@ export function TimeseriesChart({ tooltipEnterable, }: TimeseriesChartProps) { const chartRef = useRef(null); - // Tracks which bar series the user is hovering over so the tooltip + // Tracks which series the user is hovering over so the tooltip // formatter can dim the rows of non-hovered series, matching the - // emphasis dimming on the bars. Only used for bar charts — line charts - // with showSymbol: false lack hover targets for mouseover events. + // emphasis dimming on the chart. const hoveredSeriesRef = useRef(null); // Stable ref to the tooltip value formatter so the dangerousHtmlFormatter // closure (captured by useMemo) always calls the latest function without @@ -396,25 +395,19 @@ export function TimeseriesChart({ } // Track which series is hovered so the tooltip formatter can dim - // non-hovered rows, matching the emphasis dimming on the bars. - // Only enabled for bar charts — each stacked segment is a distinct - // graphical element so mouseover/mouseout fire reliably. Line charts - // with showSymbol: false lack hover targets, so tooltip dimming is - // not supported for them. - if (type === "bar") { - handlers.mouseover = (params) => { - hoveredSeriesRef.current = params.seriesName ?? null; - }; - handlers.mouseout = () => { - hoveredSeriesRef.current = null; - }; - handlers.globalout = () => { - hoveredSeriesRef.current = null; - }; - } + // non-hovered rows, matching the emphasis dimming on the chart. + handlers.mouseover = (params) => { + hoveredSeriesRef.current = params.seriesName ?? null; + }; + handlers.mouseout = () => { + hoveredSeriesRef.current = null; + }; + handlers.globalout = () => { + hoveredSeriesRef.current = null; + }; return handlers; - }, [onTimeRangeChange, type]); + }, [onTimeRangeChange]); // Activate the lineX brush cursor when a time-range callback is provided, // and deactivate it on cleanup so the cursor resets when the prop is removed.