@mui/x-charts-pro 9.0.1 → 9.0.2

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 (56) hide show
  1. package/BarChartPro/BarChartPro.js +20 -1
  2. package/BarChartPro/BarChartPro.mjs +20 -1
  3. package/CHANGELOG.md +121 -0
  4. package/ChartsDataProviderPro/ChartsDataProviderPro.js +2 -2
  5. package/ChartsDataProviderPro/ChartsDataProviderPro.mjs +2 -2
  6. package/ChartsRadialDataProvider/index.d.mts +1 -0
  7. package/ChartsRadialDataProvider/index.d.ts +1 -0
  8. package/ChartsRadialDataProvider/index.js +16 -0
  9. package/ChartsRadialDataProvider/index.mjs +2 -0
  10. package/ChartsRadialGrid/index.d.mts +1 -0
  11. package/ChartsRadialGrid/index.d.ts +1 -0
  12. package/ChartsRadialGrid/index.js +16 -0
  13. package/ChartsRadialGrid/index.mjs +2 -0
  14. package/ChartsToolbarPro/ChartsToolbarImageExportTrigger.js +1 -0
  15. package/ChartsToolbarPro/ChartsToolbarImageExportTrigger.mjs +1 -0
  16. package/ChartsToolbarPro/ChartsToolbarPrintExportTrigger.js +1 -0
  17. package/ChartsToolbarPro/ChartsToolbarPrintExportTrigger.mjs +1 -0
  18. package/ChartsToolbarPro/ChartsToolbarPro.d.mts +30 -0
  19. package/ChartsToolbarPro/ChartsToolbarPro.d.ts +30 -0
  20. package/ChartsToolbarPro/ChartsToolbarPro.js +66 -17
  21. package/ChartsToolbarPro/ChartsToolbarPro.mjs +66 -17
  22. package/ChartsToolbarPro/ChartsToolbarRangeButtonTrigger.d.mts +41 -0
  23. package/ChartsToolbarPro/ChartsToolbarRangeButtonTrigger.d.ts +41 -0
  24. package/ChartsToolbarPro/ChartsToolbarRangeButtonTrigger.js +156 -0
  25. package/ChartsToolbarPro/ChartsToolbarRangeButtonTrigger.mjs +150 -0
  26. package/ChartsToolbarPro/ChartsToolbarZoomInTrigger.js +1 -1
  27. package/ChartsToolbarPro/ChartsToolbarZoomInTrigger.mjs +1 -1
  28. package/ChartsToolbarPro/ChartsToolbarZoomOutTrigger.js +1 -1
  29. package/ChartsToolbarPro/ChartsToolbarZoomOutTrigger.mjs +1 -1
  30. package/ChartsToolbarPro/index.d.mts +1 -0
  31. package/ChartsToolbarPro/index.d.ts +1 -0
  32. package/ChartsToolbarPro/index.js +11 -0
  33. package/ChartsToolbarPro/index.mjs +1 -0
  34. package/ChartsToolbarPro/rangeButtonValueToZoom.d.mts +66 -0
  35. package/ChartsToolbarPro/rangeButtonValueToZoom.d.ts +66 -0
  36. package/ChartsToolbarPro/rangeButtonValueToZoom.js +217 -0
  37. package/ChartsToolbarPro/rangeButtonValueToZoom.mjs +212 -0
  38. package/Heatmap/Heatmap.js +4 -1
  39. package/Heatmap/Heatmap.mjs +4 -1
  40. package/LineChartPro/LineChartPro.js +20 -1
  41. package/LineChartPro/LineChartPro.mjs +20 -1
  42. package/ScatterChartPro/ScatterChartPro.js +20 -1
  43. package/ScatterChartPro/ScatterChartPro.mjs +20 -1
  44. package/index.js +1 -1
  45. package/index.mjs +1 -1
  46. package/internals/plugins/useChartProZoom/useChartProZoom.js +13 -4
  47. package/internals/plugins/useChartProZoom/useChartProZoom.mjs +13 -4
  48. package/internals/plugins/useChartProZoom/useChartProZoom.selectors.d.mts +3 -0
  49. package/internals/plugins/useChartProZoom/useChartProZoom.selectors.d.ts +3 -0
  50. package/internals/plugins/useChartProZoom/useChartProZoom.selectors.js +4 -3
  51. package/internals/plugins/useChartProZoom/useChartProZoom.selectors.mjs +3 -2
  52. package/internals/plugins/useChartProZoom/useChartProZoom.types.d.mts +10 -0
  53. package/internals/plugins/useChartProZoom/useChartProZoom.types.d.ts +10 -0
  54. package/models/seriesType/heatmap.d.mts +9 -1
  55. package/models/seriesType/heatmap.d.ts +9 -1
  56. package/package.json +33 -5
