From 4a3fedc7d9cb192ecb1f649a21221073e4feb689 Mon Sep 17 00:00:00 2001 From: Jose Quintas Date: Tue, 18 Jun 2024 16:08:55 +0200 Subject: [PATCH 01/16] Divide cartesianContextProvider --- .../src/context/CartesianContextProvider.tsx | 358 ------------------ .../CartesianProvider/CartesianContext.ts | 47 +++ .../CartesianProvider/CartesianProvider.tsx | 66 ++++ .../context/CartesianProvider/computeValue.ts | 184 +++++++++ .../CartesianProvider/getAxisExtremum.ts | 48 +++ .../src/context/CartesianProvider/index.ts | 13 + .../CartesianProvider/normalizeAxis.ts | 29 ++ .../CartesianProvider/useCartesianContext.ts | 7 + 8 files changed, 394 insertions(+), 358 deletions(-) delete mode 100644 packages/x-charts/src/context/CartesianContextProvider.tsx create mode 100644 packages/x-charts/src/context/CartesianProvider/CartesianContext.ts create mode 100644 packages/x-charts/src/context/CartesianProvider/CartesianProvider.tsx create mode 100644 packages/x-charts/src/context/CartesianProvider/computeValue.ts create mode 100644 packages/x-charts/src/context/CartesianProvider/getAxisExtremum.ts create mode 100644 packages/x-charts/src/context/CartesianProvider/index.ts create mode 100644 packages/x-charts/src/context/CartesianProvider/normalizeAxis.ts create mode 100644 packages/x-charts/src/context/CartesianProvider/useCartesianContext.ts diff --git a/packages/x-charts/src/context/CartesianContextProvider.tsx b/packages/x-charts/src/context/CartesianContextProvider.tsx deleted file mode 100644 index 9a97fbd6e807..000000000000 --- a/packages/x-charts/src/context/CartesianContextProvider.tsx +++ /dev/null @@ -1,358 +0,0 @@ -import * as React from 'react'; -import { scaleBand, scalePoint } from 'd3-scale'; -import { - AxisConfig, - AxisDefaultized, - ChartsXAxisProps, - ChartsYAxisProps, - ScaleName, - isBandScaleConfig, - isPointScaleConfig, -} from '../models/axis'; -import { getScale } from '../internals/getScale'; -import { DEFAULT_X_AXIS_KEY, DEFAULT_Y_AXIS_KEY } from '../constants'; -import { - CartesianChartSeriesType, - ChartSeriesType, - ChartSeries, - DatasetType, - ExtremumGetter, - ExtremumGetterResult, -} from '../models/seriesType/config'; -import { MakeOptional } from '../models/helpers'; -import { getTickNumber } from '../hooks/useTicks'; -import { useDrawingArea } from '../hooks/useDrawingArea'; -import { SeriesId } from '../models/seriesType/common'; -import { getColorScale, getOrdinalColorScale } from '../internals/colorScale'; -import { useSeries } from '../hooks/useSeries'; - -export type ExtremumGettersConfig = { - [K in T]?: ExtremumGetter; -}; - -export type CartesianContextProviderProps = { - /** - * The configuration of the x-axes. - * If not provided, a default axis config is used. - * An array of [[AxisConfig]] objects. - */ - xAxis?: MakeOptional, 'id'>[]; - /** - * The configuration of the y-axes. - * If not provided, a default axis config is used. - * An array of [[AxisConfig]] objects. - */ - yAxis?: MakeOptional, 'id'>[]; - /** - * An array of objects that can be used to populate series and axes data using their `dataKey` property. - */ - dataset?: DatasetType; - /** - * An object with x-axis extremum getters per series type. - */ - xExtremumGetters: ExtremumGettersConfig; - /** - * An object with y-axis extremum getters per series type. - */ - yExtremumGetters: ExtremumGettersConfig; - children: React.ReactNode; -}; - -const DEFAULT_CATEGORY_GAP_RATIO = 0.2; -const DEFAULT_BAR_GAP_RATIO = 0.1; - -type DefaultizedAxisConfig = { - [axisKey: string]: AxisDefaultized; -}; - -export const CartesianContext = React.createContext<{ - /** - * Mapping from x-axis key to scaling configuration. - */ - xAxis: { - DEFAULT_X_AXIS_KEY: AxisDefaultized; - } & DefaultizedAxisConfig; - /** - * Mapping from y-axis key to scaling configuration. - */ - yAxis: { - DEFAULT_X_AXIS_KEY: AxisDefaultized; - } & DefaultizedAxisConfig; - /** - * The x-axes IDs sorted by order they got provided. - */ - xAxisIds: string[]; - /** - * The y-axes IDs sorted by order they got provided. - */ - yAxisIds: string[]; - // @ts-ignore -}>({ xAxis: {}, yAxis: {}, xAxisIds: [], yAxisIds: [] }); - -if (process.env.NODE_ENV !== 'production') { - CartesianContext.displayName = 'CartesianContext'; -} - -function CartesianContextProvider(props: CartesianContextProviderProps) { - const { - xAxis: inXAxis, - yAxis: inYAxis, - dataset, - xExtremumGetters, - yExtremumGetters, - children, - } = props; - const formattedSeries = useSeries(); - const drawingArea = useDrawingArea(); - - const xAxis = React.useMemo( - () => - inXAxis?.map((axisConfig) => { - const dataKey = axisConfig.dataKey; - if (dataKey === undefined || axisConfig.data !== undefined) { - return axisConfig; - } - if (dataset === undefined) { - throw Error('MUI X Charts: x-axis uses `dataKey` but no `dataset` is provided.'); - } - return { - ...axisConfig, - data: dataset.map((d) => d[dataKey]), - }; - }), - [inXAxis, dataset], - ); - - const yAxis = React.useMemo( - () => - inYAxis?.map((axisConfig) => { - const dataKey = axisConfig.dataKey; - if (dataKey === undefined || axisConfig.data !== undefined) { - return axisConfig; - } - if (dataset === undefined) { - throw Error('MUI X Charts: y-axis uses `dataKey` but no `dataset` is provided.'); - } - return { - ...axisConfig, - data: dataset.map((d) => d[dataKey]), - }; - }), - [inYAxis, dataset], - ); - - const value = React.useMemo(() => { - const axisExtremumCallback = ( - acc: ExtremumGetterResult, - chartType: T, - axis: AxisConfig, - getters: { [T2 in CartesianChartSeriesType]?: ExtremumGetter }, - isDefaultAxis: boolean, - ): ExtremumGetterResult => { - const getter = getters[chartType]; - const series = (formattedSeries[chartType]?.series as Record>) ?? {}; - - const [minChartTypeData, maxChartTypeData] = getter?.({ - series, - axis, - isDefaultAxis, - }) ?? [null, null]; - - const [minData, maxData] = acc; - - if (minData === null || maxData === null) { - return [minChartTypeData!, maxChartTypeData!]; - } - - if (minChartTypeData === null || maxChartTypeData === null) { - return [minData, maxData]; - } - - return [Math.min(minChartTypeData, minData), Math.max(maxChartTypeData, maxData)]; - }; - - const getAxisExtremum = ( - axis: AxisConfig, - getters: { [T in CartesianChartSeriesType]?: ExtremumGetter }, - isDefaultAxis: boolean, - ) => { - const charTypes = Object.keys(getters) as CartesianChartSeriesType[]; - - return charTypes.reduce( - (acc, charType) => axisExtremumCallback(acc, charType, axis, getters, isDefaultAxis), - [null, null] as ExtremumGetterResult, - ); - }; - - const allXAxis = [ - ...(xAxis?.map((axis, index) => ({ id: `defaultized-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< - ScaleName, - any, - ChartsXAxisProps - >, - ] - : []), - ]; - - const completedXAxis: DefaultizedAxisConfig = {}; - allXAxis.forEach((axis, axisIndex) => { - const isDefaultAxis = axisIndex === 0; - const [minData, maxData] = getAxisExtremum(axis, xExtremumGetters, isDefaultAxis); - - const range = axis.reverse - ? [drawingArea.left + drawingArea.width, drawingArea.left] - : [drawingArea.left, drawingArea.left + drawingArea.width]; - - if (isBandScaleConfig(axis)) { - const categoryGapRatio = axis.categoryGapRatio ?? DEFAULT_CATEGORY_GAP_RATIO; - const barGapRatio = axis.barGapRatio ?? DEFAULT_BAR_GAP_RATIO; - completedXAxis[axis.id] = { - categoryGapRatio, - barGapRatio, - ...axis, - scale: scaleBand(axis.data!, range) - .paddingInner(categoryGapRatio) - .paddingOuter(categoryGapRatio / 2), - tickNumber: axis.data!.length, - colorScale: - axis.colorMap && - (axis.colorMap.type === 'ordinal' - ? getOrdinalColorScale({ values: axis.data, ...axis.colorMap }) - : getColorScale(axis.colorMap)), - }; - } - if (isPointScaleConfig(axis)) { - completedXAxis[axis.id] = { - ...axis, - scale: scalePoint(axis.data!, range), - tickNumber: axis.data!.length, - colorScale: - axis.colorMap && - (axis.colorMap.type === 'ordinal' - ? getOrdinalColorScale({ values: axis.data, ...axis.colorMap }) - : getColorScale(axis.colorMap)), - }; - } - if (axis.scaleType === 'band' || axis.scaleType === 'point') { - // Could be merged with the two previous "if conditions" but then TS does not get that `axis.scaleType` can't be `band` or `point`. - return; - } - - const scaleType = axis.scaleType ?? 'linear'; - - const extremums = [axis.min ?? minData, axis.max ?? maxData]; - const tickNumber = getTickNumber({ ...axis, range, domain: extremums }); - - const niceScale = getScale(scaleType, extremums, range).nice(tickNumber); - const niceDomain = niceScale.domain(); - const domain = [axis.min ?? niceDomain[0], axis.max ?? niceDomain[1]]; - - completedXAxis[axis.id] = { - ...axis, - scaleType, - scale: niceScale.domain(domain), - tickNumber, - colorScale: axis.colorMap && getColorScale(axis.colorMap), - } as AxisDefaultized; - }); - - const allYAxis = [ - ...(yAxis?.map((axis, index) => ({ id: `defaultized-y-axis-${index}`, ...axis })) ?? []), - ...(yAxis === undefined || yAxis.findIndex(({ id }) => id === DEFAULT_Y_AXIS_KEY) === -1 - ? [ - { id: DEFAULT_Y_AXIS_KEY, scaleType: 'linear' } as AxisConfig< - ScaleName, - any, - ChartsYAxisProps - >, - ] - : []), - ]; - - const completedYAxis: DefaultizedAxisConfig = {}; - allYAxis.forEach((axis, axisIndex) => { - const isDefaultAxis = axisIndex === 0; - const [minData, maxData] = getAxisExtremum(axis, yExtremumGetters, isDefaultAxis); - const range = axis.reverse - ? [drawingArea.top, drawingArea.top + drawingArea.height] - : [drawingArea.top + drawingArea.height, drawingArea.top]; - - if (isBandScaleConfig(axis)) { - const categoryGapRatio = axis.categoryGapRatio ?? DEFAULT_CATEGORY_GAP_RATIO; - completedYAxis[axis.id] = { - categoryGapRatio, - barGapRatio: 0, - ...axis, - scale: scaleBand(axis.data!, [range[1], range[0]]) - .paddingInner(categoryGapRatio) - .paddingOuter(categoryGapRatio / 2), - tickNumber: axis.data!.length, - colorScale: - axis.colorMap && - (axis.colorMap.type === 'ordinal' - ? getOrdinalColorScale({ values: axis.data, ...axis.colorMap }) - : getColorScale(axis.colorMap)), - }; - } - if (isPointScaleConfig(axis)) { - completedYAxis[axis.id] = { - ...axis, - scale: scalePoint(axis.data!, [range[1], range[0]]), - tickNumber: axis.data!.length, - colorScale: - axis.colorMap && - (axis.colorMap.type === 'ordinal' - ? getOrdinalColorScale({ values: axis.data, ...axis.colorMap }) - : getColorScale(axis.colorMap)), - }; - } - if (axis.scaleType === 'band' || axis.scaleType === 'point') { - // Could be merged with the two previous "if conditions" but then TS does not get that `axis.scaleType` can't be `band` or `point`. - return; - } - - const scaleType = axis.scaleType ?? 'linear'; - - const extremums = [axis.min ?? minData, axis.max ?? maxData]; - const tickNumber = getTickNumber({ ...axis, range, domain: extremums }); - - const niceScale = getScale(scaleType, extremums, range).nice(tickNumber); - const niceDomain = niceScale.domain(); - const domain = [axis.min ?? niceDomain[0], axis.max ?? niceDomain[1]]; - - completedYAxis[axis.id] = { - ...axis, - scaleType, - scale: niceScale.domain(domain), - tickNumber, - colorScale: axis.colorMap && getColorScale(axis.colorMap), - } as AxisDefaultized; - }); - - return { - xAxis: completedXAxis, - yAxis: completedYAxis, - xAxisIds: allXAxis.map(({ id }) => id), - yAxisIds: allYAxis.map(({ id }) => id), - }; - }, [ - drawingArea.height, - drawingArea.left, - drawingArea.top, - drawingArea.width, - formattedSeries, - xAxis, - xExtremumGetters, - yAxis, - yExtremumGetters, - ]); - - // @ts-ignore - return {children}; -} - -export { CartesianContextProvider }; diff --git a/packages/x-charts/src/context/CartesianProvider/CartesianContext.ts b/packages/x-charts/src/context/CartesianProvider/CartesianContext.ts new file mode 100644 index 000000000000..bbc47f2b534b --- /dev/null +++ b/packages/x-charts/src/context/CartesianProvider/CartesianContext.ts @@ -0,0 +1,47 @@ +import * as React from 'react'; + +import { Initializable } from '../context.types'; +import { + AxisDefaultized, + ScaleName, + ChartsXAxisProps, + ChartsYAxisProps, + AxisId, +} from '../../models/axis'; + +export type DefaultizedAxisConfig = { + [axisKey: string]: AxisDefaultized; +}; + +export type CartesianContextState = { + /** + * Mapping from x-axis key to scaling configuration. + */ + xAxis: DefaultizedAxisConfig; + /** + * Mapping from y-axis key to scaling configuration. + */ + yAxis: DefaultizedAxisConfig; + /** + * The x-axes IDs sorted by order they got provided. + */ + xAxisIds: AxisId[]; + /** + * The y-axes IDs sorted by order they got provided. + */ + yAxisIds: AxisId[]; +}; + +export const CartesianContext = React.createContext>({ + isInitialized: false, + data: { + xAxis: {}, + yAxis: {}, + xAxisIds: [], + yAxisIds: [], + }, +}); + +if (process.env.NODE_ENV !== 'production') { + CartesianContext.displayName = 'CartesianContext'; +} diff --git a/packages/x-charts/src/context/CartesianProvider/CartesianProvider.tsx b/packages/x-charts/src/context/CartesianProvider/CartesianProvider.tsx new file mode 100644 index 000000000000..d004c2cd802a --- /dev/null +++ b/packages/x-charts/src/context/CartesianProvider/CartesianProvider.tsx @@ -0,0 +1,66 @@ +import * as React from 'react'; +import { AxisConfig, ChartsXAxisProps, ChartsYAxisProps, ScaleName } from '../../models/axis'; +import { DatasetType } from '../../models/seriesType/config'; +import { MakeOptional } from '../../models/helpers'; +import { useDrawingArea } from '../../hooks/useDrawingArea'; +import { useSeries } from '../../hooks/useSeries'; +import { CartesianContext } from './CartesianContext'; +import { normalizeAxis } from './normalizeAxis'; +import { computeValue } from './computeValue'; +import { ExtremumGettersConfig } from '../../models'; + +export type CartesianContextProviderProps = { + /** + * The configuration of the x-axes. + * If not provided, a default axis config is used. + * An array of [[AxisConfig]] objects. + */ + xAxis?: MakeOptional, 'id'>[]; + /** + * The configuration of the y-axes. + * If not provided, a default axis config is used. + * An array of [[AxisConfig]] objects. + */ + yAxis?: MakeOptional, 'id'>[]; + /** + * An array of objects that can be used to populate series and axes data using their `dataKey` property. + */ + dataset?: DatasetType; + /** + * An object with x-axis extremum getters per series type. + */ + xExtremumGetters: ExtremumGettersConfig; + /** + * An object with y-axis extremum getters per series type. + */ + yExtremumGetters: ExtremumGettersConfig; + children: React.ReactNode; +}; + +function CartesianContextProvider(props: CartesianContextProviderProps) { + const { + xAxis: inXAxis, + yAxis: inYAxis, + dataset, + xExtremumGetters, + yExtremumGetters, + children, + } = props; + + const formattedSeries = useSeries(); + const drawingArea = useDrawingArea(); + + const xAxis = React.useMemo(() => normalizeAxis(inXAxis, dataset, 'x'), [inXAxis, dataset]); + + const yAxis = React.useMemo(() => normalizeAxis(inYAxis, dataset, 'y'), [inYAxis, dataset]); + + const value = React.useMemo( + () => + computeValue(drawingArea, formattedSeries, xAxis, yAxis, xExtremumGetters, yExtremumGetters), + [drawingArea, formattedSeries, xAxis, xExtremumGetters, yAxis, yExtremumGetters], + ); + + return {children}; +} + +export { CartesianContextProvider }; diff --git a/packages/x-charts/src/context/CartesianProvider/computeValue.ts b/packages/x-charts/src/context/CartesianProvider/computeValue.ts new file mode 100644 index 000000000000..7f54176f0fff --- /dev/null +++ b/packages/x-charts/src/context/CartesianProvider/computeValue.ts @@ -0,0 +1,184 @@ +import { scaleBand, scalePoint } from 'd3-scale'; +import { DEFAULT_X_AXIS_KEY, DEFAULT_Y_AXIS_KEY } from '../../constants'; +import { AxisConfig, ChartsXAxisProps, ScaleName } from '../../models'; +import { ChartsYAxisProps, isBandScaleConfig, isPointScaleConfig } from '../../models/axis'; +import { CartesianChartSeriesType, ExtremumGetter } from '../../models/seriesType/config'; +import { DefaultizedAxisConfig } from './CartesianContext'; +import { getColorScale, getOrdinalColorScale } from '../../internals/colorScale'; +import { getTickNumber } from '../../hooks/useTicks'; +import { getScale } from '../../internals/getScale'; +import { DrawingArea } from '../DrawingProvider'; +import { FormattedSeries } from '../SeriesContextProvider'; +import { MakeOptional } from '../../models/helpers'; +import { getAxisExtremum } from './getAxisExtremum'; + +const DEFAULT_CATEGORY_GAP_RATIO = 0.2; +const DEFAULT_BAR_GAP_RATIO = 0.1; + +export const computeValue = ( + drawingArea: DrawingArea, + formattedSeries: FormattedSeries, + xAxis: MakeOptional, 'id'>[] | undefined, + yAxis: MakeOptional, 'id'>[] | undefined, + xExtremumGetters: { [K in CartesianChartSeriesType]?: ExtremumGetter }, + yExtremumGetters: { [K in CartesianChartSeriesType]?: ExtremumGetter }, +) => { + const allXAxis: AxisConfig[] = [ + ...(xAxis?.map((axis, index) => ({ id: `defaultized-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 const }] + : []), + ]; + + const completedXAxis: DefaultizedAxisConfig = {}; + allXAxis.forEach((axis, axisIndex) => { + const isDefaultAxis = axisIndex === 0; + const [minData, maxData] = getAxisExtremum( + axis, + xExtremumGetters, + isDefaultAxis, + formattedSeries, + ); + + const range = axis.reverse + ? [drawingArea.left + drawingArea.width, drawingArea.left] + : [drawingArea.left, drawingArea.left + drawingArea.width]; + + if (isBandScaleConfig(axis)) { + const categoryGapRatio = axis.categoryGapRatio ?? DEFAULT_CATEGORY_GAP_RATIO; + const barGapRatio = axis.barGapRatio ?? DEFAULT_BAR_GAP_RATIO; + completedXAxis[axis.id] = { + categoryGapRatio, + barGapRatio, + ...axis, + scale: scaleBand(axis.data!, range) + .paddingInner(categoryGapRatio) + .paddingOuter(categoryGapRatio / 2), + tickNumber: axis.data!.length, + colorScale: + axis.colorMap && + (axis.colorMap.type === 'ordinal' + ? getOrdinalColorScale({ values: axis.data, ...axis.colorMap }) + : getColorScale(axis.colorMap)), + }; + } + if (isPointScaleConfig(axis)) { + completedXAxis[axis.id] = { + ...axis, + scale: scalePoint(axis.data!, range), + tickNumber: axis.data!.length, + colorScale: + axis.colorMap && + (axis.colorMap.type === 'ordinal' + ? getOrdinalColorScale({ values: axis.data, ...axis.colorMap }) + : getColorScale(axis.colorMap)), + }; + } + if (axis.scaleType === 'band' || axis.scaleType === 'point') { + // Could be merged with the two previous "if conditions" but then TS does not get that `axis.scaleType` can't be `band` or `point`. + return; + } + + const scaleType = axis.scaleType ?? ('linear' as const); + + const extremums = [axis.min ?? minData, axis.max ?? maxData]; + const tickNumber = getTickNumber({ ...axis, range, domain: extremums }); + + // Gotta remove ".nice" here to avoid jittering in zoom + const scale = getScale(scaleType, extremums, range); + const [minDomain, maxDomain] = scale.domain(); + const domain = [axis.min ?? minDomain, axis.max ?? maxDomain]; + + completedXAxis[axis.id] = { + ...axis, + scaleType: scaleType as any, + scale: scale.domain(domain) as any, + tickNumber, + colorScale: axis.colorMap && getColorScale(axis.colorMap), + }; + }); + + const allYAxis: AxisConfig[] = [ + ...(yAxis?.map((axis, index) => ({ id: `defaultized-y-axis-${index}`, ...axis })) ?? []), + ...(yAxis === undefined || yAxis.findIndex(({ id }) => id === DEFAULT_Y_AXIS_KEY) === -1 + ? [{ id: DEFAULT_Y_AXIS_KEY, scaleType: 'linear' as const }] + : []), + ]; + + const completedYAxis: DefaultizedAxisConfig = {}; + allYAxis.forEach((axis, axisIndex) => { + const isDefaultAxis = axisIndex === 0; + const [minData, maxData] = getAxisExtremum( + axis, + yExtremumGetters, + isDefaultAxis, + formattedSeries, + ); + const range = axis.reverse + ? [drawingArea.top, drawingArea.top + drawingArea.height] + : [drawingArea.top + drawingArea.height, drawingArea.top]; + + if (isBandScaleConfig(axis)) { + const categoryGapRatio = axis.categoryGapRatio ?? DEFAULT_CATEGORY_GAP_RATIO; + completedYAxis[axis.id] = { + categoryGapRatio, + barGapRatio: 0, + ...axis, + scale: scaleBand(axis.data!, [range[1], range[0]]) + .paddingInner(categoryGapRatio) + .paddingOuter(categoryGapRatio / 2), + tickNumber: axis.data!.length, + colorScale: + axis.colorMap && + (axis.colorMap.type === 'ordinal' + ? getOrdinalColorScale({ values: axis.data, ...axis.colorMap }) + : getColorScale(axis.colorMap)), + }; + } + if (isPointScaleConfig(axis)) { + completedYAxis[axis.id] = { + ...axis, + scale: scalePoint(axis.data!, [range[1], range[0]]), + tickNumber: axis.data!.length, + colorScale: + axis.colorMap && + (axis.colorMap.type === 'ordinal' + ? getOrdinalColorScale({ values: axis.data, ...axis.colorMap }) + : getColorScale(axis.colorMap)), + }; + } + if (axis.scaleType === 'band' || axis.scaleType === 'point') { + // Could be merged with the two previous "if conditions" but then TS does not get that `axis.scaleType` can't be `band` or `point`. + return; + } + + const scaleType = axis.scaleType ?? ('linear' as const); + + const extremums = [axis.min ?? minData, axis.max ?? maxData]; + const tickNumber = getTickNumber({ ...axis, range, domain: extremums }); + + // Gotta remove ".nice" here to avoid jittering in zoom + const scale = getScale(scaleType, extremums, range); + const [minDomain, maxDomain] = scale.domain(); + const domain = [axis.min ?? minDomain, axis.max ?? maxDomain]; + + completedYAxis[axis.id] = { + ...axis, + scaleType: scaleType as any, + scale: scale.domain(domain) as any, + tickNumber, + colorScale: axis.colorMap && getColorScale(axis.colorMap), + }; + }); + + return { + isInitialized: true, + data: { + xAxis: completedXAxis, + yAxis: completedYAxis, + xAxisIds: allXAxis.map(({ id }) => id), + yAxisIds: allYAxis.map(({ id }) => id), + }, + }; +}; diff --git a/packages/x-charts/src/context/CartesianProvider/getAxisExtremum.ts b/packages/x-charts/src/context/CartesianProvider/getAxisExtremum.ts new file mode 100644 index 000000000000..43a16a657cd4 --- /dev/null +++ b/packages/x-charts/src/context/CartesianProvider/getAxisExtremum.ts @@ -0,0 +1,48 @@ +import { AxisConfig, ExtremumGettersConfig } from '../../models'; +import { CartesianChartSeriesType, ExtremumGetterResult } from '../../models/seriesType/config'; +import { FormattedSeries } from '../SeriesContextProvider'; + +const axisExtremumCallback = ( + acc: ExtremumGetterResult, + chartType: T, + axis: AxisConfig, + getters: ExtremumGettersConfig, + isDefaultAxis: boolean, + formattedSeries: FormattedSeries, +): ExtremumGetterResult => { + const getter = getters[chartType]; + const series = formattedSeries[chartType]?.series ?? {}; + + const [minChartTypeData, maxChartTypeData] = getter?.({ + series, + axis, + isDefaultAxis, + }) ?? [null, null]; + + const [minData, maxData] = acc; + + if (minData === null || maxData === null) { + return [minChartTypeData!, maxChartTypeData!]; + } + + if (minChartTypeData === null || maxChartTypeData === null) { + return [minData, maxData]; + } + + return [Math.min(minChartTypeData, minData), Math.max(maxChartTypeData, maxData)]; +}; + +export const getAxisExtremum = ( + axis: AxisConfig, + getters: ExtremumGettersConfig, + isDefaultAxis: boolean, + formattedSeries: FormattedSeries, +) => { + const charTypes = Object.keys(getters) as CartesianChartSeriesType[]; + + return charTypes.reduce( + (acc, charType) => + axisExtremumCallback(acc, charType, axis, getters, isDefaultAxis, formattedSeries), + [null, null], + ); +}; diff --git a/packages/x-charts/src/context/CartesianProvider/index.ts b/packages/x-charts/src/context/CartesianProvider/index.ts new file mode 100644 index 000000000000..b9f4f07059dc --- /dev/null +++ b/packages/x-charts/src/context/CartesianProvider/index.ts @@ -0,0 +1,13 @@ +import { computeValue } from './computeValue'; +import { normalizeAxis } from './normalizeAxis'; + +export * from './CartesianProvider'; +export * from './CartesianContext'; +export * from './useCartesianContext'; + +const cartesianProviderUtils = { + computeValue, + normalizeAxis, +}; + +export { cartesianProviderUtils }; diff --git a/packages/x-charts/src/context/CartesianProvider/normalizeAxis.ts b/packages/x-charts/src/context/CartesianProvider/normalizeAxis.ts new file mode 100644 index 000000000000..df9d2193c265 --- /dev/null +++ b/packages/x-charts/src/context/CartesianProvider/normalizeAxis.ts @@ -0,0 +1,29 @@ +import type { AxisConfig, ScaleName } from '../../models'; +import { ChartsAxisProps } from '../../models/axis'; +import { MakeOptional } from '../../models/helpers'; +import { DatasetType } from '../../models/seriesType/config'; + +export const normalizeAxis = < + T extends ChartsAxisProps, + R extends MakeOptional, 'id'>, +>( + axis: R[] | undefined, + dataset: DatasetType | undefined, + axisName: 'x' | 'y', +): R[] | undefined => { + return axis?.map((axisConfig) => { + const dataKey = axisConfig.dataKey; + if (dataKey === undefined || axisConfig.data !== undefined) { + return axisConfig; + } + if (dataset === undefined) { + throw Error( + `MUI ${axisName.toUpperCase()} Charts: ${axisName}-axis uses \`dataKey\` but no \`dataset\` is provided.`, + ); + } + return { + ...axisConfig, + data: dataset.map((d) => d[dataKey]), + }; + }); +}; diff --git a/packages/x-charts/src/context/CartesianProvider/useCartesianContext.ts b/packages/x-charts/src/context/CartesianProvider/useCartesianContext.ts new file mode 100644 index 000000000000..ee5376958745 --- /dev/null +++ b/packages/x-charts/src/context/CartesianProvider/useCartesianContext.ts @@ -0,0 +1,7 @@ +import * as React from 'react'; +import { CartesianContext, CartesianContextState } from './CartesianContext'; + +export const useCartesianContext = (): CartesianContextState => { + const { data } = React.useContext(CartesianContext); + return data; +}; From 765499e6f89ef899be2c4409e2bde1a6f3dd3ab9 Mon Sep 17 00:00:00 2001 From: Jose Quintas Date: Tue, 18 Jun 2024 16:13:45 +0200 Subject: [PATCH 02/16] Use new useCartesianContext hook --- packages/x-charts/src/BarChart/BarPlot.tsx | 4 ++-- packages/x-charts/src/BarChart/checkScaleErrors.ts | 12 ++++++------ .../x-charts/src/ChartContainer/usePluginsMerge.ts | 3 +-- packages/x-charts/src/ChartsAxis/ChartsAxis.tsx | 10 +++++----- .../src/ChartsAxisHighlight/ChartsAxisHighlight.tsx | 4 ++-- packages/x-charts/src/ChartsGrid/ChartsGrid.tsx | 4 ++-- .../ChartsOnAxisClickHandler.tsx | 4 ++-- .../src/ChartsTooltip/ChartsAxisTooltipContent.tsx | 4 ++-- .../src/ChartsTooltip/ChartsItemTooltipContent.tsx | 4 ++-- .../ChartsVoronoiHandler/ChartsVoronoiHandler.tsx | 4 ++-- packages/x-charts/src/ChartsXAxis/ChartsXAxis.tsx | 4 ++-- packages/x-charts/src/ChartsYAxis/ChartsYAxis.tsx | 4 ++-- packages/x-charts/src/LineChart/AreaPlot.tsx | 7 ++++--- .../x-charts/src/LineChart/LineHighlightPlot.tsx | 4 ++-- packages/x-charts/src/LineChart/LinePlot.tsx | 7 ++++--- packages/x-charts/src/LineChart/MarkPlot.tsx | 4 ++-- packages/x-charts/src/ScatterChart/ScatterPlot.tsx | 4 ++-- packages/x-charts/src/hooks/useAxisEvents.ts | 6 +++--- packages/x-charts/src/hooks/useColorScale.ts | 6 +++--- packages/x-charts/src/hooks/useDrawingArea.ts | 4 ++-- packages/x-charts/src/hooks/useScale.ts | 7 +++---- packages/x-charts/src/models/plugin.ts | 11 ++++++++++- 22 files changed, 65 insertions(+), 56 deletions(-) diff --git a/packages/x-charts/src/BarChart/BarPlot.tsx b/packages/x-charts/src/BarChart/BarPlot.tsx index 7ff1e8999a57..bab8699fe686 100644 --- a/packages/x-charts/src/BarChart/BarPlot.tsx +++ b/packages/x-charts/src/BarChart/BarPlot.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import PropTypes from 'prop-types'; import { useTransition } from '@react-spring/web'; -import { CartesianContext } from '../context/CartesianContextProvider'; +import { useCartesianContext } from '../context/CartesianProvider'; import { BarElement, BarElementSlotProps, BarElementSlots } from './BarElement'; import { AxisDefaultized } from '../models/axis'; import { FormatterResult } from '../models/seriesType/config'; @@ -89,7 +89,7 @@ const useAggregatedData = (): { const seriesData = useBarSeries() ?? ({ series: {}, stackingGroups: [], seriesOrder: [] } as FormatterResult<'bar'>); - const axisData = React.useContext(CartesianContext); + const axisData = useCartesianContext(); const chartId = useChartId(); const { series, stackingGroups } = seriesData; diff --git a/packages/x-charts/src/BarChart/checkScaleErrors.ts b/packages/x-charts/src/BarChart/checkScaleErrors.ts index e3b7b0803e96..cbd3137b11d5 100644 --- a/packages/x-charts/src/BarChart/checkScaleErrors.ts +++ b/packages/x-charts/src/BarChart/checkScaleErrors.ts @@ -1,8 +1,8 @@ import { DEFAULT_X_AXIS_KEY, DEFAULT_Y_AXIS_KEY } from '../constants'; -import { AxisDefaultized, isBandScaleConfig, isPointScaleConfig } from '../models/axis'; +import { AxisDefaultized, AxisId, isBandScaleConfig, isPointScaleConfig } from '../models/axis'; import { SeriesId } from '../models/seriesType/common'; -const getAxisMessage = (axisDirection: 'x' | 'y', axisKey: string) => { +const getAxisMessage = (axisDirection: 'x' | 'y', axisKey: AxisId) => { const axisName = `${axisDirection}-axis`; const axisKeyName = `${axisDirection}Axis`; const axisDefaultKey = axisDirection === 'x' ? DEFAULT_X_AXIS_KEY : DEFAULT_Y_AXIS_KEY; @@ -14,10 +14,10 @@ const getAxisMessage = (axisDirection: 'x' | 'y', axisKey: string) => { export function checkScaleErrors( verticalLayout: boolean, seriesId: SeriesId, - xAxisKey: string, - xAxis: { DEFAULT_X_AXIS_KEY: AxisDefaultized } & { [axisKey: string]: AxisDefaultized }, - yAxisKey: string, - yAxis: { DEFAULT_X_AXIS_KEY: AxisDefaultized } & { [axisKey: string]: AxisDefaultized }, + xAxisKey: AxisId, + xAxis: { [axisKey: AxisId]: AxisDefaultized }, + yAxisKey: AxisId, + yAxis: { [axisKey: AxisId]: AxisDefaultized }, ): void { const xAxisConfig = xAxis[xAxisKey]; const yAxisConfig = yAxis[yAxisKey]; diff --git a/packages/x-charts/src/ChartContainer/usePluginsMerge.ts b/packages/x-charts/src/ChartContainer/usePluginsMerge.ts index ee1043e918f9..b269bf79adfb 100644 --- a/packages/x-charts/src/ChartContainer/usePluginsMerge.ts +++ b/packages/x-charts/src/ChartContainer/usePluginsMerge.ts @@ -1,7 +1,6 @@ import * as React from 'react'; -import { ChartsPluginType, ColorProcessorsConfig } from '../models'; +import { ChartsPluginType, ColorProcessorsConfig, ExtremumGettersConfig } from '../models'; import { ChartSeriesType } from '../models/seriesType/config'; -import { ExtremumGettersConfig } from '../context/CartesianContextProvider'; import { SeriesFormatterConfig } from '../context/SeriesContextProvider'; import { defaultPlugins } from './defaultPlugins'; diff --git a/packages/x-charts/src/ChartsAxis/ChartsAxis.tsx b/packages/x-charts/src/ChartsAxis/ChartsAxis.tsx index db42c36c7cb7..4212e2950954 100644 --- a/packages/x-charts/src/ChartsAxis/ChartsAxis.tsx +++ b/packages/x-charts/src/ChartsAxis/ChartsAxis.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import PropTypes from 'prop-types'; -import { CartesianContext } from '../context/CartesianContextProvider'; +import { useCartesianContext } from '../context/CartesianProvider'; import { ChartsXAxis } from '../ChartsXAxis'; import { ChartsYAxis } from '../ChartsYAxis'; import { @@ -49,8 +49,8 @@ export interface ChartsAxisProps { } const getAxisId = ( - propsValue: undefined | null | string | ChartsXAxisProps | ChartsYAxisProps, - defaultAxisId?: string, + propsValue: undefined | null | AxisId | ChartsXAxisProps | ChartsYAxisProps, + defaultAxisId?: AxisId, ): AxisId | null => { if (propsValue == null) { return null; @@ -62,7 +62,7 @@ const getAxisId = ( }; const mergeProps = ( - axisConfig: undefined | null | string | ChartsXAxisProps | ChartsYAxisProps, + axisConfig: undefined | null | AxisId | ChartsXAxisProps | ChartsYAxisProps, slots?: Partial, slotProps?: Partial, ) => { @@ -86,7 +86,7 @@ const mergeProps = ( */ function ChartsAxis(props: ChartsAxisProps) { const { topAxis, leftAxis, rightAxis, bottomAxis, slots, slotProps } = props; - const { xAxis, xAxisIds, yAxis, yAxisIds } = React.useContext(CartesianContext); + const { xAxis, xAxisIds, yAxis, yAxisIds } = useCartesianContext(); // TODO: use for plotting line without ticks or any thing // const drawingArea = React.useContext(DrawingContext); diff --git a/packages/x-charts/src/ChartsAxisHighlight/ChartsAxisHighlight.tsx b/packages/x-charts/src/ChartsAxisHighlight/ChartsAxisHighlight.tsx index d72082da7b8d..6485c9c3f4d6 100644 --- a/packages/x-charts/src/ChartsAxisHighlight/ChartsAxisHighlight.tsx +++ b/packages/x-charts/src/ChartsAxisHighlight/ChartsAxisHighlight.tsx @@ -5,7 +5,7 @@ import generateUtilityClass from '@mui/utils/generateUtilityClass'; import generateUtilityClasses from '@mui/utils/generateUtilityClasses'; import { styled } from '@mui/material/styles'; import { InteractionContext } from '../context/InteractionProvider'; -import { CartesianContext } from '../context/CartesianContextProvider'; +import { useCartesianContext } from '../context/CartesianProvider'; import { getValueToPositionMapper } from '../hooks/useScale'; import { isBandScale } from '../internals/isBandScale'; @@ -67,7 +67,7 @@ export type ChartsAxisHighlightProps = { */ function ChartsAxisHighlight(props: ChartsAxisHighlightProps) { const { x: xAxisHighlight, y: yAxisHighlight } = props; - const { xAxisIds, xAxis, yAxisIds, yAxis } = React.useContext(CartesianContext); + const { xAxisIds, xAxis, yAxisIds, yAxis } = useCartesianContext(); const classes = useUtilityClasses(); const USED_X_AXIS_ID = xAxisIds[0]; diff --git a/packages/x-charts/src/ChartsGrid/ChartsGrid.tsx b/packages/x-charts/src/ChartsGrid/ChartsGrid.tsx index 01b2539a4ce9..c150244b2d39 100644 --- a/packages/x-charts/src/ChartsGrid/ChartsGrid.tsx +++ b/packages/x-charts/src/ChartsGrid/ChartsGrid.tsx @@ -3,7 +3,7 @@ import PropTypes from 'prop-types'; import composeClasses from '@mui/utils/composeClasses'; import { styled, useThemeProps } from '@mui/material/styles'; -import { CartesianContext } from '../context/CartesianContextProvider'; +import { useCartesianContext } from '../context/CartesianProvider'; import { useTicks } from '../hooks/useTicks'; import { ChartsGridClasses, @@ -69,7 +69,7 @@ function ChartsGrid(props: ChartsGridProps) { const themeProps = useThemeProps({ props, name: 'MuiChartsGrid' }); const { vertical, horizontal, ...other } = themeProps; - const { xAxis, xAxisIds, yAxis, yAxisIds } = React.useContext(CartesianContext); + const { xAxis, xAxisIds, yAxis, yAxisIds } = useCartesianContext(); const classes = useUtilityClasses(themeProps); diff --git a/packages/x-charts/src/ChartsOnAxisClickHandler/ChartsOnAxisClickHandler.tsx b/packages/x-charts/src/ChartsOnAxisClickHandler/ChartsOnAxisClickHandler.tsx index f134b0b2f599..8710c72762a0 100644 --- a/packages/x-charts/src/ChartsOnAxisClickHandler/ChartsOnAxisClickHandler.tsx +++ b/packages/x-charts/src/ChartsOnAxisClickHandler/ChartsOnAxisClickHandler.tsx @@ -1,9 +1,9 @@ import * as React from 'react'; import PropTypes from 'prop-types'; import { InteractionContext } from '../context/InteractionProvider'; -import { CartesianContext } from '../context/CartesianContextProvider'; import { useSeries } from '../hooks/useSeries'; import { useSvgRef } from '../hooks'; +import { useCartesianContext } from '../context/CartesianProvider'; type AxisData = { dataIndex: number; @@ -27,7 +27,7 @@ function ChartsOnAxisClickHandler(props: ChartsOnAxisClickHandlerProps) { const svgRef = useSvgRef(); const series = useSeries(); const { axis } = React.useContext(InteractionContext); - const { xAxisIds, xAxis, yAxisIds, yAxis } = React.useContext(CartesianContext); + const { xAxisIds, xAxis, yAxisIds, yAxis } = useCartesianContext(); React.useEffect(() => { const element = svgRef.current; diff --git a/packages/x-charts/src/ChartsTooltip/ChartsAxisTooltipContent.tsx b/packages/x-charts/src/ChartsTooltip/ChartsAxisTooltipContent.tsx index 262bb3688029..e4e9487f7ab7 100644 --- a/packages/x-charts/src/ChartsTooltip/ChartsAxisTooltipContent.tsx +++ b/packages/x-charts/src/ChartsTooltip/ChartsAxisTooltipContent.tsx @@ -3,7 +3,7 @@ import PropTypes from 'prop-types'; import { SxProps, Theme } from '@mui/material/styles'; import { useSlotProps } from '@mui/base/utils'; import { AxisInteractionData } from '../context/InteractionProvider'; -import { CartesianContext } from '../context/CartesianContextProvider'; +import { useCartesianContext } from '../context/CartesianProvider'; import { ChartSeriesDefaultized, ChartSeriesType } from '../models/seriesType/config'; import { AxisDefaultized } from '../models/axis'; import { ChartsTooltipClasses } from './chartsTooltipClasses'; @@ -59,7 +59,7 @@ function ChartsAxisTooltipContent(props: { const dataIndex = isXaxis ? axisData.x && axisData.x.index : axisData.y && axisData.y.index; const axisValue = isXaxis ? axisData.x && axisData.x.value : axisData.y && axisData.y.value; - const { xAxisIds, xAxis, yAxisIds, yAxis } = React.useContext(CartesianContext); + const { xAxisIds, xAxis, yAxisIds, yAxis } = useCartesianContext(); const { zAxisIds, zAxis } = React.useContext(ZAxisContext); const series = useSeries(); diff --git a/packages/x-charts/src/ChartsTooltip/ChartsItemTooltipContent.tsx b/packages/x-charts/src/ChartsTooltip/ChartsItemTooltipContent.tsx index ea0951a2d1a4..f4356bbcfc1a 100644 --- a/packages/x-charts/src/ChartsTooltip/ChartsItemTooltipContent.tsx +++ b/packages/x-charts/src/ChartsTooltip/ChartsItemTooltipContent.tsx @@ -6,7 +6,7 @@ import { ItemInteractionData } from '../context/InteractionProvider'; import { ChartSeriesDefaultized, ChartSeriesType } from '../models/seriesType/config'; import { ChartsTooltipClasses } from './chartsTooltipClasses'; import { DefaultChartsItemTooltipContent } from './DefaultChartsItemTooltipContent'; -import { CartesianContext } from '../context/CartesianContextProvider'; +import { useCartesianContext } from '../context/CartesianProvider'; import { ZAxisContext } from '../context/ZAxisContextProvider'; import { useColorProcessor } from '../hooks/useColor'; import { useSeries } from '../hooks/useSeries'; @@ -44,7 +44,7 @@ function ChartsItemTooltipContent(props: { const series = useSeries()[itemData.type]!.series[itemData.seriesId] as ChartSeriesDefaultized; - const { xAxis, yAxis, xAxisIds, yAxisIds } = React.useContext(CartesianContext); + const { xAxis, yAxis, xAxisIds, yAxisIds } = useCartesianContext(); const { zAxis, zAxisIds } = React.useContext(ZAxisContext); const colorProcessors = useColorProcessor(); diff --git a/packages/x-charts/src/ChartsVoronoiHandler/ChartsVoronoiHandler.tsx b/packages/x-charts/src/ChartsVoronoiHandler/ChartsVoronoiHandler.tsx index 170c4a71e112..d9812c0b3baa 100644 --- a/packages/x-charts/src/ChartsVoronoiHandler/ChartsVoronoiHandler.tsx +++ b/packages/x-charts/src/ChartsVoronoiHandler/ChartsVoronoiHandler.tsx @@ -3,7 +3,7 @@ import PropTypes from 'prop-types'; import { Delaunay } from 'd3-delaunay'; import useEnhancedEffect from '@mui/utils/useEnhancedEffect'; import { InteractionContext } from '../context/InteractionProvider'; -import { CartesianContext } from '../context/CartesianContextProvider'; +import { useCartesianContext } from '../context/CartesianProvider'; import { getValueToPositionMapper } from '../hooks/useScale'; import { getSVGPoint } from '../internals/utils'; import { ScatterItemIdentifier } from '../models'; @@ -32,7 +32,7 @@ function ChartsVoronoiHandler(props: ChartsVoronoiHandlerProps) { const { voronoiMaxRadius, onItemClick } = props; const svgRef = useSvgRef(); const { left, top, width, height } = useDrawingArea(); - const { xAxis, yAxis, xAxisIds, yAxisIds } = React.useContext(CartesianContext); + const { xAxis, yAxis, xAxisIds, yAxisIds } = useCartesianContext(); const { dispatch } = React.useContext(InteractionContext); const { series, seriesOrder } = useScatterSeries() ?? {}; diff --git a/packages/x-charts/src/ChartsXAxis/ChartsXAxis.tsx b/packages/x-charts/src/ChartsXAxis/ChartsXAxis.tsx index 98146ac637d4..ea796b2e333e 100644 --- a/packages/x-charts/src/ChartsXAxis/ChartsXAxis.tsx +++ b/packages/x-charts/src/ChartsXAxis/ChartsXAxis.tsx @@ -3,7 +3,7 @@ import PropTypes from 'prop-types'; import { useSlotProps } from '@mui/base/utils'; import { unstable_composeClasses as composeClasses } from '@mui/utils'; import { useThemeProps, useTheme, Theme } from '@mui/material/styles'; -import { CartesianContext } from '../context/CartesianContextProvider'; +import { useCartesianContext } from '../context/CartesianProvider'; import { useTicks, TickItemType } from '../hooks/useTicks'; import { AxisDefaultized, ChartsXAxisProps } from '../models/axis'; import { getAxisUtilityClass } from '../ChartsAxis/axisClasses'; @@ -98,7 +98,7 @@ const defaultProps = { * - [ChartsXAxis API](https://mui.com/x/api/charts/charts-x-axis/) */ function ChartsXAxis(inProps: ChartsXAxisProps) { - const { xAxisIds, xAxis } = React.useContext(CartesianContext); + const { xAxisIds, xAxis } = useCartesianContext(); const { scale: xScale, tickNumber, reverse, ...settings } = xAxis[inProps.axisId ?? xAxisIds[0]]; const isMounted = useMounted(); diff --git a/packages/x-charts/src/ChartsYAxis/ChartsYAxis.tsx b/packages/x-charts/src/ChartsYAxis/ChartsYAxis.tsx index 784ed2640ca0..44475ad90f1f 100644 --- a/packages/x-charts/src/ChartsYAxis/ChartsYAxis.tsx +++ b/packages/x-charts/src/ChartsYAxis/ChartsYAxis.tsx @@ -3,7 +3,7 @@ import PropTypes from 'prop-types'; import { useSlotProps } from '@mui/base/utils'; import { unstable_composeClasses as composeClasses } from '@mui/utils'; import { useThemeProps, useTheme, Theme } from '@mui/material/styles'; -import { CartesianContext } from '../context/CartesianContextProvider'; +import { useCartesianContext } from '../context/CartesianProvider'; import { useTicks } from '../hooks/useTicks'; import { useDrawingArea } from '../hooks/useDrawingArea'; import { ChartsYAxisProps } from '../models/axis'; @@ -44,7 +44,7 @@ const defaultProps = { * - [ChartsYAxis API](https://mui.com/x/api/charts/charts-y-axis/) */ function ChartsYAxis(inProps: ChartsYAxisProps) { - const { yAxisIds, yAxis } = React.useContext(CartesianContext); + const { yAxisIds, yAxis } = useCartesianContext(); const { scale: yScale, tickNumber, ...settings } = yAxis[inProps.axisId ?? yAxisIds[0]]; const themedProps = useThemeProps({ props: { ...settings, ...inProps }, name: 'MuiChartsYAxis' }); diff --git a/packages/x-charts/src/LineChart/AreaPlot.tsx b/packages/x-charts/src/LineChart/AreaPlot.tsx index 51a62066c83a..daa51ea9d69a 100644 --- a/packages/x-charts/src/LineChart/AreaPlot.tsx +++ b/packages/x-charts/src/LineChart/AreaPlot.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import PropTypes from 'prop-types'; import { area as d3Area } from 'd3-shape'; -import { CartesianContext } from '../context/CartesianContextProvider'; +import { useCartesianContext } from '../context/CartesianProvider'; import { AreaElement, AreaElementProps, @@ -14,6 +14,7 @@ import { DEFAULT_X_AXIS_KEY } from '../constants'; import { LineItemIdentifier } from '../models/seriesType/line'; import { useChartGradient } from '../internals/components/ChartsAxesGradients'; import { useLineSeries } from '../hooks/useSeries'; +import { AxisId } from '../models/axis'; export interface AreaPlotSlots extends AreaElementSlots {} @@ -35,7 +36,7 @@ export interface AreaPlotProps const useAggregatedData = () => { const seriesData = useLineSeries(); - const axisData = React.useContext(CartesianContext); + const axisData = useCartesianContext(); if (seriesData === undefined) { return []; @@ -62,7 +63,7 @@ const useAggregatedData = () => { const yScale = yAxis[yAxisKey].scale; const xData = xAxis[xAxisKey].data; - const gradientUsed: [string, 'x' | 'y'] | undefined = + const gradientUsed: [AxisId, 'x' | 'y'] | undefined = (yAxis[yAxisKey].colorScale && [yAxisKey, 'y']) || (xAxis[xAxisKey].colorScale && [xAxisKey, 'x']) || undefined; diff --git a/packages/x-charts/src/LineChart/LineHighlightPlot.tsx b/packages/x-charts/src/LineChart/LineHighlightPlot.tsx index 25343dd4c129..d183bd73042d 100644 --- a/packages/x-charts/src/LineChart/LineHighlightPlot.tsx +++ b/packages/x-charts/src/LineChart/LineHighlightPlot.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import PropTypes from 'prop-types'; -import { CartesianContext } from '../context/CartesianContextProvider'; +import { useCartesianContext } from '../context/CartesianProvider'; import { LineHighlightElement, LineHighlightElementProps } from './LineHighlightElement'; import { getValueToPositionMapper } from '../hooks/useScale'; import { InteractionContext } from '../context/InteractionProvider'; @@ -43,7 +43,7 @@ function LineHighlightPlot(props: LineHighlightPlotProps) { const { slots, slotProps, ...other } = props; const seriesData = useLineSeries(); - const axisData = React.useContext(CartesianContext); + const axisData = useCartesianContext(); const { axis } = React.useContext(InteractionContext); const highlightedIndex = axis.x?.index; diff --git a/packages/x-charts/src/LineChart/LinePlot.tsx b/packages/x-charts/src/LineChart/LinePlot.tsx index 7e09416d2f96..6ee5d39eabf1 100644 --- a/packages/x-charts/src/LineChart/LinePlot.tsx +++ b/packages/x-charts/src/LineChart/LinePlot.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import PropTypes from 'prop-types'; import { line as d3Line } from 'd3-shape'; -import { CartesianContext } from '../context/CartesianContextProvider'; +import { useCartesianContext } from '../context/CartesianProvider'; import { LineElement, LineElementProps, @@ -14,6 +14,7 @@ import { DEFAULT_X_AXIS_KEY } from '../constants'; import { LineItemIdentifier } from '../models/seriesType/line'; import { useChartGradient } from '../internals/components/ChartsAxesGradients'; import { useLineSeries } from '../hooks/useSeries'; +import { AxisId } from '../models/axis'; export interface LinePlotSlots extends LineElementSlots {} @@ -35,7 +36,7 @@ export interface LinePlotProps const useAggregatedData = () => { const seriesData = useLineSeries(); - const axisData = React.useContext(CartesianContext); + const axisData = useCartesianContext(); if (seriesData === undefined) { return []; @@ -60,7 +61,7 @@ const useAggregatedData = () => { const yScale = yAxis[yAxisKey].scale; const xData = xAxis[xAxisKey].data; - const gradientUsed: [string, 'x' | 'y'] | undefined = + const gradientUsed: [AxisId, 'x' | 'y'] | undefined = (yAxis[yAxisKey].colorScale && [yAxisKey, 'y']) || (xAxis[xAxisKey].colorScale && [xAxisKey, 'x']) || undefined; diff --git a/packages/x-charts/src/LineChart/MarkPlot.tsx b/packages/x-charts/src/LineChart/MarkPlot.tsx index 23c7695f443e..ceecfbb0b6f7 100644 --- a/packages/x-charts/src/LineChart/MarkPlot.tsx +++ b/packages/x-charts/src/LineChart/MarkPlot.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import PropTypes from 'prop-types'; -import { CartesianContext } from '../context/CartesianContextProvider'; +import { useCartesianContext } from '../context/CartesianProvider'; import { MarkElement, MarkElementProps } from './MarkElement'; import { getValueToPositionMapper } from '../hooks/useScale'; import { useChartId } from '../hooks/useChartId'; @@ -56,7 +56,7 @@ function MarkPlot(props: MarkPlotProps) { const { slots, slotProps, skipAnimation, onItemClick, ...other } = props; const seriesData = useLineSeries(); - const axisData = React.useContext(CartesianContext); + const axisData = useCartesianContext(); const chartId = useChartId(); const Mark = slots?.mark ?? MarkElement; diff --git a/packages/x-charts/src/ScatterChart/ScatterPlot.tsx b/packages/x-charts/src/ScatterChart/ScatterPlot.tsx index 9b4129a1fa0c..0bdc7c1060a2 100644 --- a/packages/x-charts/src/ScatterChart/ScatterPlot.tsx +++ b/packages/x-charts/src/ScatterChart/ScatterPlot.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import PropTypes from 'prop-types'; import { Scatter, ScatterProps } from './Scatter'; -import { CartesianContext } from '../context/CartesianContextProvider'; +import { useCartesianContext } from '../context/CartesianProvider'; import getColor from './getColor'; import { ZAxisContext } from '../context/ZAxisContextProvider'; import { useScatterSeries } from '../hooks/useSeries'; @@ -40,7 +40,7 @@ export interface ScatterPlotProps extends Pick { function ScatterPlot(props: ScatterPlotProps) { const { slots, slotProps, onItemClick } = props; const seriesData = useScatterSeries(); - const axisData = React.useContext(CartesianContext); + const axisData = useCartesianContext(); const { zAxis, zAxisIds } = React.useContext(ZAxisContext); if (seriesData === undefined) { diff --git a/packages/x-charts/src/hooks/useAxisEvents.ts b/packages/x-charts/src/hooks/useAxisEvents.ts index 0663d1655ae2..1513bd7b6756 100644 --- a/packages/x-charts/src/hooks/useAxisEvents.ts +++ b/packages/x-charts/src/hooks/useAxisEvents.ts @@ -1,9 +1,9 @@ import * as React from 'react'; import { InteractionContext } from '../context/InteractionProvider'; -import { CartesianContext } from '../context/CartesianContextProvider'; +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'; @@ -13,7 +13,7 @@ function getAsANumber(value: number | Date) { export const useAxisEvents = (disableAxisListener: boolean) => { const svgRef = useSvgRef(); const { left, top, width, height } = useDrawingArea(); - const { xAxis, yAxis, xAxisIds, yAxisIds } = React.useContext(CartesianContext); + const { xAxis, yAxis, xAxisIds, yAxisIds } = useCartesianContext(); const { dispatch } = React.useContext(InteractionContext); const usedXAxis = xAxisIds[0]; diff --git a/packages/x-charts/src/hooks/useColorScale.ts b/packages/x-charts/src/hooks/useColorScale.ts index 947d2acbd6de..1443bfc727bd 100644 --- a/packages/x-charts/src/hooks/useColorScale.ts +++ b/packages/x-charts/src/hooks/useColorScale.ts @@ -1,12 +1,12 @@ import * as React from 'react'; -import { CartesianContext } from '../context/CartesianContextProvider'; +import { useCartesianContext } from '../context/CartesianProvider'; import { AxisScaleComputedConfig, ScaleName } from '../models/axis'; import { ZAxisContext } from '../context/ZAxisContextProvider'; export function useXColorScale( identifier?: number | string, ): AxisScaleComputedConfig[S]['colorScale'] | undefined { - const { xAxis, xAxisIds } = React.useContext(CartesianContext); + const { xAxis, xAxisIds } = useCartesianContext(); const id = typeof identifier === 'string' ? identifier : xAxisIds[identifier ?? 0]; @@ -16,7 +16,7 @@ export function useXColorScale( export function useYColorScale( identifier?: number | string, ): AxisScaleComputedConfig[S]['colorScale'] | undefined { - const { yAxis, yAxisIds } = React.useContext(CartesianContext); + const { yAxis, yAxisIds } = useCartesianContext(); const id = typeof identifier === 'string' ? identifier : yAxisIds[identifier ?? 0]; diff --git a/packages/x-charts/src/hooks/useDrawingArea.ts b/packages/x-charts/src/hooks/useDrawingArea.ts index e08c3e3a56b7..7b957071e1cd 100644 --- a/packages/x-charts/src/hooks/useDrawingArea.ts +++ b/packages/x-charts/src/hooks/useDrawingArea.ts @@ -1,7 +1,7 @@ import * as React from 'react'; -import { DrawingContext } from '../context/DrawingProvider'; +import { DrawingArea, DrawingContext } from '../context/DrawingProvider'; -export function useDrawingArea() { +export function useDrawingArea(): DrawingArea { const { left, top, width, height, bottom, right } = React.useContext(DrawingContext); return React.useMemo( () => ({ left, top, width, height, bottom, right }), diff --git a/packages/x-charts/src/hooks/useScale.ts b/packages/x-charts/src/hooks/useScale.ts index 648249f21c75..3af08f437bbf 100644 --- a/packages/x-charts/src/hooks/useScale.ts +++ b/packages/x-charts/src/hooks/useScale.ts @@ -1,5 +1,4 @@ -import * as React from 'react'; -import { CartesianContext } from '../context/CartesianContextProvider'; +import { useCartesianContext } from '../context/CartesianProvider'; import { isBandScale } from '../internals/isBandScale'; import { AxisScaleConfig, D3Scale, ScaleName } from '../models/axis'; @@ -19,7 +18,7 @@ export function getValueToPositionMapper(scale: D3Scale) { export function useXScale( identifier?: number | string, ): AxisScaleConfig[S]['scale'] { - const { xAxis, xAxisIds } = React.useContext(CartesianContext); + const { xAxis, xAxisIds } = useCartesianContext(); const id = typeof identifier === 'string' ? identifier : xAxisIds[identifier ?? 0]; @@ -29,7 +28,7 @@ export function useXScale( export function useYScale( identifier?: number | string, ): AxisScaleConfig[S]['scale'] { - const { yAxis, yAxisIds } = React.useContext(CartesianContext); + const { yAxis, yAxisIds } = useCartesianContext(); const id = typeof identifier === 'string' ? identifier : yAxisIds[identifier ?? 0]; diff --git a/packages/x-charts/src/models/plugin.ts b/packages/x-charts/src/models/plugin.ts index a8b4dd5287e5..49e04baa8a4f 100644 --- a/packages/x-charts/src/models/plugin.ts +++ b/packages/x-charts/src/models/plugin.ts @@ -1,4 +1,9 @@ -import { ChartSeriesType, ExtremumGetter, Formatter } from './seriesType/config'; +import { + CartesianChartSeriesType, + ChartSeriesType, + ExtremumGetter, + Formatter, +} from './seriesType/config'; import { AxisDefaultized } from './axis'; import { DefaultizedSeriesType } from './seriesType'; import { ZAxisDefaultized } from './z-axis'; @@ -23,3 +28,7 @@ export type ChartsPluginType = T extends ChartSeriesType yExtremumGetter?: ExtremumGetter; } : never; + +export type ExtremumGettersConfig = { + [K in T]?: ExtremumGetter; +}; From 457546154b21fc7d39ff8448a2ff54b555d4678c Mon Sep 17 00:00:00 2001 From: Jose Quintas Date: Tue, 18 Jun 2024 16:16:06 +0200 Subject: [PATCH 03/16] Fix import --- packages/x-charts/src/ChartContainer/ChartContainer.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/x-charts/src/ChartContainer/ChartContainer.tsx b/packages/x-charts/src/ChartContainer/ChartContainer.tsx index 7ebdd67c0e32..c389f3b3d609 100644 --- a/packages/x-charts/src/ChartContainer/ChartContainer.tsx +++ b/packages/x-charts/src/ChartContainer/ChartContainer.tsx @@ -13,7 +13,7 @@ import { ChartsSurface, ChartsSurfaceProps } from '../ChartsSurface'; import { CartesianContextProvider, CartesianContextProviderProps, -} from '../context/CartesianContextProvider'; +} from '../context/CartesianProvider'; import { ChartsAxesGradients } from '../internals/components/ChartsAxesGradients'; import { HighlightedProvider, From 67c106fa26c2a8551a1a0a48a5683e341d36338b Mon Sep 17 00:00:00 2001 From: Jose Quintas Date: Tue, 18 Jun 2024 16:16:36 +0200 Subject: [PATCH 04/16] Fix import --- packages/x-charts/src/hooks/useAxisEvents.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/x-charts/src/hooks/useAxisEvents.ts b/packages/x-charts/src/hooks/useAxisEvents.ts index 1513bd7b6756..855ec5985c1a 100644 --- a/packages/x-charts/src/hooks/useAxisEvents.ts +++ b/packages/x-charts/src/hooks/useAxisEvents.ts @@ -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/getSVGPoint'; +import { getSVGPoint } from '../internals/utils'; import { useSvgRef } from './useSvgRef'; import { useDrawingArea } from './useDrawingArea'; From 2f03e6cfdd776e6ed69f4f8549d38f77d08402e8 Mon Sep 17 00:00:00 2001 From: Jose Quintas Date: Tue, 18 Jun 2024 16:19:40 +0200 Subject: [PATCH 05/16] Fix missing component --- .../components/ChartsAxesGradients/ChartsAxesGradients.tsx | 7 ++++--- packages/x-charts/src/internals/index.ts | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/x-charts/src/internals/components/ChartsAxesGradients/ChartsAxesGradients.tsx b/packages/x-charts/src/internals/components/ChartsAxesGradients/ChartsAxesGradients.tsx index 2e388f111c11..b5e0e40e86cb 100644 --- a/packages/x-charts/src/internals/components/ChartsAxesGradients/ChartsAxesGradients.tsx +++ b/packages/x-charts/src/internals/components/ChartsAxesGradients/ChartsAxesGradients.tsx @@ -1,14 +1,15 @@ import * as React from 'react'; -import { CartesianContext } from '../../../context/CartesianContextProvider'; +import { useCartesianContext } from '../../../context/CartesianProvider'; import { DrawingContext } from '../../../context/DrawingProvider'; import { useDrawingArea } from '../../../hooks'; import ChartsPiecewiseGradient from './ChartsPiecewiseGradient'; import ChartsContinuousGradient from './ChartsContinuousGradient'; +import { AxisId } from '../../../models/axis'; export function useChartGradient() { const { chartId } = React.useContext(DrawingContext); return React.useCallback( - (axisId: string, direction: 'x' | 'y') => `${chartId}-gradient-${direction}-${axisId}`, + (axisId: AxisId, direction: 'x' | 'y') => `${chartId}-gradient-${direction}-${axisId}`, [chartId], ); } @@ -19,7 +20,7 @@ export function ChartsAxesGradients() { const svgHeight = top + height + bottom; const svgWidth = left + width + right; const getGradientId = useChartGradient(); - const { xAxisIds, xAxis, yAxisIds, yAxis } = React.useContext(CartesianContext); + const { xAxisIds, xAxis, yAxisIds, yAxis } = useCartesianContext(); return ( diff --git a/packages/x-charts/src/internals/index.ts b/packages/x-charts/src/internals/index.ts index 6e6bafd231d4..fb23fa7f025f 100644 --- a/packages/x-charts/src/internals/index.ts +++ b/packages/x-charts/src/internals/index.ts @@ -14,7 +14,7 @@ export * from './configInit'; // contexts -export * from '../context/CartesianContextProvider'; +export * from '../context/CartesianProvider'; export * from '../context/DrawingProvider'; export * from '../context/InteractionProvider'; export * from '../context/SeriesContextProvider'; From 9a3a6f10f5339b37aa36b6b6dbad35a982fe443a Mon Sep 17 00:00:00 2001 From: Jose Quintas Date: Tue, 18 Jun 2024 16:26:01 +0200 Subject: [PATCH 06/16] Add exports --- scripts/x-charts.exports.json | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/x-charts.exports.json b/scripts/x-charts.exports.json index 4863552e605e..a38d3220182e 100644 --- a/scripts/x-charts.exports.json +++ b/scripts/x-charts.exports.json @@ -135,6 +135,7 @@ { "name": "DefaultizedScatterSeriesType", "kind": "Interface" }, { "name": "DefaultizedSeriesType", "kind": "TypeAlias" }, { "name": "Direction", "kind": "TypeAlias" }, + { "name": "ExtremumGettersConfig", "kind": "TypeAlias" }, { "name": "FadeOptions", "kind": "TypeAlias" }, { "name": "Gauge", "kind": "Function" }, { "name": "gaugeClasses", "kind": "Variable" }, From 7341f891b9b6ecb609cc2793e911cd36888044e5 Mon Sep 17 00:00:00 2001 From: Jose Quintas Date: Tue, 18 Jun 2024 16:47:35 +0200 Subject: [PATCH 07/16] Add .nice back to prevent issues --- .../x-charts/src/context/CartesianProvider/computeValue.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/x-charts/src/context/CartesianProvider/computeValue.ts b/packages/x-charts/src/context/CartesianProvider/computeValue.ts index 7f54176f0fff..550177087f8c 100644 --- a/packages/x-charts/src/context/CartesianProvider/computeValue.ts +++ b/packages/x-charts/src/context/CartesianProvider/computeValue.ts @@ -85,9 +85,8 @@ export const computeValue = ( const extremums = [axis.min ?? minData, axis.max ?? maxData]; const tickNumber = getTickNumber({ ...axis, range, domain: extremums }); - // Gotta remove ".nice" here to avoid jittering in zoom const scale = getScale(scaleType, extremums, range); - const [minDomain, maxDomain] = scale.domain(); + const [minDomain, maxDomain] = scale.nice().domain(); const domain = [axis.min ?? minDomain, axis.max ?? maxDomain]; completedXAxis[axis.id] = { @@ -158,9 +157,8 @@ export const computeValue = ( const extremums = [axis.min ?? minData, axis.max ?? maxData]; const tickNumber = getTickNumber({ ...axis, range, domain: extremums }); - // Gotta remove ".nice" here to avoid jittering in zoom const scale = getScale(scaleType, extremums, range); - const [minDomain, maxDomain] = scale.domain(); + const [minDomain, maxDomain] = scale.nice().domain(); const domain = [axis.min ?? minDomain, axis.max ?? maxDomain]; completedYAxis[axis.id] = { From a5ee2ef9df3165d35d852cec5ad7db8eeda42d51 Mon Sep 17 00:00:00 2001 From: Jose Quintas Date: Tue, 18 Jun 2024 17:00:14 +0200 Subject: [PATCH 08/16] Both scale and domain should be nice --- .../src/context/CartesianProvider/computeValue.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/x-charts/src/context/CartesianProvider/computeValue.ts b/packages/x-charts/src/context/CartesianProvider/computeValue.ts index 550177087f8c..d70d50299287 100644 --- a/packages/x-charts/src/context/CartesianProvider/computeValue.ts +++ b/packages/x-charts/src/context/CartesianProvider/computeValue.ts @@ -85,8 +85,8 @@ export const computeValue = ( const extremums = [axis.min ?? minData, axis.max ?? maxData]; const tickNumber = getTickNumber({ ...axis, range, domain: extremums }); - const scale = getScale(scaleType, extremums, range); - const [minDomain, maxDomain] = scale.nice().domain(); + const scale = getScale(scaleType, extremums, range).nice(); + const [minDomain, maxDomain] = scale.domain(); const domain = [axis.min ?? minDomain, axis.max ?? maxDomain]; completedXAxis[axis.id] = { @@ -157,8 +157,8 @@ export const computeValue = ( const extremums = [axis.min ?? minData, axis.max ?? maxData]; const tickNumber = getTickNumber({ ...axis, range, domain: extremums }); - const scale = getScale(scaleType, extremums, range); - const [minDomain, maxDomain] = scale.nice().domain(); + const scale = getScale(scaleType, extremums, range).nice(); + const [minDomain, maxDomain] = scale.domain(); const domain = [axis.min ?? minDomain, axis.max ?? maxDomain]; completedYAxis[axis.id] = { From f9a953e951dcc7e07792caddc321f16c118a4cb0 Mon Sep 17 00:00:00 2001 From: Jose Quintas Date: Tue, 18 Jun 2024 17:16:53 +0200 Subject: [PATCH 09/16] Add ticknumber to nice() --- .../x-charts/src/context/CartesianProvider/computeValue.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/x-charts/src/context/CartesianProvider/computeValue.ts b/packages/x-charts/src/context/CartesianProvider/computeValue.ts index d70d50299287..307d8e32c881 100644 --- a/packages/x-charts/src/context/CartesianProvider/computeValue.ts +++ b/packages/x-charts/src/context/CartesianProvider/computeValue.ts @@ -85,7 +85,7 @@ export const computeValue = ( const extremums = [axis.min ?? minData, axis.max ?? maxData]; const tickNumber = getTickNumber({ ...axis, range, domain: extremums }); - const scale = getScale(scaleType, extremums, range).nice(); + const scale = getScale(scaleType, extremums, range).nice(tickNumber); const [minDomain, maxDomain] = scale.domain(); const domain = [axis.min ?? minDomain, axis.max ?? maxDomain]; @@ -157,7 +157,7 @@ export const computeValue = ( const extremums = [axis.min ?? minData, axis.max ?? maxData]; const tickNumber = getTickNumber({ ...axis, range, domain: extremums }); - const scale = getScale(scaleType, extremums, range).nice(); + const scale = getScale(scaleType, extremums, range).nice(tickNumber); const [minDomain, maxDomain] = scale.domain(); const domain = [axis.min ?? minDomain, axis.max ?? maxDomain]; From 2c750e2a4ef03635da31d3eb23a1dd99f961a41d Mon Sep 17 00:00:00 2001 From: Jose C Quintas Jr Date: Tue, 18 Jun 2024 17:50:31 +0200 Subject: [PATCH 10/16] Update packages/x-charts/src/context/CartesianProvider/CartesianContext.ts Co-authored-by: Alexandre Fauquette <45398769+alexfauquette@users.noreply.github.com> Signed-off-by: Jose C Quintas Jr --- .../x-charts/src/context/CartesianProvider/CartesianContext.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/x-charts/src/context/CartesianProvider/CartesianContext.ts b/packages/x-charts/src/context/CartesianProvider/CartesianContext.ts index bbc47f2b534b..6406b1e42dcc 100644 --- a/packages/x-charts/src/context/CartesianProvider/CartesianContext.ts +++ b/packages/x-charts/src/context/CartesianProvider/CartesianContext.ts @@ -10,7 +10,7 @@ import { } from '../../models/axis'; export type DefaultizedAxisConfig = { - [axisKey: string]: AxisDefaultized; + [axisKey: AxisId]: AxisDefaultized; }; export type CartesianContextState = { From ca5319a9f493af8195472be8e2d6dd6b8bbb03dd Mon Sep 17 00:00:00 2001 From: Jose Quintas Date: Tue, 18 Jun 2024 18:30:43 +0200 Subject: [PATCH 11/16] Dont error as MUI Y --- .../x-charts/src/context/CartesianProvider/normalizeAxis.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/x-charts/src/context/CartesianProvider/normalizeAxis.ts b/packages/x-charts/src/context/CartesianProvider/normalizeAxis.ts index df9d2193c265..619eccdf40a6 100644 --- a/packages/x-charts/src/context/CartesianProvider/normalizeAxis.ts +++ b/packages/x-charts/src/context/CartesianProvider/normalizeAxis.ts @@ -18,7 +18,7 @@ export const normalizeAxis = < } if (dataset === undefined) { throw Error( - `MUI ${axisName.toUpperCase()} Charts: ${axisName}-axis uses \`dataKey\` but no \`dataset\` is provided.`, + `MUI X Charts: ${axisName}-axis uses \`dataKey\` but no \`dataset\` is provided.`, ); } return { From f0040c9b02e61f61aaf1109e797c0032d3603385 Mon Sep 17 00:00:00 2001 From: Jose Quintas Date: Tue, 18 Jun 2024 18:31:00 +0200 Subject: [PATCH 12/16] Simplify computeValue for X --- .../CartesianProvider/CartesianProvider.tsx | 23 ++- .../context/CartesianProvider/computeValue.ts | 153 ++++++------------ 2 files changed, 71 insertions(+), 105 deletions(-) diff --git a/packages/x-charts/src/context/CartesianProvider/CartesianProvider.tsx b/packages/x-charts/src/context/CartesianProvider/CartesianProvider.tsx index d004c2cd802a..847f9e553afd 100644 --- a/packages/x-charts/src/context/CartesianProvider/CartesianProvider.tsx +++ b/packages/x-charts/src/context/CartesianProvider/CartesianProvider.tsx @@ -54,10 +54,27 @@ function CartesianContextProvider(props: CartesianContextProviderProps) { const yAxis = React.useMemo(() => normalizeAxis(inYAxis, dataset, 'y'), [inYAxis, dataset]); + const xValues = React.useMemo( + () => computeValue(drawingArea, formattedSeries, xAxis, xExtremumGetters, 'x'), + [drawingArea, formattedSeries, xAxis, xExtremumGetters], + ); + + const yValues = React.useMemo( + () => computeValue(drawingArea, formattedSeries, yAxis, yExtremumGetters, 'y'), + [drawingArea, formattedSeries, yAxis, yExtremumGetters], + ); + const value = React.useMemo( - () => - computeValue(drawingArea, formattedSeries, xAxis, yAxis, xExtremumGetters, yExtremumGetters), - [drawingArea, formattedSeries, xAxis, xExtremumGetters, yAxis, yExtremumGetters], + () => ({ + isInitialized: true, + data: { + xAxis: xValues.axis, + yAxis: yValues.axis, + xAxisIds: xValues.axisIds, + yAxisIds: yValues.axisIds, + }, + }), + [xValues, yValues], ); return {children}; diff --git a/packages/x-charts/src/context/CartesianProvider/computeValue.ts b/packages/x-charts/src/context/CartesianProvider/computeValue.ts index 307d8e32c881..1c0823393f42 100644 --- a/packages/x-charts/src/context/CartesianProvider/computeValue.ts +++ b/packages/x-charts/src/context/CartesianProvider/computeValue.ts @@ -1,7 +1,13 @@ import { scaleBand, scalePoint } from 'd3-scale'; import { DEFAULT_X_AXIS_KEY, DEFAULT_Y_AXIS_KEY } from '../../constants'; -import { AxisConfig, ChartsXAxisProps, ScaleName } from '../../models'; -import { ChartsYAxisProps, isBandScaleConfig, isPointScaleConfig } from '../../models/axis'; +import { AxisConfig, ScaleName } from '../../models'; +import { + ChartsXAxisProps, + ChartsAxisProps, + ChartsYAxisProps, + isBandScaleConfig, + isPointScaleConfig, +} from '../../models/axis'; import { CartesianChartSeriesType, ExtremumGetter } from '../../models/seriesType/config'; import { DefaultizedAxisConfig } from './CartesianContext'; import { getColorScale, getOrdinalColorScale } from '../../internals/colorScale'; @@ -12,31 +18,48 @@ import { FormattedSeries } from '../SeriesContextProvider'; import { MakeOptional } from '../../models/helpers'; import { getAxisExtremum } from './getAxisExtremum'; -const DEFAULT_CATEGORY_GAP_RATIO = 0.2; -const DEFAULT_BAR_GAP_RATIO = 0.1; - -export const computeValue = ( +export function computeValue( drawingArea: DrawingArea, formattedSeries: FormattedSeries, - xAxis: MakeOptional, 'id'>[] | undefined, - yAxis: MakeOptional, 'id'>[] | undefined, - xExtremumGetters: { [K in CartesianChartSeriesType]?: ExtremumGetter }, - yExtremumGetters: { [K in CartesianChartSeriesType]?: ExtremumGetter }, -) => { - const allXAxis: AxisConfig[] = [ - ...(xAxis?.map((axis, index) => ({ id: `defaultized-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 const }] + axis: MakeOptional, 'id'>[] | undefined, + extremumGetters: { [K in CartesianChartSeriesType]?: ExtremumGetter }, + axisName: 'y', +): { + axis: DefaultizedAxisConfig; + axisIds: string[]; +}; +export function computeValue( + drawingArea: DrawingArea, + formattedSeries: FormattedSeries, + inAxis: MakeOptional, 'id'>[] | undefined, + extremumGetters: { [K in CartesianChartSeriesType]?: ExtremumGetter }, + axisName: 'x', +): { + axis: DefaultizedAxisConfig; + axisIds: string[]; +}; +export function computeValue( + drawingArea: DrawingArea, + formattedSeries: FormattedSeries, + inAxis: MakeOptional, 'id'>[] | undefined, + extremumGetters: { [K in CartesianChartSeriesType]?: ExtremumGetter }, + axisName: 'x' | 'y', +) { + const DEFAULT_AXIS_KEY = axisName === 'x' ? DEFAULT_X_AXIS_KEY : DEFAULT_Y_AXIS_KEY; + + const allAxis: AxisConfig[] = [ + ...(inAxis?.map((a, index) => ({ id: `defaultized-x-axis-${index}`, ...a })) ?? []), + ...(inAxis === undefined || inAxis.findIndex(({ id }) => id === DEFAULT_AXIS_KEY) === -1 + ? [{ id: DEFAULT_AXIS_KEY, scaleType: 'linear' as const }] : []), ]; - const completedXAxis: DefaultizedAxisConfig = {}; - allXAxis.forEach((axis, axisIndex) => { + const completeAxis: DefaultizedAxisConfig = {}; + allAxis.forEach((axis, axisIndex) => { const isDefaultAxis = axisIndex === 0; const [minData, maxData] = getAxisExtremum( axis, - xExtremumGetters, + extremumGetters, isDefaultAxis, formattedSeries, ); @@ -46,9 +69,12 @@ export const computeValue = ( : [drawingArea.left, drawingArea.left + drawingArea.width]; if (isBandScaleConfig(axis)) { + const DEFAULT_CATEGORY_GAP_RATIO = 0.2; + const DEFAULT_BAR_GAP_RATIO = 0.1; + const categoryGapRatio = axis.categoryGapRatio ?? DEFAULT_CATEGORY_GAP_RATIO; const barGapRatio = axis.barGapRatio ?? DEFAULT_BAR_GAP_RATIO; - completedXAxis[axis.id] = { + completeAxis[axis.id] = { categoryGapRatio, barGapRatio, ...axis, @@ -64,7 +90,7 @@ export const computeValue = ( }; } if (isPointScaleConfig(axis)) { - completedXAxis[axis.id] = { + completeAxis[axis.id] = { ...axis, scale: scalePoint(axis.data!, range), tickNumber: axis.data!.length, @@ -89,79 +115,7 @@ export const computeValue = ( const [minDomain, maxDomain] = scale.domain(); const domain = [axis.min ?? minDomain, axis.max ?? maxDomain]; - completedXAxis[axis.id] = { - ...axis, - scaleType: scaleType as any, - scale: scale.domain(domain) as any, - tickNumber, - colorScale: axis.colorMap && getColorScale(axis.colorMap), - }; - }); - - const allYAxis: AxisConfig[] = [ - ...(yAxis?.map((axis, index) => ({ id: `defaultized-y-axis-${index}`, ...axis })) ?? []), - ...(yAxis === undefined || yAxis.findIndex(({ id }) => id === DEFAULT_Y_AXIS_KEY) === -1 - ? [{ id: DEFAULT_Y_AXIS_KEY, scaleType: 'linear' as const }] - : []), - ]; - - const completedYAxis: DefaultizedAxisConfig = {}; - allYAxis.forEach((axis, axisIndex) => { - const isDefaultAxis = axisIndex === 0; - const [minData, maxData] = getAxisExtremum( - axis, - yExtremumGetters, - isDefaultAxis, - formattedSeries, - ); - const range = axis.reverse - ? [drawingArea.top, drawingArea.top + drawingArea.height] - : [drawingArea.top + drawingArea.height, drawingArea.top]; - - if (isBandScaleConfig(axis)) { - const categoryGapRatio = axis.categoryGapRatio ?? DEFAULT_CATEGORY_GAP_RATIO; - completedYAxis[axis.id] = { - categoryGapRatio, - barGapRatio: 0, - ...axis, - scale: scaleBand(axis.data!, [range[1], range[0]]) - .paddingInner(categoryGapRatio) - .paddingOuter(categoryGapRatio / 2), - tickNumber: axis.data!.length, - colorScale: - axis.colorMap && - (axis.colorMap.type === 'ordinal' - ? getOrdinalColorScale({ values: axis.data, ...axis.colorMap }) - : getColorScale(axis.colorMap)), - }; - } - if (isPointScaleConfig(axis)) { - completedYAxis[axis.id] = { - ...axis, - scale: scalePoint(axis.data!, [range[1], range[0]]), - tickNumber: axis.data!.length, - colorScale: - axis.colorMap && - (axis.colorMap.type === 'ordinal' - ? getOrdinalColorScale({ values: axis.data, ...axis.colorMap }) - : getColorScale(axis.colorMap)), - }; - } - if (axis.scaleType === 'band' || axis.scaleType === 'point') { - // Could be merged with the two previous "if conditions" but then TS does not get that `axis.scaleType` can't be `band` or `point`. - return; - } - - const scaleType = axis.scaleType ?? ('linear' as const); - - const extremums = [axis.min ?? minData, axis.max ?? maxData]; - const tickNumber = getTickNumber({ ...axis, range, domain: extremums }); - - const scale = getScale(scaleType, extremums, range).nice(tickNumber); - const [minDomain, maxDomain] = scale.domain(); - const domain = [axis.min ?? minDomain, axis.max ?? maxDomain]; - - completedYAxis[axis.id] = { + completeAxis[axis.id] = { ...axis, scaleType: scaleType as any, scale: scale.domain(domain) as any, @@ -171,12 +125,7 @@ export const computeValue = ( }); return { - isInitialized: true, - data: { - xAxis: completedXAxis, - yAxis: completedYAxis, - xAxisIds: allXAxis.map(({ id }) => id), - yAxisIds: allYAxis.map(({ id }) => id), - }, + axis: completeAxis, + axisIds: allAxis.map(({ id }) => id), }; -}; +} From 6971548800fa4c473e9891d99312d3b6de9f581d Mon Sep 17 00:00:00 2001 From: Jose Quintas Date: Tue, 18 Jun 2024 18:42:06 +0200 Subject: [PATCH 13/16] computeValue for Y --- .../context/CartesianProvider/computeValue.ts | 26 ++++++++++++++----- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/packages/x-charts/src/context/CartesianProvider/computeValue.ts b/packages/x-charts/src/context/CartesianProvider/computeValue.ts index 1c0823393f42..15f7e55398ea 100644 --- a/packages/x-charts/src/context/CartesianProvider/computeValue.ts +++ b/packages/x-charts/src/context/CartesianProvider/computeValue.ts @@ -18,6 +18,15 @@ import { FormattedSeries } from '../SeriesContextProvider'; import { MakeOptional } from '../../models/helpers'; import { getAxisExtremum } from './getAxisExtremum'; +const getRange = (drawingArea: DrawingArea, axisName: 'x' | 'y', isReverse?: boolean) => { + const range = + axisName === 'x' + ? [drawingArea.left, drawingArea.left + drawingArea.width] + : [drawingArea.top + drawingArea.height, drawingArea.top]; + + return isReverse ? range.reverse() : range; +}; + export function computeValue( drawingArea: DrawingArea, formattedSeries: FormattedSeries, @@ -48,7 +57,8 @@ export function computeValue( const DEFAULT_AXIS_KEY = axisName === 'x' ? DEFAULT_X_AXIS_KEY : DEFAULT_Y_AXIS_KEY; const allAxis: AxisConfig[] = [ - ...(inAxis?.map((a, index) => ({ id: `defaultized-x-axis-${index}`, ...a })) ?? []), + ...(inAxis?.map((axis, index) => ({ id: `defaultized-${axisName}-axis-${index}`, ...axis })) ?? + []), ...(inAxis === undefined || inAxis.findIndex(({ id }) => id === DEFAULT_AXIS_KEY) === -1 ? [{ id: DEFAULT_AXIS_KEY, scaleType: 'linear' as const }] : []), @@ -64,21 +74,21 @@ export function computeValue( formattedSeries, ); - const range = axis.reverse - ? [drawingArea.left + drawingArea.width, drawingArea.left] - : [drawingArea.left, drawingArea.left + drawingArea.width]; + const range = getRange(drawingArea, axisName, axis.reverse); if (isBandScaleConfig(axis)) { const DEFAULT_CATEGORY_GAP_RATIO = 0.2; const DEFAULT_BAR_GAP_RATIO = 0.1; const categoryGapRatio = axis.categoryGapRatio ?? DEFAULT_CATEGORY_GAP_RATIO; - const barGapRatio = axis.barGapRatio ?? DEFAULT_BAR_GAP_RATIO; + const barGapRatio = axisName === 'x' ? axis.barGapRatio ?? DEFAULT_BAR_GAP_RATIO : 0; + const scaleRange = axisName === 'x' ? range : [...range].reverse(); + completeAxis[axis.id] = { categoryGapRatio, barGapRatio, ...axis, - scale: scaleBand(axis.data!, range) + scale: scaleBand(axis.data!, scaleRange) .paddingInner(categoryGapRatio) .paddingOuter(categoryGapRatio / 2), tickNumber: axis.data!.length, @@ -90,9 +100,11 @@ export function computeValue( }; } if (isPointScaleConfig(axis)) { + const scaleRange = axisName === 'x' ? range : [...range].reverse(); + completeAxis[axis.id] = { ...axis, - scale: scalePoint(axis.data!, range), + scale: scalePoint(axis.data!, scaleRange), tickNumber: axis.data!.length, colorScale: axis.colorMap && From 5062469ec2afee36392950a86b032407d074b7fd Mon Sep 17 00:00:00 2001 From: Jose C Quintas Jr Date: Wed, 19 Jun 2024 10:29:34 +0200 Subject: [PATCH 14/16] Update packages/x-charts/src/context/CartesianProvider/computeValue.ts Co-authored-by: Alexandre Fauquette <45398769+alexfauquette@users.noreply.github.com> Signed-off-by: Jose C Quintas Jr --- packages/x-charts/src/context/CartesianProvider/computeValue.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/x-charts/src/context/CartesianProvider/computeValue.ts b/packages/x-charts/src/context/CartesianProvider/computeValue.ts index 15f7e55398ea..fe736ebaf8d4 100644 --- a/packages/x-charts/src/context/CartesianProvider/computeValue.ts +++ b/packages/x-charts/src/context/CartesianProvider/computeValue.ts @@ -81,7 +81,7 @@ export function computeValue( const DEFAULT_BAR_GAP_RATIO = 0.1; const categoryGapRatio = axis.categoryGapRatio ?? DEFAULT_CATEGORY_GAP_RATIO; - const barGapRatio = axisName === 'x' ? axis.barGapRatio ?? DEFAULT_BAR_GAP_RATIO : 0; + const barGapRatio = axis.barGapRatio ?? DEFAULT_BAR_GAP_RATIO; const scaleRange = axisName === 'x' ? range : [...range].reverse(); completeAxis[axis.id] = { From b606028e6769fa008333314028fb0dfd39777938 Mon Sep 17 00:00:00 2001 From: Jose C Quintas Jr Date: Wed, 19 Jun 2024 10:29:47 +0200 Subject: [PATCH 15/16] Update packages/x-charts/src/context/CartesianProvider/computeValue.ts Co-authored-by: Alexandre Fauquette <45398769+alexfauquette@users.noreply.github.com> Signed-off-by: Jose C Quintas Jr --- .../x-charts/src/context/CartesianProvider/computeValue.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/x-charts/src/context/CartesianProvider/computeValue.ts b/packages/x-charts/src/context/CartesianProvider/computeValue.ts index fe736ebaf8d4..0d66626f6613 100644 --- a/packages/x-charts/src/context/CartesianProvider/computeValue.ts +++ b/packages/x-charts/src/context/CartesianProvider/computeValue.ts @@ -82,7 +82,8 @@ export function computeValue( const categoryGapRatio = axis.categoryGapRatio ?? DEFAULT_CATEGORY_GAP_RATIO; const barGapRatio = axis.barGapRatio ?? DEFAULT_BAR_GAP_RATIO; - const scaleRange = axisName === 'x' ? range : [...range].reverse(); + // Reverse range because ordinal scales are presented from top to bottom on y-axis + const scaleRange = axisName === 'x' ? range : [range[1], range[0]]; completeAxis[axis.id] = { categoryGapRatio, From a45bbc3f3047367f4d77923bebe31588dbee6add Mon Sep 17 00:00:00 2001 From: Jose Quintas Date: Wed, 19 Jun 2024 10:31:01 +0200 Subject: [PATCH 16/16] Move consts back out --- .../x-charts/src/context/CartesianProvider/computeValue.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/x-charts/src/context/CartesianProvider/computeValue.ts b/packages/x-charts/src/context/CartesianProvider/computeValue.ts index 0d66626f6613..053b51cf1afd 100644 --- a/packages/x-charts/src/context/CartesianProvider/computeValue.ts +++ b/packages/x-charts/src/context/CartesianProvider/computeValue.ts @@ -27,6 +27,9 @@ const getRange = (drawingArea: DrawingArea, axisName: 'x' | 'y', isReverse?: boo return isReverse ? range.reverse() : range; }; +const DEFAULT_CATEGORY_GAP_RATIO = 0.2; +const DEFAULT_BAR_GAP_RATIO = 0.1; + export function computeValue( drawingArea: DrawingArea, formattedSeries: FormattedSeries, @@ -77,9 +80,6 @@ export function computeValue( const range = getRange(drawingArea, axisName, axis.reverse); if (isBandScaleConfig(axis)) { - const DEFAULT_CATEGORY_GAP_RATIO = 0.2; - const DEFAULT_BAR_GAP_RATIO = 0.1; - const categoryGapRatio = axis.categoryGapRatio ?? DEFAULT_CATEGORY_GAP_RATIO; const barGapRatio = axis.barGapRatio ?? DEFAULT_BAR_GAP_RATIO; // Reverse range because ordinal scales are presented from top to bottom on y-axis