@mui/x-charts 9.12.0 → 9.14.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 (120) hide show
  1. package/BarChart/BarChart.js +1 -1
  2. package/BarChart/BarChart.mjs +1 -1
  3. package/CHANGELOG.md +301 -0
  4. package/ChartsContainer/ChartsContainer.js +1 -1
  5. package/ChartsContainer/ChartsContainer.mjs +1 -1
  6. package/ChartsXAxis/useAxisTicksProps.js +2 -1
  7. package/ChartsXAxis/useAxisTicksProps.mjs +2 -1
  8. package/ChartsYAxis/shortenLabels.js +16 -26
  9. package/ChartsYAxis/shortenLabels.mjs +17 -27
  10. package/ChartsYAxis/useAxisTicksProps.js +2 -1
  11. package/ChartsYAxis/useAxisTicksProps.mjs +2 -1
  12. package/LineChart/LineChart.js +1 -1
  13. package/LineChart/LineChart.mjs +1 -1
  14. package/LineChart/MarkElement.js +13 -6
  15. package/LineChart/MarkElement.mjs +13 -6
  16. package/RadarChart/RadarChart.js +1 -1
  17. package/RadarChart/RadarChart.mjs +1 -1
  18. package/RadarChart/RadarSeriesPlot/RadarSeriesMarks.js +6 -2
  19. package/RadarChart/RadarSeriesPlot/RadarSeriesMarks.mjs +6 -2
  20. package/RadarChart/RadarSeriesPlot/RadarSeriesPlot.js +2 -1
  21. package/RadarChart/RadarSeriesPlot/RadarSeriesPlot.mjs +2 -1
  22. package/ScatterChart/ScatterChart.js +1 -1
  23. package/ScatterChart/ScatterChart.mjs +1 -1
  24. package/SparkLineChart/SparkLineChart.js +1 -1
  25. package/SparkLineChart/SparkLineChart.mjs +1 -1
  26. package/constants/index.d.mts +3 -1
  27. package/constants/index.d.ts +3 -1
  28. package/constants/index.js +5 -2
  29. package/constants/index.mjs +4 -1
  30. package/hooks/animation/index.d.mts +1 -0
  31. package/hooks/animation/index.d.ts +1 -0
  32. package/hooks/animation/index.js +11 -0
  33. package/hooks/animation/index.mjs +1 -0
  34. package/hooks/animation/useAnimateMark.d.mts +25 -0
  35. package/hooks/animation/useAnimateMark.d.ts +25 -0
  36. package/hooks/animation/useAnimateMark.js +45 -0
  37. package/hooks/animation/useAnimateMark.mjs +39 -0
  38. package/index.js +1 -1
  39. package/index.mjs +1 -1
  40. package/internals/components/ChartsAccessibilityProxy/ChartsAccessibilityProxy.js +31 -8
  41. package/internals/components/ChartsAccessibilityProxy/ChartsAccessibilityProxy.mjs +32 -9
  42. package/internals/components/ChartsAccessibilityProxy/useZoomAnnouncement.d.mts +9 -0
  43. package/internals/components/ChartsAccessibilityProxy/useZoomAnnouncement.d.ts +9 -0
  44. package/internals/components/ChartsAccessibilityProxy/useZoomAnnouncement.js +67 -0
  45. package/internals/components/ChartsAccessibilityProxy/useZoomAnnouncement.mjs +62 -0
  46. package/internals/domUtils.js +17 -16
  47. package/internals/domUtils.mjs +17 -15
  48. package/internals/ellipsize.d.mts +30 -0
  49. package/internals/ellipsize.d.ts +30 -0
  50. package/internals/ellipsize.js +60 -0
  51. package/internals/ellipsize.mjs +59 -0
  52. package/internals/index.d.mts +1 -0
  53. package/internals/index.d.ts +1 -0
  54. package/internals/index.js +8 -0
  55. package/internals/index.mjs +1 -0
  56. package/internals/plugins/featurePlugins/useChartCartesianAxis/computeAxisAutoSize.d.mts +3 -0
  57. package/internals/plugins/featurePlugins/useChartCartesianAxis/computeAxisAutoSize.d.ts +3 -0
  58. package/internals/plugins/featurePlugins/useChartCartesianAxis/computeAxisAutoSize.js +9 -5
  59. package/internals/plugins/featurePlugins/useChartCartesianAxis/computeAxisAutoSize.mjs +9 -5
  60. package/internals/plugins/featurePlugins/useChartCartesianAxis/useChartAxisAutoSize.selectors.js +9 -4
  61. package/internals/plugins/featurePlugins/useChartCartesianAxis/useChartAxisAutoSize.selectors.mjs +9 -4
  62. package/internals/plugins/featurePlugins/useChartCartesianAxis/useChartCartesianAxis.js +75 -3
  63. package/internals/plugins/featurePlugins/useChartCartesianAxis/useChartCartesianAxis.mjs +76 -3
  64. package/internals/plugins/featurePlugins/useChartCartesianAxis/useChartCartesianAxis.types.d.mts +6 -2
  65. package/internals/plugins/featurePlugins/useChartCartesianAxis/useChartCartesianAxis.types.d.ts +6 -2
  66. package/internals/plugins/featurePlugins/useChartCartesianAxis/useChartCartesianAxisLayout.selectors.d.mts +1 -0
  67. package/internals/plugins/featurePlugins/useChartCartesianAxis/useChartCartesianAxisLayout.selectors.d.ts +1 -0
  68. package/internals/plugins/featurePlugins/useChartKeyboardNavigation/useChartKeyboardNavigation.js +59 -16
  69. package/internals/plugins/featurePlugins/useChartKeyboardNavigation/useChartKeyboardNavigation.mjs +59 -16
  70. package/internals/plugins/featurePlugins/useChartKeyboardNavigation/useChartKeyboardNavigation.selectors.d.mts +3 -0
  71. package/internals/plugins/featurePlugins/useChartKeyboardNavigation/useChartKeyboardNavigation.selectors.d.ts +3 -0
  72. package/internals/plugins/featurePlugins/useChartKeyboardNavigation/useChartKeyboardNavigation.selectors.js +2 -1
  73. package/internals/plugins/featurePlugins/useChartKeyboardNavigation/useChartKeyboardNavigation.selectors.mjs +1 -0
  74. package/internals/plugins/featurePlugins/useChartKeyboardNavigation/useChartKeyboardNavigation.types.d.mts +12 -0
  75. package/internals/plugins/featurePlugins/useChartKeyboardNavigation/useChartKeyboardNavigation.types.d.ts +12 -0
  76. package/internals/plugins/featurePlugins/useChartKeyboardNavigation/utils/getItemAtRotationIndex.d.mts +18 -0
  77. package/internals/plugins/featurePlugins/useChartKeyboardNavigation/utils/getItemAtRotationIndex.d.ts +18 -0
  78. package/internals/plugins/featurePlugins/useChartKeyboardNavigation/utils/getItemAtRotationIndex.js +65 -0
  79. package/internals/plugins/featurePlugins/useChartKeyboardNavigation/utils/getItemAtRotationIndex.mjs +60 -0
  80. package/internals/plugins/featurePlugins/useChartPolarAxis/useChartPolarAxis.js +43 -1
  81. package/internals/plugins/featurePlugins/useChartPolarAxis/useChartPolarAxis.mjs +43 -1
  82. package/internals/plugins/featurePlugins/useChartPolarAxis/useChartPolarAxis.types.d.mts +3 -2
  83. package/internals/plugins/featurePlugins/useChartPolarAxis/useChartPolarAxis.types.d.ts +3 -2
  84. package/internals/useDefaultTickLabelStyle.d.mts +7 -0
  85. package/internals/useDefaultTickLabelStyle.d.ts +7 -0
  86. package/internals/useDefaultTickLabelStyle.js +34 -0
  87. package/internals/useDefaultTickLabelStyle.mjs +28 -0
  88. package/locales/elGR.d.mts +5 -0
  89. package/locales/elGR.d.ts +5 -0
  90. package/locales/elGR.js +8 -0
  91. package/locales/elGR.mjs +8 -0
  92. package/locales/enUS.d.mts +5 -0
  93. package/locales/enUS.d.ts +5 -0
  94. package/locales/enUS.js +8 -0
  95. package/locales/enUS.mjs +8 -0
  96. package/locales/frFR.d.mts +5 -0
  97. package/locales/frFR.d.ts +5 -0
  98. package/locales/frFR.js +8 -0
  99. package/locales/frFR.mjs +8 -0
  100. package/locales/nbNO.d.mts +5 -0
  101. package/locales/nbNO.d.ts +5 -0
  102. package/locales/nbNO.js +8 -0
  103. package/locales/nbNO.mjs +8 -0
  104. package/locales/ptBR.d.mts +5 -0
  105. package/locales/ptBR.d.ts +5 -0
  106. package/locales/ptBR.js +8 -0
  107. package/locales/ptBR.mjs +8 -0
  108. package/locales/ptPT.d.mts +5 -0
  109. package/locales/ptPT.d.ts +5 -0
  110. package/locales/ptPT.js +8 -0
  111. package/locales/ptPT.mjs +8 -0
  112. package/locales/svSE.d.mts +5 -0
  113. package/locales/svSE.d.ts +5 -0
  114. package/locales/svSE.js +8 -0
  115. package/locales/svSE.mjs +8 -0
  116. package/locales/utils/chartsLocaleTextApi.d.mts +14 -0
  117. package/locales/utils/chartsLocaleTextApi.d.ts +14 -0
  118. package/locales/utils/getChartsLocalization.d.mts +5 -0
  119. package/locales/utils/getChartsLocalization.d.ts +5 -0
  120. package/package.json +7 -7
@@ -0,0 +1,62 @@
1
+ 'use client';
2
+
3
+ import * as React from 'react';
4
+ import { useChartsLocalization } from "../../../hooks/index.mjs";
5
+ import { useStore } from "../../store/useStore.mjs";
6
+ import { selectorChartsZoomAnnouncement } from "../../plugins/featurePlugins/useChartKeyboardNavigation/index.mjs";
7
+ import { selectorChartZoomMap, selectorChartZoomOptionsLookup } from "../../plugins/featurePlugins/useChartCartesianAxis/index.mjs";
8
+
9
+ /**
10
+ * Get the message announcing the visible range of the zoomable axes.
11
+ *
12
+ * The message is taken when the keyboard zoom and pan asks for it, not derived from the zoom
13
+ * itself: the zoom also moves on wheel, drag, and from the application, and the live region must
14
+ * stay silent then.
15
+ * @returns {string | null} the message to announce, or `null` when there is nothing to announce
16
+ */
17
+ export function useZoomAnnouncement() {
18
+ const store = useStore();
19
+ const announcement = store.use(selectorChartsZoomAnnouncement);
20
+ const zoomMap = store.use(selectorChartZoomMap);
21
+ const optionsLookup = store.use(selectorChartZoomOptionsLookup);
22
+ const {
23
+ localeText
24
+ } = useChartsLocalization();
25
+ const [message, setMessage] = React.useState(null);
26
+
27
+ // The announcement is written from the values of the render the request lands on, so a later
28
+ // zoom coming from elsewhere does not rewrite the live region.
29
+ const currentValuesRef = React.useRef({
30
+ zoomMap,
31
+ optionsLookup,
32
+ localeText
33
+ });
34
+ currentValuesRef.current = {
35
+ zoomMap,
36
+ optionsLookup,
37
+ localeText
38
+ };
39
+ React.useEffect(() => {
40
+ if (announcement === 0) {
41
+ setMessage(null);
42
+ return;
43
+ }
44
+ const current = currentValuesRef.current;
45
+ if (!current.zoomMap) {
46
+ return;
47
+ }
48
+ const descriptions = Object.values(current.optionsLookup).map(options => {
49
+ const zoomData = current.zoomMap.get(options.axisId);
50
+ if (!zoomData) {
51
+ return null;
52
+ }
53
+ return current.localeText.zoomRangeDescription({
54
+ axisDirection: options.axisDirection,
55
+ start: zoomData.start,
56
+ end: zoomData.end
57
+ });
58
+ }).filter(Boolean);
59
+ setMessage(descriptions.length === 0 ? null : descriptions.join(current.localeText.a11yConnector));
60
+ }, [announcement]);
61
+ return message;
62
+ }
@@ -1,6 +1,5 @@
1
1
  "use strict";
