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
2 changes: 2 additions & 0 deletions packages/x-charts/src/RadarChart/seriesConfig/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ import { ChartSeriesTypeConfig } from '../../internals/plugins/models/seriesConf
import legendGetter from './legend';
import tooltipGetter, { axisTooltipGetter } from './tooltip';
import getSeriesWithDefaultValues from './getSeriesWithDefaultValues';
import tooltipItemPositionGetter from './tooltipPosition';

export const radarSeriesConfig: ChartSeriesTypeConfig<'radar'> = {
colorProcessor: getColor,
seriesProcessor: formatter,
legendGetter,
tooltipGetter,
tooltipItemPositionGetter,
axisTooltipGetter,
getSeriesWithDefaultValues,
radiusExtremumGetter,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { D3OrdinalScale } from '../../models/axis';
import { generatePolar2svg } from '../../internals/plugins/featurePlugins/useChartPolarAxis/coordinateTransformation';
import { getDrawingAreaCenter } from '../../internals/plugins/featurePlugins/useChartPolarAxis';
import type { TooltipItemPositionGetter } from '../../internals/plugins/models/seriesConfig/tooltipItemPositionGetter.types';

const tooltipItemPositionGetter: TooltipItemPositionGetter<'radar'> = (params) => {
const { series, identifier, axesConfig, drawingArea, placement } = params;

if (!identifier) {
return null;
}
const itemSeries = series.radar?.series[identifier.seriesId];

if (itemSeries == null) {
return null;
}

const { radiusAxes, rotationAxes } = axesConfig;

if (radiusAxes === undefined || rotationAxes === undefined) {
return null;
}

// Only one rotation axis is supported for radar charts
const rotationAxis = rotationAxes.axis[rotationAxes.axisIds[0]];

const metrics = (rotationAxis.scale.domain() as (string | number)[]) ?? [];
const angles = metrics.map((key) => (rotationAxis.scale as D3OrdinalScale)(key)!);

const { cx, cy } = getDrawingAreaCenter(drawingArea);
const polar2svg = generatePolar2svg({ cx, cy });

const points = itemSeries.data.map((value, dataIndex) => {
const rId = radiusAxes.axisIds[dataIndex];
const r = radiusAxes.axis[rId].scale(value)!;

const angle = angles[dataIndex];
return polar2svg(r, angle);
});

if (points.length === 0) {
return null;
}

const [top, right, bottom, left] = points.reduce(
(acc, [x, y]) => {
return [Math.min(y, acc[0]), Math.max(x, acc[1]), Math.max(y, acc[2]), Math.min(x, acc[3])];
},
[Infinity, -Infinity, -Infinity, Infinity],
);

switch (placement) {
case 'right':
return { x: right, y: (top + bottom) / 2 };
case 'bottom':
return { x: (left + right) / 2, y: bottom };
case 'left':
return { x: left, y: (top + bottom) / 2 };
case 'top':
default:
return { x: (left + right) / 2, y: top };
}
};

export default tooltipItemPositionGetter;
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,22 @@ import {
selectorChartsLastInteraction,
} from './useChartInteraction.selectors';
import { ChartSeriesConfig } from '../../models/seriesConfig/seriesConfig.types';
import { AxisId, ChartsXAxisProps, ChartsYAxisProps } from '../../../../models/axis';
import {
AxisId,
ChartsRadiusAxisProps,
ChartsRotationAxisProps,
ChartsXAxisProps,
ChartsYAxisProps,
} from '../../../../models/axis';
import { ComputeResult } from '../useChartCartesianAxis/computeAxisValue';
import { selectorChartDrawingArea } from '../../corePlugins/useChartDimensions/useChartDimensions.selectors';
import { ChartDrawingArea } from '../../../../hooks/useDrawingArea';
import { isCartesianSeries } from '../../../isCartesian';
import {
selectorChartRadiusAxis,
selectorChartRotationAxis,
} from '../useChartPolarAxis/useChartPolarAxis.selectors';
import { ComputeResult as ComputePolarResult } from '../useChartPolarAxis/computeAxisValue';

export const selectorChartsTooltipItem = createSelector(
selectorChartsLastInteraction,
Expand All @@ -47,21 +58,68 @@ export const selectorChartsTooltipItemIsDefined = createSelector(
lastInteraction === 'keyboard' ? keyboardItemIsDefined : interactionItemIsDefined,
);

const selectorChartsTooltipAxisConfig = createSelector(
selectorChartsTooltipItem,
selectorChartXAxis,
selectorChartYAxis,
selectorChartRotationAxis,
selectorChartRadiusAxis,
selectorChartSeriesProcessed,
function selectorChartsTooltipAxisConfig<T extends ChartSeriesType>(
identifier: ChartItemIdentifierWithData<T> | null,
{ axis: xAxis, axisIds: xAxisIds }: ComputeResult<ChartsXAxisProps>,
{ axis: yAxis, axisIds: yAxisIds }: ComputeResult<ChartsYAxisProps>,
rotationAxes: ComputePolarResult<ChartsRotationAxisProps>,
radiusAxes: ComputePolarResult<ChartsRadiusAxisProps>,
series: ProcessedSeries<T>,
) {
if (!identifier) {
return {};
}

const itemSeries = series[identifier.type as T]?.series[identifier.seriesId] as
| ChartSeriesDefaultized<T>
| undefined;

if (!itemSeries) {
return {};
}
const axesConfig: TooltipPositionGetterAxesConfig = {
rotationAxes,
radiusAxes,
};

const xAxisId: AxisId | undefined = isCartesianSeries(itemSeries)
? (itemSeries.xAxisId ?? xAxisIds[0])
: undefined;
const yAxisId: AxisId | undefined = isCartesianSeries(itemSeries)
? (itemSeries.yAxisId ?? yAxisIds[0])
: undefined;

if (xAxisId !== undefined) {
axesConfig.x = xAxis[xAxisId];
}
if (yAxisId !== undefined) {
axesConfig.y = yAxis[yAxisId];
}

return axesConfig;
},
);

export const selectorChartsTooltipItemPosition = createSelector(
selectorChartsTooltipItem,
selectorChartDrawingArea,
selectorChartSeriesConfig,
selectorChartXAxis,
selectorChartYAxis,
selectorChartSeriesProcessed,
selectorChartsTooltipAxisConfig,

function selectorChartsTooltipItemPosition<T extends ChartSeriesType>(
identifier: ChartItemIdentifierWithData<T> | null,
drawingArea: ChartDrawingArea,
seriesConfig: ChartSeriesConfig<T>,
{ axis: xAxis, axisIds: xAxisIds }: ComputeResult<ChartsXAxisProps>,
{ axis: yAxis, axisIds: yAxisIds }: ComputeResult<ChartsYAxisProps>,
series: ProcessedSeries<T>,
axesConfig: TooltipPositionGetterAxesConfig,
placement: 'top' | 'bottom' | 'left' | 'right' = 'top',
) {
if (!identifier) {
Expand All @@ -72,34 +130,17 @@ export const selectorChartsTooltipItemPosition = createSelector(
| ChartSeriesDefaultized<T>
| undefined;

if (itemSeries) {
const axesConfig: TooltipPositionGetterAxesConfig = {};

const xAxisId: AxisId | undefined = isCartesianSeries(itemSeries)
? (itemSeries.xAxisId ?? xAxisIds[0])
: undefined;
const yAxisId: AxisId | undefined = isCartesianSeries(itemSeries)
? (itemSeries.yAxisId ?? yAxisIds[0])
: undefined;

if (xAxisId !== undefined) {
axesConfig.x = xAxis[xAxisId];
}
if (yAxisId !== undefined) {
axesConfig.y = yAxis[yAxisId];
}

return (
seriesConfig[itemSeries.type as T].tooltipItemPositionGetter?.({
series,
drawingArea,
axesConfig,
identifier,
placement,
}) ?? null
);
if (!itemSeries) {
return null;
}

return null;
return (
seriesConfig[itemSeries.type as T].tooltipItemPositionGetter?.({
series,
drawingArea,
axesConfig,
identifier,
placement,
}) ?? null
);
},
);
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createSelector } from '@mui/x-internals/store';
import { createSelector, createSelectorMemoized } from '@mui/x-internals/store';
import { selectorChartDrawingArea } from '../../corePlugins/useChartDimensions';
import {
selectorChartSeriesConfig,
Expand All @@ -7,6 +7,7 @@ import {
import { UseChartPolarAxisSignature } from './useChartPolarAxis.types';
import { ChartState } from '../../models/chart';
import { computeAxisValue } from './computeAxisValue';
import type { ChartDrawingArea } from '../../../../hooks/useDrawingArea';

export const selectorChartPolarAxisState = (state: ChartState<[], [UseChartPolarAxisSignature]>) =>
state.polarAxis;
Expand All @@ -25,7 +26,7 @@ export const selectorChartRawRadiusAxis = createSelector(
* The only interesting selectors that merge axis data and zoom if provided.
*/

export const selectorChartRotationAxis = createSelector(
export const selectorChartRotationAxis = createSelectorMemoized(
selectorChartRawRotationAxis,
selectorChartDrawingArea,
selectorChartSeriesProcessed,
Expand All @@ -40,7 +41,7 @@ export const selectorChartRotationAxis = createSelector(
}),
);

export const selectorChartRadiusAxis = createSelector(
export const selectorChartRadiusAxis = createSelectorMemoized(
selectorChartRawRadiusAxis,
selectorChartDrawingArea,
selectorChartSeriesProcessed,
Expand All @@ -55,7 +56,13 @@ export const selectorChartRadiusAxis = createSelector(
}),
);

export const selectorChartPolarCenter = createSelector(selectorChartDrawingArea, (drawingArea) => ({
cx: drawingArea.left + drawingArea.width / 2,
cy: drawingArea.top + drawingArea.height / 2,
}));
export function getDrawingAreaCenter(drawingArea: ChartDrawingArea) {
return {
cx: drawingArea.left + drawingArea.width / 2,
cy: drawingArea.top + drawingArea.height / 2,
};
}
export const selectorChartPolarCenter = createSelector(
selectorChartDrawingArea,
getDrawingAreaCenter,
);
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,18 @@ import type {
import {
ChartsRotationAxisProps,
ChartsRadiusAxisProps,
PolarAxisDefaultized,
ComputedXAxis,
ComputedYAxis,
} from '../../../../models/axis';
import { ChartDrawingArea } from '../../../../hooks/useDrawingArea';
import { ProcessedSeries } from '../../corePlugins/useChartSeries';
import { ComputeResult } from '../../featurePlugins/useChartPolarAxis/computeAxisValue';

export interface TooltipPositionGetterAxesConfig {
x?: ComputedXAxis;
y?: ComputedYAxis;
rotation?: PolarAxisDefaultized<any, any, ChartsRotationAxisProps>;
radius?: PolarAxisDefaultized<any, any, ChartsRadiusAxisProps>;
rotationAxes?: ComputeResult<ChartsRotationAxisProps>;
radiusAxes?: ComputeResult<ChartsRadiusAxisProps>;
}

export type TooltipItemPositionGetter<TSeriesType extends ChartSeriesType> = (params: {
Expand Down
Loading