File

src/app/components/table/table.component.ts

Description

Displays a table with provided data

Metadata

Index

Properties
Methods
Inputs
Accessors

Inputs

additionalColumnsData
Type : string[]

Column definitions of additional columns to be displayed

additionalHeaders
Type : ExtraHeader[]

Details of additional columns to be displayed

cellHeaders
Type : ExtraHeader[]

Details of cell columns to be displayed

cellHeadersData
Type : string[]

Column definitions of cell columns to be displayed

columns
Type : HeaderData[]
Default value : []

Details of the columns to be displayed

displayedColumns
Type : string[]
Default value : []

Column definitions of the columns to be displayed

isOrgan
Type : boolean
Default value : false

Flag to check if column is of organ

isTotal
Type : boolean
Default value : false

Flag to show/hide the total below the table

typeCount
Type : TableData[]

Sets the data to the table datasource

Methods

formatData
formatData(value)

Return's 'no data' if column cell is null

Parameters :
Name Optional
value No
Returns : string
getAlignmentClass
getAlignmentClass(column: HeaderData)

Returns an alignment class for the column

Parameters :
Name Type Optional
column HeaderData No
Returns : string
getTotal
getTotal(id: string)

Returns sum of numbers in a column

Parameters :
Name Type Optional
id string No
Returns : any
isNumericColumn
isNumericColumn(column: string)

Returns if column has atleast one number

Parameters :
Name Type Optional
column string No
Returns : boolean

Properties

Readonly dataSource
Default value : new MatTableDataSource<TableData>([])

Datasource to store table data

Accessors

sort
setsort(value: MatSort)

Sorts the current selected data

Parameters :
Name Type Optional
value MatSort No
Returns : void
typeCount
settypeCount(data: TableData[])

Sets the data to the table datasource

Parameters :
Name Type Optional
data TableData[] No
Returns : void
firstColumnId
getfirstColumnId()

Returns the column definition of first column

Returns : string
import { Component, Input, ViewChild } from '@angular/core';
import { MatSort } from '@angular/material/sort';
import { MatTableDataSource } from '@angular/material/table';
import { ExtraHeader, HeaderData } from './header';
import { TableData } from './table';

/** Displays a table with provided data */
@Component({
  selector: 'ccf-table',
  templateUrl: './table.component.html',
  styleUrls: ['./table.component.scss'],
})
export class TableComponent {
  /** Sorts the current selected data */
  @ViewChild(MatSort, { static: true })
  set sort(value: MatSort) {
    this.dataSource.sort = value;
  }

  /** Flag to check if column is of organ */
  @Input() isOrgan = false;

  /** Sets the data to the table datasource */
  @Input()
  set typeCount(data: TableData[]) {
    this.dataSource.data = data;
  }

  /** Column definitions of the columns to be displayed */
  @Input() displayedColumns: string[] = [];

  /** Details of the columns to be displayed */
  @Input() columns: HeaderData[] = [];

  /** Flag to show/hide the total below the table */
  @Input() isTotal = false;

  /** Details of additional columns to be displayed */
  @Input() additionalHeaders?: ExtraHeader[];

  /** Column definitions of additional columns to be displayed */
  @Input() additionalColumnsData?: string[];

  /** Details of cell columns to be displayed */
  @Input() cellHeaders?: ExtraHeader[];

  /** Column definitions of cell columns to be displayed */
  @Input() cellHeadersData?: string[];

  /** Datasource to store table data */
  readonly dataSource = new MatTableDataSource<TableData>([]);

  /** Returns the column definition of first column */
  get firstColumnId(): string {
    return this.columns[0]?.columnDef ?? '';
  }

  /** Returns sum of numbers in a column */
  getTotal(id: string) {
    const sum = this.dataSource.data
      .filter((entry) => typeof entry[id] === 'number')
      .reduce((acc, entry) => acc + (entry[id] as number), 0);
    return sum.toLocaleString();
  }

  /** Returns if column has atleast one number */
  isNumericColumn(column: string): boolean {
    if (column === 'table_version') {
      return false;
    }

    let hasAtLeastOneNumber = false;
    for (const { [column]: value } of this.dataSource.data) {
      if (value === null) {
        continue;
      } else if (typeof value !== 'number') {
        return false;
      }
      hasAtLeastOneNumber = true;
    }
    return hasAtLeastOneNumber;
  }

  /** Returns an alignment class for the column */
  getAlignmentClass(column: HeaderData): string {
    return `alignment-${column.alignment ?? 'default'}`;
  }

  /** Return's 'no data' if column cell is null */
  formatData(value: unknown): string {
    return value !== null ? `${value}` : 'no data';
  }
}
<table
  mat-table
  [dataSource]="dataSource"
  matSort
  [matSortActive]="firstColumnId"
  matSortDirection=""
  class="mat-elevation-z8 data-table"
