UNPKG

@swimlane/ngx-charts

Version:

Declarative Charting Framework for Angular2 and beyond!

169 lines (142 loc) 4.06 kB
import { Component, SimpleChanges, Input, Output, EventEmitter, OnChanges, ChangeDetectionStrategy } from '@angular/core'; import d3 from '../d3'; import { formatLabel } from '../common/label.helper'; @Component({ selector: 'g[ngx-charts-pie-series]', template: ` <svg:g *ngFor="let arc of data; trackBy:trackBy"> <svg:g ngx-charts-pie-label *ngIf="labelVisible(arc)" [data]="arc" [radius]="outerRadius" [color]="color(arc)" [label]="label(arc)" [max]="max" [value]="arc.value" [explodeSlices]="explodeSlices"> </svg:g> <svg:g ngx-charts-pie-arc [startAngle]="arc.startAngle" [endAngle]="arc.endAngle" [innerRadius]="innerRadius" [outerRadius]="outerRadius" [fill]="color(arc)" [value]="arc.data.value" [gradient]="gradient" [data]="arc.data" [max]="max" [explodeSlices]="explodeSlices" [isActive]="isActive(arc.data)" (select)="onClick($event)" (activate)="activate.emit($event)" (deactivate)="deactivate.emit($event)" ngx-tooltip [tooltipPlacement]="'top'" [tooltipType]="'tooltip'" [tooltipTitle]="tooltipText(arc)"> </svg:g> </svg:g> `, changeDetection: ChangeDetectionStrategy.OnPush, }) export class PieSeriesComponent implements OnChanges { @Input() colors; @Input() series: any = []; @Input() dims; @Input() innerRadius = 60; @Input() outerRadius = 80; @Input() explodeSlices; @Input() showLabels; @Input() gradient: boolean; @Input() activeEntries: any[]; @Output() select = new EventEmitter(); @Output() activate = new EventEmitter(); @Output() deactivate = new EventEmitter(); max: number; data: any; ngOnChanges(changes: SimpleChanges): void { this.update(); } update(): void { let pie: any = d3.pie() .value((d) => d.value) .sort(null); const arcData = pie(this.series); this.max = d3.max(arcData, (d) => { return d.value; }); this.data = this.calculateLabelPositions(arcData); } midAngle(d): number { return d.startAngle + (d.endAngle - d.startAngle) / 2; } outerArc(): any { const factor = 1.5; return d3.arc() .innerRadius(this.outerRadius * factor) .outerRadius(this.outerRadius * factor); } calculateLabelPositions(pieData): any { const minDistance = 10; let labelPositions = pieData; labelPositions.forEach((d) => { d.pos = this.outerArc().centroid(d); d.pos[0] = this.outerRadius * (this.midAngle(d) < Math.PI ? 1 : -1); }); for (let i = 0; i < labelPositions.length - 1; i++) { let a = labelPositions[i]; for (let j = i + 1; j < labelPositions.length; j++) { let b = labelPositions[j]; // if they're on the same side if (b.pos[0] * a.pos[0] > 0) { // if they're overlapping if (Math.abs(b.pos[1] - a.pos[1]) <= minDistance) { // push the second one down labelPositions[j].pos[1] = b.pos[1] + minDistance; j--; } } } } return labelPositions; } labelVisible(arc): boolean { return this.showLabels && (arc.endAngle - arc.startAngle > Math.PI / 30); } label(arc): string { return formatLabel(arc.data.name); } tooltipText(arc) { const label = this.label(arc); const val = formatLabel(arc.data.value); return ` <span class="tooltip-label">${label}</span> <span class="tooltip-val">${val}</span> `; } color(arc): any { return this.colors.getColor(this.label(arc)); } trackBy(index, item): string { return item.data.name; } onClick(data): void { this.select.emit(data); } isActive(entry): boolean { if(!this.activeEntries) return false; let item = this.activeEntries.find(d => { return entry.name === d.name && entry.series === d.series; }); return item !== undefined; } }