@mui/x-charts-pro 8.5.0 → 8.5.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 (144) hide show
  1. package/BarChartPro/BarChartPro.d.ts +3 -2
  2. package/BarChartPro/BarChartPro.js +1 -1
  3. package/CHANGELOG.md +204 -0
  4. package/ChartContainerPro/ChartProApi.d.ts +2 -0
  5. package/ChartDataProviderPro/ChartDataProviderPro.js +1 -1
  6. package/ChartZoomSlider/internals/ChartAxisZoomSliderActiveTrack.d.ts +0 -2
  7. package/ChartZoomSlider/internals/ChartAxisZoomSliderActiveTrack.js +42 -9
  8. package/ChartZoomSlider/internals/ChartAxisZoomSliderTrack.js +0 -22
  9. package/ChartsToolbarPro/ChartsToolbarPro.d.ts +2 -4
  10. package/ChartsToolbarPro/ChartsToolbarPro.js +7 -13
  11. package/ChartsToolbarPro/{ChartsToolbarZoomInButton.d.ts → ChartsToolbarZoomInTrigger.d.ts} +6 -5
  12. package/ChartsToolbarPro/{ChartsToolbarZoomInButton.js → ChartsToolbarZoomInTrigger.js} +7 -6
  13. package/ChartsToolbarPro/{ChartsToolbarZoomOutButton.d.ts → ChartsToolbarZoomOutTrigger.d.ts} +6 -5
  14. package/ChartsToolbarPro/{ChartsToolbarZoomOutButton.js → ChartsToolbarZoomOutTrigger.js} +7 -6
  15. package/ChartsToolbarPro/Toolbar.types.d.ts +15 -0
  16. package/ChartsToolbarPro/Toolbar.types.js +5 -0
  17. package/ChartsToolbarPro/index.d.ts +2 -2
  18. package/ChartsToolbarPro/index.js +8 -8
  19. package/FunnelChart/FunnelChart.js +4 -11
  20. package/FunnelChart/FunnelChart.plugins.d.ts +3 -2
  21. package/FunnelChart/FunnelChart.plugins.js +2 -1
  22. package/FunnelChart/FunnelPlot.d.ts +0 -5
  23. package/FunnelChart/FunnelPlot.js +40 -50
  24. package/FunnelChart/FunnelSection.d.ts +1 -1
  25. package/FunnelChart/FunnelSectionLabel.d.ts +21 -0
  26. package/FunnelChart/FunnelSectionLabel.js +52 -0
  27. package/FunnelChart/categoryAxis.types.d.ts +2 -1
  28. package/FunnelChart/curves/bump.d.ts +9 -5
  29. package/FunnelChart/curves/bump.js +72 -43
  30. package/FunnelChart/curves/curve.types.d.ts +6 -1
  31. package/FunnelChart/curves/getFunnelCurve.js +3 -0
  32. package/FunnelChart/curves/linear.d.ts +10 -2
  33. package/FunnelChart/curves/linear.js +97 -17
  34. package/FunnelChart/curves/pyramid.js +10 -14
  35. package/FunnelChart/curves/step-pyramid.js +4 -6
  36. package/FunnelChart/curves/step.js +1 -15
  37. package/FunnelChart/funnel.types.d.ts +1 -0
  38. package/FunnelChart/funnelAxisPlugin/computeAxisValue.d.ts +25 -0
  39. package/FunnelChart/funnelAxisPlugin/computeAxisValue.js +124 -0
  40. package/FunnelChart/funnelAxisPlugin/useChartFunnelAxis.d.ts +3 -0
  41. package/FunnelChart/funnelAxisPlugin/useChartFunnelAxis.js +173 -0
  42. package/FunnelChart/funnelAxisPlugin/useChartFunnelAxis.types.d.ts +27 -0
  43. package/FunnelChart/funnelAxisPlugin/useChartFunnelAxis.types.js +5 -0
  44. package/FunnelChart/funnelAxisPlugin/useChartFunnelAxisRendering.selectors.d.ts +1482 -0
  45. package/FunnelChart/funnelAxisPlugin/useChartFunnelAxisRendering.selectors.js +27 -0
  46. package/FunnelChart/funnelPlotSlots.types.d.ts +11 -0
  47. package/FunnelChart/funnelSectionClasses.d.ts +2 -0
  48. package/FunnelChart/funnelSectionClasses.js +17 -4
  49. package/FunnelChart/index.d.ts +3 -1
  50. package/FunnelChart/index.js +18 -2
  51. package/FunnelChart/labelUtils.d.ts +3 -2
  52. package/FunnelChart/labelUtils.js +15 -15
  53. package/FunnelChart/positionGetter.d.ts +1 -0
  54. package/FunnelChart/positionGetter.js +5 -0
  55. package/FunnelChart/useFunnelChartProps.js +1 -3
  56. package/Heatmap/Heatmap.d.ts +8 -3
  57. package/Heatmap/Heatmap.js +10 -2
  58. package/Heatmap/HeatmapTooltip/HeatmapTooltipAxesValue.d.ts +1 -1
  59. package/LineChartPro/LineChartPro.d.ts +3 -2
  60. package/LineChartPro/LineChartPro.js +1 -1
  61. package/PieChartPro/PieChartPro.d.ts +22 -0
  62. package/PieChartPro/PieChartPro.js +204 -0
  63. package/PieChartPro/PieChartPro.plugins.d.ts +4 -0
  64. package/PieChartPro/PieChartPro.plugins.js +9 -0
  65. package/PieChartPro/index.d.ts +1 -0
  66. package/PieChartPro/index.js +16 -0
  67. package/RadarChartPro/RadarChartPro.d.ts +17 -2
  68. package/RadarChartPro/RadarChartPro.js +1 -1
  69. package/ScatterChartPro/ScatterChartPro.d.ts +3 -2
  70. package/ScatterChartPro/ScatterChartPro.js +1 -1
  71. package/esm/BarChartPro/BarChartPro.d.ts +3 -2
  72. package/esm/BarChartPro/BarChartPro.js +1 -1
  73. package/esm/ChartContainerPro/ChartProApi.d.ts +2 -0
  74. package/esm/ChartDataProviderPro/ChartDataProviderPro.js +1 -1
  75. package/esm/ChartZoomSlider/internals/ChartAxisZoomSliderActiveTrack.d.ts +0 -2
  76. package/esm/ChartZoomSlider/internals/ChartAxisZoomSliderActiveTrack.js +43 -10
  77. package/esm/ChartZoomSlider/internals/ChartAxisZoomSliderTrack.js +0 -22
  78. package/esm/ChartsToolbarPro/ChartsToolbarPro.d.ts +2 -4
  79. package/esm/ChartsToolbarPro/ChartsToolbarPro.js +8 -14
  80. package/esm/ChartsToolbarPro/{ChartsToolbarZoomInButton.d.ts → ChartsToolbarZoomInTrigger.d.ts} +6 -5
  81. package/esm/ChartsToolbarPro/{ChartsToolbarZoomInButton.js → ChartsToolbarZoomInTrigger.js} +8 -7
  82. package/esm/ChartsToolbarPro/{ChartsToolbarZoomOutButton.d.ts → ChartsToolbarZoomOutTrigger.d.ts} +6 -5
  83. package/esm/ChartsToolbarPro/{ChartsToolbarZoomOutButton.js → ChartsToolbarZoomOutTrigger.js} +8 -7
  84. package/esm/ChartsToolbarPro/Toolbar.types.d.ts +15 -0
  85. package/esm/ChartsToolbarPro/Toolbar.types.js +1 -0
  86. package/esm/ChartsToolbarPro/index.d.ts +2 -2
  87. package/esm/ChartsToolbarPro/index.js +2 -2
  88. package/esm/FunnelChart/FunnelChart.js +4 -11
  89. package/esm/FunnelChart/FunnelChart.plugins.d.ts +3 -2
  90. package/esm/FunnelChart/FunnelChart.plugins.js +3 -2
  91. package/esm/FunnelChart/FunnelPlot.d.ts +0 -5
  92. package/esm/FunnelChart/FunnelPlot.js +41 -51
  93. package/esm/FunnelChart/FunnelSection.d.ts +1 -1
  94. package/esm/FunnelChart/FunnelSectionLabel.d.ts +21 -0
  95. package/esm/FunnelChart/FunnelSectionLabel.js +46 -0
  96. package/esm/FunnelChart/categoryAxis.types.d.ts +2 -1
  97. package/esm/FunnelChart/curves/bump.d.ts +9 -5
  98. package/esm/FunnelChart/curves/bump.js +72 -43
  99. package/esm/FunnelChart/curves/curve.types.d.ts +6 -1
  100. package/esm/FunnelChart/curves/getFunnelCurve.js +3 -0
  101. package/esm/FunnelChart/curves/linear.d.ts +10 -2
  102. package/esm/FunnelChart/curves/linear.js +97 -17
  103. package/esm/FunnelChart/curves/pyramid.js +10 -14
  104. package/esm/FunnelChart/curves/step-pyramid.js +4 -6
  105. package/esm/FunnelChart/curves/step.js +1 -15
  106. package/esm/FunnelChart/funnel.types.d.ts +1 -0
  107. package/esm/FunnelChart/funnelAxisPlugin/computeAxisValue.d.ts +25 -0
  108. package/esm/FunnelChart/funnelAxisPlugin/computeAxisValue.js +114 -0
  109. package/esm/FunnelChart/funnelAxisPlugin/useChartFunnelAxis.d.ts +3 -0
  110. package/esm/FunnelChart/funnelAxisPlugin/useChartFunnelAxis.js +165 -0
  111. package/esm/FunnelChart/funnelAxisPlugin/useChartFunnelAxis.types.d.ts +27 -0
  112. package/esm/FunnelChart/funnelAxisPlugin/useChartFunnelAxis.types.js +1 -0
  113. package/esm/FunnelChart/funnelAxisPlugin/useChartFunnelAxisRendering.selectors.d.ts +1482 -0
  114. package/esm/FunnelChart/funnelAxisPlugin/useChartFunnelAxisRendering.selectors.js +20 -0
  115. package/esm/FunnelChart/funnelPlotSlots.types.d.ts +11 -0
  116. package/esm/FunnelChart/funnelSectionClasses.d.ts +2 -0
  117. package/esm/FunnelChart/funnelSectionClasses.js +15 -3
  118. package/esm/FunnelChart/index.d.ts +3 -1
  119. package/esm/FunnelChart/index.js +3 -1
  120. package/esm/FunnelChart/labelUtils.d.ts +3 -2
  121. package/esm/FunnelChart/labelUtils.js +15 -15
  122. package/esm/FunnelChart/positionGetter.d.ts +1 -0
  123. package/esm/FunnelChart/positionGetter.js +1 -0
  124. package/esm/FunnelChart/useFunnelChartProps.js +1 -3
  125. package/esm/Heatmap/Heatmap.d.ts +8 -3
  126. package/esm/Heatmap/Heatmap.js +10 -2
  127. package/esm/Heatmap/HeatmapTooltip/HeatmapTooltipAxesValue.d.ts +1 -1
  128. package/esm/LineChartPro/LineChartPro.d.ts +3 -2
  129. package/esm/LineChartPro/LineChartPro.js +1 -1
  130. package/esm/PieChartPro/PieChartPro.d.ts +22 -0
  131. package/esm/PieChartPro/PieChartPro.js +197 -0
  132. package/esm/PieChartPro/PieChartPro.plugins.d.ts +4 -0
  133. package/esm/PieChartPro/PieChartPro.plugins.js +3 -0
  134. package/esm/PieChartPro/index.d.ts +1 -0
  135. package/esm/PieChartPro/index.js +1 -0
  136. package/esm/RadarChartPro/RadarChartPro.d.ts +17 -2
  137. package/esm/RadarChartPro/RadarChartPro.js +1 -1
  138. package/esm/ScatterChartPro/ScatterChartPro.d.ts +3 -2
  139. package/esm/ScatterChartPro/ScatterChartPro.js +1 -1
  140. package/esm/index.d.ts +1 -0
  141. package/esm/index.js +2 -1
  142. package/index.d.ts +1 -0
  143. package/index.js +12 -1
  144. package/package.json +7 -7
