@mui/x-charts-pro 8.5.1 → 8.5.3

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 (98) hide show
  1. package/BarChartPro/BarChartPro.d.ts +3 -2
  2. package/CHANGELOG.md +187 -10
  3. package/ChartDataProviderPro/ChartDataProviderPro.js +1 -1
  4. package/ChartZoomSlider/internals/ChartAxisZoomSlider.js +1 -1
  5. package/ChartZoomSlider/internals/ChartAxisZoomSliderActiveTrack.js +22 -32
  6. package/ChartZoomSlider/internals/ChartAxisZoomSliderTrack.js +1 -8
  7. package/ChartZoomSlider/internals/zoom-utils.d.ts +3 -1
  8. package/ChartZoomSlider/internals/zoom-utils.js +19 -8
  9. package/ChartsToolbarPro/ChartsToolbarPro.d.ts +2 -1
  10. package/ChartsToolbarPro/Toolbar.types.d.ts +15 -0
  11. package/ChartsToolbarPro/Toolbar.types.js +5 -0
  12. package/FunnelChart/FunnelChart.js +4 -11
  13. package/FunnelChart/FunnelChart.plugins.d.ts +3 -2
  14. package/FunnelChart/FunnelChart.plugins.js +2 -1
  15. package/FunnelChart/FunnelPlot.d.ts +0 -5
  16. package/FunnelChart/FunnelPlot.js +85 -67
  17. package/FunnelChart/FunnelSection.js +2 -0
  18. package/FunnelChart/categoryAxis.types.d.ts +2 -1
  19. package/FunnelChart/curves/bump.d.ts +3 -5
  20. package/FunnelChart/curves/bump.js +13 -13
  21. package/FunnelChart/curves/curve.types.d.ts +14 -1
  22. package/FunnelChart/curves/getFunnelCurve.d.ts +7 -2
  23. package/FunnelChart/curves/linear.d.ts +4 -4
  24. package/FunnelChart/curves/linear.js +42 -34
  25. package/FunnelChart/curves/pyramid.d.ts +3 -3
  26. package/FunnelChart/curves/pyramid.js +25 -24
  27. package/FunnelChart/curves/step-pyramid.d.ts +5 -5
  28. package/FunnelChart/curves/step-pyramid.js +24 -24
  29. package/FunnelChart/curves/step.d.ts +3 -3
  30. package/FunnelChart/curves/step.js +14 -26
  31. package/FunnelChart/funnelAxisPlugin/computeAxisValue.d.ts +25 -0
  32. package/FunnelChart/funnelAxisPlugin/computeAxisValue.js +124 -0
  33. package/FunnelChart/funnelAxisPlugin/useChartFunnelAxis.d.ts +3 -0
  34. package/FunnelChart/funnelAxisPlugin/useChartFunnelAxis.js +173 -0
  35. package/FunnelChart/funnelAxisPlugin/useChartFunnelAxis.types.d.ts +27 -0
  36. package/FunnelChart/funnelAxisPlugin/useChartFunnelAxis.types.js +5 -0
  37. package/FunnelChart/funnelAxisPlugin/useChartFunnelAxisRendering.selectors.d.ts +56 -0
  38. package/FunnelChart/funnelAxisPlugin/useChartFunnelAxisRendering.selectors.js +27 -0
  39. package/FunnelChart/labelUtils.d.ts +4 -11
  40. package/FunnelChart/labelUtils.js +43 -44
  41. package/FunnelChart/useFunnelChartProps.js +1 -3
  42. package/Heatmap/Heatmap.d.ts +3 -3
  43. package/LineChartPro/LineChartPro.d.ts +3 -2
  44. package/PieChartPro/PieChartPro.d.ts +3 -2
  45. package/RadarChartPro/RadarChartPro.d.ts +17 -2
  46. package/ScatterChartPro/ScatterChartPro.d.ts +3 -2
  47. package/esm/BarChartPro/BarChartPro.d.ts +3 -2
  48. package/esm/ChartDataProviderPro/ChartDataProviderPro.js +1 -1
  49. package/esm/ChartZoomSlider/internals/ChartAxisZoomSlider.js +1 -1
  50. package/esm/ChartZoomSlider/internals/ChartAxisZoomSliderActiveTrack.js +23 -33
  51. package/esm/ChartZoomSlider/internals/ChartAxisZoomSliderTrack.js +1 -8
  52. package/esm/ChartZoomSlider/internals/zoom-utils.d.ts +3 -1
  53. package/esm/ChartZoomSlider/internals/zoom-utils.js +19 -9
  54. package/esm/ChartsToolbarPro/ChartsToolbarPro.d.ts +2 -1
  55. package/esm/ChartsToolbarPro/Toolbar.types.d.ts +15 -0
  56. package/esm/ChartsToolbarPro/Toolbar.types.js +1 -0
  57. package/esm/FunnelChart/FunnelChart.js +4 -11
  58. package/esm/FunnelChart/FunnelChart.plugins.d.ts +3 -2
  59. package/esm/FunnelChart/FunnelChart.plugins.js +3 -2
  60. package/esm/FunnelChart/FunnelPlot.d.ts +0 -5
  61. package/esm/FunnelChart/FunnelPlot.js +86 -68
  62. package/esm/FunnelChart/FunnelSection.js +2 -0
  63. package/esm/FunnelChart/categoryAxis.types.d.ts +2 -1
  64. package/esm/FunnelChart/curves/bump.d.ts +3 -5
  65. package/esm/FunnelChart/curves/bump.js +13 -13
  66. package/esm/FunnelChart/curves/curve.types.d.ts +14 -1
  67. package/esm/FunnelChart/curves/getFunnelCurve.d.ts +7 -2
  68. package/esm/FunnelChart/curves/linear.d.ts +4 -4
  69. package/esm/FunnelChart/curves/linear.js +42 -34
  70. package/esm/FunnelChart/curves/pyramid.d.ts +3 -3
  71. package/esm/FunnelChart/curves/pyramid.js +25 -24
  72. package/esm/FunnelChart/curves/step-pyramid.d.ts +5 -5
  73. package/esm/FunnelChart/curves/step-pyramid.js +24 -24
  74. package/esm/FunnelChart/curves/step.d.ts +3 -3
  75. package/esm/FunnelChart/curves/step.js +14 -26
  76. package/esm/FunnelChart/funnelAxisPlugin/computeAxisValue.d.ts +25 -0
  77. package/esm/FunnelChart/funnelAxisPlugin/computeAxisValue.js +114 -0
  78. package/esm/FunnelChart/funnelAxisPlugin/useChartFunnelAxis.d.ts +3 -0
  79. package/esm/FunnelChart/funnelAxisPlugin/useChartFunnelAxis.js +165 -0
  80. package/esm/FunnelChart/funnelAxisPlugin/useChartFunnelAxis.types.d.ts +27 -0
  81. package/esm/FunnelChart/funnelAxisPlugin/useChartFunnelAxis.types.js +1 -0
  82. package/esm/FunnelChart/funnelAxisPlugin/useChartFunnelAxisRendering.selectors.d.ts +56 -0
  83. package/esm/FunnelChart/funnelAxisPlugin/useChartFunnelAxisRendering.selectors.js +20 -0
  84. package/esm/FunnelChart/labelUtils.d.ts +4 -11
  85. package/esm/FunnelChart/labelUtils.js +43 -44
  86. package/esm/FunnelChart/useFunnelChartProps.js +1 -3
  87. package/esm/Heatmap/Heatmap.d.ts +3 -3
  88. package/esm/LineChartPro/LineChartPro.d.ts +3 -2
  89. package/esm/PieChartPro/PieChartPro.d.ts +3 -2
  90. package/esm/RadarChartPro/RadarChartPro.d.ts +17 -2
  91. package/esm/ScatterChartPro/ScatterChartPro.d.ts +3 -2
  92. package/esm/index.js +1 -1
  93. package/esm/internals/plugins/useChartProZoom/useChartProZoom.selectors.d.ts +16 -595
  94. package/esm/internals/plugins/useChartProZoom/useChartProZoom.selectors.js +2 -2
  95. package/index.js +1 -1
  96. package/internals/plugins/useChartProZoom/useChartProZoom.selectors.d.ts +16 -595
  97. package/internals/plugins/useChartProZoom/useChartProZoom.selectors.js +2 -2
  98. package/package.json +6 -6
