@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.
- package/BarChartPro/BarChartPro.d.ts +3 -2
- package/CHANGELOG.md +187 -10
- package/ChartDataProviderPro/ChartDataProviderPro.js +1 -1
- package/ChartZoomSlider/internals/ChartAxisZoomSlider.js +1 -1
- package/ChartZoomSlider/internals/ChartAxisZoomSliderActiveTrack.js +22 -32
- package/ChartZoomSlider/internals/ChartAxisZoomSliderTrack.js +1 -8
- package/ChartZoomSlider/internals/zoom-utils.d.ts +3 -1
- package/ChartZoomSlider/internals/zoom-utils.js +19 -8
- package/ChartsToolbarPro/ChartsToolbarPro.d.ts +2 -1
- package/ChartsToolbarPro/Toolbar.types.d.ts +15 -0
- package/ChartsToolbarPro/Toolbar.types.js +5 -0
- package/FunnelChart/FunnelChart.js +4 -11
- package/FunnelChart/FunnelChart.plugins.d.ts +3 -2
- package/FunnelChart/FunnelChart.plugins.js +2 -1
- package/FunnelChart/FunnelPlot.d.ts +0 -5
- package/FunnelChart/FunnelPlot.js +85 -67
- package/FunnelChart/FunnelSection.js +2 -0
- package/FunnelChart/categoryAxis.types.d.ts +2 -1
- package/FunnelChart/curves/bump.d.ts +3 -5
- package/FunnelChart/curves/bump.js +13 -13
- package/FunnelChart/curves/curve.types.d.ts +14 -1
- package/FunnelChart/curves/getFunnelCurve.d.ts +7 -2
- package/FunnelChart/curves/linear.d.ts +4 -4
- package/FunnelChart/curves/linear.js +42 -34
- package/FunnelChart/curves/pyramid.d.ts +3 -3
- package/FunnelChart/curves/pyramid.js +25 -24
- package/FunnelChart/curves/step-pyramid.d.ts +5 -5
- package/FunnelChart/curves/step-pyramid.js +24 -24
- package/FunnelChart/curves/step.d.ts +3 -3
- package/FunnelChart/curves/step.js +14 -26
- package/FunnelChart/funnelAxisPlugin/computeAxisValue.d.ts +25 -0
- package/FunnelChart/funnelAxisPlugin/computeAxisValue.js +124 -0
- package/FunnelChart/funnelAxisPlugin/useChartFunnelAxis.d.ts +3 -0
- package/FunnelChart/funnelAxisPlugin/useChartFunnelAxis.js +173 -0
- package/FunnelChart/funnelAxisPlugin/useChartFunnelAxis.types.d.ts +27 -0
- package/FunnelChart/funnelAxisPlugin/useChartFunnelAxis.types.js +5 -0
- package/FunnelChart/funnelAxisPlugin/useChartFunnelAxisRendering.selectors.d.ts +56 -0
- package/FunnelChart/funnelAxisPlugin/useChartFunnelAxisRendering.selectors.js +27 -0
- package/FunnelChart/labelUtils.d.ts +4 -11
- package/FunnelChart/labelUtils.js +43 -44
- package/FunnelChart/useFunnelChartProps.js +1 -3
- package/Heatmap/Heatmap.d.ts +3 -3
- package/LineChartPro/LineChartPro.d.ts +3 -2
- package/PieChartPro/PieChartPro.d.ts +3 -2
- package/RadarChartPro/RadarChartPro.d.ts +17 -2
- package/ScatterChartPro/ScatterChartPro.d.ts +3 -2
- package/esm/BarChartPro/BarChartPro.d.ts +3 -2
- package/esm/ChartDataProviderPro/ChartDataProviderPro.js +1 -1
- package/esm/ChartZoomSlider/internals/ChartAxisZoomSlider.js +1 -1
- package/esm/ChartZoomSlider/internals/ChartAxisZoomSliderActiveTrack.js +23 -33
- package/esm/ChartZoomSlider/internals/ChartAxisZoomSliderTrack.js +1 -8
- package/esm/ChartZoomSlider/internals/zoom-utils.d.ts +3 -1
- package/esm/ChartZoomSlider/internals/zoom-utils.js +19 -9
- package/esm/ChartsToolbarPro/ChartsToolbarPro.d.ts +2 -1
- package/esm/ChartsToolbarPro/Toolbar.types.d.ts +15 -0
- package/esm/ChartsToolbarPro/Toolbar.types.js +1 -0
- package/esm/FunnelChart/FunnelChart.js +4 -11
- package/esm/FunnelChart/FunnelChart.plugins.d.ts +3 -2
- package/esm/FunnelChart/FunnelChart.plugins.js +3 -2
- package/esm/FunnelChart/FunnelPlot.d.ts +0 -5
- package/esm/FunnelChart/FunnelPlot.js +86 -68
- package/esm/FunnelChart/FunnelSection.js +2 -0
- package/esm/FunnelChart/categoryAxis.types.d.ts +2 -1
- package/esm/FunnelChart/curves/bump.d.ts +3 -5
- package/esm/FunnelChart/curves/bump.js +13 -13
- package/esm/FunnelChart/curves/curve.types.d.ts +14 -1
- package/esm/FunnelChart/curves/getFunnelCurve.d.ts +7 -2
- package/esm/FunnelChart/curves/linear.d.ts +4 -4
- package/esm/FunnelChart/curves/linear.js +42 -34
- package/esm/FunnelChart/curves/pyramid.d.ts +3 -3
- package/esm/FunnelChart/curves/pyramid.js +25 -24
- package/esm/FunnelChart/curves/step-pyramid.d.ts +5 -5
- package/esm/FunnelChart/curves/step-pyramid.js +24 -24
- package/esm/FunnelChart/curves/step.d.ts +3 -3
- package/esm/FunnelChart/curves/step.js +14 -26
- package/esm/FunnelChart/funnelAxisPlugin/computeAxisValue.d.ts +25 -0
- package/esm/FunnelChart/funnelAxisPlugin/computeAxisValue.js +114 -0
- package/esm/FunnelChart/funnelAxisPlugin/useChartFunnelAxis.d.ts +3 -0
- package/esm/FunnelChart/funnelAxisPlugin/useChartFunnelAxis.js +165 -0
- package/esm/FunnelChart/funnelAxisPlugin/useChartFunnelAxis.types.d.ts +27 -0
- package/esm/FunnelChart/funnelAxisPlugin/useChartFunnelAxis.types.js +1 -0
- package/esm/FunnelChart/funnelAxisPlugin/useChartFunnelAxisRendering.selectors.d.ts +56 -0
- package/esm/FunnelChart/funnelAxisPlugin/useChartFunnelAxisRendering.selectors.js +20 -0
- package/esm/FunnelChart/labelUtils.d.ts +4 -11
- package/esm/FunnelChart/labelUtils.js +43 -44
- package/esm/FunnelChart/useFunnelChartProps.js +1 -3
- package/esm/Heatmap/Heatmap.d.ts +3 -3
- package/esm/LineChartPro/LineChartPro.d.ts +3 -2
- package/esm/PieChartPro/PieChartPro.d.ts +3 -2
- package/esm/RadarChartPro/RadarChartPro.d.ts +17 -2
- package/esm/ScatterChartPro/ScatterChartPro.d.ts +3 -2
- package/esm/index.js +1 -1
- package/esm/internals/plugins/useChartProZoom/useChartProZoom.selectors.d.ts +16 -595
- package/esm/internals/plugins/useChartProZoom/useChartProZoom.selectors.js +2 -2
- package/index.js +1 -1
- package/internals/plugins/useChartProZoom/useChartProZoom.selectors.d.ts +16 -595
- package/internals/plugins/useChartProZoom/useChartProZoom.selectors.js +2 -2
- 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"
|
|
23
|
+
const _excluded = ["onItemClick"];
|
|
24
24
|
_internals.cartesianSeriesTypes.addType('funnel');
|
|
25
|
-
const useAggregatedData =
|
|
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,
|
|
29
|
+
axis: xAxis,
|
|
30
|
+
axisIds: xAxisIds
|
|
31
|
+
} = (0, _internals.useSelector)(store, _useChartFunnelAxisRendering.selectorChartXAxis);
|
|
31
32
|
const {
|
|
32
|
-
yAxis,
|
|
33
|
-
yAxisIds
|
|
34
|
-
} = (0,
|
|
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 (
|
|
59
|
-
const position = xScale(
|
|
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
|
-
|
|
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 (
|
|
66
|
-
const position = yScale(
|
|
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
|
-
|
|
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
|
|
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(
|
|
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
|
|
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(
|
|
140
|
+
const data = useAggregatedData();
|
|
134
141
|
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(React.Fragment, {
|
|
135
|
-
children: [data.map(
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
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
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
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)(
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
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
|
|
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 {
|
|
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
|
|
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
|
|
83
|
-
this.context.lineTo(p0.x
|
|
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
|
|
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
|
|
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
|
|
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
|
|
100
|
-
this.context.lineTo(p0.x, p0.y
|
|
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
|
|
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
|
|
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
|
|
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 {
|
|
1
|
+
import type { Path } from '@mui/x-charts-vendor/d3-path';
|
|
2
2
|
import { CurveOptions, FunnelCurveType } from "./curve.types.js";
|
|
3
|
-
|
|
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 {
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
121
|
-
const slopeStart =
|
|
122
|
-
const slopeEnd =
|
|
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
|
|
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:
|
|
128
|
-
y: yGetter(
|
|
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
|
|
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(
|
|
135
|
-
y:
|
|
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
|
-
|
|
145
|
-
|
|
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
|
-
|
|
154
|
-
|
|
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
|
|
159
|
-
},
|
|
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 {
|
|
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
|
|
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
|
-
|
|
97
|
-
|
|
98
|
-
|
|
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:
|
|
120
|
-
y: yGetter(
|
|
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(
|
|
135
|
-
y:
|
|
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.
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
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 {
|
|
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
|
|
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
|
}
|