import { Injectable , OnDestroy } from '@angular/core' ; import { ThyTreeNodeCheckState } from './tree.class' ; import { Subject } from 'rxjs' ; import { ThyTreeNode } from './tree-node.class' ; import { coerceArray } from 'ngx-tethys/util' ; function checkStateResolve ( node ) { const checkedNodes = node . children . filter ( n => n . isChecked === ThyTreeNodeCheckState . checked ) ; const unCheckedNodes = node . children . filter ( n => n . isChecked === ThyTreeNodeCheckState . unchecked ) ; if ( checkedNodes . length === node . children . length ) { return ThyTreeNodeCheckState . checked ; } else if ( unCheckedNodes . length === node . children . length ) { return ThyTreeNodeCheckState . unchecked ; } else { return ThyTreeNodeCheckState . indeterminate ; } } @ Injectable ( ) export class ThyTreeService implements OnDestroy { public treeNodes : ThyTreeNode [ ] ; private checkStateResolve : ( node : ThyTreeNode ) => ThyTreeNodeCheckState = checkStateResolve ; $statusChange = new Subject < ThyTreeFormatEmitEvent > ( ) ; constructor ( ) { } private _getParallelTreeNodes ( nodes , list = [ ] ) { ( nodes || [ ] ) . forEach ( node => { list . push ( node ) ; this . _getParallelTreeNodes ( node . children || [ ] , list ) ; } ) ; return list ; } setCheckStateResolve ( resolve = checkStateResolve ) { this . checkStateResolve = resolve ; } public resetSortedTreeNodes ( treeNodes , parent ? ) { treeNodes . forEach ( node => { node . level = node . parentNode ? node . parentNode . level + 1 : 0 ; node . origin . children = node . children . map ( n => n . origin ) ; node . parentNode = parent ; this . resetSortedTreeNodes ( node . children , node ) ; } ) ; } public getTreeNode ( key ) { const allNodes = this . _getParallelTreeNodes ( this . treeNodes ) ; return allNodes . find ( n => n . key === key ) ; } public getExpandedNodes ( ) { const allNodes = this . _getParallelTreeNodes ( this . treeNodes ) ; return allNodes . filter ( n => n . isExpanded ) ; } public getCheckedNodes ( ) { const allNodes = this . _getParallelTreeNodes ( this . treeNodes ) ; return allNodes . filter ( n => n . isChecked === ThyTreeNodeCheckState . checked ) ; } public deleteTreeNode ( node ) { const children = node . parentNode ? node . parentNode . children : this . treeNodes ; const index = children . findIndex ( n => n . key === node . key ) ; if ( index > - 1 ) { children . splice ( index , 1 ) ; } } public expandTreeNodes ( keyOrKeys ) { const keys = coerceArray ( keyOrKeys ) ; const needExpandNodes = this . _getParallelTreeNodes ( this . treeNodes ) . filter ( node => { return keys . indexOf ( node . key ) > - 1 ; } ) ; needExpandNodes . forEach ( node => { node . setExpanded ( true ) ; } ) ; } public statusChanged ( ) { return this . $statusChange . asObservable ( ) ; } public setNodeChecked ( node , checked , propagateUp = true , propagateDown = true ) { node . isChecked = checked ? ThyTreeNodeCheckState . checked : ThyTreeNodeCheckState . unchecked ; node . origin . checked = checked ; if ( propagateDown && node . children ) { node . children . forEach ( subNode => { this . setNodeChecked ( subNode , checked , false , true ) ; } ) ; } if ( propagateUp ) { this . syncNodeCheckState ( node . parentNode ) ; } } public syncNodeCheckState ( node ) { if ( node ) { node . isChecked = this . checkStateResolve ( node ) ; this . syncNodeCheckState ( node . parentNode ) ; } } ngOnDestroy ( ) { this . $statusChange . complete ( ) ; this . $statusChange = null ; } } export interface ThyTreeFormatEmitEvent { eventName : string ; node : ThyTreeNode ; event ? : MouseEvent | DragEvent ; }