@mui/x-charts 9.10.0 → 9.10.1
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/seriesConfig/bar/keyboardFocusHandler.js +3 -1
- package/BarChart/seriesConfig/bar/keyboardFocusHandler.mjs +3 -1
- package/CHANGELOG.md +106 -0
- package/ChartsAxisHighlight/getSampledBandHighlight.d.mts +1 -1
- package/ChartsAxisHighlight/getSampledBandHighlight.d.ts +1 -1
- package/ChartsAxisHighlight/getSampledBandHighlight.js +15 -3
- package/ChartsAxisHighlight/getSampledBandHighlight.mjs +15 -3
- package/LineChart/seriesConfig/keyboardFocusHandler.js +3 -1
- package/LineChart/seriesConfig/keyboardFocusHandler.mjs +3 -1
- package/RadarChart/RadarAxis/RadarAxis.utils.d.mts +1 -1
- package/RadarChart/RadarAxis/RadarAxis.utils.d.ts +1 -1
- package/RadarChart/seriesConfig/keyboardFocusHandler.js +3 -1
- package/RadarChart/seriesConfig/keyboardFocusHandler.mjs +3 -1
- package/hooks/animation/useAnimatePieArc.js +2 -1
- package/hooks/animation/useAnimatePieArc.mjs +2 -1
- package/index.js +1 -1
- package/index.mjs +1 -1
- package/internals/commonNextFocusItem.d.mts +2 -2
- package/internals/commonNextFocusItem.d.ts +2 -2
- package/internals/commonNextFocusItem.js +4 -4
- package/internals/commonNextFocusItem.mjs +4 -4
- package/internals/components/ChartsAccessibilityProxy/ChartsAccessibilityProxy.js +1 -0
- package/internals/components/ChartsAccessibilityProxy/ChartsAccessibilityProxy.mjs +1 -0
- package/internals/createGetBarDimensions.js +10 -5
- package/internals/createGetBarDimensions.mjs +10 -5
- package/internals/getSampledBucketRegion.d.mts +12 -0
- package/internals/getSampledBucketRegion.d.ts +12 -0
- package/internals/getSampledBucketRegion.js +23 -0
- package/internals/getSampledBucketRegion.mjs +17 -0
- package/internals/plugins/featurePlugins/useChartKeyboardNavigation/utils/findVisibleDataIndex.d.mts +3 -0
- package/internals/plugins/featurePlugins/useChartKeyboardNavigation/utils/findVisibleDataIndex.d.ts +3 -0
- package/internals/plugins/featurePlugins/useChartKeyboardNavigation/utils/findVisibleDataIndex.js +3 -0
- package/internals/plugins/featurePlugins/useChartKeyboardNavigation/utils/findVisibleDataIndex.mjs +3 -0
- package/internals/snapEndAngleToFullCircle.d.mts +20 -0
- package/internals/snapEndAngleToFullCircle.d.ts +20 -0
- package/internals/snapEndAngleToFullCircle.js +47 -0
- package/internals/snapEndAngleToFullCircle.mjs +41 -0
- package/package.json +4 -4
|
@@ -6,5 +6,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
exports.default = void 0;
|
|
7
7
|
var _createCommonKeyboardFocusHandler = require("../../../internals/createCommonKeyboardFocusHandler");
|
|
8
8
|
var _composition = require("../../../models/seriesType/composition");
|
|
9
|
-
const
|
|
9
|
+
const allowCycles = false;
|
|
10
|
+
const useCurrentSeriesMaxLength = false;
|
|
11
|
+
const keyboardFocusHandler = (0, _createCommonKeyboardFocusHandler.createCommonKeyboardFocusHandler)(_composition.composableCartesianSeriesTypes, allowCycles, useCurrentSeriesMaxLength);
|
|
10
12
|
var _default = exports.default = keyboardFocusHandler;
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { createCommonKeyboardFocusHandler } from "../../../internals/createCommonKeyboardFocusHandler.mjs";
|
|
2
2
|
import { composableCartesianSeriesTypes } from "../../../models/seriesType/composition.mjs";
|
|
3
|
-
const
|
|
3
|
+
const allowCycles = false;
|
|
4
|
+
const useCurrentSeriesMaxLength = false;
|
|
5
|
+
const keyboardFocusHandler = createCommonKeyboardFocusHandler(composableCartesianSeriesTypes, allowCycles, useCurrentSeriesMaxLength);
|
|
4
6
|
export default keyboardFocusHandler;
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,111 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 9.10.1
|
|
4
|
+
|
|
5
|
+
_Jul 23, 2026_
|
|
6
|
+
|
|
7
|
+
We'd like to extend a big thank you to the 11 contributors who made this release possible. Here are some highlights ✨:
|
|
8
|
+
|
|
9
|
+
- 🦮 Improve accessibility of the Charts and Pickers components.
|
|
10
|
+
- 🐞 Bugfixes
|
|
11
|
+
- 📚 Documentation improvements
|
|
12
|
+
|
|
13
|
+
Special thanks go out to these community members for their valuable contributions:
|
|
14
|
+
@kevincorizi-sbt, @mustafajw07, @SamanPandey-in
|
|
15
|
+
|
|
16
|
+
The following team members contributed to this release:
|
|
17
|
+
@brijeshb42, @hasdfa, @JCQuintas, @LukasTy, @michelengelen, @noraleonte, @rita-codes, @silviuaavram
|
|
18
|
+
|
|
19
|
+
### Data Grid
|
|
20
|
+
|
|
21
|
+
#### `@mui/x-data-grid@9.10.1`
|
|
22
|
+
|
|
23
|
+
- [data grid] Fix `getColumn` return type not reflecting that it can return undefined (#23165) @JCQuintas
|
|
24
|
+
|
|
25
|
+
#### `@mui/x-data-grid-pro@9.10.1` [](https://mui.com/r/x-pro-svg-link 'Pro plan')
|
|
26
|
+
|
|
27
|
+
Same changes as in `@mui/x-data-grid@9.10.1`.
|
|
28
|
+
|
|
29
|
+
#### `@mui/x-data-grid-premium@9.10.1` [](https://mui.com/r/x-premium-svg-link 'Premium plan')
|
|
30
|
+
|
|
31
|
+
Same changes as in `@mui/x-data-grid-pro@9.10.1`.
|
|
32
|
+
|
|
33
|
+
### Date and Time Pickers
|
|
34
|
+
|
|
35
|
+
#### `@mui/x-date-pickers@9.10.1`
|
|
36
|
+
|
|
37
|
+
- [pickers] Fix format placeholder brightness with a start adornment (#23189) @LukasTy
|
|
38
|
+
|
|
39
|
+
#### `@mui/x-date-pickers-pro@9.10.1` [](https://mui.com/r/x-pro-svg-link 'Pro plan')
|
|
40
|
+
|
|
41
|
+
Same changes as in `@mui/x-date-pickers@9.10.1`, plus:
|
|
42
|
+
|
|
43
|
+
- [DateRangePicker] Fix broken active range position underline in single input range fields (#23166) @LukasTy
|
|
44
|
+
|
|
45
|
+
### Charts
|
|
46
|
+
|
|
47
|
+
#### `@mui/x-charts@9.10.1`
|
|
48
|
+
|
|
49
|
+
- [charts] Fix pie chart disappearing when an arc spans almost the full circle (#23145) @JCQuintas
|
|
50
|
+
- [charts] Set `aria-hidden` on accessibility proxy divs at initialization (#23186) @kevincorizi-sbt
|
|
51
|
+
- [charts] Fix keyboard navigation on series with different lengths (#23182) @JCQuintas
|
|
52
|
+
- [charts] Fix out of bounds keyboard navigation (#23180) @silviuaavram
|
|
53
|
+
|
|
54
|
+
#### `@mui/x-charts-pro@9.10.1` [](https://mui.com/r/x-pro-svg-link 'Pro plan')
|
|
55
|
+
|
|
56
|
+
Same changes as in `@mui/x-charts@9.10.1`, plus:
|
|
57
|
+
|
|
58
|
+
- [charts-pro] Fix highlight shifting on sampled bar charts (#23190) @noraleonte
|
|
59
|
+
- [charts-pro] Sampled band highlight falls back to single-band width for Date/object values (#23024) @SamanPandey-in
|
|
60
|
+
|
|
61
|
+
#### `@mui/x-charts-premium@9.10.1` [](https://mui.com/r/x-premium-svg-link 'Premium plan')
|
|
62
|
+
|
|
63
|
+
Same changes as in `@mui/x-charts-pro@9.10.1`.
|
|
64
|
+
|
|
65
|
+
### Tree View
|
|
66
|
+
|
|
67
|
+
#### `@mui/x-tree-view@9.10.1`
|
|
68
|
+
|
|
69
|
+
- [TreeView] Fix selection propagation to exclude disabled items (#23012) @michelengelen
|
|
70
|
+
|
|
71
|
+
#### `@mui/x-tree-view-pro@9.10.1` [](https://mui.com/r/x-pro-svg-link 'Pro plan')
|
|
72
|
+
|
|
73
|
+
Same changes as in `@mui/x-tree-view@9.10.1`.
|
|
74
|
+
|
|
75
|
+
### Scheduler
|
|
76
|
+
|
|
77
|
+
#### `@mui/x-scheduler@9.0.0-beta.7`
|
|
78
|
+
|
|
79
|
+
- [scheduler] Polish `EventCalendar` rendering for multi-resource events (#23161) @mustafajw07
|
|
80
|
+
- [scheduler] Responsive header (#22954) @noraleonte
|
|
81
|
+
|
|
82
|
+
#### `@mui/x-scheduler-premium@9.0.0-beta.7` [](https://mui.com/r/x-premium-svg-link 'Premium plan')
|
|
83
|
+
|
|
84
|
+
Same changes as in `@mui/x-scheduler@9.0.0-beta.7`, plus:
|
|
85
|
+
|
|
86
|
+
- [scheduler-premium] Add the dependencies data model (#23117) @rita-codes
|
|
87
|
+
- [scheduler-premium] Dependencies - Render the FS arrow (#23162) @rita-codes
|
|
88
|
+
|
|
89
|
+
### Codemod
|
|
90
|
+
|
|
91
|
+
#### `@mui/x-codemod@9.10.1`
|
|
92
|
+
|
|
93
|
+
Internal changes.
|
|
94
|
+
|
|
95
|
+
### Docs
|
|
96
|
+
|
|
97
|
+
- [docs] Fix clearing, layout shift, and prop leak in Pickers demos (#23191) @LukasTy
|
|
98
|
+
|
|
99
|
+
### Core
|
|
100
|
+
|
|
101
|
+
- [code-infra] Validate working of specific code-infra canary (#23207) @brijeshb42
|
|
102
|
+
|
|
103
|
+
### Miscellaneous
|
|
104
|
+
|
|
105
|
+
- [chat] Normalize message part link URLs (#23187) @hasdfa
|
|
106
|
+
- [x-chat] Fix object URL leaks for submitted attachments (#23198) @hasdfa
|
|
107
|
+
- [internals] Make disposable types self-contained without `esnext.disposable` lib (#23164) @JCQuintas
|
|
108
|
+
|
|
3
109
|
## 9.10.0
|
|
4
110
|
|
|
5
111
|
_Jul 17, 2026_
|
|
@@ -2,7 +2,7 @@ import type { D3OrdinalScale } from "../models/axis.mjs";
|
|
|
2
2
|
interface SampledBandHighlightParams {
|
|
3
3
|
scale: D3OrdinalScale;
|
|
4
4
|
/** The highlighted value. */
|
|
5
|
-
value: number | Date;
|
|
5
|
+
value: string | number | Date;
|
|
6
6
|
/** The data index of the highlighted value, when known (O(1) lookup). */
|
|
7
7
|
dataIndex: number | undefined;
|
|
8
8
|
/** The axis data. */
|
|
@@ -2,7 +2,7 @@ import type { D3OrdinalScale } from "../models/axis.js";
|
|
|
2
2
|
interface SampledBandHighlightParams {
|
|
3
3
|
scale: D3OrdinalScale;
|
|
4
4
|
/** The highlighted value. */
|
|
5
|
-
value: number | Date;
|
|
5
|
+
value: string | number | Date;
|
|
6
6
|
/** The data index of the highlighted value, when known (O(1) lookup). */
|
|
7
7
|
dataIndex: number | undefined;
|
|
8
8
|
/** The axis data. */
|
|
@@ -4,6 +4,14 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.getSampledBandHighlight = getSampledBandHighlight;
|
|
7
|
+
var _getSampledBucketRegion = require("../internals/getSampledBucketRegion");
|
|
8
|
+
function findDataIndex(data, value) {
|
|
9
|
+
if (value instanceof Date) {
|
|
10
|
+
const time = value.getTime();
|
|
11
|
+
return data.findIndex(item => item instanceof Date && item.getTime() === time);
|
|
12
|
+
}
|
|
13
|
+
return data.indexOf(value);
|
|
14
|
+
}
|
|
7
15
|
/**
|
|
8
16
|
* Computes the band highlight rectangle along the ordinal axis.
|
|
9
17
|
* When the series is sampled (`bucketSize > 1`), the band is widened to cover the whole merged bucket.
|
|
@@ -20,12 +28,16 @@ function getSampledBandHighlight({
|
|
|
20
28
|
let bandStart = scale(value) - halfPadding;
|
|
21
29
|
let bandSize = step;
|
|
22
30
|
if (bucketSize > 1 && data) {
|
|
23
|
-
const index = dataIndex ?? data
|
|
31
|
+
const index = dataIndex ?? findDataIndex(data, value);
|
|
24
32
|
if (index >= 0) {
|
|
25
33
|
const bucketStart = Math.floor(index / bucketSize) * bucketSize;
|
|
26
34
|
const bucketEnd = Math.min(bucketStart + bucketSize - 1, data.length - 1);
|
|
27
|
-
|
|
28
|
-
|
|
35
|
+
const {
|
|
36
|
+
regionStart,
|
|
37
|
+
regionSize
|
|
38
|
+
} = (0, _getSampledBucketRegion.getSampledBucketRegion)(scale, data, bucketStart, bucketEnd);
|
|
39
|
+
bandStart = regionStart;
|
|
40
|
+
bandSize = regionSize;
|
|
29
41
|
}
|
|
30
42
|
}
|
|
31
43
|
return {
|
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
import { getSampledBucketRegion } from "../internals/getSampledBucketRegion.mjs";
|
|
2
|
+
function findDataIndex(data, value) {
|
|
3
|
+
if (value instanceof Date) {
|
|
4
|
+
const time = value.getTime();
|
|
5
|
+
return data.findIndex(item => item instanceof Date && item.getTime() === time);
|
|
6
|
+
}
|
|
7
|
+
return data.indexOf(value);
|
|
8
|
+
}
|
|
1
9
|
/**
|
|
2
10
|
* Computes the band highlight rectangle along the ordinal axis.
|
|
3
11
|
* When the series is sampled (`bucketSize > 1`), the band is widened to cover the whole merged bucket.
|
|
@@ -14,12 +22,16 @@ export function getSampledBandHighlight({
|
|
|
14
22
|
let bandStart = scale(value) - halfPadding;
|
|
15
23
|
let bandSize = step;
|
|
16
24
|
if (bucketSize > 1 && data) {
|
|
17
|
-
const index = dataIndex ?? data
|
|
25
|
+
const index = dataIndex ?? findDataIndex(data, value);
|
|
18
26
|
if (index >= 0) {
|
|
19
27
|
const bucketStart = Math.floor(index / bucketSize) * bucketSize;
|
|
20
28
|
const bucketEnd = Math.min(bucketStart + bucketSize - 1, data.length - 1);
|
|
21
|
-
|
|
22
|
-
|
|
29
|
+
const {
|
|
30
|
+
regionStart,
|
|
31
|
+
regionSize
|
|
32
|
+
} = getSampledBucketRegion(scale, data, bucketStart, bucketEnd);
|
|
33
|
+
bandStart = regionStart;
|
|
34
|
+
bandSize = regionSize;
|
|
23
35
|
}
|
|
24
36
|
}
|
|
25
37
|
return {
|
|
@@ -6,5 +6,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
exports.default = void 0;
|
|
7
7
|
var _createCommonKeyboardFocusHandler = require("../../internals/createCommonKeyboardFocusHandler");
|
|
8
8
|
var _composition = require("../../models/seriesType/composition");
|
|
9
|
-
const
|
|
9
|
+
const allowCycles = false;
|
|
10
|
+
const useCurrentSeriesMaxLength = false;
|
|
11
|
+
const keyboardFocusHandler = (0, _createCommonKeyboardFocusHandler.createCommonKeyboardFocusHandler)(_composition.composableCartesianSeriesTypes, allowCycles, useCurrentSeriesMaxLength);
|
|
10
12
|
var _default = exports.default = keyboardFocusHandler;
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { createCommonKeyboardFocusHandler } from "../../internals/createCommonKeyboardFocusHandler.mjs";
|
|
2
2
|
import { composableCartesianSeriesTypes } from "../../models/seriesType/composition.mjs";
|
|
3
|
-
const
|
|
3
|
+
const allowCycles = false;
|
|
4
|
+
const useCurrentSeriesMaxLength = false;
|
|
5
|
+
const keyboardFocusHandler = createCommonKeyboardFocusHandler(composableCartesianSeriesTypes, allowCycles, useCurrentSeriesMaxLength);
|
|
4
6
|
export default keyboardFocusHandler;
|
|
@@ -5,11 +5,11 @@ interface GetLabelAttributesParams extends Required<Pick<RadarAxisProps, 'labelO
|
|
|
5
5
|
angle: number;
|
|
6
6
|
}
|
|
7
7
|
export declare function getLabelAttributes(params: GetLabelAttributesParams): {
|
|
8
|
-
transform?: undefined;
|
|
9
8
|
x: number;
|
|
10
9
|
y: number;
|
|
11
10
|
textAnchor: "end" | "inherit" | "middle" | "start" | undefined;
|
|
12
11
|
dominantBaseline: "alphabetic" | "auto" | "central" | "hanging" | "ideographic" | "inherit" | "mathematical" | "middle" | "no-change" | "reset-size" | "text-after-edge" | "text-before-edge" | "use-script" | undefined;
|
|
12
|
+
transform?: undefined;
|
|
13
13
|
} | {
|
|
14
14
|
x: number;
|
|
15
15
|
y: number;
|
|
@@ -5,11 +5,11 @@ interface GetLabelAttributesParams extends Required<Pick<RadarAxisProps, 'labelO
|
|
|
5
5
|
angle: number;
|
|
6
6
|
}
|
|
7
7
|
export declare function getLabelAttributes(params: GetLabelAttributesParams): {
|
|
8
|
-
transform?: undefined;
|
|
9
8
|
x: number;
|
|
10
9
|
y: number;
|
|
11
10
|
textAnchor: "end" | "inherit" | "middle" | "start" | undefined;
|
|
12
11
|
dominantBaseline: "alphabetic" | "auto" | "central" | "hanging" | "ideographic" | "inherit" | "mathematical" | "middle" | "no-change" | "reset-size" | "text-after-edge" | "text-before-edge" | "use-script" | undefined;
|
|
12
|
+
transform?: undefined;
|
|
13
13
|
} | {
|
|
14
14
|
x: number;
|
|
15
15
|
y: number;
|
|
@@ -6,5 +6,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
exports.default = void 0;
|
|
7
7
|
var _createCommonKeyboardFocusHandler = require("../../internals/createCommonKeyboardFocusHandler");
|
|
8
8
|
const outSeriesTypes = new Set(['radar']);
|
|
9
|
-
const
|
|
9
|
+
const allowCycles = true;
|
|
10
|
+
const useCurrentSeriesMaxLength = false;
|
|
11
|
+
const keyboardFocusHandler = (0, _createCommonKeyboardFocusHandler.createCommonKeyboardFocusHandler)(outSeriesTypes, allowCycles, useCurrentSeriesMaxLength);
|
|
10
12
|
var _default = exports.default = keyboardFocusHandler;
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { createCommonKeyboardFocusHandler } from "../../internals/createCommonKeyboardFocusHandler.mjs";
|
|
2
2
|
const outSeriesTypes = new Set(['radar']);
|
|
3
|
-
const
|
|
3
|
+
const allowCycles = true;
|
|
4
|
+
const useCurrentSeriesMaxLength = false;
|
|
5
|
+
const keyboardFocusHandler = createCommonKeyboardFocusHandler(outSeriesTypes, allowCycles, useCurrentSeriesMaxLength);
|
|
4
6
|
export default keyboardFocusHandler;
|
|
@@ -7,6 +7,7 @@ exports.useAnimatePieArc = useAnimatePieArc;
|
|
|
7
7
|
var _d3Shape = require("@mui/x-charts-vendor/d3-shape");
|
|
8
8
|
var _d3Interpolate = require("@mui/x-charts-vendor/d3-interpolate");
|
|
9
9
|
var _useAnimate = require("./useAnimate");
|
|
10
|
+
var _snapEndAngleToFullCircle = require("../../internals/snapEndAngleToFullCircle");
|
|
10
11
|
function pieArcPropsInterpolator(from, to) {
|
|
11
12
|
const interpolateStartAngle = (0, _d3Interpolate.interpolateNumber)(from.startAngle, to.startAngle);
|
|
12
13
|
const interpolateEndAngle = (0, _d3Interpolate.interpolateNumber)(from.endAngle, to.endAngle);
|
|
@@ -53,7 +54,7 @@ function useAnimatePieArc(props) {
|
|
|
53
54
|
innerRadius: p.innerRadius,
|
|
54
55
|
outerRadius: p.outerRadius,
|
|
55
56
|
startAngle: p.startAngle,
|
|
56
|
-
endAngle: p.endAngle
|
|
57
|
+
endAngle: (0, _snapEndAngleToFullCircle.snapEndAngleToFullCircle)(p.startAngle, p.endAngle, p.outerRadius, p.paddingAngle)
|
|
57
58
|
}),
|
|
58
59
|
visibility: p.startAngle === p.endAngle ? 'hidden' : 'visible'
|
|
59
60
|
}),
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { arc as d3Arc } from '@mui/x-charts-vendor/d3-shape';
|
|
2
2
|
import { interpolateNumber } from '@mui/x-charts-vendor/d3-interpolate';
|
|
3
3
|
import { useAnimate } from "./useAnimate.mjs";
|
|
4
|
+
import { snapEndAngleToFullCircle } from "../../internals/snapEndAngleToFullCircle.mjs";
|
|
4
5
|
function pieArcPropsInterpolator(from, to) {
|
|
5
6
|
const interpolateStartAngle = interpolateNumber(from.startAngle, to.startAngle);
|
|
6
7
|
const interpolateEndAngle = interpolateNumber(from.endAngle, to.endAngle);
|
|
@@ -47,7 +48,7 @@ export function useAnimatePieArc(props) {
|
|
|
47
48
|
innerRadius: p.innerRadius,
|
|
48
49
|
outerRadius: p.outerRadius,
|
|
49
50
|
startAngle: p.startAngle,
|
|
50
|
-
endAngle: p.endAngle
|
|
51
|
+
endAngle: snapEndAngleToFullCircle(p.startAngle, p.endAngle, p.outerRadius, p.paddingAngle)
|
|
51
52
|
}),
|
|
52
53
|
visibility: p.startAngle === p.endAngle ? 'hidden' : 'visible'
|
|
53
54
|
}),
|
package/index.js
CHANGED
package/index.mjs
CHANGED
|
@@ -28,7 +28,7 @@ compatibleSeriesTypes: Set<OutSeriesType>,
|
|
|
28
28
|
*/
|
|
29
29
|
allowCycles?: boolean,
|
|
30
30
|
/**
|
|
31
|
-
* If true, series max index is defined by the current series length and not all series.
|
|
31
|
+
* If true (default), series max index is defined by the current series length and not all series.
|
|
32
32
|
*/
|
|
33
33
|
useCurrentSeriesMaxLength?: boolean): (currentItem: WorkingItem | null, state: StateParameters<InSeriesType>) => ReturnedItem<OutSeriesType>;
|
|
34
34
|
export declare function createGetPreviousIndexFocusedItem<InSeriesType extends Exclude<ChartSeriesType, 'sankey' | 'heatmap'>, OutSeriesType extends Exclude<ChartSeriesType, 'sankey' | 'heatmap'> = InSeriesType>(
|
|
@@ -41,7 +41,7 @@ compatibleSeriesTypes: Set<OutSeriesType>,
|
|
|
41
41
|
*/
|
|
42
42
|
allowCycles?: boolean,
|
|
43
43
|
/**
|
|
44
|
-
* If true, series max index is defined by the current series length and not all series.
|
|
44
|
+
* If true (default), series max index is defined by the current series length and not all series.
|
|
45
45
|
*/
|
|
46
46
|
useCurrentSeriesMaxLength?: boolean): (currentItem: WorkingItem | null, state: StateParameters<InSeriesType>) => ReturnedItem<OutSeriesType>;
|
|
47
47
|
export declare function createGetNextSeriesFocusedItem<InSeriesType extends Exclude<ChartSeriesType, 'sankey' | 'heatmap'>, OutSeriesType extends Exclude<ChartSeriesType, 'sankey' | 'heatmap'> = InSeriesType>(
|
|
@@ -28,7 +28,7 @@ compatibleSeriesTypes: Set<OutSeriesType>,
|
|
|
28
28
|
*/
|
|
29
29
|
allowCycles?: boolean,
|
|
30
30
|
/**
|
|
31
|
-
* If true, series max index is defined by the current series length and not all series.
|
|
31
|
+
* If true (default), series max index is defined by the current series length and not all series.
|
|
32
32
|
*/
|
|
33
33
|
useCurrentSeriesMaxLength?: boolean): (currentItem: WorkingItem | null, state: StateParameters<InSeriesType>) => ReturnedItem<OutSeriesType>;
|
|
34
34
|
export declare function createGetPreviousIndexFocusedItem<InSeriesType extends Exclude<ChartSeriesType, 'sankey' | 'heatmap'>, OutSeriesType extends Exclude<ChartSeriesType, 'sankey' | 'heatmap'> = InSeriesType>(
|
|
@@ -41,7 +41,7 @@ compatibleSeriesTypes: Set<OutSeriesType>,
|
|
|
41
41
|
*/
|
|
42
42
|
allowCycles?: boolean,
|
|
43
43
|
/**
|
|
44
|
-
* If true, series max index is defined by the current series length and not all series.
|
|
44
|
+
* If true (default), series max index is defined by the current series length and not all series.
|
|
45
45
|
*/
|
|
46
46
|
useCurrentSeriesMaxLength?: boolean): (currentItem: WorkingItem | null, state: StateParameters<InSeriesType>) => ReturnedItem<OutSeriesType>;
|
|
47
47
|
export declare function createGetNextSeriesFocusedItem<InSeriesType extends Exclude<ChartSeriesType, 'sankey' | 'heatmap'>, OutSeriesType extends Exclude<ChartSeriesType, 'sankey' | 'heatmap'> = InSeriesType>(
|
|
@@ -33,9 +33,9 @@ compatibleSeriesTypes,
|
|
|
33
33
|
*/
|
|
34
34
|
allowCycles = false,
|
|
35
35
|
/**
|
|
36
|
-
* If true, series max index is defined by the current series length and not all series.
|
|
36
|
+
* If true (default), series max index is defined by the current series length and not all series.
|
|
37
37
|
*/
|
|
38
|
-
useCurrentSeriesMaxLength =
|
|
38
|
+
useCurrentSeriesMaxLength = true) {
|
|
39
39
|
return function getNextIndexFocusedItem(currentItem, state) {
|
|
40
40
|
const processedSeries = (0, _useChartSeries.selectorChartSeriesProcessed)(state);
|
|
41
41
|
let seriesId = currentItem?.seriesId;
|
|
@@ -84,9 +84,9 @@ compatibleSeriesTypes,
|
|
|
84
84
|
*/
|
|
85
85
|
allowCycles = false,
|
|
86
86
|
/**
|
|
87
|
-
* If true, series max index is defined by the current series length and not all series.
|
|
87
|
+
* If true (default), series max index is defined by the current series length and not all series.
|
|
88
88
|
*/
|
|
89
|
-
useCurrentSeriesMaxLength =
|
|
89
|
+
useCurrentSeriesMaxLength = true) {
|
|
90
90
|
return function getPreviousIndexFocusedItem(currentItem, state) {
|
|
91
91
|
const processedSeries = (0, _useChartSeries.selectorChartSeriesProcessed)(state);
|
|
92
92
|
let seriesId = currentItem?.seriesId;
|
|
@@ -25,9 +25,9 @@ compatibleSeriesTypes,
|
|
|
25
25
|
*/
|
|
26
26
|
allowCycles = false,
|
|
27
27
|
/**
|
|
28
|
-
* If true, series max index is defined by the current series length and not all series.
|
|
28
|
+
* If true (default), series max index is defined by the current series length and not all series.
|
|
29
29
|
*/
|
|
30
|
-
useCurrentSeriesMaxLength =
|
|
30
|
+
useCurrentSeriesMaxLength = true) {
|
|
31
31
|
return function getNextIndexFocusedItem(currentItem, state) {
|
|
32
32
|
const processedSeries = selectorChartSeriesProcessed(state);
|
|
33
33
|
let seriesId = currentItem?.seriesId;
|
|
@@ -76,9 +76,9 @@ compatibleSeriesTypes,
|
|
|
76
76
|
*/
|
|
77
77
|
allowCycles = false,
|
|
78
78
|
/**
|
|
79
|
-
* If true, series max index is defined by the current series length and not all series.
|
|
79
|
+
* If true (default), series max index is defined by the current series length and not all series.
|
|
80
80
|
*/
|
|
81
|
-
useCurrentSeriesMaxLength =
|
|
81
|
+
useCurrentSeriesMaxLength = true) {
|
|
82
82
|
return function getPreviousIndexFocusedItem(currentItem, state) {
|
|
83
83
|
const processedSeries = selectorChartSeriesProcessed(state);
|
|
84
84
|
let seriesId = currentItem?.seriesId;
|
|
@@ -60,6 +60,7 @@ function ChartsAccessibilityProxy() {
|
|
|
60
60
|
div.setAttribute('tabindex', '0');
|
|
61
61
|
}
|
|
62
62
|
div.setAttribute('role', 'img');
|
|
63
|
+
div.setAttribute('aria-hidden', 'true');
|
|
63
64
|
div.setAttribute('aria-labelledby', i === 0 ? `voiceover-${chartId}-1` : `voiceover-${chartId}-2`);
|
|
64
65
|
div.style.width = '100%';
|
|
65
66
|
div.style.height = '100%';
|
|
@@ -56,6 +56,7 @@ export function ChartsAccessibilityProxy() {
|
|
|
56
56
|
div.setAttribute('tabindex', '0');
|
|
57
57
|
}
|
|
58
58
|
div.setAttribute('role', 'img');
|
|
59
|
+
div.setAttribute('aria-hidden', 'true');
|
|
59
60
|
div.setAttribute('aria-labelledby', i === 0 ? `voiceover-${chartId}-1` : `voiceover-${chartId}-2`);
|
|
60
61
|
div.style.width = '100%';
|
|
61
62
|
div.style.height = '100%';
|
|
@@ -7,6 +7,7 @@ exports.createGetBarDimensions = createGetBarDimensions;
|
|
|
7
7
|
exports.createGetBucketBarDimensions = createGetBucketBarDimensions;
|
|
8
8
|
var _findMinMax = require("./findMinMax");
|
|
9
9
|
var _getBandSize = require("./getBandSize");
|
|
10
|
+
var _getSampledBucketRegion = require("./getSampledBucketRegion");
|
|
10
11
|
/** Minimum on-screen gap (px) kept between merged (sampled) bars so they stay distinguishable. */
|
|
11
12
|
const MIN_SAMPLED_BAR_GAP_PX = 2;
|
|
12
13
|
function shouldInvertStartCoordinate(verticalLayout, baseValue, reverse) {
|
|
@@ -78,9 +79,12 @@ function createGetBucketBarDimensions(params) {
|
|
|
78
79
|
const step = baseScale.step();
|
|
79
80
|
const valueScale = verticalLayout ? yAxisConfig.scale : xAxisConfig.scale;
|
|
80
81
|
return function getBucketBarDimensions(startIndex, endIndex, min, max, groupIndex) {
|
|
81
|
-
|
|
82
|
+
// Region matches the axis highlight (see `getSampledBandHighlight`) so bar and highlight stay in sync.
|
|
83
|
+
const {
|
|
84
|
+
regionStart,
|
|
85
|
+
regionSize: bucketStride
|
|
86
|
+
} = (0, _getSampledBucketRegion.getSampledBucketRegion)(baseScale, baseScaleConfig.data, startIndex, endIndex);
|
|
82
87
|
const bucketCount = endIndex - startIndex + 1;
|
|
83
|
-
const bucketStride = bucketCount * step;
|
|
84
88
|
|
|
85
89
|
// Keep the gap between buckets proportional to the bucket (same ratio as an unsampled chart),
|
|
86
90
|
// but never thinner than MIN_SAMPLED_BAR_GAP_PX so it stays visible at every level and zoom.
|
|
@@ -89,7 +93,8 @@ function createGetBucketBarDimensions(params) {
|
|
|
89
93
|
barWidth,
|
|
90
94
|
offset
|
|
91
95
|
} = (0, _getBandSize.getBandSize)(bucketStride - gap, numberOfGroups, baseScaleConfig.barGapRatio);
|
|
92
|
-
|
|
96
|
+
// Center the bars in the region (split the gap to both sides) so they sit under the highlight.
|
|
97
|
+
const barOffset = gap / 2 + groupIndex * (barWidth + offset);
|
|
93
98
|
const valueCoordinates = [valueScale(min), valueScale(max)];
|
|
94
99
|
const [minValueCoord, maxValueCoord] = (0, _findMinMax.findMinMax)(valueCoordinates).map(v => Math.round(v));
|
|
95
100
|
let barSize = maxValueCoord - minValueCoord;
|
|
@@ -97,8 +102,8 @@ function createGetBucketBarDimensions(params) {
|
|
|
97
102
|
barSize = Math.max(series.minBarSize, barSize);
|
|
98
103
|
}
|
|
99
104
|
return {
|
|
100
|
-
x: verticalLayout ?
|
|
101
|
-
y: verticalLayout ? minValueCoord :
|
|
105
|
+
x: verticalLayout ? regionStart + barOffset : minValueCoord,
|
|
106
|
+
y: verticalLayout ? minValueCoord : regionStart + barOffset,
|
|
102
107
|
height: verticalLayout ? barSize : barWidth,
|
|
103
108
|
width: verticalLayout ? barWidth : barSize
|
|
104
109
|
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { findMinMax } from "./findMinMax.mjs";
|
|
2
2
|
import { getBandSize } from "./getBandSize.mjs";
|
|
3
|
+
import { getSampledBucketRegion } from "./getSampledBucketRegion.mjs";
|
|
3
4
|
|
|
4
5
|
/** Minimum on-screen gap (px) kept between merged (sampled) bars so they stay distinguishable. */
|
|
5
6
|
const MIN_SAMPLED_BAR_GAP_PX = 2;
|
|
@@ -72,9 +73,12 @@ export function createGetBucketBarDimensions(params) {
|
|
|
72
73
|
const step = baseScale.step();
|
|
73
74
|
const valueScale = verticalLayout ? yAxisConfig.scale : xAxisConfig.scale;
|
|
74
75
|
return function getBucketBarDimensions(startIndex, endIndex, min, max, groupIndex) {
|
|
75
|
-
|
|
76
|
+
// Region matches the axis highlight (see `getSampledBandHighlight`) so bar and highlight stay in sync.
|
|
77
|
+
const {
|
|
78
|
+
regionStart,
|
|
79
|
+
regionSize: bucketStride
|
|
80
|
+
} = getSampledBucketRegion(baseScale, baseScaleConfig.data, startIndex, endIndex);
|
|
76
81
|
const bucketCount = endIndex - startIndex + 1;
|
|
77
|
-
const bucketStride = bucketCount * step;
|
|
78
82
|
|
|
79
83
|
// Keep the gap between buckets proportional to the bucket (same ratio as an unsampled chart),
|
|
80
84
|
// but never thinner than MIN_SAMPLED_BAR_GAP_PX so it stays visible at every level and zoom.
|
|
@@ -83,7 +87,8 @@ export function createGetBucketBarDimensions(params) {
|
|
|
83
87
|
barWidth,
|
|
84
88
|
offset
|
|
85
89
|
} = getBandSize(bucketStride - gap, numberOfGroups, baseScaleConfig.barGapRatio);
|
|
86
|
-
|
|
90
|
+
// Center the bars in the region (split the gap to both sides) so they sit under the highlight.
|
|
91
|
+
const barOffset = gap / 2 + groupIndex * (barWidth + offset);
|
|
87
92
|
const valueCoordinates = [valueScale(min), valueScale(max)];
|
|
88
93
|
const [minValueCoord, maxValueCoord] = findMinMax(valueCoordinates).map(v => Math.round(v));
|
|
89
94
|
let barSize = maxValueCoord - minValueCoord;
|
|
@@ -91,8 +96,8 @@ export function createGetBucketBarDimensions(params) {
|
|
|
91
96
|
barSize = Math.max(series.minBarSize, barSize);
|
|
92
97
|
}
|
|
93
98
|
return {
|
|
94
|
-
x: verticalLayout ?
|
|
95
|
-
y: verticalLayout ? minValueCoord :
|
|
99
|
+
x: verticalLayout ? regionStart + barOffset : minValueCoord,
|
|
100
|
+
y: verticalLayout ? minValueCoord : regionStart + barOffset,
|
|
96
101
|
height: verticalLayout ? barSize : barWidth,
|
|
97
102
|
width: verticalLayout ? barWidth : barSize
|
|
98
103
|
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { D3OrdinalScale } from "../models/axis.mjs";
|
|
2
|
+
/**
|
|
3
|
+
* Computes the band region covered by a sampled bucket spanning `data[startIndex..endIndex]`.
|
|
4
|
+
*
|
|
5
|
+
* The region is anchored at its left-most slot and spans whole steps, so both the rendered bar
|
|
6
|
+
* (see `createGetBucketBarDimensions`) and the axis highlight (see `getSampledBandHighlight`)
|
|
7
|
+
* stay in sync — on a reversed axis `startIndex` maps to the right, hence the `Math.min`.
|
|
8
|
+
*/
|
|
9
|
+
export declare function getSampledBucketRegion(scale: D3OrdinalScale, data: readonly any[], startIndex: number, endIndex: number): {
|
|
10
|
+
regionStart: number;
|
|
11
|
+
regionSize: number;
|
|
12
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { D3OrdinalScale } from "../models/axis.js";
|
|
2
|
+
/**
|
|
3
|
+
* Computes the band region covered by a sampled bucket spanning `data[startIndex..endIndex]`.
|
|
4
|
+
*
|
|
5
|
+
* The region is anchored at its left-most slot and spans whole steps, so both the rendered bar
|
|
6
|
+
* (see `createGetBucketBarDimensions`) and the axis highlight (see `getSampledBandHighlight`)
|
|
7
|
+
* stay in sync — on a reversed axis `startIndex` maps to the right, hence the `Math.min`.
|
|
8
|
+
*/
|
|
9
|
+
export declare function getSampledBucketRegion(scale: D3OrdinalScale, data: readonly any[], startIndex: number, endIndex: number): {
|
|
10
|
+
regionStart: number;
|
|
11
|
+
regionSize: number;
|
|
12
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.getSampledBucketRegion = getSampledBucketRegion;
|
|
7
|
+
/**
|
|
8
|
+
* Computes the band region covered by a sampled bucket spanning `data[startIndex..endIndex]`.
|
|
9
|
+
*
|
|
10
|
+
* The region is anchored at its left-most slot and spans whole steps, so both the rendered bar
|
|
11
|
+
* (see `createGetBucketBarDimensions`) and the axis highlight (see `getSampledBandHighlight`)
|
|
12
|
+
* stay in sync — on a reversed axis `startIndex` maps to the right, hence the `Math.min`.
|
|
13
|
+
*/
|
|
14
|
+
function getSampledBucketRegion(scale, data, startIndex, endIndex) {
|
|
15
|
+
const step = scale.step();
|
|
16
|
+
const halfPadding = (step - scale.bandwidth()) / 2;
|
|
17
|
+
const startPos = scale(data[startIndex]);
|
|
18
|
+
const endPos = scale(data[endIndex]);
|
|
19
|
+
return {
|
|
20
|
+
regionStart: Math.min(startPos, endPos) - halfPadding,
|
|
21
|
+
regionSize: (endIndex - startIndex + 1) * step
|
|
22
|
+
};
|
|
23
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Computes the band region covered by a sampled bucket spanning `data[startIndex..endIndex]`.
|
|
3
|
+
*
|
|
4
|
+
* The region is anchored at its left-most slot and spans whole steps, so both the rendered bar
|
|
5
|
+
* (see `createGetBucketBarDimensions`) and the axis highlight (see `getSampledBandHighlight`)
|
|
6
|
+
* stay in sync — on a reversed axis `startIndex` maps to the right, hence the `Math.min`.
|
|
7
|
+
*/
|
|
8
|
+
export function getSampledBucketRegion(scale, data, startIndex, endIndex) {
|
|
9
|
+
const step = scale.step();
|
|
10
|
+
const halfPadding = (step - scale.bandwidth()) / 2;
|
|
11
|
+
const startPos = scale(data[startIndex]);
|
|
12
|
+
const endPos = scale(data[endIndex]);
|
|
13
|
+
return {
|
|
14
|
+
regionStart: Math.min(startPos, endPos) - halfPadding,
|
|
15
|
+
regionSize: (endIndex - startIndex + 1) * step
|
|
16
|
+
};
|
|
17
|
+
}
|
package/internals/plugins/featurePlugins/useChartKeyboardNavigation/utils/findVisibleDataIndex.d.mts
CHANGED
|
@@ -5,6 +5,9 @@ import type { ProcessedSeries } from "../../../corePlugins/useChartSeries/useCha
|
|
|
5
5
|
* Walk forward (or backward) from `startIndex` and return the first dataIndex
|
|
6
6
|
* whose data point is visible. Returns `null` if every traversed index is hidden.
|
|
7
7
|
*
|
|
8
|
+
* Shared-axis types (line, bar) opt out of `useCurrentSeriesMaxLength`, so `dataLength`
|
|
9
|
+
* can exceed the focused series length to focus an index with no value.
|
|
10
|
+
*
|
|
8
11
|
* Series-level hidden flags are filtered upstream (see `getNonEmptySeriesArray`),
|
|
9
12
|
* so only per-data-point hidden flags are relevant here. Today only Pie sets a
|
|
10
13
|
* `hidden` flag on individual data items; for other series types the data
|
package/internals/plugins/featurePlugins/useChartKeyboardNavigation/utils/findVisibleDataIndex.d.ts
CHANGED
|
@@ -5,6 +5,9 @@ import type { ProcessedSeries } from "../../../corePlugins/useChartSeries/useCha
|
|
|
5
5
|
* Walk forward (or backward) from `startIndex` and return the first dataIndex
|
|
6
6
|
* whose data point is visible. Returns `null` if every traversed index is hidden.
|
|
7
7
|
*
|
|
8
|
+
* Shared-axis types (line, bar) opt out of `useCurrentSeriesMaxLength`, so `dataLength`
|
|
9
|
+
* can exceed the focused series length to focus an index with no value.
|
|
10
|
+
*
|
|
8
11
|
* Series-level hidden flags are filtered upstream (see `getNonEmptySeriesArray`),
|
|
9
12
|
* so only per-data-point hidden flags are relevant here. Today only Pie sets a
|
|
10
13
|
* `hidden` flag on individual data items; for other series types the data
|
package/internals/plugins/featurePlugins/useChartKeyboardNavigation/utils/findVisibleDataIndex.js
CHANGED
|
@@ -8,6 +8,9 @@ exports.findVisibleDataIndex = findVisibleDataIndex;
|
|
|
8
8
|
* Walk forward (or backward) from `startIndex` and return the first dataIndex
|
|
9
9
|
* whose data point is visible. Returns `null` if every traversed index is hidden.
|
|
10
10
|
*
|
|
11
|
+
* Shared-axis types (line, bar) opt out of `useCurrentSeriesMaxLength`, so `dataLength`
|
|
12
|
+
* can exceed the focused series length to focus an index with no value.
|
|
13
|
+
*
|
|
11
14
|
* Series-level hidden flags are filtered upstream (see `getNonEmptySeriesArray`),
|
|
12
15
|
* so only per-data-point hidden flags are relevant here. Today only Pie sets a
|
|
13
16
|
* `hidden` flag on individual data items; for other series types the data
|
package/internals/plugins/featurePlugins/useChartKeyboardNavigation/utils/findVisibleDataIndex.mjs
CHANGED
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
* Walk forward (or backward) from `startIndex` and return the first dataIndex
|
|
3
3
|
* whose data point is visible. Returns `null` if every traversed index is hidden.
|
|
4
4
|
*
|
|
5
|
+
* Shared-axis types (line, bar) opt out of `useCurrentSeriesMaxLength`, so `dataLength`
|
|
6
|
+
* can exceed the focused series length to focus an index with no value.
|
|
7
|
+
*
|
|
5
8
|
* Series-level hidden flags are filtered upstream (see `getNonEmptySeriesArray`),
|
|
6
9
|
* so only per-data-point hidden flags are relevant here. Today only Pie sets a
|
|
7
10
|
* `hidden` flag on individual data items; for other series types the data
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Snaps the end angle of an arc to create a full circle when the arc spans almost, but not
|
|
3
|
+
* exactly, the full circle.
|
|
4
|
+
*
|
|
5
|
+
* When an arc spans almost the full circle (e.g., when the remaining slices of a pie are
|
|
6
|
+
* extremely small compared to the largest one), its start and end points are so close that they
|
|
7
|
+
* can round to the same path coordinates. SVG omits arcs whose end point equals their start
|
|
8
|
+
* point, which makes the quasi-full-circle arc invisible.
|
|
9
|
+
* See https://github.com/mui/mui-x/issues/14167
|
|
10
|
+
*
|
|
11
|
+
* The end angle is only snapped when the gap left by the arc is smaller than the precision of
|
|
12
|
+
* the serialized path, so the snapping is visually lossless.
|
|
13
|
+
*
|
|
14
|
+
* @param startAngle The start angle of the arc, in radians.
|
|
15
|
+
* @param endAngle The end angle of the arc, in radians.
|
|
16
|
+
* @param outerRadius The outer radius of the arc.
|
|
17
|
+
* @param padAngle The pad angle of the arc, in radians.
|
|
18
|
+
* @returns The end angle, snapped to `startAngle ± 2 * Math.PI` when applicable.
|
|
19
|
+
*/
|
|
20
|
+
export declare function snapEndAngleToFullCircle(startAngle: number, endAngle: number, outerRadius: number, padAngle?: number): number;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Snaps the end angle of an arc to create a full circle when the arc spans almost, but not
|
|
3
|
+
* exactly, the full circle.
|
|
4
|
+
*
|
|
5
|
+
* When an arc spans almost the full circle (e.g., when the remaining slices of a pie are
|
|
6
|
+
* extremely small compared to the largest one), its start and end points are so close that they
|
|
7
|
+
* can round to the same path coordinates. SVG omits arcs whose end point equals their start
|
|
8
|
+
* point, which makes the quasi-full-circle arc invisible.
|
|
9
|
+
* See https://github.com/mui/mui-x/issues/14167
|
|
10
|
+
*
|
|
11
|
+
* The end angle is only snapped when the gap left by the arc is smaller than the precision of
|
|
12
|
+
* the serialized path, so the snapping is visually lossless.
|
|
13
|
+
*
|
|
14
|
+
* @param startAngle The start angle of the arc, in radians.
|
|
15
|
+
* @param endAngle The end angle of the arc, in radians.
|
|
16
|
+
* @param outerRadius The outer radius of the arc.
|
|
17
|
+
* @param padAngle The pad angle of the arc, in radians.
|
|
18
|
+
* @returns The end angle, snapped to `startAngle ± 2 * Math.PI` when applicable.
|
|
19
|
+
*/
|
|
20
|
+
export declare function snapEndAngleToFullCircle(startAngle: number, endAngle: number, outerRadius: number, padAngle?: number): number;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.snapEndAngleToFullCircle = snapEndAngleToFullCircle;
|
|
7
|
+
const TAU = 2 * Math.PI;
|
|
8
|
+
|
|
9
|
+
/* `d3-shape` serializes paths with a precision of 3 digits, i.e., a resolution of 1e-3. */
|
|
10
|
+
const PATH_PRECISION = 1e-3;
|
|
11
|
+
/* If the distance between the start and end points of an arc is at least twice the path
|
|
12
|
+
* precision, at least one of their coordinates is guaranteed to round to a different value. */
|
|
13
|
+
const MIN_DISTINCT_CHORD_LENGTH = 2 * PATH_PRECISION;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Snaps the end angle of an arc to create a full circle when the arc spans almost, but not
|
|
17
|
+
* exactly, the full circle.
|
|
18
|
+
*
|
|
19
|
+
* When an arc spans almost the full circle (e.g., when the remaining slices of a pie are
|
|
20
|
+
* extremely small compared to the largest one), its start and end points are so close that they
|
|
21
|
+
* can round to the same path coordinates. SVG omits arcs whose end point equals their start
|
|
22
|
+
* point, which makes the quasi-full-circle arc invisible.
|
|
23
|
+
* See https://github.com/mui/mui-x/issues/14167
|
|
24
|
+
*
|
|
25
|
+
* The end angle is only snapped when the gap left by the arc is smaller than the precision of
|
|
26
|
+
* the serialized path, so the snapping is visually lossless.
|
|
27
|
+
*
|
|
28
|
+
* @param startAngle The start angle of the arc, in radians.
|
|
29
|
+
* @param endAngle The end angle of the arc, in radians.
|
|
30
|
+
* @param outerRadius The outer radius of the arc.
|
|
31
|
+
* @param padAngle The pad angle of the arc, in radians.
|
|
32
|
+
* @returns The end angle, snapped to `startAngle ± 2 * Math.PI` when applicable.
|
|
33
|
+
*/
|
|
34
|
+
function snapEndAngleToFullCircle(startAngle, endAngle, outerRadius, padAngle = 0) {
|
|
35
|
+
const sweep = Math.abs(endAngle - startAngle);
|
|
36
|
+
if (sweep === 0 || sweep >= TAU) {
|
|
37
|
+
return endAngle;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Chord between the start and end points of the arc, once the padding is applied.
|
|
41
|
+
// For such small angles, the chord length is approximately `radius * angle`.
|
|
42
|
+
const gapChordLength = (TAU - sweep + Math.abs(padAngle)) * outerRadius;
|
|
43
|
+
if (gapChordLength >= MIN_DISTINCT_CHORD_LENGTH) {
|
|
44
|
+
return endAngle;
|
|
45
|
+
}
|
|
46
|
+
return startAngle + Math.sign(endAngle - startAngle) * TAU;
|
|
47
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
const TAU = 2 * Math.PI;
|
|
2
|
+
|
|
3
|
+
/* `d3-shape` serializes paths with a precision of 3 digits, i.e., a resolution of 1e-3. */
|
|
4
|
+
const PATH_PRECISION = 1e-3;
|
|
5
|
+
/* If the distance between the start and end points of an arc is at least twice the path
|
|
6
|
+
* precision, at least one of their coordinates is guaranteed to round to a different value. */
|
|
7
|
+
const MIN_DISTINCT_CHORD_LENGTH = 2 * PATH_PRECISION;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Snaps the end angle of an arc to create a full circle when the arc spans almost, but not
|
|
11
|
+
* exactly, the full circle.
|
|
12
|
+
*
|
|
13
|
+
* When an arc spans almost the full circle (e.g., when the remaining slices of a pie are
|
|
14
|
+
* extremely small compared to the largest one), its start and end points are so close that they
|
|
15
|
+
* can round to the same path coordinates. SVG omits arcs whose end point equals their start
|
|
16
|
+
* point, which makes the quasi-full-circle arc invisible.
|
|
17
|
+
* See https://github.com/mui/mui-x/issues/14167
|
|
18
|
+
*
|
|
19
|
+
* The end angle is only snapped when the gap left by the arc is smaller than the precision of
|
|
20
|
+
* the serialized path, so the snapping is visually lossless.
|
|
21
|
+
*
|
|
22
|
+
* @param startAngle The start angle of the arc, in radians.
|
|
23
|
+
* @param endAngle The end angle of the arc, in radians.
|
|
24
|
+
* @param outerRadius The outer radius of the arc.
|
|
25
|
+
* @param padAngle The pad angle of the arc, in radians.
|
|
26
|
+
* @returns The end angle, snapped to `startAngle ± 2 * Math.PI` when applicable.
|
|
27
|
+
*/
|
|
28
|
+
export function snapEndAngleToFullCircle(startAngle, endAngle, outerRadius, padAngle = 0) {
|
|
29
|
+
const sweep = Math.abs(endAngle - startAngle);
|
|
30
|
+
if (sweep === 0 || sweep >= TAU) {
|
|
31
|
+
return endAngle;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Chord between the start and end points of the arc, once the padding is applied.
|
|
35
|
+
// For such small angles, the chord length is approximately `radius * angle`.
|
|
36
|
+
const gapChordLength = (TAU - sweep + Math.abs(padAngle)) * outerRadius;
|
|
37
|
+
if (gapChordLength >= MIN_DISTINCT_CHORD_LENGTH) {
|
|
38
|
+
return endAngle;
|
|
39
|
+
}
|
|
40
|
+
return startAngle + Math.sign(endAngle - startAngle) * TAU;
|
|
41
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mui/x-charts",
|
|
3
|
-
"version": "9.10.
|
|
3
|
+
"version": "9.10.1",
|
|
4
4
|
"author": "MUI Team",
|
|
5
5
|
"description": "The community edition of MUI X Charts components.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -34,9 +34,9 @@
|
|
|
34
34
|
"prop-types": "^15.8.1",
|
|
35
35
|
"reselect": "^5.2.0",
|
|
36
36
|
"use-sync-external-store": "^1.6.0",
|
|
37
|
-
"@mui/x-
|
|
38
|
-
"@mui/x-
|
|
39
|
-
"@mui/x-
|
|
37
|
+
"@mui/x-internals": "^9.10.1",
|
|
38
|
+
"@mui/x-internal-gestures": "^9.10.0",
|
|
39
|
+
"@mui/x-charts-vendor": "^9.4.0"
|
|
40
40
|
},
|
|
41
41
|
"peerDependencies": {
|
|
42
42
|
"@emotion/react": "^11.9.0",
|