Created
January 24, 2018 18:20
-
-
Save jamesona/3a4796690374c7e3732918c320d935d7 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)"><<</a> | |
</li> | |
<li *ngIf="activePage > 0"> | |
<a (click)="selectPage(activePage - 1)"><</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)">></a> | |
</li> | |
<li *ngIf="activePage < pages.length && pages.length > 10"> | |
<a (click)="selectPage(pages.length - 1)">>></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