@@ -26,7 +26,7 @@ const ChartsToolbarZoomInTrigger = /*#__PURE__*/React.forwardRef(function Charts
26
26
  instance,
27
27
  store
28
28
  } = useChartsContext();
29
- const disabled = store.use(selectorChartCanZoomIn);
29
+ const disabled = !store.use(selectorChartCanZoomIn);
30
30
  const element = useComponentRenderer(slots.baseButton, render, _extends({}, slotProps.baseButton, {
31
31
  onClick: () => instance.zoomIn(),
32
32
  disabled
@@ -33,7 +33,7 @@ const ChartsToolbarZoomOutTrigger = exports.ChartsToolbarZoomOutTrigger = /*#__P
33
33
  instance,
34
34
  store
35
35
  } = (0, _internals.useChartsContext)();
36
- const disabled = store.use(_useChartProZoom.selectorChartCanZoomOut);
36
+ const disabled = !store.use(_useChartProZoom.selectorChartCanZoomOut);
37
37
  const element = (0, _useComponentRenderer.useComponentRenderer)(slots.baseButton, render, (0, _extends2.default)({}, slotProps.baseButton, {
38
38
  onClick: () => instance.zoomOut(),
39
39
  disabled
@@ -26,7 +26,7 @@ const ChartsToolbarZoomOutTrigger = /*#__PURE__*/React.forwardRef(function Chart
26
26
  instance,
27
27
  store
28
28
  } = useChartsContext();
29
- const disabled = store.use(selectorChartCanZoomOut);
29
+ const disabled = !store.use(selectorChartCanZoomOut);
30
30
  const element = useComponentRenderer(slots.baseButton, render, _extends({}, slotProps.baseButton, {
31
31
  onClick: () => instance.zoomOut(),
32
32
  disabled
@@ -1,6 +1,7 @@
1
1
  export * from "./ChartsToolbarPro.mjs";
2
2
  export * from "./ChartsToolbarZoomInTrigger.mjs";
3
3
  export * from "./ChartsToolbarZoomOutTrigger.mjs";
4
+ export * from "./ChartsToolbarRangeButtonTrigger.mjs";
4
5
  export * from "./ChartsToolbarPrintExportTrigger.mjs";
5
6
  export * from "./ChartsToolbarImageExportTrigger.mjs";
6
7
  export type { ChartsToolbarProSlots, ChartsToolbarProSlotProps } from "./Toolbar.types.mjs";
@@ -1,6 +1,7 @@
1
1
  export * from "./ChartsToolbarPro.js";
2
2
  export * from "./ChartsToolbarZoomInTrigger.js";
3
3
  export * from "./ChartsToolbarZoomOutTrigger.js";
4
+ export * from "./ChartsToolbarRangeButtonTrigger.js";
4
5
  export * from "./ChartsToolbarPrintExportTrigger.js";
5
6
  export * from "./ChartsToolbarImageExportTrigger.js";
6
7
  export type { ChartsToolbarProSlots, ChartsToolbarProSlotProps } from "./Toolbar.types.js";
@@ -36,6 +36,17 @@ Object.keys(_ChartsToolbarZoomOutTrigger).forEach(function (key) {
36
36
  }
37
37
  });
38
38
  });
39
+ var _ChartsToolbarRangeButtonTrigger = require("./ChartsToolbarRangeButtonTrigger");
40
+ Object.keys(_ChartsToolbarRangeButtonTrigger).forEach(function (key) {
41
+ if (key === "default" || key === "__esModule") return;
42
+ if (key in exports && exports[key] === _ChartsToolbarRangeButtonTrigger[key]) return;
43
+ Object.defineProperty(exports, key, {
44
+ enumerable: true,
45
+ get: function () {
46
+ return _ChartsToolbarRangeButtonTrigger[key];
47
+ }
48
+ });
49
+ });
39
50
  var _ChartsToolbarPrintExportTrigger = require("./ChartsToolbarPrintExportTrigger");
40
51
  Object.keys(_ChartsToolbarPrintExportTrigger).forEach(function (key) {
41
52
  if (key === "default" || key === "__esModule") return;
@@ -1,6 +1,7 @@
1
1
  export * from "./ChartsToolbarPro.mjs";
2
2
  export * from "./ChartsToolbarZoomInTrigger.mjs";
3
3
  export * from "./ChartsToolbarZoomOutTrigger.mjs";
4
+ export * from "./ChartsToolbarRangeButtonTrigger.mjs";
4
5
  export * from "./ChartsToolbarPrintExportTrigger.mjs";
5
6
  export * from "./ChartsToolbarImageExportTrigger.mjs";
6
7
  export {};
@@ -0,0 +1,66 @@
1
+ /**
2
+ * A calendar interval unit for range buttons.
3
+ */
4
+ export type RangeButtonIntervalUnit = 'microsecond' | 'millisecond' | 'second' | 'minute' | 'hour' | 'day' | 'week' | 'month' | 'year';
5
+ /**
6
+ * Parameters passed to a range button function value.
7
+ */
8
+ export interface RangeButtonFunctionParams {
9
+ /**
10
+ * The scale type of the axis (e.g., `'time'`, `'band'`, `'linear'`).
11
+ */
12
+ scaleType: string;
13
+ /**
14
+ * The axis data values. Available for ordinal (band/point) axes.
15
+ */
16
+ data: readonly unknown[] | undefined;
17
+ /**
18
+ * The full domain bounds, ignoring any current zoom.
19
+ * For time axes, these are timestamps. For ordinal axes, these are indices.
20
+ */
21
+ domain: {
22
+ min: number;
23
+ max: number;
24
+ };
25
+ }
26
+ /**
27
+ * Defines the value of a range button.
28
+ *
29
+ * - `{ unit, step }` — A calendar interval from the end of the data.
30
+ * @example { unit: 'month', step: 3 } // Last 3 months
31
+ * @example { unit: 'year' } // Last year (step defaults to 1)
32
+ * - `[start, end]` — An absolute date range.
33
+ * @example [new Date(2024, 0, 1), new Date(2024, 6, 1)] // Jan–Jul 2024
34
+ * - `(params) => { start, end }` — A function that receives axis context (`scaleType`, `data`, `domain`) and returns zoom percentages (0-100).
35
+ * @example ({ domain }) => ({ start: 0, end: 50 }) // First half of data
36
+ * @example ({ data }) => {
37
+ * const lastFive = Math.max(0, data.length - 5);
38
+ * return { start: (lastFive / (data.length - 1)) * 100, end: 100 };
39
+ * } // Last 5 items on an ordinal axis
40
+ * - `null` — Resets zoom to show all data.
41
+ * @example null // Show all data
42
+ */
43
+ export type RangeButtonValue = {
44
+ unit: RangeButtonIntervalUnit;
45
+ step?: number;
46
+ } | [Date, Date] | ((params: RangeButtonFunctionParams) => {
47
+ start: number;
48
+ end: number;
49
+ }) | null;
50
+ /**
51
+ * Converts a range button value to zoom start/end percentages.
52
+ *
53
+ * The range is calculated from the end of the axis domain.
54
+ * For example, `{ unit: 'month', step: 3 }` will zoom to show the last 3 months of data.
55
+ *
56
+ * @param value The range button value.
57
+ * @param params The axis context passed to function values.
58
+ * @param params.scaleType The scale type of the axis.
59
+ * @param params.data The axis data values (for ordinal axes).
60
+ * @param params.domain The full domain bounds.
61
+ * @returns The zoom start and end percentages (0-100).
62
+ */
63
+ export declare function rangeButtonValueToZoom(value: RangeButtonValue, params: RangeButtonFunctionParams): {
64
+ start: number;
65
+ end: number;
66
+ };
@@ -0,0 +1,66 @@
1
+ /**
2
+ * A calendar interval unit for range buttons.
3
+ */
4
+ export type RangeButtonIntervalUnit = 'microsecond' | 'millisecond' | 'second' | 'minute' | 'hour' | 'day' | 'week' | 'month' | 'year';
5
+ /**
6
+ * Parameters passed to a range button function value.
7
+ */
8
+ export interface RangeButtonFunctionParams {
9
+ /**
10
+ * The scale type of the axis (e.g., `'time'`, `'band'`, `'linear'`).
11
+ */
12
+ scaleType: string;
13
+ /**
14
+ * The axis data values. Available for ordinal (band/point) axes.
15
+ */
16
+ data: readonly unknown[] | undefined;
17
+ /**
18
+ * The full domain bounds, ignoring any current zoom.
19
+ * For time axes, these are timestamps. For ordinal axes, these are indices.
20
+ */
21
+ domain: {
22
+ min: number;
23
+ max: number;
24
+ };
25
+ }
26
+ /**
27
+ * Defines the value of a range button.
28
+ *
29
+ * - `{ unit, step }` — A calendar interval from the end of the data.
30
+ * @example { unit: 'month', step: 3 } // Last 3 months
31
+ * @example { unit: 'year' } // Last year (step defaults to 1)
32
+ * - `[start, end]` — An absolute date range.
33
+ * @example [new Date(2024, 0, 1), new Date(2024, 6, 1)] // Jan–Jul 2024
34
+ * - `(params) => { start, end }` — A function that receives axis context (`scaleType`, `data`, `domain`) and returns zoom percentages (0-100).
35
+ * @example ({ domain }) => ({ start: 0, end: 50 }) // First half of data
36
+ * @example ({ data }) => {
37
+ * const lastFive = Math.max(0, data.length - 5);
38
+ * return { start: (lastFive / (data.length - 1)) * 100, end: 100 };
39
+ * } // Last 5 items on an ordinal axis
40
+ * - `null` — Resets zoom to show all data.
41
+ * @example null // Show all data
42
+ */
43
+ export type RangeButtonValue = {
44
+ unit: RangeButtonIntervalUnit;
45
+ step?: number;
46
+ } | [Date, Date] | ((params: RangeButtonFunctionParams) => {
47
+ start: number;
48
+ end: number;
49
+ }) | null;
50
+ /**
51
+ * Converts a range button value to zoom start/end percentages.
52
+ *
53
+ * The range is calculated from the end of the axis domain.
54
+ * For example, `{ unit: 'month', step: 3 }` will zoom to show the last 3 months of data.
55
+ *
56
+ * @param value The range button value.
57
+ * @param params The axis context passed to function values.
58
+ * @param params.scaleType The scale type of the axis.
59
+ * @param params.data The axis data values (for ordinal axes).
60
+ * @param params.domain The full domain bounds.
61
+ * @returns The zoom start and end percentages (0-100).
62
+ */
63
+ export declare function rangeButtonValueToZoom(value: RangeButtonValue, params: RangeButtonFunctionParams): {
64
+ start: number;
65
+ end: number;
66
+ };
@@ -0,0 +1,217 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.rangeButtonValueToZoom = rangeButtonValueToZoom;
7
+ var _warning = require("@mui/x-internals/warning");
8
+ /**
9
+ * A calendar interval unit for range buttons.
10
+ */
11
+
12
+ /**
13
+ * Parameters passed to a range button function value.
14
+ */
15
+
16
+ /**
17
+ * Defines the value of a range button.
18
+ *
19
+ * - `{ unit, step }` — A calendar interval from the end of the data.
20
+ * @example { unit: 'month', step: 3 } // Last 3 months
21
+ * @example { unit: 'year' } // Last year (step defaults to 1)
22
+ * - `[start, end]` — An absolute date range.
23
+ * @example [new Date(2024, 0, 1), new Date(2024, 6, 1)] // Jan–Jul 2024
24
+ * - `(params) => { start, end }` — A function that receives axis context (`scaleType`, `data`, `domain`) and returns zoom percentages (0-100).
25
+ * @example ({ domain }) => ({ start: 0, end: 50 }) // First half of data
26
+ * @example ({ data }) => {
27
+ * const lastFive = Math.max(0, data.length - 5);
28
+ * return { start: (lastFive / (data.length - 1)) * 100, end: 100 };
29
+ * } // Last 5 items on an ordinal axis
30
+ * - `null` — Resets zoom to show all data.
31
+ * @example null // Show all data
32
+ */
33
+
34
+ /**
35
+ * Attempts to convert a value to a timestamp.
36
+ * Handles Date objects, numeric timestamps, and date-like strings.
37
+ *
38
+ * @returns The timestamp in milliseconds, or `undefined` if the value is not date-like.
39
+ */
40
+ function toTimestamp(val) {
41
+ if (val instanceof Date) {
42
+ return val.getTime();
43
+ }
44
+ if (typeof val === 'number') {
45
+ return val;
46
+ }
47
+ if (typeof val === 'string') {
48
+ const parsed = Date.parse(val);
49
+ if (!Number.isNaN(parsed)) {
50
+ return parsed;
51
+ }
52
+ }
53
+ return undefined;
54
+ }
55
+
56
+ /**
57
+ * Converts ordinal data to timestamps.
58
+ * Items that cannot be converted are kept as `NaN` so their index is preserved.
59
+ * Returns `undefined` if fewer than 2 items could be converted.
60
+ */
61
+ function toTimestampArray(data) {
62
+ const timestamps = new Array(data.length);
63
+ let convertedItems = 0;
64
+ for (let i = 0; i < data.length; i += 1) {
65
+ const ts = toTimestamp(data[i]);
66
+ if (ts !== undefined) {
67
+ convertedItems += 1;
68
+ timestamps[i] = ts;
69
+ } else {
70
+ timestamps[i] = Number.NaN;
71
+ }
72
+ }
73
+ return convertedItems >= 2 ? timestamps : undefined;
74
+ }
75
+
76
+ /**
77
+ * Converts a range button value to zoom start/end percentages.
78
+ *
79
+ * The range is calculated from the end of the axis domain.
80
+ * For example, `{ unit: 'month', step: 3 }` will zoom to show the last 3 months of data.
81
+ *
82
+ * @param value The range button value.
83
+ * @param params The axis context passed to function values.
84
+ * @param params.scaleType The scale type of the axis.
85
+ * @param params.data The axis data values (for ordinal axes).
86
+ * @param params.domain The full domain bounds.
87
+ * @returns The zoom start and end percentages (0-100).
88
+ */
89
+ function rangeButtonValueToZoom(value, params) {
90
+ if (value === null) {
91
+ return {
92
+ start: 0,
93
+ end: 100
94
+ };
95
+ }
96
+ if (typeof value === 'function') {
97
+ const result = value(params);
98
+ if (process.env.NODE_ENV !== 'production' && result.end < result.start) {
99
+ (0, _warning.warnOnce)([`MUI X Charts: Range button function returned an end value (${result.end}) lower than the start value (${result.start}).`, 'This likely produces an unexpected zoom range.']);
100
+ }
101
+ return {
102
+ start: result.start,
103
+ end: result.end
104
+ };
105
+ }
106
+ const {
107
+ domain: {
108
+ min: domainMin,
109
+ max: domainMax
110
+ },
111
+ data: ordinalData
112
+ } = params;
113
+
114
+ // For ordinal axes with date-like data, resolve date ranges and intervals to matching indices.
115
+ const timestamps = ordinalData ? toTimestampArray(ordinalData) : undefined;
116
+ if (timestamps) {
117
+ const maxIndex = timestamps.length - 1;
118
+ if (Array.isArray(value)) {
119
+ const startTarget = value[0].getTime();
120
+ const endTarget = value[1].getTime();
121
+ if (process.env.NODE_ENV !== 'production' && endTarget < startTarget) {
122
+ (0, _warning.warnOnce)(['MUI X Charts: Range button received a date range whose end is before its start.', 'This produces an empty zoom range.']);
123
+ }
124
+ const firstGte = timestamps.findIndex(ts => !Number.isNaN(ts) && ts >= startTarget);
125
+ const lastLte = timestamps.findLastIndex(ts => !Number.isNaN(ts) && ts <= endTarget);
126
+ const startIndex = firstGte === -1 ? maxIndex : firstGte;
127
+ const endIndex = lastLte === -1 ? 0 : lastLte;
128
+ return {
129
+ start: startIndex / maxIndex * 100,
130
+ end: endIndex / maxIndex * 100
131
+ };
132
+ }
133
+
134
+ // Interval — compute target date from the last valid data point's timestamp.
135
+ const lastValidIndex = timestamps.findLastIndex(ts => !Number.isNaN(ts));
136
+ const lastTimestamp = timestamps[lastValidIndex];
137
+ const targetStartMs = computeIntervalStart(value, lastTimestamp);
138
+ const firstGte = timestamps.findIndex(ts => !Number.isNaN(ts) && ts >= targetStartMs);
139
+ const startIndex = firstGte === -1 ? maxIndex : firstGte;
140
+ return {
141
+ start: startIndex / maxIndex * 100,
142
+ end: 100
143
+ };
144
+ }
145
+ if (process.env.NODE_ENV !== 'production' && ordinalData !== undefined) {
146
+ (0, _warning.warnOnce)(['MUI X Charts: Range button received a date value for an ordinal axis whose data could not be parsed as dates.', 'The zoom range may not match the intended selection. Provide date-like axis data or use a function value.']);
147
+ }
148
+ const domainRange = domainMax - domainMin;
149
+ if (domainRange <= 0) {
150
+ return {
151
+ start: 0,
152
+ end: 100
153
+ };
154
+ }
155
+
156
+ // Absolute date range
157
+ if (Array.isArray(value)) {
158
+ const [rangeStart, rangeEnd] = value;
159
+ if (process.env.NODE_ENV !== 'production' && rangeEnd.getTime() < rangeStart.getTime()) {
160
+ (0, _warning.warnOnce)(['MUI X Charts: Range button received a date range whose end is before its start.', 'This produces an empty zoom range.']);
161
+ }
162
+ const startPercent = (rangeStart.getTime() - domainMin) / domainRange * 100;
163
+ const endPercent = (rangeEnd.getTime() - domainMin) / domainRange * 100;
164
+ return {
165
+ start: startPercent,
166
+ end: endPercent
167
+ };
168
+ }
169
+
170
+ // Interval — subtract from the end of the domain
171
+ const targetStartMs = computeIntervalStart(value, domainMax);
172
+ const startPercent = (targetStartMs - domainMin) / domainRange * 100;
173
+ return {
174
+ start: startPercent,
175
+ end: 100
176
+ };
177
+ }
178
+
179
+ /**
180
+ * Computes the start timestamp for a calendar interval subtracted from a reference point.
181
+ */
182
+ function computeIntervalStart(interval, referenceMs) {
183
+ const {
184
+ unit,
185
+ step = 1
186
+ } = interval;
187
+ switch (unit) {
188
+ case 'year':
189
+ {
190
+ const d = new Date(referenceMs);
191
+ d.setFullYear(d.getFullYear() - step);
192
+ return d.getTime();
193
+ }
194
+ case 'month':
195
+ {
196
+ const d = new Date(referenceMs);
197
+ d.setMonth(d.getMonth() - step);
198
+ return d.getTime();
199
+ }
200
+ case 'week':
201
+ return referenceMs - step * 7 * 24 * 60 * 60 * 1000;
202
+ case 'day':
203
+ return referenceMs - step * 24 * 60 * 60 * 1000;
204
+ case 'hour':
205
+ return referenceMs - step * 60 * 60 * 1000;
206
+ case 'minute':
207
+ return referenceMs - step * 60 * 1000;
208
+ case 'second':
209
+ return referenceMs - step * 1000;
210
+ case 'millisecond':
211
+ return referenceMs - step;
212
+ case 'microsecond':
213
+ return referenceMs - step / 1000;
214
+ default:
215
+ return 0;
216
+ }
217
+ }
@@ -0,0 +1,212 @@
1
+ import { warnOnce } from '@mui/x-internals/warning';
2
+
3
+ /**
4
+ * A calendar interval unit for range buttons.
5
+ */
6
+
7
+ /**
8
+ * Parameters passed to a range button function value.
9
+ */
10
+
11
+ /**
12
+ * Defines the value of a range button.
13
+ *
14
+ * - `{ unit, step }` — A calendar interval from the end of the data.
15
+ * @example { unit: 'month', step: 3 } // Last 3 months
16
+ * @example { unit: 'year' } // Last year (step defaults to 1)
17
+ * - `[start, end]` — An absolute date range.
18
+ * @example [new Date(2024, 0, 1), new Date(2024, 6, 1)] // Jan–Jul 2024
19
+ * - `(params) => { start, end }` — A function that receives axis context (`scaleType`, `data`, `domain`) and returns zoom percentages (0-100).
20
+ * @example ({ domain }) => ({ start: 0, end: 50 }) // First half of data
21
+ * @example ({ data }) => {
22
+ * const lastFive = Math.max(0, data.length - 5);
23
+ * return { start: (lastFive / (data.length - 1)) * 100, end: 100 };
24
+ * } // Last 5 items on an ordinal axis
25
+ * - `null` — Resets zoom to show all data.
26
+ * @example null // Show all data
27
+ */
28
+
29
+ /**
30
+ * Attempts to convert a value to a timestamp.
31
+ * Handles Date objects, numeric timestamps, and date-like strings.
32
+ *
33
+ * @returns The timestamp in milliseconds, or `undefined` if the value is not date-like.
34
+ */
35
+ function toTimestamp(val) {
36
+ if (val instanceof Date) {
37
+ return val.getTime();
38
+ }
39
+ if (typeof val === 'number') {
40
+ return val;
41
+ }
42
+ if (typeof val === 'string') {
43
+ const parsed = Date.parse(val);
44
+ if (!Number.isNaN(parsed)) {
45
+ return parsed;
46
+ }
47
+ }
48
+ return undefined;
49
+ }
50
+
51
+ /**
52
+ * Converts ordinal data to timestamps.
53
+ * Items that cannot be converted are kept as `NaN` so their index is preserved.
54
+ * Returns `undefined` if fewer than 2 items could be converted.
55
+ */
56
+ function toTimestampArray(data) {
57
+ const timestamps = new Array(data.length);
58
+ let convertedItems = 0;
59
+ for (let i = 0; i < data.length; i += 1) {
60
+ const ts = toTimestamp(data[i]);
61
+ if (ts !== undefined) {
62
+ convertedItems += 1;
63
+ timestamps[i] = ts;
64
+ } else {
65
+ timestamps[i] = Number.NaN;
66
+ }
67
+ }
68
+ return convertedItems >= 2 ? timestamps : undefined;
69
+ }
70
+
71
+ /**
72
+ * Converts a range button value to zoom start/end percentages.
73
+ *
74
+ * The range is calculated from the end of the axis domain.
75
+ * For example, `{ unit: 'month', step: 3 }` will zoom to show the last 3 months of data.
76
+ *
77
+ * @param value The range button value.
78
+ * @param params The axis context passed to function values.
79
+ * @param params.scaleType The scale type of the axis.
80
+ * @param params.data The axis data values (for ordinal axes).
81
+ * @param params.domain The full domain bounds.
82
+ * @returns The zoom start and end percentages (0-100).
83
+ */
84
+ export function rangeButtonValueToZoom(value, params) {
85
+ if (value === null) {
86
+ return {
87
+ start: 0,
88
+ end: 100
89
+ };
90
+ }
91
+ if (typeof value === 'function') {
92
+ const result = value(params);
93
+ if (process.env.NODE_ENV !== 'production' && result.end < result.start) {
94
+ warnOnce([`MUI X Charts: Range button function returned an end value (${result.end}) lower than the start value (${result.start}).`, 'This likely produces an unexpected zoom range.']);
95
+ }
96
+ return {
97
+ start: result.start,
98
+ end: result.end
99
+ };
100
+ }
101
+ const {
102
+ domain: {
103
+ min: domainMin,
104
+ max: domainMax
105
+ },
106
+ data: ordinalData
107
+ } = params;
108
+
109
+ // For ordinal axes with date-like data, resolve date ranges and intervals to matching indices.
110
+ const timestamps = ordinalData ? toTimestampArray(ordinalData) : undefined;
111
+ if (timestamps) {
112
+ const maxIndex = timestamps.length - 1;
113
+ if (Array.isArray(value)) {
114
+ const startTarget = value[0].getTime();
115
+ const endTarget = value[1].getTime();
116
+ if (process.env.NODE_ENV !== 'production' && endTarget < startTarget) {
117
+ warnOnce(['MUI X Charts: Range button received a date range whose end is before its start.', 'This produces an empty zoom range.']);
118
+ }
119
+ const firstGte = timestamps.findIndex(ts => !Number.isNaN(ts) && ts >= startTarget);
120
+ const lastLte = timestamps.findLastIndex(ts => !Number.isNaN(ts) && ts <= endTarget);
121
+ const startIndex = firstGte === -1 ? maxIndex : firstGte;
122
+ const endIndex = lastLte === -1 ? 0 : lastLte;
123
+ return {
124
+ start: startIndex / maxIndex * 100,
125
+ end: endIndex / maxIndex * 100
126
+ };
127
+ }
128
+
129
+ // Interval — compute target date from the last valid data point's timestamp.
130
+ const lastValidIndex = timestamps.findLastIndex(ts => !Number.isNaN(ts));
131
+ const lastTimestamp = timestamps[lastValidIndex];
132
+ const targetStartMs = computeIntervalStart(value, lastTimestamp);
133
+ const firstGte = timestamps.findIndex(ts => !Number.isNaN(ts) && ts >= targetStartMs);
134
+ const startIndex = firstGte === -1 ? maxIndex : firstGte;
135
+ return {
136
+ start: startIndex / maxIndex * 100,
137
+ end: 100
138
+ };
139
+ }
140
+ if (process.env.NODE_ENV !== 'production' && ordinalData !== undefined) {
141
+ warnOnce(['MUI X Charts: Range button received a date value for an ordinal axis whose data could not be parsed as dates.', 'The zoom range may not match the intended selection. Provide date-like axis data or use a function value.']);
142
+ }
143
+ const domainRange = domainMax - domainMin;
144
+ if (domainRange <= 0) {
145
+ return {
146
+ start: 0,
147
+ end: 100
148
+ };
149
+ }
150
+
151
+ // Absolute date range
152
+ if (Array.isArray(value)) {
153
+ const [rangeStart, rangeEnd] = value;
154
+ if (process.env.NODE_ENV !== 'production' && rangeEnd.getTime() < rangeStart.getTime()) {
155
+ warnOnce(['MUI X Charts: Range button received a date range whose end is before its start.', 'This produces an empty zoom range.']);
156
+ }
157
+ const startPercent = (rangeStart.getTime() - domainMin) / domainRange * 100;
158
+ const endPercent = (rangeEnd.getTime() - domainMin) / domainRange * 100;
159
+ return {
160
+ start: startPercent,
161
+ end: endPercent
162
+ };
163
+ }
164
+
165
+ // Interval — subtract from the end of the domain
166
+ const targetStartMs = computeIntervalStart(value, domainMax);
167
+ const startPercent = (targetStartMs - domainMin) / domainRange * 100;
168
+ return {
169
+ start: startPercent,
170
+ end: 100
171
+ };
172
+ }
173
+
174
+ /**
175
+ * Computes the start timestamp for a calendar interval subtracted from a reference point.
176
+ */
177
+ function computeIntervalStart(interval, referenceMs) {
178
+ const {
179
+ unit,
180
+ step = 1
181
+ } = interval;
182
+ switch (unit) {
183
+ case 'year':
184
+ {
185
+ const d = new Date(referenceMs);
186
+ d.setFullYear(d.getFullYear() - step);
187
+ return d.getTime();
188
+ }
189
+ case 'month':
190
+ {
191
+ const d = new Date(referenceMs);
192
+ d.setMonth(d.getMonth() - step);
193
+ return d.getTime();
194
+ }
195
+ case 'week':
196
+ return referenceMs - step * 7 * 24 * 60 * 60 * 1000;
197
+ case 'day':
198
+ return referenceMs - step * 24 * 60 * 60 * 1000;
199
+ case 'hour':
200
+ return referenceMs - step * 60 * 60 * 1000;
201
+ case 'minute':
202
+ return referenceMs - step * 60 * 1000;
203
+ case 'second':
204
+ return referenceMs - step * 1000;
205
+ case 'millisecond':
206
+ return referenceMs - step;
207
+ case 'microsecond':
208
+ return referenceMs - step / 1000;
209
+ default:
210
+ return 0;
211
+ }
212
+ }
@@ -335,6 +335,7 @@ process.env.NODE_ENV !== "production" ? Heatmap.propTypes = {
335
335
  tickSize: _propTypes.default.number,
336
336
  tickSpacing: _propTypes.default.number,
337
337
  valueFormatter: _propTypes.default.func,
338
+ valueGetter: _propTypes.default.func,
338
339
  zoom: _propTypes.default.oneOfType([_propTypes.default.shape({
339
340
  filterMode: _propTypes.default.oneOf(['discard', 'keep']),
340
341
  maxEnd: _propTypes.default.number,
@@ -415,6 +416,7 @@ process.env.NODE_ENV !== "production" ? Heatmap.propTypes = {
415
416
  tickSize: _propTypes.default.number,
416
417
  tickSpacing: _propTypes.default.number,
417
418
  valueFormatter: _propTypes.default.func,
419
+ valueGetter: _propTypes.default.func,
418
420
  width: _propTypes.default.oneOfType([_propTypes.default.oneOf(['auto']), _propTypes.default.number]),
419
421
  zoom: _propTypes.default.oneOfType([_propTypes.default.shape({
420
422
  filterMode: _propTypes.default.oneOf(['discard', 'keep']),
@@ -455,7 +457,8 @@ process.env.NODE_ENV !== "production" ? Heatmap.propTypes = {
455
457
  dataKey: _propTypes.default.string,
456
458
  id: _propTypes.default.string,
457
459
  max: _propTypes.default.number,
458
- min: _propTypes.default.number
460
+ min: _propTypes.default.number,
461
+ valueGetter: _propTypes.default.func
459
462
  })),
460
463
  /**
461
464
  * The list of zoom data related to each axis.