import { BoxSliderOptions , defaults } from './box-slider-options' ; import { Effect } from './effects' ; import { StyleStore } from './style-store' ; import { responder } from './responder' ; export type EventType = 'after' | 'before' | 'destroy' | 'pause' | 'play' ; export type EventData = { [ key ] : any ; } ; export class BoxSlider { private readonly options : BoxSliderOptions ; private activeIndex : number ; private autoScrollTimer : number ; private el : HTMLElement ; private effect : Effect ; private eventListeners : { [ ev ] : ( ( payload : EventData ) => void ) [ ] ; } ; private isDestroyed : boolean ; private slides : HTMLElement [ ] ; private styleStore : StyleStore ; private transitionPromise : Promise < BoxSlider > ; constructor ( el , options ) { if ( ! options . effect ) { throw new Error ( 'No slide effect defined in box options' ) ; } this . el = el as HTMLElement ; this . effect = options . effect ; this . options = { ... defaults , ... options } ; this . slides = Array . from ( el . children ) . filter ( ( el ) => el instanceof HTMLElement ) as HTMLElement [ ] ; this . activeIndex = this . options . startIndex ; this . eventListeners = { } ; this . styleStore = new StyleStore ( ) ; this . isDestroyed = false ; this . applyEventListeners ( ) ; responder . add ( this ) ; if ( this . slides . length > this . activeIndex ) { this . effect . initialize ( this . el , this . slides , this . styleStore , { ... this . options , effect : undefined } ) ; if ( options . autoScroll ) { this . setAutoScroll ( ) ; } } } reset ( ) { this . styleStore . revert ( ) ; this . effect . initialize ( this . el , this . slides , this . styleStore , { ... this . options , effect : undefined , startIndex : this . activeIndex } ) ; } next ( ) { return this . skipTo ( this . activeIndex === this . slides . length - 1 ? 0 : this . activeIndex + 1 , false ) ; } prev ( ) { return this . skipTo ( this . activeIndex === 0 ? this . slides . length - 1 : this . activeIndex - 1 , true ) ; } skipTo ( nextIndex , backwards ? ) { if ( this . isDestroyed ) { throw new Error ( 'Invalid attempt made to move destroyed slider instance' ) ; } if ( nextIndex < 0 || nextIndex >= this . slides . length ) { throw new Error ( `${ nextIndex } is not a valid slide index` ) ; } if ( nextIndex === this . activeIndex ) { return Promise . resolve ( this ) ; } const settings = { el : this . el , slides : this . slides , speed : this . options . speed , currentIndex : this . activeIndex , isPrevious : backwards === undefined ? nextIndex < this . activeIndex : backwards , nextIndex , } ; this . activeIndex = nextIndex ; this . transitionPromise = ( this . transitionPromise || Promise . resolve ( this ) ) . then ( ( ) => { if ( this . options . autoScroll ) { this . pause ( ) ; } this . emit ( 'before' , { currentIndex : settings . currentIndex , nextIndex : settings . nextIndex , speed : settings . speed } ) ; return this . effect . transition ( settings ) . then ( ( ) => { if ( this . options . autoScroll ) { this . play ( ) ; } this . emit ( 'after' , { activeIndex : settings . nextIndex } ) ; return this ; } ) ; } ) ; return this . transitionPromise ; } pause ( ) { if ( this . autoScrollTimer ) { this . stopAutoPlay ( ) ; this . emit ( 'pause' ) ; } return this ; } play ( ) { this . setAutoScroll ( ) ; this . emit ( 'play' ) ; return this ; } addEventListener ( ev , callback ) { this . eventListeners [ ev ] = this . eventListeners [ ev ] || [ ] ; this . eventListeners [ ev ] . push ( callback ) ; return this ; } removeEventListener ( ev , callback ) { if ( this . eventListeners [ ev ] ) { this . eventListeners [ ev ] = this . eventListeners [ ev ] . filter ( cb => cb !== callback ) ; } return this ; } destroy ( ) { this . isDestroyed = true ; this . stopAutoPlay ( ) ; ( this . transitionPromise || Promise . resolve ( null ) ) . then ( ( ) => { if ( this . effect . destroy ) { this . effect . destroy ( this . el ) ; } this . styleStore . revert ( ) ; responder . remove ( this ) ; this . emit ( 'destroy' ) ; this . eventListeners = { } ; delete this . el ; delete this . slides ; delete this . styleStore ; delete this . effect ; } ) ; } private stopAutoPlay ( ) { window . clearTimeout ( this . autoScrollTimer ) ; } private emit ( ev , payload ? ) { ( this . eventListeners [ ev ] || [ ] ) . forEach ( cb => cb ( payload ) ) ; } private setAutoScroll ( ) { if ( this . autoScrollTimer ) { this . pause ( ) ; } this . autoScrollTimer = window . setTimeout ( ( ) => this . next ( ) . then ( ( ) => this . setAutoScroll ( ) ) , this . options . timeout ) ; } private applyEventListeners ( ) { if ( this . options . pauseOnHover ) { this . el . addEventListener ( 'mouseenter' , ( ) => this . pause ( ) ) ; this . el . addEventListener ( 'mouseleave' , ( ) => this . play ( ) ) ; } if ( this . options . swipe ) { this . addSwipeNavigation ( ) ; } } private addSwipeNavigation ( ) { let pointerTraceX = 0 ; this . el . addEventListener ( 'pointerdown' , ev => { pointerTraceX = ev . clientX ; } ) ; this . el . addEventListener ( 'pointerup' , ev => { const distanceX = ev . clientX - pointerTraceX ; if ( Math . abs ( distanceX ) >= this . options . swipeTolerance ) { if ( distanceX > 0 ) { this . prev ( ) ; } else { this . next ( ) ; } ev . stopPropagation ( ) ; } } ) ; } }