@mui/x-charts-pro 8.1.0 → 8.2.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 (94) hide show
  1. package/BarChartPro/BarChartPro.js +2 -1
  2. package/CHANGELOG.md +126 -7
  3. package/ChartContainerPro/useChartContainerProProps.d.ts +1 -1
  4. package/ChartDataProviderPro/ChartDataProviderPro.js +1 -1
  5. package/ChartDataProviderPro/useChartDataProviderProProps.d.ts +2 -2
  6. package/FunnelChart/FunnelChart.js +7 -1
  7. package/FunnelChart/FunnelPlot.d.ts +5 -0
  8. package/FunnelChart/FunnelPlot.js +13 -16
  9. package/FunnelChart/curves/bump.d.ts +24 -0
  10. package/FunnelChart/curves/bump.js +82 -0
  11. package/FunnelChart/curves/curve.types.d.ts +8 -0
  12. package/FunnelChart/curves/curve.types.js +5 -0
  13. package/FunnelChart/curves/funnelStep.d.ts +25 -0
  14. package/FunnelChart/{funnelStepCurve.js → curves/funnelStep.js} +32 -19
  15. package/FunnelChart/curves/getFunnelCurve.d.ts +3 -0
  16. package/FunnelChart/curves/getFunnelCurve.js +22 -0
  17. package/FunnelChart/curves/index.d.ts +2 -0
  18. package/FunnelChart/curves/index.js +27 -0
  19. package/FunnelChart/curves/linear.d.ts +24 -0
  20. package/FunnelChart/curves/linear.js +113 -0
  21. package/FunnelChart/funnel.types.d.ts +2 -2
  22. package/FunnelChart/funnelPlotSlots.types.d.ts +1 -1
  23. package/FunnelChart/funnelSectionClasses.d.ts +1 -1
  24. package/FunnelChart/index.d.ts +3 -2
  25. package/FunnelChart/useFunnelChartProps.d.ts +1 -1
  26. package/FunnelChart/useFunnelChartProps.js +7 -4
  27. package/Heatmap/Heatmap.js +1 -1
  28. package/LineChartPro/LineChartPro.js +2 -1
  29. package/ScatterChartPro/ScatterChartPro.js +2 -1
  30. package/esm/BarChartPro/BarChartPro.js +2 -1
  31. package/esm/ChartContainerPro/useChartContainerProProps.d.ts +1 -1
  32. package/esm/ChartDataProviderPro/ChartDataProviderPro.js +1 -1
  33. package/esm/ChartDataProviderPro/useChartDataProviderProProps.d.ts +2 -2
  34. package/esm/FunnelChart/FunnelChart.js +7 -1
  35. package/esm/FunnelChart/FunnelPlot.d.ts +5 -0
  36. package/esm/FunnelChart/FunnelPlot.js +14 -17
  37. package/esm/FunnelChart/curves/bump.d.ts +24 -0
  38. package/esm/FunnelChart/curves/bump.js +75 -0
  39. package/esm/FunnelChart/curves/curve.types.d.ts +8 -0
  40. package/esm/FunnelChart/curves/curve.types.js +1 -0
  41. package/esm/FunnelChart/curves/funnelStep.d.ts +25 -0
  42. package/esm/FunnelChart/{funnelStepCurve.js → curves/funnelStep.js} +32 -19
  43. package/esm/FunnelChart/curves/getFunnelCurve.d.ts +3 -0
  44. package/esm/FunnelChart/curves/getFunnelCurve.js +15 -0
  45. package/esm/FunnelChart/curves/index.d.ts +2 -0
  46. package/esm/FunnelChart/curves/index.js +2 -0
  47. package/esm/FunnelChart/curves/linear.d.ts +24 -0
  48. package/esm/FunnelChart/curves/linear.js +106 -0
  49. package/esm/FunnelChart/funnel.types.d.ts +2 -2
  50. package/esm/FunnelChart/funnelPlotSlots.types.d.ts +1 -1
  51. package/esm/FunnelChart/funnelSectionClasses.d.ts +1 -1
  52. package/esm/FunnelChart/index.d.ts +3 -2
  53. package/esm/FunnelChart/useFunnelChartProps.d.ts +1 -1
  54. package/esm/FunnelChart/useFunnelChartProps.js +7 -4
  55. package/esm/Heatmap/Heatmap.js +1 -1
  56. package/esm/LineChartPro/LineChartPro.js +2 -1
  57. package/esm/ScatterChartPro/ScatterChartPro.js +2 -1
  58. package/esm/index.js +1 -1
  59. package/esm/internals/plugins/useChartProExport/common.d.ts +1 -0
  60. package/esm/internals/plugins/useChartProExport/common.js +8 -0
  61. package/esm/internals/plugins/useChartProExport/exportImage.d.ts +3 -0
  62. package/esm/internals/plugins/useChartProExport/exportImage.js +85 -0
  63. package/esm/internals/plugins/useChartProExport/print.js +6 -50
  64. package/esm/internals/plugins/useChartProExport/useChartProExport.js +22 -2
  65. package/esm/internals/plugins/useChartProExport/useChartProExport.types.d.ts +32 -0
  66. package/esm/internals/plugins/useChartProZoom/useChartProZoom.js +1 -1
  67. package/esm/internals/plugins/useChartProZoom/useChartProZoom.selectors.d.ts +25 -25
  68. package/esm/themeAugmentation/components.d.ts +0 -4
  69. package/esm/themeAugmentation/components.js +1 -0
  70. package/esm/themeAugmentation/index.d.ts +3 -3
  71. package/esm/themeAugmentation/overrides.d.ts +0 -3
  72. package/esm/themeAugmentation/overrides.js +2 -0
  73. package/esm/themeAugmentation/props.d.ts +0 -6
  74. package/esm/themeAugmentation/props.js +2 -0
  75. package/index.js +1 -1
  76. package/internals/plugins/useChartProExport/common.d.ts +1 -0
  77. package/internals/plugins/useChartProExport/common.js +14 -0
  78. package/internals/plugins/useChartProExport/exportImage.d.ts +3 -0
  79. package/internals/plugins/useChartProExport/exportImage.js +95 -0
  80. package/internals/plugins/useChartProExport/print.js +6 -50
  81. package/internals/plugins/useChartProExport/useChartProExport.js +22 -2
  82. package/internals/plugins/useChartProExport/useChartProExport.types.d.ts +32 -0
  83. package/internals/plugins/useChartProZoom/useChartProZoom.js +1 -1
  84. package/internals/plugins/useChartProZoom/useChartProZoom.selectors.d.ts +25 -25
  85. package/package.json +7 -4
  86. package/themeAugmentation/components.d.ts +0 -4
  87. package/themeAugmentation/components.js +5 -0
  88. package/themeAugmentation/index.d.ts +3 -3
  89. package/themeAugmentation/overrides.d.ts +0 -3
  90. package/themeAugmentation/overrides.js +5 -0
  91. package/themeAugmentation/props.d.ts +0 -6
  92. package/themeAugmentation/props.js +5 -0
  93. package/FunnelChart/funnelStepCurve.d.ts +0 -4
  94. package/esm/FunnelChart/funnelStepCurve.d.ts +0 -4
