class Sunburst extends React.Component {
componentDidMount() {
this.renderSunburst(this.props);
}
componentWillReceiveProps(nextProps) {
if (!isEqual(this.props, nextProps)) {
this.renderSunburst(nextProps);
}
}
arcTweenData(a, i, node, x, arc) {
const oi = d3.interpolate({ x0: (a.x0s ? a.x0s : 0), x1: (a.x1s ? a.x1s : 0) }, a);
function tween(t) {
const b = oi(t);
a.x0s = b.x0;
a.x1s = b.x1;
return arc(b);
}
if (i === 0) {
const xd = d3.interpolate(x.domain(), [node.x0, node.x1]);
return function (t) {
x.domain(xd(t));
return tween(t);
};
} else {
return tween;
}
}
formatNameTooltip(d) {
const name = d.data.name;
return `${name}`;
}
labelName(d) {
const name = d.data.name;
return `${name}`;
}
labelVisible(d) {
return d.y1 <= 3 && d.y0 >= 1 && (d.y1 - d.y0) * (d.x1 - d.x0) > 0.03;
}
labelTransform(d) {
const x = (d.x0 + d.x1) / 2 * 180 / Math.PI;
const y = (d.y0 + d.y1) / 2 * 130;
return `rotate(${x - 90}) translate(${y},0) rotate(${x < 180 ? 0 : 180})`;
}
update(root, firstBuild, svg, partition, hueDXScale, x, y, radius, arc, node, self) {
if (firstBuild) {
firstBuild = false;
function click(d) {
node = d; // eslint-disable-line
self.props.onSelect && self.props.onSelect(d);
svg.selectAll('path').transition().duration(1000);
}
const tooltipContent = self.props.tooltipContent;
const tooltip = d3.select(`#${self.props.keyId}`)
.append(tooltipContent ? tooltipContent.type : 'div')
.style('position', 'absolute')
.style('z-index', '10')
.style('opacity', '0');
if (tooltipContent) {
Object.keys(tooltipContent.props).forEach((key) => {
tooltip.attr(key, tooltipContent.props[key]);
});
}
svg.selectAll('path')
.data(partition(root).descendants())
.enter()
.append('path')
.style('fill', (d) => {
const current = d;
if (current.depth === 0) {
return '#ffff';
}
if (current.depth === 1) {
return '#3f51b5';
}
if (current.depth > 1) {
return '#f44336';
}
})
.attr('stroke', '#fff') // lines color
.attr('stroke-width', '2') // line width
.on('click', d => click(d, node, svg, self, x, y, radius, arc))
.on('mouseover', function (d) {
if (self.props.tooltip) {
d3.select(this).style('cursor', 'pointer');
tooltip.html(() => { const name = self.formatNameTooltip(d); return name; });
return tooltip.transition().duration(50).style('opacity', 1);
}
return null;
})
.on('mousemove', () => {
if (self.props.tooltip) {
tooltip
.style('top', `${d3.event.pageY - 50}px`)
.style('left', `${self.props.tooltipPosition === 'right' ? d3.event.pageX - 100 : d3.event.pageX - 50}px`);
}
return null;
})
.on('mouseout', function () {
if (self.props.tooltip) {
d3.select(this).style('cursor', 'default');
tooltip.transition().duration(50).style('opacity', 0);
}
return null;
})
} else {
svg.selectAll('path').data(partition(root).descendants());
}
svg.selectAll('path').transition().duration(1000).attrTween('d', (d, i) => self.arcTweenData(d, i, node, x, arc));
}
renderSunburst(props) {
if (props.data) {
const self = this, // eslint-disable-line
gWidth = props.width,
gHeight = props.height,
radius = (Math.min(gWidth, gHeight) / 2) - 10,
svg = d3.select('svg').append('g').attr('transform', `translate(${gWidth / 2},${gHeight / 2})`),
x = d3.scaleLinear().range([0, 2 * Math.PI]),
y = props.scale === 'linear' ? d3.scaleLinear().range([0, radius]) : d3.scaleSqrt().range([0, radius]),
partition = d3.partition(),
arc = d3.arc()
.startAngle(d => Math.max(0, Math.min(2 * Math.PI, x(d.x0))))
.endAngle(d => Math.max(0, Math.min(2 * Math.PI, x(d.x1))))
.innerRadius(d => Math.max(0, y(d.y0)))
.outerRadius(d => Math.max(0, y(d.y1))),
hueDXScale = d3.scaleLinear()
.domain([0, 1])
.range([0, 360]),
rootData = d3.hierarchy(props.data);
const firstBuild = true;
const node = rootData;
rootData.sum(d => d.size);
self.update(rootData, firstBuild, svg, partition, hueDXScale, x, y, radius, arc, node, self); // GO!
}
}
render() {
return (
<div id={this.props.keyId} className="text-center">
<svg style={{ width: parseInt(this.props.width, 10) || 480, height: parseInt(this.props.height, 10) || 400 }} id={`${this.props.keyId}-svg`} />
</div>
);
}
}
export default Sunburst;