Skip to content

Instantly share code, notes, and snippets.

@jamesona
Created January 24, 2018 18:20
Show Gist options
  • Save jamesona/3a4796690374c7e3732918c320d935d7 to your computer and use it in GitHub Desktop.
Save jamesona/3a4796690374c7e3732918c320d935d7 to your computer and use it in GitHub Desktop.
import {
AfterContentInit, Component, ContentChildren,
EventEmitter, Input, OnChanges,
OnInit, Output, TemplateRef
} from '@angular/core'
import { chunkArray } from '@trapeze-gui/common'
@Component({
selector: 'trapeze-table',
styleUrls: ['./table.component.scss'],
template: `
<ng-container *ngIf="pageSelector === 'top' && pages.length > 1">
<ng-container *ngTemplateOutlet="pageSelect"></ng-container>
</ng-container>
<ng-container *ngFor="let data of pages; let page = index"><table *ngIf="page===activePage">
<thead *ngIf="displayHeadings"><tr *ngIf="columns">
<th *ngFor="let column of columns" (click)="changeSort(column)">
{{ (map && map[column]) ? map[column] : column }}
<i class="fa fa-caret-up" *ngIf="columnIsAscending(column)"></i>
<i class="fa fa-caret-down" *ngIf="columnIsDescending(column)"></i>
</th>
</tr></thead>
<tbody>
<tr *ngFor="let row of data; let i = index" (click)="onRowClick(row, i)">
<td *ngFor="let column of columns" (click)="onCellClick(column, row)">
<ng-container *ngIf="templates[column]; else display">
<ng-container *ngTemplateOutlet="
templates[column];
context: {$implicit: row[column]}
"></ng-container>
</ng-container>
<ng-template #display>{{row[column]}}</ng-template>
</td>
</tr>
</tbody>
</table></ng-container>
<ng-container *ngIf="pageSelector === 'bottom' && pages.length > 1">
<ng-container *ngTemplateOutlet="pageSelect"></ng-container>
</ng-container>
<ng-template #pageSelect><ng-container *ngIf="pages.length > 1">
<ul class="page-selector">
<li *ngIf="activePage > 0 && pages.length > 10">
<a (click)="selectPage(0)">&lt;&lt;</a>
</li>
<li *ngIf="activePage > 0">
<a (click)="selectPage(activePage - 1)">&lt;</a>
</li>
<ng-container *ngFor="let page of pages; let i = index">
<li *ngIf="i > activePage - 5 && (i < activePage + 5 || i < 10)">
<a [ngClass]="{active: activePage === i}" (click)="selectPage(i)">{{i+1}}</a>
</li>
</ng-container>
<li *ngIf="activePage < pages.length">
<a (click)="selectPage(activePage + 1)">&gt;</a>
</li>
<li *ngIf="activePage < pages.length && pages.length > 10">
<a (click)="selectPage(pages.length - 1)">&gt;&gt;</a>
</li>
</ul>
</ng-container></ng-template>
`
})
export class TableComponent implements OnChanges {
@Input() data: TableData = []
@Input() displayHeadings: boolean = true
@Input() map: {[key: string]: string}
@Input() includeCols: string[]
@Input() excludeCols: string[]
@Input() order: string[]
@Input() sortBy: {[key: string]: 'ascending'|'descending'}
@Input() templates: {[column: string]: TemplateRef<any>} = {}
@Input() pageSize: number = Infinity
@Input() pageSelector: 'top'|'bottom' = 'top'
@Output() rowClick = new EventEmitter()
@Output() cellClick = new EventEmitter()
columns: string[] = []
pages: TableData[] = []
activePage: number = 0
ngOnChanges(changes): void {
if (this.order)
this.columns = this.order
else
this.determineColumns()
this.sort()
}
public determineColumns(): void {
let columns: string[] = []
if (this.data) this.data.map(item => Object.getOwnPropertyNames(item)).forEach(keys => {
keys.filter(key => {
return !this.excludeCols || this.excludeCols.indexOf(key) < 0
}).filter(key => {
return !this.includeCols || this.includeCols.indexOf(key) > -1
}).filter(key => columns.indexOf(key) < 0).forEach(newKey => {
columns.push(newKey)
})
})
this.columns = columns
}
// TODO: add compound sort?
public changeSort(column: string): void {
const direction = this.sortBy[column]
if (direction && !isDescending(direction)) {
this.sortBy = {
[column]: 'descending'
}
} else {
this.sortBy = {
[column]: 'ascending'
}
}
this.sort()
}
public sort(): void {
if (!this.sortBy) this.sortBy = {[this.columns[0]]: 'ascending'}
this.data.sort((a: TableRow, b: TableRow) => {
const key = Object.keys(this.sortBy).shift()
const direction = this.sortBy[key]
let A = (isNaN(a[key])) ? a[key] : Number(a[key])
let B = (isNaN(a[key])) ? b[key] : Number(b[key])
let result = 0
if (A < B) result = -1
else if (A > B) result = 1
if (isDescending(direction)) result = result * -1
return result
})
this.paginate()
}
public paginate(): void {
this.pages = chunkArray(this.data, this.pageSize)
}
public selectPage(num: number): void {
this.activePage = num
}
public columnIsAscending(column: string): boolean {
return this.sortBy && isAscending(this.sortBy[column])
}
public columnIsDescending(column: string): boolean {
return this.sortBy && isDescending(this.sortBy[column])
}
public onRowClick(row, index): void {
this.rowClick.emit({
row: row,
index: index
})
}
public onCellClick(column, row): void {
this.cellClick.emit({
row: row,
column: column,
value: row[column]
})
}
}
function isDescending(direction: string): boolean {
return /desc/i.test(direction)
}
function isAscending(direction: string): boolean {
return /asc/i.test(direction)
}
export interface TableRow {[column: string]: any}
export type TableData = TableRow[]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment