for (var _i2 = 0, _Object$entries2 = Object.entries(bindings); _i2 < _Object$entries2.length; _i2++) {
var _Object$entries2$_i = _Object$entries2[_i2],
event = _Object$entries2$_i[0],
fns = _Object$entries2$_i[1];
var fnsArray = Array.isArray(fns) ? fns : [fns];
var key = event + captureString;
props[key] = chainFns.apply(void 0, fnsArray);
function takeAll(array) {
return array.splice(0, array.length);
function getDomTargetFromConfig(_ref5) {
var domTarget = _ref5.domTarget;
return domTarget && 'current' in domTarget ? domTarget.current : domTarget;
* bindings is an object which keys match ReactEventHandlerKeys.
* Since a recognizer might want to bind a handler function to an event key already used by a previously
* added recognizer, we need to make sure that each event key is an array of all the functions mapped for
function addBindings(bindings, name, fn) {
if (!bindings[name]) bindings[name] = [];
function addListeners(el, listeners, options) {
if (listeners === void 0) {
if (options === void 0) {
for (var _iterator2 = _createForOfIteratorHelperLoose(listeners), _step2; !(_step2 = _iterator2()).done;) {
var _step2$value = _step2.value,
eventName = _step2$value[0],
eventHandler = _step2$value[1];
el.addEventListener(eventName, eventHandler, options);
function removeListeners(el, listeners, options) {
if (listeners === void 0) {
if (options === void 0) {
for (var _iterator3 = _createForOfIteratorHelperLoose(listeners), _step3; !(_step3 = _iterator3()).done;) {
var _step3$value = _step3.value,
eventName = _step3$value[0],
eventHandler = _step3$value[1];
el.removeEventListener(eventName, eventHandler, options);
/* eslint-disable react-hooks/exhaustive-deps */
* Utility hook called by all gesture hooks and that will be responsible for the internals.
* @param nativeHandlers - native handlers such as onClick, onMouseDown, etc.
function useRecognizers(handlers, config, nativeHandlers) {
if (nativeHandlers === void 0) {
var classes = resolveClasses(handlers);
var controller = external_React_default.a.useMemo(function () {
return new Controller(classes);
controller.config = config;
controller.handlers = handlers;
controller.nativeRefs = nativeHandlers;
external_React_default.a.useEffect(controller.effect, []); // @ts-ignore
if (controller.config.domTarget) return deprecationNoticeForDomTarget; // @ts-ignore
function deprecationNoticeForDomTarget() {
function resolveClasses(internalHandlers) {
if (internalHandlers.drag) classes.add(RecognizersMap.get('drag'));
if (internalHandlers.wheel) classes.add(RecognizersMap.get('wheel'));
if (internalHandlers.scroll) classes.add(RecognizersMap.get('scroll'));
if (internalHandlers.move) classes.add(RecognizersMap.get('move'));
if (internalHandlers.pinch) classes.add(RecognizersMap.get('pinch'));
if (internalHandlers.hover) classes.add(RecognizersMap.get('hover'));
* Abstract class for coordinates-based gesture recongizers
var CoordinatesRecognizer = /*#__PURE__*/function (_Recognizer) {
_inheritsLoose(CoordinatesRecognizer, _Recognizer);
function CoordinatesRecognizer() {
return _Recognizer.apply(this, arguments) || this;
var _proto = CoordinatesRecognizer.prototype;
* Returns the real movement (without taking intentionality into account)
_proto.getInternalMovement = function getInternalMovement(values, state) {
return subV(values, state.initial);
* In coordinates-based gesture, this function will detect the first intentional axis,
* lock the gesture axis if lockDirection is specified in the config, block the gesture
* if the first intentional axis doesn't match the specified axis in config.
_proto.checkIntentionality = function checkIntentionality(_intentional, _movement) {
if (_intentional[0] === false && _intentional[1] === false) {
_intentional: _intentional,
var _movement$map = _movement.map(Math.abs),
var axis = this.state.axis || (absX > absY ? 'x' : absX < absY ? 'y' : undefined);
if (!this.config.axis && !this.config.lockDirection) return {
_intentional: _intentional,
_intentional: [false, false],
if (!!this.config.axis && axis !== this.config.axis) return {
_intentional: _intentional,
_intentional[axis === 'x' ? 1 : 0] = false;
_intentional: _intentional,
_proto.getKinematics = function getKinematics(values, event) {
var state = this.getMovement(values);
var dt = event.timeStamp - this.state.timeStamp;
Object.assign(state, calculateAllKinematics(state.movement, state.delta, dt));
_proto.mapStateValues = function mapStateValues(state) {
return CoordinatesRecognizer;
var TAP_DISTANCE_THRESHOLD = 3;
function persistEvent(event) {
'persist' in event && typeof event.persist === 'function' && event.persist();
var DragRecognizer = /*#__PURE__*/function (_CoordinatesRecognize) {
_inheritsLoose(DragRecognizer, _CoordinatesRecognize);
function DragRecognizer() {
_this = _CoordinatesRecognize.apply(this, arguments) || this;
_this.ingKey = 'dragging';
_this.stateKey = 'drag'; // TODO add back when setPointerCapture is widely wupported
// https://caniuse.com/#search=setPointerCapture
_this.setPointerCapture = function (event) {
// don't perform pointere capture when user wants to use touch events or
// when a pointerLockElement exists as this would throw an error
if (_this.config.useTouch || document.pointerLockElement) return;
var target = event.target,
pointerId = event.pointerId;
if (target && 'setPointerCapture' in target) {
// this would work in the DOM but doesn't with react three fiber
// target.addEventListener('pointermove', this.onDragChange, this.controller.config.eventOptions)
target.setPointerCapture(pointerId);
_this.updateGestureState({
_dragPointerId: pointerId
_this.releasePointerCapture = function () {
if (_this.config.useTouch || document.pointerLockElement) return;
var _this$state = _this.state,
_dragTarget = _this$state._dragTarget,
_dragPointerId = _this$state._dragPointerId;
if (_dragPointerId && _dragTarget && 'releasePointerCapture' in _dragTarget) {
// this would work in the DOM but doesn't with react three fiber
// target.removeEventListener('pointermove', this.onDragChange, this.controller.config.eventOptions)
if (!('hasPointerCapture' in _dragTarget) || _dragTarget.hasPointerCapture(_dragPointerId)) try {
_dragTarget.releasePointerCapture(_dragPointerId);
_this.preventScroll = function (event) {
if (_this.state._dragPreventScroll && event.cancelable) {
_this.getEventId = function (event) {
if (_this.config.useTouch) return event.changedTouches[0].identifier;
_this.isValidEvent = function (event) {
// if we were using pointer events only event.isPrimary === 1 would suffice
return _this.state._pointerId === _this.getEventId(event);
_this.shouldPreventWindowScrollY = _this.config.experimental_preventWindowScrollY && _this.controller.supportsTouchEvents;
_this.setUpWindowScrollDetection = function (event) {
persistEvent(event); // we add window listeners that will prevent the scroll when the user has started dragging
updateWindowListeners(_this.controller, _this.stateKey, [['touchmove', _this.preventScroll], ['touchend', _this.clean.bind(_assertThisInitialized(_this))], ['touchcancel', _this.clean.bind(_assertThisInitialized(_this))]], {
_this.setTimeout(_this.startDrag.bind(_assertThisInitialized(_this)), 250, event);
_this.setUpDelayedDragTrigger = function (event) {
_this.state._dragDelayed = true;
_this.setTimeout(_this.startDrag.bind(_assertThisInitialized(_this)), _this.config.delay, event);
_this.setStartState = function (event) {
var values = getPointerEventValues(event, _this.transform);
_this.updateSharedState(getGenericEventData(event));
_this.updateGestureState(_extends({}, getStartGestureState(_assertThisInitialized(_this), values, event), getGenericPayload(_assertThisInitialized(_this), event, true), {
_pointerId: _this.getEventId(event)
_this.updateGestureState(_this.getMovement(values));
_this.onDragStart = function (event) {
addEventIds(_this.controller, event);
if (!_this.enabled || _this.state._active) return;
_this.setStartState(event);
_this.setPointerCapture(event);
if (_this.shouldPreventWindowScrollY) _this.setUpWindowScrollDetection(event);else if (_this.config.delay > 0) _this.setUpDelayedDragTrigger(event);else _this.startDrag(event, true); // we pass the values to the startDrag event
_this.onDragChange = function (event) {
if ( // if the gesture was canceled or
_this.state.canceled || // if onDragStart wasn't fired or
!_this.state._active || // if the event pointerId doesn't match the one that initiated the drag
!_this.isValidEvent(event) || // if the event has the same timestamp as the previous event
// note that checking type equality is ONLY for tests ¯\_(ツ)_/¯
_this.state._lastEventType === event.type && event.timeStamp === _this.state.timeStamp) return;
if (document.pointerLockElement) {
var movementX = event.movementX,
movementY = event.movementY;
values = addV(_this.transform([movementX, movementY]), _this.state.values);
} else values = getPointerEventValues(event, _this.transform);
var kinematics = _this.getKinematics(values, event); // if startDrag hasn't fired
if (!_this.state._dragStarted) {
// If the gesture isn't active then respond to the event only if
// it's been delayed via the `delay` option, in which case start
// the gesture immediately.
if (_this.state._dragDelayed) {
} // if the user wants to prevent vertical window scroll when user starts dragging
if (_this.shouldPreventWindowScrollY) {
if (!_this.state._dragPreventScroll && kinematics.axis) {
// if the user is dragging horizontally then we should allow the drag
if (kinematics.axis === 'x') {
_this.state._active = false;
var genericEventData = getGenericEventData(event);
_this.updateSharedState(genericEventData);
var genericPayload = getGenericPayload(_assertThisInitialized(_this), event); // This verifies if the drag can be assimilated to a tap by checking
// if the real distance of the drag (ie not accounting for the threshold) is
// greater than the TAP_DISTANCE_THRESHOLD.
var realDistance = calculateDistance(kinematics._movement);
var _dragIsTap = _this.state._dragIsTap;
if (_dragIsTap && realDistance >= TAP_DISTANCE_THRESHOLD) _dragIsTap = false;
_this.updateGestureState(_extends({}, genericPayload, kinematics, {
_this.fireGestureHandler();
_this.onDragEnd = function (event) {
removeEventIds(_this.controller, event); // if the event pointerId doesn't match the one that initiated the drag
// we don't want to end the drag
if (!_this.isValidEvent(event)) return;
_this.clean(); // if the gesture is no longer active (ie canceled)
if (!_this.state._active) return;
_this.state._active = false;
var tap = _this.state._dragIsTap;
var _this$state$velocitie = _this.state.velocities,
vx = _this$state$velocitie[0],
vy = _this$state$velocitie[1];
var _this$state$movement = _this.state.movement,
mx = _this$state$movement[0],
my = _this$state$movement[1];
var _this$state$_intentio = _this.state._intentional,
ix = _this$state$_intentio[0],
iy = _this$state$_intentio[1];
var _this$config$swipeVel = _this.config.swipeVelocity,
svx = _this$config$swipeVel[0],
svy = _this$config$swipeVel[1];
var _this$config$swipeDis = _this.config.swipeDistance,
sx = _this$config$swipeDis[0],
sy = _this$config$swipeDis[1];
var sd = _this.config.swipeDuration;
var endState = _extends({}, getGenericPayload(_assertThisInitialized(_this), event), _this.getMovement(_this.state.values));
if (endState.elapsedTime < sd) {
if (ix !== false && Math.abs(vx) > svx && Math.abs(mx) > sx) swipe[0] = sign(vx);
if (iy !== false && Math.abs(vy) > svy && Math.abs(my) > sy) swipe[1] = sign(vy);
_this.updateSharedState({
_this.updateGestureState(_extends({}, endState, {
_this.fireGestureHandler(_this.config.filterTaps && tap === true);
_this.clean = function () {
_CoordinatesRecognize.prototype.clean.call(_assertThisInitialized(_this));
_this.state._dragStarted = false;
_this.releasePointerCapture();
clearWindowListeners(_this.controller, _this.stateKey);
_this.onCancel = function () {
if (_this.state.canceled) return;
_this.updateGestureState({
_this.updateSharedState({
return _this.fireGestureHandler();
_this.onClick = function (event) {
if (!_this.state._dragIsTap) event.stopPropagation();
var _proto = DragRecognizer.prototype;
_proto.startDrag = function startDrag(event, onDragIsStart) {
if (onDragIsStart === void 0) {
// startDrag can happen after a timeout, so we need to check if the gesture is still active
// as the user might have lift up the pointer in between.
if ( // if the gesture isn't active (probably means)
!this.state._active || // if the drag has already started we should ignore subsequent attempts
this.state._dragStarted) return;
if (!onDragIsStart) this.setStartState(event);
this.updateGestureState({
_dragPreventScroll: true,
this.fireGestureHandler();
_proto.addBindings = function addBindings$1(bindings) {
if (this.config.useTouch) {
addBindings(bindings, 'onTouchStart', this.onDragStart);
addBindings(bindings, 'onTouchMove', this.onDragChange); // this is needed for react-three-fiber
addBindings(bindings, 'onTouchEnd', this.onDragEnd);
addBindings(bindings, 'onTouchCancel', this.onDragEnd);
addBindings(bindings, 'onPointerDown', this.onDragStart);
addBindings(bindings, 'onPointerMove', this.onDragChange); // this is needed for react-three-fiber
addBindings(bindings, 'onPointerUp', this.onDragEnd);
addBindings(bindings, 'onPointerCancel', this.onDragEnd);
if (this.config.filterTaps) {
var handler = this.controller.config.eventOptions.capture ? 'onClick' : 'onClickCapture';