2
2
 
3
- var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
4
3
  Object.defineProperty(exports, "__esModule", {
5
4
  value: true
6
5
  });
@@ -8,7 +7,6 @@ exports.batchMeasureStrings = batchMeasureStrings;
8
7
  exports.clearStringMeasurementCache = clearStringMeasurementCache;
9
8
  exports.getStringSize = void 0;
10
9
  exports.getStyleString = getStyleString;
11
- var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
12
10
  // DOM utils adapted from
13
11
  // https://github.com/recharts/recharts/blob/master/src/util/DOMUtils.ts
14
12
 
@@ -64,6 +62,17 @@ function getStyleString(style) {
64
62
  return result;
65
63
  }
66
64
 
65
+ /**
66
+ * Applies a style to a measurement element.
67
+ * Uses the CSS Object Model (CSSOM) to comply with Content Security Policy (CSP).
68
+ * https://en.wikipedia.org/wiki/Content_Security_Policy
69
+ */
70
+ function applyStyle(element, style) {
71
+ for (const styleKey of Object.keys(style)) {
72
+ element.style[camelCaseToDashCase(styleKey)] = convertPixelValue(styleKey, style[styleKey]);
73
+ }
74
+ }
75
+
67
76
  /**
68
77
  *
69
78
  * @param text The string to estimate
@@ -87,13 +96,7 @@ const getStringSize = (text, style = {}) => {
87
96
  try {
88
97
  const measurementSpanContainer = getMeasurementContainer();
89
98
  const measurementElem = document.createElementNS('http://www.w3.org/2000/svg', 'text');
90
-
91
- // Need to use CSS Object Model (CSSOM) to be able to comply with Content Security Policy (CSP)
92
- // https://en.wikipedia.org/wiki/Content_Security_Policy
93
- Object.keys(style).map(styleKey => {
94
- measurementElem.style[camelCaseToDashCase(styleKey)] = convertPixelValue(styleKey, style[styleKey]);
95
- return styleKey;
96
- });
99
+ applyStyle(measurementElem, style);
97
100
  measurementElem.textContent = str;
98
101
  measurementSpanContainer.replaceChildren(measurementElem);
99
102
  const result = measureSVGTextElement(measurementElem);
@@ -134,16 +137,14 @@ function batchMeasureStrings(texts, style = {}) {
134
137
  }
135
138
  }
136
139
  const measurementContainer = getMeasurementContainer();
137
- // Need to use CSS Object Model (CSSOM) to be able to comply with Content Security Policy (CSP)
138
- // https://en.wikipedia.org/wiki/Content_Security_Policy
139
- const measurementSpanStyle = (0, _extends2.default)({}, style);
140
- Object.keys(measurementSpanStyle).map(styleKey => {
141
- measurementContainer.style[camelCaseToDashCase(styleKey)] = convertPixelValue(styleKey, measurementSpanStyle[styleKey]);
142
- return styleKey;
143
- });
140
+
141
+ // The style goes on each text element rather than on the shared container. Styling the container
142
+ // leaks into every later measurement, because a call that omits one of those properties keeps the
143
+ // previous call's value and measures the wrong font.
144
144
  const measurementElements = [];
145
145
  for (const string of textToMeasure) {
146
146
  const measurementElem = document.createElementNS('http://www.w3.org/2000/svg', 'text');
147
+ applyStyle(measurementElem, style);
147
148
  measurementElem.textContent = `${string}`;
148
149
  measurementElements.push(measurementElem);
149
150
  }
@@ -1,4 +1,3 @@
1
- import _extends from "@babel/runtime/helpers/esm/extends";
2
1
  // DOM utils adapted from
3
2
  // https://github.com/recharts/recharts/blob/master/src/util/DOMUtils.ts
4
3
 
@@ -54,6 +53,17 @@ export function getStyleString(style) {
54
53
  return result;
55
54
  }
56
55
 
56
+ /**
57
+ * Applies a style to a measurement element.
58
+ * Uses the CSS Object Model (CSSOM) to comply with Content Security Policy (CSP).
59
+ * https://en.wikipedia.org/wiki/Content_Security_Policy
60
+ */
61
+ function applyStyle(element, style) {
62
+ for (const styleKey of Object.keys(style)) {
63
+ element.style[camelCaseToDashCase(styleKey)] = convertPixelValue(styleKey, style[styleKey]);
64
+ }
65
+ }
66
+
57
67
  /**
58
68
  *
59
69
  * @param text The string to estimate
@@ -77,13 +87,7 @@ export const getStringSize = (text, style = {}) => {
77
87
  try {
78
88
  const measurementSpanContainer = getMeasurementContainer();
79
89
  const measurementElem = document.createElementNS('http://www.w3.org/2000/svg', 'text');
80
-
81
- // Need to use CSS Object Model (CSSOM) to be able to comply with Content Security Policy (CSP)
82
- // https://en.wikipedia.org/wiki/Content_Security_Policy
83
- Object.keys(style).map(styleKey => {
84
- measurementElem.style[camelCaseToDashCase(styleKey)] = convertPixelValue(styleKey, style[styleKey]);
85
- return styleKey;
86
- });
90
+ applyStyle(measurementElem, style);
87
91
  measurementElem.textContent = str;
88
92
  measurementSpanContainer.replaceChildren(measurementElem);
89
93
  const result = measureSVGTextElement(measurementElem);
@@ -123,16 +127,14 @@ export function batchMeasureStrings(texts, style = {}) {
123
127
  }
124
128
  }
125
129
  const measurementContainer = getMeasurementContainer();
126
- // Need to use CSS Object Model (CSSOM) to be able to comply with Content Security Policy (CSP)
127
- // https://en.wikipedia.org/wiki/Content_Security_Policy
128
- const measurementSpanStyle = _extends({}, style);
129
- Object.keys(measurementSpanStyle).map(styleKey => {
130
- measurementContainer.style[camelCaseToDashCase(styleKey)] = convertPixelValue(styleKey, measurementSpanStyle[styleKey]);
131
- return styleKey;
132
- });
130
+
131
+ // The style goes on each text element rather than on the shared container. Styling the container
132
+ // leaks into every later measurement, because a call that omits one of those properties keeps the
133
+ // previous call's value and measures the wrong font.
133
134
  const measurementElements = [];
134
135
  for (const string of textToMeasure) {
135
136
  const measurementElem = document.createElementNS('http://www.w3.org/2000/svg', 'text');
137
+ applyStyle(measurementElem, style);
136
138
  measurementElem.textContent = `${string}`;
137
139
  measurementElements.push(measurementElem);
138
140
  }
@@ -1,3 +1,4 @@
1
+ import type { ChartsTextAnchor, ChartsTextBaseline } from "./getWordsByLines.mjs";
1
2
  interface EllipsizeConfig {
2
3
  width: number;
3
4
  height: number;
@@ -9,6 +10,35 @@ interface EllipsizeConfig {
9
10
  };
10
11
  }
11
12
  export declare function doesTextFitInRect(text: string, config: EllipsizeConfig): boolean;
13
+ export interface RotatedTextBoundsConfig {
14
+ /** Angle, in degrees, in which the text is displayed. */
15
+ angle: number;
16
+ textAnchor?: ChartsTextAnchor;
17
+ dominantBaseline?: ChartsTextBaseline;
18
+ isRtl?: boolean;
19
+ }
20
+ export interface RotatedTextBounds {
21
+ /** Width of the text bounding box once rotated. */
22
+ width: number;
23
+ /** How far the text extends above its anchor point. */
24
+ above: number;
25
+ /** How far the text extends below its anchor point. */
26
+ below: number;
27
+ }
28
+ /**
29
+ * Returns the size of a rotated text, and how far it extends on each side of its anchor point.
30
+ *
31
+ * Which side of the anchor the text is drawn on depends on both the text anchoring and the rotation,
32
+ * so `above` and `below` are not necessarily equal. Either of them is negative when the whole text
33
+ * sits on the other side of the anchor point.
34
+ *
35
+ * @param textSize The size of the text before rotation.
36
+ * @param config How the text is anchored and rotated.
37
+ */
38
+ export declare function getRotatedTextBounds(textSize: {
39
+ width: number;
40
+ height: number;
41
+ }, config: RotatedTextBoundsConfig): RotatedTextBounds;
12
42
  /** This function finds the best place to clip the text to add an ellipsis.
13
43
  * This function assumes that the {@link doesTextFit} never returns true for longer text after returning false for
14
44
  * shorter text.
@@ -1,3 +1,4 @@
1
+ import type { ChartsTextAnchor, ChartsTextBaseline } from "./getWordsByLines.js";
1
2
  interface EllipsizeConfig {
2
3
  width: number;
3
4
  height: number;
@@ -9,6 +10,35 @@ interface EllipsizeConfig {
9
10
  };
10
11
  }
11
12
  export declare function doesTextFitInRect(text: string, config: EllipsizeConfig): boolean;
13
+ export interface RotatedTextBoundsConfig {
14
+ /** Angle, in degrees, in which the text is displayed. */
15
+ angle: number;
16
+ textAnchor?: ChartsTextAnchor;
17
+ dominantBaseline?: ChartsTextBaseline;
18
+ isRtl?: boolean;
19
+ }
20
+ export interface RotatedTextBounds {
21
+ /** Width of the text bounding box once rotated. */
22
+ width: number;
23
+ /** How far the text extends above its anchor point. */
24
+ above: number;
25
+ /** How far the text extends below its anchor point. */
26
+ below: number;
27
+ }
28
+ /**
29
+ * Returns the size of a rotated text, and how far it extends on each side of its anchor point.
30
+ *
31
+ * Which side of the anchor the text is drawn on depends on both the text anchoring and the rotation,
32
+ * so `above` and `below` are not necessarily equal. Either of them is negative when the whole text
33
+ * sits on the other side of the anchor point.
34
+ *
35
+ * @param textSize The size of the text before rotation.
36
+ * @param config How the text is anchored and rotated.
37
+ */
38
+ export declare function getRotatedTextBounds(textSize: {
39
+ width: number;
40
+ height: number;
41
+ }, config: RotatedTextBoundsConfig): RotatedTextBounds;
12
42
  /** This function finds the best place to clip the text to add an ellipsis.
13
43
  * This function assumes that the {@link doesTextFit} never returns true for longer text after returning false for
14
44
  * shorter text.
@@ -5,6 +5,7 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.doesTextFitInRect = doesTextFitInRect;
7
7
  exports.ellipsize = ellipsize;
8
+ exports.getRotatedTextBounds = getRotatedTextBounds;
8
9
  var _getGraphemeCount = require("./getGraphemeCount");
9
10
  var _degToRad = require("./degToRad");
10
11
  var _sliceUntil = require("./sliceUntil");
@@ -22,6 +23,65 @@ function doesTextFitInRect(text, config) {
22
23
  return angledWidth <= width && angledHeight <= height;
23
24
  }
24
25
 
26
+ /**
27
+ * Position of the text bounding box relative to its anchor point, as a fraction of the text width.
28
+ * `0` means the box starts at the anchor, `-1` that it ends there.
29
+ */
30
+ function getHorizontalAnchorOffset(textAnchor, isRtl) {
31
+ const anchor = textAnchor ?? 'middle';
32
+ if (anchor === 'middle') {
33
+ return -1 / 2;
34
+ }
35
+ const startsAtAnchor = isRtl ? anchor === 'end' : anchor === 'start';
36
+ return startsAtAnchor ? 0 : -1;
37
+ }
38
+
39
+ /**
40
+ * Position of the text bounding box relative to its anchor point, as a fraction of the text height.
41
+ * `0` means the box starts at the anchor, `-1` that it ends there.
42
+ */
43
+ function getVerticalAnchorOffset(dominantBaseline) {
44
+ switch (dominantBaseline ?? 'central') {
45
+ case 'hanging':
46
+ case 'text-before-edge':
47
+ return 0;
48
+ case 'central':
49
+ return -1 / 2;
50
+ default:
51
+ return -1;
52
+ }
53
+ }
54
+ /**
55
+ * Returns the size of a rotated text, and how far it extends on each side of its anchor point.
56
+ *
57
+ * Which side of the anchor the text is drawn on depends on both the text anchoring and the rotation,
58
+ * so `above` and `below` are not necessarily equal. Either of them is negative when the whole text
59
+ * sits on the other side of the anchor point.
60
+ *
61
+ * @param textSize The size of the text before rotation.
62
+ * @param config How the text is anchored and rotated.
63
+ */
64
+ function getRotatedTextBounds(textSize, config) {
65
+ const angle = (0, _degToRad.degToRad)(config.angle);
66
+ const sin = Math.sin(angle);
67
+ const cos = Math.cos(angle);
68
+ const left = getHorizontalAnchorOffset(config.textAnchor, config.isRtl ?? false) * textSize.width;
69
+ const right = left + textSize.width;
70
+ const top = getVerticalAnchorOffset(config.dominantBaseline) * textSize.height;
71
+ const bottom = top + textSize.height;
72
+
73
+ /* A rotation by `angle` maps a point to `(x * cos - y * sin, x * sin + y * cos)`. Both terms of
74
+ * the vertical coordinate are independent, so the extrema of their sum are the sums of their
75
+ * extrema. */
76
+ const fromWidth = [left * sin, right * sin];
77
+ const fromHeight = [top * cos, bottom * cos];
78
+ return {
79
+ width: Math.abs(textSize.width * cos) + Math.abs(textSize.height * sin),
80
+ above: -(Math.min(...fromWidth) + Math.min(...fromHeight)),
81
+ below: Math.max(...fromWidth) + Math.max(...fromHeight)
82
+ };
83
+ }
84
+
25
85
  /** This function finds the best place to clip the text to add an ellipsis.
26
86
  * This function assumes that the {@link doesTextFit} never returns true for longer text after returning false for
27
87
  * shorter text.
@@ -15,6 +15,65 @@ export function doesTextFitInRect(text, config) {
15
15
  return angledWidth <= width && angledHeight <= height;
16
16
  }
17
17
 
18
+ /**
19
+ * Position of the text bounding box relative to its anchor point, as a fraction of the text width.
20
+ * `0` means the box starts at the anchor, `-1` that it ends there.
21
+ */
22
+ function getHorizontalAnchorOffset(textAnchor, isRtl) {
23
+ const anchor = textAnchor ?? 'middle';
24
+ if (anchor === 'middle') {
25
+ return -1 / 2;
26
+ }
27
+ const startsAtAnchor = isRtl ? anchor === 'end' : anchor === 'start';
28
+ return startsAtAnchor ? 0 : -1;
29
+ }
30
+
31
+ /**
32
+ * Position of the text bounding box relative to its anchor point, as a fraction of the text height.
33
+ * `0` means the box starts at the anchor, `-1` that it ends there.
34
+ */
35
+ function getVerticalAnchorOffset(dominantBaseline) {
36
+ switch (dominantBaseline ?? 'central') {
37
+ case 'hanging':
38
+ case 'text-before-edge':
39
+ return 0;
40
+ case 'central':
41
+ return -1 / 2;
42
+ default:
43
+ return -1;
44
+ }
45
+ }
46
+ /**
47
+ * Returns the size of a rotated text, and how far it extends on each side of its anchor point.
48
+ *
49
+ * Which side of the anchor the text is drawn on depends on both the text anchoring and the rotation,
50
+ * so `above` and `below` are not necessarily equal. Either of them is negative when the whole text
51
+ * sits on the other side of the anchor point.
52
+ *
53
+ * @param textSize The size of the text before rotation.
54
+ * @param config How the text is anchored and rotated.
55
+ */
56
+ export function getRotatedTextBounds(textSize, config) {
57
+ const angle = degToRad(config.angle);
58
+ const sin = Math.sin(angle);
59
+ const cos = Math.cos(angle);
60
+ const left = getHorizontalAnchorOffset(config.textAnchor, config.isRtl ?? false) * textSize.width;
61
+ const right = left + textSize.width;
62
+ const top = getVerticalAnchorOffset(config.dominantBaseline) * textSize.height;
63
+ const bottom = top + textSize.height;
64
+
65
+ /* A rotation by `angle` maps a point to `(x * cos - y * sin, x * sin + y * cos)`. Both terms of
66
+ * the vertical coordinate are independent, so the extrema of their sum are the sums of their
67
+ * extrema. */
68
+ const fromWidth = [left * sin, right * sin];
69
+ const fromHeight = [top * cos, bottom * cos];
70
+ return {
71
+ width: Math.abs(textSize.width * cos) + Math.abs(textSize.height * sin),
72
+ above: -(Math.min(...fromWidth) + Math.min(...fromHeight)),
73
+ below: Math.max(...fromWidth) + Math.max(...fromHeight)
74
+ };
75
+ }
76
+
18
77
  /** This function finds the best place to clip the text to add an ellipsis.
19
78
  * This function assumes that the {@link doesTextFit} never returns true for longer text after returning false for
20
79
  * shorter text.
@@ -83,6 +83,7 @@ export { processLineLikeSeries } from "./processLineLikeSeries.mjs";
83
83
  export { getLineLikeTooltip } from "./getLineLikeTooltip.mjs";
84
84
  export { checkBarChartScaleErrors } from "../BarChart/checkBarChartScaleErrors.mjs";
85
85
  export { getBandSize } from "./getBandSize.mjs";
86
+ export { useDefaultTickLabelStyle } from "./useDefaultTickLabelStyle.mjs";
86
87
  export * from "./plugins/utils/defaultSeriesConfig.mjs";
87
88
  export { EPSILON } from "../utils/epsilon.mjs";
88
89
  export { useUtilityClasses as useChartsTooltipUtilityClasses, getChartsTooltipUtilityClass } from "../ChartsTooltip/chartsTooltipClasses.mjs";
@@ -83,6 +83,7 @@ export { processLineLikeSeries } from "./processLineLikeSeries.js";
83
83
  export { getLineLikeTooltip } from "./getLineLikeTooltip.js";
84
84
  export { checkBarChartScaleErrors } from "../BarChart/checkBarChartScaleErrors.js";
85
85
  export { getBandSize } from "./getBandSize.js";
86
+ export { useDefaultTickLabelStyle } from "./useDefaultTickLabelStyle.js";
86
87
  export * from "./plugins/utils/defaultSeriesConfig.js";
87
88
  export { EPSILON } from "../utils/epsilon.js";
88
89
  export { useUtilityClasses as useChartsTooltipUtilityClasses, getChartsTooltipUtilityClass } from "../ChartsTooltip/chartsTooltipClasses.js";
@@ -30,6 +30,7 @@ var _exportNames = {
30
30
  getLineLikeTooltip: true,
31
31
  checkBarChartScaleErrors: true,
32
32
  getBandSize: true,
33
+ useDefaultTickLabelStyle: true,
33
34
  EPSILON: true,
34
35
  useChartsTooltipUtilityClasses: true,
35
36
  getChartsTooltipUtilityClass: true,
@@ -157,6 +158,12 @@ Object.defineProperty(exports, "useChartsTooltipUtilityClasses", {
157
158
  return _chartsTooltipClasses.useUtilityClasses;
158
159
  }
159
160
  });
161
+ Object.defineProperty(exports, "useDefaultTickLabelStyle", {
162
+ enumerable: true,
163
+ get: function () {
164
+ return _useDefaultTickLabelStyle.useDefaultTickLabelStyle;
165
+ }
166
+ });
160
167
  Object.defineProperty(exports, "useDrawingArea", {
161
168
  enumerable: true,
162
169
  get: function () {
@@ -967,6 +974,7 @@ var _processLineLikeSeries = require("./processLineLikeSeries");
967
974
  var _getLineLikeTooltip = require("./getLineLikeTooltip");
968
975
  var _checkBarChartScaleErrors = require("../BarChart/checkBarChartScaleErrors");
969
976
  var _getBandSize = require("./getBandSize");
977
+ var _useDefaultTickLabelStyle = require("./useDefaultTickLabelStyle");
970
978
  var _defaultSeriesConfig = require("./plugins/utils/defaultSeriesConfig");
971
979
  Object.keys(_defaultSeriesConfig).forEach(function (key) {
972
980
  if (key === "default" || key === "__esModule") return;
@@ -91,6 +91,7 @@ export { processLineLikeSeries } from "./processLineLikeSeries.mjs";
91
91
  export { getLineLikeTooltip } from "./getLineLikeTooltip.mjs";
92
92
  export { checkBarChartScaleErrors } from "../BarChart/checkBarChartScaleErrors.mjs";
93
93
  export { getBandSize } from "./getBandSize.mjs";
94
+ export { useDefaultTickLabelStyle } from "./useDefaultTickLabelStyle.mjs";
94
95
  export * from "./plugins/utils/defaultSeriesConfig.mjs";
95
96
  export { EPSILON } from "../utils/epsilon.mjs";
96
97
  export { useUtilityClasses as useChartsTooltipUtilityClasses, getChartsTooltipUtilityClass } from "../ChartsTooltip/chartsTooltipClasses.mjs";
@@ -1,4 +1,5 @@
1
1
  import type { DefaultedXAxis, DefaultedYAxis } from "../../../../models/axis.mjs";
2
+ import type { ChartsTextStyle } from "../../../getWordsByLines.mjs";
2
3
  import type { DomainDefinition } from "./domain.mjs";
3
4
  interface ComputeAxisAutoSizeOptions {
4
5
  axis: DefaultedXAxis | DefaultedYAxis;
@@ -9,6 +10,8 @@ interface ComputeAxisAutoSizeOptions {
9
10
  * so tick labels measured from them match what the chart renders.
10
11
  */
