UNPKG

@swimlane/ngx-charts

Version:

Declarative Charting Framework for Angular2 and beyond!

183 lines (156 loc) 4.86 kB
import { Component, Input, Output, EventEmitter, ElementRef, SimpleChanges, OnChanges, ViewChild, ChangeDetectionStrategy, ChangeDetectorRef, NgZone, OnDestroy } from '@angular/core'; import { trimLabel } from '../common/trim-label.helper'; import { invertColor } from '../utils/color-utils'; import { count, decimalChecker } from '../common/count'; @Component({ selector: 'g[ngx-charts-card]', template: ` <svg:g [attr.transform]="transform" class="cell" (click)="onClick()"> <svg:rect class="card" [style.fill]="color" style="cursor: pointer;" [attr.width]="cardWidth" [attr.height]="cardHeight" rx="3" ry="3" /> <title>{{label}}</title> <svg:foreignObject x="5" [attr.y]="height * 0.7" [attr.width]="textWidth" [attr.height]="height * 0.3" style="font-size: 12px; pointer-events: none; text-transform: uppercase; overflow: hidden; text-align: center; line-height: 1em;"> <xhtml:p [style.color]="getTextColor(color)" style="overflow: hidden; white-space: nowrap; text-overflow: ellipsis; width: 100%;"> {{trimmedLabel}} </xhtml:p> </svg:foreignObject> <svg:text #textEl [attr.x]="cardWidth / 2" [attr.y]="height * 0.30" dy=".35em" class="value-text" [style.fill]="getTextColor(color)" text-anchor="middle" [style.font-size.pt]="textFontSize" style="pointer-events: none;"> {{value}} </svg:text> </svg:g> `, changeDetection: ChangeDetectionStrategy.OnPush }) export class CardComponent implements OnChanges, OnDestroy { @Input() color; @Input() x; @Input() y; @Input() width; @Input() height; @Input() label; @Input() data; @Output() select = new EventEmitter(); @ViewChild('textEl') textEl: ElementRef; element: HTMLElement; value: string = ''; transform: string; trimmedLabel: string; cardWidth: number; cardHeight: number; textWidth: number; resizeScale: number = 1; textFontSize: number = 35; textTransform: string = ''; originalWidth: number; originalHeight: number; originalWidthRatio: number; originalHeightRatio: number; initialized: boolean = false; animationReq: any; constructor(element: ElementRef, private cd: ChangeDetectorRef, private zone: NgZone) { this.element = element.nativeElement; } ngOnChanges(changes: SimpleChanges): void { this.update(); } ngOnDestroy(): void { cancelAnimationFrame(this.animationReq); } update(): void { this.zone.run(() => { this.transform = `translate(${this.x} , ${this.y})`; this.textWidth = Math.max(0, this.width - 15); this.cardWidth = Math.max(0, this.width - 5); this.cardHeight = Math.max(0, this.height - 5); this.label = this.data.name; this.trimmedLabel = trimLabel(this.label, 55); this.value = this.data.value.toLocaleString(); setTimeout(() => this.scaleText()); setTimeout(() => this.startCount(), 20); }); } getTextColor(color): string { return invertColor(color); } startCount(): void { if (!this.initialized) { cancelAnimationFrame(this.animationReq); const val = this.data.value; const decs = decimalChecker(val); const callback = ({ value }) => { this.zone.run(() => { this.value = value.toLocaleString(); this.cd.markForCheck(); }); }; this.animationReq = count(0, val, decs, 1, callback); this.initialized = true; } } scaleText(): void { this.zone.run(() => { let { width, height } = this.textEl.nativeElement.getBoundingClientRect(); if (width === 0 || height === 0) { return; } let availableWidth = this.cardWidth * 0.85; let availableHeight = this.cardHeight * 0.60; if (!this.originalWidthRatio) { this.originalWidthRatio = availableWidth / width; this.originalWidth = availableWidth; } if (!this.originalHeightRatio) { this.originalHeightRatio = availableHeight / height; this.originalHeight = availableHeight; } let newWidthRatio = (availableWidth / this.originalWidth) * this.originalWidthRatio; let newHeightRatio = (availableHeight / this.originalHeight) * this.originalHeightRatio; this.resizeScale = Math.min(newWidthRatio, newHeightRatio); this.textFontSize = Number.parseInt((35 * this.resizeScale).toString()); this.cd.markForCheck(); }); } onClick(): void { this.select.emit({ name: this.data.name, value: this.data.value }); } }