UNPKG

victory-chart

Version:
562 lines (519 loc) 25.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); 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 _axisLine = require("./axis-line"); var _axisLine2 = _interopRequireDefault(_axisLine); var _grid = require("./grid"); var _grid2 = _interopRequireDefault(_grid); var _tick = require("./tick"); var _tick2 = _interopRequireDefault(_tick); var _helperMethods = require("./helper-methods"); var _helperMethods2 = _interopRequireDefault(_helperMethods); var _axis = require("../../helpers/axis"); var _axis2 = _interopRequireDefault(_axis); 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 = { axis: { stroke: "#756f6a", fill: "none", strokeWidth: 2, strokeLinecap: "round" }, axisLabel: { stroke: "transparent", fill: "#756f6a", fontSize: 16, fontFamily: "Helvetica" }, grid: { stroke: "none", fill: "none", strokeLinecap: "round" }, ticks: { stroke: "#756f6a", fill: "none", padding: 5, strokeWidth: 2, strokeLinecap: "round", size: 4 }, tickLabels: { stroke: "transparent", fill: "#756f6a", fontFamily: "Helvetica", fontSize: 10, padding: 5 } }; var orientationSign = { top: -1, left: -1, right: 1, bottom: 1 }; var getStyles = function getStyles(props) { var style = props.style || {}; var parentStyleProps = { height: "auto", width: "100%" }; return { parent: (0, _defaults3.default)(parentStyleProps, style.parent, defaultStyles.parent), axis: (0, _defaults3.default)({}, style.axis, defaultStyles.axis), axisLabel: (0, _defaults3.default)({}, style.axisLabel, defaultStyles.axisLabel), grid: (0, _defaults3.default)({}, style.grid, defaultStyles.grid), ticks: (0, _defaults3.default)({}, style.ticks, defaultStyles.ticks), tickLabels: (0, _defaults3.default)({}, style.tickLabels, defaultStyles.tickLabels) }; }; var VictoryAxis = function (_React$Component) { _inherits(VictoryAxis, _React$Component); function VictoryAxis() { _classCallCheck(this, VictoryAxis); var _this = _possibleConstructorReturn(this, Object.getPrototypeOf(VictoryAxis).call(this)); _this.state = {}; _this.getEvents = _victoryCore.Helpers.getEvents.bind(_this); _this.getEventState = _victoryCore.Helpers.getEventState.bind(_this); return _this; } _createClass(VictoryAxis, [{ key: "getTickProps", value: function getTickProps(props) { var stringTicks = _axis2.default.stringTicks(props); var scale = _helperMethods2.default.getScale(props); var ticks = _helperMethods2.default.getTicks(props, scale); return { scale: scale, ticks: ticks, stringTicks: stringTicks }; } }, { key: "getLayoutProps", value: function getLayoutProps(props) { var style = getStyles(props); var padding = _victoryCore.Helpers.getPadding(props); var orientation = props.orientation || (props.dependentAxis ? "left" : "bottom"); var isVertical = _axis2.default.isVertical(props); var labelPadding = _helperMethods2.default.getLabelPadding(props, style); var offset = _helperMethods2.default.getOffset(props, style); return { style: style, padding: padding, orientation: orientation, isVertical: isVertical, labelPadding: labelPadding, offset: offset }; } }, { key: "renderLine", value: function renderLine(props, layoutProps) { var style = layoutProps.style; var padding = layoutProps.padding; var isVertical = layoutProps.isVertical; var axisEvents = this.getEvents(props.events.axis, "axis"); var axisProps = (0, _defaults3.default)({}, this.getEventState(0, "axis"), props.axisComponent.props, { style: style.axis, x1: isVertical ? null : padding.left, x2: isVertical ? null : props.width - padding.right, y1: isVertical ? padding.top : null, y2: isVertical ? props.height - padding.bottom : null }); return _react2.default.cloneElement(props.axisComponent, (0, _assign3.default)({}, axisProps, { events: _victoryCore.Helpers.getPartialEvents(axisEvents, 0, axisProps) })); } }, { key: "getAnchors", value: function getAnchors(orientation, isVertical) { var anchorOrientation = { top: "end", left: "end", right: "start", bottom: "start" }; var anchor = anchorOrientation[orientation]; return { textAnchor: isVertical ? anchor : "middle", verticalAnchor: isVertical ? "middle" : anchor }; } }, { key: "renderTicks", value: function renderTicks(props, layoutProps, dataProps) { var _this2 = this; var style = layoutProps.style; var orientation = layoutProps.orientation; var isVertical = layoutProps.isVertical; var scale = dataProps.scale; var ticks = dataProps.ticks; var stringTicks = dataProps.stringTicks; var tickFormat = _helperMethods2.default.getTickFormat(props, dataProps); var tickPosition = _helperMethods2.default.getTickPosition(style.ticks, orientation, isVertical); var tickEvents = this.getEvents(props.events.ticks, "ticks"); var labelEvents = this.getEvents(props.events.tickLabels, "tickLabels"); return ticks.map(function (data, index) { var tick = stringTicks ? props.tickValues[data - 1] : data; var groupPosition = scale(data); var yTransform = isVertical ? groupPosition : 0; var xTransform = isVertical ? 0 : groupPosition; var tickProps = (0, _defaults3.default)({}, _this2.getEventState(index, "ticks"), props.tickComponent.props, { key: "tick-" + index, style: _victoryCore.Helpers.evaluateStyle(style.ticks, tick), x1: xTransform, y1: yTransform, x2: xTransform + tickPosition.x2, y2: yTransform + tickPosition.y2, tick: tick }); var tickComponent = _react2.default.cloneElement(props.tickComponent, (0, _assign3.default)({}, tickProps, { events: _victoryCore.Helpers.getPartialEvents(tickEvents, index, tickProps) })); var labelComponent = void 0; var label = tickFormat.call(_this2, tick, index); if (label !== null && label !== undefined) { var anchors = _this2.getAnchors(orientation, isVertical); var labelStyle = _victoryCore.Helpers.evaluateStyle(style.tickLabels, tick); var labelProps = (0, _defaults3.default)({}, _this2.getEventState(index, "tickLabels"), props.tickLabelComponent.props, { key: "tick-label-" + index, style: labelStyle, x: xTransform + tickPosition.x, y: yTransform + tickPosition.y, verticalAnchor: labelStyle.verticalAnchor || anchors.verticalAnchor, textAnchor: labelStyle.textAnchor || anchors.textAnchor, angle: labelStyle.angle, text: label, tick: tick }); labelComponent = _react2.default.cloneElement(props.tickLabelComponent, (0, _assign3.default)({}, labelProps, { events: _victoryCore.Helpers.getPartialEvents(labelEvents, index, labelProps) })); } return _react2.default.createElement( "g", { key: "tick-group-" + index }, tickComponent, labelComponent ); }); } }, { key: "renderGrid", value: function renderGrid(props, layoutProps, tickProps) { var _this3 = this; var scale = tickProps.scale; var ticks = tickProps.ticks; var stringTicks = tickProps.stringTicks; var style = layoutProps.style; var padding = layoutProps.padding; var isVertical = layoutProps.isVertical; var offset = layoutProps.offset; var orientation = layoutProps.orientation; var xPadding = orientation === "right" ? padding.right : padding.left; var yPadding = orientation === "top" ? padding.top : padding.bottom; var sign = -orientationSign[orientation]; var xOffset = props.crossAxis ? offset.x - xPadding : 0; var yOffset = props.crossAxis ? offset.y - yPadding : 0; var x2 = isVertical ? sign * (props.width - (padding.left + padding.right)) : 0; var y2 = isVertical ? 0 : sign * (props.height - (padding.top + padding.bottom)); var gridEvents = this.getEvents(props.events.grid, "grid"); return ticks.map(function (data, index) { var tick = stringTicks ? props.tickValues[data - 1] : data; // determine the position and translation of each gridline var position = scale(data); var xTransform = isVertical ? -xOffset : position; var yTransform = isVertical ? position : yOffset; var gridProps = (0, _defaults3.default)({}, _this3.getEventState(index, "grid"), props.gridComponent.props, { key: "grid-" + index, style: _victoryCore.Helpers.evaluateStyle(style.grid, tick), x1: xTransform, y1: yTransform, x2: x2 + xTransform, y2: y2 + yTransform, tick: tick }); var gridComponent = _react2.default.cloneElement(props.gridComponent, (0, _assign3.default)({}, gridProps, { events: _victoryCore.Helpers.getPartialEvents(gridEvents, index, gridProps) })); return gridComponent; }); } }, { key: "renderLabel", value: function renderLabel(props, layoutProps) { if (!props.label) { return undefined; } var style = layoutProps.style; var orientation = layoutProps.orientation; var padding = layoutProps.padding; var labelPadding = layoutProps.labelPadding; var isVertical = layoutProps.isVertical; var sign = orientationSign[orientation]; var hPadding = padding.left + padding.right; var vPadding = padding.top + padding.bottom; var x = isVertical ? -((props.height - vPadding) / 2) - padding.top : (props.width - hPadding) / 2 + padding.left; var y = sign * labelPadding; var verticalAnchor = sign < 0 ? "end" : "start"; var transform = isVertical ? "rotate(-90)" : ""; var labelEvents = this.getEvents(props.events.axisLabel, "axisLabel"); var labelStyle = style.axisLabel; var labelProps = (0, _defaults3.default)({}, this.getEventState(0, "axisLabel"), props.axisLabelComponent.props, { verticalAnchor: labelStyle.verticalAnchor || verticalAnchor, textAnchor: labelStyle.textAnchor || "middle", angle: labelStyle.angle, style: labelStyle, transform: transform, x: x, y: y, text: props.label }); return _react2.default.cloneElement(props.axisLabelComponent, (0, _assign3.default)({}, labelProps, { events: _victoryCore.Helpers.getPartialEvents(labelEvents, 0, labelProps) })); } }, { key: "render", value: function render() { if (this.props.animate) { // Do less work by having `VictoryAnimation` tween only values that // make sense to tween. In the future, allow customization of animated // prop whitelist/blacklist? var whitelist = ["style", "domain", "range", "tickCount", "tickValues", "offsetX", "offsetY", "padding", "width", "height"]; return _react2.default.createElement( _victoryCore.VictoryTransition, { animate: this.props.animate, animationWhitelist: whitelist }, _react2.default.createElement(VictoryAxis, this.props) ); } var layoutProps = this.getLayoutProps(this.props); var tickProps = this.getTickProps(this.props); var style = layoutProps.style; var transform = _helperMethods2.default.getTransform(this.props, layoutProps); var group = _react2.default.createElement( "g", { style: style.parent, transform: transform }, this.renderGrid(this.props, layoutProps, tickProps), this.renderLine(this.props, layoutProps), this.renderTicks(this.props, layoutProps, tickProps), this.renderLabel(this.props, layoutProps) ); 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 VictoryAxis; }(_react2.default.Component); VictoryAxis.role = "axis"; VictoryAxis.defaultTransitions = { onExit: { duration: 500 }, onEnter: { duration: 500 } }; VictoryAxis.propTypes = { /** * The animate prop specifies props for victory-animation to use. It this prop is * not given, the axis will not tween between changing data / style props. * Large datasets might animate slowly due to the inherent limits of svg rendering. * @examples {duration: 500, onEnd: () => alert("done!")} */ animate: _react.PropTypes.object, /** * The axisComponent prop takes in an entire component which will be used * to create the axis line. The new element created from the passed axisComponent * will be supplied with the following properties: x1, y1, x2, y2, style 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 an axisComponent * is not supplied, VictoryAxis will render its default AxisLine component. */ axisComponent: _react.PropTypes.element, /** * The axisLabelComponent prop takes in an entire component which will be used * to create the axis label. The new element created from the passed axisLabelComponent * will be supplied with the following properties: x, y, verticalAnchor, textAnchor, * angle, transform, style 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 an axisLabelComponent is not supplied, a new * VictoryLabel will be created with props described above */ axisLabelComponent: _react.PropTypes.element, /** * This prop specifies whether a given axis is intended to cross another axis. */ crossAxis: _react.PropTypes.bool, /** * The dependentAxis prop specifies whether the axis corresponds to the * dependent variable (usually y). This prop is useful when composing axis * with other components to form a chart. */ dependentAxis: _react.PropTypes.bool, /** * The domain prop describes the range of values your axis will include. This prop should be * given as a array of the minimum and maximum expected values for your axis. * If this value is not given it will be calculated based on the scale or tickValues. * @examples [-1, 1] */ domain: _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 VictoryAxis * i.e. `this.state.[index].axis = {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 VictoryAxis * if one exists. If VictoryAxis 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 {ticks: { * onClick: () => * return {ticks: {style: {stroke: "green"}}, tickLabels: {style: {stroke: "black"}} *}} */ events: _react.PropTypes.shape({ parent: _react.PropTypes.object, axis: _react.PropTypes.object, axisLabel: _react.PropTypes.object, grid: _react.PropTypes.object, ticks: _react.PropTypes.object, tickLabels: _react.PropTypes.object }), /** * The gridComponent prop takes in an entire component which will be used * to create grid lines. The new element created from the passed gridComponent * will be supplied with the following properties: x1, y1, x2, y2, tick, style 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 a gridComponent * is not supplied, VictoryAxis will render its default GridLine component. */ gridComponent: _react.PropTypes.element, /** * 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 label prop defines the label that will appear along the axis. This * prop should be given as a value or an entire, HTML-complete label * component. If a label component is given, it will be cloned. The new * element's properties x, y, textAnchor, verticalAnchor, and transform * will have defaults provided by the axis; styles filled out with * defaults provided by the axis, and overrides from the label component. * If a value is given, a new VictoryLabel will be created with props and * styles from the axis. */ label: _react.PropTypes.any, /** * This value describes how far from the "edge" of its permitted area each axis * will be set back in the x-direction. If this prop is not given, * the offset is calculated based on font size, axis orientation, and label padding. */ offsetX: _react.PropTypes.number, /** * This value describes how far from the "edge" of its permitted area each axis * will be set back in the y-direction. If this prop is not given, * the offset is calculated based on font size, axis orientation, and label padding. */ offsetY: _react.PropTypes.number, /** * The orientation prop specifies the position and orientation of your axis. */ orientation: _react.PropTypes.oneOf(["top", "bottom", "left", "right"]), /** * 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 axis should use. This prop can be * given as a `d3-scale@0.3.0` function or as a string corresponding to a supported d3-string * function. * @examples d3Scale.time(), "linear", "time", "log", "sqrt" */ scale: _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 VictoryAxis with other components within an enclosing <svg> tag. */ standalone: _react.PropTypes.bool, /** * The style prop specifies styles for your VictoryAxis. 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. * @examples {axis: {stroke: "#756f6a"}, grid: {stroke: "grey"}, ticks: {stroke: "grey"}, * tickLabels: {fontSize: 10, padding: 5}, axisLabel: {fontSize: 16, padding: 20}} */ style: _react.PropTypes.shape({ parent: _react.PropTypes.object, axis: _react.PropTypes.object, axisLabel: _react.PropTypes.object, grid: _react.PropTypes.object, ticks: _react.PropTypes.object, tickLabels: _react.PropTypes.object }), /** * The tickComponent prop takes in an entire component which will be used * to create tick lines. The new element created from the passed tickComponent * will be supplied with the following properties: x1, y1, x2, y2, tick, style 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 a tickComponent * is not supplied, VictoryAxis will render its default Tick component. */ tickComponent: _react.PropTypes.element, /** * The tickCount prop specifies approximately how many ticks should be drawn on the axis if * tickValues are not explicitly provided. This values is calculated by d3 scale and * prioritizes returning "nice" values and evenly spaced ticks over an exact numnber of ticks */ tickCount: _victoryCore.PropTypes.nonNegative, /** * The tickLabelComponent prop takes in an entire component which will be used * to create the tick labels. The new element created from the passed tickLabelComponent * will be supplied with the following properties: x, y, verticalAnchor, textAnchor, * angle, tick, style 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 an tickLabelComponent is not supplied, a new * VictoryLabel will be created with props described above */ tickLabelComponent: _react.PropTypes.element, /** * The tickFormat prop specifies how tick values should be expressed visually. * tickFormat can be given as a function to be applied to every tickValue, or as * an array of display values for each tickValue. * @examples d3.time.format("%Y"), (x) => x.toPrecision(2), ["first", "second", "third"] */ tickFormat: _react.PropTypes.oneOfType([_react.PropTypes.func, _victoryCore.PropTypes.homogeneousArray]), /** * The tickValues prop explicitly specifies which tick values to draw on the axis. * @examples ["apples", "bananas", "oranges"], [2, 4, 6, 8] */ tickValues: _victoryCore.PropTypes.homogeneousArray, /** * 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 }; VictoryAxis.defaultProps = { axisComponent: _react2.default.createElement(_axisLine2.default, null), axisLabelComponent: _react2.default.createElement(_victoryCore.VictoryLabel, null), tickLabelComponent: _react2.default.createElement(_victoryCore.VictoryLabel, null), tickComponent: _react2.default.createElement(_tick2.default, null), gridComponent: _react2.default.createElement(_grid2.default, null), events: {}, height: 300, padding: 50, scale: "linear", standalone: true, tickCount: 5, width: 450 }; VictoryAxis.getDomain = _helperMethods2.default.getDomain.bind(_helperMethods2.default); VictoryAxis.getAxis = _helperMethods2.default.getAxis.bind(_helperMethods2.default); VictoryAxis.getScale = _helperMethods2.default.getScale.bind(_helperMethods2.default); VictoryAxis.getStyles = getStyles; exports.default = VictoryAxis;