import { Component , Input , ViewChild , AfterViewChecked , OnDestroy , NgZone , ChangeDetectionStrategy } from '@angular/core' ; import { BehaviorSubject , Observable , Subscription } from 'rxjs' ; import { debounceTime , distinctUntilChanged , map , tap } from 'rxjs/operators' ; import { NgScrollbar } from 'ngx-scrollbar' ; @ Component ( { selector : 'app-navigation-toolbar' , templateUrl : './navigation-toolbar.component.html' , styleUrls : [ './navigation-toolbar.component.scss' ] , changeDetection : ChangeDetectionStrategy . OnPush } ) export class NavigationToolbarComponent implements AfterViewChecked , OnDestroy { private mainScrolledSubscription = Subscription . EMPTY ; private state = new BehaviorSubject < NavigationButtons [ ] > ( examplesData ) ; private mainScrolled : Observable < any > ; @ Input ( 'scrollable' ) mainScrollbar : NgScrollbar ; @ ViewChild ( NgScrollbar , { static : true } ) navScrollbar : NgScrollbar ; examples : Observable < NavigationButtons [ ] > = this . state . pipe ( distinctUntilChanged ( ) ) ; constructor ( private zone ) { } ngAfterViewChecked ( ) { if ( ! this . mainScrolled && this . mainScrollbar . verticalScrolled ) { this . mainScrolled = this . mainScrollbar . verticalScrolled ; this . mainScrolledSubscription = this . mainScrolled . pipe ( debounceTime ( 100 ) , map ( ( e ) => { const fromTop = e . target . scrollTop + 200 ; return this . state . value . map ( ( example ) => { const section = document . querySelector ( example . selector ) ; if ( section && section . firstChild ) { const firstChild = section . firstChild as HTMLElement ; return { ... example , active : firstChild . offsetTop - 56 <= fromTop && firstChild . offsetTop + firstChild . offsetHeight + 56 > fromTop } ; } return undefined ; } ) ; } ) , tap ( ( examples ) => { console . log ( examples ) ; const shouldScrollToElement = ! ! examples . filter ( ( example ) => example . active ) . length ; if ( shouldScrollToElement ) { this . zone . run ( ( ) => this . state . next ( examples ) ) ; } else { } } ) ) . subscribe ( ) ; } } ngOnDestroy ( ) { this . mainScrolledSubscription . unsubscribe ( ) ; } } interface NavigationButtons { label ? : string ; selector ? : string ; top ? : number ; duration ? : number ; active ? : boolean ; } const examplesData = [ { label : 'Lab' , selector : 'app-example-x' , top : 200 , duration : 600 } , { label : 'Ex 1' , selector : 'app-example2' , top : 200 , duration : 600 } , { label : 'Ex 2' , selector : 'app-example3' , top : 200 , duration : 600 } , { label : 'Ex 3' , selector : 'app-example4' , top : 200 , duration : 600 } , { label : 'Ex 4' , selector : 'app-example5' , top : 200 , duration : 800 } , { label : 'Infinite Scroll' , selector : 'app-example-infinite-scroll' , top : 200 , duration : 800 } , { label : 'Virtual Scroll' , selector : 'app-example-virtual-scroll' , top : 200 , duration : 800 } , { label : 'Nested Virtual Scroll' , selector : 'app-example-nested-virtual-scroll' , top : 100 , duration : 800 } ] ;