UNPKG

victory-chart

Version:
464 lines (427 loc) 20.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var _omit2 = require("lodash/omit"); var _omit3 = _interopRequireDefault(_omit2); var _defaults2 = require("lodash/defaults"); var _defaults3 = _interopRequireDefault(_defaults2); var _assign2 = require("lodash/assign"); var _assign3 = _interopRequireDefault(_assign2); 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 _react = require("react"); var _react2 = _interopRequireDefault(_react); var _victoryCore = require("victory-core"); var _bar = require("./bar"); var _bar2 = _interopRequireDefault(_bar); var _data = require("../../helpers/data"); var _data2 = _interopRequireDefault(_data); var _domain = require("../../helpers/domain"); var _domain2 = _interopRequireDefault(_domain); var _scale = require("../../helpers/scale"); var _scale2 = _interopRequireDefault(_scale); 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 defaultStyles = { data: { width: 8, padding: 6, stroke: "transparent", strokeWidth: 0, fill: "#756f6a", opacity: 1 }, labels: { fontSize: 12, padding: 4, fill: "black" } }; var defaultData = [{ x: 1, y: 1 }, { x: 2, y: 2 }, { x: 3, y: 3 }, { x: 4, y: 4 }]; var VictoryBar = function (_React$Component) { _inherits(VictoryBar, _React$Component); function VictoryBar() { _classCallCheck(this, VictoryBar); var _this = _possibleConstructorReturn(this, Object.getPrototypeOf(VictoryBar).call(this)); _this.state = {}; _this.getEvents = _victoryCore.Helpers.getEvents.bind(_this); _this.getEventState = _victoryCore.Helpers.getEventState.bind(_this); return _this; } _createClass(VictoryBar, [{ key: "getScale", value: function getScale(props) { var range = { x: _victoryCore.Helpers.getRange(props, "x"), y: _victoryCore.Helpers.getRange(props, "y") }; var domain = { x: _domain2.default.getDomainWithZero(props, "x"), y: _domain2.default.getDomainWithZero(props, "y") }; return { x: _scale2.default.getBaseScale(props, "x").domain(domain.x).range(range.x), y: _scale2.default.getBaseScale(props, "y").domain(domain.y).range(range.y) }; } }, { key: "getBarPosition", value: function getBarPosition(props, datum, scale) { var yOffset = datum.yOffset || 0; var xOffset = datum.xOffset || 0; var y0 = yOffset; var y = datum.y + yOffset; var x = datum.x + xOffset; var formatValue = function formatValue(value, axis) { return datum[axis] instanceof Date ? new Date(value) : value; }; return { x: scale.x(formatValue(x, "x")), y0: scale.y(formatValue(y0, "y")), y: scale.y(formatValue(y, "y")) }; } }, { key: "getBarStyle", value: function getBarStyle(datum, baseStyle) { var styleData = (0, _omit3.default)(datum, ["xName", "yName", "x", "y", "label"]); return (0, _defaults3.default)({}, styleData, baseStyle); } }, { key: "getLabelStyle", value: function getLabelStyle(style, datum) { var labelStyle = (0, _defaults3.default)({ angle: datum.angle, textAnchor: datum.textAnchor, verticalAnchor: datum.verticalAnchor }, style); return _victoryCore.Helpers.evaluateStyle(labelStyle, datum); } }, { key: "getLabel", value: function getLabel(props, datum, index) { var propsLabel = Array.isArray(props.labels) ? props.labels[index] : _victoryCore.Helpers.evaluateProp(props.labels, datum); return datum.label || propsLabel; } }, { key: "getLabelAnchors", value: function getLabelAnchors(datum, horizontal) { var sign = datum.y >= 0 ? 1 : -1; if (!horizontal) { return { vertical: sign >= 0 ? "end" : "start", text: "middle" }; } else { return { vertical: "middle", text: sign >= 0 ? "start" : "end" }; } } }, { key: "getlabelPadding", value: function getlabelPadding(style, horizontal) { var defaultPadding = style.padding || 0; return { x: horizontal ? defaultPadding : 0, y: horizontal ? 0 : defaultPadding }; } }, { key: "renderData", value: function renderData(props, data, style) { var _this2 = this; var scale = this.getScale(props); var dataEvents = this.getEvents(props.events.data, "data"); var labelEvents = this.getEvents(props.events.labels, "labels"); var horizontal = props.horizontal; var labelComponent = props.labelComponent; return data.map(function (datum, index) { var position = _this2.getBarPosition(props, datum, scale); var barStyle = _this2.getBarStyle(datum, style.data); var dataProps = (0, _defaults3.default)({}, _this2.getEventState(index, "data"), props.dataComponent.props, position, { key: "bar-" + index, style: _victoryCore.Helpers.evaluateStyle(barStyle, datum), index: index, datum: datum, scale: scale, horizontal: horizontal }); var barComponent = _react2.default.cloneElement(props.dataComponent, (0, _assign3.default)({}, dataProps, { events: _victoryCore.Helpers.getPartialEvents(dataEvents, index, dataProps) })); var text = _this2.getLabel(props, dataProps.datum, index); if (text !== null && text !== undefined) { var labelStyle = _this2.getLabelStyle(style.labels, dataProps.datum); var padding = _this2.getlabelPadding(labelStyle, horizontal); var anchors = _this2.getLabelAnchors(dataProps.datum, horizontal); var labelPosition = { x: horizontal ? position.y : position.x, y: horizontal ? position.x : position.y }; var labelProps = (0, _defaults3.default)({}, _this2.getEventState(index, "labels"), labelComponent.props, { key: "bar-label-" + index, style: labelStyle, x: labelPosition.x + padding.x, y: labelPosition.y - padding.y, y0: position.y0, text: text, index: index, scale: scale, datum: dataProps.datum, textAnchor: labelStyle.textAnchor || anchors.text, verticalAnchor: labelStyle.verticalAnchor || anchors.vertical, angle: labelStyle.angle }); var barLabel = _react2.default.cloneElement(labelComponent, (0, _assign3.default)({ events: _victoryCore.Helpers.getPartialEvents(labelEvents, index, labelProps) }, labelProps)); return _react2.default.createElement( "g", { key: "bar-group-" + index }, barComponent, barLabel ); } return barComponent; }); } }, { key: "render", value: function render() { // If animating, return a `VictoryAnimation` element that will create // a new `VictoryBar` with nearly identical props, except (1) tweened // and (2) `animate` set to null so we don't recurse forever. if (this.props.animate) { var whitelist = ["data", "domain", "height", "padding", "style", "width"]; return _react2.default.createElement( _victoryCore.VictoryTransition, { animate: this.props.animate, animationWhitelist: whitelist }, _react2.default.createElement(VictoryBar, this.props) ); } var style = _victoryCore.Helpers.getStyles(this.props.style, defaultStyles, "auto", "100%"); var data = _data2.default.getData(this.props); var group = _react2.default.createElement( "g", { style: style.parent }, this.renderData(this.props, data, style) ); return this.props.standalone ? _react2.default.createElement( "svg", _extends({ style: style.parent, viewBox: "0 0 " + this.props.width + " " + this.props.height }, this.props.events.parent), group ) : group; } }]); return VictoryBar; }(_react2.default.Component); VictoryBar.role = "bar"; VictoryBar.defaultTransitions = { onExit: { duration: 500, before: function before() { return { y: 0, yOffset: 0 }; } }, onEnter: { duration: 500, before: function before() { return { y: 0, yOffset: 0 }; }, after: function after(datum) { return { y: datum.y, yOffset: datum.yOffset }; } } }; VictoryBar.propTypes = { /** * The animate prop specifies props for VictoryAnimation to use. The animate prop should * also be used to specify enter and exit transition configurations with the `onExit` * and `onEnter` namespaces respectively. * @examples {duration: 500, onEnd: () => {}, onEnter: {duration: 500, before: () => ({y: 0})})} */ animate: _react.PropTypes.object, /** * The categories prop specifies how categorical data for a chart should be ordered. * This prop should be given as an array of string values, or an object with * these arrays of values specified for x and y. If this prop is not set, * categorical data will be plotted in the order it was given in the data array * @examples ["dogs", "cats", "mice"] */ categories: _react.PropTypes.oneOfType([_react.PropTypes.arrayOf(_react.PropTypes.string), _react.PropTypes.shape({ x: _react.PropTypes.arrayOf(_react.PropTypes.string), y: _react.PropTypes.arrayOf(_react.PropTypes.string) })]), /** * The data prop specifies the data to be plotted. Data should be in the form of an array * of data points. Each data point may be any format you wish * (depending on the `x` and `y` accessor props), but by default, an object * with x and y properties is expected. * @examples [{x: 1, y: 2}, {x: 2, y: 3}], [[1, 2], [2, 3]], * [[{x: "a", y: 1}, {x: "b", y: 2}], [{x: "a", y: 2}, {x: "b", y: 3}]] */ data: _react.PropTypes.array, /** * The dataComponent prop takes an entire component which will be used to create bars for * each datum in the chart. The new element created from the passed dataComponent will be * provided with the following properties calculated by VictoryBar: datum, index, scale, * style, events, horizontal (boolean), x, y, and y0. Any of these props may be overridden * by passing in props to the supplied component, or modified or ignored within the custom * component itself. If a dataComponent is not provided, VictoryBar will use its default * Bar component. */ dataComponent: _react.PropTypes.element, /** * The domain prop describes the range of values your bar chart will cover. This prop can be * given as a array of the minimum and maximum expected values for your bar chart, * or as an object that specifies separate arrays for x and y. * If this prop is not provided, a domain will be calculated from data, or other * available information. * @examples [-1, 1], {x: [0, 100], y: [0, 1]} */ domain: _react.PropTypes.oneOfType([_victoryCore.PropTypes.domain, _react.PropTypes.shape({ x: _victoryCore.PropTypes.domain, y: _victoryCore.PropTypes.domain })]), /** * The events prop attaches arbitrary event handlers to data and label elements * Event handlers are called with their corresponding events, corresponding component props, * and their index in the data array, and event name. The return value of event handlers * will be stored by index and namespace on the state object of VictoryBar * i.e. `this.state[index].data = {style: {fill: "red"}...}`, and will be * applied by index to the appropriate child component. Event props on the * parent namespace are just spread directly on to the top level svg of VictoryBar * if one exists. If VictoryBar is set up to render g elements i.e. when it is * rendered within chart, or when `standalone={false}` parent events will not be applied. * * @examples {data: { * onClick: () => return {data: {style: {fill: "green"}}, labels: {style: {fill: "black"}}} *}} */ events: _react.PropTypes.shape({ data: _react.PropTypes.object, labels: _react.PropTypes.object, parent: _react.PropTypes.object }), /** * The height props specifies the height the svg viewBox of the chart container. * This value should be given as a number of pixels */ height: _victoryCore.PropTypes.nonNegative, /** * The horizontal prop determines whether the bars will be laid vertically or * horizontally. The bars will be vertical if this prop is false or unspecified, * or horizontal if the prop is set to true. */ horizontal: _react.PropTypes.bool, /** * The labels prop defines labels that will appear above each bar in your bar chart. * This prop should be given as an array of values or as a function of data. * If given as an array, the number of elements in the array should be equal to * the length of the data array. Labels may also be added directly to the data object * like data={[{x: 1, y: 1, label: "first"}]}. * @examples: ["spring", "summer", "fall", "winter"], (datum) => datum.title */ labels: _react.PropTypes.oneOfType([_react.PropTypes.func, _react.PropTypes.array]), /** * The labelComponent prop takes in an entire label component which will be used * to create labels for each bar in the bar chart. The new element created from * the passed labelComponent will be supplied with the following properties: * x, y, y0, index, datum, verticalAnchor, textAnchor, angle, style, text, and events. * Any of these props may be overridden by passing in props to the supplied component, * or modified or ignored within the custom component itself. If labelComponent is omitted, * a new VictoryLabel will be created with props described above. */ labelComponent: _react.PropTypes.element, /** * The padding props specifies the amount of padding in number of pixels between * the edge of the chart and any rendered child components. This prop can be given * as a number or as an object with padding specified for top, bottom, left * and right. */ padding: _react.PropTypes.oneOfType([_react.PropTypes.number, _react.PropTypes.shape({ top: _react.PropTypes.number, bottom: _react.PropTypes.number, left: _react.PropTypes.number, right: _react.PropTypes.number })]), /** * The scale prop determines which scales your chart should use. This prop can be * given as a string specifying a supported scale ("linear", "time", "log", "sqrt"), * as a d3 scale function, or as an object with scales specified for x and y * @exampes d3Scale.time(), {x: "linear", y: "log"} */ scale: _react.PropTypes.oneOfType([_victoryCore.PropTypes.scale, _react.PropTypes.shape({ x: _victoryCore.PropTypes.scale, y: _victoryCore.PropTypes.scale })]), /** * The standalone prop determines whether the component will render a standalone svg * or a <g> tag that will be included in an external svg. Set standalone to false to * compose VictoryBar with other components within an enclosing <svg> tag. */ standalone: _react.PropTypes.bool, /** * The style prop specifies styles for your VictoryBar. Any valid inline style properties * will be applied. Height, width, and padding should be specified via the height, * width, and padding props, as they are used to calculate the alignment of * components within chart. In addition to normal style properties, angle and verticalAnchor * may also be specified via the labels object, and they will be passed as props to * VictoryLabel, or any custom labelComponent. * @examples {data: {fill: "red", width: 8}, labels: {fontSize: 12}} */ style: _react.PropTypes.shape({ parent: _react.PropTypes.object, data: _react.PropTypes.object, labels: _react.PropTypes.object }), /** * The width props specifies the width of the svg viewBox of the chart container * This value should be given as a number of pixels */ width: _victoryCore.PropTypes.nonNegative, /** * The x prop specifies how to access the X value of each data point. * If given as a function, it will be run on each data point, and returned value will be used. * If given as an integer, it will be used as an array index for array-type data points. * If given as a string, it will be used as a property key for object-type data points. * If given as an array of strings, or a string containing dots or brackets, * it will be used as a nested object property path (for details see Lodash docs for _.get). * If `null` or `undefined`, the data value will be used as is (identity function/pass-through). * @examples 0, 'x', 'x.value.nested.1.thing', 'x[2].also.nested', null, d => Math.sin(d) */ x: _react.PropTypes.oneOfType([_react.PropTypes.func, _victoryCore.PropTypes.allOfType([_victoryCore.PropTypes.integer, _victoryCore.PropTypes.nonNegative]), _react.PropTypes.string, _react.PropTypes.arrayOf(_react.PropTypes.string)]), /** * The y prop specifies how to access the Y value of each data point. * If given as a function, it will be run on each data point, and returned value will be used. * If given as an integer, it will be used as an array index for array-type data points. * If given as a string, it will be used as a property key for object-type data points. * If given as an array of strings, or a string containing dots or brackets, * it will be used as a nested object property path (for details see Lodash docs for _.get). * If `null` or `undefined`, the data value will be used as is (identity function/pass-through). * @examples 0, 'y', 'y.value.nested.1.thing', 'y[2].also.nested', null, d => Math.sin(d) */ y: _react.PropTypes.oneOfType([_react.PropTypes.func, _victoryCore.PropTypes.allOfType([_victoryCore.PropTypes.integer, _victoryCore.PropTypes.nonNegative]), _react.PropTypes.string, _react.PropTypes.arrayOf(_react.PropTypes.string)]) }; VictoryBar.defaultProps = { data: defaultData, dataComponent: _react2.default.createElement(_bar2.default, null), labelComponent: _react2.default.createElement(_victoryCore.VictoryLabel, null), events: {}, height: 300, padding: 50, scale: "linear", standalone: true, width: 450, x: "x", y: "y" }; VictoryBar.getDomain = _domain2.default.getDomainWithZero.bind(_domain2.default); VictoryBar.getData = _data2.default.getData.bind(_data2.default); exports.default = VictoryBar;