@@ -13,25 +13,27 @@ var React = _react;
13
13
  var _propTypes = _interopRequireDefault(require("prop-types"));
14
14
  var _d3Shape = require("@mui/x-charts-vendor/d3-shape");
15
15
  var _internals = require("@mui/x-charts/internals");
16
- var _hooks = require("@mui/x-charts/hooks");
17
16
  var _FunnelSection = require("./FunnelSection");
18
17
  var _labelUtils = require("./labelUtils");
19
18
  var _useFunnelSeries = require("../hooks/useFunnelSeries");
20
19
  var _curves = require("./curves");
21
20
  var _FunnelSectionLabel = require("./FunnelSectionLabel");
21
+ var _useChartFunnelAxisRendering = require("./funnelAxisPlugin/useChartFunnelAxisRendering.selectors");
22
22
  var _jsxRuntime = require("react/jsx-runtime");
23
- const _excluded = ["onItemClick", "gap"];
23
+ const _excluded = ["onItemClick"];
24
24
  _internals.cartesianSeriesTypes.addType('funnel');
25
- const useAggregatedData = gap => {
25
+ const useAggregatedData = () => {
26
26
  const seriesData = (0, _useFunnelSeries.useFunnelSeriesContext)();
27
+ const store = (0, _internals.useStore)();
27
28
  const {
28
- xAxis,
29
- xAxisIds
30
- } = (0, _hooks.useXAxes)();
29
+ axis: xAxis,
30
+ axisIds: xAxisIds
31
+ } = (0, _internals.useSelector)(store, _useChartFunnelAxisRendering.selectorChartXAxis);
31
32
  const {
32
- yAxis,
33
- yAxisIds
34
- } = (0, _hooks.useYAxes)();
33
+ axis: yAxis,
34
+ axisIds: yAxisIds
35
+ } = (0, _internals.useSelector)(store, _useChartFunnelAxisRendering.selectorChartYAxis);
36
+ const gap = (0, _internals.useSelector)(store, _useChartFunnelAxisRendering.selectorFunnelGap);
35
37
  const allData = React.useMemo(() => {
36
38
  if (seriesData === undefined) {
37
39
  return [];
@@ -54,22 +56,28 @@ const useAggregatedData = gap => {
54
56
  const bandWidth = (isXAxisBand || isYAxisBand) && baseScaleConfig.scale?.bandwidth() || 0;
55
57
  const xScale = xAxis[xAxisId].scale;
56
58
  const yScale = yAxis[yAxisId].scale;
57
- const xPosition = (value, bandIndex, stackOffset, useBand) => {
58
- if (isXAxisBand) {
59
- const position = xScale(bandIndex);
59
+ const xPosition = (value, bandIndex, bandIdentifier, stackOffset, useBand) => {
60
+ if ((0, _internals.isBandScale)(xScale)) {
61
+ const position = xScale(bandIdentifier);
60
62
  return useBand ? position + bandWidth : position;
61
63
  }
62
- return xScale(isHorizontal ? value + (stackOffset || 0) : value);
64
+ if (isHorizontal) {
65
+ return xScale(value + (stackOffset || 0)) + bandIndex * gap;
66
+ }
67
+ return xScale(value);
63
68
  };
64
- const yPosition = (value, bandIndex, stackOffset, useBand) => {
65
- if (isYAxisBand) {
66
- const position = yScale(bandIndex);
69
+ const yPosition = (value, bandIndex, bandIdentifier, stackOffset, useBand) => {
70
+ if ((0, _internals.isBandScale)(yScale)) {
71
+ const position = yScale(bandIdentifier);
67
72
  return useBand ? position + bandWidth : position;
68
73
  }
69
- return yScale(isHorizontal ? value : value + (stackOffset || 0));
74
+ if (isHorizontal) {
75
+ return yScale(value);
76
+ }
77
+ return yScale(value + (stackOffset || 0)) + bandIndex * gap;
70
78
  };
71
- const allY = currentSeries.dataPoints.flatMap((d, dataIndex) => d.flatMap(v => yPosition(v.y, baseScaleConfig.data?.[dataIndex], v.stackOffset, v.useBandWidth)));
72
- const allX = currentSeries.dataPoints.flatMap((d, dataIndex) => d.flatMap(v => xPosition(v.x, baseScaleConfig.data?.[dataIndex], v.stackOffset, v.useBandWidth)));
79
+ const allY = currentSeries.dataPoints.flatMap((d, dataIndex) => d.flatMap(v => yPosition(v.y, dataIndex, baseScaleConfig.data?.[dataIndex], v.stackOffset, v.useBandWidth)));
80
+ const allX = currentSeries.dataPoints.flatMap((d, dataIndex) => d.flatMap(v => xPosition(v.x, dataIndex, baseScaleConfig.data?.[dataIndex], v.stackOffset, v.useBandWidth)));
73
81
  const minPoint = {
74
82
  x: Math.min(...allX),
75
83
  y: Math.min(...allY)
@@ -97,21 +105,21 @@ const useAggregatedData = gap => {
97
105
  min: minPoint,
98
106
  max: maxPoint
99
107
  });
100
- const line = (0, _d3Shape.line)().x(d => xPosition(d.x, baseScaleConfig.data?.[dataIndex], d.stackOffset, d.useBandWidth)).y(d => yPosition(d.y, baseScaleConfig.data?.[dataIndex], d.stackOffset, d.useBandWidth)).curve(curve);
108
+ const bandPoints = curve({}).processPoints(values.map(v => ({
109
+ x: xPosition(v.x, dataIndex, baseScaleConfig.data?.[dataIndex], v.stackOffset, v.useBandWidth),
110
+ y: yPosition(v.y, dataIndex, baseScaleConfig.data?.[dataIndex], v.stackOffset, v.useBandWidth)
111
+ })));
112
+ const line = (0, _d3Shape.line)().x(v => v.x).y(v => v.y).curve(curve);
101
113
  return {
102
- d: line(values),
114
+ d: line(bandPoints),
103
115
  color,
104
116
  id,
105
117
  seriesId,
106
118
  dataIndex,
107
119
  variant: currentSeries.variant,
108
120
  label: sectionLabel !== false && (0, _extends2.default)({}, (0, _labelUtils.positionLabel)((0, _extends2.default)({}, sectionLabel, {
109
- xPosition,
110
- yPosition,
111
121
  isHorizontal,
112
- values,
113
- dataIndex,
114
- baseScaleData: baseScaleConfig.data ?? []
122
+ values: bandPoints
115
123
  })), (0, _labelUtils.alignLabel)(sectionLabel ?? {}), {
116
124
  value: valueFormatter ? valueFormatter(currentSeries.data[dataIndex], {
117
125
  dataIndex
@@ -120,53 +128,68 @@ const useAggregatedData = gap => {
120
128
  };
121
129
  });
122
130
  });
123
- return result.flat();
131
+ return result;
124
132
  }, [seriesData, xAxis, xAxisIds, yAxis, yAxisIds, gap]);
125
133
  return allData;
126
134
  };
127
135
  function FunnelPlot(props) {
128
136
  const {
129
- onItemClick,
130
- gap
137
+ onItemClick
131
138
  } = props,
132
139
  other = (0, _objectWithoutPropertiesLoose2.default)(props, _excluded);
133
- const data = useAggregatedData(gap);
140
+ const data = useAggregatedData();
134
141
  return /*#__PURE__*/(0, _jsxRuntime.jsxs)(React.Fragment, {
135
- children: [data.map(({
136
- d,
137
- color,
138
- id,
139
- seriesId,
140
- dataIndex,
141
- variant
142
- }) => /*#__PURE__*/(0, _react.createElement)(_FunnelSection.FunnelSection, (0, _extends2.default)({}, other, {
143
- d: d,
144
- color: color,
145
- key: id,
146
- dataIndex: dataIndex,
147
- seriesId: seriesId,
148
- variant: variant,
149
- onClick: onItemClick && (event => {
150
- onItemClick(event, {
151
- type: 'funnel',
142
+ children: [data.map(series => {
143
+ if (series.length === 0) {
144
+ return null;
145
+ }
146
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)("g", {
147
+ "data-series": series[0].seriesId,
148
+ children: series.map(({
149
+ d,
150
+ color,
151
+ id,
152
152
  seriesId,
153
- dataIndex
154
- });
155
- })
156
- }))), data.map(({
157
- id,
158
- label,
159
- seriesId,
160
- dataIndex
161
- }) => {
162
- if (!label || !label.value) {
153
+ dataIndex,
154
+ variant
155
+ }) => /*#__PURE__*/(0, _react.createElement)(_FunnelSection.FunnelSection, (0, _extends2.default)({}, other, {
156
+ d: d,
157
+ color: color,
158
+ key: id,
159
+ dataIndex: dataIndex,
160
+ seriesId: seriesId,
161
+ variant: variant,
162
+ onClick: onItemClick && (event => {
163
+ onItemClick(event, {
164
+ type: 'funnel',
165
+ seriesId,
166
+ dataIndex
167
+ });
168
+ })
169
+ })))
170
+ }, series[0].seriesId);
171
+ }), data.map(series => {
172
+ if (series.length === 0) {
163
173
  return null;
164
174
  }
165
- return /*#__PURE__*/(0, _jsxRuntime.jsx)(_FunnelSectionLabel.FunnelSectionLabel, (0, _extends2.default)({
166
- label: label,
167
- dataIndex: dataIndex,
168
- seriesId: seriesId
169
- }, other), id);
175
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)("g", {
176
+ "data-series": series[0].seriesId,
177
+ children: series.map(({
178
+ id,
179
+ label,
180
+ seriesId,
181
+ dataIndex
182
+ }) => {
183
+ if (!label || !label.value) {
184
+ return null;
185
+ }
186
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_FunnelSectionLabel.FunnelSectionLabel, (0, _extends2.default)({
187
+ label: label,
188
+ dataIndex: dataIndex,
189
+ seriesId: seriesId
190
+ }, other), id);
191
+ })
192
+ }, series[0].seriesId);
170
193
  })]
171
194
  });
172
195
  }
@@ -175,11 +198,6 @@ process.env.NODE_ENV !== "production" ? FunnelPlot.propTypes = {
175
198
  // | These PropTypes are generated from the TypeScript type definitions |
176
199
  // | To update them edit the TypeScript types and run "pnpm proptypes" |
177
200
  // ----------------------------------------------------------------------
178
- /**
179
- * The gap, in pixels, between funnel sections.
180
- * @default 0
181
- */
182
- gap: _propTypes.default.number,
183
201
  /**
184
202
  * Callback fired when a funnel item is clicked.
185
203
  * @param {React.MouseEvent<SVGElement, MouseEvent>} event The event source of the callback.
@@ -60,6 +60,8 @@ const FunnelSection = exports.FunnelSection = (0, _internals.consumeSlots)('MuiF
60
60
  strokeWidth: isOutlined ? 1.5 : 0,
61
61
  cursor: onClick ? 'pointer' : 'unset',
62
62
  onClick: onClick,
63
+ "data-highlighted": isHighlighted || undefined,
64
+ "data-faded": isFaded || undefined,
63
65
  className: (0, _clsx.default)(classes?.root, className, isOutlined ? classes?.outlined : classes?.filled, isHighlighted && classes?.highlighted, isFaded && classes?.faded)
64
66
  }, other, {
65
67
  ref: ref
@@ -1,6 +1,7 @@
1
1
  import { AxisConfig, ScaleName } from '@mui/x-charts/models';
2
2
  import { MakeOptional } from '@mui/x-internals/types';
3
- export type CategoryAxis<S extends ScaleName = ScaleName> = S extends ScaleName ? {
3
+ export type FunnelScaleName = Exclude<ScaleName, 'point'>;
4
+ export type CategoryAxis<S extends FunnelScaleName = FunnelScaleName> = S extends FunnelScaleName ? {
4
5
  /**
5
6
  * The categories to be displayed on the axis.
6
7
  * The order of the categories is the order in which they are displayed.
@@ -1,5 +1,4 @@
1
- import { CurveGenerator } from '@mui/x-charts-vendor/d3-shape';
2
- import { CurveOptions } from "./curve.types.js";
1
+ import { FunnelCurveGenerator, CurveOptions, Point } from "./curve.types.js";
3
2
  /**
4
3
  * This is a custom "bump" curve generator.
5
4
  * It draws smooth curves for the 4 provided points,
@@ -8,16 +7,14 @@ import { CurveOptions } from "./curve.types.js";
8
7
  * The implementation is based on the d3-shape bump curve generator.
9
8
  * https://github.com/d3/d3-shape/blob/a82254af78f08799c71d7ab25df557c4872a3c51/src/curve/bump.js
10
9
  */
11
- export declare class Bump implements CurveGenerator {
10
+ export declare class Bump implements FunnelCurveGenerator {
12
11
  private context;
13
12
  private isHorizontal;
14
- private gap;
15
13
  private min;
16
14
  private max;
17
15
  private points;
18
16
  constructor(context: CanvasRenderingContext2D, {
19
17
  isHorizontal,
20
- gap,
21
18
  min,
22
19
  max,
23
20
  isIncreasing
@@ -26,6 +23,7 @@ export declare class Bump implements CurveGenerator {
26
23
  areaEnd(): void;
27
24
  lineStart(): void;
28
25
  lineEnd(): void;
26
+ processPoints(points: Point[]): Point[];
29
27
  point(x: number, y: number): void;
30
28
  private drawPath;
31
29
  private drawHorizontalPath;
@@ -17,14 +17,12 @@ exports.Bump = void 0;
17
17
  class Bump {
18
18
  constructor(context, {
19
19
  isHorizontal,
20
- gap,
21
20
  min,
22
21
  max,
23
22
  isIncreasing
24
23
  }) {
25
24
  this.context = void 0;
26
25
  this.isHorizontal = false;
27
- this.gap = 0;
28
26
  this.min = {
29
27
  x: 0,
30
28
  y: 0
@@ -36,7 +34,6 @@ class Bump {
36
34
  this.points = [];
37
35
  this.context = context;
38
36
  this.isHorizontal = isHorizontal ?? false;
39
- this.gap = (gap ?? 0) / 2;
40
37
  this.min = min ?? {
41
38
  x: 0,
42
39
  y: 0
@@ -56,6 +53,9 @@ class Bump {
56
53
  areaEnd() {}
57
54
  lineStart() {}
58
55
  lineEnd() {}
56
+ processPoints(points) {
57
+ return points;
58
+ }
59
59
  point(x, y) {
60
60
  this.points.push({
61
61
  x,
@@ -79,34 +79,34 @@ class Bump {
79
79
  const [p0, p1, p2, p3] = this.points;
80
80
 
81
81
  // 0 is the top-left corner
82
- this.context.moveTo(p0.x + this.gap, p0.y);
83
- this.context.lineTo(p0.x + this.gap, p0.y);
82
+ this.context.moveTo(p0.x, p0.y);
83
+ this.context.lineTo(p0.x, p0.y);
84
84
 
85
85
  // Bezier curve to point 1
86
- this.context.bezierCurveTo((p0.x + p1.x) / 2, p0.y, (p0.x + p1.x) / 2, p1.y, p1.x - this.gap, p1.y);
86
+ this.context.bezierCurveTo((p0.x + p1.x) / 2, p0.y, (p0.x + p1.x) / 2, p1.y, p1.x, p1.y);
87
87
 
88
88
  // Line to point 2
89
- this.context.lineTo(p2.x - this.gap, p2.y);
89
+ this.context.lineTo(p2.x, p2.y);
90
90
 
91
91
  // Bezier curve back to point 3
92
- this.context.bezierCurveTo((p2.x + p3.x) / 2, p2.y, (p2.x + p3.x) / 2, p3.y, p3.x + this.gap, p3.y);
92
+ this.context.bezierCurveTo((p2.x + p3.x) / 2, p2.y, (p2.x + p3.x) / 2, p3.y, p3.x, p3.y);
93
93
  this.context.closePath();
94
94
  }
95
95
  drawVerticalPath() {
96
96
  const [p0, p1, p2, p3] = this.points;
97
97
 
98
98
  // 0 is the top-right corner
99
- this.context.moveTo(p0.x, p0.y + this.gap);
100
- this.context.lineTo(p0.x, p0.y + this.gap);
99
+ this.context.moveTo(p0.x, p0.y);
100
+ this.context.lineTo(p0.x, p0.y);
101
101
 
102
102
  // Bezier curve to point 1
103
- this.context.bezierCurveTo(p0.x, (p0.y + p1.y) / 2, p1.x, (p0.y + p1.y) / 2, p1.x, p1.y - this.gap);
103
+ this.context.bezierCurveTo(p0.x, (p0.y + p1.y) / 2, p1.x, (p0.y + p1.y) / 2, p1.x, p1.y);
104
104
 
105
105
  // Line to point 2
106
- this.context.lineTo(p2.x, p2.y - this.gap);
106
+ this.context.lineTo(p2.x, p2.y);
107
107
 
108
108
  // Bezier curve back to point 3
109
- this.context.bezierCurveTo(p2.x, (p2.y + p3.y) / 2, p3.x, (p2.y + p3.y) / 2, p3.x, p3.y + this.gap);
109
+ this.context.bezierCurveTo(p2.x, (p2.y + p3.y) / 2, p3.x, (p2.y + p3.y) / 2, p3.x, p3.y);
110
110
  this.context.closePath();
111
111
  }
112
112
  }
@@ -1,3 +1,4 @@
1
+ import { CurveGenerator } from '@mui/x-charts-vendor/d3-shape';
1
2
  export type CurveOptions = {
2
3
  /**
3
4
  * The gap between each segment.
@@ -44,4 +45,16 @@ export type FunnelPointShape = 'square' | 'sharp';
44
45
  export type Point = {
45
46
  x: number;
46
47
  y: number;
47
- };
48
+ };
49
+ export interface FunnelCurveGenerator extends CurveGenerator {
50
+ /**
51
+ * Processes the points to create a curve based on the provided options.
52
+ * This does not draw the curve but prepares the points for rendering.
53
+ *
54
+ * @param points The points to process.
55
+ * @param options The options for the curve.
56
+ * @returns The processed points.
57
+ */
58
+ processPoints(points: Point[], xPosition: PositionGetter, yPosition: PositionGetter): Point[];
59
+ }
60
+ export type PositionGetter = (value: number, bandIndex: number, bandIdentifier: string | number, stackOffset?: number, useBand?: boolean) => number;
@@ -1,3 +1,8 @@
1
- import { CurveFactory } from '@mui/x-charts-vendor/d3-shape';
1
+ import type { Path } from '@mui/x-charts-vendor/d3-path';
2
2
  import { CurveOptions, FunnelCurveType } from "./curve.types.js";
3
- export declare const getFunnelCurve: (curve: FunnelCurveType | undefined, options: CurveOptions) => CurveFactory;
3
+ import { Step } from "./step.js";
4
+ import { Linear } from "./linear.js";
5
+ import { Bump } from "./bump.js";
6
+ import { Pyramid } from "./pyramid.js";
7
+ import { StepPyramid } from "./step-pyramid.js";
8
+ export declare const getFunnelCurve: (curve: FunnelCurveType | undefined, options: CurveOptions) => (context: CanvasRenderingContext2D | Path) => Step | Linear | Bump | Pyramid | StepPyramid;
@@ -1,14 +1,13 @@
1
- import { CurveGenerator } from '@mui/x-charts-vendor/d3-shape';
2
- import { CurveOptions } from "./curve.types.js";
1
+ import { FunnelCurveGenerator, CurveOptions, Point } from "./curve.types.js";
3
2
  /**
4
3
  * This is a custom "linear" curve generator.
5
4
  * It draws straight lines for the 4 provided points,
6
- * with the option to add a gap between sections while also properly handling the border radius.
5
+ * with the option to properly handling the border radius.
7
6
  *
8
7
  * The implementation is based on the d3-shape linear curve generator.
9
8
  * https://github.com/d3/d3-shape/blob/a82254af78f08799c71d7ab25df557c4872a3c51/src/curve/linear.js
10
9
  */
11
- export declare class Linear implements CurveGenerator {
10
+ export declare class Linear implements FunnelCurveGenerator {
12
11
  private context;
13
12
  private position;
14
13
  private sections;
@@ -36,5 +35,6 @@ export declare class Linear implements CurveGenerator {
36
35
  lineStart(): void;
37
36
  lineEnd(): void;
38
37
  protected getBorderRadius(): number | number[];
38
+ processPoints(points: Point[]): Point[];
39
39
  point(xIn: number, yIn: number): void;
40
40
  }
@@ -11,7 +11,7 @@ var _utils = require("./utils");
11
11
  /**
12
12
  * This is a custom "linear" curve generator.
13
13
  * It draws straight lines for the 4 provided points,
14
- * with the option to add a gap between sections while also properly handling the border radius.
14
+ * with the option to properly handling the border radius.
15
15
  *
16
16
  * The implementation is based on the d3-shape linear curve generator.
17
17
  * https://github.com/d3/d3-shape/blob/a82254af78f08799c71d7ab25df557c4872a3c51/src/curve/linear.js
@@ -47,7 +47,7 @@ class Linear {
47
47
  this.pointShape = 'square';
48
48
  this.context = context;
49
49
  this.isHorizontal = isHorizontal ?? false;
50
- this.gap = (gap ?? 0) / 2;
50
+ this.gap = gap ?? 0;
51
51
  this.position = position ?? 0;
52
52
  this.sections = sections ?? 1;
53
53
  this.borderRadius = borderRadius ?? 0;
@@ -97,7 +97,7 @@ class Linear {
97
97
  }
98
98
  // Is smallest section and shaped like a triangle
99
99
  if (this.position === this.sections - 1 && this.pointShape === 'sharp') {
100
- return [this.borderRadius];
100
+ return [0, 0, this.borderRadius];
101
101
  }
102
102
 
103
103
  // Is smallest section
@@ -107,32 +107,22 @@ class Linear {
107
107
  }
108
108
  return 0;
109
109
  }
110
- point(xIn, yIn) {
111
- this.points.push({
112
- x: xIn,
113
- y: yIn
114
- });
115
- if (this.points.length < 4) {
116
- return;
117
- }
118
-
110
+ processPoints(points) {
119
111
  // Add gaps where they are needed.
120
- this.points = this.points.map((point, index) => {
121
- const slopeStart = this.points.at(index <= 1 ? 0 : 2);
122
- const slopeEnd = this.points.at(index <= 1 ? 1 : 3);
112
+ const processedPoints = points.map((point, index) => {
113
+ const slopeStart = points.at(index <= 1 ? 0 : 3);
114
+ const slopeEnd = points.at(index <= 1 ? 1 : 2);
123
115
  if (this.isHorizontal) {
124
- const yGetter = (0, _utils.lerpY)(slopeStart.x - this.gap, slopeStart.y, slopeEnd.x - this.gap, slopeEnd.y);
125
- const xGap = point.x + (index === 0 || index === 3 ? this.gap : -this.gap);
116
+ const yGetter = (0, _utils.lerpY)(slopeStart.x - this.gap, slopeStart.y, slopeEnd.x, slopeEnd.y);
126
117
  return {
127
- x: xGap,
128
- y: yGetter(xGap)
118
+ x: point.x,
119
+ y: yGetter(point.x)
129
120
  };
130
121
  }
131
- const xGetter = (0, _utils.lerpX)(slopeStart.x, slopeStart.y - this.gap, slopeEnd.x, slopeEnd.y - this.gap);
132
- const yGap = point.y + (index === 0 || index === 3 ? this.gap : -this.gap);
122
+ const xGetter = (0, _utils.lerpX)(slopeStart.x, slopeStart.y - this.gap, slopeEnd.x, slopeEnd.y);
133
123
  return {
134
- x: xGetter(yGap),
135
- y: yGap
124
+ x: xGetter(point.y),
125
+ y: point.y
136
126
  };
137
127
  });
138
128
  if (this.pointShape === 'sharp') {
@@ -140,25 +130,43 @@ class Linear {
140
130
  // Else the algorithm will break.
141
131
  const isLastSection = this.position === this.sections - 1;
142
132
  const isFirstSection = this.position === 0;
133
+ let firstPoint = null;
134
+ let secondPoint = null;
143
135
  if (isFirstSection && this.isIncreasing) {
144
- this.points = [this.isHorizontal ? {
145
- x: this.max.x + this.gap,
146
- y: (this.max.y + this.min.y) / 2
147
- } : {
148
- x: (this.max.x + this.min.x) / 2,
149
- y: this.max.y + this.gap
150
- }, this.points[1], this.points[2]];
136
+ firstPoint = processedPoints[1];
137
+ secondPoint = processedPoints[2];
151
138
  }
152
139
  if (isLastSection && !this.isIncreasing) {
153
- this.points = [this.points[0], this.isHorizontal ? {
154
- x: this.max.x - this.gap,
140
+ firstPoint = processedPoints[3];
141
+ secondPoint = processedPoints[0];
142
+ }
143
+ if (firstPoint && secondPoint) {
144
+ return [
145
+ // Sharp point at the start
146
+ this.isHorizontal ? {
147
+ x: this.max.x,
155
148
  y: (this.max.y + this.min.y) / 2
156
149
  } : {
157
150
  x: (this.max.x + this.min.x) / 2,
158
- y: this.max.y - this.gap
159
- }, this.points[3]];
151
+ y: this.max.y
152
+ },
153
+ // Then other points
154
+ firstPoint, secondPoint];
160
155
  }
161
156
  }
157
+ return processedPoints;
158
+ }
159
+ point(xIn, yIn) {
160
+ this.points.push({
161
+ x: xIn,
162
+ y: yIn
163
+ });
164
+ const isLastSection = this.position === this.sections - 1;
165
+ const isFirstSection = this.position === 0;
166
+ const isSharpPoint = this.pointShape === 'sharp' && (isLastSection && !this.isIncreasing || isFirstSection && this.isIncreasing);
167
+ if (this.points.length < (isSharpPoint ? 3 : 4)) {
168
+ return;
169
+ }
162
170
  (0, _borderRadiusPolygon.borderRadiusPolygon)(this.context, this.points, this.getBorderRadius());
163
171
  }
164
172
  }
@@ -1,12 +1,11 @@
1
- import { CurveGenerator } from '@mui/x-charts-vendor/d3-shape';
2
- import { CurveOptions } from "./curve.types.js";
1
+ import { FunnelCurveGenerator, CurveOptions, Point } from "./curve.types.js";
3
2
  /**
4
3
  * This is a custom "pyramid" curve generator.
5
4
  * It draws straight lines for the 4 provided points. The slopes are calculated
6
5
  * based on the min and max values of the x and y axes.
7
6
  * with the option to add a gap between sections while also properly handling the border radius.
8
7
  */
9
- export declare class Pyramid implements CurveGenerator {
8
+ export declare class Pyramid implements FunnelCurveGenerator {
10
9
  private context;
11
10
  private position;
12
11
  private sections;
@@ -32,5 +31,6 @@ export declare class Pyramid implements CurveGenerator {
32
31
  lineStart(): void;
33
32
  lineEnd(): void;
34
33
  protected getBorderRadius(): number | number[];
34
+ processPoints(points: Point[]): Point[];
35
35
  point(xIn: number, yIn: number): void;
36
36
  }
@@ -93,17 +93,9 @@ class Pyramid {
93
93
  }
94
94
  return 0;
95
95
  }
96
- point(xIn, yIn) {
97
- this.points.push({
98
- x: xIn,
99
- y: yIn
100
- });
101
- if (this.points.length < 4) {
102
- return;
103
- }
104
-
105
- // Add gaps where they are needed.
106
- this.points = this.points.map((point, index) => {
96
+ processPoints(points) {
97
+ // Replace funnel points by pyramids ones.
98
+ const processedPoints = points.map((point, index) => {
107
99
  if (this.isHorizontal) {
108
100
  const slopeEnd = {
109
101
  x: this.max.x,
@@ -114,10 +106,9 @@ class Pyramid {
114
106
  y: this.max.y
115
107
  };
116
108
  const yGetter = (0, _utils.lerpY)(slopeStart.x, slopeStart.y, slopeEnd.x, slopeEnd.y);
117
- const xGap = point.x + (index === 0 || index === 3 ? this.gap : -this.gap);
118
109
  return {
119
- x: xGap,
120
- y: yGetter(xGap)
110
+ x: point.x,
111
+ y: yGetter(point.x)
121
112
  };
122
113
  }
123
114
  const slopeEnd = {
@@ -129,10 +120,9 @@ class Pyramid {
129
120
  y: this.min.y
130
121
  } : this.min;
131
122
  const xGetter = (0, _utils.lerpX)(slopeStart.x, slopeStart.y, slopeEnd.x, slopeEnd.y);
132
- const yGap = point.y + (index === 0 || index === 3 ? this.gap : -this.gap);
133
123
  return {
134
- x: xGetter(yGap),
135
- y: yGap
124
+ x: xGetter(point.y),
125
+ y: point.y
136
126
  };
137
127
  });
138
128
 
@@ -140,13 +130,24 @@ class Pyramid {
140
130
  // Else the algorithm will break.
141
131
  const isLastSection = this.position === this.sections - 1;
142
132
  const isFirstSection = this.position === 0;
143
- if (this.gap <= 0) {
144
- if (isFirstSection && this.isIncreasing) {
145
- this.points = [this.points[0], this.points[1], this.points[2]];
146
- }
147
- if (isLastSection && !this.isIncreasing) {
148
- this.points = [this.points[0], this.points[1], this.points[3]];
149
- }
133
+ if (isFirstSection && this.isIncreasing) {
134
+ return [processedPoints[0], processedPoints[1], processedPoints[2]];
135
+ }
136
+ if (isLastSection && !this.isIncreasing) {
137
+ return [processedPoints[0], processedPoints[1], processedPoints[3]];
138
+ }
139
+ return processedPoints;
140
+ }
141
+ point(xIn, yIn) {
142
+ this.points.push({
143
+ x: xIn,
144
+ y: yIn
145
+ });
146
+ const isLastSection = this.position === this.sections - 1;
147
+ const isFirstSection = this.position === 0;
148
+ const isSharpPoint = isLastSection && !this.isIncreasing || isFirstSection && this.isIncreasing;
149
+ if (this.points.length < (isSharpPoint ? 3 : 4)) {
150
+ return;
150
151
  }
151
152
  (0, _borderRadiusPolygon.borderRadiusPolygon)(this.context, this.points, this.getBorderRadius());
152
153
  }
@@ -1,11 +1,10 @@
1
- import { CurveGenerator } from '@mui/x-charts-vendor/d3-shape';
2
- import { CurveOptions, Point } from "./curve.types.js";
1
+ import { FunnelCurveGenerator, CurveOptions, Point } from "./curve.types.js";
3
2
  /**
4
3
  * This is a custom "step-pyramid" curve generator.
5
4
  * It creates a step pyramid, which is a step-like shape with static lengths.
6
5
  * It has the option to add a gap between sections while also properly handling the border radius.
7
6
  */
8
- export declare class StepPyramid implements CurveGenerator {
7
+ export declare class StepPyramid implements FunnelCurveGenerator {
9
8
  private context;
10
9
  private position;
11
10
  private sections;
@@ -33,7 +32,8 @@ export declare class StepPyramid implements CurveGenerator {
33
32
  protected getBorderRadius(): number | number[];
34
33
  slopeStart(index: number): Point;
35
34
  slopeEnd(index: number): Point;
36
- initialX(index: number): number;
37
- initialY(index: number): number;
35
+ initialX(index: number, points: Point[]): number;
36
+ initialY(index: number, points: Point[]): number;
37
+ processPoints(points: Point[]): Point[];
38
38
  point(xIn: number, yIn: number): void;
39
39
  }