packages/showcase/plot/complex-chart.js (168 lines of code) (raw):
// Copyright (c) 2016 - 2017 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
import React from 'react';
import {
XAxis,
YAxis,
FlexibleWidthXYPlot,
HorizontalGridLines,
LineSeries,
VerticalRectSeries,
DiscreteColorLegend,
Crosshair
} from 'react-vis';
/**
* Get the array of x and y pairs.
* The function tries to avoid too large changes of the chart.
* @param {number} total Total number of values.
* @returns {Array} Array of data.
* @private
*/
function getRandomSeriesData(total) {
const result = [];
let lastY = Math.random() * 40 - 20;
let y;
const firstY = lastY;
for (let i = 0; i < Math.max(total, 3); i++) {
y = Math.random() * firstY - firstY / 2 + lastY;
result.push({
left: i,
top: y
});
lastY = y;
}
return result;
}
export default class Example extends React.Component {
constructor(props) {
super(props);
const totalValues = Math.random() * 50;
this.state = {
crosshairValues: [],
series: [
{
title: 'Apples',
disabled: false,
data: getRandomSeriesData(totalValues)
},
{
title: 'Bananas',
disabled: false,
data: getRandomSeriesData(totalValues)
}
]
};
}
/**
* A callback to format the crosshair items.
* @param {Object} values Array of values.
* @returns {Array<Object>} Array of objects with titles and values.
* @private
*/
_formatCrosshairItems = values => {
const {series} = this.state;
return values.map((v, i) => {
return {
title: series[i].title,
value: v.top
};
});
};
/**
* Format the title line of the crosshair.
* @param {Array} values Array of values.
* @returns {Object} The caption and the value of the title.
* @private
*/
_formatCrosshairTitle = values => {
return {
title: 'X',
value: values[0].left
};
};
/**
* Click handler for the legend.
* @param {Object} item Clicked item of the legend.
* @param {number} i Index of the legend.
* @private
*/
_legendClickHandler = (item, i) => {
const {series} = this.state;
series[i].disabled = !series[i].disabled;
this.setState({series});
};
/**
* Event handler for onMouseLeave.
* @private
*/
_mouseLeaveHandler = () => {
this.setState({crosshairValues: []});
};
/**
* Event handler for onNearestX.
* @param {Object} value Selected value.
* @param {number} index Index of the series.
* @private
*/
_nearestXHandler = (value, {index}) => {
const {series} = this.state;
this.setState({
crosshairValues: series.map(s => s.data[index])
});
};
_updateButtonClicked = () => {
const {series} = this.state;
const totalValues = Math.random() * 50;
series.forEach(s => {
s.data = getRandomSeriesData(totalValues);
});
this.setState({series});
};
render() {
const {series, crosshairValues} = this.state;
return (
<div className="example-with-click-me">
<div className="legend">
<DiscreteColorLegend
onItemClick={this._legendClickHandler}
width={180}
items={series}
/>
</div>
<div className="chart">
<FlexibleWidthXYPlot
animation
getX={d => d.left}
getY={d => d.top}
onMouseLeave={this._mouseLeaveHandler}
xDomain={[-0.5, series[0].data.length - 1]}
height={300}
>
<HorizontalGridLines />
<YAxis
className="cool-custom-name"
tickSizeInner={0}
tickSizeOuter={8}
/>
<XAxis
className="even-cooler-custom-name"
tickSizeInner={0}
tickSizeOuter={8}
/>
<VerticalRectSeries
data={series[0].data.map(({left, top}) => ({
x0: left - 0.5,
left: left + 0.5,
top
}))}
stroke="white"
onNearestX={this._nearestXHandler}
{...(series[0].disabled ? {opacity: 0.2} : null)}
/>
<LineSeries
data={series[1].data}
curve="curveMonotoneX"
{...(series[1].disabled ? {opacity: 0.2} : null)}
/>
<Crosshair
itemsFormat={this._formatCrosshairItems}
titleFormat={this._formatCrosshairTitle}
values={crosshairValues}
/>
</FlexibleWidthXYPlot>
</div>
<button className="click-me" onClick={this._updateButtonClicked}>
Click to update
</button>
</div>
);
}
}