export interface BespokeNavigationOption { interval ? : number ; } type Direction = 'X' | 'Y' ; const isScrollable = ( elm , dir ) : boolean => hasScrollableArea ( elm , dir ) && hasScrollableOverflow ( getComputedStyle ( elm ) , dir ) ; const hasScrollableArea = ( elm , dir ) => { const length = dir === 'X' ? 'Width' : 'Height' ; return elm [ `client${ length }` ] < elm [ `scroll${ length }` ] ; } ; const hasScrollableOverflow = ( style , dir ) => { const { overflow } = style ; const overflowDir = style [ `overflow${ dir }` ] ; return ( overflow === 'auto' || overflow === 'scroll' || overflowDir === 'auto' || overflowDir === 'scroll' ) ; } ; const bespokeNavigation = ( { interval = 250 } = { } ) => ( deck ) => { document . addEventListener ( 'keydown' , ( e ) => { if ( e . key === ' ' && e . shiftKey ) { deck . prev ( ) ; } else if ( e . key === 'ArrowLeft' || e . key === 'ArrowUp' || e . key === 'PageUp' ) { deck . prev ( { fragment : ! e . shiftKey } ) ; } else if ( e . key === ' ' && ! e . shiftKey ) { deck . next ( ) ; } else if ( e . key === 'ArrowRight' || e . key === 'ArrowDown' || e . key === 'PageDown' ) { deck . next ( { fragment : ! e . shiftKey } ) ; } else if ( e . key === 'End' ) { deck . slide ( deck . slides . length - 1 , { fragment : - 1 } ) ; } else if ( e . key === 'Home' ) { deck . slide ( 0 ) ; } else { return ; } e . preventDefault ( ) ; } ) ; let lastWheelNavigationAt = 0 ; let lastWheelDelta ; let wheelIntervalTimer ; deck . parent . addEventListener ( 'wheel' , ( e ) => { let scrollable = false ; const detectScrollable = ( elm , dir ) => { if ( elm ) scrollable = scrollable || isScrollable ( elm , dir ) ; if ( elm ?. parentElement ) detectScrollable ( elm . parentElement , dir ) ; } ; if ( e . deltaX !== 0 ) detectScrollable ( < HTMLElement > e . target , 'X' ) ; if ( e . deltaY !== 0 ) detectScrollable ( < HTMLElement > e . target , 'Y' ) ; if ( scrollable ) return ; e . preventDefault ( ) ; const currentWheelDelta = Math . sqrt ( e . deltaX ** 2 + e . deltaY ** 2 ) ; if ( e . wheelDelta !== undefined ) { if ( e . webkitForce === undefined ) { if ( Math . abs ( e . wheelDelta ) < 40 ) return ; } if ( e . deltaMode === e . DOM_DELTA_PIXEL && currentWheelDelta < 4 ) return ; } else { if ( e . deltaMode === e . DOM_DELTA_PIXEL && currentWheelDelta < 12 ) return ; } if ( wheelIntervalTimer ) clearTimeout ( wheelIntervalTimer ) ; wheelIntervalTimer = setTimeout ( ( ) => { lastWheelDelta = 0 ; } , interval ) ; const debouncing = Date . now ( ) - lastWheelNavigationAt < interval ; const attenuated = currentWheelDelta <= lastWheelDelta ; lastWheelDelta = currentWheelDelta ; if ( debouncing || attenuated ) return ; let direction ; if ( e . deltaX > 0 || e . deltaY > 0 ) direction = 'next' ; if ( e . deltaX < 0 || e . deltaY < 0 ) direction = 'prev' ; if ( ! direction ) return ; deck [ direction ] ( ) ; lastWheelNavigationAt = Date . now ( ) ; } ) ; } ; export default bespokeNavigation ;