@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.
- package/BarChart/BarChart.js +3 -2
- package/CHANGELOG.md +206 -10
- package/ChartContainer/ChartContainer.js +1 -1
- package/ChartsAxis/axisClasses.d.ts +5 -0
- package/ChartsAxis/axisClasses.js +1 -1
- package/ChartsGrid/ChartsHorizontalGrid.js +2 -1
- package/ChartsGrid/ChartsVerticalGrid.js +2 -1
- package/ChartsLegend/ChartsLegend.js +1 -0
- package/ChartsLocalizationProvider/ChartsLocalizationProvider.js +1 -3
- package/ChartsSurface/ChartsSurface.js +1 -0
- package/ChartsXAxis/ChartsXAxis.js +9 -17
- package/ChartsYAxis/ChartsYAxis.js +6 -9
- package/Gauge/Gauge.js +1 -0
- package/Gauge/GaugeContainer.js +1 -0
- package/Gauge/GaugeProvider.js +1 -3
- package/LineChart/AnimatedLine.js +1 -0
- package/LineChart/LineChart.js +3 -2
- package/LineChart/LineHighlightPlot.js +1 -4
- package/LineChart/MarkPlot.js +1 -4
- package/PieChart/PieArc.js +1 -0
- package/PieChart/PieArcLabel.js +1 -0
- package/PieChart/PieChart.js +7 -6
- package/RadarChart/RadarChart.js +3 -2
- package/ScatterChart/Scatter.js +3 -11
- package/ScatterChart/ScatterChart.js +5 -4
- package/SparkLineChart/SparkLineChart.js +31 -26
- package/Toolbar/ToolbarButton.js +1 -0
- package/context/ChartProvider/ChartContext.js +1 -3
- package/esm/BarChart/BarChart.js +3 -2
- package/esm/ChartContainer/ChartContainer.js +1 -1
- package/esm/ChartsAxis/axisClasses.d.ts +5 -0
- package/esm/ChartsAxis/axisClasses.js +1 -1
- package/esm/ChartsGrid/ChartsHorizontalGrid.js +2 -1
- package/esm/ChartsGrid/ChartsVerticalGrid.js +2 -1
- package/esm/ChartsLegend/ChartsLegend.js +1 -0
- package/esm/ChartsLocalizationProvider/ChartsLocalizationProvider.js +1 -3
- package/esm/ChartsSurface/ChartsSurface.js +1 -0
- package/esm/ChartsXAxis/ChartsXAxis.js +9 -17
- package/esm/ChartsYAxis/ChartsYAxis.js +6 -9
- package/esm/Gauge/Gauge.js +1 -0
- package/esm/Gauge/GaugeContainer.js +1 -0
- package/esm/Gauge/GaugeProvider.js +1 -3
- package/esm/LineChart/AnimatedLine.js +1 -0
- package/esm/LineChart/LineChart.js +3 -2
- package/esm/LineChart/LineHighlightPlot.js +1 -4
- package/esm/LineChart/MarkPlot.js +1 -4
- package/esm/PieChart/PieArc.js +1 -0
- package/esm/PieChart/PieArcLabel.js +1 -0
- package/esm/PieChart/PieChart.js +5 -4
- package/esm/RadarChart/RadarChart.js +3 -2
- package/esm/ScatterChart/Scatter.js +3 -11
- package/esm/ScatterChart/ScatterChart.js +5 -4
- package/esm/SparkLineChart/SparkLineChart.js +31 -26
- package/esm/Toolbar/ToolbarButton.js +1 -0
- package/esm/context/ChartProvider/ChartContext.js +1 -3
- package/esm/hooks/useTicks.d.ts +1 -4
- package/esm/hooks/useTicks.js +29 -28
- package/esm/index.js +1 -1
- package/esm/internals/constants.d.ts +1 -1
- package/esm/internals/constants.js +1 -1
- package/esm/internals/consumeSlots.js +2 -1
- package/esm/internals/consumeThemeProps.js +3 -1
- package/esm/internals/plugins/corePlugins/useChartDimensions/useChartDimensions.js +11 -17
- package/esm/internals/plugins/corePlugins/useChartDimensions/useChartDimensions.types.d.ts +16 -13
- package/esm/internals/plugins/featurePlugins/useChartCartesianAxis/computeAxisValue.js +2 -2
- package/esm/internals/plugins/featurePlugins/useChartCartesianAxis/defaultizeAxis.js +2 -3
- package/esm/internals/plugins/featurePlugins/useChartCartesianAxis/defaultizeZoom.js +3 -1
- package/esm/internals/plugins/featurePlugins/useChartCartesianAxis/useChartAxisSize.selectors.js +4 -5
- package/esm/internals/plugins/featurePlugins/useChartCartesianAxis/useChartCartesianAxis.js +1 -3
- package/esm/internals/plugins/featurePlugins/useChartCartesianAxis/zoom.types.d.ts +8 -0
- package/esm/internals/plugins/featurePlugins/useChartPolarAxis/computeAxisValue.js +2 -2
- package/esm/internals/plugins/featurePlugins/useChartPolarAxis/useChartPolarAxis.js +1 -3
- package/esm/internals/plugins/featurePlugins/useChartVoronoi/useChartVoronoi.js +2 -5
- package/esm/internals/ticks.d.ts +6 -0
- package/esm/internals/ticks.js +22 -0
- package/hooks/useTicks.d.ts +1 -4
- package/hooks/useTicks.js +29 -29
- package/index.js +1 -1
- package/internals/constants.d.ts +1 -1
- package/internals/constants.js +2 -2
- package/internals/consumeSlots.js +2 -1
- package/internals/consumeThemeProps.js +3 -1
- package/internals/plugins/corePlugins/useChartDimensions/useChartDimensions.js +11 -17
- package/internals/plugins/corePlugins/useChartDimensions/useChartDimensions.types.d.ts +16 -13
- package/internals/plugins/featurePlugins/useChartCartesianAxis/computeAxisValue.js +3 -3
- package/internals/plugins/featurePlugins/useChartCartesianAxis/defaultizeAxis.js +7 -8
- package/internals/plugins/featurePlugins/useChartCartesianAxis/defaultizeZoom.js +3 -1
- package/internals/plugins/featurePlugins/useChartCartesianAxis/useChartAxisSize.selectors.js +4 -5
- package/internals/plugins/featurePlugins/useChartCartesianAxis/useChartCartesianAxis.js +1 -3
- package/internals/plugins/featurePlugins/useChartCartesianAxis/zoom.types.d.ts +8 -0
- package/internals/plugins/featurePlugins/useChartPolarAxis/computeAxisValue.js +3 -3
- package/internals/plugins/featurePlugins/useChartPolarAxis/useChartPolarAxis.js +1 -3
- package/internals/plugins/featurePlugins/useChartVoronoi/useChartVoronoi.js +2 -5
- package/internals/ticks.d.ts +6 -0
- package/internals/ticks.js +29 -0
- package/package.json +3 -3
package/esm/hooks/useTicks.d.ts
CHANGED
|
@@ -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/esm/hooks/useTicks.js
CHANGED
|
@@ -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
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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,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
|
|
4
|
+
export declare const DEFAULT_ZOOM_SLIDER_SIZE: number;
|
|
@@ -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
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
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 (
|
|
152
|
+
if (targetElement && targetElement.closest('[data-drawing-container]')) {
|
|
152
153
|
return true;
|
|
153
154
|
}
|
|
154
|
-
|
|
155
|
-
|
|
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 {
|
|
69
|
-
* @param {number}
|
|
70
|
-
* @param {
|
|
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: (
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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 "
|
|
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
|
|
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] +=
|
|
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] +=
|
|
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) => {
|
package/esm/internals/plugins/featurePlugins/useChartCartesianAxis/useChartAxisSize.selectors.js
CHANGED
|
@@ -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 ?
|
|
5
|
-
export const selectorChartRightAxisSize = createSelector([selectorChartRawYAxis], yAxis => (yAxis ?? []).reduce((acc, axis) => axis.position === 'right' ? acc + (axis.width || 0) + (axis.zoom?.slider.enabled ?
|
|
6
|
-
export const selectorChartTopAxisSize = createSelector([selectorChartRawXAxis], xAxis => (xAxis ?? []).reduce((acc, axis) => axis.position === 'top' ? acc + (axis.height || 0) + (axis.zoom?.slider.enabled ?
|
|
7
|
-
export const selectorChartBottomAxisSize = createSelector([selectorChartRawXAxis], xAxis => (xAxis ?? []).reduce((acc, axis) => axis.position === 'bottom' ? acc + (axis.height || 0) + (axis.zoom?.slider.enabled ?
|
|
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 "
|
|
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
|
|
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,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
|
+
}
|
package/hooks/useTicks.d.ts
CHANGED
|
@@ -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
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
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
package/internals/constants.d.ts
CHANGED
|
@@ -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
|
|
4
|
+
export declare const DEFAULT_ZOOM_SLIDER_SIZE: number;
|
package/internals/constants.js
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.
|
|
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
|
|
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
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
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 (
|
|
159
|
+
if (targetElement && targetElement.closest('[data-drawing-container]')) {
|
|
159
160
|
return true;
|
|
160
161
|
}
|
|
161
|
-
|
|
162
|
-
|
|
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
|
};
|