d3rrc
Version:
Reusable D3 Charts for React.js
183 lines (167 loc) • 5.14 kB
JSX
var React = require('react');
var Support = require('../lib/support');
var d3 = require('d3');
var PieChart = React.createClass({
chart: function(selection){
var idx = 1;
var {
margin,
width,
height,
duration,
identity,
innerRadius,
sliceText,
style,
value: getValue,
colorRange,
color,
text: getText,
getIdentity,
enterSlice,
updateSlice,
exitSlice,
} = Support.getProps(this, 'chart', {
margin: Support.types.Object({
top: Support.types.Number(20),
right: Support.types.Number(10),
bottom: Support.types.Number(20),
left: Support.types.Number(50)
}),
width: Support.types.Number(-1),
height: Support.types.Number(500),
duration: Support.types.Number(500),
identity: Support.types.String('_id'),
innerRadius: Support.types.Number(0),
sliceText: Support.types.Function(function(){}),
style: Support.types.Object(false),
value: Support.types.Function(function x(d){
return +d.value;
}),
colorRange: Support.types.Function(false),
color: Support.types.Function(false),
text: Support.types.Function(function(d){
return d.text||'';
}),
getIdentity: Support.types.Function(function(d){
return d.data[identity] || (d.data[identity] = ++idx);
}),
enterSlice: Support.types.Function(function(node, arc){
node.append('path')
.style('fill', function(d){
return color(d.data);
})
.append('title')
;
node.append("text")
.attr("transform", function(d) { return "translate(" + arc.centroid(d) + ")"; })
.attr("dy", ".35em")
.style("text-anchor", "middle")
.text(function(d) { return getText(d.data); });
}),
updateSlice: Support.types.Function(function(node, arc){
node.select('path')
.style('fill', function(d){
return color(d.data);
})
.attrTween('d', function(d){
this._current = this._current || d;
var interpolate = d3.interpolate(this._current, d);
this._current = interpolate(0);
return function(t) {
return arc(interpolate(t));
};
});
node.select('title')
.text(sliceText);
node.select('text')
.attr("transform", function(d) { return "translate(" + arc.centroid(d) + ")"; })
.text(function(d) { return getText(d.data); });
}),
exitSlice: Support.types.Function(function(node, arc){})
});
color = color || function(d){
return d.color||colorRange(getValue(d), d, d[identity]);
};
selection.each(function(data){
var wid = width===-1?this.offsetWidth:width;
var w = wid - margin.left - margin.right;
var h = height - margin.top - margin.bottom;
var r = (w>h)?Math.floor(h/2):Math.floor(w/2);
var svg = d3.select(this).select('svg');
var min = d3.min(data, getValue), max = d3.max(data, getValue);
var i=0, l=data.length;
for(;i<l; i++){
data[i][identity] = data[i][identity] || idx++;
}
colorRange = colorRange || d3.scale.linear().domain([min, max]).range(["#ddd", "#333"]);
if(!svg[0][0]){
svg = d3.select(this).append('svg');
}
svg
.attr('width', wid)
.attr('height', height)
;
var arc = d3.svg.arc()
.outerRadius(r)
.innerRadius(innerRadius);
var pie = d3.layout.pie()
.value(getValue);
var main = svg.select('g');
var slices = main.select('.slices');
if(!main[0][0]){
main = svg.append('g');
slices = main.append('g').attr('class', 'slices');
}
main
.attr('transform', 'translate('+(margin.left+(w/2))+', '+(margin.top+(h/2))+')')
;
var slice = slices.selectAll('g.slice')
.data(pie(data, getIdentity));
;
var sliceEnter = slice.enter()
.append('g')
.attr('class', 'slice')
;
enterSlice(sliceEnter, arc);
updateSlice(slice.transition().duration(duration), arc);
var sliceExit = slice.exit()
.remove()
;
exitSlice(sliceExit);
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}>
</div>
);
}
});
module.exports = {
PieChart: PieChart
};