recharts
Version:
React charts
201 lines (174 loc) • 5.64 kB
JavaScript
/**
* @fileOverview Line
*/
import React, { Component, PropTypes } from 'react';
import classNames from 'classnames';
import Curve from '../shape/Curve';
import Dot from '../shape/Dot';
import Layer from '../container/Layer';
import pureRender from 'pure-render-decorator';
import ReactUtils, { PRESENTATION_ATTRIBUTES } from '../util/ReactUtils';
import { findDOMNode } from 'react-dom';
import Animate from 'react-smooth';
class Line extends Component {
static displayName = 'Line';
static propTypes = {
...PRESENTATION_ATTRIBUTES,
className: PropTypes.string,
type: PropTypes.oneOf(['linear', 'monotone', 'step', 'stepBefore', 'stepAfter']),
unit: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
name: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
dataKey: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
yAxisId: PropTypes.number,
xAxisId: PropTypes.number,
legendType: PropTypes.string,
formatter: PropTypes.func,
// whether have dot in line
dot: PropTypes.oneOfType([PropTypes.object, PropTypes.element, PropTypes.bool]),
label: PropTypes.oneOfType([PropTypes.object, PropTypes.element, PropTypes.bool]),
points: PropTypes.arrayOf(PropTypes.shape({
x: PropTypes.number,
y: PropTypes.number,
value: PropTypes.value,
})),
onMouseEnter: PropTypes.func,
onMouseLeave: PropTypes.func,
onClick: PropTypes.func,
isAnimationActive: PropTypes.bool,
animationBegin: PropTypes.number,
animationDuration: PropTypes.number,
animationEasing: PropTypes.oneOf(['ease', 'ease-in', 'ease-out', 'ease-in-out', 'linear']),
};
static defaultProps = {
xAxisId: 0,
yAxisId: 0,
dot: true,
legendType: 'line',
stroke: '#3182bd',
strokeWidth: 1,
fill: '#fff',
points: [],
onClick() {},
onMouseEnter() {},
onMouseLeave() {},
isAnimationActive: true,
animationBegin: 0,
animationDuration: 1500,
animationEasing: 'ease',
};
state = {
isAnimationFinished: false,
totalLength: 0,
};
componentDidMount() {
const { isAnimationActive } = this.props;
if (!isAnimationActive) {
return;
}
const curveDom = findDOMNode(this.refs.curve);
const totalLength = (curveDom && curveDom.getTotalLength && curveDom.getTotalLength()) || 0;
this.setState({ totalLength });
}
handleAnimationEnd() {
this.setState({ isAnimationFinished: true });
}
renderDots() {
const { isAnimationActive } = this.props;
if (isAnimationActive && !this.state.isAnimationFinished) {
return null;
}
const { dot, points } = this.props;
const lineProps = ReactUtils.getPresentationAttributes(this.props);
const customDotProps = ReactUtils.getPresentationAttributes(dot);
const isDotElement = React.isValidElement(dot);
const dots = points.map((entry, i) => {
const dotProps = {
key: `dot-${i}`,
r: 3,
...lineProps,
...customDotProps,
cx: entry.x,
cy: entry.y,
index: i,
payload: entry,
};
return isDotElement ?
React.cloneElement(dot, dotProps) :
<Dot className="recharts-line-dot" {...dotProps}/>;
});
return <Layer className="recharts-line-dots">{dots}</Layer>;
}
renderLabels() {
const { points, label } = this.props;
const lineProps = ReactUtils.getPresentationAttributes(this.props);
const customLabelProps = ReactUtils.getPresentationAttributes(label);
const isLabelElement = React.isValidElement(label);
const labels = points.map((entry, i) => {
const x = entry.x + entry.width / 2;
const y = entry.y;
const labelProps = {
textAnchor: 'middle',
...entry,
...lineProps,
...customLabelProps,
index: i,
key: `label-${i}`,
payload: entry,
};
return isLabelElement ? React.cloneElement(label, labelProps) : (
<text {...labelProps} className="recharts-line-label">{entry.value}</text>
);
});
return <Layer className="recharts-line-labels">{labels}</Layer>;
}
render() {
const {
dot,
points,
label,
className,
isAnimationActive,
animationBegin,
animationDuration,
animationEasing,
...other,
} = this.props;
const { totalLength } = this.state;
if (!points || !points.length) {
return null;
}
const hasSinglePoint = points.length === 1;
const layerClass = classNames('recharts-line', className);
return (
<Layer className={layerClass}>
{!hasSinglePoint && (
<Animate isActive={isAnimationActive}
begin={animationBegin}
canBegin={totalLength > 0}
from={'0px ' + (totalLength === 0 ? 1 : totalLength) + 'px'}
to={totalLength + 'px 0px'}
easing={animationEasing}
duration={animationDuration}
attributeName="strokeDasharray"
onAnimationEnd={::this.handleAnimationEnd}
>
<Curve
{...other}
className="recharts-line-curve"
fill="none"
onMouseEnter={this.props.onMouseEnter}
onMouseLeave={this.props.onMouseLeave}
onClick={this.props.onClick}
points={points}
ref="curve"
/>
</Animate>
)}
{(hasSinglePoint || dot) && this.renderDots()}
{label && this.renderLabels()}
</Layer>
);
}
}
export default Line;