react-use-gesture
Version:
React hook for receiving gestures https://use-gesture.netlify.app
1,665 lines (1,425 loc) • 90.3 kB
JavaScript
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('react')) :
typeof define === 'function' && define.amd ? define(['exports', 'react'], factory) :
(global = global || self, factory(global.ReactUseGesture = {}, global.React));
}(this, (function (exports, React) { 'use strict';
var React__default = 'default' in React ? React['default'] : React;
// vector add
function addV(v1, v2) {
return v1.map(function (v, i) {
return v + v2[i];
});
} // vector substract
function subV(v1, v2) {
return v1.map(function (v, i) {
return v - v2[i];
});
}
/**
* Calculates distance
* @param movement the difference between current and initial vectors
* @returns distance
*/
function calculateDistance(movement) {
return Math.hypot.apply(Math, movement);
}
function calculateAllGeometry(movement, delta) {
if (delta === void 0) {
delta = movement;
}
var dl = calculateDistance(delta);
var alpha = dl === 0 ? 0 : 1 / dl;
var direction = delta.map(function (v) {
return alpha * v;
});
var distance = calculateDistance(movement);
return {
distance: distance,
direction: direction
};
}
/**
* Calculates all kinematics
* @template T the expected vector type
* @param movement the difference between current and initial vectors
* @param delta the difference between current and previous vectors
* @param delta_t the time difference between current and previous timestamps
* @returns all kinematics
*/
function calculateAllKinematics(movement, delta, dt) {
var dl = calculateDistance(delta);
var alpha = dl === 0 ? 0 : 1 / dl;
var beta = dt === 0 ? 0 : 1 / dt;
var velocity = beta * dl;
var velocities = delta.map(function (v) {
return beta * v;
});
var direction = delta.map(function (v) {
return alpha * v;
});
var distance = calculateDistance(movement);
return {
velocities: velocities,
velocity: velocity,
distance: distance,
direction: direction
};
}
/**
* Because IE doesn't support `Math.sign` function, so we use the polyfill version of the function.
* This polyfill function is suggested by Mozilla:
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sign#Polyfill
* @param x target number
*/
function sign(x) {
if (Math.sign) return Math.sign(x);
return Number(x > 0) - Number(x < 0) || +x;
}
function minMax(value, min, max) {
return Math.max(min, Math.min(value, max));
} // Based on @aholachek ;)
// https://twitter.com/chpwn/status/285540192096497664
// iOS constant = 0.55
// https://medium.com/@nathangitter/building-fluid-interfaces-ios-swift-9732bb934bf5
function rubberband2(distance, constant) {
// default constant from the article is 0.7
return Math.pow(distance, constant * 5);
}
function rubberband(distance, dimension, constant) {
if (dimension === 0 || Math.abs(dimension) === Infinity) return rubberband2(distance, constant);
return distance * dimension * constant / (dimension + constant * distance);
}
function rubberbandIfOutOfBounds(position, min, max, constant) {
if (constant === void 0) {
constant = 0.15;
}
if (constant === 0) return minMax(position, min, max);
if (position < min) return -rubberband(min - position, max - min, constant) + min;
if (position > max) return +rubberband(position - max, max - min, constant) + max;
return position;
}
function _defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
function _createClass(Constructor, protoProps, staticProps) {
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
if (staticProps) _defineProperties(Constructor, staticProps);
return Constructor;
}
function _extends() {
_extends = Object.assign || function (target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
}
return target;
};
return _extends.apply(this, arguments);
}
function _inheritsLoose(subClass, superClass) {
subClass.prototype = Object.create(superClass.prototype);
subClass.prototype.constructor = subClass;
subClass.__proto__ = superClass;
}
function _objectWithoutPropertiesLoose(source, excluded) {
if (source == null) return {};
var target = {};
var sourceKeys = Object.keys(source);
var key, i;
for (i = 0; i < sourceKeys.length; i++) {
key = sourceKeys[i];
if (excluded.indexOf(key) >= 0) continue;
target[key] = source[key];
}
return target;
}
function _assertThisInitialized(self) {
if (self === void 0) {
throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
}
return self;
}
function _unsupportedIterableToArray(o, minLen) {
if (!o) return;
if (typeof o === "string") return _arrayLikeToArray(o, minLen);
var n = Object.prototype.toString.call(o).slice(8, -1);
if (n === "Object" && o.constructor) n = o.constructor.name;
if (n === "Map" || n === "Set") return Array.from(o);
if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
}
function _arrayLikeToArray(arr, len) {
if (len == null || len > arr.length) len = arr.length;
for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];
return arr2;
}
function _createForOfIteratorHelperLoose(o, allowArrayLike) {
var it;
if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) {
if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") {
if (it) o = it;
var i = 0;
return function () {
if (i >= o.length) return {
done: true
};
return {
done: false,
value: o[i++]
};
};
}
throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}
it = o[Symbol.iterator]();
return it.next.bind(it);
}
function noop() {}
/**
* TODO Beware that only optimized cases are covered in tests =)
* TODO Need to cover general case as well
*
* @param fns
*/
function chainFns() {
for (var _len = arguments.length, fns = new Array(_len), _key = 0; _key < _len; _key++) {
fns[_key] = arguments[_key];
}
if (fns.length === 0) return noop;
if (fns.length === 1) return fns[0];
return function () {
var result;
for (var _iterator = _createForOfIteratorHelperLoose(fns), _step; !(_step = _iterator()).done;) {
var fn = _step.value;
result = fn.apply(this, arguments) || result;
}
return result;
};
}
/**
* Expects a simple value or 2D vector (an array with 2 elements) and
* always returns 2D vector. If simple value is passed, returns a
* vector with this value as both coordinates.
*
* @param value
*/
function ensureVector(value, fallback) {
if (value === undefined) {
if (fallback === undefined) {
throw new Error('Must define fallback value if undefined is expected');
}
value = fallback;
}
if (Array.isArray(value)) return value;
return [value, value];
}
/**
* Helper for defining a default value
*
* @param value
* @param fallback
*/
function assignDefault(value, fallback) {
return Object.assign({}, fallback, value || {});
}
/**
* Resolves getters (functions) by calling them
* If simple value is given it just passes through
*
* @param v
*/
function valueFn(v) {
if (typeof v === 'function') {
for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
args[_key2 - 1] = arguments[_key2];
}
// @ts-ignore
return v.apply(void 0, args);
} else {
return v;
}
}
function resolveWith(config, resolvers) {
if (config === void 0) {
config = {};
}
var result = {};
for (var _i = 0, _Object$entries = Object.entries(resolvers); _i < _Object$entries.length; _i++) {
var _Object$entries$_i = _Object$entries[_i],
key = _Object$entries$_i[0],
resolver = _Object$entries$_i[1];
switch (typeof resolver) {
case 'function':
result[key] = resolver.call(result, config[key], key, config);
break;
case 'object':
result[key] = resolveWith(config[key], resolver);
break;
case 'boolean':
if (resolver) result[key] = config[key];
break;
}
}
return result;
}
/**
* Whether the browser supports GestureEvent (ie Safari)
* @returns true if the browser supports gesture event
*/
function supportsGestureEvents() {
try {
// TODO [TS] possibly find GestureEvent definitions?
// @ts-ignore: no type definitions for webkit GestureEvents
return 'constructor' in GestureEvent;
} catch (e) {
return false;
}
}
function supportsTouchEvents() {
return typeof window !== 'undefined' && 'ontouchstart' in window;
}
function supportsPointerEvents() {
return typeof window !== 'undefined' && 'onpointerdown' in window;
}
function getEventTouches(event) {
if ('pointerId' in event) return null;
return event.type === 'touchend' ? event.changedTouches : event.targetTouches;
}
function getTouchIds(event) {
return Array.from(getEventTouches(event)).map(function (t) {
return t.identifier;
});
}
function getGenericEventData(event) {
var buttons = 'buttons' in event ? event.buttons : 0;
var shiftKey = event.shiftKey,
altKey = event.altKey,
metaKey = event.metaKey,
ctrlKey = event.ctrlKey; // TODO check if this might create some overrides?
return {
buttons: buttons,
shiftKey: shiftKey,
altKey: altKey,
metaKey: metaKey,
ctrlKey: ctrlKey
};
}
var identity = function identity(xy) {
return xy;
};
/**
* Gets pointer event values.
* @param event
* @returns pointer event values
*/
function getPointerEventValues(event, transform) {
if (transform === void 0) {
transform = identity;
}
var touchEvents = getEventTouches(event);
var _ref = touchEvents ? touchEvents[0] : event,
clientX = _ref.clientX,
clientY = _ref.clientY;
return transform([clientX, clientY]);
}
/**
* Gets two touches event data
* @param event
* @returns two touches event data
*/
function getTwoTouchesEventValues(event, pointerIds, transform) {
if (transform === void 0) {
transform = identity;
}
var _Array$from$filter = Array.from(event.touches).filter(function (t) {
return pointerIds.includes(t.identifier);
}),
A = _Array$from$filter[0],
B = _Array$from$filter[1];
if (!A || !B) throw Error("The event doesn't have two pointers matching the pointerIds");
var dx = B.clientX - A.clientX;
var dy = B.clientY - A.clientY;
var cx = (B.clientX + A.clientX) / 2;
var cy = (B.clientY + A.clientY) / 2; // const e: any = 'nativeEvent' in event ? event.nativeEvent : event
var distance = Math.hypot(dx, dy); // FIXME rotation has inconsistant values so we're not using it atm
// const angle = (e.rotation as number) ?? -(Math.atan2(dx, dy) * 180) / Math.PI
var angle = -(Math.atan2(dx, dy) * 180) / Math.PI;
var values = transform([distance, angle]);
var origin = transform([cx, cy]);
return {
values: values,
origin: origin
};
}
/**
* Gets scroll event values
* @param event
* @returns scroll event values
*/
function getScrollEventValues(event, transform) {
if (transform === void 0) {
transform = identity;
}
// If the currentTarget is the window then we return the scrollX/Y position.
// If not (ie the currentTarget is a DOM element), then we return scrollLeft/Top
var _event$currentTarget = event.currentTarget,
scrollX = _event$currentTarget.scrollX,
scrollY = _event$currentTarget.scrollY,
scrollLeft = _event$currentTarget.scrollLeft,
scrollTop = _event$currentTarget.scrollTop;
return transform([scrollX || scrollLeft || 0, scrollY || scrollTop || 0]);
} // wheel delta defaults from https://github.com/facebookarchive/fixed-data-table/blob/master/src/vendor_upstream/dom/normalizeWheel.js
var LINE_HEIGHT = 40;
var PAGE_HEIGHT = 800;
/**
* Gets wheel event values.
* @param event
* @returns wheel event values
*/
function getWheelEventValues(event, transform) {
if (transform === void 0) {
transform = identity;
}
var deltaX = event.deltaX,
deltaY = event.deltaY,
deltaMode = event.deltaMode; // normalize wheel values, especially for Firefox
if (deltaMode === 1) {
deltaX *= LINE_HEIGHT;
deltaY *= LINE_HEIGHT;
} else if (deltaMode === 2) {
deltaX *= PAGE_HEIGHT;
deltaY *= PAGE_HEIGHT;
}
return transform([deltaX, deltaY]);
}
/**
* Gets webkit gesture event values.
* @param event
* @returns webkit gesture event values
*/
function getWebkitGestureEventValues(event, transform) {
if (transform === void 0) {
transform = identity;
}
return transform([event.scale, event.rotation]);
}
var DEFAULT_DRAG_DELAY = 180;
var DEFAULT_RUBBERBAND = 0.15;
var DEFAULT_SWIPE_VELOCITY = 0.5;
var DEFAULT_SWIPE_DISTANCE = 50;
var DEFAULT_SWIPE_DURATION = 250;
var InternalGestureOptionsNormalizers = {
threshold: function threshold(value) {
if (value === void 0) {
value = 0;
}
return ensureVector(value);
},
rubberband: function rubberband(value) {
if (value === void 0) {
value = 0;
}
switch (value) {
case true:
return ensureVector(DEFAULT_RUBBERBAND);
case false:
return ensureVector(0);
default:
return ensureVector(value);
}
},
enabled: function enabled(value) {
if (value === void 0) {
value = true;
}
return value;
},
triggerAllEvents: function triggerAllEvents(value) {
if (value === void 0) {
value = false;
}
return value;
},
initial: function initial(value) {
if (value === void 0) {
value = 0;
}
if (typeof value === 'function') return value;
return ensureVector(value);
},
transform: true
};
var InternalCoordinatesOptionsNormalizers = /*#__PURE__*/_extends({}, InternalGestureOptionsNormalizers, {
axis: true,
lockDirection: function lockDirection(value) {
if (value === void 0) {
value = false;
}
return value;
},
bounds: function bounds(value) {
if (value === void 0) {
value = {};
}
if (typeof value === 'function') return function (state) {
return InternalCoordinatesOptionsNormalizers.bounds(value(state));
};
var _value2 = value,
_value2$left = _value2.left,
left = _value2$left === void 0 ? -Infinity : _value2$left,
_value2$right = _value2.right,
right = _value2$right === void 0 ? Infinity : _value2$right,
_value2$top = _value2.top,
top = _value2$top === void 0 ? -Infinity : _value2$top,
_value2$bottom = _value2.bottom,
bottom = _value2$bottom === void 0 ? Infinity : _value2$bottom;
return [[left, right], [top, bottom]];
}
});
var isBrowser = typeof window !== 'undefined' && window.document && window.document.createElement;
var InternalGenericOptionsNormalizers = {
enabled: function enabled(value) {
if (value === void 0) {
value = true;
}
return value;
},
domTarget: true,
window: /*#__PURE__*/function (_window) {
function window(_x) {
return _window.apply(this, arguments);
}
window.toString = function () {
return _window.toString();
};
return window;
}(function (value) {
if (value === void 0) {
value = isBrowser ? window : undefined;
}
return value;
}),
eventOptions: function eventOptions(_temp) {
var _ref = _temp === void 0 ? {} : _temp,
_ref$passive = _ref.passive,
passive = _ref$passive === void 0 ? true : _ref$passive,
_ref$capture = _ref.capture,
capture = _ref$capture === void 0 ? false : _ref$capture;
return {
passive: passive,
capture: capture
};
},
transform: true
};
var InternalDistanceAngleOptionsNormalizers = /*#__PURE__*/_extends({}, InternalGestureOptionsNormalizers, {
bounds: function bounds(_value, _key, _ref2) {
var _ref2$distanceBounds = _ref2.distanceBounds,
distanceBounds = _ref2$distanceBounds === void 0 ? {} : _ref2$distanceBounds,
_ref2$angleBounds = _ref2.angleBounds,
angleBounds = _ref2$angleBounds === void 0 ? {} : _ref2$angleBounds;
var _distanceBounds = function _distanceBounds(state) {
var D = assignDefault(valueFn(distanceBounds, state), {
min: -Infinity,
max: Infinity
});
return [D.min, D.max];
};
var _angleBounds = function _angleBounds(state) {
var A = assignDefault(valueFn(angleBounds, state), {
min: -Infinity,
max: Infinity
});
return [A.min, A.max];
};
if (typeof distanceBounds !== 'function' && typeof angleBounds !== 'function') return [_distanceBounds(), _angleBounds()];
return function (state) {
return [_distanceBounds(state), _angleBounds(state)];
};
}
});
var InternalDragOptionsNormalizers = /*#__PURE__*/_extends({}, InternalCoordinatesOptionsNormalizers, {
useTouch: function useTouch(value) {
if (value === void 0) {
value = false;
}
var supportsTouch = supportsTouchEvents();
var supportsPointer = supportsPointerEvents();
if (value && supportsTouch) return true;
if (supportsTouch && !supportsPointer) return true;
return false;
},
experimental_preventWindowScrollY: function experimental_preventWindowScrollY(value) {
if (value === void 0) {
value = false;
}
return value;
},
threshold: function threshold(v, _k, _ref3) {
var _ref3$filterTaps = _ref3.filterTaps,
filterTaps = _ref3$filterTaps === void 0 ? false : _ref3$filterTaps,
_ref3$lockDirection = _ref3.lockDirection,
lockDirection = _ref3$lockDirection === void 0 ? false : _ref3$lockDirection,
_ref3$axis = _ref3.axis,
axis = _ref3$axis === void 0 ? undefined : _ref3$axis;
var A = ensureVector(v, filterTaps ? 3 : lockDirection ? 1 : axis ? 1 : 0);
this.filterTaps = filterTaps;
return A;
},
swipeVelocity: function swipeVelocity(v) {
if (v === void 0) {
v = DEFAULT_SWIPE_VELOCITY;
}
return ensureVector(v);
},
swipeDistance: function swipeDistance(v) {
if (v === void 0) {
v = DEFAULT_SWIPE_DISTANCE;
}
return ensureVector(v);
},
swipeDuration: function swipeDuration(value) {
if (value === void 0) {
value = DEFAULT_SWIPE_DURATION;
}
return value;
},
delay: function delay(value) {
if (value === void 0) {
value = 0;
}
switch (value) {
case true:
return DEFAULT_DRAG_DELAY;
case false:
return 0;
default:
return value;
}
}
});
function getInternalGenericOptions(config) {
if (config === void 0) {
config = {};
}
// TODO warn when passive is set to true and domTarget is undefined
return resolveWith(config, InternalGenericOptionsNormalizers);
}
function getInternalCoordinatesOptions(config) {
if (config === void 0) {
config = {};
}
return resolveWith(config, InternalCoordinatesOptionsNormalizers);
}
function getInternalDistanceAngleOptions(config) {
if (config === void 0) {
config = {};
}
return resolveWith(config, InternalDistanceAngleOptionsNormalizers);
}
function getInternalDragOptions(config) {
if (config === void 0) {
config = {};
}
return resolveWith(config, InternalDragOptionsNormalizers);
}
function _buildMoveConfig(_ref) {
var domTarget = _ref.domTarget,
eventOptions = _ref.eventOptions,
window = _ref.window,
enabled = _ref.enabled,
rest = _objectWithoutPropertiesLoose(_ref, ["domTarget", "eventOptions", "window", "enabled"]);
var opts = getInternalGenericOptions({
domTarget: domTarget,
eventOptions: eventOptions,
window: window,
enabled: enabled
});
opts.move = getInternalCoordinatesOptions(rest);
return opts;
}
function _buildHoverConfig(_ref2) {
var domTarget = _ref2.domTarget,
eventOptions = _ref2.eventOptions,
window = _ref2.window,
enabled = _ref2.enabled,
rest = _objectWithoutPropertiesLoose(_ref2, ["domTarget", "eventOptions", "window", "enabled"]);
var opts = getInternalGenericOptions({
domTarget: domTarget,
eventOptions: eventOptions,
window: window,
enabled: enabled
});
opts.hover = _extends({
enabled: true
}, rest);
return opts;
}
function _buildDragConfig(_ref3) {
var domTarget = _ref3.domTarget,
eventOptions = _ref3.eventOptions,
window = _ref3.window,
enabled = _ref3.enabled,
rest = _objectWithoutPropertiesLoose(_ref3, ["domTarget", "eventOptions", "window", "enabled"]);
var opts = getInternalGenericOptions({
domTarget: domTarget,
eventOptions: eventOptions,
window: window,
enabled: enabled
});
opts.drag = getInternalDragOptions(rest);
return opts;
}
function _buildPinchConfig(_ref4) {
var domTarget = _ref4.domTarget,
eventOptions = _ref4.eventOptions,
window = _ref4.window,
enabled = _ref4.enabled,
rest = _objectWithoutPropertiesLoose(_ref4, ["domTarget", "eventOptions", "window", "enabled"]);
var opts = getInternalGenericOptions({
domTarget: domTarget,
eventOptions: eventOptions,
window: window,
enabled: enabled
});
opts.pinch = getInternalDistanceAngleOptions(rest);
return opts;
}
function _buildScrollConfig(_ref5) {
var domTarget = _ref5.domTarget,
eventOptions = _ref5.eventOptions,
window = _ref5.window,
enabled = _ref5.enabled,
rest = _objectWithoutPropertiesLoose(_ref5, ["domTarget", "eventOptions", "window", "enabled"]);
var opts = getInternalGenericOptions({
domTarget: domTarget,
eventOptions: eventOptions,
window: window,
enabled: enabled
});
opts.scroll = getInternalCoordinatesOptions(rest);
return opts;
}
function _buildWheelConfig(_ref6) {
var domTarget = _ref6.domTarget,
eventOptions = _ref6.eventOptions,
window = _ref6.window,
enabled = _ref6.enabled,
rest = _objectWithoutPropertiesLoose(_ref6, ["domTarget", "eventOptions", "window", "enabled"]);
var opts = getInternalGenericOptions({
domTarget: domTarget,
eventOptions: eventOptions,
window: window,
enabled: enabled
});
opts.wheel = getInternalCoordinatesOptions(rest);
return opts;
}
function buildComplexConfig(config, actions) {
if (config === void 0) {
config = {};
}
if (actions === void 0) {
actions = new Set();
}
var _config = config,
drag = _config.drag,
wheel = _config.wheel,
move = _config.move,
scroll = _config.scroll,
pinch = _config.pinch,
hover = _config.hover,
eventOptions = _config.eventOptions,
window = _config.window,
transform = _config.transform,
domTarget = _config.domTarget,
enabled = _config.enabled;
var mergedConfig = getInternalGenericOptions({
domTarget: domTarget,
eventOptions: eventOptions,
transform: transform,
window: window,
enabled: enabled
});
if (actions.has('onDrag')) mergedConfig.drag = getInternalDragOptions(drag);
if (actions.has('onWheel')) mergedConfig.wheel = getInternalCoordinatesOptions(wheel);
if (actions.has('onScroll')) mergedConfig.scroll = getInternalCoordinatesOptions(scroll);
if (actions.has('onMove')) mergedConfig.move = getInternalCoordinatesOptions(move);
if (actions.has('onPinch')) mergedConfig.pinch = getInternalDistanceAngleOptions(pinch);
if (actions.has('onHover')) mergedConfig.hover = _extends({
enabled: true
}, hover);
return mergedConfig;
}
function getInitial(mixed) {
return _extends({
_active: false,
_blocked: false,
_intentional: [false, false],
_movement: [0, 0],
_initial: [0, 0],
_bounds: [[-Infinity, Infinity], [-Infinity, Infinity]],
_threshold: [0, 0],
_lastEventType: undefined,
_dragStarted: false,
_dragPreventScroll: false,
_dragIsTap: true,
_dragDelayed: false,
event: undefined,
intentional: false,
values: [0, 0],
velocities: [0, 0],
delta: [0, 0],
movement: [0, 0],
offset: [0, 0],
lastOffset: [0, 0],
direction: [0, 0],
initial: [0, 0],
previous: [0, 0],
first: false,
last: false,
active: false,
timeStamp: 0,
startTime: 0,
elapsedTime: 0,
cancel: noop,
canceled: false,
memo: undefined,
args: undefined
}, mixed);
}
function getInitialState() {
var shared = {
hovering: false,
scrolling: false,
wheeling: false,
dragging: false,
moving: false,
pinching: false,
touches: 0,
buttons: 0,
down: false,
shiftKey: false,
altKey: false,
metaKey: false,
ctrlKey: false,
locked: false
};
var drag = getInitial({
_pointerId: undefined,
axis: undefined,
xy: [0, 0],
vxvy: [0, 0],
velocity: 0,
distance: 0,
tap: false,
swipe: [0, 0]
});
var pinch = getInitial({
// @ts-expect-error when used _pointerIds we can assert its type will be [number, number]
_pointerIds: [],
da: [0, 0],
vdva: [0, 0],
// @ts-expect-error origin can never be passed as undefined in userland
origin: undefined,
turns: 0
});
var wheel = getInitial({
axis: undefined,
xy: [0, 0],
vxvy: [0, 0],
velocity: 0,
distance: 0
});
var move = getInitial({
axis: undefined,
xy: [0, 0],
vxvy: [0, 0],
velocity: 0,
distance: 0
});
var scroll = getInitial({
axis: undefined,
xy: [0, 0],
vxvy: [0, 0],
velocity: 0,
distance: 0
});
return {
shared: shared,
drag: drag,
pinch: pinch,
wheel: wheel,
move: move,
scroll: scroll
};
}
var RecognizersMap = /*#__PURE__*/new Map();
var identity$1 = function identity(xy) {
return xy;
};
/**
* @private
* Recognizer abstract class.
*/
var Recognizer = /*#__PURE__*/function () {
/**
* Creates an instance of a gesture recognizer.
* @param stateKey drag, move, pinch, etc.
* @param controller the controller attached to the gesture
* @param [args] the args that should be passed to the gesture handler
*/
function Recognizer(controller, args) {
var _this = this;
if (args === void 0) {
args = [];
}
this.controller = controller;
this.args = args;
this.debounced = true; // Convenience method to set a timeout for a given gesture
this.setTimeout = function (callback, ms) {
var _window;
if (ms === void 0) {
ms = 140;
}
clearTimeout(_this.controller.timeouts[_this.stateKey]);
for (var _len = arguments.length, args = new Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {
args[_key - 2] = arguments[_key];
}
_this.controller.timeouts[_this.stateKey] = (_window = window).setTimeout.apply(_window, [callback, ms].concat(args));
}; // Convenience method to clear a timeout for a given gesture
this.clearTimeout = function () {
clearTimeout(_this.controller.timeouts[_this.stateKey]);
};
/**
* Fires the gesture handler
*/
this.fireGestureHandler = function (forceFlag) {
if (forceFlag === void 0) {
forceFlag = false;
}
/**
* If the gesture has been blocked (this can happen when the gesture has started in an unwanted direction),
* clean everything and don't do anything.
*/
if (_this.state._blocked) {
// we need debounced gestures to end by themselves
if (!_this.debounced) {
_this.state._active = false;
_this.clean();
}
return null;
} // If the gesture has no intentional dimension, don't fire the handler.
if (!forceFlag && !_this.state.intentional && !_this.config.triggerAllEvents) return null;
if (_this.state.intentional) {
var prev_active = _this.state.active;
var next_active = _this.state._active;
_this.state.active = next_active;
_this.state.first = next_active && !prev_active;
_this.state.last = prev_active && !next_active;
_this.controller.state.shared[_this.ingKey] = next_active; // Sets dragging, pinching, etc. to the gesture active state
}
var touches = _this.controller.pointerIds.size || _this.controller.touchIds.size;
var down = _this.controller.state.shared.buttons > 0 || touches > 0;
var state = _extends({}, _this.controller.state.shared, _this.state, _this.mapStateValues(_this.state), {
locked: !!document.pointerLockElement,
touches: touches,
down: down
}); // @ts-expect-error
var newMemo = _this.handler(state); // Sets memo to the returned value of the handler (unless it's not undefined)
_this.state.memo = newMemo !== void 0 ? newMemo : _this.state.memo;
return state;
};
this.controller = controller;
this.args = args;
} // Returns the gesture config
var _proto = Recognizer.prototype;
// Convenience method to update the shared state
_proto.updateSharedState = function updateSharedState(sharedState) {
Object.assign(this.controller.state.shared, sharedState);
} // Convenience method to update the gesture state
;
_proto.updateGestureState = function updateGestureState(gestureState) {
Object.assign(this.state, gestureState);
}
/**
* Returns state properties depending on the movement and state.
*
* Should be overriden for custom behavior, doesn't do anything in the implementation
* below.
*/
;
_proto.checkIntentionality = function checkIntentionality(_intentional, _movement) {
return {
_intentional: _intentional,
_blocked: false
};
}
/**
* Returns basic movement properties for the gesture based on the next values and current state.
*/
;
_proto.getMovement = function getMovement(values) {
var rubberband = this.config.rubberband;
var _this$state = this.state,
_bounds = _this$state._bounds,
_initial = _this$state._initial,
_active = _this$state._active,
wasIntentional = _this$state._intentional,
lastOffset = _this$state.lastOffset,
prevMovement = _this$state.movement,
_T = _this$state._threshold;
var M = this.getInternalMovement(values, this.state);
var i0 = wasIntentional[0] === false ? getIntentionalDisplacement(M[0], _T[0]) : wasIntentional[0];
var i1 = wasIntentional[1] === false ? getIntentionalDisplacement(M[1], _T[1]) : wasIntentional[1]; // Get gesture specific state properties based on intentionality and movement.
var intentionalityCheck = this.checkIntentionality([i0, i1], M);
if (intentionalityCheck._blocked) {
return _extends({}, intentionalityCheck, {
_movement: M,
delta: [0, 0]
});
}
var _intentional = intentionalityCheck._intentional;
var _movement = M;
/**
* The movement sent to the handler has 0 in its dimensions when intentionality is false.
* It is calculated from the actual movement minus the threshold.
*/
var movement = [_intentional[0] !== false ? M[0] - _intentional[0] : 0, _intentional[1] !== false ? M[1] - _intentional[1] : 0];
var offset = addV(movement, lastOffset);
/**
* Rubberband should be 0 when the gesture is no longer active, so that movement
* and offset can return within their bounds.
*/
var _rubberband = _active ? rubberband : [0, 0];
movement = computeRubberband(_bounds, addV(movement, _initial), _rubberband);
return _extends({}, intentionalityCheck, {
intentional: _intentional[0] !== false || _intentional[1] !== false,
_initial: _initial,
_movement: _movement,
movement: movement,
values: values,
offset: computeRubberband(_bounds, offset, _rubberband),
delta: subV(movement, prevMovement)
});
} // Cleans the gesture. Can be overriden by gestures.
;
_proto.clean = function clean() {
this.clearTimeout();
};
_createClass(Recognizer, [{
key: "config",
get: function get() {
return this.controller.config[this.stateKey];
} // Is the gesture enabled
}, {
key: "enabled",
get: function get() {
return this.controller.config.enabled && this.config.enabled;
} // Returns the controller state for a given gesture
}, {
key: "state",
get: function get() {
return this.controller.state[this.stateKey];
} // Returns the gesture handler
}, {
key: "handler",
get: function get() {
return this.controller.handlers[this.stateKey];
}
}, {
key: "transform",
get: function get() {
return this.config.transform || this.controller.config.transform || identity$1;
}
}]);
return Recognizer;
}(); //--------------------------------------------
function getIntentionalDisplacement(movement, threshold) {
if (Math.abs(movement) >= threshold) {
return sign(movement) * threshold;
} else {
return false;
}
}
function computeRubberband(bounds, _ref, _ref2) {
var Vx = _ref[0],
Vy = _ref[1];
var Rx = _ref2[0],
Ry = _ref2[1];
var _bounds$ = bounds[0],
X1 = _bounds$[0],
X2 = _bounds$[1],
_bounds$2 = bounds[1],
Y1 = _bounds$2[0],
Y2 = _bounds$2[1];
return [rubberbandIfOutOfBounds(Vx, X1, X2, Rx), rubberbandIfOutOfBounds(Vy, Y1, Y2, Ry)];
}
/**
* Returns a generic, common payload for all gestures from an event.
*/
function getGenericPayload(_ref3, event, isStartEvent) {
var state = _ref3.state;
var timeStamp = event.timeStamp,
_lastEventType = event.type;
var previous = state.values;
var elapsedTime = isStartEvent ? 0 : timeStamp - state.startTime;
return {
_lastEventType: _lastEventType,
event: event,
timeStamp: timeStamp,
elapsedTime: elapsedTime,
previous: previous
};
}
/**
* Returns the reinitialized start state for the gesture.
* Should be common to all gestures.
*/
function getStartGestureState(_ref4, values, event, initial) {
var state = _ref4.state,
config = _ref4.config,
stateKey = _ref4.stateKey,
args = _ref4.args,
transform = _ref4.transform;
var offset = state.offset;
var startTime = event.timeStamp;
var initialFn = config.initial,
bounds = config.bounds,
threshold = config.threshold; // the _threshold is the difference between a [0,0] offset converted to
// its new space coordinates
var _threshold = subV(transform(threshold), transform([0, 0])).map(Math.abs);
var _state = _extends({}, getInitialState()[stateKey], {
_active: true,
args: args,
values: values,
initial: initial != null ? initial : values,
_threshold: _threshold,
offset: offset,
lastOffset: offset,
startTime: startTime
});
return _extends({}, _state, {
_initial: valueFn(initialFn, _state),
_bounds: valueFn(bounds, _state)
});
}
/**
* The controller will keep track of the state for all gestures and also keep
* track of timeouts, and window listeners.
*/
var Controller = function Controller(classes) {
var _this = this;
this.classes = classes;
this.pointerIds = new Set(); // register Pointer Events pointerIds
this.touchIds = new Set(); // register Touch Events identifiers
this.supportsTouchEvents = supportsTouchEvents();
this.supportsGestureEvents = supportsGestureEvents();
this.bind = function () {
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
var bindings = {};
for (var _iterator = _createForOfIteratorHelperLoose(_this.classes), _step; !(_step = _iterator()).done;) {
var RecognizerClass = _step.value;
new RecognizerClass(_this, args).addBindings(bindings);
} // // we also add event bindings for native handlers
var _loop = function _loop(eventKey) {
addBindings(bindings, eventKey, function (event) {
return _this.nativeRefs[eventKey](_extends({}, _this.state.shared, {
event: event,
args: args
}));
});
};
for (var eventKey in _this.nativeRefs) {
_loop(eventKey);
}
if (_this.config.domTarget) {
// If config.domTarget is set we add event listeners to it and return the clean function.
return updateDomListeners(_this, bindings);
} else {
// If not, we return an object that contains gesture handlers mapped to react handler event keys.
return getPropsListener(_this, bindings);
}
};
this.effect = function () {
if (_this.config.domTarget) _this.bind();
return _this.clean;
};
/**
* Function ran on component unmount: cleans timeouts and removes dom listeners set by the bind function.
*/
this.clean = function () {
var domTarget = getDomTargetFromConfig(_this.config);
var eventOptions = _this.config.eventOptions;
if (domTarget) removeListeners(domTarget, takeAll(_this.domListeners), eventOptions);
Object.values(_this.timeouts).forEach(clearTimeout);
clearAllWindowListeners(_this);
};
this.classes = classes;
this.state = getInitialState();
this.timeouts = {};
this.domListeners = [];
this.windowListeners = {};
};
function addEventIds(controller, event) {
if ('pointerId' in event) {
controller.pointerIds.add(event.pointerId);
} else {
controller.touchIds = new Set(getTouchIds(event));
}
}
function removeEventIds(controller, event) {
if ('pointerId' in event) {
controller.pointerIds["delete"](event.pointerId);
} else {
getTouchIds(event).forEach(function (id) {
return controller.touchIds["delete"](id);
});
}
}
function clearAllWindowListeners(controller) {
var _controller$config = controller.config,
el = _controller$config.window,
eventOptions = _controller$config.eventOptions,
windowListeners = controller.windowListeners;
if (!el) return;
for (var stateKey in windowListeners) {
var handlers = windowListeners[stateKey];
removeListeners(el, handlers, eventOptions);
}
controller.windowListeners = {};
}
function clearWindowListeners(_ref, stateKey, options) {
var config = _ref.config,
windowListeners = _ref.windowListeners;
if (options === void 0) {
options = config.eventOptions;
}
if (!config.window) return;
removeListeners(config.window, windowListeners[stateKey], options);
delete windowListeners[stateKey];
}
function updateWindowListeners(_ref2, stateKey, listeners, options) {
var config = _ref2.config,
windowListeners = _ref2.windowListeners;
if (listeners === void 0) {
listeners = [];
}
if (options === void 0) {
options = config.eventOptions;
}
if (!config.window) return;
removeListeners(config.window, windowListeners[stateKey], options);
addListeners(config.window, windowListeners[stateKey] = listeners, options);
}
function updateDomListeners(_ref3, bindings) {
var config = _ref3.config,
domListeners = _ref3.domListeners;
var domTarget = getDomTargetFromConfig(config);
if (!domTarget) throw new Error('domTarget must be defined');
var eventOptions = config.eventOptions;
removeListeners(domTarget, takeAll(domListeners), eventOptions);
for (var _i = 0, _Object$entries = Object.entries(bindings); _i < _Object$entries.length; _i++) {
var _Object$entries$_i = _Object$entries[_i],
key = _Object$entries$_i[0],
fns = _Object$entries$_i[1];
var name = key.slice(2).toLowerCase();
domListeners.push([name, chainFns.apply(void 0, fns)]);
}
addListeners(domTarget, domListeners, eventOptions);
}
function getPropsListener(_ref4, bindings) {
var config = _ref4.config;
var props = {};
var captureString = config.eventOptions.capture ? 'Capture' : '';
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);
}
return props;
}
function takeAll(array) {
if (array === void 0) {
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
* that key.
*/
function addBindings(bindings, name, fn) {
if (!bindings[name]) bindings[name] = [];
bindings[name].push(fn);
}
function addListeners(el, listeners, options) {
if (listeners === void 0) {
listeners = [];
}
if (options === void 0) {
options = {};
}
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) {
listeners = [];
}
if (options === void 0) {
options = {};
}
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 handlers
* @param classes
* @param config
* @param nativeHandlers - native handlers such as onClick, onMouseDown, etc.
*/
function useRecognizers(handlers, config, nativeHandlers) {
if (nativeHandlers === void 0) {
nativeHandlers = {};
}
var classes = resolveClasses(handlers);
var controller = React__default.useMemo(function () {
return new Controller(classes);
}, []);
controller.config = config;
controller.handlers = handlers;
controller.nativeRefs = nativeHandlers;
React__default.useEffect(controller.effect, []); // @ts-ignore
if (controller.config.domTarget) return deprecationNoticeForDomTarget; // @ts-ignore
return controller.bind;
}
function deprecationNoticeForDomTarget() {
{
// eslint-disable-next-line no-console
console.warn("Deprecation notice: When the `domTarget` option is specified, you don't need to write `useEffect(bind, [bind])` anymore: event binding is now made handled internally to this lib.\n\nNext version won't return anything when `domTarget` is provided, therefore your code will break if you try to call `useEffect`.");
}
}
function resolveClasses(internalHandlers) {
var classes = new Set();
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'));
return classes;
}
/**
* @private
* 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) {
return {
_intentional: _intentional,
axis: this.state.axis
};
}
var _movement$map = _movement.map(Math.abs),
absX = _movement$map[0],
absY = _movement$map[1];
var axis = this.state.axis || (absX > absY ? 'x' : absX < absY ? 'y' : undefined);
if (!this.config.axis && !this.config.lockDirection) return {
_intentional: _intentional,
_blocked: false,
axis: axis
};
if (!axis) return {
_intentional: [false, false],
_blocked: false,
axis: axis
};
if (!!this.config.axis && axis !== this.config.axis) return {
_intentional: _intentional,
_blocked: true,
axis: axis
};
_intentional[axis === 'x' ? 1 : 0] = false;
return {
_intentional: _intentional,
_blocked: false,
axis: axis
};
};
_proto.getKinematics = function getKinematics(values, event) {
var state = this.getMovement(values);
if (!state._blocked) {
var dt = event.timeStamp - this.state.timeStamp;
Object.assign(state, calculateAllKinematics(state.movement, state.delta, dt));
}
return state;
};
_proto.mapStateValues = function mapStateValues(state) {
return {
xy: state.values,
vxvy: state.velocities
};
};
return CoordinatesRecognizer;
}(Recognizer);
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() {
var _this;
_this = _CoordinatesRecognize.apply(this, arguments) || this;
_this.ingKey = 'dragging';
_this.stateKey = 'drag'; // TODO add back when