Created
July 20, 2017 19:16
-
-
Save keithshep/2b92696dad8cdc2d80bf429150ee7d6c to your computer and use it in GitHub Desktop.
Tree-Table implemented with Angular
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 { | |
Component, | |
ContentChildren, | |
Input, | |
QueryList, | |
TemplateRef, | |
} from '@angular/core'; | |
@Component({ | |
selector: 'pxa-tree-table', | |
template: ` | |
<table class="table"> | |
<tr *ngFor="let rowData of flattenedData; trackBy: trackByData"> | |
<td *ngFor="let n of range(rowData.depth + 1); last as isLastNodeExpandCol" class="node-expand-col"> | |
<i | |
*ngIf="isLastNodeExpandCol && showExpandFor(rowData)" | |
(click)="rowData.data.collapsed = false;" | |
class="fa fa-plus-square-o" | |
aria-hidden="true"></i> | |
<i | |
*ngIf="isLastNodeExpandCol && showCollapseFor(rowData)" | |
(click)="rowData.data.collapsed = true;" | |
class="fa fa-minus-square-o" | |
aria-hidden="true"></i> | |
<!-- this last hidden icon is just to ensure consistent spacing --> | |
<i | |
*ngIf="isLastNodeExpandCol && !showExpandFor(rowData) && !showCollapseFor(rowData)" | |
class="fa fa-minus-square-o" | |
aria-hidden="true" | |
style="visibility: hidden !important;"></i> | |
</td> | |
<td | |
*ngFor="let colTemp of cellTemplates(rowData.data); first as isFirstDataCol; index as i;" | |
[attr.colspan]="colspanOf(rowData, i)"> | |
<ng-container *ngTemplateOutlet="colTemp; context: rowData.data"></ng-container> | |
</td> | |
</tr> | |
</table> | |
`, | |
styles: [` | |
.node-expand-col { | |
width: 1px; | |
} | |
`], | |
}) | |
export class TreeTableComponent { | |
@Input() trees: TreeTableData[]; | |
@ContentChildren('column') columnTemplates: QueryList<TemplateRef<any>>; | |
colspanOf(currFD: FlattenedData, colIndex: number) { | |
let colspans = currFD.data.colspans; | |
let colspan = colspans ? colspans[colIndex] : 1; | |
if(colIndex === 0) { | |
colspan += this.treeDepth - currFD.depth - 1; | |
} | |
return colspan; | |
} | |
get treeDepth() { | |
// TODO: since tree depth is so expensive and is used a lot | |
// we should allow the user to provide this | |
// as an input if they want | |
if(this.trees) { | |
let depth = 0; | |
for(let tree of this.trees) { | |
let currDepth = this._treeDepthRecursive(tree); | |
if(currDepth > depth) { | |
depth = currDepth; | |
} | |
} | |
return depth; | |
} else { | |
return 0; | |
} | |
} | |
cellTemplates(ttd: TreeTableData) { | |
if(ttd.cellTemplates) { | |
return ttd.cellTemplates; | |
} else { | |
return this.columnTemplates; | |
} | |
} | |
private _treeDepthRecursive(ttd: TreeTableData) { | |
let depth = 1; | |
if(ttd.children) { | |
for(let child of ttd.children) { | |
let currDepth = 1 + this._treeDepthRecursive(child); | |
if(currDepth > depth) { | |
depth = currDepth; | |
} | |
} | |
} | |
return depth; | |
} | |
get flattenedData(): FlattenedData[] { | |
let flattenedDataArr = []; | |
if(this.trees) { | |
for(let tree of this.trees) { | |
generateFlattenedData(flattenedDataArr, 0, tree); | |
} | |
} | |
return flattenedDataArr; | |
} | |
range(x) { | |
let vals = []; | |
for(let i = 0; i < x; i++) { | |
vals.push(i); | |
} | |
return vals; | |
} | |
trackByData(index: number, item: FlattenedData) { | |
return item.data; | |
} | |
showExpandFor(flattenedData: FlattenedData): boolean { | |
let data = flattenedData.data; | |
return data.children && data.children.length && data.collapsed; | |
} | |
showCollapseFor(flattenedData: FlattenedData): boolean { | |
let data = flattenedData.data; | |
return data.children && data.children.length && !data.collapsed; | |
} | |
} | |
// NOTE: this will be cleaner once I can use ES6 generators | |
// https://kangax.github.io/compat-table/es6/ | |
function generateFlattenedData(flattenedDataArr: FlattenedData[], depth: number, data: TreeTableData): void { | |
flattenedDataArr.push({depth, data}); | |
if(data.children && !data.collapsed) { | |
for(const child of data.children) { | |
generateFlattenedData(flattenedDataArr, depth + 1, child); | |
} | |
} | |
} | |
export class TreeTableData { | |
row: any; | |
collapsed?: boolean; | |
children?: TreeTableData[]; | |
cellTemplates?: QueryList<TemplateRef<any>>; | |
colspans?: number[]; | |
} | |
class FlattenedData { | |
depth: number; | |
data: TreeTableData; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment