Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -80,11 +80,34 @@ function ChartsAxisHighlight(props: ChartsAxisHighlightProps) {

const getXPosition = getValueToPositionMapper(xScale);
const getYPosition = getValueToPositionMapper(yScale);

const axisX = axis.x;
const axisY = axis.y;

const isBandScaleX = xAxisHighlight === 'band' && axisX !== null && isBandScale(xScale);
const isBandScaleY = yAxisHighlight === 'band' && axisY !== null && isBandScale(yScale);

if (process.env.NODE_ENV !== 'production') {
const isXError = isBandScaleX && xScale(axisX.value) === undefined;
const isYError = isBandScaleY && yScale(axisY.value) === undefined;

if (isXError || isYError) {
console.error(
[
`MUI X Charts: The position value provided for the axis is not valid for the current scale.`,
`This probably means something is wrong with the data passed to the chart.`,
`The ChartsAxisHighlight component will not be displayed.`,
].join('\n'),
);
}
}

return (
<React.Fragment>
{xAxisHighlight === 'band' && axis.x !== null && isBandScale(xScale) && (
{isBandScaleX && xScale(axisX.value) !== undefined && (
<ChartsAxisHighlightPath
d={`M ${xScale(axis.x.value)! - (xScale.step() - xScale.bandwidth()) / 2} ${
// @ts-expect-error, xScale value is checked in the statement above
d={`M ${xScale(axisX.value) - (xScale.step() - xScale.bandwidth()) / 2} ${
yScale.range()[0]
} l ${xScale.step()} 0 l 0 ${
yScale.range()[1] - yScale.range()[0]
Expand All @@ -94,10 +117,11 @@ function ChartsAxisHighlight(props: ChartsAxisHighlightProps) {
/>
)}

{yAxisHighlight === 'band' && axis.y !== null && isBandScale(yScale) && (
{isBandScaleY && yScale(axisY.value) === undefined && (
<ChartsAxisHighlightPath
d={`M ${xScale.range()[0]} ${
yScale(axis.y.value)! - (yScale.step() - yScale.bandwidth()) / 2
// @ts-expect-error, yScale value is checked in the statement above
yScale(axisY.value) - (yScale.step() - yScale.bandwidth()) / 2
} l 0 ${yScale.step()} l ${
xScale.range()[1] - xScale.range()[0]
} 0 l 0 ${-yScale.step()} Z`}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import useEnhancedEffect from '@mui/utils/useEnhancedEffect';
import { InteractionContext } from '../context/InteractionProvider';
import { useCartesianContext } from '../context/CartesianProvider';
import { getValueToPositionMapper } from '../hooks/useScale';
import { getSVGPoint } from '../internals/utils';
import { getSVGPoint } from '../internals/getSVGPoint';
import { ScatterItemIdentifier } from '../models';
import { SeriesId } from '../models/seriesType/common';
import { useDrawingArea, useSvgRef } from '../hooks';
Expand Down
13 changes: 5 additions & 8 deletions packages/x-charts/src/hooks/useAxisEvents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { InteractionContext } from '../context/InteractionProvider';
import { useCartesianContext } from '../context/CartesianProvider';
import { isBandScale } from '../internals/isBandScale';
import { AxisDefaultized } from '../models/axis';
import { getSVGPoint } from '../internals/utils';
import { getSVGPoint } from '../internals/getSVGPoint';
import { useSvgRef } from './useSvgRef';
import { useDrawingArea } from './useDrawingArea';

Expand Down Expand Up @@ -31,10 +31,7 @@ export const useAxisEvents = (disableAxisListener: boolean) => {
return () => {};
}

const getUpdate = (axisConfig: AxisDefaultized, mouseValue: number) => {
if (usedXAxis === null) {
return null;
}
function getNewAxisState(axisConfig: AxisDefaultized, mouseValue: number) {
const { scale, data: axisData, reverse } = axisConfig;

if (!isBandScale(scale)) {
Expand Down Expand Up @@ -93,7 +90,7 @@ export const useAxisEvents = (disableAxisListener: boolean) => {
index: dataIndex,
value: axisData![dataIndex],
};
};
}

const handleOut = () => {
mousePosition.current = {
Expand All @@ -118,8 +115,8 @@ export const useAxisEvents = (disableAxisListener: boolean) => {
dispatch({ type: 'exitChart' });
return;
}
const newStateX = getUpdate(xAxis[usedXAxis], svgPoint.x);
const newStateY = getUpdate(yAxis[usedYAxis], svgPoint.y);
const newStateX = getNewAxisState(xAxis[usedXAxis], svgPoint.x);
const newStateY = getNewAxisState(yAxis[usedYAxis], svgPoint.y);

dispatch({ type: 'updateAxis', data: { x: newStateX, y: newStateY } });
};
Expand Down
2 changes: 1 addition & 1 deletion packages/x-charts/src/hooks/useScale.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { AxisScaleConfig, D3Scale, ScaleName } from '../models/axis';
*/
export function getValueToPositionMapper(scale: D3Scale) {
if (isBandScale(scale)) {
return (value: any) => scale(value)! + scale.bandwidth() / 2;
return (value: any) => (scale(value) ?? 0) + scale.bandwidth() / 2;
}
return (value: any) => scale(value) as number;
}
Expand Down
11 changes: 11 additions & 0 deletions packages/x-charts/src/internals/getSVGPoint.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/**
* Transform mouse event position to coordinates inside the SVG.
* @param svg The SVG element
* @param event The mouseEvent to transform
*/
export function getSVGPoint(svg: SVGSVGElement, event: Pick<MouseEvent, 'clientX' | 'clientY'>) {
const pt = svg.createSVGPoint();
pt.x = event.clientX;
pt.y = event.clientY;
return pt.matrixTransform(svg.getScreenCTM()!.inverse());
}
2 changes: 2 additions & 0 deletions packages/x-charts/src/internals/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export * from '../ResponsiveChartContainer/ResizableContainer';
// hooks
export { useReducedMotion } from '../hooks/useReducedMotion';
export { useSeries } from '../hooks/useSeries';
export { useDrawingArea } from '../hooks/useDrawingArea';
export { useChartContainerHooks } from '../ChartContainer/useChartContainerHooks';
export { useScatterChartProps } from '../ScatterChart/useScatterChartProps';
export { useLineChartProps } from '../LineChart/useLineChartProps';
Expand All @@ -15,6 +16,7 @@ export { useBarChartProps } from '../BarChart/useBarChartProps';
// utils
export * from './defaultizeValueFormatter';
export * from './configInit';
export * from './getSVGPoint';

// contexts

Expand Down
12 changes: 0 additions & 12 deletions packages/x-charts/src/internals/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,6 @@ export function getSymbol(shape: SymbolsTypes): number {
type Without<T, U> = { [P in Exclude<keyof T, keyof U>]?: never };
export type XOR<T, U> = T | U extends object ? (Without<T, U> & U) | (Without<U, T> & T) : T | U;

/**
* Transform mouse event position to coordinates inside the SVG.
* @param svg The SVG element
* @param event The mouseEvent to transform
*/
export function getSVGPoint(svg: SVGSVGElement, event: Pick<MouseEvent, 'clientX' | 'clientY'>) {
const pt = svg.createSVGPoint();
pt.x = event.clientX;
pt.y = event.clientY;
return pt.matrixTransform(svg.getScreenCTM()!.inverse());
}

/**
* Helper that converts values and percentages into values.
* @param value The value provided by the developer. Can either be a number or a string with '%' or 'px'.
Expand Down