@mui/x-charts 8.3.0 → 8.4.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 (96) hide show
  1. package/BarChart/BarChart.js +3 -2
  2. package/CHANGELOG.md +206 -10
  3. package/ChartContainer/ChartContainer.js +1 -1
  4. package/ChartsAxis/axisClasses.d.ts +5 -0
  5. package/ChartsAxis/axisClasses.js +1 -1
  6. package/ChartsGrid/ChartsHorizontalGrid.js +2 -1
  7. package/ChartsGrid/ChartsVerticalGrid.js +2 -1
  8. package/ChartsLegend/ChartsLegend.js +1 -0
  9. package/ChartsLocalizationProvider/ChartsLocalizationProvider.js +1 -3
  10. package/ChartsSurface/ChartsSurface.js +1 -0
  11. package/ChartsXAxis/ChartsXAxis.js +9 -17
  12. package/ChartsYAxis/ChartsYAxis.js +6 -9
  13. package/Gauge/Gauge.js +1 -0
  14. package/Gauge/GaugeContainer.js +1 -0
  15. package/Gauge/GaugeProvider.js +1 -3
  16. package/LineChart/AnimatedLine.js +1 -0
  17. package/LineChart/LineChart.js +3 -2
  18. package/LineChart/LineHighlightPlot.js +1 -4
  19. package/LineChart/MarkPlot.js +1 -4
  20. package/PieChart/PieArc.js +1 -0
  21. package/PieChart/PieArcLabel.js +1 -0
  22. package/PieChart/PieChart.js +7 -6
  23. package/RadarChart/RadarChart.js +3 -2
  24. package/ScatterChart/Scatter.js +3 -11
  25. package/ScatterChart/ScatterChart.js +5 -4
  26. package/SparkLineChart/SparkLineChart.js +31 -26
  27. package/Toolbar/ToolbarButton.js +1 -0
  28. package/context/ChartProvider/ChartContext.js +1 -3
  29. package/esm/BarChart/BarChart.js +3 -2
  30. package/esm/ChartContainer/ChartContainer.js +1 -1
  31. package/esm/ChartsAxis/axisClasses.d.ts +5 -0
  32. package/esm/ChartsAxis/axisClasses.js +1 -1
  33. package/esm/ChartsGrid/ChartsHorizontalGrid.js +2 -1
  34. package/esm/ChartsGrid/ChartsVerticalGrid.js +2 -1
  35. package/esm/ChartsLegend/ChartsLegend.js +1 -0
  36. package/esm/ChartsLocalizationProvider/ChartsLocalizationProvider.js +1 -3
  37. package/esm/ChartsSurface/ChartsSurface.js +1 -0
  38. package/esm/ChartsXAxis/ChartsXAxis.js +9 -17
  39. package/esm/ChartsYAxis/ChartsYAxis.js +6 -9
  40. package/esm/Gauge/Gauge.js +1 -0
  41. package/esm/Gauge/GaugeContainer.js +1 -0
  42. package/esm/Gauge/GaugeProvider.js +1 -3
  43. package/esm/LineChart/AnimatedLine.js +1 -0
  44. package/esm/LineChart/LineChart.js +3 -2
  45. package/esm/LineChart/LineHighlightPlot.js +1 -4
  46. package/esm/LineChart/MarkPlot.js +1 -4
  47. package/esm/PieChart/PieArc.js +1 -0
  48. package/esm/PieChart/PieArcLabel.js +1 -0
  49. package/esm/PieChart/PieChart.js +5 -4
  50. package/esm/RadarChart/RadarChart.js +3 -2
  51. package/esm/ScatterChart/Scatter.js +3 -11
  52. package/esm/ScatterChart/ScatterChart.js +5 -4
  53. package/esm/SparkLineChart/SparkLineChart.js +31 -26
  54. package/esm/Toolbar/ToolbarButton.js +1 -0
  55. package/esm/context/ChartProvider/ChartContext.js +1 -3
  56. package/esm/hooks/useTicks.d.ts +1 -4
  57. package/esm/hooks/useTicks.js +29 -28
  58. package/esm/index.js +1 -1
  59. package/esm/internals/constants.d.ts +1 -1
  60. package/esm/internals/constants.js +1 -1
  61. package/esm/internals/consumeSlots.js +2 -1
  62. package/esm/internals/consumeThemeProps.js +3 -1
  63. package/esm/internals/plugins/corePlugins/useChartDimensions/useChartDimensions.js +11 -17
  64. package/esm/internals/plugins/corePlugins/useChartDimensions/useChartDimensions.types.d.ts +16 -13
  65. package/esm/internals/plugins/featurePlugins/useChartCartesianAxis/computeAxisValue.js +2 -2
  66. package/esm/internals/plugins/featurePlugins/useChartCartesianAxis/defaultizeAxis.js +2 -3
  67. package/esm/internals/plugins/featurePlugins/useChartCartesianAxis/defaultizeZoom.js +3 -1
  68. package/esm/internals/plugins/featurePlugins/useChartCartesianAxis/useChartAxisSize.selectors.js +4 -5
  69. package/esm/internals/plugins/featurePlugins/useChartCartesianAxis/useChartCartesianAxis.js +1 -3
  70. package/esm/internals/plugins/featurePlugins/useChartCartesianAxis/zoom.types.d.ts +8 -0
  71. package/esm/internals/plugins/featurePlugins/useChartPolarAxis/computeAxisValue.js +2 -2
  72. package/esm/internals/plugins/featurePlugins/useChartPolarAxis/useChartPolarAxis.js +1 -3
  73. package/esm/internals/plugins/featurePlugins/useChartVoronoi/useChartVoronoi.js +2 -5
  74. package/esm/internals/ticks.d.ts +6 -0
  75. package/esm/internals/ticks.js +22 -0
  76. package/hooks/useTicks.d.ts +1 -4
  77. package/hooks/useTicks.js +29 -29
  78. package/index.js +1 -1
  79. package/internals/constants.d.ts +1 -1
  80. package/internals/constants.js +2 -2
  81. package/internals/consumeSlots.js +2 -1
  82. package/internals/consumeThemeProps.js +3 -1
  83. package/internals/plugins/corePlugins/useChartDimensions/useChartDimensions.js +11 -17
  84. package/internals/plugins/corePlugins/useChartDimensions/useChartDimensions.types.d.ts +16 -13
  85. package/internals/plugins/featurePlugins/useChartCartesianAxis/computeAxisValue.js +3 -3
  86. package/internals/plugins/featurePlugins/useChartCartesianAxis/defaultizeAxis.js +7 -8
  87. package/internals/plugins/featurePlugins/useChartCartesianAxis/defaultizeZoom.js +3 -1
  88. package/internals/plugins/featurePlugins/useChartCartesianAxis/useChartAxisSize.selectors.js +4 -5
  89. package/internals/plugins/featurePlugins/useChartCartesianAxis/useChartCartesianAxis.js +1 -3
  90. package/internals/plugins/featurePlugins/useChartCartesianAxis/zoom.types.d.ts +8 -0
  91. package/internals/plugins/featurePlugins/useChartPolarAxis/computeAxisValue.js +3 -3
  92. package/internals/plugins/featurePlugins/useChartPolarAxis/useChartPolarAxis.js +1 -3
  93. package/internals/plugins/featurePlugins/useChartVoronoi/useChartVoronoi.js +2 -5
  94. package/internals/ticks.d.ts +6 -0
  95. package/internals/ticks.js +29 -0
  96. package/package.json +3 -3