@@ -1,29 +1,31 @@
1
1
  import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/esm/objectWithoutPropertiesLoose";
2
2
  import _extends from "@babel/runtime/helpers/esm/extends";
3
- const _excluded = ["onItemClick", "gap"];
3
+ const _excluded = ["onItemClick"];
4
4
  import * as React from 'react';
5
5
  import PropTypes from 'prop-types';
6
6
  import { line as d3Line } from '@mui/x-charts-vendor/d3-shape';
7
- import { cartesianSeriesTypes } from '@mui/x-charts/internals';
8
- import { useXAxes, useYAxes } from '@mui/x-charts/hooks';
9
- import { useTheme } from '@mui/material/styles';
7
+ import { cartesianSeriesTypes, useSelector, useStore, isBandScale } from '@mui/x-charts/internals';
10
8
  import { FunnelSection } from "./FunnelSection.js";
11
9
  import { alignLabel, positionLabel } from "./labelUtils.js";
12
10
  import { useFunnelSeriesContext } from "../hooks/useFunnelSeries.js";
13
11
  import { getFunnelCurve } from "./curves/index.js";
12
+ import { FunnelSectionLabel } from "./FunnelSectionLabel.js";
13
+ import { selectorChartXAxis, selectorChartYAxis, selectorFunnelGap } from "./funnelAxisPlugin/useChartFunnelAxisRendering.selectors.js";
14
14
  import { createElement as _createElement } from "react";