>
  <ng-container *ngIf="cellHeaders?.length">
    <ng-container *ngFor="let cells of cellHeaders" [matColumnDef]="cells.columnDef">
      <th
        mat-header-cell
        *matHeaderCellDef
        [attr.colspan]="cells.colspan ?? 1"
        [attr.rowspan]="cells.rowspan ?? 1"
        class="table-title additional-header"
      >
        {{ cells.header }}
      </th>
    </ng-container>
  </ng-container>

  <ng-container *ngIf="additionalHeaders?.length">
    <ng-container *ngFor="let addition of additionalHeaders" [matColumnDef]="addition.columnDef">
      <th
        mat-header-cell
        *matHeaderCellDef
        [attr.colspan]="addition.colspan ?? 1"
        [attr.rowspan]="addition.rowspan ?? 1"
        class="table-title additional-header"
      >
        {{ addition.header }}
      </th>
    </ng-container>
  </ng-container>

  <ng-container *ngFor="let column of columns" [matColumnDef]="column.columnDef">
    <ng-container *ngIf="column.sorting ?? true; else noSortHeader">
      <th mat-header-cell *matHeaderCellDef class="table-title" mat-sort-header [class]="getAlignmentClass(column)">
        {{ column.header }}
      </th>
    </ng-container>
    <ng-template #noSortHeader>
      <th mat-header-cell *matHeaderCellDef class="table-title">
        {{ column.header }}
      </th>
    </ng-template>

    <td
      mat-cell
      *matCellDef="let row"
      class="table-cell"
      [innerHtml]="formatData(column.cell(row))"
      [class.transform]="isOrgan"
      [class]="getAlignmentClass(column)"
    ></td>

    <ng-container *ngIf="isTotal">
      <td mat-footer-cell *matFooterCellDef class="table-footer">
        <ng-container *ngIf="isNumericColumn(column.columnDef); else totalLabel">
          {{ getTotal(column.columnDef) }}
        </ng-container>
        <ng-template #totalLabel>
          <ng-container *ngIf="column.isTotalRequired">Totals</ng-container>
        </ng-template>
      </td>
    </ng-container>
  </ng-container>

  <ng-container *ngIf="cellHeaders?.length">
    <tr mat-header-row *matHeaderRowDef="cellHeadersData"></tr>
  </ng-container>

  <ng-container *ngIf="additionalHeaders?.length">
    <tr mat-header-row *matHeaderRowDef="additionalColumnsData"></tr>
  </ng-container>

  <tr mat-header-row *matHeaderRowDef="displayedColumns" class="row-one"></tr>

  <div class="first-column">
    <tr mat-row *matRowDef="let row; columns: displayedColumns"></tr>
  </div>

  <div *ngIf="isTotal">
    <tr mat-footer-row *matFooterRowDef="displayedColumns"></tr>
  </div>
</table>

./table.component.scss

$text-color: #212121;
$background-color: #ffffff;
$border-color: #e0e0e0;

@mixin default-font($weight: 500) {
  font-family: 'Inter';
  font-style: normal;
  font-weight: $weight;
  font-size: 0.875rem;
  line-height: 1.5rem;
  letter-spacing: 0.005rem;
}

@mixin text-alignment($type, $text-align) {
  $text-align-to-justify-content: (
    start: start,
    left: start,
    end: end,
    right: end,
    center: center,
  );
  $text-align-to-padding-side: (
    start: left,
    left: left,
    end: right,
    right: right,
    center: '',
  );
  $text-align-to-text-align: (
    start: left,
    left: left,
    end: right,
    right: right,
    center: '',
  );
  $justify-content: map-get($text-align-to-justify-content, $text-align);
  $padding-side: map-get($text-align-to-padding-side, $text-align);
  $text-align: map-get($text-align-to-text-align, $text-align);

  .alignment-#{$type} {
    text-align: $text-align;

    @if $padding-side != '' {
      padding-#{$padding-side}: 1rem;
    }
  }

  ::ng-deep th.alignment-#{$type} > * {
    justify-content: $justify-content;

    .mat-sort-header-content {
      text-align: $text-align;
    }
  }
}

:host {
  display: block;
  width: 100%;
  overflow-x: auto;

  .data-table {
    table-layout: fixed;
    min-width: 100%;
    background-color: $background-color;
    border: 0.063rem solid $border-color;
    box-shadow: 0rem 0.125rem 0.063rem rgba(0, 0, 0, 0.16);

    .table-title,
    .table-cell,
    .table-footer {
      @include default-font();
      color: $text-color;
      text-overflow: ellipsis;
      white-space: normal;
    }

    .table-title {
      vertical-align: text-top;
      padding: 1rem;
      max-width: 150px;
    }

    td.mat-footer-cell:first-of-type {
      padding-left: 1rem;
    }

    .table-cell {
      font-weight: 300;
      box-sizing: border-box;
    }

    .table-footer:not(:first-child) {
      text-align: right;
      box-sizing: border-box;
      white-space: unset;
      padding-right: 1rem;
    }

    td.mat-cell:last-of-type {
      box-sizing: border-box;
      padding-right: 1rem;
    }

    td.mat-footer-cell:last-of-type {
      box-sizing: border-box;
      padding-right: 1rem;
    }

    @include text-alignment(default, right);
    @include text-alignment(start, start);
    @include text-alignment(end, end);
    @include text-alignment(center, center);

    ::ng-deep .mat-sort-header-arrow {
      color: #212121 !important;
      &:hover {
        transform: rotate(180deg) !important;
        color: #ebebeb !important;
      }
    }

    .additional-header {
      text-align: center;
    }

    ::ng-deep .mat-sort-header-container:not(.mat-sort-header-sorted) .mat-sort-header-arrow {
      opacity: 0.54 !important;
      transform: rotate(180deg) !important;
      color: #ebebeb !important;
      size: 3rem;
      &:hover {
        color: #8b8383 !important;
      }
    }

    ::ng-deep .mat-sort-header-content {
      white-space: normal;
    }

    ::ng-deep .mat-column-csv {
      white-space: nowrap !important;
      align-items: center;
    }

    ::ng-deep td.mat-column-csv {
      text-align: left !important;
      padding-left: 1rem;
      padding-right: unset !important;
    }

    ::ng-deep .mat-column-Organ {
      min-width: 12rem;
    }
  }
}
Legend
Html element
Component
Html element with directive

results matching ""

    No results matching ""