Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
alexfauquette committed Mar 19, 2024
1 parent acea8ee commit 7bf37e8
Show file tree
Hide file tree
Showing 10 changed files with 406 additions and 13 deletions.
32 changes: 30 additions & 2 deletions docs/data/charts/bar-demo/TinyBarChart.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as React from 'react';
import { ChartContainer, BarPlot } from '@mui/x-charts';
import { ChartContainer, BarPlot, ChartsTooltip } from '@mui/x-charts';

const uData = [4000, 3000, 2000, 2780, 1890, 2390, 3490];
const xLabels = [
Expand All @@ -18,9 +18,37 @@ export default function TinyBarChart() {
width={500}
height={300}
series={[{ data: uData, label: 'uv', type: 'bar' }]}
xAxis={[{ scaleType: 'band', data: xLabels }]}
yAxis={[
{
colorMap: {
type: 'continuous',
min: 2000,
max: 5000,
color: ['blue', 'red'],
},
},
]}
xAxis={[
{
scaleType: 'band',
data: xLabels,
colorMap: {
type: 'ordinal',
colors: [
'#ffff00',
'#ffff00',
'#f0f',
'#ffff00',
'#ffff00',
'#ffff00',
'#ffff00',
],
},
},
]}
>
<BarPlot />
<ChartsTooltip trigger="item" />
</ChartContainer>
);
}
39 changes: 33 additions & 6 deletions packages/x-charts/src/BarChart/BarPlot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,18 @@ import { useTransition } from '@react-spring/web';
import { SeriesContext } from '../context/SeriesContextProvider';
import { CartesianContext } from '../context/CartesianContextProvider';
import { BarElement, BarElementProps, BarElementSlotProps, BarElementSlots } from './BarElement';
import { isBandScaleConfig } from '../models/axis';
import {
AxisDefaultized,
ContinuouseScaleName,
isBandScaleConfig,
isPointScaleConfig,
} from '../models/axis';
import { FormatterResult } from '../models/seriesType/config';
import { HighlightScope } from '../context/HighlightProvider';
import { BarItemIdentifier, BarSeriesType } from '../models';
import { DEFAULT_X_AXIS_KEY, DEFAULT_Y_AXIS_KEY } from '../constants';
import { SeriesId } from '../models/seriesType/common';
import { getColor } from './getColor';