11
12
  domain?: DomainDefinition;
13
+ /** Font the ticks render with, used only for measuring. */
14
+ defaultTickLabelStyle?: ChartsTextStyle;
12
15
  }
13
16
  /**
14
17
  * Result of auto-size computation for axes.
@@ -1,4 +1,5 @@
1
1
  import type { DefaultedXAxis, DefaultedYAxis } from "../../../../models/axis.js";
2
+ import type { ChartsTextStyle } from "../../../getWordsByLines.js";
2
3
  import type { DomainDefinition } from "./domain.js";
3
4
  interface ComputeAxisAutoSizeOptions {
4
5
  axis: DefaultedXAxis | DefaultedYAxis;
@@ -9,6 +10,8 @@ interface ComputeAxisAutoSizeOptions {
9
10
  * so tick labels measured from them match what the chart renders.
10
11
  */
11
12
  domain?: DomainDefinition;
13
+ /** Font the ticks render with, used only for measuring. */
14
+ defaultTickLabelStyle?: ChartsTextStyle;
12
15
  }
13
16
  /**
14
17
  * Result of auto-size computation for axes.
@@ -219,7 +219,7 @@ function getRotatedDimension(width, height, angle, direction) {
219
219
  *
220
220
  * Returns both the total size and the computed tick sizes for each group level.
221
221
  */
