--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/clientjs/packages/dashboard-components/src/ui/RowChart.jsx Fri Sep 14 17:57:34 2018 +0200
@@ -0,0 +1,122 @@
+/* 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);