UNPKG

d3rrc

Version:

Reusable D3 Charts for React.js

350 lines (324 loc) 10.2 kB
var React = require('react'); var Support = require('../lib/support'); var d3 = require('d3'); var VBarChart = React.createClass({ chart: function (selection) { var { margin, width, height, xRoundBands, names, values, xAxisBottom, duration, color, enterBar, updateBar, exitBar, style } = Support.getProps(this, 'chart', { margin: Support.types.Object({ top: Support.types.Number(10), right: Support.types.Number(10), bottom: Support.types.Number(20), left: Support.types.Number(50) }), width: Support.types.Number(-1), height: Support.types.Number(420), xRoundBands: Support.types.Number(0.2), names: Support.types.Function(function (d) { return d[0]; }), values: Support.types.Function(function (d) { return d[1]; }), duration: Support.types.Number(500), color: Support.types.Function(false), enterBar: Support.types.Function(function (bar) { bar = bar.append('rect') .attr('class', function (d, i) { return values(d) < 0 ? 'negative' : 'positive'; }); if (color) { bar.style('fill', function (d) { return color(names(d)); }); } }), updateBar: Support.types.Function(function (bar) { bar.select('rect') .attr('class', function (d) { return values(d) < 0 ? 'negative' : 'positive'; }) .attr('x', function (d) { return X(d); }) .attr('y', function (d) { return values(d) < 0 ? Y0() : Y(d); }) .attr('width', xScale.rangeBand()) .attr('height', function (d, i) { return Math.abs(Y(d) - Y0()); }); }), exitBar: Support.types.Function(function (bar) {}), style: Support.types.Object(false) }); var X = function (d) { return xScale(names(d)); }; var Y0 = function () { return yScale(0); }; var Y = function (d) { return yScale(values(d)); }; var scaleO = xScale = d3.scale.ordinal(); var scaleL = yScale = d3.scale.linear(); var yAxis = d3.svg.axis().scale(scaleL).orient('left'); var xAxis = d3.svg.axis().scale(scaleO); var xAxisBottom = d3.svg.axis().scale(scaleO); height = parseInt(height); width = parseInt(width); duration = parseInt(duration); selection.each(function (data) { var w = width === -1 ? this.offsetWidth : width; // Update the x-scale. xScale .domain(data.map(names)) .rangeRoundBands([0, w - margin.left - margin.right], parseFloat(xRoundBands)); var ys = d3.extent(data.map(values)); if (ys[0] > 0) { ys[0] = 0; } // Update the y-scale. yScale .domain(ys) .range([height - margin.top - margin.bottom, 0]) .nice(); // Select the svg element, if it exists. var svg = d3.select(this).selectAll('svg').data([data]); // Otherwise, create the skeletal chart. var gEnter = svg.enter().append('svg').append('g'); gEnter.append('g').attr('class', 'bars'); gEnter.append('g').attr('class', 'y axis'); gEnter.append('g').attr('class', 'x axis bottom'); gEnter.append('g').attr('class', 'x axis zero'); // Update the outer dimensions. svg.attr('width', w) .attr('height', height); // Update the inner dimensions. var g = svg.select('g') .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); // Update the bars. var bars = svg.select('.bars'); //.selectAll('.bar');//.data(data); var bar = bars.selectAll('g.bar').data(data); var barEnter = bar.enter() .append('g') .attr('class', 'bar'); enterBar(barEnter); updateBar(bar.transition().duration(duration)); var barExit = bar.exit() .remove(); exitBar(barExit); // x axis at the bottom of the chart g.select('.x.axis.bottom') .attr('transform', 'translate(0,' + (height - margin.top - margin.bottom) + ')') .call(xAxisBottom.orient('bottom')); // zero line g.select('.x.axis.zero') .attr('transform', 'translate(0,' + Y0() + ')') .call(xAxis.tickFormat('').tickSize(0)); // Update the y-axis. g.select('.y.axis') .call(yAxis); g.selectAll('.axis line') .attr('style', 'shape-rendering: crispEdges; stroke: black; fill: none;'); g.selectAll('.axis path') .attr('style', 'shape-rendering: crispEdges; stroke: black; fill: none;'); if (style) { Object.keys(style).forEach(function (key) { svg.selectAll(key).attr('style', style[key]); }); } }); }, renderChart: function (data) { if (!data) { return; } var container = this.getDOMNode(); d3.select(container) .datum(data) .call(this.chart); }, componentDidMount: function () { return this.renderChart(this.props.data); }, componentDidUpdate: function () { return this.renderChart(this.props.data); }, render: function () { var classNames = Support.classList(this, { 'bar-chart': true }); return <div className={classNames} />; } }); var HBarChart = React.createClass({ chart: function (selection) { var { margin, width, height, names, values, duration, color, enterBar, updateBar, exitBar, style } = Support.getProps(this, 'chart', { margin: Support.types.Object({ top: Support.types.Number(0), right: Support.types.Number(10), bottom: Support.types.Number(20), left: Support.types.Number(50) }), width: Support.types.Number(-1), height: Support.types.Number(420), names: Support.types.Function(function (d) { return d[0]; }), values: Support.types.Function(function (d) { return d[1]; }), duration: Support.types.Number(500), color: Support.types.Function(d3.scale.category10()), enterBar: Support.types.Function(function (bar) { bar = bar.append('rect') .attr('class', 'bar'); if (color) { bar.style('fill', function (d) { return color(names(d)); }); } }), updateBar: Support.types.Function(function (bar) { bar.select('rect') .attr('x', function (d) { return margin.left; }) .attr('y', function (d) { return Y(d); }) .attr('width', function (d) { return X(d); }) .attr('height', yScale.rangeBand()); }), exitBar: Support.types.Function(function (bar) {}), style: Support.types.Object(false) }); var xScale = d3.scale.linear(); var yScale = d3.scale.ordinal(); var X = function (d) { return xScale(values(d)); }; var Y = function (d) { return yScale(names(d)) + margin.top; }; selection.each(function (data) { var w = width === -1 ? this.offsetWidth : width; var maxChartHeight = height - margin.top - margin.bottom; if (color && color.domain) { var cnames = data.map(names); color.domain(cnames); } var svg = d3.select(this).selectAll('svg').data([data]); svg.enter().append('svg') .attr('width', w) .attr('height', height); var maxValue = d3.max(data, values); yScale.domain(data.map(names)); xScale.range([0, w - margin.left - margin.right]).domain([0, maxValue]).nice(); yScale.rangeBands([0, maxChartHeight], 0.1, 0.25); var yAxis = d3.svg.axis().scale(yScale).orient('left'); var xAxis = d3.svg.axis().scale(xScale).orient('bottom'); var bars = svg.selectAll('.bar') .data(data); var barEnter = bars.enter() .append('g') .attr('class', 'bar'); enterBar(barEnter); updateBar(bars.transition().duration(duration)); var barExit = bars.exit() .remove(); exitBar(barExit); //append x axis svg.append("g") .attr("transform", "translate(" + margin.left + ", " + (maxChartHeight + margin.top) + ")") .attr("text-anchor", "middle") .attr("font-family", "sans-serif") .attr("font-size", "10px") .style("stroke", "black") .style("fill", "none") .style("stroke-width", 1) .style("shape-rendering", "crispEdges") .call(xAxis) .selectAll("text") .attr("stroke", "none") .attr("fill", "black"); //append y axis svg.append("g") .attr("transform", "translate(" + margin.left + ", " + margin.top + ")") .attr("text-anchor", "middle") .attr("font-family", "sans-serif") .attr("font-size", "10px") .style("stroke", "black") .style("fill", "none") .style("stroke-width", 1) .style("shape-rendering", "crispEdges") .call(yAxis) .selectAll("text") .attr("stroke", "none") .attr("fill", "black"); if (style) { Object.keys(style).forEach(function (key) { svg.selectAll(key).attr('style', style[key]); }); } }); }, renderChart: function (data) { if (!data) { return; } var container = this.getDOMNode(); d3.select(container) .datum(data) .call(this.chart); }, componentDidMount: function () { return this.renderChart(this.props.data); }, componentDidUpdate: function () { return this.renderChart(this.props.data); }, componentWillReceiveProps: function(props) { console.log('got new props'); return this.renderChart(props.data); }, render: function () { var classNames = Support.classList(this, { 'bar-chart': true }); return <div className={classNames} />; } }); module.exports={ VBarChart: VBarChart, HBarChart: HBarChart };