victory-chart
Version:
Chart Component for Victory
488 lines (446 loc) • 20 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
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 _defaults = require("lodash/defaults");
var _defaults2 = _interopRequireDefault(_defaults);
var _react = require("react");
var _react2 = _interopRequireDefault(_react);
var _victoryCore = require("victory-core");
var _axisLine = require("./axis-line");
var _axisLine2 = _interopRequireDefault(_axisLine);
var _axisLabel = require("./axis-label");
var _axisLabel2 = _interopRequireDefault(_axisLabel);
var _grid = require("./grid");
var _grid2 = _interopRequireDefault(_grid);
var _tick = require("./tick");
var _tick2 = _interopRequireDefault(_tick);
var _tickLabel = require("./tick-label");
var _tickLabel2 = _interopRequireDefault(_tickLabel);
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, _defaults2.default)(parentStyleProps, style.parent, defaultStyles.parent),
axis: (0, _defaults2.default)({}, style.axis, defaultStyles.axis),
axisLabel: (0, _defaults2.default)({}, style.axisLabel, defaultStyles.axisLabel),
grid: (0, _defaults2.default)({}, style.grid, defaultStyles.grid),
ticks: (0, _defaults2.default)({}, style.ticks, defaultStyles.ticks),
tickLabels: (0, _defaults2.default)({}, style.tickLabels, defaultStyles.tickLabels)
};
};
var VictoryAxis = function (_React$Component) {
_inherits(VictoryAxis, _React$Component);
function VictoryAxis() {
_classCallCheck(this, VictoryAxis);
return _possibleConstructorReturn(this, Object.getPrototypeOf(VictoryAxis).apply(this, arguments));
}
_createClass(VictoryAxis, [{
key: "componentWillMount",
value: function componentWillMount() {
this.state = {
axisState: {},
axisLabelState: {},
gridState: {},
ticksState: {},
tickLabelsState: {}
};
}
}, {
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 getBoundEvents = _victoryCore.Helpers.getEvents.bind(this);
return _react2.default.createElement(_axisLine2.default, _extends({ key: "line",
events: getBoundEvents(this.props.events.axis, "axis"),
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
}, this.state.axisState[0]));
}
}, {
key: "renderTicks",
value: function renderTicks(props, layoutProps, tickProps) {
var _this2 = this;
var style = layoutProps.style;
var orientation = layoutProps.orientation;
var scale = tickProps.scale;
var ticks = tickProps.ticks;
var stringTicks = tickProps.stringTicks;
var tickFormat = _helperMethods2.default.getTickFormat(props, tickProps);
return ticks.map(function (tick, index) {
var isVertical = orientation === "left" || orientation === "right";
var tickPosition = _helperMethods2.default.getTickPosition(style.ticks, orientation, isVertical);
var getBoundEvents = _victoryCore.Helpers.getEvents.bind(_this2);
var tickComponent = _react2.default.createElement(_tick2.default, _extends({ key: "tick-" + index,
index: index,
events: getBoundEvents(_this2.props.events.ticks, "ticks"),
position: tickPosition,
tick: stringTicks ? props.tickValues[tick - 1] : tick,
style: style.ticks
}, _this2.state.ticksState[index]));
var label = tickFormat.call(_this2, tick, index);
var labelComponent = void 0;
if (label) {
labelComponent = _react2.default.createElement(_tickLabel2.default, _extends({ key: "tick-label-" + index,
index: index,
events: getBoundEvents(_this2.props.events.tickLabels, "tickLabels"),
position: tickPosition,
label: label,
tick: stringTicks ? props.tickValues[tick - 1] : tick,
orientation: orientation,
isVertical: isVertical,
style: style.tickLabels
}, _this2.state.tickLabelsState[index]));
}
var groupPosition = scale(tick);
var transform = isVertical ? "translate(0, " + groupPosition + ")" : "translate(" + groupPosition + ", 0)";
return _react2.default.createElement(
"g",
{ key: "tick-group-" + index, transform: transform },
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));
return ticks.map(function (tick, index) {
// determine the position and translation of each gridline
var position = scale(tick);
var getBoundEvents = _victoryCore.Helpers.getEvents.bind(_this3);
return _react2.default.createElement(_grid2.default, _extends({ key: "grid-" + index,
index: index,
events: getBoundEvents(_this3.props.events.grid, "grid"),
tick: stringTicks ? props.tickValues[tick - 1] : tick,
x2: x2,
y2: y2,
xTransform: isVertical ? -xOffset : position,
yTransform: isVertical ? position : yOffset,
style: style.grid
}, _this3.state.gridState[index]));
});
}
}, {
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 getBoundEvents = _victoryCore.Helpers.getEvents.bind(this);
return _react2.default.createElement(_axisLabel2.default, _extends({
events: getBoundEvents(this.props.events.axisLabel, "axisLabels"),
verticalAnchor: verticalAnchor,
transform: transform,
position: { x: x, y: y },
label: this.props.label,
style: style.axisLabel
}, this.state.axisLabelState[0]));
}
}, {
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,
/**
* 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 unique index on the state object of VictoryAxis
* i.e. `this.state.axisState[axisIndex] = {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 {axis: {
* onClick: () => return {style: {stroke: "green"}}
*}}
*/
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 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 tickCount prop specifies how many ticks should be drawn on the axis if
* tickValues are not explicitly provided.
*/
tickCount: _victoryCore.PropTypes.nonNegative,
/**
* 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 = {
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;