11import * as _ from 'lodash' ;
2- import { TreeModel , RenamableNode , FoldingType , TreeStatus , TreeModelSettings } from './tree.types' ;
2+ import { Observable , Observer } from 'rxjs' ;
3+ import { TreeModel , RenamableNode , FoldingType , TreeStatus , TreeModelSettings , ChildrenLoadingFunction } from './tree.types' ;
4+
5+ enum ChildrenLoadingState {
6+ NotStarted ,
7+ Loading ,
8+ Completed
9+ }
310
411export class Tree {
512 private _children : Tree [ ] ;
13+ private _loadChildren : ChildrenLoadingFunction ;
14+ private _childrenLoadingState : ChildrenLoadingState = ChildrenLoadingState . NotStarted ;
15+ public node : TreeModel ;
16+ public parent : Tree ;
617
7- public constructor ( public node : TreeModel , public parent : Tree = null , isBranch : boolean = false ) {
8- this . _children = isBranch ? [ ] : null ;
18+ public constructor ( node : TreeModel , parent : Tree = null , isBranch : boolean = false ) {
19+ this . buildTreeFromModel ( node , parent , isBranch ) ;
20+ }
21+
22+ /**
23+ * Build an instance of Tree from an object implementing TreeModel interface.
24+ * @param {TreeModel } model - A model that is used to build a tree.
25+ * @param {Tree } [parent] - An optional parent if you want to build a tree from the model that should be a child of an existing Tree instance.
26+ * @param {boolean } [isBranch] - An option that makes a branch from created tree. Branch can have children.
27+ * @static
28+ */
29+ private buildTreeFromModel ( model : TreeModel , parent : Tree , isBranch : boolean ) : void {
30+ this . parent = parent ;
31+ this . node = _ . extend ( _ . omit ( model , 'children' ) as TreeModel , {
32+ settings : TreeModelSettings . merge ( model , _ . get ( parent , 'node' ) as TreeModel )
33+ } ) as TreeModel ;
34+
35+ if ( _ . isFunction ( this . node . loadChildren ) ) {
36+ this . _loadChildren = this . node . loadChildren ;
37+ } else {
38+ _ . forEach ( _ . get ( model , 'children' ) as TreeModel [ ] , ( child : TreeModel , index : number ) => {
39+ this . _addChild ( new Tree ( child , this ) , index ) ;
40+ } ) ;
41+ }
42+
43+ if ( ! Array . isArray ( this . _children ) ) {
44+ this . _children = this . node . loadChildren || isBranch ? [ ] : null ;
45+ }
46+ }
47+
48+ public childrenAreBeingLoaded ( ) : boolean {
49+ return ( this . _childrenLoadingState === ChildrenLoadingState . Loading ) ;
50+ }
51+
52+ private canLoadChildren ( ) : boolean {
53+ return ( this . _childrenLoadingState === ChildrenLoadingState . NotStarted )
54+ && ( this . foldingType === FoldingType . Expanded )
55+ && ( ! ! this . _loadChildren ) ;
56+ }
57+
58+ public childrenShouldBeLoaded ( ) : boolean {
59+ return ! ! this . _loadChildren ;
960 }
1061
1162 /**
@@ -16,6 +67,22 @@ export class Tree {
1667 return this . _children ;
1768 }
1869
70+ public get childrenAsync ( ) : Observable < Tree [ ] > {
71+ if ( this . canLoadChildren ( ) ) {
72+ setTimeout ( ( ) => this . _childrenLoadingState = ChildrenLoadingState . Loading ) ;
73+ return new Observable ( ( observer : Observer < Tree [ ] > ) => {
74+ this . _loadChildren ( ( children : TreeModel [ ] ) => {
75+ this . _children = _ . map ( children , ( child : TreeModel ) => new Tree ( child , this ) ) ;
76+ this . _childrenLoadingState = ChildrenLoadingState . Completed ;
77+ observer . next ( this . children ) ;
78+ observer . complete ( ) ;
79+ } ) ;
80+ } ) ;
81+ }
82+
83+ return Observable . of ( this . children ) ;
84+ }
85+
1986 /**
2087 * Create a new node in the current tree.
2188 * @param {boolean } isBranch - A flag that indicates whether a new node should be a "Branch". "Leaf" node will be created by default
@@ -215,7 +282,9 @@ export class Tree {
215282 */
216283 public get foldingType ( ) : FoldingType {
217284 if ( ! this . node . _foldingType ) {
218- if ( this . _children ) {
285+ if ( this . childrenShouldBeLoaded ( ) ) {
286+ this . node . _foldingType = FoldingType . Collapsed ;
287+ } else if ( this . _children ) {
219288 this . node . _foldingType = FoldingType . Expanded ;
220289 } else {
221290 this . node . _foldingType = FoldingType . Leaf ;
@@ -271,24 +340,6 @@ export class Tree {
271340
272341 // STATIC METHODS ----------------------------------------------------------------------------------------------------
273342
274- /**
275- * Build an instance of Tree from an object implementing TreeModel interface.
276- * @param {TreeModel } model - A model that is used to build a tree.
277- * @param {Tree } [parent] - An optional parent if you want to build a tree from the model that should be a child of an existing Tree instance.
278- * @returns {Tree } A tree build from given model (with parent if it was given)
279- * @static
280- */
281- public static buildTreeFromModel ( model : TreeModel , parent : Tree = null ) : Tree {
282- model . settings = TreeModelSettings . merge ( model , _ . get ( parent , 'node' ) as TreeModel ) ;
283- const tree = new Tree ( _ . omit ( model , 'children' ) as TreeModel , parent ) ;
284-
285- _ . forEach ( model . children , ( child : TreeModel , index : number ) => {
286- tree . _addChild ( Tree . buildTreeFromModel ( child , tree ) , index ) ;
287- } ) ;
288-
289- return tree ;
290- }
291-
292343 /**
293344 * Check that value passed is not empty (it doesn't consist of only whitespace symbols).
294345 * @param {string } value - A value that should be checked.
0 commit comments