UNPKG

react-vis

Version:

Data visualization library based on React and d3.

248 lines (222 loc) 9.63 kB
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 _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; }; 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; } // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { pie as pieBuilder } from 'd3-shape'; import { AnimationPropType } from '../animation'; import ArcSeries from '../plot/series/arc-series'; import LabelSeries from '../plot/series/label-series'; import XYPlot from '../plot/xy-plot'; import { DISCRETE_COLOR_RANGE } from '../theme'; import { MarginPropType, getRadialLayoutMargin } from '../utils/chart-utils'; import { getRadialDomain } from '../utils/series-utils'; var predefinedClassName = 'rv-radial-chart'; var DEFAULT_RADIUS_MARGIN = 15; /** * Create the list of wedges to render. * @param {Object} props props.data {Object} - tree structured data (each node has a name anc an array of children) * @returns {Array} Array of nodes. */ function getWedgesToRender(_ref) { var data = _ref.data, getAngle = _ref.getAngle; var pie = pieBuilder().sort(null).value(getAngle); var pieData = pie(data).reverse(); return pieData.map(function (row, index) { return _extends({}, row.data, { angle0: row.startAngle, angle: row.endAngle, radius0: row.data.innerRadius || 0, radius: row.data.radius || 1, color: row.data.color || index }); }); } function generateLabels(mappedData, accessors) { var getLabel = accessors.getLabel, getSubLabel = accessors.getSubLabel; return mappedData.reduce(function (res, row) { var angle = row.angle, angle0 = row.angle0, radius = row.radius; var centeredAngle = (angle + angle0) / 2; // unfortunate, but true fact: d3 starts its radians at 12 oclock rather than 3 // and move clockwise rather than counter clockwise. why why why! var updatedAngle = -1 * centeredAngle + Math.PI / 2; var newLabels = []; if (getLabel(row)) { newLabels.push({ angle: updatedAngle, radius: radius * 1.1, label: getLabel(row), style: { fontSize: '12px' } }); } if (getSubLabel(row)) { newLabels.push({ angle: updatedAngle, radius: radius * 1.1, label: getSubLabel(row), yOffset: 12, style: { fontSize: '10px' } }); } return res.concat(newLabels); }, []); // could add force direction here to make sure the labels dont overlap } /** * Get the max radius so the chart can extend to the margin. * @param {Number} width - container width * @param {Number} height - container height * @return {Number} radius */ function getMaxRadius(width, height) { return Math.min(width, height) / 2 - DEFAULT_RADIUS_MARGIN; } var RadialChart = function (_Component) { _inherits(RadialChart, _Component); function RadialChart() { _classCallCheck(this, RadialChart); return _possibleConstructorReturn(this, (RadialChart.__proto__ || Object.getPrototypeOf(RadialChart)).apply(this, arguments)); } _createClass(RadialChart, [{ key: 'render', value: function render() { var _props = this.props, animation = _props.animation, className = _props.className, children = _props.children, data = _props.data, height = _props.height, hideRootNode = _props.hideRootNode, width = _props.width, colorType = _props.colorType, radius = _props.radius, innerRadius = _props.innerRadius, showLabels = _props.showLabels, margin = _props.margin, onMouseLeave = _props.onMouseLeave, onMouseEnter = _props.onMouseEnter, labelsAboveChildren = _props.labelsAboveChildren, getAngle = _props.getAngle, getLabel = _props.getLabel, getSubLabel = _props.getSubLabel; var mappedData = getWedgesToRender({ data: data, height: height, hideRootNode: hideRootNode, width: width, getAngle: getAngle }); var radialDomain = getRadialDomain(mappedData); var arcProps = _extends({ colorType: colorType }, this.props, { animation: animation, radiusDomain: [0, radialDomain], data: mappedData, radiusNoFallBack: true, arcClassName: 'rv-radial-chart__series--pie__slice' }); if (radius) { arcProps.radiusDomain = [0, 1]; arcProps.radiusRange = [innerRadius || 0, radius]; arcProps.radiusType = 'linear'; } var maxRadius = radius ? radius : getMaxRadius(width, height); var defaultMargin = getRadialLayoutMargin(width, height, maxRadius); var labels = generateLabels(mappedData, { getLabel: getLabel, getSubLabel: getSubLabel }); return React.createElement( XYPlot, { height: height, width: width, margin: _extends({}, margin, defaultMargin), className: className + ' ' + predefinedClassName, onMouseLeave: onMouseLeave, onMouseEnter: onMouseEnter, xDomain: [-radialDomain, radialDomain], yDomain: [-radialDomain, radialDomain] }, React.createElement(ArcSeries, _extends({}, arcProps, { getAngle: function getAngle(d) { return d.angle; } })), showLabels && !labelsAboveChildren && React.createElement(LabelSeries, { data: labels }), children, showLabels && labelsAboveChildren && React.createElement(LabelSeries, { data: labels }) ); } }]); return RadialChart; }(Component); RadialChart.displayName = 'RadialChart'; RadialChart.propTypes = { animation: AnimationPropType, className: PropTypes.string, colorType: PropTypes.string, data: PropTypes.arrayOf(PropTypes.shape({ angle: PropTypes.number, className: PropTypes.string, label: PropTypes.string, radius: PropTypes.number, style: PropTypes.object })).isRequired, getAngle: PropTypes.func, getAngle0: PropTypes.func, getRadius: PropTypes.func, getRadius0: PropTypes.func, getLabel: PropTypes.func, height: PropTypes.number.isRequired, labelsAboveChildren: PropTypes.bool, margin: MarginPropType, onValueClick: PropTypes.func, onValueMouseOver: PropTypes.func, onValueMouseOut: PropTypes.func, width: PropTypes.number.isRequired, showLabels: PropTypes.bool, subLabel: PropTypes.func }; RadialChart.defaultProps = { className: '', colorType: 'category', colorRange: DISCRETE_COLOR_RANGE, getAngle: function getAngle(d) { return d.angle; }, getAngle0: function getAngle0(d) { return d.angle0; }, getRadius: function getRadius(d) { return d.radius; }, getRadius0: function getRadius0(d) { return d.radius0; }, getLabel: function getLabel(d) { return d.label; }, getSubLabel: function getSubLabel(d) { return d.subLabel; } }; export default RadialChart;