@mui/x-charts
Version:
The community edition of MUI X Charts components.
168 lines (165 loc) • 6.73 kB
JavaScript
'use client';
import _extends from "@babel/runtime/helpers/esm/extends";
import * as React from 'react';
import { warnOnce } from '@mui/x-internals/warning';
import { rainbowSurgePalette } from "../../../../colorPalettes/index.js";
import { useSelector } from "../../../store/useSelector.js";
import { selectorChartDrawingArea } from "../../corePlugins/useChartDimensions/useChartDimensions.selectors.js";
import { selectorChartSeriesProcessed } from "../../corePlugins/useChartSeries/useChartSeries.selectors.js";
import { defaultizeXAxis, defaultizeYAxis } from "./defaultizeAxis.js";
import { selectorChartXAxis, selectorChartYAxis } from "./useChartCartesianAxisRendering.selectors.js";
import { getAxisIndex } from "./getAxisValue.js";
import { getSVGPoint } from "../../../getSVGPoint.js";
import { selectorChartsInteractionIsInitialized } from "../useChartInteraction/index.js";
export const useChartCartesianAxis = ({
params,
store,
seriesConfig,
svgRef,
instance
}) => {
const {
xAxis,
yAxis,
dataset
} = params;
if (process.env.NODE_ENV !== 'production') {
const ids = [...(xAxis ?? []), ...(yAxis ?? [])].filter(axis => axis.id).map(axis => axis.id);
const duplicates = new Set(ids.filter((id, index) => ids.indexOf(id) !== index));
if (duplicates.size > 0) {
warnOnce([`MUI X Charts: The following axis ids are duplicated: ${Array.from(duplicates).join(', ')}.`, `Please make sure that each axis has a unique id.`].join('\n'), 'error');
}
}
const drawingArea = useSelector(store, selectorChartDrawingArea);
const processedSeries = useSelector(store, selectorChartSeriesProcessed);
const isInteractionEnabled = useSelector(store, selectorChartsInteractionIsInitialized);
const {
axis: xAxisWithScale,
axisIds: xAxisIds
} = useSelector(store, selectorChartXAxis);
const {
axis: yAxisWithScale,
axisIds: yAxisIds
} = useSelector(store, selectorChartYAxis);
// The effect do not track any value defined synchronously during the 1st render by hooks called after `useChartCartesianAxis`
// As a consequence, the state generated by the 1st run of this useEffect will always be equal to the initialization one
const isFirstRender = React.useRef(true);
React.useEffect(() => {
if (isFirstRender.current) {
isFirstRender.current = false;
return;
}
store.update(prev => _extends({}, prev, {
cartesianAxis: _extends({}, prev.cartesianAxis, {
x: defaultizeXAxis(xAxis, dataset),
y: defaultizeYAxis(yAxis, dataset)
})
}));
}, [seriesConfig, drawingArea, xAxis, yAxis, dataset, store]);
const usedXAxis = xAxisIds[0];
const usedYAxis = yAxisIds[0];
React.useEffect(() => {
const element = svgRef.current;
if (!isInteractionEnabled || element === null || params.disableAxisListener) {
return () => {};
}
const handleOut = () => {
instance.cleanInteraction?.();
};
const handleMove = event => {
const target = 'targetTouches' in event ? event.targetTouches[0] : event;
const svgPoint = getSVGPoint(element, target);
if (!instance.isPointInside(svgPoint.x, svgPoint.y, event.target)) {
instance.cleanInteraction?.();
return;
}
instance.setPointerCoordinate?.(svgPoint);
};
const handleDown = event => {
const target = event.currentTarget;
if (!target) {
return;
}
if ('hasPointerCapture' in target && target.hasPointerCapture(event.pointerId)) {
target.releasePointerCapture(event.pointerId);
}
};
element.addEventListener('pointerdown', handleDown);
element.addEventListener('pointermove', handleMove);
element.addEventListener('pointercancel', handleOut);
element.addEventListener('pointerleave', handleOut);
return () => {
element.removeEventListener('pointerdown', handleDown);
element.removeEventListener('pointermove', handleMove);
element.removeEventListener('pointercancel', handleOut);
element.removeEventListener('pointerleave', handleOut);
};
}, [svgRef, store, xAxisWithScale, usedXAxis, yAxisWithScale, usedYAxis, instance, params.disableAxisListener, isInteractionEnabled]);
React.useEffect(() => {
const element = svgRef.current;
const onAxisClick = params.onAxisClick;
if (element === null || !onAxisClick) {
return () => {};
}
const handleMouseClick = event => {
event.preventDefault();
let dataIndex = null;
let isXAxis = false;
const svgPoint = getSVGPoint(element, event);
const xIndex = getAxisIndex(xAxisWithScale[usedXAxis], svgPoint.x);
isXAxis = xIndex !== -1;
dataIndex = isXAxis ? xIndex : getAxisIndex(yAxisWithScale[usedYAxis], svgPoint.y);
const USED_AXIS_ID = isXAxis ? xAxisIds[0] : yAxisIds[0];
if (dataIndex == null || dataIndex === -1) {
return;
}
// The .data exist because otherwise the dataIndex would be null or -1.
const axisValue = (isXAxis ? xAxisWithScale : yAxisWithScale)[USED_AXIS_ID].data[dataIndex];
const seriesValues = {};
Object.keys(processedSeries).filter(seriesType => ['bar', 'line'].includes(seriesType)).forEach(seriesType => {
processedSeries[seriesType]?.seriesOrder.forEach(seriesId => {
const seriesItem = processedSeries[seriesType].series[seriesId];
const providedXAxisId = seriesItem.xAxisId;
const providedYAxisId = seriesItem.yAxisId;
const axisKey = isXAxis ? providedXAxisId : providedYAxisId;
if (axisKey === undefined || axisKey === USED_AXIS_ID) {
seriesValues[seriesId] = seriesItem.data[dataIndex];
}
});
});
onAxisClick(event, {
dataIndex,
axisValue,
seriesValues
});
};
element.addEventListener('click', handleMouseClick);
return () => {
element.removeEventListener('click', handleMouseClick);
};
}, [params.onAxisClick, processedSeries, svgRef, xAxisWithScale, xAxisIds, yAxisWithScale, yAxisIds, usedXAxis, usedYAxis]);
return {};
};
useChartCartesianAxis.params = {
xAxis: true,
yAxis: true,
dataset: true,
onAxisClick: true,
disableAxisListener: true
};
useChartCartesianAxis.getDefaultizedParams = ({
params
}) => {
return _extends({}, params, {
colors: params.colors ?? rainbowSurgePalette,
theme: params.theme ?? 'light',
defaultizedXAxis: defaultizeXAxis(params.xAxis, params.dataset),
defaultizedYAxis: defaultizeYAxis(params.yAxis, params.dataset)
});
};
useChartCartesianAxis.getInitialState = params => ({
cartesianAxis: {
x: params.defaultizedXAxis,
y: params.defaultizedYAxis
}
});