import { Component, Input, Output, EventEmitter } from '@angular/core'; declare let d3: any; declare let $: any; @Component({ selector: 'angular-d3-donut', template: `
` }) export class DoughnutChartComponent { @Input() public width: number; @Input() public height: number; @Input() public iconWidth: number; @Input() public iconHeight: number; @Input() public outerRadius: number; @Input() public innerRadius: number; @Input() public data: any; @Input() public centerImage: any; @Input() public spreadSlice: boolean; @Input() public chartID: string; @Input() public middleText: string; @Input() public middleTextColor: string; @Input() public middleTextFontSize: string; @Output() public centerImageEvent = new EventEmitter(); constructor() { } public ngOnInit() { this.renderChart(); } public renderChart() { let chartComponent = this; let imageWidth = this.iconWidth ? this.iconWidth : 40; let imageHeight = this.iconHeight ? this.iconHeight : 40; let width = this.width ? this.width : 700; let height = this.height ? this.height : 400; let radius = 250; let piedata = this.data; this.outerRadius = this.outerRadius ? this.outerRadius : 150; this.innerRadius = this.innerRadius ? this.innerRadius : 70; this.spreadSlice = this.spreadSlice ? this.spreadSlice : false; let chartID = this.chartID ? this.chartID : 'donutChart'; let middleText = this.middleText ? this.middleText : ''; let middleTextColor = this.middleTextColor ? this.middleTextColor : 'black'; let middleTextFontSize = this.middleTextFontSize ? this.middleTextFontSize : '1em'; this.outerRadius > 150 ? this.outerRadius = 150 : this.outerRadius; let pie = d3.layout.pie() .startAngle(Math.PI / 2) .endAngle(Math.PI * 2 + Math.PI / 2) .value((d) => { return d.value; }).sort(null); let arc = d3.svg.arc() .outerRadius(this.outerRadius) .innerRadius(this.innerRadius); let arcNew = d3.svg.arc() .outerRadius(this.outerRadius + 10) .innerRadius(this.innerRadius); let svg = d3.select('#' + chartID).append('svg') .attr('width', width) .attr('height', height) .append('g') .attr('transform', 'translate(' + (400 - radius + 10) + ',' + (400 - radius + 10) + ')'); svg.append("text") .attr("text-anchor", "middle") .attr('font-size', middleTextFontSize) .style('fill', middleTextColor) .text(middleText); let g = svg.selectAll('.arc') .data(pie(piedata)) .enter().append('g') .attr('class', 'arc'); /* g.append("text") .attr("transform", function (d) { var _d = arc.centroid(d); _d[0] *= 1.3; //multiply by a constant factor _d[1] *= 1.3; //multiply by a constant factor return "translate(" + _d + ")"; }) .attr("dy", ".50em") .style("text-anchor", "middle") .text(function (d) { return d.data.label }); */ g.append('path') .attr('d', arc) .style('stroke', 'white').style('fill', function (d, i) { return d.data.color; }) .attr('id', function (d) { return 'iconId' + d.data.id; }) .attr('cursor', this.spreadSlice ? 'pointer' : 'default') .on('click', function (d) { d3.selectAll('path').transition() .duration(50) .attr('d', function (d) { if (this.selectedId === d.data.id) { d.data.expanded = true; this.selectedId = null; return arc(d); } else { d.data.expanded = false; this.selectedId = null; return arc(d); } }) if (chartComponent.spreadSlice) { d3.select(this).transition() .duration(50) .attr('d', function (d) { if (d.data.expanded) { this.selectedId = null; d.data.expanded = false; return arc(d); } else { d.data.expanded = true; this.selectedId = d.data.id; return arcNew(d); } }); } }); g.append('g') .attr('transform', function (d) { return 'translate(' + arc.centroid(d) + ')'; }) .append('svg:image') .attr('xlink:href', function (d) { return d.data.iconImage; }) .attr('id', function (d) { return d.data.id; }) .attr('width', imageWidth) .attr('height', imageHeight) .attr('x', -1 * imageWidth / 2) .attr('y', -1 * imageHeight / 2) .attr('cursor', this.spreadSlice ? 'pointer' : 'default') .on('click', function (d) { d3.selectAll('path').transition() .duration(50) .attr('d', function (d) { if (this.selectedId == d.data.id) { d.data.expanded = true; this.selectedId = null; return arc(d); } else { d.data.expanded = false; this.selectedId = null; return arc(d); } }); if (chartComponent.spreadSlice) { d3.select('path#iconId' + d.data.id).transition() .duration(50) .attr('d', function (d) { if (d.data.expanded) { this.selectedId = null; d.data.expanded = false; return arc(d); } else { d.data.expanded = true; this.selectedId = d.data.id; return arcNew(d); } }); } }); if (this.centerImage) { svg.append('svg:image') .attr('id', 'center_image') .attr('x', -60) .attr('y', -60) .attr('width', 120) .attr('height', 120) .attr('cursor', 'pointer') .attr('xlink:href', this.centerImage) .on('click', function click(d) { chartComponent.centerImageEvent.emit() }); } var legendLabels = []; var legendColors = []; for (let i = 0; i < piedata.length; i++) { legendLabels.push(piedata[i].label); legendColors.push(piedata[i].color); } var color = d3.scale.ordinal() .domain(legendLabels) .range(legendColors); var legendItemSize = 18 var legendSpacing = 4 var legend = svg .selectAll('.legend') .data(color.domain()) .enter() .append('g') .attr('class', 'legend') .attr('transform', (d, i) => { let outerRadiusCircle: any = this.outerRadius; let multiplicationFactor = outerRadiusCircle > 100 ? (outerRadiusCircle < 130 ? 8 : 9) : 6 var height = legendItemSize + legendSpacing; var offset = height * color.domain().length / 2; var x = legendItemSize * multiplicationFactor; var y = ((i * height) - offset); return `translate(${x}, ${y})` }) legend .append('rect') .attr('width', legendItemSize) .attr('height', legendItemSize) .style('fill', color); legend .append('text') .attr('x', legendItemSize + legendSpacing) .attr('y', legendItemSize - legendSpacing) .text((d) => d) } }