import {NgModule,Component,Input,Output,EventEmitter,AfterContentInit,ElementRef,ContentChild,IterableDiffers,ChangeDetectorRef,ContentChildren,QueryList,Inject,forwardRef,OnInit,Renderer2,ViewChild} from '@angular/core';
import {CommonModule} from '@angular/common';
import {TreeNode} from '../common/treenode';
import {Header,Footer,Column} from '../common/shared';
import {SharedModule} from '../common/shared';
import {Subscription} from 'rxjs/Subscription';
import {DomHandler} from '../dom/domhandler';
@Component({
selector: '[pTreeRow]',
template: `
|
{{resolveFieldData(node.data,col.field)}}
|
`
})
export class UITreeRow implements OnInit {
@Input() node: TreeNode;
@Input() parentNode: TreeNode;
@Input() level: number = 0;
@Input() labelExpand: string = "Expand";
@Input() labelCollapse: string = "Collapse";
constructor(@Inject(forwardRef(() => TreeTable)) public treeTable:TreeTable) {}
ngOnInit() {
this.node.parent = this.parentNode;
}
toggle(event: Event) {
if(this.node.expanded)
this.treeTable.onNodeCollapse.emit({originalEvent: event, node: this.node});
else
this.treeTable.onNodeExpand.emit({originalEvent: event, node: this.node});
this.node.expanded = !this.node.expanded;
event.preventDefault();
}
isLeaf() {
return this.node.leaf == false ? false : !(this.node.children&&this.node.children.length);
}
isSelected() {
return this.treeTable.isSelected(this.node);
}
onRowClick(event: MouseEvent) {
this.treeTable.onRowClick(event, this.node);
}
onRowRightClick(event: MouseEvent) {
this.treeTable.onRowRightClick(event, this.node);
}
rowDblClick(event: MouseEvent) {
this.treeTable.onRowDblclick.emit({originalEvent: event, node: this.node});
}
onRowTouchEnd() {
this.treeTable.onRowTouchEnd();
}
resolveFieldData(data: any, field: string): any {
if(data && field) {
if(field.indexOf('.') == -1) {
return data[field];
}
else {
let fields: string[] = field.split('.');
let value = data;
for(var i = 0, len = fields.length; i < len; ++i) {
value = value[fields[i]];
}
return value;
}
}
else {
return null;
}
}
}
@Component({
selector: 'p-treeTable',
template: `
|
{{col.header}}
|
|
{{col.footer}}
|
`,
providers: [DomHandler]
})
export class TreeTable implements AfterContentInit {
@Input() value: TreeNode[];
@Input() selectionMode: string;
@Input() selection: any;
@Input() style: any;
@Input() styleClass: string;
@Input() labelExpand: string = "Expand";
@Input() labelCollapse: string = "Collapse";
@Input() metaKeySelection: boolean = true;
@Input() contextMenu: any;
@Input() toggleColumnIndex: number = 0;
@Input() tableStyle: any;
@Input() tableStyleClass: string;
@Input() collapsedIcon: string = "fa-caret-right";
@Input() expandedIcon: string = "fa-caret-down";
@Output() onRowDblclick: EventEmitter = new EventEmitter();
@Output() selectionChange: EventEmitter = new EventEmitter();
@Output() onNodeSelect: EventEmitter = new EventEmitter();
@Output() onNodeUnselect: EventEmitter = new EventEmitter();
@Output() onNodeExpand: EventEmitter = new EventEmitter();
@Output() onNodeCollapse: EventEmitter = new EventEmitter();
@Output() onContextMenuSelect: EventEmitter = new EventEmitter();
@ContentChild(Header) header: Header;
@ContentChild(Footer) footer: Footer;
@ContentChildren(Column) cols: QueryList;
@ViewChild('tbl') tableViewChild: ElementRef;
public rowTouched: boolean;
public columns: Column[];
columnsSubscription: Subscription;
constructor (public el: ElementRef, public domHandler: DomHandler,public changeDetector: ChangeDetectorRef,public renderer: Renderer2) {}
ngAfterContentInit() {
this.initColumns();
this.columnsSubscription = this.cols.changes.subscribe(_ => {
this.initColumns();
this.changeDetector.markForCheck();
});
}
initColumns(): void {
this.columns = this.cols.toArray();
}
onRowClick(event: MouseEvent, node: TreeNode) {
let eventTarget = ( event.target);
if(eventTarget.className && eventTarget.className.indexOf('ui-treetable-toggler') === 0) {
return;
}
else if(this.selectionMode) {
if(node.selectable === false) {
return;
}
let metaSelection = this.rowTouched ? false : this.metaKeySelection;
let index = this.findIndexInSelection(node);
let selected = (index >= 0);
if(this.isCheckboxSelectionMode()) {
if(selected) {
this.propagateSelectionDown(node, false);
if(node.parent) {
this.propagateSelectionUp(node.parent, false);
}
this.selectionChange.emit(this.selection);
this.onNodeUnselect.emit({originalEvent: event, node: node});
}
else {
this.propagateSelectionDown(node, true);
if(node.parent) {
this.propagateSelectionUp(node.parent, true);
}
this.selectionChange.emit(this.selection);
this.onNodeSelect.emit({originalEvent: event, node: node});
}
}
else {
if(metaSelection) {
let metaKey = (event.metaKey||event.ctrlKey);
if(selected && metaKey) {
if(this.isSingleSelectionMode()) {
this.selectionChange.emit(null);
}
else {
this.selection = this.selection.filter((val,i) => i!=index);
this.selectionChange.emit(this.selection);
}
this.onNodeUnselect.emit({originalEvent: event, node: node});
}
else {
if(this.isSingleSelectionMode()) {
this.selectionChange.emit(node);
}
else if(this.isMultipleSelectionMode()) {
this.selection = (!metaKey) ? [] : this.selection||[];
this.selection = [...this.selection,node];
this.selectionChange.emit(this.selection);
}
this.onNodeSelect.emit({originalEvent: event, node: node});
}
}
else {
if(this.isSingleSelectionMode()) {
if(selected) {
this.selection = null;
this.onNodeUnselect.emit({originalEvent: event, node: node});
}
else {
this.selection = node;
this.onNodeSelect.emit({originalEvent: event, node: node});
}
}
else {
if(selected) {
this.selection = this.selection.filter((val,i) => i!=index);
this.onNodeUnselect.emit({originalEvent: event, node: node});
}
else {
this.selection = [...this.selection||[],node];
this.onNodeSelect.emit({originalEvent: event, node: node});
}
}
this.selectionChange.emit(this.selection);
}
}
}
this.rowTouched = false;
}
onRowTouchEnd() {
this.rowTouched = true;
}
onRowRightClick(event: MouseEvent, node: TreeNode) {
if(this.contextMenu) {
let index = this.findIndexInSelection(node);
let selected = (index >= 0);
if(!selected) {
if(this.isSingleSelectionMode()) {
this.selection = node;
}
else if(this.isMultipleSelectionMode()) {
this.selection = [node];
this.selectionChange.emit(this.selection);
}
this.selectionChange.emit(this.selection);
}
this.contextMenu.show(event);
this.onContextMenuSelect.emit({originalEvent: event, node: node});
}
}
findIndexInSelection(node: TreeNode) {
let index: number = -1;
if(this.selectionMode && this.selection) {
if(this.isSingleSelectionMode()) {
index = (this.selection == node) ? 0 : - 1;
}
else {
for(let i = 0; i < this.selection.length; i++) {
if(this.selection[i] == node) {
index = i;
break;
}
}
}
}
return index;
}
propagateSelectionUp(node: TreeNode, select: boolean) {
if(node.children && node.children.length) {
let selectedCount: number = 0;
let childPartialSelected: boolean = false;
for(let child of node.children) {
if(this.isSelected(child)) {
selectedCount++;
}
else if(child.partialSelected) {
childPartialSelected = true;
}
}
if(select && selectedCount == node.children.length) {
this.selection = [...this.selection||[],node];
node.partialSelected = false;
}
else {
if(!select) {
let index = this.findIndexInSelection(node);
if(index >= 0) {
this.selection = this.selection.filter((val,i) => i!=index);
}
}
if(childPartialSelected || selectedCount > 0 && selectedCount != node.children.length)
node.partialSelected = true;
else
node.partialSelected = false;
}
}
let parent = node.parent;
if(parent) {
this.propagateSelectionUp(parent, select);
}
}
propagateSelectionDown(node: TreeNode, select: boolean) {
let index = this.findIndexInSelection(node);
if(select && index == -1) {
this.selection = [...this.selection||[],node];
}
else if(!select && index > -1) {
this.selection = this.selection.filter((val,i) => i!=index);
}
node.partialSelected = false;
if(node.children && node.children.length) {
for(let child of node.children) {
this.propagateSelectionDown(child, select);
}
}
}
isSelected(node: TreeNode) {
return this.findIndexInSelection(node) != -1;
}
isSingleSelectionMode() {
return this.selectionMode && this.selectionMode == 'single';
}
isMultipleSelectionMode() {
return this.selectionMode && this.selectionMode == 'multiple';
}
isCheckboxSelectionMode() {
return this.selectionMode && this.selectionMode == 'checkbox';
}
hasFooter() {
if(this.columns) {
let columnsArr = this.cols.toArray();
for(let i = 0; i < columnsArr.length; i++) {
if(columnsArr[i].footer) {
return true;
}
}
}
return false;
}
}
@NgModule({
imports: [CommonModule],
exports: [TreeTable,SharedModule],
declarations: [TreeTable,UITreeRow]
})
export class TreeTableModule { }