import { AfterViewInit , Directive , ElementRef , HostListener , Input , OnDestroy } from '@angular/core' ; import { NavigationEnd , Router } from '@angular/router' ; import { Platform } from '@angular/cdk/platform' ; import { Subject } from 'rxjs' ; import { filter , takeUntil } from 'rxjs/operators' ; import PerfectScrollbar from 'perfect-scrollbar' ; import * as _ from 'lodash' ; import { FuseConfigService } from '@fuse/services/config.service' ; @ Directive ( { selector : '[fusePerfectScrollbar]' } ) export class FusePerfectScrollbarDirective implements AfterViewInit , OnDestroy { isInitialized : boolean ; isMobile : boolean ; ps : PerfectScrollbar | any ; private _enabled : boolean | '' ; private _debouncedUpdate : any ; private _options : any ; private _unsubscribeAll : Subject < any > ; param { ElementRef } elementRef param { FuseConfigService } _fuseConfigService param { Platform } _platform param { Router } _router constructor ( public elementRef , private _fuseConfigService , private _platform , private _router ) { this . isInitialized = false ; this . isMobile = false ; this . _enabled = false ; this . _debouncedUpdate = _ . debounce ( this . update , 150 ) ; this . _options = { updateOnRouteChange : false } ; this . _unsubscribeAll = new Subject ( ) ; } param value @ Input ( ) set fusePerfectScrollbarOptions ( value ) { this . _options = _ . merge ( { } , this . _options , value ) ; } get fusePerfectScrollbarOptions ( ) : any { return this . _options ; } param { boolean | "" } value @ Input ( 'fusePerfectScrollbar' ) set enabled ( value ) { if ( value === '' ) { value = true ; } if ( this . enabled === value ) { return ; } this . _enabled = value ; if ( this . enabled ) { this . _init ( ) ; } else { this . _destroy ( ) ; } } get enabled ( ) : boolean | '' { return this . _enabled ; } /**
     * After view init
     */ ngAfterViewInit ( ) { this . _fuseConfigService . config . pipe ( takeUntil ( this . _unsubscribeAll ) ) . subscribe ( ( settings ) => { this . enabled = settings . customScrollbars ; } ) ; if ( this . fusePerfectScrollbarOptions . updateOnRouteChange ) { this . _router . events . pipe ( takeUntil ( this . _unsubscribeAll ) , filter ( event => event instanceof NavigationEnd ) ) . subscribe ( ( ) => { setTimeout ( ( ) => { this . scrollToTop ( ) ; this . update ( ) ; } , 0 ) ; } ) ; } } /**
     * On destroy
     */ ngOnDestroy ( ) { this . _destroy ( ) ; this . _unsubscribeAll . next ( ) ; this . _unsubscribeAll . complete ( ) ; } private _init ( ) { if ( this . isInitialized ) { return ; } if ( this . _platform . ANDROID || this . _platform . IOS ) { this . isMobile = true ; } if ( this . isMobile ) { return ; } this . isInitialized = true ; this . ps = new PerfectScrollbar ( this . elementRef . nativeElement , { ... this . fusePerfectScrollbarOptions } ) ; this . ps . event . eventElements . forEach ( ( eventElement ) => { if ( typeof eventElement . handlers [ 'keydown' ] !== 'undefined' ) { eventElement . element . removeEventListener ( 'keydown' , eventElement . handlers [ 'keydown' ] [ 0 ] ) ; } } ) ; } private _destroy ( ) { if ( ! this . isInitialized || ! this . ps ) { return ; } this . ps . destroy ( ) ; this . ps = null ; this . isInitialized = false ; } private @ HostListener ( 'window:resize' ) _updateOnResize ( ) { this . _debouncedUpdate ( ) ; } param { Event } event @ HostListener ( 'document:click' , [ '$event' ] ) documentClick ( event ) { if ( ! this . isInitialized || ! this . ps ) { return ; } this . ps . update ( ) ; } /**
     * Update the scrollbar
     */ update ( ) { if ( ! this . isInitialized ) { return ; } this . ps . update ( ) ; } /**
     * Destroy the scrollbar
     */ destroy ( ) { this . ngOnDestroy ( ) ; } param { number } x param { number } speed scrollToX ( x , speed ? ) { this . animateScrolling ( 'scrollLeft' , x , speed ) ; } param { number } y param { number } speed scrollToY ( y , speed ? ) { this . animateScrolling ( 'scrollTop' , y , speed ) ; } param { number } offset param { number } speed scrollToTop ( offset ? , speed ? ) { this . animateScrolling ( 'scrollTop' , ( offset || 0 ) , speed ) ; } param { number } offset param { number } speed scrollToLeft ( offset ? , speed ? ) { this . animateScrolling ( 'scrollLeft' , ( offset || 0 ) , speed ) ; } param { number } offset param { number } speed scrollToRight ( offset ? , speed ? ) { const width = this . elementRef . nativeElement . scrollWidth ; this . animateScrolling ( 'scrollLeft' , width - ( offset || 0 ) , speed ) ; } param { number } offset param { number } speed scrollToBottom ( offset ? , speed ? ) { const height = this . elementRef . nativeElement . scrollHeight ; this . animateScrolling ( 'scrollTop' , height - ( offset || 0 ) , speed ) ; } param { string } target param { number } value param { number } speed animateScrolling ( target , value , speed ? ) { if ( ! speed ) { this . elementRef . nativeElement [ target ] = value ; this . update ( ) ; this . update ( ) ; } else if ( value !== this . elementRef . nativeElement [ target ] ) { let newValue = 0 ; let scrollCount = 0 ; let oldTimestamp = performance . now ( ) ; let oldValue = this . elementRef . nativeElement [ target ] ; const cosParameter = ( oldValue - value ) / 2 ; const step = ( newTimestamp ) => { scrollCount += Math . PI / ( speed / ( newTimestamp - oldTimestamp ) ) ; newValue = Math . round ( value + cosParameter + cosParameter * Math . cos ( scrollCount ) ) ; if ( this . elementRef . nativeElement [ target ] === oldValue ) { if ( scrollCount >= Math . PI ) { this . elementRef . nativeElement [ target ] = value ; this . update ( ) ; this . update ( ) ; } else { this . elementRef . nativeElement [ target ] = oldValue = newValue ; oldTimestamp = newTimestamp ; window . requestAnimationFrame ( step ) ; } } } ; window . requestAnimationFrame ( step ) ; } } }