/* eslint func-names: "off" */
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import * as d3 from 'd3';
import { withRouter } from 'react-router-dom';
import './RowChart.scss';
/**
* TODO: As a first implementation we have chosen to delagate the barchart DOM drawing to D3.
* This may not be the optimum solution. This may have to be rewrittten to use
* React for elementt creation and D3 as the visualisation kernel.
*/
class RowChart extends Component {
static propTypes = {
data: PropTypes.PropTypes.arrayOf(PropTypes.object).isRequired,
tagPrefix: PropTypes.string.isRequired,
history: PropTypes.object.isRequired,
}
componentDidMount() {
this.drawChart();
}
componentDidUpdate() {
this.drawChart();
}
drawChart() {
const { node } = this;
const { data, tagPrefix } = this.props;
const width = 800;
const barHeight = 30;
const height = barHeight * data.length;
const valueMargin = 4;
const yAxisHMargin = 10;
const yAxisVMargin = 10;
const barVMargin = 10;
const y = d3.scaleBand()
.rangeRound([0, height])
.padding(0.1)
.domain(data.map(d => d.tag));
const yAxis = d3.axisLeft(y);
d3.select(node).selectAll('svg').remove();
const chart = d3.select(node)
.append('svg')
.attr('width', '100%');
const barsContainer = chart.append('g')
.attr('class', 'bars-container');
const yAxisContainer = chart.append('g')
.attr('class', 'y-axis axis')
.call(yAxis);
let yAxisWidth = 0;
chart.selectAll('.y-axis text')
.each(function () { yAxisWidth = Math.max(yAxisWidth, this.getBBox().width); });
const x = d3.scaleLinear()
.range([0, width - yAxisWidth - yAxisHMargin])
.domain([0, d3.max(data, d => d.count)]);
const xAxis = d3.axisBottom(x);
const xAxisContainer = chart.append('g')
.attr('class', 'y-axis axis')
.attr('transform', `translate(${[yAxisWidth + 2 * yAxisHMargin, height + yAxisVMargin]})`)
.call(xAxis);
const bar = barsContainer.selectAll('.bars-container')
.data(data)
.enter().append('g')
.attr('transform', (d, i) => `translate(0,${i * barHeight})`)
.attr('class', 'graph-bar');
bar.append('rect')
.attr('width', d => x(d.count))
.attr('height', barHeight - barVMargin)
.attr('fill', d => d.color)
.attr('stroke', d => d.color)
.attr('transform', 'translate(0,5)')
.on('click', (d) => {
const { history } = this.props;
history.push(`/annotations/${tagPrefix}${d.tag}`);
});
bar.append('text')
.attr('y', barHeight / 2)
.attr('dy', '.35em')
.attr('dx', -valueMargin)
.attr('text-anchor', 'end')
.text(d => d.count)
.attr('x', function (d) {
const xWidth = this.getBBox().width;
return Math.max(xWidth + valueMargin, x(d.count));
});
yAxisContainer.attr('transform', `translate(${yAxisWidth + yAxisHMargin},0)`);
barsContainer.attr('transform', `translate(${yAxisWidth + 2 * yAxisHMargin},0)`);
const totalHeight = height + xAxisContainer.node().getBBox().height + yAxisVMargin;
const totalWidth = width + 2 * yAxisHMargin;
chart.attr('viewBox', [0, 0, totalWidth, totalHeight].join(' '));
}
render() {
return (
<div className="row chart-container">
<div className="col col-12" ref={(elt) => { this.node = elt; }} />
</div>
);
}
}
export default withRouter(RowChart);