project link: https://chuanshuoge1-react-chart3.herokuapp.com/
legend, chart, table
increase energy slider, other sectors decrease proportionally.
add basic materials sectors
increase percentage for basic materials
hover on energy, display sector, percentage
delete energy sector, other sectors distributed proportionally
pieChart.js
import React, { Component } from 'react';import '../App.css';
import '../../node_modules/react-vis/dist/style.css';
import { Table } from 'reactstrap';
import TextField from '@material-ui/core/TextField';
import { withStyles } from '@material-ui/core/styles';
import MenuItem from '@material-ui/core/MenuItem';
import DeleteIcon from '@material-ui/icons/Delete';
import Button from '@material-ui/core/Button';
import {
XYPlot,
XAxis,
YAxis,
VerticalGridLines,
HorizontalGridLines,
ArcSeries,
CircularGridLines,
DiscreteColorLegend,
} from 'react-vis';
const styles = theme => ({
textField: {
margin: 0,
width: 130,
},
textFieldShort: {
margin: 0,
width: 50,
},
button: {
margin: theme.spacing.unit,
},
});
class PieChart extends Component {
constructor(props) {
super(props);
this.state = {
data: [
{ angle0: 0, angle: 2 * Math.PI / 3, title: 'Financial Service' },
{ angle0: 2 * Math.PI / 3, angle: 4 * Math.PI / 3, title: 'Energy' },
{ angle0: 4 * Math.PI / 3, angle: 2 * Math.PI, title: 'Technology' },
],
mouseOverData: { title: 'mouse over', percentage: 0 },
newSector: '',
};
}
handlePercentageChange(percent, index) {
//calculate old angle of updated arc
const oldAngle = this.state.data[index].angle - this.state.data[index].angle0;
//calculate percentage of each sector in new data set
const newPercentage = this.state.data.map((item, index2) => {
return index === index2 ? percent / 100 :
(item.angle - item.angle0) / (2 * Math.PI - oldAngle) * (1 - percent / 100);
})
let angleEnd = 0, angleStart = 0;
const newData = this.state.data.map((item, index) => {
angleStart = angleEnd;
angleEnd = angleStart + newPercentage[index] * (2 * Math.PI);
return {
title: item.title,
angle0: angleStart,
angle: angleEnd,
}
})
this.setState({ data: newData });
}
handleTitleChange(value, index) {
const dataLength = this.state.data.length;
const oldProperty = this.state.data[index];
//check if it is an update or add request
const newProperty = {
title: value,
angle0: index < dataLength ? oldProperty.angle0 : null,
angle: index < dataLength ? oldProperty.angle : null,
}
const newData = [...this.state.data];
newData.splice(index, 1, newProperty);
this.setState({ data: newData });
}
async handleDelete(index) {
//set percentage of deleted data to 0
await this.handlePercentageChange(0, index);
const newData = [...this.state.data];
newData.splice(index, 1);
this.setState({ data: newData });
}
render() {
const { classes } = this.props;
const stateData = this.state.data;
return (
<div>
<h4 className="my-subTitle">Mutual Fund Sectors</h4>
<DiscreteColorLegend
orientation="horizontal"
width={300}
items={
stateData.map((item, index) => {
return { title: item.title, color: colorArray[index] }
})}
/>
<XYPlot
margin={{ top: 0, bottom: 0, left: 0, right: 0 }}
xDomain={[-5, 5]}
yDomain={[-5, 5]}
width={300}
height={300}>
<XAxis top={150} tickValues={[-4, -3, -2, -1, 0, 1, 2, 3, 4]} />
<YAxis left={150} tickValues={[-4, -3, -2, -1, 0, 1, 2, 3, 4]} />
<CircularGridLines tickValues={[-4, -3, -2, -1,]} />
{
stateData.map((item, index) => {
const arcData = {
angle0: item.angle0,
angle: item.angle,
radius0: 90,
radius: 120,
}
return <ArcSeries
key={index}
animation
center={{ x: 0, y: 0 }}
radiusType={'literal'}
data={[arcData,]}
color={colorArray[index]}
onSeriesMouseOver={(event) => {
const percent = (stateData[index].angle - stateData[index].angle0) / (2 * Math.PI);
this.setState({ mouseOverData: { title: stateData[index].title, percentage: percent.toFixed(4) } });
}}
onSeriesMouseOut={(event) => {
this.setState({ mouseOverData: { title: 'mouse over', percentage: 0 } });
}}
/>
})
}
</XYPlot>
Sector: {this.state.mouseOverData.title} |
Percentage: {this.state.mouseOverData.percentage * 100} %
<Table bordered hover size="sm">
<thead className="my-table-header">
<tr>
<th>Sector</th>
<th>Percentage</th>
<th>Delete</th>
</tr>
</thead>
<tbody>
{
stateData.map((item, index) =>
<tr key={index}>
<td>
<TextField
required
onChange={(e) => this.handleTitleChange(e.target.value, index)}
value={item.title}
className={classes.textField}
/>
</td>
<td>
<TextField
required
onChange={(e) => this.handlePercentageChange(e.target.value, index)}
value={((item.angle - item.angle0) * 100 / (2 * Math.PI)).toFixed(2)}
type="number"
className={classes.textFieldShort}
/>
<input type="range" min="0" max="10000"
value={(item.angle - item.angle0) / (2 * Math.PI) * 10000}
onChange={(e) => this.handlePercentageChange(e.target.value / 100, index)}
/>
</td>
<td>
<DeleteIcon className="deleteIcon"
onClick={() => this.handleDelete(index)} />
</td>
</tr>
)
}
{/*-----------add new sector----------*/}
<tr>
<td>
<TextField
required
label="New Sector"
onChange={(e) => this.setState({ newSector: e.target.value })}
className={classes.textField}
/>
</td>
<td>
<Button variant="contained" size="small"
className={classes.button}
onClick={() => this.handleTitleChange(this.state.newSector, stateData.length)}>
Add new
</Button>
</td>
<td></td>
<td></td>
</tr>
</tbody>
</Table>
</div>
);
}
}
const colorArray = [
"#ef0707",
"#dbef07",
"#07c8ef",
"#8e07ef",
"#ef0799",
"#2b5b36",
"#ef6707",
"#35ef07",
"#072def",
"#ef0707",
"#392b5b",
"#dee5e5",
"#12939A",
"#ef2907",
"#ef5007",
"#ef8a07",
"#efd307",
"#92ef07",
"#07ef7e",
"#07efeb",
"#0786ef",
"#6307ef",
"#b807ef",
"#df07ef",
"#ef0763",
"#5b542c",
"#2b465b",
"#5b2b52",
"#000000",
];
export default withStyles(styles)(PieChart);
reference:
No comments:
Post a Comment