Skip to content

Commit

Permalink
fix(D3 plugin): support chart.event.click for all visualizations (#510)
Browse files Browse the repository at this point in the history
  • Loading branch information
kuzmadom authored Aug 23, 2024
1 parent 6ffbc9e commit b521380
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 49 deletions.
42 changes: 39 additions & 3 deletions src/plugins/d3/renderer/components/Chart.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, {MouseEventHandler} from 'react';
import React from 'react';

import {pointer} from 'd3';
import throttle from 'lodash/throttle';
Expand Down Expand Up @@ -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<SVGSVGElement> = (event) => {
const isOutsideBounds = React.useCallback(
(x: number, y: number) => {
return x < 0 || x > boundsWidth || y < 0 || y > boundsHeight;
},
[boundsHeight, boundsWidth],
);

const handleMouseMove: React.MouseEventHandler<SVGSVGElement> = (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;
}
Expand All @@ -138,6 +145,34 @@ export const Chart = (props: Props) => {
dispatcher.call('hover-shape', {}, undefined);
};

const handleChartClick = React.useCallback(
(event: React.MouseEvent<SVGSVGElement>) => {
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 (
<React.Fragment>
<svg
Expand All @@ -147,6 +182,7 @@ export const Chart = (props: Props) => {
height={height}
onMouseMove={throttledHandleMouseMove}
onMouseLeave={handleMouseLeave}
onClick={handleChartClick}
>
{title && <Title {...title} chartWidth={width} />}
<g transform={`translate(0, ${boundsOffsetTop})`}>
Expand Down
21 changes: 0 additions & 21 deletions src/plugins/d3/renderer/hooks/useShapes/pie/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -144,30 +144,9 @@ export function PieSeriesShapes(args: PreparePieSeriesArgs) {
}
});

const getSelectedSegment = (element: Element) => {
const datum = select<BaseType, PieArcDatum<SegmentData> | PieLabelData>(
element,
).datum();
const seriesId = get(datum, 'data.series.id', get(datum, 'series.id'));
return preparedData.reduce<SegmentData | undefined>((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;
Expand Down
16 changes: 0 additions & 16 deletions src/plugins/d3/renderer/hooks/useShapes/scatter/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<BaseType, PreparedScatterData>(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;

Expand Down
9 changes: 0 additions & 9 deletions src/plugins/d3/renderer/hooks/useShapes/treemap/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<BaseType, HierarchyRectangularNode<TreemapSeriesData>>(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;
Expand Down

0 comments on commit b521380

Please sign in to comment.