swimlane/ngx-charts

View on GitHub
projects/swimlane/ngx-charts/src/lib/common/legend/advanced-legend.component.ts

Summary

Maintainability
A
0 mins
Test Coverage
import {
  ChangeDetectionStrategy,
  Component,
  EventEmitter,
  Input,
  OnChanges,
  Output,
  SimpleChanges,
  ViewEncapsulation
} from '@angular/core';
import { trimLabel } from '../trim-label.helper';
import { formatLabel } from '../label.helper';
import { DataItem, StringOrNumberOrDate } from '../../models/chart-data.model';
import { ColorHelper } from '../color.helper';

export interface AdvancedLegendItem {
  value: StringOrNumberOrDate;
  _value: StringOrNumberOrDate;
  color: string;
  data: DataItem;
  label: string;
  displayLabel: string;
  originalLabel: string;
  percentage: string;
}

@Component({
  selector: 'ngx-charts-advanced-legend',
  template: `
    <div class="advanced-pie-legend" [style.width.px]="width">
      <div
        *ngIf="animations"
        class="total-value"
        ngx-charts-count-up
        [countTo]="roundedTotal"
        [valueFormatting]="valueFormatting"
      ></div>
      <div class="total-value" *ngIf="!animations">
        {{ valueFormatting ? valueFormatting(roundedTotal) : defaultValueFormatting(roundedTotal) }}
      </div>
      <div class="total-label">
        {{ label }}
      </div>
      <div class="legend-items-container">
        <div class="legend-items">
          <div
            *ngFor="let legendItem of legendItems; trackBy: trackBy"
            tabindex="-1"
            class="legend-item"
            (mouseenter)="activate.emit(legendItem.data)"
            (mouseleave)="deactivate.emit(legendItem.data)"
            (click)="select.emit(legendItem.data)"
          >
            <div class="item-color" [style.border-left-color]="legendItem.color"></div>
            <div
              *ngIf="animations"
              class="item-value"
              ngx-charts-count-up
              [countTo]="legendItem._value"
              [valueFormatting]="valueFormatting"
            ></div>
            <div *ngIf="!animations" class="item-value">
              {{ valueFormatting ? valueFormatting(legendItem.value) : defaultValueFormatting(legendItem.value) }}
            </div>
            <div class="item-label">{{ legendItem.displayLabel }}</div>
            <div
              *ngIf="animations"
              class="item-percent"
              ngx-charts-count-up
              [countTo]="legendItem.percentage"
              [countSuffix]="'%'"
            ></div>
            <div *ngIf="!animations" class="item-percent">{{ legendItem.percentage.toLocaleString() }}%</div>
          </div>
        </div>
      </div>
    </div>
  `,
  styleUrls: ['./advanced-legend.component.scss'],
  encapsulation: ViewEncapsulation.None,
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class AdvancedLegendComponent implements OnChanges {
  @Input() width: number;
  @Input() data: DataItem[];
  @Input() colors: ColorHelper;
  @Input() label: string = 'Total';
  @Input() animations: boolean = true;

  @Output() select: EventEmitter<DataItem> = new EventEmitter();
  @Output() activate: EventEmitter<DataItem> = new EventEmitter();
  @Output() deactivate: EventEmitter<DataItem> = new EventEmitter();

  legendItems: AdvancedLegendItem[] = [];
  total: number;
  roundedTotal: number;

  @Input() valueFormatting: (value: StringOrNumberOrDate) => any;
  @Input() labelFormatting: (value: string) => string = label => label;
  @Input() percentageFormatting: (value: number) => number = percentage => percentage;

  defaultValueFormatting: (value: StringOrNumberOrDate) => string = value => value.toLocaleString();

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

  getTotal(): number {
    return this.data.map(d => Number(d.value)).reduce((sum, d) => sum + d, 0);
  }

  update(): void {
    this.total = this.getTotal();
    this.roundedTotal = this.total;

    this.legendItems = this.getLegendItems();
  }

  getLegendItems(): AdvancedLegendItem[] {
    return (this.data as any).map(d => {
      const label = formatLabel(d.name);
      const value = d.value;
      const color = this.colors.getColor(label);
      const percentage = this.total > 0 ? (value / this.total) * 100 : 0;
      const formattedLabel = typeof this.labelFormatting === 'function' ? this.labelFormatting(label) : label;

      return {
        _value: value,
        data: d,
        value,
        color,
        label: formattedLabel,
        displayLabel: trimLabel(formattedLabel, 20),
        origialLabel: d.name,
        percentage: this.percentageFormatting ? this.percentageFormatting(percentage) : percentage.toLocaleString()
      };
    });
  }

  trackBy(index: number, item: AdvancedLegendItem) {
    return item.label;
  }
}