Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { SeriesProcessor, ChartSeriesDefaultized } from '@mui/x-charts/internals';
import { SeriesProcessorWithoutDimensions, ChartSeriesDefaultized } from '@mui/x-charts/internals';
import type { FunnelCurveType } from '../curves';

const createPoint = ({
Expand Down Expand Up @@ -38,7 +38,7 @@ const getFunnelDirection = (
: 'decreasing';
};

const seriesProcessor: SeriesProcessor<'funnel'> = (params) => {
const seriesProcessor: SeriesProcessorWithoutDimensions<'funnel'> = (params) => {
const { seriesOrder, series } = params;

const completedSeries: Record<string, ChartSeriesDefaultized<'funnel'>> = {};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { SeriesProcessor, SeriesId } from '@mui/x-charts/internals';
import { SeriesProcessorWithoutDimensions, SeriesId } from '@mui/x-charts/internals';
import { DefaultizedHeatmapSeriesType } from '../../models/seriesType/heatmap';

const seriesProcessor: SeriesProcessor<'heatmap'> = (params) => {
const seriesProcessor: SeriesProcessorWithoutDimensions<'heatmap'> = (params) => {
const { series, seriesOrder } = params;

const defaultizedSeries: Record<SeriesId, DefaultizedHeatmapSeriesType> = {};
Expand Down
21 changes: 5 additions & 16 deletions packages/x-charts-pro/src/SankeyChart/SankeyPlot.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
'use client';
import * as React from 'react';
import PropTypes from 'prop-types';
import { styled, useTheme } from '@mui/material/styles';
import { useDrawingArea } from '@mui/x-charts/hooks';
import { styled } from '@mui/material/styles';
import {
SankeyLayout,
type SankeyLinkIdentifierWithData,
type SankeyNodeIdentifierWithData,
} from './sankey.types';
import { calculateSankeyLayout } from './calculateSankeyLayout';
import { SankeyNodeElement } from './SankeyNodeElement';
import { SankeyLinkElement } from './SankeyLinkElement';
import { SankeyLinkLabel } from './SankeyLinkLabel';
Expand Down Expand Up @@ -66,15 +63,7 @@ function SankeyPlot(props: SankeyPlotProps) {

const series = seriesContext.series[seriesContext.seriesOrder?.[0]];
const classes = useUtilityClasses({ classes: inputClasses });
const drawingArea = useDrawingArea();
const { data, linkOptions, nodeOptions } = series;
const theme = useTheme();

// Calculate layout based on data and dimensions
const layout: SankeyLayout = React.useMemo(
() => calculateSankeyLayout(data, drawingArea, theme, series),
[drawingArea, data, series, theme],
);
const { data, linkOptions, nodeOptions, sankeyLayout } = series;

// Early return if no data or dimensions
if (!data || !data.links) {
Expand All @@ -84,7 +73,7 @@ function SankeyPlot(props: SankeyPlotProps) {
return (
<SankeyPlotRoot className={classes.root}>
<g className={classes.links}>
{layout.links.map((link) => (
{sankeyLayout.links.map((link) => (
<SankeyLinkElement
seriesId={series.id}
key={`${link.source.id}-${link.target.id}`}
Expand All @@ -96,7 +85,7 @@ function SankeyPlot(props: SankeyPlotProps) {
</g>

<g className={classes.nodes}>
{layout.nodes.map((node) => (
{sankeyLayout.nodes.map((node) => (
<SankeyNodeElement
seriesId={series.id}
key={node.id}
Expand All @@ -109,7 +98,7 @@ function SankeyPlot(props: SankeyPlotProps) {

{linkOptions?.showValues && (
<g className={classes.linkLabels}>
{layout.links.map((link) => (
{sankeyLayout.links.map((link) => (
<SankeyLinkLabel key={`label-${link.source.id}-${link.target.id}`} link={link} />
))}
</g>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
'use client';
import { sankey, type SankeyGraph } from '@mui/x-charts-vendor/d3-sankey';
import type { ChartDrawingArea } from '@mui/x-charts/hooks';
import type { Theme } from '@mui/material/styles';
import { path } from '@mui/x-charts-vendor/d3-path';
import type {
SankeySeriesType,
SankeyLayout,
SankeyLayoutLink,
SankeyLayoutNode,
Expand All @@ -22,12 +20,10 @@ import { getNodeAlignFunction } from './utils';
*/

export function calculateSankeyLayout(
data: DefaultizedSankeySeriesType['data'],
series: DefaultizedSankeySeriesType,
drawingArea: ChartDrawingArea,
theme: Theme,
series: Pick<SankeySeriesType, 'nodeOptions' | 'linkOptions' | 'iterations'> = {},
): SankeyLayout {
const { iterations = 6, nodeOptions, linkOptions } = series;
const { data, iterations = 6, nodeOptions, linkOptions } = series;
const {
width: nodeWidth = 15,
padding: nodePadding = 10,
Expand Down
23 changes: 22 additions & 1 deletion packages/x-charts-pro/src/SankeyChart/seriesConfig/index.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,37 @@
import type { ChartSeriesTypeConfig } from '@mui/x-charts/internals';
import type {
ChartSeriesTypeConfig,
SeriesProcessor,
SeriesProcessorResult,
} from '@mui/x-charts/internals';
import { getSeriesWithDefaultValues } from './getSeriesWithDefaultValues';
import { tooltipGetter } from './tooltipGetter';
import { calculateSankeyLayout } from '../calculateSankeyLayout';

// Simple passthrough functions for sankey chart
const seriesProcessor = (series: any) => series;
const colorProcessor = (series: any) => series;
const legendGetter = () => [];

const seriesProcessorWithDrawingArea: SeriesProcessor<'sankey'> = (series, drawingArea) => {
if (series.seriesOrder.length === 0) {
return series as SeriesProcessorResult<'sankey'>;
}
return {
series: {
[series.seriesOrder[0]]: {
...series.series[series.seriesOrder[0]],
sankeyLayout: calculateSankeyLayout(series.series[series.seriesOrder[0]], drawingArea),
},
},
seriesOrder: series.seriesOrder,
} satisfies SeriesProcessorResult<'sankey'>;
};

export const sankeySeriesConfig: ChartSeriesTypeConfig<'sankey'> = {
seriesProcessor,
colorProcessor,
legendGetter,
tooltipGetter,
getSeriesWithDefaultValues,
seriesProcessorWithDrawingArea,
};
4 changes: 4 additions & 0 deletions packages/x-charts-pro/src/typeOverloads/modules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
HeatmapValueType,
} from '../models/seriesType/heatmap';
import {
SankeyLayout,
SankeySeriesType,
type DefaultizedSankeySeriesType,
type SankeyItemIdentifier,
Expand All @@ -24,6 +25,7 @@ declare module '@mui/x-charts/internals' {
heatmap: {
seriesInput: DefaultizedProps<HeatmapSeriesType, 'id'>;
series: DefaultizedHeatmapSeriesType;
seriesComputedPosition: {};
seriesProp: HeatmapSeriesType;
itemIdentifier: HeatmapItemIdentifier;
itemIdentifierWithData: HeatmapItemIdentifier;
Expand All @@ -35,6 +37,7 @@ declare module '@mui/x-charts/internals' {
data: MakeRequired<FunnelValueType, 'color'>[];
};
series: DefaultizedFunnelSeriesType;
seriesComputedPosition: {};
seriesProp: FunnelSeriesType;
itemIdentifier: FunnelItemIdentifier;
itemIdentifierWithData: FunnelItemIdentifier;
Expand All @@ -44,6 +47,7 @@ declare module '@mui/x-charts/internals' {
sankey: {
seriesInput: DefaultizedSankeySeriesType;
series: DefaultizedSankeySeriesType;
seriesComputedPosition: { sankeyLayout: SankeyLayout };
seriesProp: SankeySeriesType;
itemIdentifier: SankeyItemIdentifier;
itemIdentifierWithData: SankeyItemIdentifierWithData;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ import type { DefaultizedBarSeriesType } from '../../../models';
import { getStackingGroups } from '../../../internals/stackSeries';
import { DatasetElementType, DatasetType } from '../../../models/seriesType/config';
import { SeriesId } from '../../../models/seriesType/common';
import { SeriesProcessor } from '../../../internals/plugins/models';
import { SeriesProcessorWithoutDimensions } from '../../../internals/plugins/models';

type BarDataset = DatasetType<number | null>;

const barValueFormatter = ((v) =>
v == null ? '' : v.toLocaleString()) as DefaultizedBarSeriesType['valueFormatter'];

const seriesProcessor: SeriesProcessor<'bar'> = (params, dataset) => {
const seriesProcessor: SeriesProcessorWithoutDimensions<'bar'> = (params, dataset) => {
const { seriesOrder, series } = params;
const stackingGroups = getStackingGroups(params);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import { getStackingGroups } from '../../internals/stackSeries';
import { ChartSeries, DatasetElementType, DatasetType } from '../../models/seriesType/config';
import { defaultizeValueFormatter } from '../../internals/defaultizeValueFormatter';
import { SeriesId } from '../../models/seriesType/common';
import { SeriesProcessor } from '../../internals/plugins/models';
import { SeriesProcessorWithoutDimensions } from '../../internals/plugins/models';

const seriesProcessor: SeriesProcessor<'line'> = (params, dataset) => {
const seriesProcessor: SeriesProcessorWithoutDimensions<'line'> = (params, dataset) => {
const { seriesOrder, series } = params;
const stackingGroups = getStackingGroups({ ...params, defaultStrategy: { stackOffset: 'none' } });

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { ChartSeriesDefaultized } from '../../models/seriesType/config';
import { ChartsPieSorting, PieValueType } from '../../models/seriesType/pie';
import { SeriesId } from '../../models/seriesType/common';
import { getLabel } from '../../internals/getLabel';
import { SeriesProcessor } from '../../internals/plugins/models';
import { SeriesProcessorWithoutDimensions } from '../../internals/plugins/models';
import { deg2rad } from '../../internals/angleConversion';

const getSortingComparator = (comparator: ChartsPieSorting = 'none') => {
Expand All @@ -22,7 +22,7 @@ const getSortingComparator = (comparator: ChartsPieSorting = 'none') => {
}
};

const seriesProcessor: SeriesProcessor<'pie'> = (params) => {
const seriesProcessor: SeriesProcessorWithoutDimensions<'pie'> = (params) => {
const { seriesOrder, series } = params;

const defaultizedSeries: Record<SeriesId, ChartSeriesDefaultized<'pie'>> = {};
Expand Down
4 changes: 2 additions & 2 deletions packages/x-charts/src/RadarChart/seriesConfig/formatter.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { defaultizeValueFormatter } from '../../internals/defaultizeValueFormatter';
import { SeriesProcessor } from '../../internals/plugins/models/seriesConfig';
import { SeriesProcessorWithoutDimensions } from '../../internals/plugins/models/seriesConfig';

const formatter: SeriesProcessor<'radar'> = (params) => {
const formatter: SeriesProcessorWithoutDimensions<'radar'> = (params) => {
const { seriesOrder, series } = params;

return {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { ScatterValueType } from '../../models';
import { SeriesProcessor } from '../../internals/plugins/models';
import { SeriesProcessorWithoutDimensions } from '../../internals/plugins/models';

const seriesProcessor: SeriesProcessor<'scatter'> = ({ series, seriesOrder }, dataset) => {
const seriesProcessor: SeriesProcessorWithoutDimensions<'scatter'> = (
{ series, seriesOrder },
dataset,
) => {
const completeSeries = Object.fromEntries(
Object.entries(series).map(([seriesId, seriesData]) => {
const datasetKeys = seriesData?.datasetKeys;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@ import { SeriesId } from '../../../../models/seriesType/common';
import { AllSeriesType } from '../../../../models/seriesType';
import { ChartSeriesType, DatasetType } from '../../../../models/seriesType/config';
import { ChartSeriesConfig } from '../../models/seriesConfig';
import { DefaultizedSeriesGroups, ProcessedSeries } from './useChartSeries.types';
import {
DefaultizedSeriesGroups,
ProcessedSeries,
ProcessedSeriesWithoutDimensions,
} from './useChartSeries.types';
import { ChartDrawingArea } from '../../../../hooks/useDrawingArea';

/**
* This method groups series by type and adds defaultized values such as the ids and colors.
Expand Down Expand Up @@ -55,7 +60,7 @@ export const defaultizeSeries = <TSeriesType extends ChartSeriesType>({
* @param dataset The optional dataset
* @returns Processed series with all transformations applied
*/
export const applySeriesProcessors = <TSeriesType extends ChartSeriesType>(
export const applySeriesProcessorsWithoutDimensions = <TSeriesType extends ChartSeriesType>(
defaultizedSeries: DefaultizedSeriesGroups<TSeriesType>,
seriesConfig: ChartSeriesConfig<TSeriesType>,
dataset?: Readonly<DatasetType>,
Expand All @@ -73,3 +78,37 @@ export const applySeriesProcessors = <TSeriesType extends ChartSeriesType>(

return processedSeries;
};

/**
* Applies series processors with drawing area to series if defined.
* @param processedSeries The processed series groups
* @param seriesConfig The series configuration
* @param drawingArea The drawing area
* @returns Processed series with all transformations applied
*/
export const applySeriesProcessors = <TSeriesType extends ChartSeriesType>(
processedSeries: ProcessedSeriesWithoutDimensions<TSeriesType>,
seriesConfig: ChartSeriesConfig<TSeriesType>,
drawingArea: ChartDrawingArea,
): ProcessedSeries<TSeriesType> => {
let processingDetected = false;
const modifiedProcessedSeries: ProcessedSeries<TSeriesType> = {};

// Apply processors on series type per group
(Object.keys(processedSeries) as TSeriesType[]).forEach((type) => {
const processor = seriesConfig[type]?.seriesProcessorWithDrawingArea;
if (processor !== undefined) {
const newValue = processor(processedSeries[type] as any, drawingArea);

if (newValue && newValue !== processedSeries[type]) {
processingDetected = true;
(modifiedProcessedSeries as any)[type] = newValue;
}
}
});

if (!processingDetected) {
return processedSeries as ProcessedSeries<TSeriesType>;
}
Comment on lines +110 to +112
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is processingDetected just to avoid breaking selectors' cache when processing results in the same value?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that way either not providing seriesProcessor() or having seriesProcessor=(x)=>x will be transparent

return { ...processedSeries, ...modifiedProcessedSeries };
};
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { createSelectorMemoized, createSelector } from '@mui/x-internals/store';
import { ChartRootSelector } from '../../utils/selectors';
import { UseChartSeriesSignature } from './useChartSeries.types';
import { applySeriesProcessors } from './processSeries';
import { applySeriesProcessors, applySeriesProcessorsWithoutDimensions } from './processSeries';
import { selectorChartDrawingArea } from '../useChartDimensions';

export const selectorChartSeriesState: ChartRootSelector<UseChartSeriesSignature> = (state) =>
state.series;
Expand All @@ -28,13 +29,28 @@ export const selectorChartDataset = createSelector(
/**
* Get the processed series after applying series processors.
* This selector computes the processed series on-demand from the defaultized series.
* It's a first step before applying the drawing-area-dependent processors.
* @returns {ProcessedSeries} The processed series.
*/
export const selectorChartSeriesProcessed = createSelectorMemoized(
export const selectorChartSeriesProcessedWithoutDimensions = createSelectorMemoized(
selectorChartDefaultizedSeries,
selectorChartSeriesConfig,
selectorChartDataset,
function selectorChartSeriesProcessed(defaultizedSeries, seriesConfig, dataset) {
return applySeriesProcessors(defaultizedSeries, seriesConfig, dataset);
function selectorChartSeriesProcessedWithoutDimensions(defaultizedSeries, seriesConfig, dataset) {
return applySeriesProcessorsWithoutDimensions(defaultizedSeries, seriesConfig, dataset);
},
);

/**
* Get the processed series after applying series processors.
* This selector computes the processed series on-demand from the defaultized series.
* @returns {ProcessedSeries} The processed series.
*/
export const selectorChartSeriesProcessed = createSelectorMemoized(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we are using the same selector, why not make the regular series processor use the drawing area?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With this approach if the drawing area is modified, but no series uses it the selectors is an identity function.

Not sure it's usefull. But if some selector uses the processed series and not the drawing area they should be able to avoid a recomputation. Maybe bernarod's work on the scatter zoom performance can benefit from it. Not sure

selectorChartSeriesProcessedWithoutDimensions,
selectorChartSeriesConfig,
selectorChartDrawingArea,
function selectorChartSeriesProcessed(processedSeries, seriesConfig, drawingArea) {
return applySeriesProcessors(processedSeries, seriesConfig, drawingArea);
},
);
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import { ChartSeriesType, DatasetType } from '../../../../models/seriesType/conf
import {
SeriesProcessorParams,
SeriesProcessorResult,
SeriesProcessorWithoutDimensionsResult,
} from '../../models/seriesConfig/seriesProcessor.types';
import { UseChartDimensionsSignature } from '../useChartDimensions';

export interface UseChartSeriesParameters<T extends ChartSeriesType = ChartSeriesType> {
/**
Expand Down Expand Up @@ -46,6 +48,12 @@ export type ProcessedSeries<TSeriesTypes extends ChartSeriesType = ChartSeriesTy
[type in TSeriesTypes]?: SeriesProcessorResult<type>;
};

export type ProcessedSeriesWithoutDimensions<
TSeriesTypes extends ChartSeriesType = ChartSeriesType,
> = {
[type in TSeriesTypes]?: SeriesProcessorWithoutDimensionsResult<type>;
};

export type DefaultizedSeriesGroups<TSeriesTypes extends ChartSeriesType = ChartSeriesType> = {
[type in TSeriesTypes]?: SeriesProcessorParams<type>;
};
Expand All @@ -66,4 +74,5 @@ export type UseChartSeriesSignature<SeriesType extends ChartSeriesType = ChartSe
defaultizedParams: UseChartSeriesDefaultizedParameters<SeriesType>;
state: UseChartSeriesState<SeriesType>;
instance: UseChartSeriesInstance;
dependencies: [UseChartDimensionsSignature];
}>;
Loading
Loading