import { Injectable , OnDestroy } from '@angular/core' ; import { NavigationCancel , NavigationEnd , NavigationError , Router } from '@angular/router' ; import { BehaviorSubject , Subject } from 'rxjs' ; import { filter , map , takeUntil } from 'rxjs/operators' ; import type { PageHeaderNavigationDropdownItem , PageHeaderNavigationItem } from './navigation/navigation.component' ; @ Injectable ( ) export class PageHeaderService implements OnDestroy { items$ = new BehaviorSubject < PageHeaderNavigationItem [ ] > ( [ ] ) ; selected$ = new BehaviorSubject < PageHeaderNavigationItem > ( null ) ; selectedRoot$ = new BehaviorSubject < PageHeaderNavigationItem > ( null ) ; secondary$ = new BehaviorSubject < boolean > ( false ) ; secondaryNavigationAutoselect = false ; private _onDestroy = new Subject ( ) ; constructor ( private _router ) { this . selected$ . pipe ( takeUntil ( this . _onDestroy ) , map ( selected => this . getRoot ( selected ) ) ) . subscribe ( root => this . selectedRoot$ . next ( root ) ) ; this . _router . events . pipe ( filter ( event => event instanceof NavigationEnd || event instanceof NavigationCancel || event instanceof NavigationError ) , takeUntil ( this . _onDestroy ) ) . subscribe ( ( ) => this . updateItemsWithActiveRoute ( ) ) ; } ngOnDestroy ( ) { this . _onDestroy . next ( ) ; this . _onDestroy . complete ( ) ; } select ( item , navigate = true ) { if ( ! item ) { return ; } if ( item . routerLink && navigate ) { const routerLink = Array . isArray ( item . routerLink ) ? item . routerLink : [ item . routerLink ] ; this . _router . navigate ( routerLink , item . routerExtras ) ; } else if ( this . secondaryNavigationAutoselect && item . children && item . children . length > 0 ) { const selectedChild = item . children . find ( child => child . selected ) ; if ( selectedChild ) { this . select ( selectedChild ) ; this . selected$ . next ( selectedChild ) ; return ; } const firstChild = item . children . find ( _item => ! _item . disabled ) ; if ( firstChild ) { this . select ( firstChild ) ; } } else { if ( this . secondary$ . getValue ( ) === true && this . isParentOf ( this . selected$ . getValue ( ) , item ) ) { return ; } this . selected$ . next ( item ) ; } } deselect ( item ) { item . selected = false ; if ( item . children ) { item . children . forEach ( _item => this . deselect ( _item ) ) ; } } deselectAll ( ) { this . items$ . getValue ( ) . forEach ( item => this . deselect ( item ) ) ; } updateItem ( item , selected ) { item . selected = item === selected || this . isParentOf ( selected , item ) ; if ( item === selected ) { if ( item . select ) { item . select . call ( item , item ) ; } } } setItems ( items = [ ] ) { items . forEach ( item => this . setParent ( item ) ) ; this . items$ . next ( items ) ; const initialSelectedItem = items . find ( item => item . selected === true ) ; if ( initialSelectedItem ) { this . select ( initialSelectedItem ) ; } else { this . updateItemsWithActiveRoute ( ) ; } } setSecondaryNavigation ( enabled ) { this . secondary$ . next ( enabled ) ; } private getRoot ( item ) { return item && item . parent ? this . getRoot ( item . parent ) : item ; } private setParent ( item , parent ? ) { item . parent = parent ; if ( item . children ) { item . children . forEach ( child => this . setParent ( child , item ) ) ; } } private isParentOf ( node , parent ) { if ( ! node || ! node . parent ) { return false ; } if ( node . parent === parent ) { return true ; } return this . isParentOf ( node . parent , parent ) ; } private updateItemsWithActiveRoute ( ) { const activeItem = new PageHeaderActiveNavigationItem ( ) ; for ( const item of this . items$ . getValue ( ) ) { this . findActiveItem ( item , activeItem ) ; if ( activeItem . exact ) { break ; } } if ( activeItem . item ) { this . selected$ . next ( activeItem . item ) ; } } private findActiveItem ( item , activeItem ) { if ( item . routerLink ) { const routerLink = Array . isArray ( item . routerLink ) ? item . routerLink : [ item . routerLink ] ; const urlTree = this . _router . createUrlTree ( routerLink , item . routerExtras ) ; if ( this . _router . isActive ( urlTree , true ) && ! activeItem . exact ) { activeItem . item = item ; activeItem . exact = true ; return ; } if ( this . _router . isActive ( urlTree , false ) ) { activeItem . item = item ; activeItem . exact = false ; } } if ( item . children ) { for ( const childItem of item . children ) { this . findActiveItem ( childItem , activeItem ) ; if ( activeItem . exact ) { return ; } } } } } export type PageHeaderNavigation = PageHeaderNavigationItem | PageHeaderNavigationDropdownItem ; class PageHeaderActiveNavigationItem { item : PageHeaderNavigationItem ; exact : boolean ; }