import { EmblaCarouselType , EmblaPluginType } from 'embla-carousel' ; import WheelGestures , { WheelEventState } from 'wheel-gestures' ; export type WheelGesturesPluginOptions = { wheelDraggingClass : string ; forceWheelAxis ? : 'x' | 'y' ; } ; type WheelGesturesPluginType = EmblaPluginType < WheelGesturesPluginOptions > ; const defaultOptions = { wheelDraggingClass : 'is-wheel-dragging' , forceWheelAxis : undefined , } ; const __DEV__ = process . env . NODE_ENV !== 'production' ; export function WheelGesturesPlugin ( userOptions ? ) { const options = { ... defaultOptions , ... userOptions , } ; let cleanup = ( ) => { } ; function init ( embla ) { const engine = embla . internalEngine ( ) ; const targetNode = embla . containerNode ( ) . parentNode as Element ; const wheelAxis = options . forceWheelAxis ?? engine . options . axis ; const wheelGestures = WheelGestures ( { preventWheelAction : wheelAxis , reverseSign : [ true , true , false ] , } ) ; const unobserveTargetNode = wheelGestures . observe ( targetNode ) ; const offWheel = wheelGestures . on ( 'wheel' , handleWheel ) ; let isStarted = false ; let startEvent ; function wheelGestureStarted ( state ) { try { startEvent = new MouseEvent ( 'mousedown' , state . event ) ; dispatchEvent ( startEvent ) ; } catch ( e ) { if ( __DEV__ ) { console . warn ( 'Legacy browser requires events-polyfill (https://github.com/xiel/embla-carousel-wheel-gestures#legacy-browsers)' ) ; } return cleanup ( ) ; } isStarted = true ; addNativeMouseEventListeners ( ) ; if ( options . wheelDraggingClass ) { targetNode . classList . add ( options . wheelDraggingClass ) ; } } function wheelGestureEnded ( state ) { isStarted = false ; dispatchEvent ( createRelativeMouseEvent ( 'mouseup' , state ) ) ; removeNativeMouseEventListeners ( ) ; if ( options . wheelDraggingClass ) { targetNode . classList . remove ( options . wheelDraggingClass ) ; } } function addNativeMouseEventListeners ( ) { document . documentElement . addEventListener ( 'mousemove' , preventNativeMouseHandler , true ) ; document . documentElement . addEventListener ( 'mouseup' , preventNativeMouseHandler , true ) ; document . documentElement . addEventListener ( 'mousedown' , preventNativeMouseHandler , true ) ; } function removeNativeMouseEventListeners ( ) { document . documentElement . removeEventListener ( 'mousemove' , preventNativeMouseHandler , true ) ; document . documentElement . removeEventListener ( 'mouseup' , preventNativeMouseHandler , true ) ; document . documentElement . removeEventListener ( 'mousedown' , preventNativeMouseHandler , true ) ; } function preventNativeMouseHandler ( e ) { if ( isStarted && e . isTrusted ) { e . stopImmediatePropagation ( ) ; } } function createRelativeMouseEvent ( type , state ) { let moveX , moveY ; if ( wheelAxis === engine . options . axis ) { ; [ moveX , moveY ] = state . axisMovement ; } else { ; [ moveY , moveX ] = state . axisMovement ; } return new MouseEvent ( type , { clientX : startEvent . clientX + moveX , clientY : startEvent . clientY + moveY , screenX : startEvent . screenX + moveX , screenY : startEvent . screenY + moveY , movementX : moveX , movementY : moveY , button : 0 , bubbles : true , cancelable : true , } ) ; } function dispatchEvent ( event ) { embla . containerNode ( ) . dispatchEvent ( event ) ; } function handleWheel ( state ) { const { axisDelta : [ deltaX , deltaY ] , } = state ; const primaryAxisDelta = wheelAxis === 'x' ? deltaX : deltaY ; const crossAxisDelta = wheelAxis === 'x' ? deltaY : deltaX ; const isRelease = state . isMomentum && state . previous && ! state . previous . isMomentum ; const isEndingOrRelease = ( state . isEnding && ! state . isMomentum ) || isRelease ; const primaryAxisDeltaIsDominant = Math . abs ( primaryAxisDelta ) > Math . abs ( crossAxisDelta ) ; if ( primaryAxisDeltaIsDominant && ! isStarted && ! state . isMomentum ) { wheelGestureStarted ( state ) ; } if ( ! isStarted ) return ; if ( isEndingOrRelease ) { wheelGestureEnded ( state ) ; } else { dispatchEvent ( createRelativeMouseEvent ( 'mousemove' , state ) ) ; } } cleanup = ( ) => { unobserveTargetNode ( ) ; offWheel ( ) ; removeNativeMouseEventListeners ( ) ; } ; } return { name : 'wheel-gestures' , init , destroy : ( ) => cleanup ( ) , options , } ; }