@@ -40,10 +40,6 @@ export interface TickParams {
40
40
  */
41
41
  tickLabelPlacement?: 'middle' | 'tick';
42
42
  }
43
- export declare function getTickNumber(params: TickParams & {
44
- range: number[];
45
- domain: any[];
46
- }): number;
47
43
  export type TickItemType = {
48
44
  /**
49
45
  * This property is undefined only if it's the tick closing the last band
@@ -56,4 +52,5 @@ export type TickItemType = {
56
52
  export declare function useTicks(options: {
57
53
  scale: D3Scale;
58
54
  valueFormatter?: AxisConfig['valueFormatter'];
55
+ direction: 'x' | 'y';
59
56
  } & Pick<TickParams, 'tickNumber' | 'tickInterval' | 'tickPlacement' | 'tickLabelPlacement'>): TickItemType[];
@@ -1,21 +1,9 @@
1
1
  'use client';
2
2
 
3
3
  import * as React from 'react';
4
+ import { useChartContext } from "../context/ChartProvider/index.js";
4
5
  import { isBandScale } from "../internals/isBandScale.js";
5
6
  import { isInfinity } from "../internals/isInfinity.js";
6
- export function getTickNumber(params) {
7
- const {
8
- tickMaxStep,
9
- tickMinStep,
10
- tickNumber,
11
- range,
12
- domain
13
- } = params;
14
- const maxTicks = tickMinStep === undefined ? 999 : Math.floor(Math.abs(domain[1] - domain[0]) / tickMinStep);
15
- const minTicks = tickMaxStep === undefined ? 2 : Math.ceil(Math.abs(domain[1] - domain[0]) / tickMaxStep);
16
- const defaultizedTickNumber = tickNumber ?? Math.floor(Math.abs(range[1] - range[0]) / 50);
17
- return Math.min(maxTicks, Math.max(minTicks, defaultizedTickNumber));
18
- }
19
7
  const offsetRatio = {
20
8
  start: 0,
21
9
  extremities: 0,
@@ -29,8 +17,12 @@ export function useTicks(options) {
29
17
  valueFormatter,
30
18
  tickInterval,
31
19
  tickPlacement = 'extremities',
32
- tickLabelPlacement: tickLabelPlacementProp
20
+ tickLabelPlacement: tickLabelPlacementProp,
21
+ direction
33
22
  } = options;
23
+ const {
24
+ instance
25
+ } = useChartContext();
34
26
  return React.useMemo(() => {
35
27
  // band scale
36
28
  if (isBandScale(scale)) {
@@ -74,18 +66,27 @@ export function useTicks(options) {
74
66
  }
75
67
  const tickLabelPlacement = tickLabelPlacementProp;
76
68
  const ticks = typeof tickInterval === 'object' ? tickInterval : scale.ticks(tickNumber);
77
- return ticks.map((value, i) => {
78
- return {
79
- value,
80
- formattedValue: valueFormatter?.(value, {
81
- location: 'tick',
82
- scale
83
- }) ?? scale.tickFormat(tickNumber)(value),
84
- offset: scale(value),
85
- // Allowing the label to be placed in the middle of a continuous scale is weird.
86
- // But it is useful in some cases, like funnel categories with a linear scale.
87
- labelOffset: tickLabelPlacement === 'middle' ? scale(ticks[i - 1] ?? 0) - (scale(value) + scale(ticks[i - 1] ?? 0)) / 2 : 0
88
- };
89
- });
90
- }, [scale, tickInterval, tickNumber, valueFormatter, tickPlacement, tickLabelPlacementProp]);
69
+
70
+ // Ticks inside the drawing area
71
+ const visibleTicks = [];
72
+ for (let i = 0; i < ticks.length; i += 1) {
73
+ const value = ticks[i];
74
+ const offset = scale(value);
75
+ const isInside = direction === 'x' ? instance.isXInside(offset) : instance.isYInside(offset);
76
+ if (isInside) {
77
+ visibleTicks.push({
78
+ value,
79
+ formattedValue: valueFormatter?.(value, {
80
+ location: 'tick',
81
+ scale
82
+ }) ?? scale.tickFormat(tickNumber)(value),
83
+ offset,
84
+ // Allowing the label to be placed in the middle of a continuous scale is weird.
85
+ // But it is useful in some cases, like funnel categories with a linear scale.
86
+ labelOffset: tickLabelPlacement === 'middle' ? scale(ticks[i - 1] ?? 0) - (offset + scale(ticks[i - 1] ?? 0)) / 2 : 0
87
+ });
88
+ }
89
+ }
90
+ return visibleTicks;
91
+ }, [scale, tickLabelPlacementProp, tickInterval, tickNumber, tickPlacement, valueFormatter, direction, instance]);
91
92
  }
package/esm/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @mui/x-charts v8.3.0
2
+ * @mui/x-charts v8.4.0
3
3
  *
4
4
  * @license MIT
5
5
  * This source code is licensed under the MIT license found in the
@@ -1,4 +1,4 @@
1
1
  /** Margin in the opposite direction of the axis, i.e., horizontal if the axis is vertical and vice versa. */
2
2
  export declare const ZOOM_SLIDER_MARGIN = 4;
3
3
  /** Size reserved for the zoom slider. The actual size of the slider might be smaller. */
4
- export declare const ZOOM_SLIDER_SIZE: number;
4
+ export declare const DEFAULT_ZOOM_SLIDER_SIZE: number;
@@ -2,4 +2,4 @@
2
2
  export const ZOOM_SLIDER_MARGIN = 4;
3
3
 
4
4
  /** Size reserved for the zoom slider. The actual size of the slider might be smaller. */
5
- export const ZOOM_SLIDER_SIZE = 20 + 2 * ZOOM_SLIDER_MARGIN;
5
+ export const DEFAULT_ZOOM_SLIDER_SIZE = 20 + 2 * ZOOM_SLIDER_MARGIN;
@@ -94,4 +94,5 @@ export const consumeSlots = (name, slotPropName, options, InComponent) => {
94
94
  }));
