import { Output , Directive } from '@angular/core' ; import { animationFrameScheduler , of , fromEvent , Observable , Subject } from 'rxjs' ; import { distinctUntilChanged , map , mergeMap , pluck , takeUntil , tap } from 'rxjs/operators' ; import { enableSelection , preventSelection , stopPropagation } from '../common' ; import { TrackAdapter } from '../track/track' ; import { NgScrollbarBase } from '../../ng-scrollbar-base' ; @ Directive ( ) export abstract class ThumbAdapter { private _dragging = new Subject < boolean > ( ) ; @ Output ( ) dragging = this . _dragging . pipe ( distinctUntilChanged ( ) ) ; protected abstract get pageProperty ( ) : string ; protected abstract get clientProperty ( ) : string ; abstract get dragStartOffset ( ) : number ; abstract get size ( ) : number ; protected abstract get viewportScrollSize ( ) : number ; protected abstract get viewportScrollOffset ( ) : number ; abstract get viewportScrollMax ( ) : number ; get trackMax ( ) : number { return this . track ! . size - this . size ; } get clientRect ( ) : DOMRect { return this . thumbElement . getBoundingClientRect ( ) ; } get clicked ( ) : Observable < MouseEvent > { return fromEvent < MouseEvent > ( this . thumbElement , 'mousedown' , { passive : true } ) . pipe ( stopPropagation ( ) ) ; } protected constructor ( protected cmp , protected track , protected thumbElement , protected document ) { } update ( ) { const size = calculateThumbSize ( this . track ! . size , this . viewportScrollSize , this . cmp . minThumbSize ! ) ; const position = calculateThumbPosition ( this . viewportScrollOffset , this . viewportScrollMax , this . trackMax ) ; animationFrameScheduler . schedule ( ( ) => this . updateStyles ( this . handleDirection ( position , this . trackMax ) , size ) ) ; } /**
     * Stream that emits the 'scrollTo' position when a scrollbar thumb element is dragged
     * This function is called by thumb drag event using viewport or scrollbar pointer events
     */ dragged ( event ) { let trackMaxStart ; let scrollMaxStart ; const dragStart = of < MouseEvent > ( event ) . pipe ( preventSelection ( this . document ) , tap ( ( ) => { trackMaxStart = this . trackMax ; scrollMaxStart = this . viewportScrollMax ; this . setDragging ( true ) ; } ) ) ; const dragging = fromEvent < MouseEvent > ( this . document , 'mousemove' , { capture : true , passive : true } ) . pipe ( stopPropagation ( ) ) ; const dragEnd = fromEvent < MouseEvent > ( this . document , 'mouseup' , { capture : true } ) . pipe ( stopPropagation ( ) , enableSelection ( this . document ) , tap ( ( ) => this . setDragging ( false ) ) ) ; return dragStart . pipe ( pluck < any , string > ( this . pageProperty ) , map ( ( pageOffset ) => pageOffset - this . dragStartOffset ) , mergeMap ( ( mouseDownOffset ) => dragging . pipe ( pluck ( this . clientProperty ) , map ( ( mouseOffset ) => mouseOffset - this . track ! . offset ) , map ( ( offset ) => scrollMaxStart * ( offset - mouseDownOffset ) / trackMaxStart ) , map ( ( position ) => this . handleDrag ( position , scrollMaxStart ) ) , tap ( ( position ) => this . scrollTo ( position ) ) , takeUntil ( dragEnd ) ) ) ) ; } protected abstract setDragging ( value ) ; protected abstract scrollTo ( position ) ; protected abstract updateStyles ( position , size ) ; protected abstract handleDrag ( position , scrollMax ? ) ; protected abstract handleDirection ( position , scrollMax ? ) ; } /**
 * Calculate scrollbar thumb size
 */ function calculateThumbSize ( trackSize , contentSize , minThumbSize ) { const scrollbarRatio = trackSize / contentSize ; const thumbSize = scrollbarRatio * trackSize ; return Math . max ( ~ ~ thumbSize , minThumbSize ) ; } /**
 * Calculate scrollbar thumb position
 */ function calculateThumbPosition ( scrollPosition , scrollMax , trackMax ) { return scrollPosition * trackMax / scrollMax ; }