Skip to content
Snippets Groups Projects
BarChart.js 4.32 KiB
Newer Older
Matthieu BELLUCCI's avatar
Matthieu BELLUCCI committed
// From https://codesandbox.io/s/w0ol90x9z5
import React, { Component } from "react";
import NodeGroup from "react-move/NodeGroup";
/* import {
  getAppendedData,
  getTruncatedData,
  getUpdatedData
} from "./helpers"; */
import "./barchart.css";

Matthieu BELLUCCI's avatar
Matthieu BELLUCCI committed
let barHeight = 50;
Matthieu BELLUCCI's avatar
Matthieu BELLUCCI committed
let barPadding = 2;
let widthScale = d => d * 5;

function getTextWidth(text, font) {
  const canvas = document.createElement('canvas');
  const context = canvas.getContext('2d');

  context.font = font || getComputedStyle(document.body).font;

  return context.measureText(text).width;
}

Matthieu BELLUCCI's avatar
Matthieu BELLUCCI committed
function chooseColour(value) {
  if (value >= 70) {
Matthieu BELLUCCI's avatar
Matthieu BELLUCCI committed
    return "green";
  } else if (value <= 30) {
Matthieu BELLUCCI's avatar
Matthieu BELLUCCI committed
    return "red";
  } else {
    return "orange";
  }
};

Matthieu BELLUCCI's avatar
Matthieu BELLUCCI committed
function BarGroup(props) {
  let width = widthScale(props.state.value);
  let yMid = barHeight * 0.5;
Matthieu BELLUCCI's avatar
Matthieu BELLUCCI committed
  const barColour = chooseColour(props.state.value)
Matthieu BELLUCCI's avatar
Matthieu BELLUCCI committed
  return (
    <g className="bar-group" transform={`translate(0, ${props.state.y})`}>
      <rect
        y={barPadding * 0.5}
        width={width}
        height={barHeight - barPadding}
        style={{ fill: barColour, opacity: props.state.opacity }}
      />
      <text
        className="value-label"
Matthieu BELLUCCI's avatar
Matthieu BELLUCCI committed
        y={yMid}
        alignmentBaseline="middle"
      >
        {props.state.value.toFixed(0)}
      </text>
      <text
        className="name-label"
        x="-6"
        y={yMid}
        alignmentBaseline="middle"
        style={{ opacity: props.state.opacity }}
      >
        {props.data.name}
      </text>
    </g>
  );
}

class BarChart extends Component {
  constructor(props) {
    super(props);
    this.state = {
      data: this.handleData(props.data)
    };
    let max_name_width = 10;
    for (let i = 0; i < this.state.data.length; i++){
      let text_width = getTextWidth(this.state.data[i].name);
      if (text_width > max_name_width){
        max_name_width = text_width;
      }
    }
    const svg_width = widthScale(100) + max_name_width;
    const svg_height = barHeight*this.state.data.length + 10;
    this.state = {
      data: this.state.data,
      max_name_width: max_name_width,
      svg_width: svg_width,
      svg_height: svg_height,
    };
Matthieu BELLUCCI's avatar
Matthieu BELLUCCI committed

/*     this.handleAdd = this.handleAdd.bind(this);
    this.handleRemove = this.handleRemove.bind(this);
    this.handleUpdate = this.handleUpdate.bind(this);
  }

  handleAdd() {
    this.setState({
      data: getAppendedData(this.state.data)
    });
  }

  handleRemove() {
    this.setState({
      data: getTruncatedData(this.state.data)
    });
  }

  handleUpdate() {
    this.setState({
      data: getUpdatedData(this.state.data)
    }); */
  };

  handleData(arr) {
    var items = Object.keys(arr).map((key) => [key, arr[key].probability]);
    items.sort((first, second) => second[1] - first[1]);
    let numItems = items.length;
    let data = Array(numItems);
    for (let i = 0; i < items.length; i++) {
Matthieu BELLUCCI's avatar
Matthieu BELLUCCI committed
        let item = {
            id: "id-" + i,
            value: items[i][1],
            name: items[i][0],
Matthieu BELLUCCI's avatar
Matthieu BELLUCCI committed
        };
        data[i] = item
    }
    data.map(d => d)
    return data;
  };
  startTransition(d, i) {
    return { value: 0, y: i * barHeight, opacity: 0 };
  }

  enterTransition(d) {
    return { value: [d.value], opacity: [1], timing: { duration: 250 } };
  }

  updateTransition(d, i) {
    return { value: [d.value], y: [i * barHeight], timing: { duration: 300 } };
  }

  leaveTransition(d) {
    return { y: [-barHeight], opacity: [0], timing: { duration: 250 } };
  }

  render() {
    return (
      <div>
        <svg width="100%" height={this.state.svg_height} viewBox={"0 0 " + this.state.svg_width + " "+this.state.svg_height} preserveAspectRatio="xMinYMin">
          <g className="chart" transform={"translate(" + this.state.max_name_width.toString() + ",10)"}>
Matthieu BELLUCCI's avatar
Matthieu BELLUCCI committed
            <NodeGroup
              data={this.state.data}
              keyAccessor={d => d.name}
              start={this.startTransition}
              enter={this.enterTransition}
              update={this.updateTransition}
              leave={this.leaveTransition}
            >
              {nodes => (
                <g>
                  {nodes.map(({ key, data, state }) => (
                    <BarGroup key={key} data={data} state={state} />
                  ))}
                </g>
              )}
            </NodeGroup>
          </g>
        </svg>
      </div>
    );
  }
}

export default BarChart;