diff -r 000000000000 -r 5f4fcbc80b37 clientjs/packages/dashboard-components/src/ui/RowChart.jsx --- /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 ( +
+
{ this.node = elt; }} /> +
+ ); + } +} + +export default withRouter(RowChart);