microting/ngx-charts

View on GitHub
src/number-card/card-series.component.ts

Summary

Maintainability
A
3 hrs
Test Coverage
import {
  Component,
  Input,
  Output,
  EventEmitter,
  OnChanges,
  SimpleChanges,
  ChangeDetectionStrategy
} from '@angular/core';
import { invertColor } from '../utils/color-utils';
import { ColorHelper } from '../common';

export interface CardModel {
  x;
  y;
  width: number;
  height: number;
  color: string;
  label: string;
  data;
  tooltipText: string;
}

@Component({
  selector: 'g[ngx-charts-card-series]',
  template: `
    <svg:rect
      *ngFor="let c of emptySlots; trackBy: trackBy"
      class="card-empty"
      [attr.x]="c.x"
      [attr.y]="c.y"
      [style.fill]="emptyColor"
      [attr.width]="c.width"
      [attr.height]="c.height"
      rx="3"
      ry="3"
    />
    <svg:g
      ngx-charts-card
      *ngFor="let c of cards; trackBy: trackBy"
      [x]="c.x"
      [y]="c.y"
      [width]="c.width"
      [height]="c.height"
      [color]="c.color"
      [bandColor]="c.bandColor"
      [textColor]="c.textColor"
      [data]="c.data"
      [label]="c.label"
      [medianSize]="medianSize"
      [valueFormatting]="valueFormatting"
      [labelFormatting]="labelFormatting"
      [animations]="animations"
      (select)="onClick($event)"
    />
  `,
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class CardSeriesComponent implements OnChanges {
  @Input() data: any[];
  @Input() slots: any[];
  @Input() dims;
  @Input() colors: ColorHelper;
  @Input() innerPadding = 15;

  @Input() cardColor;
  @Input() bandColor;
  @Input() emptyColor = 'rgba(0, 0, 0, 0)';
  @Input() textColor;
  @Input() valueFormatting: any;
  @Input() labelFormatting: any;
  @Input() animations: boolean = true;

  @Output() select = new EventEmitter();

  cards: CardModel[];
  emptySlots: any[];
  medianSize: number;

  ngOnChanges(changes: SimpleChanges): void {
    this.update();
  }

  update(): void {
    if (this.data.length > 2) {
      const valueFormatting = this.valueFormatting || (card => card.value.toLocaleString());

      const sortedLengths = this.data
        .map(d => {
          const hasValue = d && d.data && typeof d.data.value !== 'undefined' && d.data.value !== null;
          return hasValue
            ? valueFormatting({
              data: d.data,
              label: d ? d.data.name : '',
              value: d && d.data ? d.data.value : ''
            }).length
            : 0;
        })
        .sort((a, b) => b - a);
      const idx = Math.ceil(this.data.length / 2);
      this.medianSize = sortedLengths[idx];
    }

    const cards = this.getCards();
    this.cards = cards.filter(d => d.data.value !== null);
    this.emptySlots = cards.filter(d => d.data.value === null);
  }

  getCards(): any[] {
    const yPadding =
      typeof this.innerPadding === 'number' ? this.innerPadding : this.innerPadding[0] + this.innerPadding[2];
    const xPadding =
      typeof this.innerPadding === 'number' ? this.innerPadding : this.innerPadding[1] + this.innerPadding[3];

    return this.data.map((d, index) => {
      let label = d.data.name;
      if (label && label.constructor.name === 'Date') {
        label = label.toLocaleDateString();
      } else {
        label = label ? label.toLocaleString() : label;
      }

      const value = d.data.value;
      const valueColor = label ? this.colors.getColor(label) : this.emptyColor;
      const color = this.cardColor || valueColor || '#000';
      return {
        x: d.x,
        y: d.y,
        width: d.width - xPadding,
        height: d.height - yPadding,
        color,
        bandColor: this.bandColor || valueColor,
        textColor: this.textColor || invertColor(color),
        label,
        data: d.data,
        tooltipText: `${label}: ${value}`
      };
    });
  }

  trackBy(index, card): string {
    return card.label;
  }

  onClick(data): void {
    this.select.emit(data);
  }
}