UNPKG

recharts

Version:
401 lines (328 loc) 15.2 kB
'use strict'; var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); var _class, _temp; /** * @fileOverview Bar Chart */ Object.defineProperty(exports, "__esModule", { value: true }); var _react = require('react'); var _react2 = _interopRequireDefault(_react); var _classnames = require('classnames'); var _classnames2 = _interopRequireDefault(_classnames); var _Surface = require('../container/Surface'); var _Surface2 = _interopRequireDefault(_Surface); var _Tooltip = require('../component/Tooltip'); var _Tooltip2 = _interopRequireDefault(_Tooltip); var _Rectangle = require('../shape/Rectangle'); var _Rectangle2 = _interopRequireDefault(_Rectangle); var _LodashUtils = require('../util/LodashUtils'); var _LodashUtils2 = _interopRequireDefault(_LodashUtils); var _ReactUtils = require('../util/ReactUtils'); var _ReactUtils2 = _interopRequireDefault(_ReactUtils); var _CartesianChart2 = require('./CartesianChart'); var _CartesianChart3 = _interopRequireDefault(_CartesianChart2); var _Bar = require('../cartesian/Bar'); var _Bar2 = _interopRequireDefault(_Bar); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } var BarChart = (_temp = _class = function (_CartesianChart) { _inherits(BarChart, _CartesianChart); function BarChart() { _classCallCheck(this, BarChart); return _possibleConstructorReturn(this, Object.getPrototypeOf(BarChart).apply(this, arguments)); } _createClass(BarChart, [{ key: 'getComposedData', /** * Compose the data of each group * @param {Array} barPosition The offset and size of each bar * @param {Object} xAxis The configuration of x-axis * @param {Object} yAxis The configuration of y-axis * @param {Object} offset The offset of main part in the svg element * @param {String} dataKey The unique key of a group * @param {Array} stackedData The stacked data of a bar item * @return {Array} Composed data */ value: function getComposedData(barPosition, xAxis, yAxis, offset, dataKey, stackedData) { var layout = this.props.layout; var _state = this.state; var dataStartIndex = _state.dataStartIndex; var dataEndIndex = _state.dataEndIndex; var data = this.props.data.slice(dataStartIndex, dataEndIndex + 1); var pos = barPosition[dataKey]; var xTicks = this.getAxisTicks(xAxis); var yTicks = this.getAxisTicks(yAxis); var baseValue = this.getBaseValue(xAxis, yAxis); var hasStack = stackedData && stackedData.length; return data.map(function (entry, index) { var value = stackedData ? stackedData[dataStartIndex + index] : [baseValue, entry[dataKey]]; var x = undefined; var y = undefined; var width = undefined; var height = undefined; if (layout === 'horizontal') { x = xTicks[index].coord + pos.offset; y = yAxis.scale(xAxis.orientation === 'top' ? value[0] : value[1]); width = pos.size; height = xAxis.orientation === 'top' ? yAxis.scale(value[1]) - yAxis.scale(value[0]) : yAxis.scale(value[0]) - yAxis.scale(value[1]); } else { x = xAxis.scale(yAxis.orientation === 'left' ? value[0] : value[1]); y = yTicks[index].coord + pos.offset; width = yAxis.orientation === 'left' ? xAxis.scale(value[1]) - xAxis.scale(value[0]) : xAxis.scale(value[0]) - xAxis.scale(value[1]); height = pos.size; } return _extends({}, entry, { x: x, y: y, width: width, height: height, value: value[1] }); }); } }, { key: 'getBaseValue', value: function getBaseValue(xAxis, yAxis) { var layout = this.props.layout; var numberAxis = layout === 'horizontal' ? yAxis : xAxis; var domain = numberAxis.scale.domain(); if (numberAxis.type === 'number') { return Math.max(Math.min(domain[0], domain[1]), 0); } return domain[0]; } /** * Calculate the size of each bar and the gap between two bars * @param {Number} bandSize The size of each category * @param {sizeList} sizeList The size of all groups * @return {Number} The size of each bar and the gap between two bars */ }, { key: 'getBarPosition', value: function getBarPosition(bandSize, sizeList) { var _props = this.props; var barGap = _props.barGap; var barCategoryGap = _props.barCategoryGap; var len = sizeList.length; var result = undefined; // whether or not is barSize setted by user if (sizeList[0].barSize === +sizeList[0].barSize) { (function () { var sum = sizeList.reduce(function (res, entry) { return res + entry.barSize; }, 0); sum += (len - 1) * barGap; var offset = (bandSize - sum) / 2 >> 0; var prev = { offset: offset - barGap, size: 0 }; result = sizeList.reduce(function (res, entry) { res[entry.dataKey] = { offset: prev.offset + prev.size + barGap, size: entry.barSize }; prev = res[entry.dataKey]; if (entry.stackList && entry.stackList.length) { entry.stackList.forEach(function (key) { res[key] = res[entry.dataKey]; }); } return res; }, {}); })(); } else { (function () { var offset = _LodashUtils2.default.getPercentValue(barCategoryGap, bandSize); var size = (bandSize - 2 * offset - (len - 1) * barGap) / len >> 0; result = sizeList.reduce(function (res, entry, i) { res[entry.dataKey] = { offset: offset + (size + barGap) * i, size: size }; if (entry.stackList && entry.stackList.length) { entry.stackList.forEach(function (key) { res[key] = res[entry.dataKey]; }); } return res; }, {}); })(); } return result; } /** * Calculate the size of all groups * @param {Object} stackGroups The items grouped by axisId and stackId * @return {Object} The size of all groups */ }, { key: 'getSizeList', value: function getSizeList(stackGroups) { var _props2 = this.props; var layout = _props2.layout; var barSize = _props2.barSize; return Object.keys(stackGroups).reduce(function (result, axisId) { var sgs = stackGroups[axisId].stackGroups; result[axisId] = Object.keys(sgs).map(function (stackId) { var items = sgs[stackId].items; var dataKey = items[0].props.dataKey; return { dataKey: dataKey, stackList: items.slice(1).map(function (item) { return item.props.dataKey; }), barSize: items[0].props.barSize || barSize }; }); return result; }, {}); } /** * Calculate the size between two category * @param {Function} scale Scale function * @return {Number} Size */ }, { key: 'getBandSize', value: function getBandSize(scale) { if (scale && scale.bandwidth) { return scale.bandwidth(); } return 0; } /** * Handler of mouse entering bar chart * @param {String} key The unique key of a group of data * @return {Object} null */ }, { key: 'handleBarMouseEnter', value: function handleBarMouseEnter(key) { this.setState({ activeBarKey: key }); } /** * Handler of mouse leaving area chart * @return {Object} null */ }, { key: 'handleBarMouseLeave', value: function handleBarMouseLeave() { this.setState({ activeBarKey: null }); } }, { key: 'renderCursor', value: function renderCursor(xAxisMap, yAxisMap, offset) { var children = this.props.children; var tooltipItem = _ReactUtils2.default.findChildByType(children, _Tooltip2.default); if (!tooltipItem || !tooltipItem.props.cursor || !this.state.isTooltipActive) { return null; } var layout = this.props.layout; var activeTooltipIndex = this.state.activeTooltipIndex; var axisMap = layout === 'horizontal' ? xAxisMap : yAxisMap; var ids = Object.keys(axisMap); var axis = axisMap[ids[0]]; var bandSize = axis.scale.bandwidth(); var ticks = this.getAxisTicks(axis); var start = ticks[activeTooltipIndex].coord; var cursorProps = _extends({ fill: '#f1f1f1' }, _ReactUtils2.default.getPresentationAttributes(tooltipItem.props.cursor), { x: layout === 'horizontal' ? start : offset.left + 0.5, y: layout === 'horizontal' ? offset.top + 0.5 : start, width: layout === 'horizontal' ? bandSize : offset.width - 1, height: layout === 'horizontal' ? offset.height - 1 : bandSize }); return _react2.default.isValidElement(tooltipItem.props.cursor) ? _react2.default.cloneElement(tooltipItem.props.cursor, cursorProps) : _react2.default.createElement(_Rectangle2.default, cursorProps); } /** * Draw the main part of bar chart * @param {Array} items All the instance of Bar * @param {Object} xAxisMap The configuration of all x-axis * @param {Object} yAxisMap The configuration of all y-axis * @param {Object} offset The offset of main part in the svg element * @param {Object} stackGroups The items grouped by axisId and stackId * @return {ReactComponent} All the instances of Bar */ }, { key: 'renderItems', value: function renderItems(items, xAxisMap, yAxisMap, offset, stackGroups) { var _this2 = this; if (!items || !items.length) { return null; } var layout = this.props.layout; var sizeList = this.getSizeList(stackGroups); var barPositionMap = {}; return items.map(function (child, i) { var _child$props = child.props; var xAxisId = _child$props.xAxisId; var yAxisId = _child$props.yAxisId; var dataKey = _child$props.dataKey; var axisId = layout === 'horizontal' ? xAxisId : yAxisId; var bandSize = _this2.getBandSize(layout === 'horizontal' ? xAxisMap[xAxisId].scale : yAxisMap[yAxisId].scale); var barPosition = barPositionMap[axisId] || _this2.getBarPosition(bandSize, sizeList[axisId]); var stackedData = stackGroups && stackGroups[axisId] && stackGroups[axisId].hasStack && _this2.getStackedDataOfItem(child, stackGroups[axisId].stackGroups); return _react2.default.cloneElement(child, { key: 'bar-' + i, layout: layout, onMouseLeave: _this2.handleBarMouseLeave.bind(_this2), onMouseEnter: _this2.handleBarMouseEnter.bind(_this2, dataKey), data: _this2.getComposedData(barPosition, xAxisMap[xAxisId], yAxisMap[yAxisId], offset, dataKey, stackedData) }); }, this); } }, { key: 'render', value: function render() { if (!_ReactUtils2.default.validateWidthHeight(this)) { return null; } var _props3 = this.props; var style = _props3.style; var children = _props3.children; var className = _props3.className; var layout = _props3.layout; var width = _props3.width; var height = _props3.height; var numberAxisName = layout === 'horizontal' ? 'yAxis' : 'xAxis'; var items = _ReactUtils2.default.findAllByType(children, _Bar2.default); var stackGroups = this.getStackGroupsByAxisId(items, numberAxisName + 'Id'); var xAxisMap = this.getAxisMap('xAxis', items, numberAxisName === 'xAxis' && stackGroups); var yAxisMap = this.getAxisMap('yAxis', items, numberAxisName === 'yAxis' && stackGroups); var offset = this.getOffset(xAxisMap, yAxisMap); xAxisMap = this.getFormatAxisMap(xAxisMap, offset, 'xAxis'); yAxisMap = this.getFormatAxisMap(yAxisMap, offset, 'yAxis'); return _react2.default.createElement( 'div', { className: (0, _classnames2.default)('recharts-wrapper', className), style: _extends({ position: 'relative', cursor: 'default' }, style), onMouseEnter: this.handleMouseEnter.bind(this, offset, xAxisMap, yAxisMap), onMouseMove: this.handleMouseMove.bind(this, offset, xAxisMap, yAxisMap), onMouseLeave: this.handleMouseLeave.bind(this) }, _react2.default.createElement( _Surface2.default, { width: width, height: height }, this.renderGrid(xAxisMap, yAxisMap, offset), this.renderReferenceLines(xAxisMap, yAxisMap, offset), this.renderXAxis(xAxisMap), this.renderYAxis(yAxisMap), this.renderCursor(xAxisMap, yAxisMap, offset), this.renderItems(items, xAxisMap, yAxisMap, offset, stackGroups), this.renderBrush(xAxisMap, yAxisMap, offset) ), this.renderLegend(items), this.renderTooltip(items, offset) ); } }]); return BarChart; }(_CartesianChart3.default), _class.displayName = 'BarChart', _class.defaultProps = { style: {}, barCategoryGap: '10%', barGap: 4, layout: 'horizontal', margin: { top: 5, right: 5, bottom: 5, left: 5 } }, _temp); exports.default = BarChart;