@@ -2,19 +2,23 @@
2
2
  * This is a custom "step" curve generator for the funnel chart.
3
3
  * It is used to draw the funnel using "rectangles" without having to rework the rendering logic.
4
4
  *
5
+ * It takes into account the gap between the points and draws a smooth curve between them.
6
+ *
5
7
  * It is based on the d3-shape step curve generator.
6
8
  * https://github.com/d3/d3-shape/blob/a82254af78f08799c71d7ab25df557c4872a3c51/src/curve/step.js
7
9
  */
8
- class FunnelStep {
9
- constructor(context, isHorizontal) {
10
+ export class FunnelStep {
11
+ constructor(context, isHorizontal, gap = 0) {
10
12
  this.context = void 0;
11
13
  this.line = NaN;
12
14
  this.x = NaN;
13
15
  this.y = NaN;
14
16
  this.currentPoint = 0;
15
17
  this.isHorizontal = false;
18
+ this.gap = 0;
16
19
  this.context = context;
17
20
  this.isHorizontal = isHorizontal;
21
+ this.gap = gap / 2;
18
22
  }
19
23
  areaStart() {
20
24
  this.line = 0;
@@ -41,27 +45,36 @@ class FunnelStep {
41
45
  point(x, y) {
42
46
  x = +x;
43
47
  y = +y;
48
+
49
+ // 0 is the top-left corner.
50
+ if (this.isHorizontal) {
51
+ if (this.currentPoint === 0) {
52
+ this.context.moveTo(x + this.gap, y);
53
+ } else if (this.currentPoint === 1 || this.currentPoint === 2) {
54
+ this.context.lineTo(x - this.gap, this.y);
55
+ this.context.lineTo(x - this.gap, y);
56
+ } else {
57
+ this.context.lineTo(this.x - this.gap, y);
58
+ this.context.lineTo(x + this.gap, y);
59
+ }
60
+ this.currentPoint += 1;
61
+ this.x = x;
62
+ this.y = y;
63
+ return;
64
+ }
65
+
66
+ // 0 is the top-right corner.
44
67
  if (this.currentPoint === 0) {
45
- this.context.moveTo(x, y);
46
- } else if (this.isHorizontal && (this.currentPoint === 2 || this.currentPoint === 1)) {
47
- this.context.lineTo(x, this.y);
48
- this.context.lineTo(x, y);
49
- } else if (this.currentPoint === 3 && !this.isHorizontal) {
50
- this.context.lineTo(x, this.y);
51
- this.context.lineTo(x, y);
68
+ this.context.moveTo(x, y + this.gap);
69
+ } else if (this.currentPoint === 3) {
70
+ this.context.lineTo(x, this.y - this.gap);
71
+ this.context.lineTo(x, y + this.gap);
52
72
  } else {
53
- this.context.lineTo(this.x, y);
54
- this.context.lineTo(x, y);
73
+ this.context.lineTo(this.x, y - this.gap);
74
+ this.context.lineTo(x, y - this.gap);
55
75
  }
56
76
  this.currentPoint += 1;
57
77
  this.x = x;
58
78
  this.y = y;
59
79
  }
60
- }
61
- const funnelHorizontalStepCurve = context => {
62
- return new FunnelStep(context, true);
63
- };
64
- const funnelVerticalStepCurve = context => {
65
- return new FunnelStep(context, false);
66
- };
67
- export { funnelHorizontalStepCurve, funnelVerticalStepCurve };
80
+ }
@@ -0,0 +1,3 @@
1
+ import { CurveFactory } from '@mui/x-charts-vendor/d3-shape';
2
+ import { FunnelCurveType } from "./curve.types.js";
3
+ export declare const getFunnelCurve: (curve: FunnelCurveType | undefined, isHorizontal: boolean, gap?: number) => CurveFactory;
@@ -0,0 +1,15 @@
1
+ import { FunnelStep } from "./funnelStep.js";
2
+ import { Linear } from "./linear.js";
3
+ import { Bump } from "./bump.js";
4
+ const curveConstructor = curve => {
5
+ if (curve === 'step') {
6
+ return FunnelStep;
7
+ }
8
+ if (curve === 'bump') {
9
+ return Bump;
10
+ }
11
+ return Linear;
12
+ };
13
+ export const getFunnelCurve = (curve, isHorizontal, gap = 0) => {
14
+ return context => new (curveConstructor(curve))(context, isHorizontal, gap);
15
+ };
@@ -0,0 +1,2 @@
1
+ export * from "./curve.types.js";
2
+ export * from "./getFunnelCurve.js";
@@ -0,0 +1,2 @@
1
+ export * from "./curve.types.js";
2
+ export * from "./getFunnelCurve.js";
@@ -0,0 +1,24 @@
1
+ import { CurveGenerator } from '@mui/x-charts-vendor/d3-shape';
2
+ /**
3
+ * This is a custom "linear" curve generator.
4
+ *
5
+ * It takes into account the gap between the points and draws a smooth curve between them.
6
+ *
7
+ * It is based on the d3-shape linear curve generator.
8
+ * https://github.com/d3/d3-shape/blob/a82254af78f08799c71d7ab25df557c4872a3c51/src/curve/linear.js
9
+ */
10
+ export declare class Linear implements CurveGenerator {
11
+ private context;
12
+ private line;
13
+ private x;
14
+ private y;
15
+ private currentPoint;
16
+ private isHorizontal;
17
+ private gap;
18
+ constructor(context: CanvasRenderingContext2D, isHorizontal: boolean, gap?: number);
19
+ areaStart(): void;
20
+ areaEnd(): void;
21
+ lineStart(): void;
22
+ lineEnd(): void;
23
+ point(x: number, y: number): void;
24
+ }
@@ -0,0 +1,106 @@
1
+ // From point1 to point2, get the x value from y
2
+ const xFromY = (x1, y1, x2, y2) => y => {
3
+ if (y1 === y2) {
4
+ return x1;
5
+ }
6
+ const result = (x2 - x1) * (y - y1) / (y2 - y1) + x1;
7
+ return Number.isNaN(result) ? 0 : result;
8
+ };
9
+
10
+ // From point1 to point2, get the y value from x
11
+ const yFromX = (x1, y1, x2, y2) => x => {
12
+ if (x1 === x2) {
13
+ return y1;
14
+ }
15
+ const result = (y2 - y1) * (x - x1) / (x2 - x1) + y1;
16
+ return Number.isNaN(result) ? 0 : result;
17
+ };
18
+
19
+ /**
20
+ * This is a custom "linear" curve generator.
21
+ *
22
+ * It takes into account the gap between the points and draws a smooth curve between them.
23
+ *
24
+ * It is based on the d3-shape linear curve generator.
25
+ * https://github.com/d3/d3-shape/blob/a82254af78f08799c71d7ab25df557c4872a3c51/src/curve/linear.js
26
+ */
27
+ export class Linear {
28
+ constructor(context, isHorizontal, gap = 0) {
29
+ this.context = void 0;
30
+ this.line = NaN;
31
+ this.x = NaN;
32
+ this.y = NaN;
33
+ this.currentPoint = 0;
34
+ this.isHorizontal = false;
35
+ this.gap = 0;
36
+ this.context = context;
37
+ this.isHorizontal = isHorizontal;
38
+ this.gap = gap / 2;
39
+ }
40
+ areaStart() {
41
+ this.line = 0;
42
+ }
43
+ areaEnd() {
44
+ this.line = NaN;
45
+ }
46
+ lineStart() {
47
+ this.currentPoint = 0;
48
+ }
49
+ lineEnd() {
50
+ if (this.line || this.line !== 0 && this.currentPoint === 1) {
51
+ this.context.closePath();
52
+ }
53
+ this.line = 1 - this.line;
54
+ }
55
+ point(x, y) {
56
+ x = +x;
57
+ y = +y;
58
+
59
+ // We draw the lines only at currentPoint 1 & 3 because we need
60
+ // The data of a pair of points to draw the lines.
61
+ // Hence currentPoint 1 draws a line from point 0 to point 1 and point 1 to point 2.
62
+ // currentPoint 3 draws a line from point 2 to point 3 and point 3 to point 0.
63
+
64
+ if (this.isHorizontal) {
65
+ const yGetter = yFromX(this.x, this.y, x, y);
66
+ let xGap = 0;
67
+
68
+ // 0 is the top-left corner.
69
+ if (this.currentPoint === 1) {
70
+ xGap = this.x + this.gap;
71
+ this.context.moveTo(xGap, yGetter(xGap));
72
+ this.context.lineTo(xGap, yGetter(xGap));
73
+ xGap = x - this.gap;
74
+ this.context.lineTo(xGap, yGetter(xGap));
75
+ } else if (this.currentPoint === 3) {
76
+ xGap = this.x - this.gap;
77
+ this.context.lineTo(xGap, yGetter(xGap));
78
+ xGap = x + this.gap;
79
+ this.context.lineTo(xGap, yGetter(xGap));
80
+ }
81
+ }
82
+ if (!this.isHorizontal) {
83
+ const xGetter = xFromY(this.x, this.y, x, y);
84
+ let yGap = 0;
85
+
86
+ // 0 is the top-right corner.
87
+ if (this.currentPoint === 1) {
88
+ yGap = this.y + this.gap;
89
+ this.context.moveTo(xGetter(yGap), yGap);
90
+ this.context.lineTo(xGetter(yGap), yGap);
91
+ yGap = y - this.gap;
92
+ this.context.lineTo(xGetter(yGap), yGap);
93
+ } else if (this.currentPoint === 3) {
94
+ yGap = this.y - this.gap;
95
+ this.context.lineTo(xGetter(yGap), yGap);
96
+ yGap = y + this.gap;
97
+ this.context.lineTo(xGetter(yGap), yGap);
98
+ }
99
+ }
100
+
101
+ // Increment the values
102
+ this.currentPoint += 1;
103
+ this.x = x;
104
+ this.y = y;
105
+ }
106
+ }
@@ -1,9 +1,9 @@
1
1
  import { ChartsLabelMarkProps } from '@mui/x-charts/ChartsLabel';
2
2
  import { CommonSeriesType, CartesianSeriesType, CommonDefaultizedProps, SeriesId } from '@mui/x-charts/internals';
3
- import { CurveType, Position } from '@mui/x-charts/models';
3
+ import { Position } from '@mui/x-charts/models';
4
4
  import { DefaultizedProps, MakeRequired } from '@mui/x-internals/types';
5
+ import { FunnelCurveType } from "./curves/index.js";
5
6
  export type FunnelItemId = string | number;
6
- export type FunnelCurveType = Extract<CurveType, 'linear' | 'step'> | 'bump';
7
7
  export type FunnelValueType = {
8
8
  /**
9
9
  * A unique identifier of the funnel section.
@@ -1,5 +1,5 @@
1
1
  import * as React from 'react';
2
- import type { FunnelSectionProps } from './FunnelSection';
2
+ import type { FunnelSectionProps } from "./FunnelSection.js";
3
3
  export interface FunnelPlotSlots {
4
4
  funnelSection?: React.ElementType<FunnelSectionProps>;
5
5
  }
@@ -1,4 +1,4 @@
1
- import type { FunnelSectionProps } from './FunnelSection';
1
+ import type { FunnelSectionProps } from "./FunnelSection.js";
2
2
  export interface FunnelSectionClasses {
3
3
  /** Styles applied to the root element. */
4
4
  root: string;
@@ -1,8 +1,9 @@
1
1
  export { FunnelChart as Unstable_FunnelChart } from "./FunnelChart.js";
2
- export type { FunnelChartProps } from './FunnelChart';
2
+ export type { FunnelChartProps } from "./FunnelChart.js";
3
3
  export * from "./FunnelPlot.js";
4
4
  export * from "./funnel.types.js";
5
5
  export * from "./categoryAxis.types.js";
6
6
  export * from "./funnelSlots.types.js";
7
+ export type { FunnelCurveType } from "./curves/index.js";
7
8
  export { funnelSectionClasses } from "./funnelSectionClasses.js";
8
- export type { FunnelSectionClasses } from './funnelSectionClasses';
9
+ export type { FunnelSectionClasses } from "./funnelSectionClasses.js";
@@ -5,7 +5,7 @@ import { ChartsClipPathProps } from '@mui/x-charts/ChartsClipPath';
5
5
  import { ChartsWrapperProps } from '@mui/x-charts/internals';
6
6
  import { ChartsAxisHighlightProps } from '@mui/x-charts/ChartsAxisHighlight';
7
7
  import { FunnelPlotProps } from "./FunnelPlot.js";
8
- import type { FunnelChartProps } from './FunnelChart';
8
+ import type { FunnelChartProps } from "./FunnelChart.js";
9
9
  import { ChartContainerProProps } from "../ChartContainerPro/index.js";
10
10
  /**
11
11
  * A helper function that extracts FunnelChartProps from the input props
@@ -2,11 +2,12 @@
2
2
 
3
3
  import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/esm/objectWithoutPropertiesLoose";
4
4
  import _extends from "@babel/runtime/helpers/esm/extends";
5
- const _excluded = ["categoryAxis", "series", "width", "height", "margin", "colors", "sx", "children", "slots", "slotProps", "skipAnimation", "loading", "onItemClick", "highlightedItem", "onHighlightChange", "className", "hideLegend", "axisHighlight", "apiRef"];
5
+ const _excluded = ["categoryAxis", "series", "width", "height", "margin", "colors", "sx", "children", "slots", "slotProps", "skipAnimation", "loading", "onItemClick", "highlightedItem", "onHighlightChange", "className", "hideLegend", "axisHighlight", "apiRef", "gap"];
6
6
  import { DEFAULT_MARGINS, DEFAULT_X_AXIS_KEY, DEFAULT_Y_AXIS_KEY } from '@mui/x-charts/constants';
7
7
  import useId from '@mui/utils/useId';
8
8
  import { defaultizeMargin } from '@mui/x-charts/internals';
9
9
  import { warnOnce } from '@mui/x-internals/warning';
10
+ import { strawberrySkyPalette } from '@mui/x-charts/colorPalettes';
10
11
  function getCategoryAxisConfig(categoryAxis, series, isHorizontal, direction) {
11
12
  const maxSeriesLength = Math.max(...series.map(s => (s.data ?? []).length), 0);
12
13
  const maxSeriesValue = Array.from({
@@ -14,7 +15,7 @@ function getCategoryAxisConfig(categoryAxis, series, isHorizontal, direction) {
14
15
  }, (_, index) => series.reduce((a, s) => a + (s.data?.[index]?.value ?? 0), 0));
15
16
  if (process.env.NODE_ENV !== 'production') {
16
17
  if ((categoryAxis?.position === 'left' || categoryAxis?.position === 'right') && isHorizontal || (categoryAxis?.position === 'top' || categoryAxis?.position === 'bottom') && !isHorizontal) {
17
- warnOnce([`MUI X: the categoryAxis position is set to '${categoryAxis.position}' but the series layout is ${isHorizontal ? 'horizontal' : 'vertical'}.`, `Ensure that the categoryAxis position is set to '${isHorizontal ? 'top' : 'left'}' or '${isHorizontal ? 'bottom' : 'right'}' for ${isHorizontal ? 'horizontal' : 'vertical'} layout.\n`], 'warning');
18
+ warnOnce([`MUI X Charts: the categoryAxis position is set to '${categoryAxis.position}' but the series layout is ${isHorizontal ? 'horizontal' : 'vertical'}.`, `Ensure that the categoryAxis position is set to '${isHorizontal ? 'top' : 'left'}' or '${isHorizontal ? 'bottom' : 'right'}' for ${isHorizontal ? 'horizontal' : 'vertical'} layout.\n`], 'warning');
18
19
  }
19
20
  }
20
21
  const side = isHorizontal ? 'bottom' : 'left';
@@ -84,7 +85,8 @@ export const useFunnelChartProps = props => {
84
85
  onHighlightChange,
85
86
  className,
86
87
  axisHighlight,
87
- apiRef
88
+ apiRef,
89
+ gap
88
90
  } = props,
89
91
  rest = _objectWithoutPropertiesLoose(props, _excluded);
90
92
  const margin = defaultizeMargin(marginProps, DEFAULT_MARGINS);
@@ -107,7 +109,7 @@ export const useFunnelChartProps = props => {
107
109
  width,
108
110
  height,
109
111
  margin,
110
- colors,
112
+ colors: colors ?? strawberrySkyPalette,
111
113
  xAxis: [xAxis],
112
114
  yAxis: [yAxis],
113
115
  sx,
@@ -117,6 +119,7 @@ export const useFunnelChartProps = props => {
117
119
  apiRef
118
120
  });
119
121
  const funnelPlotProps = {
122
+ gap,
120
123
  onItemClick,
121
124
  slots,
122
125
  slotProps
@@ -187,7 +187,7 @@ process.env.NODE_ENV !== "production" ? Heatmap.propTypes = {
187
187
  * The function called for onClick events.
188
188
  * The second argument contains information about all line/bar elements at the current mouse position.
189
189
  * @param {MouseEvent} event The mouse event recorded on the `<svg/>` element.
190
- * @param {null | AxisData} data The data about the clicked axis and items associated with it.
190
+ * @param {null | ChartsAxisData} data The data about the clicked axis and items associated with it.
191
191
  */
192
192
  onAxisClick: PropTypes.func,
193
193
  /**
@@ -89,6 +89,7 @@ process.env.NODE_ENV !== "production" ? LineChartPro.propTypes = {
89
89
  // ----------------------------------------------------------------------
90
90
  apiRef: PropTypes.shape({
91
91
  current: PropTypes.shape({
92
+ exportAsImage: PropTypes.func.isRequired,
92
93
  exportAsPrint: PropTypes.func.isRequired,
93
94
  setZoomData: PropTypes.func.isRequired
94
95
  })
@@ -190,7 +191,7 @@ process.env.NODE_ENV !== "production" ? LineChartPro.propTypes = {
190
191
  * The function called for onClick events.
191
192
  * The second argument contains information about all line/bar elements at the current mouse position.
192
193
  * @param {MouseEvent} event The mouse event recorded on the `<svg/>` element.
193
- * @param {null | AxisData} data The data about the clicked axis and items associated with it.
194
+ * @param {null | ChartsAxisData} data The data about the clicked axis and items associated with it.
194
195
  */
195
196
  onAxisClick: PropTypes.func,
196
197
  /**
@@ -83,6 +83,7 @@ process.env.NODE_ENV !== "production" ? ScatterChartPro.propTypes = {
83
83
  // ----------------------------------------------------------------------
84
84
  apiRef: PropTypes.shape({
85
85
  current: PropTypes.shape({
86
+ exportAsImage: PropTypes.func.isRequired,
86
87
  exportAsPrint: PropTypes.func.isRequired,
87
88
  setZoomData: PropTypes.func.isRequired
88
89
  })
@@ -181,7 +182,7 @@ process.env.NODE_ENV !== "production" ? ScatterChartPro.propTypes = {
181
182
  * The function called for onClick events.
182
183
  * The second argument contains information about all line/bar elements at the current mouse position.
183
184
  * @param {MouseEvent} event The mouse event recorded on the `<svg/>` element.
184
- * @param {null | AxisData} data The data about the clicked axis and items associated with it.
185
+ * @param {null | ChartsAxisData} data The data about the clicked axis and items associated with it.
185
186
  */
186
187
  onAxisClick: PropTypes.func,
187
188
  /**
package/esm/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @mui/x-charts-pro v8.1.0
2
+ * @mui/x-charts-pro v8.2.0
3
3
  *
4
4
  * @license MUI X Commercial
5
5
  * This source code is licensed under the commercial license found in the
@@ -0,0 +1 @@
1
+ export declare function createExportIframe(title?: string): HTMLIFrameElement;
@@ -0,0 +1,8 @@
1
+ export function createExportIframe(title) {
2
+ const iframeEl = document.createElement('iframe');
3
+ iframeEl.style.position = 'absolute';
4
+ iframeEl.style.width = '0px';
5
+ iframeEl.style.height = '0px';
6
+ iframeEl.title = title || document.title;
7
+ return iframeEl;
8
+ }
@@ -0,0 +1,3 @@
1
+ import { ChartImageExportOptions } from "./useChartProExport.types.js";
2
+ export declare const getDrawDocument: () => Promise<typeof import("rasterizehtml").drawDocument>;
3
+ export declare function exportImage(element: HTMLElement | SVGElement, params?: ChartImageExportOptions): Promise<void>;
@@ -0,0 +1,85 @@
1
+ import ownerDocument from '@mui/utils/ownerDocument';
2
+ import { loadStyleSheets } from '@mui/x-internals/export';
3
+ import { createExportIframe } from "./common.js";
4
+ export const getDrawDocument = async () => {
5
+ try {
6
+ const module = await import('rasterizehtml');
7
+ return module.drawDocument;
8
+ } catch (error) {
9
+ throw new Error(`MUI X Charts: Failed to import 'rasterizehtml' module. This dependency is mandatory when exporting a chart as an image. Make sure you have it installed as a dependency.`, {
10
+ cause: error
11
+ });
12
+ }
13
+ };
14
+ export async function exportImage(element, params) {
15
+ const {
16
+ fileName,
17
+ type = 'image/png',
18
+ quality = 0.9
19
+ } = params ?? {};
20
+ const drawDocumentPromise = getDrawDocument();
21
+ const {
22
+ width,
23
+ height
24
+ } = element.getBoundingClientRect();
25
+ const doc = ownerDocument(element);
26
+ const canvas = document.createElement('canvas');
27
+ const ratio = window.devicePixelRatio || 1;
28
+ canvas.width = width * ratio;
29
+ canvas.height = height * ratio;
30
+ canvas.style.width = `${width}px`;
31
+ canvas.style.height = `${height}px`;
32
+ const iframe = createExportIframe(fileName);
33
+ let resolve;
34
+ const iframeLoadPromise = new Promise(res => {
35
+ resolve = res;
36
+ });
37
+ iframe.onload = async () => {
38
+ const exportDoc = iframe.contentDocument;
39
+ const elementClone = element.cloneNode(true);
40
+ const container = document.createElement('div');
41
+ container.appendChild(elementClone);
42
+ exportDoc.body.innerHTML = container.innerHTML;
43
+ exportDoc.body.style.margin = '0px';
44
+ const rootCandidate = element.getRootNode();
45
+ const root = rootCandidate.constructor.name === 'ShadowRoot' ? rootCandidate : doc;
46
+ await Promise.all(loadStyleSheets(exportDoc, root));
47
+ resolve();
48
+ };
49
+ doc.body.appendChild(iframe);
50
+ const [drawDocument] = await Promise.all([drawDocumentPromise, iframeLoadPromise]);
51
+ try {
52
+ await drawDocument(iframe.contentDocument, canvas, {
53
+ // Handle retina displays: https://github.com/cburgmer/rasterizeHTML.js/blob/262b3404d1c469ce4a7750a2976dec09b8ae2d6c/examples/retina.html#L71
54
+ zoom: ratio
55
+ });
56
+ } finally {
57
+ doc.body.removeChild(iframe);
58
+ }
59
+ let resolveBlobPromise;
60
+ const blobPromise = new Promise(res => {
61
+ resolveBlobPromise = res;
62
+ });
63
+ canvas.toBlob(blob => resolveBlobPromise(blob), type, quality);
64
+ let blob;
65
+ try {
66
+ blob = await blobPromise;
67
+ } catch (error) {
68
+ throw new Error('MUI X Charts: Failed to create blob from canvas.', {
69
+ cause: error
70
+ });
71
+ }
72
+ if (!blob) {
73
+ throw new Error('MUI X Charts: Failed to create blob from canvas.');
74
+ return;
75
+ }
76
+ const url = URL.createObjectURL(blob);
77
+ triggerDownload(url, fileName || document.title);
78
+ URL.revokeObjectURL(url);
79
+ }
80
+ function triggerDownload(url, name) {
81
+ const a = document.createElement('a');
82
+ a.href = url;
83
+ a.download = name;
84
+ a.click();
85
+ }
@@ -1,8 +1,10 @@
1
1
  import ownerDocument from '@mui/utils/ownerDocument';
2
+ import { loadStyleSheets } from '@mui/x-internals/export';
3
+ import { createExportIframe } from "./common.js";
2
4
  export function printChart(element, {
3
5
  fileName
4
6
  } = {}) {
5
- const printWindow = buildPrintWindow(fileName);
7
+ const printWindow = createExportIframe(fileName);
6
8
  const doc = ownerDocument(element);
7
9
  printWindow.onload = async () => {
8
10
  const printDoc = printWindow.contentDocument;
@@ -10,7 +12,9 @@ export function printChart(element, {
10
12
  const container = document.createElement('div');
11
13
  container.appendChild(elementClone);
12
14
  printDoc.body.innerHTML = container.innerHTML;
13
- await loadStyleSheets(printDoc, element);
15
+ const rootCandidate = element.getRootNode();
16
+ const root = rootCandidate.constructor.name === 'ShadowRoot' ? rootCandidate : doc;
17
+ await Promise.all(loadStyleSheets(printDoc, root));
14
18
  printWindow.contentWindow.print();
15
19
  const mediaQueryList = printWindow.contentWindow.matchMedia('print');
16
20
  mediaQueryList.addEventListener('change', mql => {
@@ -21,52 +25,4 @@ export function printChart(element, {
21
25
  });
22
26
  };
23
27
  doc.body.appendChild(printWindow);
24
- }
25
- function buildPrintWindow(title) {
26
- const iframeEl = document.createElement('iframe');
27
- iframeEl.style.position = 'absolute';
28
- iframeEl.style.width = '0px';
29
- iframeEl.style.height = '0px';
30
- iframeEl.title = title || document.title;
31
- return iframeEl;
32
- }
33
- function loadStyleSheets(printDoc, element) {
34
- const stylesheetLoadPromises = [];
35
- const doc = ownerDocument(element);
36
- const rootCandidate = element.getRootNode();
37
- const root = rootCandidate.constructor.name === 'ShadowRoot' ? rootCandidate : doc;
38
- const headStyleElements = root.querySelectorAll("style, link[rel='stylesheet']");
39
- for (let i = 0; i < headStyleElements.length; i += 1) {
40
- const node = headStyleElements[i];
41
- if (node.tagName === 'STYLE') {
42
- const newHeadStyleElements = printDoc.createElement(node.tagName);
43
- const sheet = node.sheet;
44
- if (sheet) {
45
- let styleCSS = '';
46
- // NOTE: for-of is not supported by IE
47
- for (let j = 0; j < sheet.cssRules.length; j += 1) {
48
- if (typeof sheet.cssRules[j].cssText === 'string') {
49
- styleCSS += `${sheet.cssRules[j].cssText}\r\n`;
50
- }
51
- }
52
- newHeadStyleElements.appendChild(printDoc.createTextNode(styleCSS));
53
- printDoc.head.appendChild(newHeadStyleElements);
54
- }
55
- } else if (node.getAttribute('href')) {
56
- // If `href` tag is empty, avoid loading these links
57
-
58
- const newHeadStyleElements = printDoc.createElement(node.tagName);
59
- for (let j = 0; j < node.attributes.length; j += 1) {
60
- const attr = node.attributes[j];
61
- if (attr) {
62
- newHeadStyleElements.setAttribute(attr.nodeName, attr.nodeValue || '');
63
- }
64
- }
65
- stylesheetLoadPromises.push(new Promise(resolve => {
66
- newHeadStyleElements.addEventListener('load', () => resolve());
67
- }));
68
- printDoc.head.appendChild(newHeadStyleElements);
69
- }
70
- }
71
- return Promise.all(stylesheetLoadPromises);
72
28
  }
@@ -1,5 +1,6 @@
1
1
  import _extends from "@babel/runtime/helpers/esm/extends";
2
2
  import { printChart } from "./print.js";
3
+ import { exportImage } from "./exportImage.js";
3
4
  function waitForAnimationFrame() {
4
5
  let resolve;
5
6
  const promise = new Promise(res => {
@@ -22,6 +23,23 @@ export const useChartProExport = ({
22
23
  // Wait for animation frame to ensure the animation finished
23
24
  await waitForAnimationFrame();
24
25
  printChart(chartRoot, options);
26
+ } catch (error) {
27
+ console.error('MUI X Charts: Error exporting chart as print:', error);
28
+ } finally {
29
+ enableAnimation();
30
+ }
31
+ }
32
+ };
33
+ const exportAsImage = async options => {
34
+ const chartRoot = chartRootRef.current;
35
+ if (chartRoot) {
36
+ const enableAnimation = instance.disableAnimation();
37
+ try {
38
+ // Wait for animation frame to ensure the animation finished
39
+ await waitForAnimationFrame();
40
+ await exportImage(chartRoot, options);
41
+ } catch (error) {
42
+ console.error('MUI X Charts: Error exporting chart as image:', error);
25
43
  } finally {
26
44
  enableAnimation();
27
45
  }
@@ -29,10 +47,12 @@ export const useChartProExport = ({
29
47
  };
30
48
  return {
31
49
  publicAPI: {
32
- exportAsPrint
50
+ exportAsPrint,
51
+ exportAsImage
33
52
  },
34
53
  instance: {
35
- exportAsPrint
54
+ exportAsPrint,
55
+ exportAsImage
36
56
  }
37
57
  };
38
58
  };
@@ -16,6 +16,30 @@ export interface ChartPrintExportOptions {
16
16
  */
17
17
  fileName?: string;
18
18
  }
19
+ /**
20
+ * The options to apply on the image export.
21
+ * @demos
22
+ * - [Image export](/x/react-charts/export/#export-as-image)
23
+ */
24
+ export interface ChartImageExportOptions {
25
+ /**
26
+ * The value to be used as the print window title.
27
+ * @default The title of the page.
28
+ */
29
+ fileName?: string;
30
+ /**
31
+ * The format of the image to be exported.
32
+ * Browsers are required to support 'image/png'. Some browsers also support 'image/jpeg' and 'image/webp'.
33
+ * @default 'image/png'
34
+ */
35
+ type?: 'image/png' | string;
36
+ /**
37
+ * The quality of the image to be exported between 0 and 1. This is only applicable for lossy formats, such as
38
+ * 'image/jpeg' and 'image/webp'. 'image/png' does not support this option.
39
+ * @default 0.9
40
+ */
41
+ quality?: number;
42
+ }
19
43
  export interface UseChartProExportPublicApi {
20
44
  /**
21
45
  * Opens the browser's print dialog, which can be used to print the chart or export it as PDF.
@@ -23,6 +47,14 @@ export interface UseChartProExportPublicApi {
23
47
  * @returns {void}
24
48
  */
25
49
  exportAsPrint: (options?: ChartPrintExportOptions) => void;
50
+ /**
51
+ * Exports the chart as an image.
52
+ * If the provided `type` is not supported by the browser, it will default to `image/png`.
53
+ *
54
+ * @param {ChartPrintExportOptions} options Options to customize the print export.
55
+ * @returns {void}
56
+ */
57
+ exportAsImage: (options?: ChartImageExportOptions) => void;
26
58
  }
27
59
  export interface UseChartProExportInstance extends UseChartProExportPublicApi {}
28
60
  export type UseChartProExportSignature = ChartPluginSignature<{