@mui/x-charts
Version:
The community edition of the charts components (MUI X).
126 lines (125 loc) • 3.99 kB
JavaScript
import * as React from 'react';
import { InteractionContext } from '../context/InteractionProvider';
import { CartesianContext } from '../context/CartesianContextProvider';
import { SVGContext, DrawingContext } from '../context/DrawingProvider';
import { isBandScale } from '../internals/isBandScale';
import { getSVGPoint } from '../internals/utils';
function getAsANumber(value) {
return value instanceof Date ? value.getTime() : value;
}
export const useAxisEvents = disableAxisListener => {
const svgRef = React.useContext(SVGContext);
const {
width,
height,
top,
left
} = React.useContext(DrawingContext);
const {
xAxis,
yAxis,
xAxisIds,
yAxisIds
} = React.useContext(CartesianContext);
const {
dispatch
} = React.useContext(InteractionContext);
const usedXAxis = xAxisIds[0];
const usedYAxis = yAxisIds[0];
// Use a ref to avoid rerendering on every mousemove event.
const mousePosition = React.useRef({
x: -1,
y: -1
});
React.useEffect(() => {
const element = svgRef.current;
if (element === null || disableAxisListener) {
return () => {};
}
const getUpdate = (axisConfig, mouseValue) => {
if (usedXAxis === null) {
return null;
}
const {
scale,
data: axisData
} = axisConfig;
if (!isBandScale(scale)) {
const value = scale.invert(mouseValue);
if (axisData === undefined) {
return {
value
};
}
const valueAsNumber = getAsANumber(value);
const closestIndex = axisData?.findIndex((pointValue, index) => {
const v = getAsANumber(pointValue);
if (v > valueAsNumber) {
if (index === 0 || Math.abs(valueAsNumber - v) <= Math.abs(valueAsNumber - getAsANumber(axisData[index - 1]))) {
return true;
}
}
if (v <= valueAsNumber) {
if (index === axisData.length - 1 ||
// @ts-ignore
Math.abs(value - v) < Math.abs(value - getAsANumber(axisData[index + 1]))) {
return true;
}
}
return false;
});
return {
value: closestIndex !== undefined && closestIndex >= 0 ? axisData[closestIndex] : value,
index: closestIndex
};
}
const dataIndex = scale.bandwidth() === 0 ? Math.floor((mouseValue - Math.min(...scale.range()) + scale.step() / 2) / scale.step()) : Math.floor((mouseValue - Math.min(...scale.range())) / scale.step());
if (dataIndex < 0 || dataIndex >= axisData.length) {
return null;
}
return {
index: dataIndex,
value: axisData[dataIndex]
};
};
const handleMouseOut = () => {
mousePosition.current = {
x: -1,
y: -1
};
dispatch({
type: 'exitChart'
});
};
const handleMouseMove = event => {
const svgPoint = getSVGPoint(svgRef.current, event);
mousePosition.current = {
x: svgPoint.x,
y: svgPoint.y
};
const outsideX = svgPoint.x < left || svgPoint.x > left + width;
const outsideY = svgPoint.y < top || svgPoint.y > top + height;
if (outsideX || outsideY) {
dispatch({
type: 'exitChart'
});
return;
}
const newStateX = getUpdate(xAxis[usedXAxis], svgPoint.x);
const newStateY = getUpdate(yAxis[usedYAxis], svgPoint.y);
dispatch({
type: 'updateAxis',
data: {
x: newStateX,
y: newStateY
}
});
};
element.addEventListener('mouseout', handleMouseOut);
element.addEventListener('mousemove', handleMouseMove);
return () => {
element.removeEventListener('mouseout', handleMouseOut);
element.removeEventListener('mousemove', handleMouseMove);
};
}, [svgRef, dispatch, left, width, top, height, usedYAxis, yAxis, usedXAxis, xAxis, disableAxisListener]);
};