@mui/x-charts 8.9.2 → 8.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/BarChart/BarChart.js +16 -0
  2. package/CHANGELOG.md +121 -6
  3. package/ChartContainer/ChartContainer.js +24 -0
  4. package/ChartsAxisHighlight/ChartsXAxisHighlight.js +1 -3
  5. package/ChartsAxisHighlight/ChartsYAxisHighlight.js +1 -3
  6. package/ChartsTooltip/ChartsTooltip.d.ts +2 -1
  7. package/ChartsTooltip/ChartsTooltip.js +3 -3
  8. package/ChartsTooltip/ChartsTooltipContainer.d.ts +5 -5
  9. package/ChartsTooltip/ChartsTooltipContainer.js +3 -3
  10. package/ChartsXAxis/ChartsGroupedXAxis.d.ts +7 -0
  11. package/ChartsXAxis/ChartsGroupedXAxis.js +142 -0
  12. package/ChartsXAxis/ChartsSingleXAxis.d.ts +7 -0
  13. package/ChartsXAxis/ChartsSingleXAxis.js +144 -0
  14. package/ChartsXAxis/ChartsXAxis.d.ts +1 -1
  15. package/ChartsXAxis/ChartsXAxis.js +8 -210
  16. package/ChartsXAxis/getVisibleLabels.d.ts +2 -2
  17. package/ChartsXAxis/useAxisProps.d.ts +4526 -0
  18. package/ChartsXAxis/useAxisProps.js +105 -0
  19. package/ChartsXAxis/utilities.d.ts +11 -0
  20. package/ChartsXAxis/utilities.js +43 -0
  21. package/LineChart/LineChart.js +16 -0
  22. package/RadarChart/index.d.ts +9 -2
  23. package/RadarChart/index.js +13 -14
  24. package/ScatterChart/ScatterChart.d.ts +8 -1
  25. package/ScatterChart/ScatterChart.js +16 -0
  26. package/SparkLineChart/SparkLineChart.js +16 -0
  27. package/esm/BarChart/BarChart.js +16 -0
  28. package/esm/ChartContainer/ChartContainer.js +24 -0
  29. package/esm/ChartsAxisHighlight/ChartsXAxisHighlight.js +1 -3
  30. package/esm/ChartsAxisHighlight/ChartsYAxisHighlight.js +1 -3
  31. package/esm/ChartsTooltip/ChartsTooltip.d.ts +2 -1
  32. package/esm/ChartsTooltip/ChartsTooltip.js +3 -3
  33. package/esm/ChartsTooltip/ChartsTooltipContainer.d.ts +5 -5
  34. package/esm/ChartsTooltip/ChartsTooltipContainer.js +3 -3
  35. package/esm/ChartsXAxis/ChartsGroupedXAxis.d.ts +7 -0
  36. package/esm/ChartsXAxis/ChartsGroupedXAxis.js +136 -0
  37. package/esm/ChartsXAxis/ChartsSingleXAxis.d.ts +7 -0
  38. package/esm/ChartsXAxis/ChartsSingleXAxis.js +140 -0
  39. package/esm/ChartsXAxis/ChartsXAxis.d.ts +1 -1
  40. package/esm/ChartsXAxis/ChartsXAxis.js +7 -207
  41. package/esm/ChartsXAxis/getVisibleLabels.d.ts +2 -2
  42. package/esm/ChartsXAxis/useAxisProps.d.ts +4526 -0
  43. package/esm/ChartsXAxis/useAxisProps.js +98 -0
  44. package/esm/ChartsXAxis/utilities.d.ts +11 -0
  45. package/esm/ChartsXAxis/utilities.js +35 -0
  46. package/esm/LineChart/LineChart.js +16 -0
  47. package/esm/RadarChart/index.d.ts +9 -2
  48. package/esm/RadarChart/index.js +12 -2
  49. package/esm/ScatterChart/ScatterChart.d.ts +8 -1
  50. package/esm/ScatterChart/ScatterChart.js +16 -0
  51. package/esm/SparkLineChart/SparkLineChart.js +16 -0
  52. package/esm/hooks/useTicksGrouped.d.ts +28 -0
  53. package/esm/hooks/useTicksGrouped.js +98 -0
  54. package/esm/index.js +1 -1
  55. package/esm/internals/getScale.d.ts +1 -1
  56. package/esm/internals/plugins/featurePlugins/useChartCartesianAxis/createAxisFilterMapper.d.ts +3 -1
  57. package/esm/internals/plugins/featurePlugins/useChartCartesianAxis/createAxisFilterMapper.js +32 -23
  58. package/esm/internals/plugins/featurePlugins/useChartCartesianAxis/getAxisExtremum.d.ts +1 -1
  59. package/esm/models/axis.d.ts +61 -0
  60. package/hooks/useTicksGrouped.d.ts +28 -0
  61. package/hooks/useTicksGrouped.js +104 -0
  62. package/index.js +1 -1
  63. package/internals/getScale.d.ts +1 -1
  64. package/internals/plugins/featurePlugins/useChartCartesianAxis/createAxisFilterMapper.d.ts +3 -1
  65. package/internals/plugins/featurePlugins/useChartCartesianAxis/createAxisFilterMapper.js +34 -23
  66. package/internals/plugins/featurePlugins/useChartCartesianAxis/getAxisExtremum.d.ts +1 -1
  67. package/models/axis.d.ts +61 -0
  68. package/package.json +3 -4
