@mui/x-charts
Version:
The community edition of MUI X Charts components.
67 lines (64 loc) • 2.65 kB
JavaScript
'use client';
import * as React from 'react';
import { useSvgRef } from "../hooks/useSvgRef.js";
import { useChartContext } from "../context/ChartProvider/index.js";
import { getSVGPoint } from "../internals/getSVGPoint.js";
import { useStore } from "../internals/store/useStore.js";
import { selectorBarItemAtPosition } from "../internals/plugins/featurePlugins/useChartCartesianAxis/useChartCartesianAxisPosition.selectors.js";
/**
* Hook that registers pointer event handlers for chart item clicking.
* @param onItemClick Callback for item click events.
*/
export function useRegisterItemClickHandlers(onItemClick) {
const {
instance
} = useChartContext();
const svgRef = useSvgRef();
const store = useStore();
React.useEffect(() => {
const element = svgRef.current;
if (!element || !onItemClick) {
return undefined;
}
let lastPointerUp = null;
const onClick = function onClick(event) {
let point = event;
/* The click event doesn't contain decimal values in clientX/Y, but the pointermove does.
* This caused a problem when rendering many bars that were thinner than a pixel where the tooltip or the highlight
* would refer to a different bar than the click since those rely on the pointermove event.
* As a fix, we use the pointerup event to get the decimal values and check if the pointer up event was close enough
* to the click event (1px difference in each direction); if so, then we can use the pointerup's clientX/Y; if not,
* we default to the click event's clientX/Y. */
if (lastPointerUp) {
if (Math.abs(event.clientX - lastPointerUp.clientX) <= 1 && Math.abs(event.clientY - lastPointerUp.clientY) <= 1) {
point = {
clientX: lastPointerUp.clientX,
clientY: lastPointerUp.clientY
};
}
}
lastPointerUp = null;
const svgPoint = getSVGPoint(element, point);
if (!instance.isPointInside(svgPoint.x, svgPoint.y)) {
return;
}
const item = selectorBarItemAtPosition(store.state, svgPoint);
if (item) {
onItemClick(event, {
type: 'bar',
seriesId: item.seriesId,
dataIndex: item.dataIndex
});
}
};
const onPointerUp = function onPointerUp(event) {
lastPointerUp = event;
};
element.addEventListener('click', onClick);
element.addEventListener('pointerup', onPointerUp);
return () => {
element.removeEventListener('click', onClick);
element.removeEventListener('pointerup', onPointerUp);
};
}, [instance, onItemClick, store, svgRef]);
}