95
95
  }
96
96
  return /*#__PURE__*/React.forwardRef(ConsumeSlotsInternal);
97
- };
97
+ };
98
+ if (process.env.NODE_ENV !== "production") consumeSlots.displayName = "consumeSlots";
@@ -60,6 +60,7 @@ export const consumeThemeProps = (name, options, InComponent) => /*#__PURE__*/Re
60
60
  const theme = useTheme();
61
61
  const classes = options.classesResolver?.(outProps, theme);
62
62
  const OutComponent = /*#__PURE__*/React.forwardRef(InComponent);
63
+ if (process.env.NODE_ENV !== "production") OutComponent.displayName = "OutComponent";
63
64
  if (process.env.NODE_ENV !== 'production') {
64
65
  OutComponent.displayName = `consumeThemeProps(${name})`;
65
66
  }
@@ -67,4 +68,5 @@ export const consumeThemeProps = (name, options, InComponent) => /*#__PURE__*/Re
67
68
  classes: classes,
68
69
  ref: ref
69
70
  }));
70
- });
71
+ });
72
+ if (process.env.NODE_ENV !== "production") consumeThemeProps.displayName = "consumeThemeProps";
@@ -4,6 +4,7 @@ import _extends from "@babel/runtime/helpers/esm/extends";
4
4
  import * as React from 'react';
5
5
  import useEnhancedEffect from '@mui/utils/useEnhancedEffect';
6
6
  import ownerWindow from '@mui/utils/ownerWindow';
7
+ import { useSelector } from "../../../store/useSelector.js";
7
8
  import { DEFAULT_MARGINS } from "../../../../constants/index.js";
8
9
  import { selectorChartDrawingArea } from "./useChartDimensions.selectors.js";
9
10
  import { defaultizeMargin } from "../../../defaultizeMargin.js";
@@ -143,28 +144,21 @@ export const useChartDimensions = ({
143
144
  stateRef.current.displayError = false;
144
145
  }
145
146
  }
