victory-chart
Version:
Chart Component for Victory
562 lines (519 loc) • 25.1 kB
JavaScript
"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;