@mui/x-charts
Version:
The community edition of the Charts components (MUI X).
307 lines (305 loc) • 10.5 kB
JavaScript
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.BarPlot = BarPlot;
var _objectWithoutPropertiesLoose2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutPropertiesLoose"));
var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
var React = _interopRequireWildcard(require("react"));
var _propTypes = _interopRequireDefault(require("prop-types"));
var _web = require("@react-spring/web");
var _CartesianProvider = require("../context/CartesianProvider");
var _BarElement = require("./BarElement");
var _getColor = _interopRequireDefault(require("./getColor"));
var _hooks = require("../hooks");
var _BarClipPath = require("./BarClipPath");
var _BarLabelPlot = require("./BarLabel/BarLabelPlot");
var _checkScaleErrors = require("./checkScaleErrors");
var _useSeries = require("../hooks/useSeries");
var _jsxRuntime = require("react/jsx-runtime");
const _excluded = ["skipAnimation", "onItemClick", "borderRadius", "barLabel"];
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
/**
* Solution of the equations
* W = barWidth * N + offset * (N-1)
* offset / (offset + barWidth) = r
* @param bandWidth The width available to place bars.
* @param numberOfGroups The number of bars to place in that space.
* @param gapRatio The ratio of the gap between bars over the bar width.
* @returns The bar width and the offset between bars.
*/
function getBandSize({
bandWidth: W,
numberOfGroups: N,
gapRatio: r
}) {
if (r === 0) {
return {
barWidth: W / N,
offset: 0
};
}
const barWidth = W / (N + (N - 1) * r);
const offset = r * barWidth;
return {
barWidth,
offset
};
}
const useAggregatedData = () => {
const seriesData = (0, _useSeries.useBarSeries)() ?? {
series: {},
stackingGroups: [],
seriesOrder: []
};
const axisData = (0, _CartesianProvider.useCartesianContext)();
const chartId = (0, _hooks.useChartId)();
const {
series,
stackingGroups
} = seriesData;
const {
xAxis,
yAxis,
xAxisIds,
yAxisIds
} = axisData;
const defaultXAxisId = xAxisIds[0];
const defaultYAxisId = yAxisIds[0];
const masks = {};
const data = stackingGroups.flatMap(({
ids: groupIds
}, groupIndex) => {
return groupIds.flatMap(seriesId => {
const xAxisId = series[seriesId].xAxisId ?? series[seriesId].xAxisKey ?? defaultXAxisId;
const yAxisId = series[seriesId].yAxisId ?? series[seriesId].yAxisKey ?? defaultYAxisId;
const xAxisConfig = xAxis[xAxisId];
const yAxisConfig = yAxis[yAxisId];
const verticalLayout = series[seriesId].layout === 'vertical';
(0, _checkScaleErrors.checkScaleErrors)(verticalLayout, seriesId, xAxisId, xAxis, yAxisId, yAxis);
const baseScaleConfig = verticalLayout ? xAxisConfig : yAxisConfig;
const xScale = xAxisConfig.scale;
const yScale = yAxisConfig.scale;
const colorGetter = (0, _getColor.default)(series[seriesId], xAxis[xAxisId], yAxis[yAxisId]);
const bandWidth = baseScaleConfig.scale.bandwidth();
const {
barWidth,
offset
} = getBandSize({
bandWidth,
numberOfGroups: stackingGroups.length,
gapRatio: baseScaleConfig.barGapRatio
});
const barOffset = groupIndex * (barWidth + offset);
const {
stackedData
} = series[seriesId];
return stackedData.map((values, dataIndex) => {
const valueCoordinates = values.map(v => verticalLayout ? yScale(v) : xScale(v));
const minValueCoord = Math.round(Math.min(...valueCoordinates));
const maxValueCoord = Math.round(Math.max(...valueCoordinates));
const stackId = series[seriesId].stack;
const result = {
seriesId,
dataIndex,
layout: series[seriesId].layout,
x: verticalLayout ? xScale(xAxis[xAxisId].data?.[dataIndex]) + barOffset : minValueCoord,
y: verticalLayout ? minValueCoord : yScale(yAxis[yAxisId].data?.[dataIndex]) + barOffset,
xOrigin: xScale(0),
yOrigin: yScale(0),
height: verticalLayout ? maxValueCoord - minValueCoord : barWidth,
width: verticalLayout ? barWidth : maxValueCoord - minValueCoord,
color: colorGetter(dataIndex),
value: series[seriesId].data[dataIndex],
maskId: `${chartId}_${stackId || seriesId}_${groupIndex}_${dataIndex}`
};
if (!masks[result.maskId]) {
masks[result.maskId] = {
id: result.maskId,
width: 0,
height: 0,
hasNegative: false,
hasPositive: false,
layout: result.layout,
xOrigin: xScale(0),
yOrigin: yScale(0),
x: 0,
y: 0
};
}
const mask = masks[result.maskId];
mask.width = result.layout === 'vertical' ? result.width : mask.width + result.width;
mask.height = result.layout === 'vertical' ? mask.height + result.height : result.height;
mask.x = Math.min(mask.x === 0 ? Infinity : mask.x, result.x);
mask.y = Math.min(mask.y === 0 ? Infinity : mask.y, result.y);
mask.hasNegative = mask.hasNegative || (result.value ?? 0) < 0;
mask.hasPositive = mask.hasPositive || (result.value ?? 0) > 0;
return result;
});
});
});
return {
completedData: data,
masksData: Object.values(masks)
};
};
const leaveStyle = ({
layout,
yOrigin,
x,
width,
y,
xOrigin,
height
}) => (0, _extends2.default)({}, layout === 'vertical' ? {
y: yOrigin,
x,
height: 0,
width
} : {
y,
x: xOrigin,
height,
width: 0
});
const enterStyle = ({
x,
width,
y,
height
}) => ({
y,
x,
height,
width
});
/**
* Demos:
*
* - [Bars](https://mui.com/x/react-charts/bars/)
* - [Bar demonstration](https://mui.com/x/react-charts/bar-demo/)
* - [Stacking](https://mui.com/x/react-charts/stacking/)
*
* API:
*
* - [BarPlot API](https://mui.com/x/api/charts/bar-plot/)
*/
function BarPlot(props) {
const {
completedData,
masksData
} = useAggregatedData();
const {
skipAnimation,
onItemClick,
borderRadius,
barLabel
} = props,
other = (0, _objectWithoutPropertiesLoose2.default)(props, _excluded);
const withoutBorderRadius = !borderRadius || borderRadius <= 0;
const transition = (0, _web.useTransition)(completedData, {
keys: bar => `${bar.seriesId}-${bar.dataIndex}`,
from: leaveStyle,
leave: leaveStyle,
enter: enterStyle,
update: enterStyle,
immediate: skipAnimation
});
const maskTransition = (0, _web.useTransition)(withoutBorderRadius ? [] : masksData, {
keys: v => v.id,
from: leaveStyle,
leave: leaveStyle,
enter: enterStyle,
update: enterStyle,
immediate: skipAnimation
});
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(React.Fragment, {
children: [!withoutBorderRadius && maskTransition((style, {
id,
hasPositive,
hasNegative,
layout
}) => {
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_BarClipPath.BarClipPath, {
maskId: id,
borderRadius: borderRadius,
hasNegative: hasNegative,
hasPositive: hasPositive,
layout: layout,
style: style
});
}), transition((style, {
seriesId,
dataIndex,
color,
maskId
}) => {
const barElement = /*#__PURE__*/(0, _jsxRuntime.jsx)(_BarElement.BarElement, (0, _extends2.default)({
id: seriesId,
dataIndex: dataIndex,
color: color
}, other, {
onClick: onItemClick && (event => {
onItemClick(event, {
type: 'bar',
seriesId,
dataIndex
});
}),
style: style
}));
if (withoutBorderRadius) {
return barElement;
}
return /*#__PURE__*/(0, _jsxRuntime.jsx)("g", {
clipPath: `url(#${maskId})`,
children: barElement
});
}), barLabel && /*#__PURE__*/(0, _jsxRuntime.jsx)(_BarLabelPlot.BarLabelPlot, (0, _extends2.default)({
bars: completedData,
skipAnimation: skipAnimation,
barLabel: barLabel
}, other))]
});
}
process.env.NODE_ENV !== "production" ? BarPlot.propTypes = {
// ----------------------------- Warning --------------------------------
// | These PropTypes are generated from the TypeScript type definitions |
// | To update them edit the TypeScript types and run "pnpm proptypes" |
// ----------------------------------------------------------------------
/**
* If provided, the function will be used to format the label of the bar.
* It can be set to 'value' to display the current value.
* @param {BarItem} item The item to format.
* @param {BarLabelContext} context data about the bar.
* @returns {string} The formatted label.
*/
barLabel: _propTypes.default.oneOfType([_propTypes.default.oneOf(['value']), _propTypes.default.func]),
/**
* Defines the border radius of the bar element.
*/
borderRadius: _propTypes.default.number,
/**
* Callback fired when a bar item is clicked.
* @param {React.MouseEvent<SVGElement, MouseEvent>} event The event source of the callback.
* @param {BarItemIdentifier} barItemIdentifier The bar item identifier.
*/
onItemClick: _propTypes.default.func,
/**
* If `true`, animations are skipped.
* @default false
*/
skipAnimation: _propTypes.default.bool,
/**
* The props used for each component slot.
* @default {}
*/
slotProps: _propTypes.default.object,
/**
* Overridable component slots.
* @default {}
*/
slots: _propTypes.default.object
} : void 0;
;