146
- const isPointInside = React.useCallback(({
147
- x,
148
- y
149
- }, options) => {
147
+ const drawingArea = useSelector(store, selectorChartDrawingArea);
148
+ const isXInside = React.useCallback(x => x >= drawingArea.left - 1 && x <= drawingArea.left + drawingArea.width, [drawingArea.left, drawingArea.width]);
149
+ const isYInside = React.useCallback(y => y >= drawingArea.top - 1 && y <= drawingArea.top + drawingArea.height, [drawingArea.height, drawingArea.top]);
150
+ const isPointInside = React.useCallback((x, y, targetElement) => {
150
151
  // For element allowed to overflow, wrapping them in <g data-drawing-container /> make them fully part of the drawing area.
151
- if (options?.targetElement && options?.targetElement.closest('[data-drawing-container]')) {
152
+ if (targetElement && targetElement.closest('[data-drawing-container]')) {
152
153
  return true;
153
154
  }
154
- const drawingArea = selectorChartDrawingArea(store.value);
155
- const isInsideX = x >= drawingArea.left - 1 && x <= drawingArea.left + drawingArea.width;
156
- const isInsideY = y >= drawingArea.top - 1 && y <= drawingArea.top + drawingArea.height;
157
- if (options?.direction === 'x') {
158
- return isInsideX;
159
- }
160
- if (options?.direction === 'y') {
161
- return isInsideY;
162
- }
163
- return isInsideX && isInsideY;
164
- }, [store.value]);
155
+ return isXInside(x) && isYInside(y);
156
+ }, [isXInside, isYInside]);
165
157
  return {
166
158
  instance: {
167
- isPointInside
159
+ isPointInside,
160
+ isXInside,
161
+ isYInside
168
162
  }
169
163
  };
170
164
  };