@@ -0,0 +1,98 @@
1
+ 'use client';
2
+
3
+ import _extends from "@babel/runtime/helpers/esm/extends";
4
+ import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/esm/objectWithoutPropertiesLoose";
5
+ const _excluded = ["scale", "tickNumber", "reverse"];
6
+ import useSlotProps from '@mui/utils/useSlotProps';
7
+ import { useThemeProps, useTheme } from '@mui/material/styles';
8
+ import { useRtl } from '@mui/system/RtlProvider';
9
+ import { ChartsText } from "../ChartsText/index.js";
10
+ import { useXAxes } from "../hooks/useAxis.js";
11
+ import { getDefaultBaseline, getDefaultTextAnchor } from "../ChartsText/defaultTextPlacement.js";
12
+ import { invertTextAnchor } from "../internals/invertTextAnchor.js";
13
+ import { defaultProps, useUtilityClasses } from "./utilities.js";
14
+ import { isBandScale } from "../internals/isBandScale.js";
15
+ import { isInfinity } from "../internals/isInfinity.js";
16
+ export const useAxisProps = inProps => {
17
+ const {
18
+ xAxis,
19
+ xAxisIds
20
+ } = useXAxes();
21
+ const _xAxis = xAxis[inProps.axisId ?? xAxisIds[0]],
22
+ {
23
+ scale: xScale,
24
+ tickNumber,
25
+ reverse
26
+ } = _xAxis,
27
+ settings = _objectWithoutPropertiesLoose(_xAxis, _excluded);
28
+
29
+ // eslint-disable-next-line material-ui/mui-name-matches-component-name
30
+ const themedProps = useThemeProps({
31
+ props: _extends({}, settings, inProps),
32
+ name: 'MuiChartsXAxis'
33
+ });
34
+ const defaultizedProps = _extends({}, defaultProps, themedProps);
35
+ const {
36
+ position,
37
+ tickLabelStyle,
38
+ labelStyle,
39
+ slots,
40
+ slotProps
41
+ } = defaultizedProps;
42
+ const theme = useTheme();
43
+ const isRtl = useRtl();
44
+ const classes = useUtilityClasses(defaultizedProps);
45
+ const positionSign = position === 'bottom' ? 1 : -1;
46
+ const Line = slots?.axisLine ?? 'line';
47
+ const Tick = slots?.axisTick ?? 'line';
48
+ const TickLabel = slots?.axisTickLabel ?? ChartsText;
49
+ const Label = slots?.axisLabel ?? ChartsText;
50
+ const defaultTextAnchor = getDefaultTextAnchor((position === 'bottom' ? 0 : 180) - (tickLabelStyle?.angle ?? 0));
51
+ const defaultDominantBaseline = getDefaultBaseline((position === 'bottom' ? 0 : 180) - (tickLabelStyle?.angle ?? 0));
52
+ const axisTickLabelProps = useSlotProps({
53
+ elementType: TickLabel,
54
+ externalSlotProps: slotProps?.axisTickLabel,
55
+ additionalProps: {
56
+ style: _extends({}, theme.typography.caption, {
57
+ fontSize: 12,
58
+ lineHeight: 1.25,
59
+ textAnchor: isRtl ? invertTextAnchor(defaultTextAnchor) : defaultTextAnchor,
60
+ dominantBaseline: defaultDominantBaseline
61
+ }, tickLabelStyle)
62
+ },
63
+ className: classes.tickLabel,
64
+ ownerState: {}
65
+ });
66
+ const axisLabelProps = useSlotProps({
67
+ elementType: Label,
68
+ externalSlotProps: slotProps?.axisLabel,
69
+ additionalProps: {
70
+ style: _extends({}, theme.typography.body1, {
71
+ lineHeight: 1,
72
+ fontSize: 14,
73
+ textAnchor: 'middle',
74
+ dominantBaseline: position === 'bottom' ? 'text-after-edge' : 'text-before-edge'
75
+ }, labelStyle)
76
+ },
77
+ ownerState: {}
78
+ });
79
+ const domain = xScale.domain();
80
+ const isScaleBand = isBandScale(xScale);
81
+ const skipAxisRendering = isScaleBand && domain.length === 0 || !isScaleBand && domain.some(isInfinity) || position === 'none';
82
+ return {
83
+ xScale,
84
+ defaultizedProps,
85
+ tickNumber,
86
+ positionSign,
87
+ skipAxisRendering,
88
+ classes,
89
+ Line,
90
+ Tick,
91
+ TickLabel,
92
+ Label,
93
+ axisTickLabelProps,
94
+ axisLabelProps,
95
+ reverse,
96
+ isRtl
97
+ };
98
+ };
@@ -0,0 +1,11 @@
1
+ import { AxisConfig, ChartsXAxisProps } from "../models/axis.js";
2
+ export declare const useUtilityClasses: (ownerState: AxisConfig<any, any, ChartsXAxisProps>) => Record<"root" | "line" | "label" | "tickContainer" | "tick" | "tickLabel", string>;
3
+ export declare const TICK_LABEL_GAP = 3;
4
+ export declare const AXIS_LABEL_TICK_LABEL_GAP = 4;
5
+ export declare const XAxisRoot: import("@emotion/styled").StyledComponent<Pick<import("@mui/system").MUIStyledCommonProps<import("@mui/material/styles").Theme> & Pick<import("react").SVGProps<SVGGElement>, keyof import("react").SVGProps<SVGGElement>>, keyof import("react").SVGProps<SVGGElement> | keyof import("@mui/system").MUIStyledCommonProps<import("@mui/material/styles").Theme>> & import("@mui/system").MUIStyledCommonProps<import("@mui/material/styles").Theme>, {}, {}>;
6
+ export declare const defaultProps: {
7
+ readonly disableLine: false;
8
+ readonly disableTicks: false;
9
+ readonly tickSize: 6;
10
+ readonly tickLabelMinGap: 4;
11
+ };
@@ -0,0 +1,35 @@
1
+ import composeClasses from '@mui/utils/composeClasses';
2
+ import { styled } from '@mui/material/styles';
3
+ import { getAxisUtilityClass } from "../ChartsAxis/axisClasses.js";
4
+ import { AxisRoot } from "../internals/components/AxisSharedComponents.js";
5
+ export const useUtilityClasses = ownerState => {
6
+ const {
7
+ classes,
8
+ position,
9
+ id
10
+ } = ownerState;
11
+ const slots = {
12
+ root: ['root', 'directionX', position, `id-${id}`],
13
+ line: ['line'],
14
+ tickContainer: ['tickContainer'],
15
+ tick: ['tick'],
16
+ tickLabel: ['tickLabel'],
17
+ label: ['label']
18
+ };
19
+ return composeClasses(slots, getAxisUtilityClass, classes);
20
+ };
21
+
22
+ /* Gap between a tick and its label. */
23
+ export const TICK_LABEL_GAP = 3;
24
+ /* Gap between the axis label and tick labels. */
25
+ export const AXIS_LABEL_TICK_LABEL_GAP = 4;
26
+ export const XAxisRoot = styled(AxisRoot, {
27
+ name: 'MuiChartsXAxis',
28
+ slot: 'Root'
29
+ })({});
30
+ export const defaultProps = {
31
+ disableLine: false,
32
+ disableTicks: false,
33
+ tickSize: 6,
34
+ tickLabelMinGap: 4
35
+ };
@@ -270,6 +270,10 @@ process.env.NODE_ENV !== "production" ? LineChart.propTypes = {
270
270
  disableTicks: PropTypes.bool,
271
271
  domainLimit: PropTypes.oneOfType([PropTypes.oneOf(['nice', 'strict']), PropTypes.func]),
272
272
  fill: PropTypes.string,
273
+ groups: PropTypes.arrayOf(PropTypes.shape({
274
+ getValue: PropTypes.func.isRequired,
275
+ tickSize: PropTypes.number
276
+ })),
273
277
  height: PropTypes.number,
274
278
  hideTooltip: PropTypes.bool,
275
279
  id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
@@ -321,6 +325,10 @@ process.env.NODE_ENV !== "production" ? LineChart.propTypes = {
321
325
  disableTicks: PropTypes.bool,
322
326
  domainLimit: PropTypes.oneOfType([PropTypes.oneOf(['nice', 'strict']), PropTypes.func]),
323
327
  fill: PropTypes.string,
328
+ groups: PropTypes.arrayOf(PropTypes.shape({
329
+ getValue: PropTypes.func.isRequired,
330
+ tickSize: PropTypes.number
331
+ })),
324
332
  height: PropTypes.number,
325
333
  hideTooltip: PropTypes.bool,
326
334
  id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
@@ -703,6 +711,10 @@ process.env.NODE_ENV !== "production" ? LineChart.propTypes = {
703
711
  disableTicks: PropTypes.bool,
704
712
  domainLimit: PropTypes.oneOfType([PropTypes.oneOf(['nice', 'strict']), PropTypes.func]),
705
713
  fill: PropTypes.string,
714
+ groups: PropTypes.arrayOf(PropTypes.shape({
715
+ getValue: PropTypes.func.isRequired,
716
+ tickSize: PropTypes.number
717
+ })),
706
718
  hideTooltip: PropTypes.bool,
707
719
  id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
708
720
  ignoreTooltip: PropTypes.bool,
@@ -753,6 +765,10 @@ process.env.NODE_ENV !== "production" ? LineChart.propTypes = {
753
765
  disableTicks: PropTypes.bool,
754
766
  domainLimit: PropTypes.oneOfType([PropTypes.oneOf(['nice', 'strict']), PropTypes.func]),
755
767
  fill: PropTypes.string,
768
+ groups: PropTypes.arrayOf(PropTypes.shape({
769
+ getValue: PropTypes.func.isRequired,
770
+ tickSize: PropTypes.number
771
+ })),
756
772
  hideTooltip: PropTypes.bool,
757
773
  id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
758
774
  ignoreTooltip: PropTypes.bool,
@@ -1,7 +1,14 @@
1
- export { RadarChart as Unstable_RadarChart } from "./RadarChart.js";
1
+ import { RadarDataProvider } from "./RadarDataProvider/index.js";
2
2
  export { RadarChart } from "./RadarChart.js";
3
- export { RadarDataProvider as Unstable_RadarDataProvider } from "./RadarDataProvider/index.js";
3
+ /**
4
+ * @deprecated radar chart is now stable, import `RadarChart` instead
5
+ */
6
+ export declare const Unstable_RadarChart: import("react").ForwardRefExoticComponent<import("./RadarChart.js").RadarChartProps & import("react").RefAttributes<SVGSVGElement>>;
4
7
  export { RadarDataProvider } from "./RadarDataProvider/index.js";
8
+ /**
9
+ * @deprecated radar data provider is now stable, import `RadarDataProvider` instead
10
+ */
11
+ export declare const Unstable_RadarDataProvider: typeof RadarDataProvider;
5
12
  export type { RadarChartProps, RadarChartSlots, RadarChartSlotProps } from "./RadarChart.js";
6
13
  export type { RadarDataProviderProps, RadarSeries } from "./RadarDataProvider/index.js";
7
14
  export * from "./RadarGrid/index.js";
@@ -1,7 +1,17 @@
1
- export { RadarChart as Unstable_RadarChart } from "./RadarChart.js";
1
+ import { RadarChart } from "./RadarChart.js";
2
+ import { RadarDataProvider } from "./RadarDataProvider/index.js";
2
3
  export { RadarChart } from "./RadarChart.js";
3
- export { RadarDataProvider as Unstable_RadarDataProvider } from "./RadarDataProvider/index.js";
4
+ /**
5
+ * @deprecated radar chart is now stable, import `RadarChart` instead
6
+ */
7
+ // eslint-disable-next-line @typescript-eslint/naming-convention
8
+ export const Unstable_RadarChart = RadarChart;
4
9
  export { RadarDataProvider } from "./RadarDataProvider/index.js";
10
+ /**
11
+ * @deprecated radar data provider is now stable, import `RadarDataProvider` instead
12
+ */
13
+ // eslint-disable-next-line @typescript-eslint/naming-convention
14
+ export const Unstable_RadarDataProvider = RadarDataProvider;
5
15
  export * from "./RadarGrid/index.js";
6
16
  export * from "./RadarAxisHighlight/index.js";
7
17
  export * from "./RadarMetricLabels/index.js";
@@ -6,6 +6,7 @@ import { ScatterPlotProps, ScatterPlotSlotProps, ScatterPlotSlots } from "./Scat
6
6
  import { ChartContainerProps } from "../ChartContainer/index.js";
7
7
  import { ChartsAxisProps } from "../ChartsAxis/index.js";
8
8
  import { ScatterSeriesType } from "../models/seriesType/scatter.js";
9
+ import { ChartsTooltipProps } from "../ChartsTooltip/index.js";
9
10
  import { ChartsTooltipSlots, ChartsTooltipSlotProps } from "../ChartsTooltip/ChartTooltip.types.js";
10
11
  import { ChartsLegendSlotProps, ChartsLegendSlots } from "../ChartsLegend/index.js";
11
12
  import { ChartsOverlayProps, ChartsOverlaySlotProps, ChartsOverlaySlots } from "../ChartsOverlay/index.js";
@@ -15,7 +16,13 @@ import { ChartsGridProps } from "../ChartsGrid/index.js";
15
16
  import { UseChartVoronoiSignature } from "../internals/plugins/featurePlugins/useChartVoronoi/index.js";
16
17
  import { ScatterChartPluginsSignatures } from "./ScatterChart.plugins.js";
17
18
  export interface ScatterChartSlots extends ChartsAxisSlots, ScatterPlotSlots, ChartsLegendSlots, ChartsOverlaySlots, ChartsTooltipSlots, ChartsToolbarSlots, Partial<ChartsSlots> {}
18
- export interface ScatterChartSlotProps extends ChartsAxisSlotProps, ScatterPlotSlotProps, ChartsLegendSlotProps, ChartsOverlaySlotProps, ChartsTooltipSlotProps, ChartsToolbarSlotProps, Partial<ChartsSlotProps> {}
19
+ export interface ScatterChartSlotProps extends ChartsAxisSlotProps, ScatterPlotSlotProps, ChartsLegendSlotProps, ChartsOverlaySlotProps, Omit<ChartsTooltipSlotProps, 'tooltip'>, ChartsToolbarSlotProps, Partial<ChartsSlotProps> {
20
+ /**
21
+ * Slot props for the tooltip component.
22
+ * @default {}
23
+ */
24
+ tooltip?: Partial<ChartsTooltipProps<'item' | 'none'>>;
25
+ }
19
26
  export type ScatterSeries = MakeOptional<ScatterSeriesType, 'type'>;
20
27
  export interface ScatterChartProps extends Omit<ChartContainerProps<'scatter', ScatterChartPluginsSignatures>, 'series' | 'plugins' | 'onItemClick' | 'experimentalFeatures' | 'highlightedAxis' | 'onHighlightedAxisChange'>, Omit<ChartsAxisProps, 'slots' | 'slotProps'>, Omit<ChartsOverlayProps, 'slots' | 'slotProps'> {
21
28
  /**
@@ -239,6 +239,10 @@ process.env.NODE_ENV !== "production" ? ScatterChart.propTypes = {
239
239
  disableTicks: PropTypes.bool,
240
240
  domainLimit: PropTypes.oneOfType([PropTypes.oneOf(['nice', 'strict']), PropTypes.func]),
241
241
  fill: PropTypes.string,
242
+ groups: PropTypes.arrayOf(PropTypes.shape({
243
+ getValue: PropTypes.func.isRequired,
244
+ tickSize: PropTypes.number
245
+ })),
242
246
  height: PropTypes.number,
243
247
  hideTooltip: PropTypes.bool,
244
248
  id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
@@ -290,6 +294,10 @@ process.env.NODE_ENV !== "production" ? ScatterChart.propTypes = {
290
294
  disableTicks: PropTypes.bool,
291
295
  domainLimit: PropTypes.oneOfType([PropTypes.oneOf(['nice', 'strict']), PropTypes.func]),
292
296
  fill: PropTypes.string,
297
+ groups: PropTypes.arrayOf(PropTypes.shape({
298
+ getValue: PropTypes.func.isRequired,
299
+ tickSize: PropTypes.number
300
+ })),
293
301
  height: PropTypes.number,
294
302
  hideTooltip: PropTypes.bool,
295
303
  id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
@@ -672,6 +680,10 @@ process.env.NODE_ENV !== "production" ? ScatterChart.propTypes = {
672
680
  disableTicks: PropTypes.bool,
673
681
  domainLimit: PropTypes.oneOfType([PropTypes.oneOf(['nice', 'strict']), PropTypes.func]),
674
682
  fill: PropTypes.string,
683
+ groups: PropTypes.arrayOf(PropTypes.shape({
684
+ getValue: PropTypes.func.isRequired,
685
+ tickSize: PropTypes.number
686
+ })),
675
687
  hideTooltip: PropTypes.bool,
676
688
  id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
677
689
  ignoreTooltip: PropTypes.bool,
@@ -722,6 +734,10 @@ process.env.NODE_ENV !== "production" ? ScatterChart.propTypes = {
722
734
  disableTicks: PropTypes.bool,
723
735
  domainLimit: PropTypes.oneOfType([PropTypes.oneOf(['nice', 'strict']), PropTypes.func]),
724
736
  fill: PropTypes.string,
737
+ groups: PropTypes.arrayOf(PropTypes.shape({
738
+ getValue: PropTypes.func.isRequired,
739
+ tickSize: PropTypes.number
740
+ })),
725
741
  hideTooltip: PropTypes.bool,
726
742
  id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
727
743
  ignoreTooltip: PropTypes.bool,
@@ -353,6 +353,10 @@ process.env.NODE_ENV !== "production" ? SparkLineChart.propTypes = {
353
353
  disableTicks: PropTypes.bool,
354
354
  domainLimit: PropTypes.oneOfType([PropTypes.oneOf(['nice', 'strict']), PropTypes.func]),
355
355
  fill: PropTypes.string,
356
+ groups: PropTypes.arrayOf(PropTypes.shape({
357
+ getValue: PropTypes.func.isRequired,
358
+ tickSize: PropTypes.number
359
+ })),
356
360
  height: PropTypes.number,
357
361
  hideTooltip: PropTypes.bool,
358
362
  id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
@@ -404,6 +408,10 @@ process.env.NODE_ENV !== "production" ? SparkLineChart.propTypes = {
404
408
  disableTicks: PropTypes.bool,
405
409
  domainLimit: PropTypes.oneOfType([PropTypes.oneOf(['nice', 'strict']), PropTypes.func]),
406
410
  fill: PropTypes.string,
411
+ groups: PropTypes.arrayOf(PropTypes.shape({
412
+ getValue: PropTypes.func.isRequired,
413
+ tickSize: PropTypes.number
414
+ })),
407
415
  height: PropTypes.number,
408
416
  hideTooltip: PropTypes.bool,
409
417
  id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
@@ -785,6 +793,10 @@ process.env.NODE_ENV !== "production" ? SparkLineChart.propTypes = {
785
793
  disableTicks: PropTypes.bool,
786
794
  domainLimit: PropTypes.oneOfType([PropTypes.oneOf(['nice', 'strict']), PropTypes.func]),
787
795
  fill: PropTypes.string,
796
+ groups: PropTypes.arrayOf(PropTypes.shape({
797
+ getValue: PropTypes.func.isRequired,
798
+ tickSize: PropTypes.number
799
+ })),
788
800
  hideTooltip: PropTypes.bool,
789
801
  id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
790
802
  ignoreTooltip: PropTypes.bool,
@@ -835,6 +847,10 @@ process.env.NODE_ENV !== "production" ? SparkLineChart.propTypes = {
835
847
  disableTicks: PropTypes.bool,
836
848
  domainLimit: PropTypes.oneOfType([PropTypes.oneOf(['nice', 'strict']), PropTypes.func]),
837
849
  fill: PropTypes.string,
850
+ groups: PropTypes.arrayOf(PropTypes.shape({
851
+ getValue: PropTypes.func.isRequired,
852
+ tickSize: PropTypes.number
853
+ })),
838
854
  hideTooltip: PropTypes.bool,
839
855
  id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
840
856
  ignoreTooltip: PropTypes.bool,
@@ -0,0 +1,28 @@
1
+ import type { ScaleBand, ScalePoint } from '@mui/x-charts-vendor/d3-scale';
2
+ import { AxisConfig, type AxisGroup } from "../models/axis.js";
3
+ import type { TickParams } from "./useTicks.js";
4
+ export type GroupedTickItemType = {
5
+ /**
6
+ * This property is undefined only if it's the tick closing the last band
7
+ */
8
+ value?: any;
9
+ formattedValue?: string;
10
+ offset: number;
11
+ labelOffset: number;
12
+ /**
13
+ * In band scales, we remove some redundant ticks.
14
+ */
15
+ ignoreTick?: boolean;
16
+ dataIndex?: number;
17
+ /**
18
+ * The index of the group this tick belongs to. If `getGrouping` returns `[[0, 1], [2, 3]]`
19
+ * both ticks with value `0` and `2` will have `groupIndex: 0`, and both ticks with value `1` and `3` will have `groupIndex: 1`.
20
+ */
21
+ groupIndex?: number;
22
+ };
23
+ export declare function useTicksGrouped(options: {
24
+ scale: ScaleBand<any> | ScalePoint<any>;
25
+ valueFormatter?: AxisConfig['valueFormatter'];
26
+ direction: 'x' | 'y';
27
+ groups: AxisGroup[];
28
+ } & Pick<TickParams, 'tickNumber' | 'tickInterval' | 'tickPlacement' | 'tickLabelPlacement'>): GroupedTickItemType[];
@@ -0,0 +1,98 @@
1
+ 'use client';
2
+
3
+ import * as React from 'react';
4
+ import { isBandScale } from "../internals/isBandScale.js";
5
+ const offsetRatio = {
6
+ start: 0,
7
+ extremities: 0,
8
+ end: 1,
9
+ middle: 0.5,
10
+ tick: 0
11
+ };
12
+ export function useTicksGrouped(options) {
13
+ const {
14
+ scale,
15
+ tickInterval,
16
+ tickLabelPlacement = 'middle',
17
+ tickPlacement = 'extremities',
18
+ groups
19
+ } = options;
20
+ return React.useMemo(() => {
21
+ const domain = scale.domain();
22
+ const filteredDomain = typeof tickInterval === 'function' && domain.filter(tickInterval) || typeof tickInterval === 'object' && tickInterval || domain;
23
+ if (scale.bandwidth() > 0) {
24
+ // scale type = 'band'
25
+ const entries = mapToGrouping(filteredDomain, groups, tickPlacement, tickLabelPlacement, scale);
26
+ if (entries[0]) {
27
+ entries[0].ignoreTick = true;
28
+ }
29
+ return [{
30
+ formattedValue: undefined,
31
+ offset: scale.range()[0],
32
+ labelOffset: 0,
33
+ groupIndex: groups.length - 1
34
+ }, ...entries,
35
+ // Last tick
36
+ {
37
+ formattedValue: undefined,
38
+ offset: scale.range()[1],
39
+ labelOffset: 0,
40
+ groupIndex: groups.length - 1
41
+ }];
42
+ }
43
+
44
+ // scale type = 'point'
45
+ return mapToGrouping(filteredDomain, groups, tickPlacement, tickLabelPlacement, scale);
46
+ }, [scale, tickInterval, groups, tickPlacement, tickLabelPlacement]);
47
+ }
48
+ function mapToGrouping(tickValues, groups, tickPlacement, tickLabelPlacement, scale) {
49
+ const allTickItems = [];
50
+ // Map to keep track of offsets and their corresponding tick indexes
51
+ // Used to remove redundant ticks when they are in the same position
52
+ const dataIndexToTickIndex = new Map();
53
+ let currentValueCount = 0;
54
+ for (let groupIndex = 0; groupIndex < groups.length; groupIndex += 1) {
55
+ for (let dataIndex = 0; dataIndex < tickValues.length; dataIndex += 1) {
56
+ const tickValue = tickValues[dataIndex];
57
+ const groupValue = groups[groupIndex].getValue(tickValue, dataIndex);
58
+ const lastItem = allTickItems[allTickItems.length - 1];
59
+
60
+ // Check if this is a new unique value for this group
61
+ const isNew = lastItem?.value !== groupValue || lastItem?.groupIndex !== groupIndex;
62
+ if (isNew) {
63
+ currentValueCount = 1;
64
+ // Calculate tick offset
65
+ const tickOffset = isBandScale(scale) ? scale(tickValue) - (scale.step() - scale.bandwidth()) / 2 + offsetRatio[tickPlacement] * scale.step() : scale(tickValue);
66
+
67
+ // Calculate the label offset
68
+ const labelOffset = scale.step() * currentValueCount * (offsetRatio[tickLabelPlacement] - offsetRatio[tickPlacement]);
69
+
70
+ // Add a new item
71
+ allTickItems.push({
72
+ value: groupValue,
73
+ formattedValue: `${groupValue}`,
74
+ offset: tickOffset,
75
+ groupIndex,
76
+ dataIndex,
77
+ ignoreTick: false,
78
+ labelOffset
79
+ });
80
+ if (!dataIndexToTickIndex.has(dataIndex)) {
81
+ dataIndexToTickIndex.set(dataIndex, new Set());
82
+ }
83
+ const tickIndexes = dataIndexToTickIndex.get(dataIndex);
84
+ for (const previousIndex of tickIndexes.values()) {
85
+ allTickItems[previousIndex].ignoreTick = true;
86
+ }
87
+ tickIndexes.add(allTickItems.length - 1);
88
+ } else {
89
+ currentValueCount += 1;
90
+
91
+ // Calculate the label offset
92
+ const labelOffset = scale.step() * currentValueCount * (offsetRatio[tickLabelPlacement] - offsetRatio[tickPlacement]);
93
+ lastItem.labelOffset = labelOffset;
94
+ }
95
+ }
96
+ }
97
+ return allTickItems;
98
+ }
package/esm/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @mui/x-charts v8.9.2
2
+ * @mui/x-charts v8.10.0
3
3
  *
4
4
  * @license MIT
5
5
  * This source code is licensed under the MIT license found in the
@@ -1,2 +1,2 @@
1
1
  import { ContinuousScaleName, D3ContinuousScale } from "../models/axis.js";
2
- export declare function getScale(scaleType: ContinuousScaleName, domain: any[], range: any[]): D3ContinuousScale;
2
+ export declare function getScale(scaleType: ContinuousScaleName, domain: readonly any[], range: readonly any[]): D3ContinuousScale;
@@ -1,4 +1,4 @@
1
- import { AxisId, ChartsXAxisProps, ChartsYAxisProps, ScaleName } from "../../../../models/axis.js";
1
+ import { AxisId, ChartsXAxisProps, ChartsYAxisProps, ContinuousScaleName, ScaleName } from "../../../../models/axis.js";
2
2
  import { CartesianChartSeriesType } from "../../../../models/seriesType/config.js";
3
3
  import { ProcessedSeries } from "../../corePlugins/useChartSeries/index.js";
4
4
  import { AxisConfig } from "../../../../models/index.js";
@@ -19,4 +19,6 @@ export declare function createAxisFilterMapper(params: {
19
19
  formattedSeries: ProcessedSeries;
20
20
  direction: 'y';
21
21
  }): (axis: AxisConfig<ScaleName, any, ChartsYAxisProps>, axisIndex: number) => ExtremumFilter | null;
22
+ export declare function createDiscreteScaleGetAxisFilter(axisData: AxisConfig['data'], zoomStart: number, zoomEnd: number, direction: 'x' | 'y'): ExtremumFilter;
23
+ export declare function createContinuousScaleGetAxisFilter(scaleType: ContinuousScaleName | undefined, extrema: readonly [number, number], zoomStart: number, zoomEnd: number, direction: 'x' | 'y', axisData: AxisConfig['data']): ExtremumFilter;
22
24
  export declare const createGetAxisFilters: (filters: ZoomAxisFilters) => GetZoomAxisFilters;
@@ -18,32 +18,41 @@ export function createAxisFilterMapper({
18
18
  // No zoom, or zoom with all data visible
19
19
  return null;
20
20
  }
21
- let extremums = [];
22
21
  const scaleType = axis.scaleType;
23
22
  if (scaleType === 'point' || scaleType === 'band') {
24
- extremums = [0, (axis.data?.length ?? 1) - 1];
25
- } else {
26
- extremums = getAxisExtremum(axis, direction, seriesConfig, axisIndex, formattedSeries);
23
+ return createDiscreteScaleGetAxisFilter(axis.data, zoom.start, zoom.end, direction);
27
24
  }
28
- let min;
29
- let max;
30
- const continuousScaleType = !scaleType || scaleType === 'band' || scaleType === 'point' ? 'linear' : scaleType;
31
- [min, max] = getScale(continuousScaleType, extremums, [0, 100]).nice().domain();
32
- min = min instanceof Date ? min.getTime() : min;
33
- max = max instanceof Date ? max.getTime() : max;
34
- const minVal = min + zoom.start * (max - min) / 100;
35
- const maxVal = min + zoom.end * (max - min) / 100;
36
- return (value, dataIndex) => {
37
- const val = value[direction] ?? axis.data?.[dataIndex];
38
- if (val == null) {
39
- // If the value does not exist because of missing data point, or out of range index, we just ignore.
40
- return true;
41
- }
42
- if (axis.scaleType === 'point' || axis.scaleType === 'band' || typeof val === 'string') {
43
- return dataIndex >= minVal && dataIndex <= maxVal;
44
- }
45
- return val >= minVal && val <= maxVal;
46
- };
25
+ return createContinuousScaleGetAxisFilter(scaleType, getAxisExtremum(axis, direction, seriesConfig, axisIndex, formattedSeries), zoom.start, zoom.end, direction, axis.data);
26
+ };
27
+ }
28
+ export function createDiscreteScaleGetAxisFilter(axisData, zoomStart, zoomEnd, direction) {
29
+ const maxIndex = axisData?.length ?? 0;
30
+ const minVal = Math.floor(zoomStart * maxIndex / 100);
31
+ const maxVal = Math.ceil(zoomEnd * maxIndex / 100);
32
+ return function filterAxis(value, dataIndex) {
33
+ const val = value[direction] ?? axisData?.[dataIndex];
34
+ if (val == null) {
35
+ // If the value does not exist because of missing data point, or out of range index, we just ignore.
36
+ return true;
37
+ }
38
+ return dataIndex >= minVal && dataIndex < maxVal;
39
+ };
40
+ }
41
+ export function createContinuousScaleGetAxisFilter(scaleType, extrema, zoomStart, zoomEnd, direction, axisData) {
42
+ let min;
43
+ let max;
44
+ [min, max] = getScale(scaleType ?? 'linear', extrema, [0, 100]).nice().domain();
45
+ min = min instanceof Date ? min.getTime() : min;
46
+ max = max instanceof Date ? max.getTime() : max;
47
+ const minVal = min + zoomStart * (max - min) / 100;
48
+ const maxVal = min + zoomEnd * (max - min) / 100;
49
+ return function filterAxis(value, dataIndex) {
50
+ const val = value[direction] ?? axisData?.[dataIndex];
51
+ if (val == null) {
52
+ // If the value does not exist because of missing data point, or out of range index, we just ignore.
53
+ return true;
54
+ }
55
+ return val >= minVal && val <= maxVal;
47
56
  };
48
57
  }
49
58
  export const createGetAxisFilters = filters => ({
@@ -3,4 +3,4 @@ import { CartesianChartSeriesType } from "../../../../models/seriesType/config.j
3
3
  import { ChartSeriesConfig } from "../../models/seriesConfig/index.js";
4
4
  import { ProcessedSeries } from "../../corePlugins/useChartSeries/useChartSeries.types.js";
5
5
  import { GetZoomAxisFilters } from "./zoom.types.js";
6
- export declare const getAxisExtremum: <T extends CartesianChartSeriesType>(axis: AxisConfig, axisDirection: "x" | "y", seriesConfig: ChartSeriesConfig<T>, axisIndex: number, formattedSeries: ProcessedSeries<T>, getFilters?: GetZoomAxisFilters) => number[];
6
+ export declare const getAxisExtremum: <T extends CartesianChartSeriesType>(axis: AxisConfig, axisDirection: "x" | "y", seriesConfig: ChartSeriesConfig<T>, axisIndex: number, formattedSeries: ProcessedSeries<T>, getFilters?: GetZoomAxisFilters) => [number, number];
@@ -180,6 +180,21 @@ export interface ChartsRadiusAxisProps extends ChartsAxisProps {
180
180
  }
181
181
  export type ScaleName = keyof AxisScaleConfig;
182
182
  export type ContinuousScaleName = 'linear' | 'log' | 'symlog' | 'pow' | 'sqrt' | 'time' | 'utc';
183
+ export type AxisGroup = {
184
+ /**
185
+ * The function used to return the value for this group.
186
+ *
187
+ * @param {any} value The value of the axis item.
188
+ * @param {number} dataIndex The index of the data item.
189
+ * @returns {string | number | Date} The value that will be used to group the axis items.
190
+ */
191
+ getValue: (value: any, dataIndex: number) => string | number | Date;
192
+ /**
193
+ * The size of the tick in pixels.
194
+ * @default 6
195
+ */
196
+ tickSize?: number;
197
+ };
183
198
  export interface AxisScaleConfig {
184
199
  band: {
185
200
  scaleType: 'band';
@@ -197,11 +212,57 @@ export interface AxisScaleConfig {
197
212
  */
198
213
  barGapRatio: number;
199
214
  colorMap?: OrdinalColorConfig | ContinuousColorConfig | PiecewiseColorConfig;
215
+ /**
216
+ * Each group will have a label that is the stringified value of the group.
217
+ *
218
+ * @example
219
+ * If the axis is grouped by day, month and year.
220
+ *
221
+ * ```tsx
222
+ * [
223
+ * { getValue: getDate },
224
+ * { getValue: getMonth },
225
+ * { getValue: getFullYear }
226
+ * ]
227
+ * ```
228
+ *
229
+ * Then the axis will have three rows, one for each group.
230
+ *
231
+ * ```bash
232
+ * | 31 | 1 | 2 |
233
+ * | Jan | Feb |
234
+ * | 2021 |
235
+ * ```
236
+ */
237
+ groups?: AxisGroup[];
200
238
  } & Pick<TickParams, 'tickPlacement' | 'tickLabelPlacement'>;
201
239
  point: {
202
240
  scaleType: 'point';
203
241
  scale: ScalePoint<number | Date | string>;
204
242
  colorMap?: OrdinalColorConfig | ContinuousColorConfig | PiecewiseColorConfig;
243
+ /**
244
+ * Each group will have a label that is the stringified value of the group.
245
+ *
246
+ * @example
247
+ * If the axis is grouped by day, month and year.
248
+ *
249
+ * ```tsx
250
+ * [
251
+ * { getValue: getDate },
252
+ * { getValue: getMonth },
253
+ * { getValue: getFullYear }
254
+ * ]
255
+ * ```
256
+ *
257
+ * Then the axis will have three rows, one for each group.
258
+ *
259
+ * ```bash
260
+ * | 31 | 1 | 2 |
261
+ * | Jan | Feb |
262
+ * | 2021 |
263
+ * ```
264
+ */
265
+ groups?: AxisGroup[];
205
266
  };
206
267
  log: {
207
268
  scaleType: 'log';