222
- function computeGroupedAxisAutoSize(axis, direction) {
222
+ function computeGroupedAxisAutoSize(axis, direction, defaultTickLabelStyle) {
223
223
  const {
224
224
  groups,
225
225
  data,
@@ -238,7 +238,7 @@ function computeGroupedAxisAutoSize(axis, direction) {
238
238
  for (let groupIndex = 0; groupIndex < groups.length; groupIndex += 1) {
239
239
  const group = groups[groupIndex];
240
240
  const groupLabels = selectLargestCandidates(getGroupLabels(data, group));
241
- const groupTickLabelStyle = (0, _extends2.default)({}, axisTickLabelStyle, group.tickLabelStyle);
241
+ const groupTickLabelStyle = (0, _extends2.default)({}, defaultTickLabelStyle, axisTickLabelStyle, group.tickLabelStyle);
242
242
  const angle = groupTickLabelStyle?.angle;
243
243
  const {
244
244
  maxWidth,
@@ -284,12 +284,16 @@ function computeAxisAutoSize(options) {
284
284
  const {
285
285
  axis,
286
286
  direction,
287
- domain
287
+ domain,
288
+ defaultTickLabelStyle
288
289
  } = options;
289
290
  if (hasGroups(axis)) {
290
- return computeGroupedAxisAutoSize(axis, direction);
291
+ return computeGroupedAxisAutoSize(axis, direction, defaultTickLabelStyle);
291
292
  }
292
- const tickLabelStyle = axis.tickLabelStyle;
293
+
294
+ // Measure with the font the ticks render with. `axis.tickLabelStyle` only holds what the user set,
295
+ // so on its own the measurement element would inherit the document font instead.
296
+ const tickLabelStyle = (0, _extends2.default)({}, defaultTickLabelStyle, axis.tickLabelStyle);
293
297
  const tickSize = axis.tickSize ?? _autoSizeConstants.AXIS_AUTO_SIZE_TICK_SIZE;
294
298
  const hasLabel = Boolean(axis.label);
295
299
  const angle = tickLabelStyle?.angle;
@@ -212,7 +212,7 @@ function getRotatedDimension(width, height, angle, direction) {
212
212
  *
213
213
  * Returns both the total size and the computed tick sizes for each group level.
214
214
  */
215
- function computeGroupedAxisAutoSize(axis, direction) {
215
+ function computeGroupedAxisAutoSize(axis, direction, defaultTickLabelStyle) {
216
216
  const {
217
217
  groups,
218
218
  data,
@@ -231,7 +231,7 @@ function computeGroupedAxisAutoSize(axis, direction) {
231
231
  for (let groupIndex = 0; groupIndex < groups.length; groupIndex += 1) {
232
232
  const group = groups[groupIndex];
233
233
  const groupLabels = selectLargestCandidates(getGroupLabels(data, group));
234
- const groupTickLabelStyle = _extends({}, axisTickLabelStyle, group.tickLabelStyle);
234
+ const groupTickLabelStyle = _extends({}, defaultTickLabelStyle, axisTickLabelStyle, group.tickLabelStyle);
235
235
  const angle = groupTickLabelStyle?.angle;
236
236
  const {
237
237
  maxWidth,
@@ -277,12 +277,16 @@ export function computeAxisAutoSize(options) {
277
277
  const {
278
278
  axis,
279
279
  direction,
280
- domain
280
+ domain,
281
+ defaultTickLabelStyle
281
282
  } = options;
282
283
  if (hasGroups(axis)) {
283
- return computeGroupedAxisAutoSize(axis, direction);
284
+ return computeGroupedAxisAutoSize(axis, direction, defaultTickLabelStyle);
284
285
  }
285
- const tickLabelStyle = axis.tickLabelStyle;
286
+
287
+ // Measure with the font the ticks render with. `axis.tickLabelStyle` only holds what the user set,
288
+ // so on its own the measurement element would inherit the document font instead.
289
+ const tickLabelStyle = _extends({}, defaultTickLabelStyle, axis.tickLabelStyle);
286
290
  const tickSize = axis.tickSize ?? AXIS_AUTO_SIZE_TICK_SIZE;
287
291
  const hasLabel = Boolean(axis.label);
288
292
  const angle = tickLabelStyle?.angle;
@@ -10,6 +10,9 @@ var _computeAxisAutoSize = require("./computeAxisAutoSize");
10
10
  var _useChartAxisDomains = require("./useChartAxisDomains.selectors");
11
11
  // Direct state access to avoid circular dependency
12
12
  const selectorIsHydrated = state => state.dimensions.width && state.dimensions.height;
13
+
14
+ // Direct state access to avoid circular dependency
15
+ const selectorDefaultTickLabelStyle = state => state.cartesianAxis?.defaultTickLabelStyle;
13
16
  const EMPTY_SIZES = {};
14
17
  const EMPTY_RESULTS = {};
15
18
 
@@ -17,7 +20,7 @@ const EMPTY_RESULTS = {};
17
20
  * Selector that computes full auto-size results for X axes that have `height: 'auto'`.
18
21
  * Returns a map of axis ID to full result (including group tick sizes for grouped axes).
19
22
  */
20
- const selectorChartXAxisAutoSizeResults = exports.selectorChartXAxisAutoSizeResults = (0, _store.createSelectorMemoized)(_useChartCartesianAxisLayout.selectorChartRawXAxis, selectorIsHydrated, _useChartAxisDomains.selectorChartXAxisDomainsForAutoSize, function selectorChartXAxisAutoSizeResults(xAxes, isHydrated, domainsMap) {
23
+ const selectorChartXAxisAutoSizeResults = exports.selectorChartXAxisAutoSizeResults = (0, _store.createSelectorMemoized)(_useChartCartesianAxisLayout.selectorChartRawXAxis, selectorIsHydrated, _useChartAxisDomains.selectorChartXAxisDomainsForAutoSize, selectorDefaultTickLabelStyle, function selectorChartXAxisAutoSizeResults(xAxes, isHydrated, domainsMap, defaultTickLabelStyle) {
21
24
  const hasAutoAxis = xAxes?.some(axis => axis.height === 'auto');
22
25
  if (!hasAutoAxis || !isHydrated) {
23
26
  return EMPTY_RESULTS;
@@ -29,7 +32,8 @@ const selectorChartXAxisAutoSizeResults = exports.selectorChartXAxisAutoSizeResu
29
32
  const computed = (0, _computeAxisAutoSize.computeAxisAutoSize)({
30
33
  axis,
31
34
  direction: 'x',
32
- domain: domainsMap[axis.id]
35
+ domain: domainsMap[axis.id],
36
+ defaultTickLabelStyle
33
37
  });
34
38
  if (computed !== undefined) {
35
39
  results[axis.id] = computed;
@@ -58,7 +62,7 @@ const selectorChartXAxisAutoSizes = exports.selectorChartXAxisAutoSizes = (0, _s
58
62
  * Selector that computes full auto-size results for Y axes that have `width: 'auto'`.
59
63
  * Returns a map of axis ID to full result (including group tick sizes for grouped axes).
60
64
  */
61
- const selectorChartYAxisAutoSizeResults = exports.selectorChartYAxisAutoSizeResults = (0, _store.createSelectorMemoized)(_useChartCartesianAxisLayout.selectorChartRawYAxis, selectorIsHydrated, _useChartAxisDomains.selectorChartYAxisDomainsForAutoSize, function selectorChartYAxisAutoSizeResults(yAxes, isHydrated, domainsMap) {
65
+ const selectorChartYAxisAutoSizeResults = exports.selectorChartYAxisAutoSizeResults = (0, _store.createSelectorMemoized)(_useChartCartesianAxisLayout.selectorChartRawYAxis, selectorIsHydrated, _useChartAxisDomains.selectorChartYAxisDomainsForAutoSize, selectorDefaultTickLabelStyle, function selectorChartYAxisAutoSizeResults(yAxes, isHydrated, domainsMap, defaultTickLabelStyle) {
62
66
  const hasAutoAxis = yAxes?.some(axis => axis.width === 'auto');
63
67
  if (!hasAutoAxis || !isHydrated) {
64
68
  return EMPTY_RESULTS;
@@ -70,7 +74,8 @@ const selectorChartYAxisAutoSizeResults = exports.selectorChartYAxisAutoSizeResu
70
74
  const computed = (0, _computeAxisAutoSize.computeAxisAutoSize)({
71
75
  axis,
72
76
  direction: 'y',
73
- domain: domainsMap[axis.id]
77
+ domain: domainsMap[axis.id],
78
+ defaultTickLabelStyle
74
79
  });
75
80
  if (computed !== undefined) {
76
81
  results[axis.id] = computed;