src/components/BarChart.js
import { easeLinear } from 'd3-ease';
import { select, event } from 'd3-selection';
import 'd3-transition';
import AxisChart from './AxisChart';
/**
* Create BarCharts from the supplied data, based on the JSON config.
*
* @class BarChart
* @extends {AxisChart}
* @constructor
*/
export default class BarChart extends AxisChart {
/**
* The local collection of bars
*
* @property aBars
* @type {Array}
*/
aBars;
/**
* Constructor used to set chart type
*
* @method constructor
*/
constructor(oParams = {}) {
super(oParams);
this.sChartType = 'bar';
}
/**
* Render the chart including bars, axes and labels
*
* @method renderChart
* @param {Boolean} bReset optionally reset the chart data
* @param {Boolean} bHeightTransition optionally transition height
*/
renderChart(bReset = false, bHeightTransition = true) {
super.renderChart();
const { aValues, sBarType = 'side' } = this.jConfig;
const { iInnerHeight, oScaleX, oScaleY } = this;
const iBarWidth = oScaleX.bandwidth() / aValues.length;
this.aBars = this.aBars || [];
// Iterate through config value keys
aValues.forEach((oValues, i) => {
const { oColor } = oValues;
const iBarOffset = sBarType === 'side' ? (iBarWidth * i) : 0;
// Reset bars data and clear graph
if (bReset && this.aBars[i]) {
this.aBars[i] = this.d3ChartGroup.selectAll(`rect.bars-${i}`).data({});
this.aBars[i].exit().remove();
this.aBars[i] = undefined;
}
// Add bars for each value
if (!this.aBars[i]) {
// Bind bars data
this.aBars[i] = this.d3ChartGroup.selectAll(`rect.bars-${i}`).data(this.aData);
// Add new rect elements and set base attributes
this.aBars[i]
.enter()
.append('rect')
.on('mousemove', (d) => {
this.oTooltip.ping([d.sLabel, oValues.sName, d.aValues[i]]);
})
.on('mouseover', (d) => {
d.oColor = oColor;
select(event.target).attr('fill', d.oColor.darker(1));
})
.on('mousedown', (d) => {
if (this.jConfig.fnClickCallback) {
this.jConfig.fnClickCallback({
oEvent: event,
jData: d
});
}
})
.on('mouseout', (d) => {
this.oTooltip.hide();
select(event.target).attr('fill', d.oColor);
})
.attr('class', `bars bars-${i}`)
.attr('fill', oColor)
.attr('y', iInnerHeight)
.attr('height', 0);
}
// Update bars (IIFE used to allow for optional transition)
(() => {
const d3BarGroup = this.d3ChartGroup.selectAll(`rect.bars-${i}`);
d3BarGroup.data(this.aData)
.attr('x', d => oScaleX(d.sLabel) + iBarOffset)
.attr('width', iBarWidth);
if (bHeightTransition) {
return d3BarGroup.transition(false)
.ease(easeLinear)
.duration(this.iTransitionTime);
}
return d3BarGroup;
})().attr('y', (d) => {
let iValue = d.aValues[i];
iValue = iValue < 0 ? Math.abs(iValue) : 0;
return oScaleY(d.aValues[i] + iValue);
})
.attr('height', (d) => {
const iModifier = this.iMinValue < 0 ? Math.abs(this.iMinValue) : 0;
return iInnerHeight - oScaleY(Math.abs(d.aValues[i]) - iModifier);
});
});
}
}
module.exports = BarChart;