/**
* Solution of the equations
Expand Down Expand Up @@ -98,7 +104,8 @@ const useAggregatedData = (): CompletedBarData[] => {
const yAxisConfig = yAxis[yAxisKey];

const verticalLayout = series[seriesId].layout === 'vertical';
let baseScaleConfig;
let baseScaleConfig: AxisDefaultized<'band'>;
// let valueScaleConfig: AxisDefaultized<ContinuouseScaleName>;
if (verticalLayout) {
if (!isBandScaleConfig(xAxisConfig)) {
throw new Error(
Expand All @@ -118,7 +125,17 @@ const useAggregatedData = (): CompletedBarData[] => {
} shoud have data property.`,
);
}
baseScaleConfig = xAxisConfig;
baseScaleConfig = xAxisConfig as AxisDefaultized<'band'>;
if (isBandScaleConfig(yAxisConfig) || isPointScaleConfig(yAxisConfig)) {
throw new Error(
`MUI X Charts: ${
yAxisKey === DEFAULT_Y_AXIS_KEY
? 'The first `yAxis`'
: `The y-axis with id "${yAxisKey}"`
} shoud be a continuouse type to display the bar series of id "${seriesId}".`,
);
}
// valueScaleConfig = yAxisConfig as AxisDefaultized<ContinuouseScaleName>;
} else {
if (!isBandScaleConfig(yAxisConfig)) {
throw new Error(
Expand All @@ -139,12 +156,22 @@ const useAggregatedData = (): CompletedBarData[] => {
} shoud have data property.`,
);
}
baseScaleConfig = yAxisConfig;
baseScaleConfig = yAxisConfig as AxisDefaultized<'band'>;
if (isBandScaleConfig(xAxisConfig) || isPointScaleConfig(xAxisConfig)) {
throw new Error(
`MUI X Charts: ${
xAxisKey === DEFAULT_X_AXIS_KEY
? 'The first `xAxis`'
: `The x-axis with id "${xAxisKey}"`
} shoud be a continuouse type to display the bar series of id "${seriesId}".`,
);
}
}

const xScale = xAxisConfig.scale;
const yScale = yAxisConfig.scale;

const colorGetter = getColor(series[seriesId], xAxis[xAxisKey], yAxis[yAxisKey]);
const bandWidth = baseScaleConfig.scale.bandwidth();

const { barWidth, offset } = getBandSize({
Expand All @@ -154,7 +181,7 @@ const useAggregatedData = (): CompletedBarData[] => {
});
const barOffset = groupIndex * (barWidth + offset);

const { stackedData, color } = series[seriesId];
const { stackedData } = series[seriesId];

return stackedData.map((values, dataIndex: number) => {
const valueCoordinates = values.map((v) => (verticalLayout ? yScale(v)! : xScale(v)!));
Expand All @@ -176,7 +203,7 @@ const useAggregatedData = (): CompletedBarData[] => {
yOrigin: yScale(0)!,
height: verticalLayout ? maxValueCoord - minValueCoord : barWidth,
width: verticalLayout ? barWidth : maxValueCoord - minValueCoord,
color,
color: colorGetter(dataIndex),
highlightScope: series[seriesId].highlightScope,
};
});
Expand Down
28 changes: 28 additions & 0 deletions packages/x-charts/src/BarChart/getColor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { AxisDefaultized } from '../models/axis';
import { DefaultizedBarSeriesType } from '../models/seriesType/bar';

export default function getColor(
series: DefaultizedBarSeriesType,
xAxis: AxisDefaultized,
yAxis: AxisDefaultized,
) {
const verticalLayout = series.layout === 'vertical';

const bandColorScale = verticalLayout ? xAxis.colorScale : yAxis.colorScale;
const valueColorScale = verticalLayout ? yAxis.colorScale : xAxis.colorScale;
const bandValues = verticalLayout ? xAxis.data! : yAxis.data!;

if (valueColorScale) {
return (dataIndex: number) => {
const value = series.data[dataIndex];
return value === null ? series.color : valueColorScale(value);
};
}
if (bandColorScale) {
return (dataIndex: number) => {
const value = bandValues[dataIndex];
return value === null ? series.color : bandColorScale(value);
};
}
return () => series.color;
}
15 changes: 15 additions & 0 deletions packages/x-charts/src/ChartsTooltip/ChartsItemTooltipContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { SeriesContext } from '../context/SeriesContextProvider';
import { ChartSeriesDefaultized, ChartSeriesType } from '../models/seriesType/config';
import { ChartsTooltipClasses } from './chartsTooltipClasses';
import { DefaultChartsItemTooltipContent } from './DefaultChartsItemTooltipContent';
import { CartesianContext } from '../context/CartesianContextProvider';
import { getColor } from '../BarChart/getColor';

export type ChartsItemContentProps<T extends ChartSeriesType = ChartSeriesType> = {
/**
Expand Down Expand Up @@ -37,6 +39,18 @@ function ChartsItemTooltipContent<T extends ChartSeriesType>(props: {
itemData.seriesId
] as ChartSeriesDefaultized<T>;

const axisData = React.useContext(CartesianContext);

const { xAxis, yAxis, xAxisIds, yAxisIds } = axisData;
const defaultXAxisId = xAxisIds[0];
const defaultYAxisId = yAxisIds[0];

const color = getColor(
series,
xAxis[series.xAxisKey ?? defaultXAxisId],
yAxis[series.yAxisKey ?? defaultYAxisId],
);

const Content = content ?? DefaultChartsItemTooltipContent;
const chartTooltipContentProps = useSlotProps({
elementType: Content,
Expand All @@ -46,6 +60,7 @@ function ChartsItemTooltipContent<T extends ChartSeriesType>(props: {
series,
sx,
classes,
getColor: color,
},
ownerState: {},
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { CommonSeriesType } from '../models/seriesType/common';
function DefaultChartsItemTooltipContent<T extends ChartSeriesType = ChartSeriesType>(
props: ChartsItemContentProps<T>,
) {
const { series, itemData, sx, classes } = props;
const { series, itemData, sx, classes, getColor } = props;

if (itemData.dataIndex === undefined) {
return null;
Expand All @@ -27,10 +27,12 @@ function DefaultChartsItemTooltipContent<T extends ChartSeriesType = ChartSeries
displayedLabel: series.data[itemData.dataIndex].label,
}
: {
color: series.color,
color: getColor(itemData.dataIndex) ?? series.color,
displayedLabel: series.label,
};

console.log(itemData);
console.log(getColor(itemData.dataIndex));
const value = series.data[itemData.dataIndex];
const formattedValue = (
series.valueFormatter as CommonSeriesType<typeof value>['valueFormatter']
Expand Down
21 changes: 19 additions & 2 deletions packages/x-charts/src/context/CartesianContextProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
import { MakeOptional } from '../models/helpers';
import { getTickNumber } from '../hooks/useTicks';
import { SeriesId } from '../models/seriesType/common';
import { getColorScale, getOrdinalColorScale } from '../internals/colorScale';

export type CartesianContextProviderProps = {
/**
Expand Down Expand Up @@ -179,7 +180,10 @@ function CartesianContextProvider(props: CartesianContextProviderProps) {
};

const allXAxis: AxisConfig[] = [
...(xAxis?.map((axis, index) => ({ id: `deaultized-x-axis-${index}`, ...axis })) ?? []),
...(xAxis?.map((axis, index) => ({
id: `deaultized-x-axis-${index}`,
...axis,
})) ?? []),
// Allows to specify an axis with id=DEFAULT_X_AXIS_KEY
...(xAxis === undefined || xAxis.findIndex(({ id }) => id === DEFAULT_X_AXIS_KEY) === -1
? [{ id: DEFAULT_X_AXIS_KEY, scaleType: 'linear' } as AxisConfig]
Expand All @@ -206,13 +210,17 @@ function CartesianContextProvider(props: CartesianContextProviderProps) {
.paddingInner(categoryGapRatio)
.paddingOuter(categoryGapRatio / 2),
tickNumber: axis.data!.length,
colorScale:
axis.colorMap && getOrdinalColorScale({ values: axis.data, ...axis.colorMap }),
};
}
if (isPointScaleConfig(axis)) {
completedXAxis[axis.id] = {
...axis,
scale: scalePoint(axis.data!, range),
tickNumber: axis.data!.length,
colorScale:
axis.colorMap && getOrdinalColorScale({ values: axis.data, ...axis.colorMap }),
};
}
if (axis.scaleType === 'band' || axis.scaleType === 'point') {
Expand All @@ -234,11 +242,15 @@ function CartesianContextProvider(props: CartesianContextProviderProps) {
scaleType,
scale: niceScale.domain(domain),
tickNumber,
colorScale: axis.colorMap && getColorScale(axis.colorMap),
} as AxisDefaultized<typeof scaleType>;
});

const allYAxis: AxisConfig[] = [
...(yAxis?.map((axis, index) => ({ id: `deaultized-y-axis-${index}`, ...axis })) ?? []),
...(yAxis?.map((axis, index) => ({
id: `deaultized-y-axis-${index}`,
...axis,
})) ?? []),
...(yAxis === undefined || yAxis.findIndex(({ id }) => id === DEFAULT_Y_AXIS_KEY) === -1
? [{ id: DEFAULT_Y_AXIS_KEY, scaleType: 'linear' } as AxisConfig]
: []),
Expand All @@ -262,13 +274,17 @@ function CartesianContextProvider(props: CartesianContextProviderProps) {
.paddingInner(categoryGapRatio)
.paddingOuter(categoryGapRatio / 2),
tickNumber: axis.data!.length,
colorScale:
axis.colorMap && getOrdinalColorScale({ values: axis.data, ...axis.colorMap }),
};
}
if (isPointScaleConfig(axis)) {
completedYAxis[axis.id] = {
...axis,
scale: scalePoint(axis.data!, [range[1], range[0]]),
tickNumber: axis.data!.length,
colorScale:
axis.colorMap && getOrdinalColorScale({ values: axis.data, ...axis.colorMap }),
};
}
if (axis.scaleType === 'band' || axis.scaleType === 'point') {
Expand All @@ -290,6 +306,7 @@ function CartesianContextProvider(props: CartesianContextProviderProps) {
scaleType,
scale: niceScale.domain(domain),
tickNumber,
colorScale: axis.colorMap && getColorScale(axis.colorMap),
} as AxisDefaultized<typeof scaleType>;
});

Expand Down
80 changes: 80 additions & 0 deletions packages/x-charts/src/context/ColorProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import * as React from 'react';
import { useTheme } from '@mui/material/styles';
import barGetColor from '../BarChart/getColor';
import scatterSeriesFormatter from '../ScatterChart/formatter';
import lineSeriesFormatter from '../LineChart/formatter';
import pieSeriesFormatter from '../PieChart/formatter';
import { AllSeriesType, DefaultizedSeriesType } from '../models/seriesType';
import { defaultizeColor } from '../internals/defaultizeColor';
import {
ChartSeriesDefaultized,
ChartSeriesType,
DatasetType,
FormatterParams,
FormatterResult,
} from '../models/seriesType/config';
import { ChartsColorPalette, blueberryTwilightPalette } from '../colorPalettes';
import { SeriesId } from '../models/seriesType/common';
import { CartesianContext } from './CartesianContextProvider';

export type ColorProviderProps = {
children: React.ReactNode;
};

export type ColorGetter = { series: Record<SeriesId, (itemIndex: number) => string> };

export const ColorsContext = React.createContext<ColorGetter>({ series: {} });

if (process.env.NODE_ENV !== 'production') {
ColorsContext.displayName = 'ColorsContext';
}

const seriesColorGetter: {
[type in ChartSeriesType]?: (
series: DefaultizedSeriesType<type>,
xAxis: AxisDefaultized,
yAxis: AxisDefaultized,
) => (itemIndex: number) => string;
} = {
bar: barGetColor,
// scatter: scatterSeriesFormatter,
// line: lineSeriesFormatter,
// pie: pieSeriesFormatter,
};

function ColorProvider(props: ColorProviderProps) {

const series = React.useContext(SeriesContext)[itemData.type]!.series[
itemData.seriesId
] as ChartSeriesDefaultized<T>;

const axisData = React.useContext(CartesianContext);

const { xAxis, yAxis, xAxisIds, yAxisIds } = axisData;
const defaultXAxisId = xAxisIds[0];
const defaultYAxisId = yAxisIds[0];

const color = getColor(
series,
xAxis[series.xAxisKey ?? defaultXAxisId],
yAxis[series.yAxisKey ?? defaultYAxisId],
);

const { series, dataset, colors = blueberryTwilightPalette, children } = props;

const theme = useTheme();

const formattedSeries = React.useMemo(
() =>
formatSeries(
series,
typeof colors === 'function' ? colors(theme.palette.mode) : colors,
dataset as DatasetType<number>,
),
[series, colors, theme.palette.mode, dataset],
);

return <SeriesContext.Provider value={formattedSeries}>{children}</SeriesContext.Provider>;
}

export { ColorProvider };

0 comments on commit 7bf37e8

Please sign in to comment.