1+ import { CdkMenuModule } from '@angular/cdk/menu' ;
12import { CdkTree } from '@angular/cdk/tree' ;
2- import { Component , inject , OnInit , viewChild } from '@angular/core' ;
3+ import {
4+ ChangeDetectorRef ,
5+ Component ,
6+ inject ,
7+ OnInit ,
8+ viewChild ,
9+ } from '@angular/core' ;
310import { MatButtonModule } from '@angular/material/button' ;
411import {
512 MatCheckboxChange ,
613 MatCheckboxModule ,
714} from '@angular/material/checkbox' ;
815import { MatIconModule } from '@angular/material/icon' ;
16+ import { MatMenuModule , MatMenuTrigger } from '@angular/material/menu' ;
917import { MatTreeModule , MatTreeNestedDataSource } from '@angular/material/tree' ;
1018import { combineLatest , of } from 'rxjs' ;
1119
@@ -20,14 +28,22 @@ const MIN_OPEN_LEVEL = 2;
2028@Component ( {
2129 selector : 'app-filter-tree' ,
2230 standalone : true ,
23- imports : [ MatTreeModule , MatIconModule , MatButtonModule , MatCheckboxModule ] ,
31+ imports : [
32+ MatTreeModule ,
33+ MatIconModule ,
34+ MatButtonModule ,
35+ MatCheckboxModule ,
36+ MatMenuModule ,
37+ CdkMenuModule ,
38+ ] ,
2439 templateUrl : './filter-tree.component.html' ,
2540 styleUrl : './filter-tree.component.css' ,
2641} )
2742export class FilterTreeComponent implements OnInit {
2843 private folderService = inject ( FolderService ) ;
2944 private configService = inject ( ConfigService ) ;
3045 private eventService = inject ( EventService ) ;
46+ private cdr = inject ( ChangeDetectorRef ) ;
3147
3248 tree = viewChild . required < CdkTree < Folder > > ( CdkTree ) ;
3349 dataSource = new MatTreeNestedDataSource < Folder > ( ) ;
@@ -36,27 +52,38 @@ export class FilterTreeComponent implements OnInit {
3652 selected = new Set < string > ( ) ;
3753 folders : Folder [ ] = [ ] ;
3854
39- childrenAccessor = ( folder : Folder ) => of ( folder . folders ) ;
40- hasChild = ( _ : number , node : Folder ) =>
41- ! ! node . folders && node . folders . length > 0 ;
55+ childrenAccessor = ( { folders } : Folder ) => of ( folders ) ;
56+ hasChild = ( _ : number , { folders } : Folder ) => ! ! folders ?. length ;
4257
4358 ngOnInit ( ) : void {
4459 const folders$ = this . folderService . load ( ) ;
4560 const config$ = this . configService . load ( ) ;
4661 combineLatest ( {
4762 folders : folders$ ,
4863 config : config$ ,
49- } ) . subscribe ( ( result ) => {
50- this . dataSource . data = result . folders ;
51- this . config = result . config ;
52- this . folders = result . folders ;
64+ } ) . subscribe ( ( { folders, config } ) => {
65+ const focusFolder = this . findFolder ( folders , config . focus ) ;
66+ this . dataSource . data = focusFolder ? [ focusFolder ] : folders ;
67+ this . config = config ;
68+ this . folders = folders ;
5369 this . selected . clear ( ) ;
5470 this . config . scopes . forEach ( ( scope ) => this . selected . add ( scope ) ) ;
55- this . expandChecked ( result . folders ) ;
71+ this . expandChecked ( this . dataSource . data ) ;
5672 removeFocus ( ) ;
5773 } ) ;
5874 }
5975
76+ private findFolder ( folders : Folder [ ] , focus ?: string ) : Folder | undefined {
77+ if ( ! focus || ! folders ?. length ) return undefined ;
78+ return (
79+ folders . find ( ( folder ) => folder && folder . path === focus ) ??
80+ this . findFolder (
81+ folders . flatMap ( ( { folders } ) => folders ) ,
82+ focus
83+ )
84+ ) ;
85+ }
86+
6087 expandChecked ( folders : Folder [ ] , depth = 0 ) : boolean {
6188 let open = depth <= MIN_OPEN_LEVEL ;
6289 for ( const folder of folders ) {
@@ -71,8 +98,40 @@ export class FilterTreeComponent implements OnInit {
7198 return open ;
7299 }
73100
74- isChecked ( folder : Folder ) : boolean {
75- return this . selected . has ( folder . path ) ;
101+ noContextMenu ( event : MouseEvent ) {
102+ event . preventDefault ( ) ;
103+ }
104+
105+ onContextMenu ( event : MouseEvent , trigger : MatMenuTrigger ) {
106+ event . preventDefault ( ) ;
107+ trigger . openMenu ( ) ;
108+ }
109+
110+ selectChildren ( folder : Folder ) {
111+ this . deselectParents ( folder ) ;
112+ this . selected . delete ( folder . path ) ;
113+ for ( const child of folder . folders ) {
114+ this . selected . add ( child . path ) ;
115+ }
116+ this . tree ( ) . expand ( folder ) ;
117+ this . updateConfig ( ) ;
118+ }
119+
120+ focusTree ( folder ?: Folder ) {
121+ this . dataSource = new MatTreeNestedDataSource < Folder > ( ) ;
122+ this . dataSource . data = folder ? [ { ...folder } ] : this . folders ;
123+ this . config . focus = folder ?. path ;
124+ this . expandChecked ( this . dataSource . data ) ;
125+ this . tree ( ) . renderNodeChanges ( this . dataSource . data ) ;
126+ this . updateConfig ( ) ;
127+ }
128+
129+ isChecked ( { path } : Folder ) : boolean {
130+ return this . selected . has ( path ) ;
131+ }
132+
133+ hasFocus ( { path } : Folder ) : boolean {
134+ return path === this . config . focus ;
76135 }
77136
78137 onCheckChange ( folder : Folder , $event : MatCheckboxChange ) {
@@ -84,7 +143,10 @@ export class FilterTreeComponent implements OnInit {
84143
85144 this . deselectParents ( folder ) ;
86145 this . deselectSubtree ( folder . folders ) ;
146+ this . updateConfig ( ) ;
147+ }
87148
149+ private updateConfig ( ) {
88150 this . config . scopes = [ ...this . selected ] ;
89151 this . config . groups = this . findParents ( ) ;
90152
0 commit comments