@mui/x-charts 7.3.2 → 7.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/BarChart/BarChart.js +7 -1
- package/BarChart/BarClipPath.d.ts +14 -0
- package/BarChart/BarClipPath.js +50 -0
- package/BarChart/BarPlot.d.ts +4 -0
- package/BarChart/BarPlot.js +102 -30
- package/BarChart/getRadius.d.ts +15 -0
- package/BarChart/getRadius.js +37 -0
- package/BarChart/types.d.ts +25 -0
- package/BarChart/types.js +5 -0
- package/CHANGELOG.md +67 -2
- package/ChartsGrid/ChartsGrid.js +22 -11
- package/esm/BarChart/BarChart.js +7 -1
- package/esm/BarChart/BarClipPath.js +42 -0
- package/esm/BarChart/BarPlot.js +103 -31
- package/esm/BarChart/getRadius.js +30 -0
- package/esm/BarChart/types.js +1 -0
- package/esm/ChartsGrid/ChartsGrid.js +23 -12
- package/index.js +1 -1
- package/modern/BarChart/BarChart.js +7 -1
- package/modern/BarChart/BarClipPath.js +42 -0
- package/modern/BarChart/BarPlot.js +103 -31
- package/modern/BarChart/getRadius.js +30 -0
- package/modern/BarChart/types.js +1 -0
- package/modern/ChartsGrid/ChartsGrid.js +23 -12
- package/modern/index.js +1 -1
- package/package.json +2 -2
- package/themeAugmentation/components.d.ts +4 -0
- package/themeAugmentation/overrides.d.ts +2 -0
- package/themeAugmentation/props.d.ts +2 -0
package/esm/BarChart/BarPlot.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/esm/objectWithoutPropertiesLoose";
|
|
2
2
|
import _extends from "@babel/runtime/helpers/esm/extends";
|
|
3
|
-
const _excluded = ["skipAnimation", "onItemClick"];
|
|
3
|
+
const _excluded = ["skipAnimation", "onItemClick", "borderRadius"];
|
|
4
4
|
import * as React from 'react';
|
|
5
5
|
import PropTypes from 'prop-types';
|
|
6
6
|
import { useTransition } from '@react-spring/web';
|
|
@@ -10,6 +10,8 @@ import { BarElement } from './BarElement';
|
|
|
10
10
|
import { isBandScaleConfig, isPointScaleConfig } from '../models/axis';
|
|
11
11
|
import { DEFAULT_X_AXIS_KEY, DEFAULT_Y_AXIS_KEY } from '../constants';
|
|
12
12
|
import getColor from './getColor';
|
|
13
|
+
import { useChartId } from '../hooks';
|
|
14
|
+
import { BarClipPath } from './BarClipPath';
|
|
13
15
|
|
|
14
16
|
/**
|
|
15
17
|
* Solution of the equations
|
|
@@ -20,7 +22,7 @@ import getColor from './getColor';
|
|
|
20
22
|
* @param gapRatio The ratio of the gap between bars over the bar width.
|
|
21
23
|
* @returns The bar width and the offset between bars.
|
|
22
24
|
*/
|
|
23
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
25
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
24
26
|
function getBandSize({
|
|
25
27
|
bandWidth: W,
|
|
26
28
|
numberOfGroups: N,
|
|
@@ -46,6 +48,7 @@ const useAggregatedData = () => {
|
|
|
46
48
|
seriesOrder: []
|
|
47
49
|
};
|
|
48
50
|
const axisData = React.useContext(CartesianContext);
|
|
51
|
+
const chartId = useChartId();
|
|
49
52
|
const {
|
|
50
53
|
series,
|
|
51
54
|
stackingGroups
|
|
@@ -58,6 +61,7 @@ const useAggregatedData = () => {
|
|
|
58
61
|
} = axisData;
|
|
59
62
|
const defaultXAxisId = xAxisIds[0];
|
|
60
63
|
const defaultYAxisId = yAxisIds[0];
|
|
64
|
+
const masks = {};
|
|
61
65
|
const data = stackingGroups.flatMap(({
|
|
62
66
|
ids: groupIds
|
|
63
67
|
}, groupIndex) => {
|
|
@@ -111,7 +115,8 @@ const useAggregatedData = () => {
|
|
|
111
115
|
const valueCoordinates = values.map(v => verticalLayout ? yScale(v) : xScale(v));
|
|
112
116
|
const minValueCoord = Math.round(Math.min(...valueCoordinates));
|
|
113
117
|
const maxValueCoord = Math.round(Math.max(...valueCoordinates));
|
|
114
|
-
|
|
118
|
+
const stackId = series[seriesId].stack;
|
|
119
|
+
const result = {
|
|
115
120
|
seriesId,
|
|
116
121
|
dataIndex,
|
|
117
122
|
layout: series[seriesId].layout,
|
|
@@ -122,14 +127,41 @@ const useAggregatedData = () => {
|
|
|
122
127
|
height: verticalLayout ? maxValueCoord - minValueCoord : barWidth,
|
|
123
128
|
width: verticalLayout ? barWidth : maxValueCoord - minValueCoord,
|
|
124
129
|
color: colorGetter(dataIndex),
|
|
125
|
-
highlightScope: series[seriesId].highlightScope
|
|
130
|
+
highlightScope: series[seriesId].highlightScope,
|
|
131
|
+
value: series[seriesId].data[dataIndex],
|
|
132
|
+
maskId: `${chartId}_${stackId || seriesId}_${groupIndex}_${dataIndex}`
|
|
126
133
|
};
|
|
134
|
+
if (!masks[result.maskId]) {
|
|
135
|
+
masks[result.maskId] = {
|
|
136
|
+
id: result.maskId,
|
|
137
|
+
width: 0,
|
|
138
|
+
height: 0,
|
|
139
|
+
hasNegative: false,
|
|
140
|
+
hasPositive: false,
|
|
141
|
+
layout: result.layout,
|
|
142
|
+
xOrigin: xScale(0),
|
|
143
|
+
yOrigin: yScale(0),
|
|
144
|
+
x: 0,
|
|
145
|
+
y: 0
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
const mask = masks[result.maskId];
|
|
149
|
+
mask.width = result.layout === 'vertical' ? result.width : mask.width + result.width;
|
|
150
|
+
mask.height = result.layout === 'vertical' ? mask.height + result.height : result.height;
|
|
151
|
+
mask.x = Math.min(mask.x === 0 ? Infinity : mask.x, result.x);
|
|
152
|
+
mask.y = Math.min(mask.y === 0 ? Infinity : mask.y, result.y);
|
|
153
|
+
mask.hasNegative = mask.hasNegative || (result.value ?? 0) < 0;
|
|
154
|
+
mask.hasPositive = mask.hasPositive || (result.value ?? 0) > 0;
|
|
155
|
+
return result;
|
|
127
156
|
});
|
|
128
157
|
});
|
|
129
158
|
});
|
|
130
|
-
return
|
|
159
|
+
return {
|
|
160
|
+
completedData: data,
|
|
161
|
+
masksData: Object.values(masks)
|
|
162
|
+
};
|
|
131
163
|
};
|
|
132
|
-
const
|
|
164
|
+
const leaveStyle = ({
|
|
133
165
|
layout,
|
|
134
166
|
yOrigin,
|
|
135
167
|
x,
|
|
@@ -148,7 +180,7 @@ const getOutStyle = ({
|
|
|
148
180
|
height,
|
|
149
181
|
width: 0
|
|
150
182
|
});
|
|
151
|
-
const
|
|
183
|
+
const enterStyle = ({
|
|
152
184
|
x,
|
|
153
185
|
width,
|
|
154
186
|
y,
|
|
@@ -172,41 +204,77 @@ const getInStyle = ({
|
|
|
172
204
|
* - [BarPlot API](https://mui.com/x/api/charts/bar-plot/)
|
|
173
205
|
*/
|
|
174
206
|
function BarPlot(props) {
|
|
175
|
-
const
|
|
207
|
+
const {
|
|
208
|
+
completedData,
|
|
209
|
+
masksData
|
|
210
|
+
} = useAggregatedData();
|
|
176
211
|
const {
|
|
177
212
|
skipAnimation,
|
|
178
|
-
onItemClick
|
|
213
|
+
onItemClick,
|
|
214
|
+
borderRadius
|
|
179
215
|
} = props,
|
|
180
216
|
other = _objectWithoutPropertiesLoose(props, _excluded);
|
|
181
217
|
const transition = useTransition(completedData, {
|
|
182
218
|
keys: bar => `${bar.seriesId}-${bar.dataIndex}`,
|
|
183
|
-
from:
|
|
184
|
-
leave:
|
|
185
|
-
enter:
|
|
186
|
-
update:
|
|
219
|
+
from: leaveStyle,
|
|
220
|
+
leave: leaveStyle,
|
|
221
|
+
enter: enterStyle,
|
|
222
|
+
update: enterStyle,
|
|
223
|
+
immediate: skipAnimation
|
|
224
|
+
});
|
|
225
|
+
const maskTransition = useTransition(masksData, {
|
|
226
|
+
keys: v => v.id,
|
|
227
|
+
from: leaveStyle,
|
|
228
|
+
leave: leaveStyle,
|
|
229
|
+
enter: enterStyle,
|
|
230
|
+
update: enterStyle,
|
|
187
231
|
immediate: skipAnimation
|
|
188
232
|
});
|
|
189
|
-
return /*#__PURE__*/
|
|
190
|
-
children:
|
|
233
|
+
return /*#__PURE__*/_jsxs(React.Fragment, {
|
|
234
|
+
children: [maskTransition((style, {
|
|
235
|
+
id,
|
|
236
|
+
hasPositive,
|
|
237
|
+
hasNegative,
|
|
238
|
+
layout
|
|
239
|
+
}) => {
|
|
240
|
+
return /*#__PURE__*/_jsx(BarClipPath, {
|
|
241
|
+
maskId: id,
|
|
242
|
+
borderRadius: borderRadius,
|
|
243
|
+
hasNegative: hasNegative,
|
|
244
|
+
hasPositive: hasPositive,
|
|
245
|
+
layout: layout,
|
|
246
|
+
style: style
|
|
247
|
+
});
|
|
248
|
+
}), transition((style, {
|
|
191
249
|
seriesId,
|
|
192
250
|
dataIndex,
|
|
193
251
|
color,
|
|
194
|
-
highlightScope
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
252
|
+
highlightScope,
|
|
253
|
+
maskId
|
|
254
|
+
}) => {
|
|
255
|
+
const barElement = /*#__PURE__*/_jsx(BarElement, _extends({
|
|
256
|
+
id: seriesId,
|
|
257
|
+
dataIndex: dataIndex,
|
|
258
|
+
color: color,
|
|
259
|
+
highlightScope: highlightScope
|
|
260
|
+
}, other, {
|
|
261
|
+
onClick: onItemClick && (event => {
|
|
262
|
+
onItemClick(event, {
|
|
263
|
+
type: 'bar',
|
|
264
|
+
seriesId,
|
|
265
|
+
dataIndex
|
|
266
|
+
});
|
|
267
|
+
}),
|
|
268
|
+
style: style
|
|
269
|
+
}));
|
|
270
|
+
if (!borderRadius || borderRadius <= 0) {
|
|
271
|
+
return barElement;
|
|
272
|
+
}
|
|
273
|
+
return /*#__PURE__*/_jsx("g", {
|
|
274
|
+
clipPath: `url(#${maskId})`,
|
|
275
|
+
children: barElement
|
|
276
|
+
});
|
|
277
|
+
})]
|
|
210
278
|
});
|
|
211
279
|
}
|
|
212
280
|
process.env.NODE_ENV !== "production" ? BarPlot.propTypes = {
|
|
@@ -214,6 +282,10 @@ process.env.NODE_ENV !== "production" ? BarPlot.propTypes = {
|
|
|
214
282
|
// | These PropTypes are generated from the TypeScript type definitions |
|
|
215
283
|
// | To update them edit the TypeScript types and run "yarn proptypes" |
|
|
216
284
|
// ----------------------------------------------------------------------
|
|
285
|
+
/**
|
|
286
|
+
* Defines the border radius of the bar element.
|
|
287
|
+
*/
|
|
288
|
+
borderRadius: PropTypes.number,
|
|
217
289
|
/**
|
|
218
290
|
* Callback fired when a bar item is clicked.
|
|
219
291
|
* @param {React.MouseEvent<SVGElement, MouseEvent>} event The event source of the callback.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns if the corner should have a radius or not based on the layout and the data.
|
|
3
|
+
* @param {GetRadiusCorner} corner The corner to check.
|
|
4
|
+
* @param {GetRadiusData} cornerData The data for the corner.
|
|
5
|
+
* @returns {number} The radius for the corner.
|
|
6
|
+
*/
|
|
7
|
+
export const getRadius = (corner, {
|
|
8
|
+
hasNegative,
|
|
9
|
+
hasPositive,
|
|
10
|
+
borderRadius,
|
|
11
|
+
layout
|
|
12
|
+
}) => {
|
|
13
|
+
if (!borderRadius) {
|
|
14
|
+
return 0;
|
|
15
|
+
}
|
|
16
|
+
const isVertical = layout === 'vertical';
|
|
17
|
+
if (corner === 'top-left' && (isVertical && hasPositive || !isVertical && hasNegative)) {
|
|
18
|
+
return borderRadius;
|
|
19
|
+
}
|
|
20
|
+
if (corner === 'top-right' && (isVertical && hasPositive || !isVertical && hasPositive)) {
|
|
21
|
+
return borderRadius;
|
|
22
|
+
}
|
|
23
|
+
if (corner === 'bottom-right' && (isVertical && hasNegative || !isVertical && hasPositive)) {
|
|
24
|
+
return borderRadius;
|
|
25
|
+
}
|
|
26
|
+
if (corner === 'bottom-left' && (isVertical && hasNegative || !isVertical && hasNegative)) {
|
|
27
|
+
return borderRadius;
|
|
28
|
+
}
|
|
29
|
+
return 0;
|
|
30
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -4,7 +4,7 @@ const _excluded = ["vertical", "horizontal"];
|
|
|
4
4
|
import * as React from 'react';
|
|
5
5
|
import PropTypes from 'prop-types';
|
|
6
6
|
import composeClasses from '@mui/utils/composeClasses';
|
|
7
|
-
import { styled } from '@mui/material/styles';
|
|
7
|
+
import { styled, useThemeProps } from '@mui/material/styles';
|
|
8
8
|
import { CartesianContext } from '../context/CartesianContextProvider';
|
|
9
9
|
import { useTicks } from '../hooks/useTicks';
|
|
10
10
|
import { getChartsGridUtilityClass, chartsGridClasses } from './chartsGridClasses';
|
|
@@ -12,15 +12,22 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
12
12
|
const GridRoot = styled('g', {
|
|
13
13
|
name: 'MuiChartsGrid',
|
|
14
14
|
slot: 'Root',
|
|
15
|
-
overridesResolver: (props, styles) =>
|
|
15
|
+
overridesResolver: (props, styles) => [{
|
|
16
|
+
[`&.${chartsGridClasses.verticalLine}`]: styles.verticalLine
|
|
17
|
+
}, {
|
|
18
|
+
[`&.${chartsGridClasses.horizontalLine}`]: styles.horizontalLine
|
|
19
|
+
}, styles.root]
|
|
20
|
+
})({});
|
|
21
|
+
const GridLine = styled('line', {
|
|
22
|
+
name: 'MuiChartsGrid',
|
|
23
|
+
slot: 'Line',
|
|
24
|
+
overridesResolver: (props, styles) => styles.line
|
|
16
25
|
})(({
|
|
17
26
|
theme
|
|
18
27
|
}) => ({
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
strokeWidth: 1
|
|
23
|
-
}
|
|
28
|
+
stroke: (theme.vars || theme).palette.divider,
|
|
29
|
+
shapeRendering: 'crispEdges',
|
|
30
|
+
strokeWidth: 1
|
|
24
31
|
}));
|
|
25
32
|
const useUtilityClasses = ({
|
|
26
33
|
classes
|
|
@@ -42,18 +49,22 @@ const useUtilityClasses = ({
|
|
|
42
49
|
* - [ChartsGrid API](https://mui.com/x/api/charts/charts-axis/)
|
|
43
50
|
*/
|
|
44
51
|
function ChartsGrid(props) {
|
|
52
|
+
const themeProps = useThemeProps({
|
|
53
|
+
props,
|
|
54
|
+
name: 'MuiChartsGrid'
|
|
55
|
+
});
|
|
45
56
|
const {
|
|
46
57
|
vertical,
|
|
47
58
|
horizontal
|
|
48
|
-
} =
|
|
49
|
-
other = _objectWithoutPropertiesLoose(
|
|
59
|
+
} = themeProps,
|
|
60
|
+
other = _objectWithoutPropertiesLoose(themeProps, _excluded);
|
|
50
61
|
const {
|
|
51
62
|
xAxis,
|
|
52
63
|
xAxisIds,
|
|
53
64
|
yAxis,
|
|
54
65
|
yAxisIds
|
|
55
66
|
} = React.useContext(CartesianContext);
|
|
56
|
-
const classes = useUtilityClasses(
|
|
67
|
+
const classes = useUtilityClasses(themeProps);
|
|
57
68
|
const horizontalAxisId = yAxisIds[0];
|
|
58
69
|
const verticalAxisId = xAxisIds[0];
|
|
59
70
|
const {
|
|
@@ -81,7 +92,7 @@ function ChartsGrid(props) {
|
|
|
81
92
|
children: [vertical && xTicks.map(({
|
|
82
93
|
formattedValue,
|
|
83
94
|
offset
|
|
84
|
-
}) => /*#__PURE__*/_jsx(
|
|
95
|
+
}) => /*#__PURE__*/_jsx(GridLine, {
|
|
85
96
|
y1: yScale.range()[0],
|
|
86
97
|
y2: yScale.range()[1],
|
|
87
98
|
x1: offset,
|
|
@@ -90,7 +101,7 @@ function ChartsGrid(props) {
|
|
|
90
101
|
}, `vertical-${formattedValue}`)), horizontal && yTicks.map(({
|
|
91
102
|
formattedValue,
|
|
92
103
|
offset
|
|
93
|
-
}) => /*#__PURE__*/_jsx(
|
|
104
|
+
}) => /*#__PURE__*/_jsx(GridLine, {
|
|
94
105
|
y1: offset,
|
|
95
106
|
y2: offset,
|
|
96
107
|
x1: xScale.range()[0],
|
package/index.js
CHANGED
|
@@ -46,6 +46,7 @@ const BarChart = /*#__PURE__*/React.forwardRef(function BarChart(props, ref) {
|
|
|
46
46
|
rightAxis,
|
|
47
47
|
bottomAxis,
|
|
48
48
|
skipAnimation,
|
|
49
|
+
borderRadius,
|
|
49
50
|
onItemClick,
|
|
50
51
|
onAxisClick,
|
|
51
52
|
children,
|
|
@@ -98,7 +99,8 @@ const BarChart = /*#__PURE__*/React.forwardRef(function BarChart(props, ref) {
|
|
|
98
99
|
slots: slots,
|
|
99
100
|
slotProps: slotProps,
|
|
100
101
|
skipAnimation: skipAnimation,
|
|
101
|
-
onItemClick: onItemClick
|
|
102
|
+
onItemClick: onItemClick,
|
|
103
|
+
borderRadius: borderRadius
|
|
102
104
|
}), /*#__PURE__*/_jsx(ChartsOverlay, {
|
|
103
105
|
loading: loading,
|
|
104
106
|
slots: slots,
|
|
@@ -137,6 +139,10 @@ process.env.NODE_ENV !== "production" ? BarChart.propTypes = {
|
|
|
137
139
|
x: PropTypes.oneOf(['band', 'line', 'none']),
|
|
138
140
|
y: PropTypes.oneOf(['band', 'line', 'none'])
|
|
139
141
|
}),
|
|
142
|
+
/**
|
|
143
|
+
* Defines the border radius of the bar element.
|
|
144
|
+
*/
|
|
145
|
+
borderRadius: PropTypes.number,
|
|
140
146
|
/**
|
|
141
147
|
* Indicate which axis to display the bottom of the charts.
|
|
142
148
|
* Can be a string (the id of the axis) or an object `ChartsXAxisProps`.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/esm/objectWithoutPropertiesLoose";
|
|
2
|
+
import _extends from "@babel/runtime/helpers/esm/extends";
|
|
3
|
+
const _excluded = ["style", "maskId"];
|
|
4
|
+
import * as React from 'react';
|
|
5
|
+
import { animated } from '@react-spring/web';
|
|
6
|
+
import { getRadius } from './getRadius';
|
|
7
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
8
|
+
const buildInset = corners => `inset(0px round ${corners.topLeft}px ${corners.topRight}px ${corners.bottomRight}px ${corners.bottomLeft}px)`;
|
|
9
|
+
function BarClipRect(props) {
|
|
10
|
+
const radiusData = props.ownerState;
|
|
11
|
+
return /*#__PURE__*/_jsx(animated.rect, {
|
|
12
|
+
style: _extends({}, props.style, {
|
|
13
|
+
clipPath: (props.ownerState.layout === 'vertical' ? props.style?.height : props.style?.width).to(value => buildInset({
|
|
14
|
+
topLeft: Math.min(value, getRadius('top-left', radiusData)),
|
|
15
|
+
topRight: Math.min(value, getRadius('top-right', radiusData)),
|
|
16
|
+
bottomRight: Math.min(value, getRadius('bottom-right', radiusData)),
|
|
17
|
+
bottomLeft: Math.min(value, getRadius('bottom-left', radiusData))
|
|
18
|
+
}))
|
|
19
|
+
})
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* @ignore - internal component.
|
|
24
|
+
*/
|
|
25
|
+
function BarClipPath(props) {
|
|
26
|
+
const {
|
|
27
|
+
style,
|
|
28
|
+
maskId
|
|
29
|
+
} = props,
|
|
30
|
+
rest = _objectWithoutPropertiesLoose(props, _excluded);
|
|
31
|
+
if (!props.borderRadius || props.borderRadius <= 0) {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
return /*#__PURE__*/_jsx("clipPath", {
|
|
35
|
+
id: maskId,
|
|
36
|
+
children: /*#__PURE__*/_jsx(BarClipRect, {
|
|
37
|
+
ownerState: rest,
|
|
38
|
+
style: style
|
|
39
|
+
})
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
export { BarClipPath };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/esm/objectWithoutPropertiesLoose";
|
|
2
2
|
import _extends from "@babel/runtime/helpers/esm/extends";
|
|
3
|
-
const _excluded = ["skipAnimation", "onItemClick"];
|
|
3
|
+
const _excluded = ["skipAnimation", "onItemClick", "borderRadius"];
|
|
4
4
|
import * as React from 'react';
|
|
5
5
|
import PropTypes from 'prop-types';
|
|
6
6
|
import { useTransition } from '@react-spring/web';
|
|
@@ -10,6 +10,8 @@ import { BarElement } from './BarElement';
|
|
|
10
10
|
import { isBandScaleConfig, isPointScaleConfig } from '../models/axis';
|
|
11
11
|
import { DEFAULT_X_AXIS_KEY, DEFAULT_Y_AXIS_KEY } from '../constants';
|
|
12
12
|
import getColor from './getColor';
|
|
13
|
+
import { useChartId } from '../hooks';
|
|
14
|
+
import { BarClipPath } from './BarClipPath';
|
|
13
15
|
|
|
14
16
|
/**
|
|
15
17
|
* Solution of the equations
|
|
@@ -20,7 +22,7 @@ import getColor from './getColor';
|
|
|
20
22
|
* @param gapRatio The ratio of the gap between bars over the bar width.
|
|
21
23
|
* @returns The bar width and the offset between bars.
|
|
22
24
|
*/
|
|
23
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
25
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
24
26
|
function getBandSize({
|
|
25
27
|
bandWidth: W,
|
|
26
28
|
numberOfGroups: N,
|
|
@@ -46,6 +48,7 @@ const useAggregatedData = () => {
|
|
|
46
48
|
seriesOrder: []
|
|
47
49
|
};
|
|
48
50
|
const axisData = React.useContext(CartesianContext);
|
|
51
|
+
const chartId = useChartId();
|
|
49
52
|
const {
|
|
50
53
|
series,
|
|
51
54
|
stackingGroups
|
|
@@ -58,6 +61,7 @@ const useAggregatedData = () => {
|
|
|
58
61
|
} = axisData;
|
|
59
62
|
const defaultXAxisId = xAxisIds[0];
|
|
60
63
|
const defaultYAxisId = yAxisIds[0];
|
|
64
|
+
const masks = {};
|
|
61
65
|
const data = stackingGroups.flatMap(({
|
|
62
66
|
ids: groupIds
|
|
63
67
|
}, groupIndex) => {
|
|
@@ -111,7 +115,8 @@ const useAggregatedData = () => {
|
|
|
111
115
|
const valueCoordinates = values.map(v => verticalLayout ? yScale(v) : xScale(v));
|
|
112
116
|
const minValueCoord = Math.round(Math.min(...valueCoordinates));
|
|
113
117
|
const maxValueCoord = Math.round(Math.max(...valueCoordinates));
|
|
114
|
-
|
|
118
|
+
const stackId = series[seriesId].stack;
|
|
119
|
+
const result = {
|
|
115
120
|
seriesId,
|
|
116
121
|
dataIndex,
|
|
117
122
|
layout: series[seriesId].layout,
|
|
@@ -122,14 +127,41 @@ const useAggregatedData = () => {
|
|
|
122
127
|
height: verticalLayout ? maxValueCoord - minValueCoord : barWidth,
|
|
123
128
|
width: verticalLayout ? barWidth : maxValueCoord - minValueCoord,
|
|
124
129
|
color: colorGetter(dataIndex),
|
|
125
|
-
highlightScope: series[seriesId].highlightScope
|
|
130
|
+
highlightScope: series[seriesId].highlightScope,
|
|
131
|
+
value: series[seriesId].data[dataIndex],
|
|
132
|
+
maskId: `${chartId}_${stackId || seriesId}_${groupIndex}_${dataIndex}`
|
|
126
133
|
};
|
|
134
|
+
if (!masks[result.maskId]) {
|
|
135
|
+
masks[result.maskId] = {
|
|
136
|
+
id: result.maskId,
|
|
137
|
+
width: 0,
|
|
138
|
+
height: 0,
|
|
139
|
+
hasNegative: false,
|
|
140
|
+
hasPositive: false,
|
|
141
|
+
layout: result.layout,
|
|
142
|
+
xOrigin: xScale(0),
|
|
143
|
+
yOrigin: yScale(0),
|
|
144
|
+
x: 0,
|
|
145
|
+
y: 0
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
const mask = masks[result.maskId];
|
|
149
|
+
mask.width = result.layout === 'vertical' ? result.width : mask.width + result.width;
|
|
150
|
+
mask.height = result.layout === 'vertical' ? mask.height + result.height : result.height;
|
|
151
|
+
mask.x = Math.min(mask.x === 0 ? Infinity : mask.x, result.x);
|
|
152
|
+
mask.y = Math.min(mask.y === 0 ? Infinity : mask.y, result.y);
|
|
153
|
+
mask.hasNegative = mask.hasNegative || (result.value ?? 0) < 0;
|
|
154
|
+
mask.hasPositive = mask.hasPositive || (result.value ?? 0) > 0;
|
|
155
|
+
return result;
|
|
127
156
|
});
|
|
128
157
|
});
|
|
129
158
|
});
|
|
130
|
-
return
|
|
159
|
+
return {
|
|
160
|
+
completedData: data,
|
|
161
|
+
masksData: Object.values(masks)
|
|
162
|
+
};
|
|
131
163
|
};
|
|
132
|
-
const
|
|
164
|
+
const leaveStyle = ({
|
|
133
165
|
layout,
|
|
134
166
|
yOrigin,
|
|
135
167
|
x,
|
|
@@ -148,7 +180,7 @@ const getOutStyle = ({
|
|
|
148
180
|
height,
|
|
149
181
|
width: 0
|
|
150
182
|
});
|
|
151
|
-
const
|
|
183
|
+
const enterStyle = ({
|
|
152
184
|
x,
|
|
153
185
|
width,
|
|
154
186
|
y,
|
|
@@ -172,41 +204,77 @@ const getInStyle = ({
|
|
|
172
204
|
* - [BarPlot API](https://mui.com/x/api/charts/bar-plot/)
|
|
173
205
|
*/
|
|
174
206
|
function BarPlot(props) {
|
|
175
|
-
const
|
|
207
|
+
const {
|
|
208
|
+
completedData,
|
|
209
|
+
masksData
|
|
210
|
+
} = useAggregatedData();
|
|
176
211
|
const {
|
|
177
212
|
skipAnimation,
|
|
178
|
-
onItemClick
|
|
213
|
+
onItemClick,
|
|
214
|
+
borderRadius
|
|
179
215
|
} = props,
|
|
180
216
|
other = _objectWithoutPropertiesLoose(props, _excluded);
|
|
181
217
|
const transition = useTransition(completedData, {
|
|
182
218
|
keys: bar => `${bar.seriesId}-${bar.dataIndex}`,
|
|
183
|
-
from:
|
|
184
|
-
leave:
|
|
185
|
-
enter:
|
|
186
|
-
update:
|
|
219
|
+
from: leaveStyle,
|
|
220
|
+
leave: leaveStyle,
|
|
221
|
+
enter: enterStyle,
|
|
222
|
+
update: enterStyle,
|
|
223
|
+
immediate: skipAnimation
|
|
224
|
+
});
|
|
225
|
+
const maskTransition = useTransition(masksData, {
|
|
226
|
+
keys: v => v.id,
|
|
227
|
+
from: leaveStyle,
|
|
228
|
+
leave: leaveStyle,
|
|
229
|
+
enter: enterStyle,
|
|
230
|
+
update: enterStyle,
|
|
187
231
|
immediate: skipAnimation
|
|
188
232
|
});
|
|
189
|
-
return /*#__PURE__*/
|
|
190
|
-
children:
|
|
233
|
+
return /*#__PURE__*/_jsxs(React.Fragment, {
|
|
234
|
+
children: [maskTransition((style, {
|
|
235
|
+
id,
|
|
236
|
+
hasPositive,
|
|
237
|
+
hasNegative,
|
|
238
|
+
layout
|
|
239
|
+
}) => {
|
|
240
|
+
return /*#__PURE__*/_jsx(BarClipPath, {
|
|
241
|
+
maskId: id,
|
|
242
|
+
borderRadius: borderRadius,
|
|
243
|
+
hasNegative: hasNegative,
|
|
244
|
+
hasPositive: hasPositive,
|
|
245
|
+
layout: layout,
|
|
246
|
+
style: style
|
|
247
|
+
});
|
|
248
|
+
}), transition((style, {
|
|
191
249
|
seriesId,
|
|
192
250
|
dataIndex,
|
|
193
251
|
color,
|
|
194
|
-
highlightScope
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
252
|
+
highlightScope,
|
|
253
|
+
maskId
|
|
254
|
+
}) => {
|
|
255
|
+
const barElement = /*#__PURE__*/_jsx(BarElement, _extends({
|
|
256
|
+
id: seriesId,
|
|
257
|
+
dataIndex: dataIndex,
|
|
258
|
+
color: color,
|
|
259
|
+
highlightScope: highlightScope
|
|
260
|
+
}, other, {
|
|
261
|
+
onClick: onItemClick && (event => {
|
|
262
|
+
onItemClick(event, {
|
|
263
|
+
type: 'bar',
|
|
264
|
+
seriesId,
|
|
265
|
+
dataIndex
|
|
266
|
+
});
|
|
267
|
+
}),
|
|
268
|
+
style: style
|
|
269
|
+
}));
|
|
270
|
+
if (!borderRadius || borderRadius <= 0) {
|
|
271
|
+
return barElement;
|
|
272
|
+
}
|
|
273
|
+
return /*#__PURE__*/_jsx("g", {
|
|
274
|
+
clipPath: `url(#${maskId})`,
|
|
275
|
+
children: barElement
|
|
276
|
+
});
|
|
277
|
+
})]
|
|
210
278
|
});
|
|
211
279
|
}
|
|
212
280
|
process.env.NODE_ENV !== "production" ? BarPlot.propTypes = {
|
|
@@ -214,6 +282,10 @@ process.env.NODE_ENV !== "production" ? BarPlot.propTypes = {
|
|
|
214
282
|
// | These PropTypes are generated from the TypeScript type definitions |
|
|
215
283
|
// | To update them edit the TypeScript types and run "yarn proptypes" |
|
|
216
284
|
// ----------------------------------------------------------------------
|
|
285
|
+
/**
|
|
286
|
+
* Defines the border radius of the bar element.
|
|
287
|
+
*/
|
|
288
|
+
borderRadius: PropTypes.number,
|
|
217
289
|
/**
|
|
218
290
|
* Callback fired when a bar item is clicked.
|
|
219
291
|
* @param {React.MouseEvent<SVGElement, MouseEvent>} event The event source of the callback.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns if the corner should have a radius or not based on the layout and the data.
|
|
3
|
+
* @param {GetRadiusCorner} corner The corner to check.
|
|
4
|
+
* @param {GetRadiusData} cornerData The data for the corner.
|
|
5
|
+
* @returns {number} The radius for the corner.
|
|
6
|
+
*/
|
|
7
|
+
export const getRadius = (corner, {
|
|
8
|
+
hasNegative,
|
|
9
|
+
hasPositive,
|
|
10
|
+
borderRadius,
|
|
11
|
+
layout
|
|
12
|
+
}) => {
|
|
13
|
+
if (!borderRadius) {
|
|
14
|
+
return 0;
|
|
15
|
+
}
|
|
16
|
+
const isVertical = layout === 'vertical';
|
|
17
|
+
if (corner === 'top-left' && (isVertical && hasPositive || !isVertical && hasNegative)) {
|
|
18
|
+
return borderRadius;
|
|
19
|
+
}
|
|
20
|
+
if (corner === 'top-right' && (isVertical && hasPositive || !isVertical && hasPositive)) {
|
|
21
|
+
return borderRadius;
|
|
22
|
+
}
|
|
23
|
+
if (corner === 'bottom-right' && (isVertical && hasNegative || !isVertical && hasPositive)) {
|
|
24
|
+
return borderRadius;
|
|
25
|
+
}
|
|
26
|
+
if (corner === 'bottom-left' && (isVertical && hasNegative || !isVertical && hasNegative)) {
|
|
27
|
+
return borderRadius;
|
|
28
|
+
}
|
|
29
|
+
return 0;
|
|
30
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|