From 2dcbc0cbddbf9c09bcacb3e02888583925efa293 Mon Sep 17 00:00:00 2001 From: "Irina V. Kuzmina" Date: Fri, 23 Aug 2024 18:12:10 +0300 Subject: [PATCH] fix(D3 plugin): chart.event.click does not work for some visualizations --- src/plugins/d3/renderer/components/Chart.tsx | 42 +++++++++++++++++-- .../d3/renderer/hooks/useShapes/pie/index.tsx | 21 ---------- .../hooks/useShapes/scatter/index.tsx | 16 ------- .../hooks/useShapes/treemap/index.tsx | 9 ---- 4 files changed, 39 insertions(+), 49 deletions(-) diff --git a/src/plugins/d3/renderer/components/Chart.tsx b/src/plugins/d3/renderer/components/Chart.tsx index e35acfa2..cd84f8d0 100644 --- a/src/plugins/d3/renderer/components/Chart.tsx +++ b/src/plugins/d3/renderer/components/Chart.tsx @@ -1,4 +1,4 @@ -import React, {MouseEventHandler} from 'react'; +import React from 'react'; import {pointer} from 'd3'; import throttle from 'lodash/throttle'; @@ -116,11 +116,18 @@ export const Chart = (props: Props) => { // We only need to consider the width of the first left axis const boundsOffsetLeft = chart.margin.left + getYAxisWidth(yAxis[0]); - const handleMouseMove: MouseEventHandler = (event) => { + const isOutsideBounds = React.useCallback( + (x: number, y: number) => { + return x < 0 || x > boundsWidth || y < 0 || y > boundsHeight; + }, + [boundsHeight, boundsWidth], + ); + + const handleMouseMove: React.MouseEventHandler = (event) => { const [pointerX, pointerY] = pointer(event, svgRef.current); const x = pointerX - boundsOffsetLeft; const y = pointerY - boundsOffsetTop; - if (x < 0 || x > boundsWidth || y < 0 || y > boundsHeight) { + if (isOutsideBounds(x, y)) { dispatcher.call('hover-shape', {}, undefined); return; } @@ -138,6 +145,34 @@ export const Chart = (props: Props) => { dispatcher.call('hover-shape', {}, undefined); }; + const handleChartClick = React.useCallback( + (event: React.MouseEvent) => { + const [pointerX, pointerY] = pointer(event, svgRef.current); + const x = pointerX - boundsOffsetLeft; + const y = pointerY - boundsOffsetTop; + if (isOutsideBounds(x, y)) { + return; + } + + const items = getClosestPoints({ + position: [x, y], + shapesData, + }); + const selected = items?.find((item) => item.closest); + if (!selected) { + return; + } + + dispatcher.call( + 'click-chart', + undefined, + {point: selected.data, series: selected.series}, + event, + ); + }, + [boundsOffsetLeft, boundsOffsetTop, dispatcher, isOutsideBounds, shapesData], + ); + return ( { height={height} onMouseMove={throttledHandleMouseMove} onMouseLeave={handleMouseLeave} + onClick={handleChartClick} > {title && } diff --git a/src/plugins/d3/renderer/hooks/useShapes/pie/index.tsx b/src/plugins/d3/renderer/hooks/useShapes/pie/index.tsx index 7ec1d1dd..d8fcf811 100644 --- a/src/plugins/d3/renderer/hooks/useShapes/pie/index.tsx +++ b/src/plugins/d3/renderer/hooks/useShapes/pie/index.tsx @@ -144,30 +144,9 @@ export function PieSeriesShapes(args: PreparePieSeriesArgs) { } }); - const getSelectedSegment = (element: Element) => { - const datum = select | PieLabelData>( - element, - ).datum(); - const seriesId = get(datum, 'data.series.id', get(datum, 'series.id')); - return preparedData.reduce((result, pie) => { - return result || pie.segments.find((s) => s.data.series.id === seriesId)?.data; - }, undefined); - }; - const eventName = `hover-shape.pie`; const hoverOptions = get(seriesOptions, 'pie.states.hover'); const inactiveOptions = get(seriesOptions, 'pie.states.inactive'); - svgElement.on('click', (e) => { - const selectedSegment = getSelectedSegment(e.target); - if (selectedSegment) { - dispatcher.call( - 'click-chart', - undefined, - {point: selectedSegment.series.data, series: selectedSegment.series}, - e, - ); - } - }); dispatcher.on(eventName, (data?: TooltipDataChunkPie[]) => { const selectedSeriesId = data?.[0]?.series?.id; diff --git a/src/plugins/d3/renderer/hooks/useShapes/scatter/index.tsx b/src/plugins/d3/renderer/hooks/useShapes/scatter/index.tsx index b67478fa..e9fdecb6 100644 --- a/src/plugins/d3/renderer/hooks/useShapes/scatter/index.tsx +++ b/src/plugins/d3/renderer/hooks/useShapes/scatter/index.tsx @@ -51,22 +51,6 @@ export function ScatterSeriesShape(props: ScatterSeriesShapeProps) { .attr('opacity', (d) => d.point.opacity) .attr('cursor', (d) => d.point.series.cursor); - const getSelectedPoint = (element: Element) => { - return select(element).datum(); - }; - - svgElement.on('click', (e) => { - const datum = getSelectedPoint(e.target); - if (datum) { - dispatcher.call( - 'click-chart', - undefined, - {point: datum.point.data, series: datum.point.series}, - e, - ); - } - }); - const hoverEnabled = hoverOptions?.enabled; const inactiveEnabled = inactiveOptions?.enabled; diff --git a/src/plugins/d3/renderer/hooks/useShapes/treemap/index.tsx b/src/plugins/d3/renderer/hooks/useShapes/treemap/index.tsx index 049f4de9..a6626335 100644 --- a/src/plugins/d3/renderer/hooks/useShapes/treemap/index.tsx +++ b/src/plugins/d3/renderer/hooks/useShapes/treemap/index.tsx @@ -63,18 +63,9 @@ export const TreemapSeriesShape = (props: ShapeProps) => { .style('fill', () => series.dataLabels.style?.fontColor || null) .call(setEllipsisForOverflowTexts, (d) => d.width); - const getSelectedPart = (node: Element) => { - const hoveredRect = select>(node); - return hoveredRect.datum(); - }; - const eventName = `hover-shape.treemap`; const hoverOptions = get(seriesOptions, 'treemap.states.hover'); const inactiveOptions = get(seriesOptions, 'treemap.states.inactive'); - svgElement.on('click', (e) => { - const datum = getSelectedPart(e.target); - dispatcher.call('click-chart', undefined, {point: datum.data, series}, e); - }); dispatcher.on(eventName, (data?: TooltipDataChunkTreemap[]) => { const hoverEnabled = hoverOptions?.enabled;