15
15
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
16
16
  cartesianSeriesTypes.addType('funnel');
17
- const useAggregatedData = gap => {
17
+ const useAggregatedData = () => {
18
18
  const seriesData = useFunnelSeriesContext();
19
+ const store = useStore();
19
20
  const {
20
- xAxis,
21
- xAxisIds
22
- } = useXAxes();
21
+ axis: xAxis,
22
+ axisIds: xAxisIds
23
+ } = useSelector(store, selectorChartXAxis);
23
24
  const {
24
- yAxis,
25
- yAxisIds
26
- } = useYAxes();
25
+ axis: yAxis,
26
+ axisIds: yAxisIds
27
+ } = useSelector(store, selectorChartYAxis);
28
+ const gap = useSelector(store, selectorFunnelGap);
27
29
  const allData = React.useMemo(() => {
28
30
  if (seriesData === undefined) {
29
31
  return [];
@@ -46,22 +48,28 @@ const useAggregatedData = gap => {
46
48
  const bandWidth = (isXAxisBand || isYAxisBand) && baseScaleConfig.scale?.bandwidth() || 0;
47
49
  const xScale = xAxis[xAxisId].scale;
48
50
  const yScale = yAxis[yAxisId].scale;
49
- const xPosition = (value, bandIndex, stackOffset, useBand) => {
50
- if (isXAxisBand) {
51
- const position = xScale(bandIndex);
51
+ const xPosition = (value, bandIndex, bandIdentifier, stackOffset, useBand) => {
52
+ if (isBandScale(xScale)) {
53
+ const position = xScale(bandIdentifier);
52
54
  return useBand ? position + bandWidth : position;
53
55
  }
54
- return xScale(isHorizontal ? value + (stackOffset || 0) : value);
56
+ if (isHorizontal) {
57
+ return xScale(value + (stackOffset || 0)) + bandIndex * gap;
58
+ }
59
+ return xScale(value);
55
60
  };
56
- const yPosition = (value, bandIndex, stackOffset, useBand) => {
57
- if (isYAxisBand) {
58
- const position = yScale(bandIndex);
61
+ const yPosition = (value, bandIndex, bandIdentifier, stackOffset, useBand) => {
62
+ if (isBandScale(yScale)) {
63
+ const position = yScale(bandIdentifier);
59
64
  return useBand ? position + bandWidth : position;
60
65
  }
61
- return yScale(isHorizontal ? value : value + (stackOffset || 0));
66
+ if (isHorizontal) {
67
+ return yScale(value);
68
+ }
69
+ return yScale(value + (stackOffset || 0)) + bandIndex * gap;
62
70
  };
63
- const allY = currentSeries.dataPoints.flatMap((d, dataIndex) => d.flatMap(v => yPosition(v.y, baseScaleConfig.data?.[dataIndex], v.stackOffset, v.useBandWidth)));
64
- const allX = currentSeries.dataPoints.flatMap((d, dataIndex) => d.flatMap(v => xPosition(v.x, baseScaleConfig.data?.[dataIndex], v.stackOffset, v.useBandWidth)));
71
+ const allY = currentSeries.dataPoints.flatMap((d, dataIndex) => d.flatMap(v => yPosition(v.y, dataIndex, baseScaleConfig.data?.[dataIndex], v.stackOffset, v.useBandWidth)));
72
+ const allX = currentSeries.dataPoints.flatMap((d, dataIndex) => d.flatMap(v => xPosition(v.x, dataIndex, baseScaleConfig.data?.[dataIndex], v.stackOffset, v.useBandWidth)));
65
73
  const minPoint = {
66
74
  x: Math.min(...allX),
67
75
  y: Math.min(...allY)
@@ -89,7 +97,7 @@ const useAggregatedData = gap => {
89
97
  min: minPoint,
90
98
  max: maxPoint
91
99
  });
92
- const line = d3Line().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);
100
+ const line = d3Line().x(d => xPosition(d.x, dataIndex, baseScaleConfig.data?.[dataIndex], d.stackOffset, d.useBandWidth)).y(d => yPosition(d.y, dataIndex, baseScaleConfig.data?.[dataIndex], d.stackOffset, d.useBandWidth)).curve(curve);
93
101
  return {
94
102
  d: line(values),
95
103
  color,
@@ -118,12 +126,10 @@ const useAggregatedData = gap => {
118
126
  };
119
127
  function FunnelPlot(props) {
120
128
  const {
121
- onItemClick,
122
- gap
129
+ onItemClick
123
130
  } = props,
124
131
  other = _objectWithoutPropertiesLoose(props, _excluded);
125
- const theme = useTheme();
126
- const data = useAggregatedData(gap);
132
+ const data = useAggregatedData();
127
133
  return /*#__PURE__*/_jsxs(React.Fragment, {
128
134
  children: [data.map(({
129
135
  d,
@@ -148,29 +154,18 @@ function FunnelPlot(props) {
148
154
  })
149
155
  }))), data.map(({
150
156
  id,
151
- label
157
+ label,
158
+ seriesId,
159
+ dataIndex
152
160
  }) => {
153
- if (!label) {
161
+ if (!label || !label.value) {
154
162
  return null;
155
163
  }
156
- return /*#__PURE__*/_jsx("text", {
157
- x: label.x,
158
- y: label.y,
159
- textAnchor: label.textAnchor,
160
- dominantBaseline: label.dominantBaseline,
161
- stroke: "none",
162
- pointerEvents: "none",
163
- fontFamily: theme.typography.body2.fontFamily,
164
- fontSize: theme.typography.body2.fontSize,
165
- fontSizeAdjust: theme.typography.body2.fontSizeAdjust,
166
- fontWeight: theme.typography.body2.fontWeight,
167
- letterSpacing: theme.typography.body2.letterSpacing,
168
- fontStretch: theme.typography.body2.fontStretch,
169
- fontStyle: theme.typography.body2.fontStyle,
170
- fontVariant: theme.typography.body2.fontVariant,
171
- fill: (theme.vars || theme)?.palette?.text?.primary,
172
- children: label.value
173
- }, id);
164
+ return /*#__PURE__*/_jsx(FunnelSectionLabel, _extends({
165
+ label: label,
166
+ dataIndex: dataIndex,
167
+ seriesId: seriesId
168
+ }, other), id);
174
169
  })]
175
170
  });
176
171
  }
@@ -179,11 +174,6 @@ process.env.NODE_ENV !== "production" ? FunnelPlot.propTypes = {
179
174
  // | These PropTypes are generated from the TypeScript type definitions |
180
175
  // | To update them edit the TypeScript types and run "pnpm proptypes" |
181
176
  // ----------------------------------------------------------------------
182
- /**
183
- * The gap, in pixels, between funnel sections.
184
- * @default 0
185
- */
186
- gap: PropTypes.number,
187
177
  /**
188
178
  * Callback fired when a funnel item is clicked.
189
179
  * @param {React.MouseEvent<SVGElement, MouseEvent>} event The event source of the callback.
@@ -8,7 +8,7 @@ export interface FunnelSectionProps extends Omit<React.SVGProps<SVGPathElement>,
8
8
  classes?: Partial<FunnelSectionClasses>;
9
9
  variant?: 'filled' | 'outlined';
10
10
  }
11
- export declare const FunnelSectionPath: import("@emotion/styled").StyledComponent<import("@mui/system").MUIStyledCommonProps<import("@mui/material").Theme>, React.SVGProps<SVGPathElement>, {}>;
11
+ export declare const FunnelSectionPath: import("@emotion/styled").StyledComponent<import("@mui/system").MUIStyledCommonProps<import("@mui/material/styles").Theme>, React.SVGProps<SVGPathElement>, {}>;
12
12
  /**
13
13
  * @ignore - internal component.
14
14
  */
@@ -0,0 +1,21 @@
1
+ import * as React from 'react';
2
+ import { SeriesId } from '@mui/x-charts/internals';
3
+ import { FunnelSectionClasses } from "./funnelSectionClasses.js";
4
+ export interface FunnelSectionLabelConfig {
5
+ x: number;
6
+ y: number;
7
+ value: string | null;
8
+ textAnchor?: React.SVGProps<SVGTextElement>['textAnchor'];
9
+ dominantBaseline?: React.SVGProps<SVGTextElement>['dominantBaseline'];
10
+ }
11
+ export interface FunnelSectionLabelProps extends Omit<React.SVGProps<SVGTextElement>, 'ref' | 'id'> {
12
+ classes?: Partial<FunnelSectionClasses>;
13
+ label: FunnelSectionLabelConfig;
14
+ seriesId: SeriesId;
15
+ dataIndex: number;
16
+ }
17
+ /**
18
+ * @ignore - internal component.
19
+ */
20
+ declare const FunnelSectionLabel: React.ForwardRefExoticComponent<FunnelSectionLabelProps & React.RefAttributes<{}>>;
21
+ export { FunnelSectionLabel };
@@ -0,0 +1,46 @@
1
+ 'use client';
2
+
3
+ import _extends from "@babel/runtime/helpers/esm/extends";
4
+ import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/esm/objectWithoutPropertiesLoose";
5
+ const _excluded = ["classes", "color", "onClick", "className", "label", "seriesId", "dataIndex"];
6
+ import * as React from 'react';
7
+ import { useTheme } from '@mui/material/styles';
8
+ import { consumeSlots } from '@mui/x-charts/internals';
9
+ import { useLabelUtilityClasses } from "./funnelSectionClasses.js";
10
+ import { jsx as _jsx } from "react/jsx-runtime";
11
+ /**
12
+ * @ignore - internal component.
13
+ */
14
+ const FunnelSectionLabel = consumeSlots('MuiFunnelSectionLabel', 'funnelSectionLabel', {
15
+ classesResolver: useLabelUtilityClasses
16
+ }, /*#__PURE__*/React.forwardRef(function FunnelSectionLabel(props, ref) {
17
+ const {
18
+ classes,
19
+ label
20
+ } = props,
21
+ other = _objectWithoutPropertiesLoose(props, _excluded);
22
+ const theme = useTheme();
23
+ return /*#__PURE__*/_jsx("text", _extends({
24
+ stroke: "none",
25
+ pointerEvents: "none",
26
+ fontFamily: theme.typography.body2.fontFamily,
27
+ fontSize: theme.typography.body2.fontSize,
28
+ fontSizeAdjust: theme.typography.body2.fontSizeAdjust,
29
+ fontWeight: theme.typography.body2.fontWeight,
30
+ letterSpacing: theme.typography.body2.letterSpacing,
31
+ fontStretch: theme.typography.body2.fontStretch,
32
+ fontStyle: theme.typography.body2.fontStyle,
33
+ fontVariant: theme.typography.body2.fontVariant,
34
+ fill: (theme.vars || theme)?.palette?.text?.primary,
35
+ className: classes?.label,
36
+ x: label.x,
37
+ y: label.y,
38
+ textAnchor: label.textAnchor ?? 'middle',
39
+ dominantBaseline: label.dominantBaseline ?? 'central'
40
+ }, other, {
41
+ ref: ref,
42
+ children: label.value
43
+ }));
44
+ }));
45
+ if (process.env.NODE_ENV !== "production") FunnelSectionLabel.displayName = "FunnelSectionLabel";
46
+ export { FunnelSectionLabel };
@@ -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.
@@ -10,18 +10,22 @@ import { CurveOptions } from "./curve.types.js";
10
10
  */
11
11
  export declare class Bump implements CurveGenerator {
12
12
  private context;
13
- private x;
14
- private y;
15
- private currentPoint;
16
13
  private isHorizontal;
17
- private gap;
14
+ private min;
15
+ private max;
16
+ private points;
18
17
  constructor(context: CanvasRenderingContext2D, {
19
18
  isHorizontal,
20
- gap
19
+ min,
20
+ max,
21
+ isIncreasing
21
22
  }: CurveOptions);
22
23
  areaStart(): void;
23
24
  areaEnd(): void;
24
25
  lineStart(): void;
25
26
  lineEnd(): void;
26
27
  point(x: number, y: number): void;
28
+ private drawPath;
29
+ private drawHorizontalPath;
30
+ private drawVerticalPath;
27
31
  }
@@ -11,64 +11,93 @@
11
11
  export class Bump {
12
12
  constructor(context, {
13
13
  isHorizontal,
14
- gap
14
+ min,
15
+ max,
16
+ isIncreasing
15
17
  }) {
16
18
  this.context = void 0;
17
- this.x = NaN;
18
- this.y = NaN;
19
- this.currentPoint = 0;
20
19
  this.isHorizontal = false;
21
- this.gap = 0;
20
+ this.min = {
21
+ x: 0,
22
+ y: 0
23
+ };
24
+ this.max = {
25
+ x: 0,
26
+ y: 0
27
+ };
28
+ this.points = [];
22
29
  this.context = context;
23
30
  this.isHorizontal = isHorizontal ?? false;
24
- this.gap = (gap ?? 0) / 2;
31
+ this.min = min ?? {
32
+ x: 0,
33
+ y: 0
34
+ };
35
+ this.max = max ?? {
36
+ x: 0,
37
+ y: 0
38
+ };
39
+ if (isIncreasing) {
40
+ const currentMin = this.min;
41
+ const currentMax = this.max;
42
+ this.min = currentMax;
43
+ this.max = currentMin;
44
+ }
25
45
  }
26
46
  areaStart() {}
27
47
  areaEnd() {}
28
48
  lineStart() {}
29
49
  lineEnd() {}
30
50
  point(x, y) {
31
- x = +x;
32
- y = +y;
33
-
34
- // 0 is the top-left corner.
35
- if (this.isHorizontal) {
36
- if (this.currentPoint === 0) {
37
- this.context.moveTo(x + this.gap, y);
38
- this.context.lineTo(x + this.gap, y);
39
- } else if (this.currentPoint === 1) {
40
- this.context.bezierCurveTo((this.x + x) / 2, this.y, (this.x + x) / 2, y, x - this.gap, y);
41
- } else if (this.currentPoint === 2) {
42
- this.context.lineTo(x - this.gap, y);
43
- } else {
44
- this.context.bezierCurveTo((this.x + x) / 2, this.y, (this.x + x) / 2, y, x + this.gap, y);
45
- }
46
- if (this.currentPoint === 3) {
47
- this.context.closePath();
48
- }
49
- this.currentPoint += 1;
50
- this.x = x;
51
- this.y = y;
51
+ this.points.push({
52
+ x,
53
+ y
54
+ });
55
+ if (this.points.length < 4) {
52
56
  return;
53
57
  }
54
58
 
55
- // 0 is the top-right corner.
56
- if (this.currentPoint === 0) {
57
- // X from Y
58
- this.context.moveTo(x, y + this.gap);
59
- this.context.lineTo(x, y + this.gap);
60
- } else if (this.currentPoint === 1) {
61
- this.context.bezierCurveTo(this.x, (this.y + y) / 2, x, (this.y + y) / 2, x, y - this.gap);
62
- } else if (this.currentPoint === 2) {
63
- this.context.lineTo(x, y - this.gap);
59
+ // Draw the path using bezier curves
60
+ this.drawPath();
61
+ }
62
+ drawPath() {
63
+ if (this.isHorizontal) {
64
+ this.drawHorizontalPath();
64
65
  } else {
65
- this.context.bezierCurveTo(this.x, (this.y + y) / 2, x, (this.y + y) / 2, x, y + this.gap);
66
- }
67
- if (this.currentPoint === 3) {
68
- this.context.closePath();
66
+ this.drawVerticalPath();
69
67
  }
70
- this.currentPoint += 1;
71
- this.x = x;
72
- this.y = y;
68
+ }
69
+ drawHorizontalPath() {
70
+ const [p0, p1, p2, p3] = this.points;
71
+
72
+ // 0 is the top-left corner
73
+ this.context.moveTo(p0.x, p0.y);
74
+ this.context.lineTo(p0.x, p0.y);
75
+
76
+ // Bezier curve to point 1
77
+ this.context.bezierCurveTo((p0.x + p1.x) / 2, p0.y, (p0.x + p1.x) / 2, p1.y, p1.x, p1.y);
78
+
79
+ // Line to point 2
80
+ this.context.lineTo(p2.x, p2.y);
81
+
82
+ // Bezier curve back to point 3
83
+ this.context.bezierCurveTo((p2.x + p3.x) / 2, p2.y, (p2.x + p3.x) / 2, p3.y, p3.x, p3.y);
84
+ this.context.closePath();
85
+ }
86
+ drawVerticalPath() {
87
+ const [p0, p1, p2, p3] = this.points;
88
+
89
+ // 0 is the top-right corner
90
+ this.context.moveTo(p0.x, p0.y);
91
+ this.context.lineTo(p0.x, p0.y);
92
+
93
+ // Bezier curve to point 1
94
+ this.context.bezierCurveTo(p0.x, (p0.y + p1.y) / 2, p1.x, (p0.y + p1.y) / 2, p1.x, p1.y);
95
+
96
+ // Line to point 2
97
+ this.context.lineTo(p2.x, p2.y);
98
+
99
+ // Bezier curve back to point 3
100
+ this.context.bezierCurveTo(p2.x, (p2.y + p3.y) / 2, p3.x, (p2.y + p3.y) / 2, p3.x, p3.y);
101
+ this.context.closePath();
73
102
  }
74
103
  }
@@ -34,8 +34,13 @@ export type CurveOptions = {
34
34
  * The maximum point for all the segments.
35
35
  */
36
36
  max?: Point;
37
+ /**
38
+ * The shape of the point of the funnel for the curves that support it.
39
+ */
40
+ pointShape?: FunnelPointShape;
37
41
  };
38
- export type FunnelCurveType = 'linear' | 'step' | 'bump' | 'pyramid' | 'step-pyramid';
42
+ export type FunnelCurveType = 'linear' | 'linear-sharp' | 'step' | 'bump' | 'pyramid' | 'step-pyramid';
43
+ export type FunnelPointShape = 'square' | 'sharp';
39
44
  export type Point = {
40
45
  x: number;
41
46
  y: number;
@@ -19,5 +19,8 @@ const curveConstructor = curve => {
19
19
  return Linear;
20
20
  };
21
21
  export const getFunnelCurve = (curve, options) => {
22
+ if (curve === 'linear-sharp') {
23
+ options.pointShape = 'sharp';
24
+ }
22
25
  return context => new (curveConstructor(curve))(context, options);
23
26
  };
@@ -3,7 +3,7 @@ import { CurveOptions } from "./curve.types.js";
3
3
  /**
4
4
  * This is a custom "linear" curve generator.
5
5
  * 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.
6
+ * with the option to properly handling the border radius.
7
7
  *
8
8
  * The implementation is based on the d3-shape linear curve generator.
9
9
  * https://github.com/d3/d3-shape/blob/a82254af78f08799c71d7ab25df557c4872a3c51/src/curve/linear.js
@@ -13,15 +13,23 @@ export declare class Linear implements CurveGenerator {
13
13
  private position;
14
14
  private sections;
15
15
  private isHorizontal;
16
+ private isIncreasing;
16
17
  private gap;
17
18
  private borderRadius;
19
+ private min;
20
+ private max;
18
21
  private points;
22
+ private pointShape;
19
23
  constructor(context: CanvasRenderingContext2D, {
20
24
  isHorizontal,
21
25
  gap,
22
26
  position,
23
27
  sections,
24
- borderRadius
28
+ borderRadius,
29
+ min,
30
+ max,
31
+ isIncreasing,
32
+ pointShape
25
33
  }: CurveOptions);
26
34
  areaStart(): void;
27
35
  areaEnd(): void;
@@ -6,7 +6,7 @@ import { lerpX, lerpY } from "./utils.js";
6
6
  /**
7
7
  * This is a custom "linear" curve generator.
8
8
  * It draws straight lines for the 4 provided points,
9
- * with the option to add a gap between sections while also properly handling the border radius.
9
+ * with the option to properly handling the border radius.
10
10
  *
11
11
  * The implementation is based on the d3-shape linear curve generator.
12
12
  * https://github.com/d3/d3-shape/blob/a82254af78f08799c71d7ab25df557c4872a3c51/src/curve/linear.js
@@ -17,21 +17,51 @@ export class Linear {
17
17
  gap,
18
18
  position,
19
19
  sections,
20
- borderRadius
20
+ borderRadius,
21
+ min,
22
+ max,
23
+ isIncreasing,
24
+ pointShape
21
25
  }) {
22
26
  this.context = void 0;
23
27
  this.position = 0;
24
28
  this.sections = 0;
25
29
  this.isHorizontal = false;
30
+ this.isIncreasing = false;
26
31
  this.gap = 0;
27
32
  this.borderRadius = 0;
33
+ this.min = {
34
+ x: 0,
35
+ y: 0
36
+ };
37
+ this.max = {
38
+ x: 0,
39
+ y: 0
40
+ };
28
41
  this.points = [];
42
+ this.pointShape = 'square';
29
43
  this.context = context;
30
44
  this.isHorizontal = isHorizontal ?? false;
31
- this.gap = (gap ?? 0) / 2;
45
+ this.gap = gap ?? 0;
32
46
  this.position = position ?? 0;
33
47
  this.sections = sections ?? 1;
34
48
  this.borderRadius = borderRadius ?? 0;
49
+ this.isIncreasing = isIncreasing ?? false;
50
+ this.min = min ?? {
51
+ x: 0,
52
+ y: 0
53
+ };
54
+ this.max = max ?? {
55
+ x: 0,
56
+ y: 0
57
+ };
58
+ this.pointShape = pointShape ?? 'square';
59
+ if (isIncreasing) {
60
+ const currentMin = this.min;
61
+ const currentMax = this.max;
62
+ this.min = currentMax;
63
+ this.max = currentMin;
64
+ }
35
65
  }
36
66
  areaStart() {}
37
67
  areaEnd() {}
@@ -41,11 +71,34 @@ export class Linear {
41
71
  if (this.gap > 0) {
42
72
  return this.borderRadius;
43
73
  }
44
- if (this.position === 0) {
45
- return [0, 0, this.borderRadius, this.borderRadius];
74
+ if (this.isIncreasing) {
75
+ // Is largest section
76
+ if (this.position === this.sections - 1) {
77
+ return [this.borderRadius, this.borderRadius];
78
+ }
79
+ // Is smallest section and shaped like a triangle
80
+ if (this.position === 0 && this.pointShape === 'sharp') {
81
+ return [0, 0, this.borderRadius];
82
+ }
83
+ // Is smallest section
84
+ if (this.position === 0) {
85
+ return [0, 0, this.borderRadius, this.borderRadius];
86
+ }
46
87
  }
47
- if (this.position === this.sections - 1) {
48
- return [this.borderRadius, this.borderRadius];
88
+ if (!this.isIncreasing) {
89
+ // Is largest section
90
+ if (this.position === 0) {
91
+ return [0, 0, this.borderRadius, this.borderRadius];
92
+ }
93
+ // Is smallest section and shaped like a triangle
94
+ if (this.position === this.sections - 1 && this.pointShape === 'sharp') {
95
+ return [this.borderRadius];
96
+ }
97
+
98
+ // Is smallest section
99
+ if (this.position === this.sections - 1) {
100
+ return [this.borderRadius, this.borderRadius];
101
+ }
49
102
  }
50
103
  return 0;
51
104
  }
@@ -60,23 +113,50 @@ export class Linear {
60
113
 
61
114
  // Add gaps where they are needed.
62
115
  this.points = this.points.map((point, index) => {
63
- const slopeStart = this.points.at(index <= 1 ? 0 : 2);
64
- const slopeEnd = this.points.at(index <= 1 ? 1 : 3);
116
+ const slopeStart = this.points.at(index <= 1 ? 0 : 3);
117
+ const slopeEnd = this.points.at(index <= 1 ? 1 : 2);
65
118
  if (this.isHorizontal) {
66
- const yGetter = lerpY(slopeStart.x - this.gap, slopeStart.y, slopeEnd.x - this.gap, slopeEnd.y);
67
- const xGap = point.x + (index === 0 || index === 3 ? this.gap : -this.gap);
119
+ const yGetter = lerpY(slopeStart.x - this.gap, slopeStart.y, slopeEnd.x, slopeEnd.y);
68
120
  return {
69
- x: xGap,
70
- y: yGetter(xGap)
121
+ x: point.x,
122
+ y: yGetter(point.x)
71
123
  };
72
124
  }
73
- const xGetter = lerpX(slopeStart.x, slopeStart.y - this.gap, slopeEnd.x, slopeEnd.y - this.gap);
74
- const yGap = point.y + (index === 0 || index === 3 ? this.gap : -this.gap);
125
+ const xGetter = lerpX(slopeStart.x, slopeStart.y - this.gap, slopeEnd.x, slopeEnd.y);
75
126
  return {
76
- x: xGetter(yGap),
77
- y: yGap
127
+ x: xGetter(point.y),
128
+ y: point.y
78
129
  };
79
130
  });
131
+ if (this.pointShape === 'sharp') {
132
+ // In the last section, to form a triangle we need 3 points instead of 4
133
+ // Else the algorithm will break.
134
+ const isLastSection = this.position === this.sections - 1;
135
+ const isFirstSection = this.position === 0;
136
+ let firstPoint = null;
137
+ let secondPoint = null;
138
+ if (isFirstSection && this.isIncreasing) {
139
+ firstPoint = this.points[1];
140
+ secondPoint = this.points[2];
141
+ }
142
+ if (isLastSection && !this.isIncreasing) {
143
+ firstPoint = this.points[3];
144
+ secondPoint = this.points[0];
145
+ }
146
+ if (firstPoint && secondPoint) {
147
+ this.points = [
148
+ // Sharp point at the start
149
+ this.isHorizontal ? {
150
+ x: this.max.x,
151
+ y: (this.max.y + this.min.y) / 2
152
+ } : {
153
+ x: (this.max.x + this.min.x) / 2,
154
+ y: this.max.y
155
+ },
156
+ // Then other points
157
+ firstPoint, secondPoint];
158
+ }
159
+ }
80
160
  borderRadiusPolygon(this.context, this.points, this.getBorderRadius());
81
161
  }
82
162
  }