@mui/x-charts
Version:
The community edition of MUI X Charts components.
234 lines (231 loc) • 8.7 kB
JavaScript
'use client';
import { useSeries } from "../hooks/useSeries.mjs";
import { useColorProcessor } from "../internals/plugins/corePlugins/useChartSeries/useColorProcessor.mjs";
import { useStore } from "../internals/store/useStore.mjs";
import { getLabel } from "../internals/getLabel.mjs";
import { utcFormatter } from "./utils.mjs";
import { useRadiusAxes, useRadiusAxis, useRotationAxes, useRotationAxis, useXAxes, useXAxis, useYAxes, useYAxis } from "../hooks/useAxis.mjs";
import { useZAxes } from "../hooks/useZAxis.mjs";
import { selectorChartsInteractionTooltipXAxes, selectorChartsInteractionTooltipYAxes } from "../internals/plugins/featurePlugins/useChartCartesianAxis/index.mjs";
import { selectorChartsInteractionTooltipRadiusAxes, selectorChartsInteractionTooltipRotationAxes } from "../internals/plugins/featurePlugins/useChartPolarAxis/useChartPolarInteraction.selectors.mjs";
import { isPolarSeriesType } from "../internals/isPolar.mjs";
import { selectorIsItemVisibleGetter } from "../internals/plugins/featurePlugins/useChartVisibilityManager/useChartVisibilityManager.selectors.mjs";
import { composableCartesianSeriesTypes } from "../models/seriesType/composition.mjs";
function defaultAxisTooltipConfig(axis, dataIndex, axisDirection) {
const axisValue = axis.data?.[dataIndex] ?? null;
const axisFormatter = axis.valueFormatter ?? (v => axis.scaleType === 'utc' ? utcFormatter(v) : v.toLocaleString());
const axisFormattedValue = axisFormatter(axisValue, {
location: 'tooltip',
scale: axis.scale
});
return {
axisDirection,
axisId: axis.id,
mainAxis: axis,
dataIndex,
axisValue,
axisFormattedValue,
seriesItems: []
};
}
function getSeriesMark(series) {
if (!('showMark' in series) || !series.showMark) {
return undefined;
}
if ('shape' in series && series.shape) {
return series.shape;
}
return 'circle';
}
/**
* Returns the axes to display in the tooltip and the series item related to them.
*/
export function useAxesTooltip(params) {
const {
directions
} = params ?? {};
const defaultXAxis = useXAxis();
const defaultYAxis = useYAxis();
const defaultRotationAxis = useRotationAxis();
const defaultRadiusAxis = useRadiusAxis();
const store = useStore();
const tooltipXAxes = store.use(selectorChartsInteractionTooltipXAxes);
const tooltipYAxes = store.use(selectorChartsInteractionTooltipYAxes);
const tooltipRotationAxes = store.use(selectorChartsInteractionTooltipRotationAxes);
const tooltipRadiusAxes = store.use(selectorChartsInteractionTooltipRadiusAxes);
const series = useSeries();
const {
xAxis
} = useXAxes();
const {
yAxis
} = useYAxes();
const {
zAxis,
zAxisIds
} = useZAxes();
const {
rotationAxis
} = useRotationAxes();
const {
radiusAxis
} = useRadiusAxes();
const colorProcessors = useColorProcessor();
const isItemVisible = store.use(selectorIsItemVisibleGetter);
if (tooltipXAxes.length === 0 && tooltipYAxes.length === 0 && tooltipRotationAxes.length === 0 && tooltipRadiusAxes.length === 0) {
return null;
}
const tooltipAxes = [];
if (directions === undefined || directions.includes('x')) {
tooltipXAxes.forEach(({
axisId,
dataIndex
}) => {
tooltipAxes.push(defaultAxisTooltipConfig(xAxis[axisId], dataIndex, 'x'));
});
}
if (directions === undefined || directions.includes('y')) {
tooltipYAxes.forEach(({
axisId,
dataIndex
}) => {
tooltipAxes.push(defaultAxisTooltipConfig(yAxis[axisId], dataIndex, 'y'));
});
}
if (directions === undefined || directions.includes('rotation')) {
tooltipRotationAxes.forEach(({
axisId,
dataIndex
}) => {
tooltipAxes.push(defaultAxisTooltipConfig(rotationAxis[axisId], dataIndex, 'rotation'));
});
}
if (directions === undefined || directions.includes('radius')) {
tooltipRadiusAxes.forEach(({
axisId,
dataIndex
}) => {
tooltipAxes.push(defaultAxisTooltipConfig(radiusAxis[axisId], dataIndex, 'radius'));
});
}
Object.keys(series).filter(seriesType => composableCartesianSeriesTypes.has(seriesType)).forEach(seriesType => {
const seriesOfType = series[seriesType];
if (!seriesOfType) {
return [];
}
return seriesOfType.seriesOrder.forEach(seriesId => {
const seriesToAdd = seriesOfType.series[seriesId];
// Skip hidden series (only if visibility manager is available)
if (isItemVisible && !isItemVisible({
type: seriesType,
seriesId
})) {
return;
}
const providedXAxisId = seriesToAdd.xAxisId ?? defaultXAxis.id;
const providedYAxisId = seriesToAdd.yAxisId ?? defaultYAxis.id;
const tooltipItemIndex = tooltipAxes.findIndex(({
axisDirection,
axisId
}) => axisDirection === 'x' && axisId === providedXAxisId || axisDirection === 'y' && axisId === providedYAxisId);
// Test if the series uses the default axis
if (tooltipItemIndex >= 0) {
const zAxisId = 'zAxisId' in seriesToAdd ? seriesToAdd.zAxisId : zAxisIds[0];
const {
dataIndex
} = tooltipAxes[tooltipItemIndex];
const color = colorProcessors[seriesType]?.(seriesToAdd, xAxis[providedXAxisId], yAxis[providedYAxisId], zAxisId ? zAxis[zAxisId] : undefined)(dataIndex) ?? '';
const rawValue = seriesToAdd.data[dataIndex] ?? null;
const formattedLabel = getLabel(seriesToAdd.label, 'tooltip') ?? null;
let value;
let formattedValue;
if (seriesType === 'ohlc' && Array.isArray(rawValue)) {
const [open, high, low, close] = rawValue;
const formatter = seriesToAdd.valueFormatter;
value = {
open,
high,
low,
close
};
formattedValue = {
open: formatter(open, {
dataIndex,
field: 'open'
}),
high: formatter(high, {
dataIndex,
field: 'high'
}),
low: formatter(low, {
dataIndex,
field: 'low'
}),
close: formatter(close, {
dataIndex,
field: 'close'
})
};
} else {
value = rawValue;
formattedValue = seriesToAdd.valueFormatter(rawValue, {
dataIndex
});
}
tooltipAxes[tooltipItemIndex].seriesItems.push({
seriesId,
color,
value,
formattedValue,
formattedLabel,
markType: seriesToAdd.labelMarkType,
markShape: getSeriesMark(seriesToAdd)
});
}
});
});
Object.keys(series).filter(isPolarSeriesType).forEach(seriesType => {
const seriesOfType = series[seriesType];
if (!seriesOfType) {
return [];
}
return seriesOfType.seriesOrder.forEach(seriesId => {
const seriesToAdd = seriesOfType.series[seriesId];
// Skip hidden series (only if visibility manager is available)
if (isItemVisible && !isItemVisible({
type: seriesType,
seriesId
})) {
return;
}
const providedRotationAxisId = ('rotationAxisId' in seriesToAdd ? seriesToAdd.rotationAxisId : undefined) ?? defaultRotationAxis?.id;
const providedRadiusAxisId = ('radiusAxisId' in seriesToAdd ? seriesToAdd.radiusAxisId : undefined) ?? defaultRadiusAxis?.id;
const tooltipItemIndex = tooltipAxes.findIndex(({
axisDirection,
axisId
}) => axisDirection === 'rotation' && axisId === providedRotationAxisId || axisDirection === 'radius' && axisId === providedRadiusAxisId);
// Test if the series uses the default axis
if (tooltipItemIndex >= 0) {
const {
dataIndex
} = tooltipAxes[tooltipItemIndex];
const color = colorProcessors[seriesType]?.(seriesToAdd, providedRotationAxisId !== undefined ? rotationAxis[providedRotationAxisId] : undefined, providedRadiusAxisId !== undefined ? radiusAxis[providedRadiusAxisId] : undefined)(dataIndex) ?? '';
const value = seriesToAdd.data[dataIndex] ?? null;
const formattedValue = seriesToAdd.valueFormatter(value, {
dataIndex
});
const formattedLabel = getLabel(seriesToAdd.label, 'tooltip') ?? null;
tooltipAxes[tooltipItemIndex].seriesItems.push({
seriesId,
color,
value,
formattedValue,
formattedLabel,
markType: seriesToAdd.labelMarkType,
markShape: getSeriesMark(seriesToAdd)
});
}
});
});
return tooltipAxes;
}