diff --git a/package.json b/package.json
index 785f0ff3..e345a228 100644
--- a/package.json
+++ b/package.json
@@ -88,7 +88,7 @@
"less": "^2.7.2",
"less-loader": "^4.0.3",
"react-addons-perf": "^15.4.2",
- "react-measure": "^1.4.7",
+ "react-container-dimensions": "^1.4.1",
"react-testutils-additions": "^15.2.0",
"react-transition-group": "^1.1.2",
"rimraf": "^2.6.1",
diff --git a/src/app/components/LineChart.jsx b/src/app/components/LineChart.jsx
index 9dd1d3ee..b4113ee6 100644
--- a/src/app/components/LineChart.jsx
+++ b/src/app/components/LineChart.jsx
@@ -1,283 +1,281 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import Measure from 'react-measure';
-import * as d3 from 'd3';
-import TranslatedComponent from './TranslatedComponent';
-
-const MARGIN = { top: 15, right: 20, bottom: 35, left: 60 };
-
-/**
- * Line Chart
- */
-export default class LineChart extends TranslatedComponent {
-
- static defaultProps = {
- code: '',
- xMin: 0,
- yMin: 0,
- points: 20,
- colors: ['#ff8c0d'],
- aspect: 0.5
- };
-
- static propTypes = {
- func: PropTypes.func.isRequired,
- xLabel: PropTypes.string.isRequired,
- xMin: PropTypes.number,
- xMax: PropTypes.number.isRequired,
- xUnit: PropTypes.string.isRequired,
- xMark: PropTypes.number,
- yLabel: PropTypes.string.isRequired,
- yMin: PropTypes.number,
- yMax: PropTypes.number.isRequired,
- yUnit: PropTypes.string,
- series: PropTypes.array,
- colors: PropTypes.array,
- points: PropTypes.number,
- aspect: PropTypes.number,
- code: PropTypes.string,
- };
-
- /**
- * Constructor
- * @param {Object} props React Component properties
- * @param {Object} context React Component context
- */
- constructor(props, context) {
- super(props);
-
- this._updateDimensions = this._updateDimensions.bind(this);
- this._updateSeries = this._updateSeries.bind(this);
- this._tooltip = this._tooltip.bind(this);
- this._showTip = this._showTip.bind(this);
- this._hideTip = this._hideTip.bind(this);
- this._moveTip = this._moveTip.bind(this);
-
- const series = props.series;
-
- let xScale = d3.scaleLinear();
- let yScale = d3.scaleLinear();
- let xAxisScale = d3.scaleLinear();
-
- this.xAxis = d3.axisBottom(xAxisScale).tickSizeOuter(0);
- this.yAxis = d3.axisLeft(yScale).ticks(6).tickSizeOuter(0);
-
- this.state = {
- xScale,
- xAxisScale,
- yScale,
- tipHeight: 2 + (1.2 * (series ? series.length : 0.8)),
- dimensions: {
- width: 100,
- height: 100
- }
- };
- }
-
- /**
- * Update tooltip content
- * @param {number} xPos x coordinate
- */
- _tooltip(xPos) {
- let { xLabel, yLabel, xUnit, yUnit, func, series } = this.props;
- let { xScale, yScale } = this.state;
- let { width } = this.state.dimensions;
- let { formats, translate } = this.context.language;
- let x0 = xScale.invert(xPos),
- y0 = func(x0),
- tips = this.tipContainer,
- yTotal = 0,
- flip = (xPos / width > 0.50),
- tipWidth = 0,
- tipHeightPx = tips.selectAll('rect').node().getBoundingClientRect().height;
-
-
- xPos = xScale(x0); // Clamp xPos
-
- tips.selectAll('text.text-tip.y').text(function(d, i) {
- let yVal = series ? y0[series[i]] : y0;
- yTotal += yVal;
- return (series ? translate(series[i]) : '') + ' ' + formats.f2(yVal);
- }).append('tspan').attr('class', 'metric').text(yUnit ? ' ' + yUnit : '');
-
- tips.selectAll('text').each(function() {
- if (this.getBBox().width > tipWidth) {
- tipWidth = Math.ceil(this.getBBox().width);
- }
- });
-
- let tipY = Math.floor(yScale(yTotal / (series ? series.length : 1)) - (tipHeightPx / 2));
-
- tipWidth += 8;
- tips.attr('transform', 'translate(' + xPos + ',' + tipY + ')');
- tips.selectAll('text.text-tip').attr('x', flip ? -12 : 12).style('text-anchor', flip ? 'end' : 'start');
- tips.selectAll('text.text-tip.x').text(formats.f2(x0)).append('tspan').attr('class', 'metric').text(' ' + xUnit);
- tips.selectAll('rect').attr('width', tipWidth + 4).attr('x', flip ? -tipWidth - 12 : 8).attr('y', 0).style('text-anchor', flip ? 'end' : 'start');
- this.markersContainer.selectAll('circle').attr('cx', xPos).attr('cy', (d, i) => yScale(series ? y0[series[i]] : y0));
- }
-
- /**
- * Update dimensions based on properties and scale
- * @param {Object} props React Component properties
- * @param {number} scale size ratio / scale
- * @returns {Object} calculated dimensions
- */
- _updateDimensions(props, scale) {
- const { xMax, xMin, yMin, yMax } = props;
- const { width, height } = this.state.dimensions;
- const innerWidth = width - MARGIN.left - MARGIN.right;
- const outerHeight = Math.round(width * props.aspect);
- const innerHeight = outerHeight - MARGIN.top - MARGIN.bottom;
-
- this.state.xScale.range([0, innerWidth]).domain([xMin, xMax || 1]).clamp(true);
- this.state.xAxisScale.range([0, innerWidth]).domain([xMin, xMax]).clamp(true);
- this.state.yScale.range([innerHeight, 0]).domain([yMin, yMax + (yMax - yMin) * 0.1]); // 10% higher than maximum value for tooltip visibility
- return { innerWidth, outerHeight, innerHeight };
- }
-
- /**
- * Show tooltip
- * @param {SyntheticEvent} e Event
- */
- _showTip(e) {
- e.preventDefault();
- this.tipContainer.style('display', null);
- this.markersContainer.style('display', null);
- this._moveTip(e);
- }
-
- /**
- * Move and update tooltip
- * @param {SyntheticEvent} e Event
- */
- _moveTip(e) {
- let clientX = e.touches ? e.touches[0].clientX : e.clientX;
- this._tooltip(Math.round(clientX - e.currentTarget.getBoundingClientRect().left));
- }
-
- /**
- * Hide tooltip
- * @param {SyntheticEvent} e Event
- */
- _hideTip(e) {
- e.preventDefault();
- this.tipContainer.style('display', 'none');
- this.markersContainer.style('display', 'none');
- }
-
- /**
- * Update series generated from props
- * @param {Object} props React Component properties
- * @param {Object} state React Component state
- */
- _updateSeries(props, state) {
- let { func, xMin, xMax, series, points } = props;
- let delta = (xMax - xMin) / points;
- let seriesData = new Array(points);
-
- if (delta) {
- seriesData = new Array(points);
- for (let i = 0, x = xMin; i < points; i++) {
- seriesData[i] = [x, func(x)];
- x += delta;
- }
- seriesData[points - 1] = [xMax, func(xMax)];
- } else {
- let yVal = func(xMin);
- seriesData = [[0, yVal], [1, yVal]];
- }
-
- const markerElems = [];
- const detailElems = [];
- const seriesLines = [];
- for (let i = 0, l = series ? series.length : 1; i < l; i++) {
- const yAccessor = series ? function(d) { return state.yScale(d[1][this]); }.bind(series[i]) : (d) => state.yScale(d[1]);
- seriesLines.push(d3.line().x((d, i) => this.state.xScale(d[0])).y(yAccessor));
- detailElems.push();
- markerElems.push();
- }
-
- const tipHeight = 2 + (1.2 * (seriesLines ? seriesLines.length : 0.8));
-
- this.setState({ markerElems, detailElems, seriesLines, seriesData, tipHeight });
- }
-
- /**
- * Update dimensions and series data based on props and context.
- */
- componentWillMount() {
- this._updateSeries(this.props, this.state);
- }
-
- /**
- * Update state based on property and context changes
- * @param {Object} nextProps Incoming/Next properties
- * @param {Object} nextContext Incoming/Next conext
- */
- componentWillReceiveProps(nextProps, nextContext) {
- const props = this.props;
-
- if (props.code != nextProps.code) {
- this._updateSeries(nextProps, this.state);
- }
- }
-
- /**
- * Render the chart
- * @return {React.Component} Chart SVG
- */
- render() {
- const { innerWidth, outerHeight, innerHeight } = this._updateDimensions(this.props, this.context.sizeRatio);
- const { width, height } = this.state.dimensions;
- const { xMin, xMax, xLabel, yLabel, xUnit, yUnit, xMark, colors } = this.props;
- const { tipHeight, detailElems, markerElems, seriesData, seriesLines } = this.state;
- const line = this.line;
- const lines = seriesLines.map((line, i) => ).reverse();
-
- const markX = xMark ? innerWidth * (xMark - xMin) / (xMax - xMin) : 0;
- const xmark = xMark ? : '';
-
- return (
- { this.setState({ dimensions }); }}>
-
-
-
-
- );
- }
-}
+import React from 'react';
+import PropTypes from 'prop-types';
+import ContainerDimensions from 'react-container-dimensions';
+import * as d3 from 'd3';
+import TranslatedComponent from './TranslatedComponent';
+
+const MARGIN = { top: 15, right: 20, bottom: 35, left: 60 };
+
+/**
+ * Line Chart
+ */
+export default class LineChart extends TranslatedComponent {
+
+ static defaultProps = {
+ code: '',
+ xMin: 0,
+ yMin: 0,
+ points: 20,
+ colors: ['#ff8c0d'],
+ aspect: 0.5
+ };
+
+ static propTypes = {
+ func: PropTypes.func.isRequired,
+ xLabel: PropTypes.string.isRequired,
+ xMin: PropTypes.number,
+ xMax: PropTypes.number.isRequired,
+ xUnit: PropTypes.string.isRequired,
+ xMark: PropTypes.number,
+ yLabel: PropTypes.string.isRequired,
+ yMin: PropTypes.number,
+ yMax: PropTypes.number.isRequired,
+ yUnit: PropTypes.string,
+ series: PropTypes.array,
+ colors: PropTypes.array,
+ points: PropTypes.number,
+ aspect: PropTypes.number,
+ code: PropTypes.string,
+ };
+
+ /**
+ * Constructor
+ * @param {Object} props React Component properties
+ * @param {Object} context React Component context
+ */
+ constructor(props, context) {
+ super(props);
+
+ this._updateDimensions = this._updateDimensions.bind(this);
+ this._updateSeries = this._updateSeries.bind(this);
+ this._tooltip = this._tooltip.bind(this);
+ this._showTip = this._showTip.bind(this);
+ this._hideTip = this._hideTip.bind(this);
+ this._moveTip = this._moveTip.bind(this);
+
+ const series = props.series;
+
+ let xScale = d3.scaleLinear();
+ let yScale = d3.scaleLinear();
+ let xAxisScale = d3.scaleLinear();
+
+ this.xAxis = d3.axisBottom(xAxisScale).tickSizeOuter(0);
+ this.yAxis = d3.axisLeft(yScale).ticks(6).tickSizeOuter(0);
+
+ this.state = {
+ xScale,
+ xAxisScale,
+ yScale,
+ tipHeight: 2 + (1.2 * (series ? series.length : 0.8)),
+ };
+ }
+
+ /**
+ * Update tooltip content
+ * @param {number} xPos x coordinate
+ * @param {number} width current container width
+ */
+ _tooltip(xPos, width) {
+ let { xLabel, yLabel, xUnit, yUnit, func, series } = this.props;
+ let { xScale, yScale } = this.state;
+ let { formats, translate } = this.context.language;
+ let x0 = xScale.invert(xPos),
+ y0 = func(x0),
+ tips = this.tipContainer,
+ yTotal = 0,
+ flip = (xPos / width > 0.50),
+ tipWidth = 0,
+ tipHeightPx = tips.selectAll('rect').node().getBoundingClientRect().height;
+
+
+ xPos = xScale(x0); // Clamp xPos
+
+ tips.selectAll('text.text-tip.y').text(function(d, i) {
+ let yVal = series ? y0[series[i]] : y0;
+ yTotal += yVal;
+ return (series ? translate(series[i]) : '') + ' ' + formats.f2(yVal);
+ }).append('tspan').attr('class', 'metric').text(yUnit ? ' ' + yUnit : '');
+
+ tips.selectAll('text').each(function() {
+ if (this.getBBox().width > tipWidth) {
+ tipWidth = Math.ceil(this.getBBox().width);
+ }
+ });
+
+ let tipY = Math.floor(yScale(yTotal / (series ? series.length : 1)) - (tipHeightPx / 2));
+
+ tipWidth += 8;
+ tips.attr('transform', 'translate(' + xPos + ',' + tipY + ')');
+ tips.selectAll('text.text-tip').attr('x', flip ? -12 : 12).style('text-anchor', flip ? 'end' : 'start');
+ tips.selectAll('text.text-tip.x').text(formats.f2(x0)).append('tspan').attr('class', 'metric').text(' ' + xUnit);
+ tips.selectAll('rect').attr('width', tipWidth + 4).attr('x', flip ? -tipWidth - 12 : 8).attr('y', 0).style('text-anchor', flip ? 'end' : 'start');
+ this.markersContainer.selectAll('circle').attr('cx', xPos).attr('cy', (d, i) => yScale(series ? y0[series[i]] : y0));
+ }
+
+ /**
+ * Update dimensions based on properties and scale
+ * @param {Object} props React Component properties
+ * @param {number} scale size ratio / scale
+ * @param {number} width current width of the container
+ * @returns {Object} calculated dimensions
+ */
+ _updateDimensions(props, scale, width) {
+ const { xMax, xMin, yMin, yMax } = props;
+ const innerWidth = width - MARGIN.left - MARGIN.right;
+ const outerHeight = Math.round(width * props.aspect);
+ const innerHeight = outerHeight - MARGIN.top - MARGIN.bottom;
+
+ this.state.xScale.range([0, innerWidth]).domain([xMin, xMax || 1]).clamp(true);
+ this.state.xAxisScale.range([0, innerWidth]).domain([xMin, xMax]).clamp(true);
+ this.state.yScale.range([innerHeight, 0]).domain([yMin, yMax + (yMax - yMin) * 0.1]); // 10% higher than maximum value for tooltip visibility
+ return { innerWidth, outerHeight, innerHeight };
+ }
+
+ /**
+ * Show tooltip
+ * @param {SyntheticEvent} e Event
+ */
+ _showTip(e) {
+ e.preventDefault();
+ this.tipContainer.style('display', null);
+ this.markersContainer.style('display', null);
+ this._moveTip(e);
+ }
+
+ /**
+ * Move and update tooltip
+ * @param {SyntheticEvent} e Event
+ * @param {number} width current container width
+ */
+ _moveTip(e, width) {
+ let clientX = e.touches ? e.touches[0].clientX : e.clientX;
+ this._tooltip(Math.round(clientX - e.currentTarget.getBoundingClientRect().left), width);
+ }
+
+ /**
+ * Hide tooltip
+ * @param {SyntheticEvent} e Event
+ */
+ _hideTip(e) {
+ e.preventDefault();
+ this.tipContainer.style('display', 'none');
+ this.markersContainer.style('display', 'none');
+ }
+
+ /**
+ * Update series generated from props
+ * @param {Object} props React Component properties
+ * @param {Object} state React Component state
+ */
+ _updateSeries(props, state) {
+ let { func, xMin, xMax, series, points } = props;
+ let delta = (xMax - xMin) / points;
+ let seriesData = new Array(points);
+
+ if (delta) {
+ seriesData = new Array(points);
+ for (let i = 0, x = xMin; i < points; i++) {
+ seriesData[i] = [x, func(x)];
+ x += delta;
+ }
+ seriesData[points - 1] = [xMax, func(xMax)];
+ } else {
+ let yVal = func(xMin);
+ seriesData = [[0, yVal], [1, yVal]];
+ }
+
+ const markerElems = [];
+ const detailElems = [];
+ const seriesLines = [];
+ for (let i = 0, l = series ? series.length : 1; i < l; i++) {
+ const yAccessor = series ? function(d) { return state.yScale(d[1][this]); }.bind(series[i]) : (d) => state.yScale(d[1]);
+ seriesLines.push(d3.line().x((d, i) => this.state.xScale(d[0])).y(yAccessor));
+ detailElems.push();
+ markerElems.push();
+ }
+
+ const tipHeight = 2 + (1.2 * (seriesLines ? seriesLines.length : 0.8));
+
+ this.setState({ markerElems, detailElems, seriesLines, seriesData, tipHeight });
+ }
+
+ /**
+ * Update dimensions and series data based on props and context.
+ */
+ componentWillMount() {
+ this._updateSeries(this.props, this.state);
+ }
+
+ /**
+ * Update state based on property and context changes
+ * @param {Object} nextProps Incoming/Next properties
+ * @param {Object} nextContext Incoming/Next conext
+ */
+ componentWillReceiveProps(nextProps, nextContext) {
+ const props = this.props;
+
+ if (props.code != nextProps.code) {
+ this._updateSeries(nextProps, this.state);
+ }
+ }
+
+ /**
+ * Render the chart
+ * @return {React.Component} Chart SVG
+ */
+ render() {
+ return (
+
+ { ({ width, height }) => {
+ const { innerWidth, outerHeight, innerHeight } = this._updateDimensions(this.props, this.context.sizeRatio, width, height);
+ const { xMin, xMax, xLabel, yLabel, xUnit, yUnit, xMark, colors } = this.props;
+ const { tipHeight, detailElems, markerElems, seriesData, seriesLines } = this.state;
+ const lines = seriesLines.map((line, i) => ).reverse();
+
+ const markX = xMark ? innerWidth * (xMark - xMin) / (xMax - xMin) : 0;
+ const xmark = xMark ? : '';
+ return (
+
+
+
+ );
+ }}
+
+ );
+ }
+}
diff --git a/src/app/components/PieChart.jsx b/src/app/components/PieChart.jsx
index d1ce9717..1c9ccac8 100644
--- a/src/app/components/PieChart.jsx
+++ b/src/app/components/PieChart.jsx
@@ -1,97 +1,92 @@
-import React, { Component } from 'react';
-import PropTypes from 'prop-types';
-import Measure from 'react-measure';
-import * as d3 from 'd3';
-
-const CORIOLIS_COLOURS = ['#FF8C0D', '#1FB0FF', '#71A052', '#D5D54D'];
-const LABEL_COLOUR = '#000000';
-
-/**
- * A pie chart
- */
-export default class PieChart extends Component {
-
- static propTypes = {
- data : PropTypes.array.isRequired
- };
-
- /**
- * Constructor
- * @param {Object} props React Component properties
- * @param {Object} context React Component context
- */
- constructor(props, context) {
- super(props);
-
- this.pie = d3.pie().value((d) => d.value);
- this.colors = CORIOLIS_COLOURS;
- this.arc = d3.arc();
- this.arc.innerRadius(0);
-
- this.state = {
- dimensions: {
- width: 100,
- height: 100
- }
- };
- }
-
-
- /**
- * Generate a slice of the pie chart
- * @param {Object} d the data for this slice
- * @param {number} i the index of this slice
- * @returns {Object} the SVG for the slice
- */
- sliceGenerator(d, i) {
- if (!d || d.value == 0) {
- // Ignore 0 values
- return null;
- }
-
- const { width, height } = this.state.dimensions;
- const { data } = this.props;
-
- // Push the labels further out from the centre of the slice
- let [labelX, labelY] = this.arc.centroid(d);
- const labelTranslate = `translate(${labelX * 1.5}, ${labelY * 1.5})`;
-
- // Put the keys in a line with equal spacing
- const nonZeroItems = data.filter(d => d.value != 0).length;
- const thisItemIndex = data.slice(0, i + 1).filter(d => d.value != 0).length - 1;
- const keyX = -width / 2 + (width / nonZeroItems) * (thisItemIndex + 0.5);
- const keyTranslate = `translate(${keyX}, ${width * 0.45})`;
-
- return (
-
-
- {d.value}
- {d.data.label}
-
- );
- }
-
- /**
- * Render the component
- * @returns {object} Markup
- */
- render() {
- const { width, height } = this.state.dimensions;
- const pie = this.pie(this.props.data),
- translate = `translate(${width / 2}, ${width * 0.4})`;
-
- this.arc.outerRadius(width * 0.4);
-
- return (
- { this.setState({ dimensions }); }}>
-
-
-
-
- );
- }
-}
+import React, { Component } from 'react';
+import PropTypes from 'prop-types';
+import ContainerDimensions from 'react-container-dimensions';
+import * as d3 from 'd3';
+
+const CORIOLIS_COLOURS = ['#FF8C0D', '#1FB0FF', '#71A052', '#D5D54D'];
+const LABEL_COLOUR = '#000000';
+
+/**
+ * A pie chart
+ */
+export default class PieChart extends Component {
+
+ static propTypes = {
+ data : PropTypes.array.isRequired
+ };
+
+ /**
+ * Constructor
+ * @param {Object} props React Component properties
+ * @param {Object} context React Component context
+ */
+ constructor(props, context) {
+ super(props);
+
+ this.pie = d3.pie().value((d) => d.value);
+ this.colors = CORIOLIS_COLOURS;
+ this.arc = d3.arc();
+ this.arc.innerRadius(0);
+ }
+
+
+ /**
+ * Generate a slice of the pie chart
+ * @param {Object} d the data for this slice
+ * @param {number} i the index of this slice
+ * @param {number} width the current width of the parent container
+ * @returns {Object} the SVG for the slice
+ */
+ sliceGenerator(d, i, width) {
+ if (!d || d.value == 0) {
+ // Ignore 0 values
+ return null;
+ }
+
+ const { data } = this.props;
+
+ // Push the labels further out from the centre of the slice
+ let [labelX, labelY] = this.arc.centroid(d);
+ const labelTranslate = `translate(${labelX * 1.5}, ${labelY * 1.5})`;
+
+ // Put the keys in a line with equal spacing
+ const nonZeroItems = data.filter(d => d.value != 0).length;
+ const thisItemIndex = data.slice(0, i + 1).filter(d => d.value != 0).length - 1;
+ const keyX = -width / 2 + (width / nonZeroItems) * (thisItemIndex + 0.5);
+ const keyTranslate = `translate(${keyX}, ${width * 0.45})`;
+
+ return (
+
+
+ {d.value}
+ {d.data.label}
+
+ );
+ }
+
+ /**
+ * Render the component
+ * @returns {object} Markup
+ */
+ render() {
+ return (
+
+ { ({ width }) => {
+ const pie = this.pie(this.props.data),
+ translate = `translate(${width / 2}, ${width * 0.4})`;
+
+ this.arc.outerRadius(width * 0.4);
+ return (
+
+
+
+ );
+ }}
+
+ );
+ }
+}
diff --git a/src/app/components/VerticalBarChart.jsx b/src/app/components/VerticalBarChart.jsx
index d4cbee36..52774ef9 100644
--- a/src/app/components/VerticalBarChart.jsx
+++ b/src/app/components/VerticalBarChart.jsx
@@ -1,111 +1,105 @@
-import TranslatedComponent from './TranslatedComponent';
-import React, { PropTypes } from 'react';
-import Measure from 'react-measure';
-import { BarChart, Bar, XAxis, YAxis } from 'recharts';
-
-const CORIOLIS_COLOURS = ['#FF8C0D', '#1FB0FF', '#71A052', '#D5D54D'];
-const LABEL_COLOUR = '#000000';
-const AXIS_COLOUR = '#C06400';
-
-const ASPECT = 1;
-
-const merge = function(one, two) {
- return Object.assign({}, one, two);
-};
-
-/**
- * A vertical bar chart
- */
-export default class VerticalBarChart extends TranslatedComponent {
-
- static propTypes = {
- data : PropTypes.array.isRequired,
- yMax : PropTypes.number
- };
-
- /**
- * Constructor
- * @param {Object} props React Component properties
- * @param {Object} context React Component context
- */
- constructor(props, context) {
- super(props);
-
- this._termtip = this._termtip.bind(this);
-
- this.state = {
- dimensions: {
- width: 300,
- height: 300
- }
- };
- }
-
- /**
- * Render the bar chart
- * @returns {Object} the markup
- */
- render() {
- const { width, height } = this.state.dimensions;
- const { tooltip, termtip } = this.context;
-
- // Calculate maximum for Y
- let dataMax = Math.max(...this.props.data.map(d => d.value));
- if (dataMax == -Infinity) dataMax = 0;
- let yMax = this.props.yMax ? Math.round(this.props.yMax) : 0;
- const localMax = Math.max(dataMax, yMax);
-
- return (
- this.setState({ dimensions }) }>
-
-
-
-
- } fill={CORIOLIS_COLOURS[0]} isAnimationActive={false} onMouseOver={this._termtip} onMouseOut={tooltip.bind(null, null)}/>
-
-
-
- );
- }
-
- /**
- * Generate a term tip
- * @param {Object} d the data
- * @param {number} i the index
- * @param {Object} e the event
- * @returns {Object} termtip markup
- */
- _termtip(d, i, e) {
- if (this.props.data[i].tooltip) {
- return this.context.termtip(this.props.data[i].tooltip, e);
- } else {
- return null;
- }
- }
-}
-
-/**
- * A label that displays the value within the bar of the chart
- */
-class ValueLabel extends React.Component {
- static propTypes = {
- x: PropTypes.number,
- y: PropTypes.number,
- payload: PropTypes.object,
- value: PropTypes.number
- };
-
- /**
- * Render offence
- * @return {React.Component} contents
- */
- render() {
- const { x, y, payload, value } = this.props;
-
- const em = value < 1000 ? '1em' : value < 1000 ? '0.8em' : '0.7em';
-
- return (
- {value}
- );
- }
-};
+import TranslatedComponent from './TranslatedComponent';
+import React, { PropTypes } from 'react';
+import ContainerDimensions from 'react-container-dimensions';
+import { BarChart, Bar, XAxis, YAxis } from 'recharts';
+
+const CORIOLIS_COLOURS = ['#FF8C0D', '#1FB0FF', '#71A052', '#D5D54D'];
+const LABEL_COLOUR = '#000000';
+const AXIS_COLOUR = '#C06400';
+
+const ASPECT = 1;
+
+const merge = function(one, two) {
+ return Object.assign({}, one, two);
+};
+
+/**
+ * A vertical bar chart
+ */
+export default class VerticalBarChart extends TranslatedComponent {
+
+ static propTypes = {
+ data : PropTypes.array.isRequired,
+ yMax : PropTypes.number
+ };
+
+ /**
+ * Constructor
+ * @param {Object} props React Component properties
+ * @param {Object} context React Component context
+ */
+ constructor(props, context) {
+ super(props);
+
+ this._termtip = this._termtip.bind(this);
+ }
+
+ /**
+ * Render the bar chart
+ * @returns {Object} the markup
+ */
+ render() {
+ const { tooltip, termtip } = this.context;
+
+ // Calculate maximum for Y
+ let dataMax = Math.max(...this.props.data.map(d => d.value));
+ if (dataMax == -Infinity) dataMax = 0;
+ let yMax = this.props.yMax ? Math.round(this.props.yMax) : 0;
+ const localMax = Math.max(dataMax, yMax);
+
+ return (
+
+ { ({ width }) => (
+
+
+
+
+ } fill={CORIOLIS_COLOURS[0]} isAnimationActive={false} onMouseOver={this._termtip} onMouseOut={tooltip.bind(null, null)}/>
+
+
+ )}
+
+ );
+ }
+
+ /**
+ * Generate a term tip
+ * @param {Object} d the data
+ * @param {number} i the index
+ * @param {Object} e the event
+ * @returns {Object} termtip markup
+ */
+ _termtip(d, i, e) {
+ if (this.props.data[i].tooltip) {
+ return this.context.termtip(this.props.data[i].tooltip, e);
+ } else {
+ return null;
+ }
+ }
+}
+
+/**
+ * A label that displays the value within the bar of the chart
+ */
+class ValueLabel extends React.Component {
+ static propTypes = {
+ x: PropTypes.number,
+ y: PropTypes.number,
+ payload: PropTypes.object,
+ value: PropTypes.number
+ };
+
+ /**
+ * Render offence
+ * @return {React.Component} contents
+ */
+ render() {
+ const { x, y, payload, value } = this.props;
+
+ const em = value < 1000 ? '1em' : value < 1000 ? '0.8em' : '0.7em';
+
+ return (
+ {value}
+ );
+ }
+};