@@ -65,21 +65,24 @@ export interface UseChartDimensionsState {
65
65
  export interface UseChartDimensionsInstance {
66
66
  /**
67
67
  * Checks if a point is inside the drawing area.
68
- * @param {Object} point The point to check.
69
- * @param {number} point.x The x coordinate of the point.
70
- * @param {number} point.y The y coordinate of the point.
71
- * @param {Object} options The options of the check.
72
- * @param {Element} [options.targetElement] The element to check if it is allowed to overflow the drawing area.
73
- * @param {'x' | 'y'} [options.direction] The direction to check.
68
+ * @param {number} x The x coordinate of the point.
69
+ * @param {number} y The y coordinate of the point.
70
+ * @param {Element} targetElement The element to check if it is allowed to overflow the drawing area.
74
71
  * @returns {boolean} `true` if the point is inside the drawing area, `false` otherwise.
75
72
  */
76
- isPointInside: (point: {
77
- x: number;
78
- y: number;
79
- }, options?: {
80
- targetElement?: Element;
81
- direction?: 'x' | 'y';
82
- }) => boolean;
73
+ isPointInside: (x: number, y: number, targetElement?: Element) => boolean;
74
+ /**
75
+ * Checks if the x coordinate is inside the drawing area.
76
+ * @param {number} x The x coordinate of the point.
77
+ * @returns {boolean} `true` if the point is inside the drawing area, `false` otherwise.
78
+ */
79
+ isXInside: (x: number) => boolean;
80
+ /**
81
+ * Checks if the y coordinate is inside the drawing area.
82
+ * @param {number} y The y coordinate of the point.
83
+ * @returns {boolean} `true` if the point is inside the drawing area, `false` otherwise.
84
+ */
85
+ isYInside: (y: number) => boolean;
83
86
  }
84
87
  export type UseChartDimensionsSignature = ChartPluginSignature<{
85
88
  params: UseChartDimensionsParameters;
@@ -2,7 +2,7 @@ import _extends from "@babel/runtime/helpers/esm/extends";
2
2
  import { scaleBand, scalePoint, scaleTime } from '@mui/x-charts-vendor/d3-scale';
3
3
  import { isBandScaleConfig, isPointScaleConfig } from "../../../../models/axis.js";
4
4
  import { getColorScale, getOrdinalColorScale } from "../../../colorScale.js";
5
- import { getTickNumber } from "../../../../hooks/useTicks.js";
5
+ import { getTickNumber, scaleTickNumberByRange } from "../../../ticks.js";
6
6
  import { getScale } from "../../../getScale.js";
7
7
  import { zoomScaleRange } from "./zoom.js";
8
8
  import { getAxisExtremum } from "./getAxisExtremum.js";
@@ -114,7 +114,7 @@ export function computeAxisValue({
114
114
  range,
115
115
  domain: axisExtremums
116
116
  }));
117
- const tickNumber = rawTickNumber / ((zoomRange[1] - zoomRange[0]) / 100);
117
+ const tickNumber = scaleTickNumberByRange(rawTickNumber, zoomRange);
118
118
  const zoomedRange = zoomScaleRange(range, zoomRange);
119
119
  const scale = getScale(scaleType, axisExtremums, zoomedRange);
120
120
  const finalScale = domainLimit === 'nice' ? scale.nice(rawTickNumber) : scale;
@@ -1,5 +1,4 @@
1
1
  import _extends from "@babel/runtime/helpers/esm/extends";
2
- import { ZOOM_SLIDER_SIZE } from "../../../constants.js";
3
2
  import { defaultizeZoom } from "./defaultizeZoom.js";
4
3
  import { DEFAULT_X_AXIS_KEY, DEFAULT_Y_AXIS_KEY, DEFAULT_AXIS_SIZE_HEIGHT, DEFAULT_AXIS_SIZE_WIDTH, AXIS_LABEL_DEFAULT_HEIGHT } from "../../../../constants/index.js";
5
4
  export function defaultizeXAxis(inAxes, dataset) {
@@ -33,7 +32,7 @@ export function defaultizeXAxis(inAxes, dataset) {
33
32
  if (position !== 'none') {
34
33
  offsets[position] += sharedConfig.height;
35
34
  if (sharedConfig.zoom?.slider.enabled) {
36
- offsets[position] += ZOOM_SLIDER_SIZE;
35
+ offsets[position] += sharedConfig.zoom.slider.size;
37
36
  }
38
37
  }
39
38
 
@@ -83,7 +82,7 @@ export function defaultizeYAxis(inAxes, dataset) {
83
82
  if (position !== 'none') {
84
83
  offsets[position] += sharedConfig.width;
85
84
  if (sharedConfig.zoom?.slider.enabled) {
86
- offsets[position] += ZOOM_SLIDER_SIZE;
85
+ offsets[position] += sharedConfig.zoom.slider.size;
87
86
  }
88
87
  }
89
88
 
@@ -1,4 +1,5 @@
1
1
  import _extends from "@babel/runtime/helpers/esm/extends";
2
+ import { DEFAULT_ZOOM_SLIDER_SIZE } from "../../../constants.js";
2
3
  const defaultZoomOptions = {
3
4
  minStart: 0,
4
5
  maxEnd: 100,
@@ -8,7 +9,8 @@ const defaultZoomOptions = {
8
9
  panning: true,
9
10
  filterMode: 'keep',
10
11
  slider: {
11
- enabled: false
12
+ enabled: false,
13
+ size: DEFAULT_ZOOM_SLIDER_SIZE
12
14
  }
13
15
  };
14
16
  export const defaultizeZoom = (zoom, axisId, axisDirection) => {
@@ -1,7 +1,6 @@
1
- import { ZOOM_SLIDER_SIZE } from "../../../constants.js";
2
1
  import { selectorChartRawXAxis, selectorChartRawYAxis } from "./useChartCartesianAxisLayout.selectors.js";
3
2
  import { createSelector } from "../../utils/selectors.js";
4
- export const selectorChartLeftAxisSize = createSelector([selectorChartRawYAxis], yAxis => (yAxis ?? []).reduce((acc, axis) => axis.position === 'left' ? acc + (axis.width || 0) + (axis.zoom?.slider.enabled ? ZOOM_SLIDER_SIZE : 0) : acc, 0));
5
- export const selectorChartRightAxisSize = createSelector([selectorChartRawYAxis], yAxis => (yAxis ?? []).reduce((acc, axis) => axis.position === 'right' ? acc + (axis.width || 0) + (axis.zoom?.slider.enabled ? ZOOM_SLIDER_SIZE : 0) : acc, 0));
6
- export const selectorChartTopAxisSize = createSelector([selectorChartRawXAxis], xAxis => (xAxis ?? []).reduce((acc, axis) => axis.position === 'top' ? acc + (axis.height || 0) + (axis.zoom?.slider.enabled ? ZOOM_SLIDER_SIZE : 0) : acc, 0));
7
- export const selectorChartBottomAxisSize = createSelector([selectorChartRawXAxis], xAxis => (xAxis ?? []).reduce((acc, axis) => axis.position === 'bottom' ? acc + (axis.height || 0) + (axis.zoom?.slider.enabled ? ZOOM_SLIDER_SIZE : 0) : acc, 0));
3
+ export const selectorChartLeftAxisSize = createSelector([selectorChartRawYAxis], yAxis => (yAxis ?? []).reduce((acc, axis) => axis.position === 'left' ? acc + (axis.width || 0) + (axis.zoom?.slider.enabled ? axis.zoom.slider.size : 0) : acc, 0));
4
+ export const selectorChartRightAxisSize = createSelector([selectorChartRawYAxis], yAxis => (yAxis ?? []).reduce((acc, axis) => axis.position === 'right' ? acc + (axis.width || 0) + (axis.zoom?.slider.enabled ? axis.zoom.slider.size : 0) : acc, 0));
5
+ export const selectorChartTopAxisSize = createSelector([selectorChartRawXAxis], xAxis => (xAxis ?? []).reduce((acc, axis) => axis.position === 'top' ? acc + (axis.height || 0) + (axis.zoom?.slider.enabled ? axis.zoom.slider.size : 0) : acc, 0));
6
+ export const selectorChartBottomAxisSize = createSelector([selectorChartRawXAxis], xAxis => (xAxis ?? []).reduce((acc, axis) => axis.position === 'bottom' ? acc + (axis.height || 0) + (axis.zoom?.slider.enabled ? axis.zoom.slider.size : 0) : acc, 0));
@@ -71,9 +71,7 @@ export const useChartCartesianAxis = ({
71
71
  const handleMove = event => {
72
72
  const target = 'targetTouches' in event ? event.targetTouches[0] : event;
73
73
  const svgPoint = getSVGPoint(element, target);
74
- if (!instance.isPointInside(svgPoint, {
75
- targetElement: event.target
76
- })) {
74
+ if (!instance.isPointInside(svgPoint.x, svgPoint.y, event.target)) {
77
75
  instance.cleanInteraction?.();
78
76
  return;
79
77
  }
@@ -77,6 +77,14 @@ export interface ZoomSliderOptions {
77
77
  * If `true`, the slider will be shown.
78
78
  */
79
79
  enabled?: boolean;
80
+ /**
81
+ * The size reserved for the zoom slider. The actual size of the slider might be smaller, so
82
+ * increasing this value effectively increases the margin around the slider.
83
+ * This means the height for the x-axis and the width for the y-axis.
84
+ *
85
+ * @default 28
86
+ */
87
+ size?: number;
80
88
  }
81
89
  export type ZoomAxisFilters = Record<AxisId, ExtremumFilter>;
82
90
  export type GetZoomAxisFilters = (params: {
@@ -2,7 +2,7 @@ import _extends from "@babel/runtime/helpers/esm/extends";
2
2
  import { scaleBand, scalePoint, scaleTime } from '@mui/x-charts-vendor/d3-scale';
3
3
  import { isBandScaleConfig, isPointScaleConfig } from "../../../../models/axis.js";
4
4
  import { getColorScale, getOrdinalColorScale } from "../../../colorScale.js";
5
- import { getTickNumber } from "../../../../hooks/useTicks.js";
5
+ import { getTickNumber, scaleTickNumberByRange } from "../../../ticks.js";
6
6
  import { getScale } from "../../../getScale.js";
7
7
  import { getAxisExtremum } from "./getAxisExtremum.js";
8
8
  import { deg2rad } from "../../../angleConversion.js";
@@ -109,7 +109,7 @@ export function computeAxisValue({
109
109
  range,
110
110
  domain: axisExtremums
111
111
  }));
112
- const tickNumber = rawTickNumber / ((range[1] - range[0]) / 100);
112
+ const tickNumber = scaleTickNumberByRange(rawTickNumber, range);
113
113
  const scale = getScale(scaleType, axisExtremums, range);
114
114
  const finalScale = domainLimit === 'nice' ? scale.nice(rawTickNumber) : scale;
115
115
  const [minDomain, maxDomain] = finalScale.domain();
@@ -97,9 +97,7 @@ export const useChartPolarAxis = ({
97
97
  mousePosition.current.y = svgPoint.y;
98
98
 
99
99
  // Test if it's in the drawing area
100
- if (!instance.isPointInside(svgPoint, {
101
- targetElement: event.target
102
- })) {
100
+ if (!instance.isPointInside(svgPoint.x, svgPoint.y, event.target)) {
103
101
  if (mousePosition.current.isInChart) {
104
102
  instance?.cleanInteraction();
105
103
  mousePosition.current.isInChart = false;
@@ -71,10 +71,7 @@ export const useChartVoronoi = ({
71
71
  }) => {
72
72
  const pointX = getXPosition(x);
73
73
  const pointY = getYPosition(y);
74
- if (!instance.isPointInside({
75
- x: pointX,
76
- y: pointY
77
- })) {
74
+ if (!instance.isPointInside(pointX, pointY)) {
78
75
  // If the point is not displayed we move them to a trash coordinate.
79
76
  // This avoids managing index mapping before/after filtering.
80
77
  // The trash point is far enough such that any point in the drawing area will be closer to the mouse than the trash coordinate.
@@ -100,7 +97,7 @@ export const useChartVoronoi = ({
100
97
  function getClosestPoint(event) {
101
98
  // Get mouse coordinate in global SVG space
102
99
  const svgPoint = getSVGPoint(element, event);
103
- if (!instance.isPointInside(svgPoint)) {
100
+ if (!instance.isPointInside(svgPoint.x, svgPoint.y)) {
104
101
  lastFind.current = undefined;
105
102
  return 'outside-chart';
106
103
  }
@@ -0,0 +1,6 @@
1
+ import type { TickParams } from "../hooks/useTicks.js";
2
+ export declare function getTickNumber(params: TickParams & {
3
+ range: number[];
4
+ domain: any[];
5
+ }): number;
6
+ export declare function scaleTickNumberByRange(tickNumber: number, range: number[]): number;
@@ -0,0 +1,22 @@
1
+ export function getTickNumber(params) {
2
+ const {
3
+ tickMaxStep,
4
+ tickMinStep,
5
+ tickNumber,
6
+ range,
7
+ domain
8
+ } = params;
9
+ const maxTicks = tickMinStep === undefined ? 999 : Math.floor(Math.abs(domain[1] - domain[0]) / tickMinStep);
10
+ const minTicks = tickMaxStep === undefined ? 2 : Math.ceil(Math.abs(domain[1] - domain[0]) / tickMaxStep);
11
+ const defaultizedTickNumber = tickNumber ?? Math.floor(Math.abs(range[1] - range[0]) / 50);
12
+ return Math.min(maxTicks, Math.max(minTicks, defaultizedTickNumber));
13
+ }
14
+ export function scaleTickNumberByRange(tickNumber, range) {
15
+ const rangeGap = range[1] - range[0];
16
+
17
+ /* If the range start and end are the same, `tickNumber` will become infinity, so we default to 1. */
18
+ if (rangeGap === 0) {
19
+ return 1;
20
+ }
21
+ return tickNumber / ((range[1] - range[0]) / 100);
22
+ }
@@ -40,10 +40,6 @@ export interface TickParams {
40
40
  */
41
41
  tickLabelPlacement?: 'middle' | 'tick';
42
42
  }
43
- export declare function getTickNumber(params: TickParams & {
44
- range: number[];
45
- domain: any[];
46
- }): number;
47
43
  export type TickItemType = {
48
44
  /**
49
45
  * This property is undefined only if it's the tick closing the last band
@@ -56,4 +52,5 @@ export type TickItemType = {
56
52
  export declare function useTicks(options: {
57
53
  scale: D3Scale;
58
54
  valueFormatter?: AxisConfig['valueFormatter'];
55
+ direction: 'x' | 'y';
59
56
  } & Pick<TickParams, 'tickNumber' | 'tickInterval' | 'tickPlacement' | 'tickLabelPlacement'>): TickItemType[];
package/hooks/useTicks.js CHANGED
@@ -5,24 +5,11 @@ var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWild
5
5
  Object.defineProperty(exports, "__esModule", {
6
6
  value: true
7
7
  });
8
- exports.getTickNumber = getTickNumber;
9
8
  exports.useTicks = useTicks;
10
9
  var React = _interopRequireWildcard(require("react"));
10
+ var _ChartProvider = require("../context/ChartProvider");
11
11
  var _isBandScale = require("../internals/isBandScale");
12
12
  var _isInfinity = require("../internals/isInfinity");
13
- function getTickNumber(params) {
14
- const {
15
- tickMaxStep,
16
- tickMinStep,
17
- tickNumber,
18
- range,
19
- domain
20
- } = params;
21
- const maxTicks = tickMinStep === undefined ? 999 : Math.floor(Math.abs(domain[1] - domain[0]) / tickMinStep);
22
- const minTicks = tickMaxStep === undefined ? 2 : Math.ceil(Math.abs(domain[1] - domain[0]) / tickMaxStep);
23
- const defaultizedTickNumber = tickNumber ?? Math.floor(Math.abs(range[1] - range[0]) / 50);
24
- return Math.min(maxTicks, Math.max(minTicks, defaultizedTickNumber));
25
- }
26
13
  const offsetRatio = {
27
14
  start: 0,
28
15
  extremities: 0,
@@ -36,8 +23,12 @@ function useTicks(options) {
36
23
  valueFormatter,
37
24
  tickInterval,
38
25
  tickPlacement = 'extremities',
39
- tickLabelPlacement: tickLabelPlacementProp
26
+ tickLabelPlacement: tickLabelPlacementProp,
27
+ direction
40
28
  } = options;
29
+ const {
30
+ instance
31
+ } = (0, _ChartProvider.useChartContext)();
41
32
  return React.useMemo(() => {
42
33
  // band scale
43
34
  if ((0, _isBandScale.isBandScale)(scale)) {
@@ -81,18 +72,27 @@ function useTicks(options) {
81
72
  }
82
73
  const tickLabelPlacement = tickLabelPlacementProp;
83
74
  const ticks = typeof tickInterval === 'object' ? tickInterval : scale.ticks(tickNumber);
84
- return ticks.map((value, i) => {
85
- return {
86
- value,
87
- formattedValue: valueFormatter?.(value, {
88
- location: 'tick',
89
- scale
90
- }) ?? scale.tickFormat(tickNumber)(value),
91
- offset: scale(value),
92
- // Allowing the label to be placed in the middle of a continuous scale is weird.
93
- // But it is useful in some cases, like funnel categories with a linear scale.
94
- labelOffset: tickLabelPlacement === 'middle' ? scale(ticks[i - 1] ?? 0) - (scale(value) + scale(ticks[i - 1] ?? 0)) / 2 : 0
95
- };
96
- });
97
- }, [scale, tickInterval, tickNumber, valueFormatter, tickPlacement, tickLabelPlacementProp]);
75
+
76
+ // Ticks inside the drawing area
77
+ const visibleTicks = [];
78
+ for (let i = 0; i < ticks.length; i += 1) {
79
+ const value = ticks[i];
80
+ const offset = scale(value);
81
+ const isInside = direction === 'x' ? instance.isXInside(offset) : instance.isYInside(offset);
82
+ if (isInside) {
83
+ visibleTicks.push({
84
+ value,
85
+ formattedValue: valueFormatter?.(value, {
86
+ location: 'tick',
87
+ scale
88
+ }) ?? scale.tickFormat(tickNumber)(value),
89
+ offset,
90
+ // Allowing the label to be placed in the middle of a continuous scale is weird.
91
+ // But it is useful in some cases, like funnel categories with a linear scale.
92
+ labelOffset: tickLabelPlacement === 'middle' ? scale(ticks[i - 1] ?? 0) - (offset + scale(ticks[i - 1] ?? 0)) / 2 : 0
93
+ });
94
+ }
95
+ }
96
+ return visibleTicks;
97
+ }, [scale, tickLabelPlacementProp, tickInterval, tickNumber, tickPlacement, valueFormatter, direction, instance]);
98
98
  }
package/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @mui/x-charts v8.3.0
2
+ * @mui/x-charts v8.4.0
3
3
  *
4
4
  * @license MIT
5
5
  * This source code is licensed under the MIT license found in the
@@ -1,4 +1,4 @@
1
1
  /** Margin in the opposite direction of the axis, i.e., horizontal if the axis is vertical and vice versa. */
2
2
  export declare const ZOOM_SLIDER_MARGIN = 4;
3
3
  /** Size reserved for the zoom slider. The actual size of the slider might be smaller. */
4
- export declare const ZOOM_SLIDER_SIZE: number;
4
+ export declare const DEFAULT_ZOOM_SLIDER_SIZE: number;
@@ -3,9 +3,9 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.ZOOM_SLIDER_SIZE = exports.ZOOM_SLIDER_MARGIN = void 0;
6
+ exports.ZOOM_SLIDER_MARGIN = exports.DEFAULT_ZOOM_SLIDER_SIZE = void 0;
7
7
  /** Margin in the opposite direction of the axis, i.e., horizontal if the axis is vertical and vice versa. */
8
8
  const ZOOM_SLIDER_MARGIN = exports.ZOOM_SLIDER_MARGIN = 4;
9
9
 
10
10
  /** Size reserved for the zoom slider. The actual size of the slider might be smaller. */
11
- const ZOOM_SLIDER_SIZE = exports.ZOOM_SLIDER_SIZE = 20 + 2 * ZOOM_SLIDER_MARGIN;
11
+ const DEFAULT_ZOOM_SLIDER_SIZE = exports.DEFAULT_ZOOM_SLIDER_SIZE = 20 + 2 * ZOOM_SLIDER_MARGIN;
@@ -102,4 +102,5 @@ const consumeSlots = (name, slotPropName, options, InComponent) => {
102
102
  }
103
103
  return /*#__PURE__*/React.forwardRef(ConsumeSlotsInternal);
104
104
  };
105
- exports.consumeSlots = consumeSlots;
105
+ exports.consumeSlots = consumeSlots;
106
+ if (process.env.NODE_ENV !== "production") consumeSlots.displayName = "consumeSlots";
@@ -67,6 +67,7 @@ const consumeThemeProps = (name, options, InComponent) => /*#__PURE__*/React.for
67
67
  const theme = (0, _styles.useTheme)();
68
68
  const classes = options.classesResolver?.(outProps, theme);
69
69
  const OutComponent = /*#__PURE__*/React.forwardRef(InComponent);
70
+ if (process.env.NODE_ENV !== "production") OutComponent.displayName = "OutComponent";
70
71
  if (process.env.NODE_ENV !== 'production') {
71
72
  OutComponent.displayName = `consumeThemeProps(${name})`;
72
73
  }
@@ -75,4 +76,5 @@ const consumeThemeProps = (name, options, InComponent) => /*#__PURE__*/React.for
75
76
  ref: ref
76
77
  }));
77
78
  });
78
- exports.consumeThemeProps = consumeThemeProps;
79
+ exports.consumeThemeProps = consumeThemeProps;
80
+ if (process.env.NODE_ENV !== "production") consumeThemeProps.displayName = "consumeThemeProps";
@@ -11,6 +11,7 @@ var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends")
11
11
  var React = _interopRequireWildcard(require("react"));
12
12
  var _useEnhancedEffect = _interopRequireDefault(require("@mui/utils/useEnhancedEffect"));
13
13
  var _ownerWindow = _interopRequireDefault(require("@mui/utils/ownerWindow"));
14
+ var _useSelector = require("../../../store/useSelector");
14
15
  var _constants = require("../../../../constants");
15
16
  var _useChartDimensions = require("./useChartDimensions.selectors");
16
17
  var _defaultizeMargin = require("../../../defaultizeMargin");
@@ -150,28 +151,21 @@ const useChartDimensions = ({
150
151
  stateRef.current.displayError = false;
151
152
  }
152
153
  }
153
- const isPointInside = React.useCallback(({
154
- x,
155
- y
156
- }, options) => {
154
+ const drawingArea = (0, _useSelector.useSelector)(store, _useChartDimensions.selectorChartDrawingArea);
155
+ const isXInside = React.useCallback(x => x >= drawingArea.left - 1 && x <= drawingArea.left + drawingArea.width, [drawingArea.left, drawingArea.width]);
156
+ const isYInside = React.useCallback(y => y >= drawingArea.top - 1 && y <= drawingArea.top + drawingArea.height, [drawingArea.height, drawingArea.top]);
157
+ const isPointInside = React.useCallback((x, y, targetElement) => {
157
158
  // For element allowed to overflow, wrapping them in <g data-drawing-container /> make them fully part of the drawing area.
158
- if (options?.targetElement && options?.targetElement.closest('[data-drawing-container]')) {
159
+ if (targetElement && targetElement.closest('[data-drawing-container]')) {
159
160
  return true;
160
161
  }
161
- const drawingArea = (0, _useChartDimensions.selectorChartDrawingArea)(store.value);
162
- const isInsideX = x >= drawingArea.left - 1 && x <= drawingArea.left + drawingArea.width;
163
- const isInsideY = y >= drawingArea.top - 1 && y <= drawingArea.top + drawingArea.height;
164
- if (options?.direction === 'x') {
165
- return isInsideX;
166
- }
167
- if (options?.direction === 'y') {
168
- return isInsideY;
169
- }
170
- return isInsideX && isInsideY;
171
- }, [store.value]);
162
+ return isXInside(x) && isYInside(y);
163
+ }, [isXInside, isYInside]);
172
164
  return {
173
165
  instance: {
174
- isPointInside
166
+ isPointInside,
167
+ isXInside,
168
+ isYInside
175
169
  }
176
170
  };
177
171
  };