preact-gestures
Version:
Preact compatible HOCs for gesture detection
298 lines (247 loc) • 8.57 kB
JavaScript
import { h } from 'preact';
function _slicedToArray(arr, i) {
return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest();
}
function _toArray(arr) {
return _arrayWithHoles(arr) || _iterableToArray(arr) || _nonIterableRest();
}
function _arrayWithHoles(arr) {
if (Array.isArray(arr)) return arr;
}
function _iterableToArray(iter) {
if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter);
}
function _iterableToArrayLimit(arr, i) {
var _arr = [];
var _n = true;
var _d = false;
var _e = undefined;
try {
for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {
_arr.push(_s.value);
if (i && _arr.length === i) break;
}
} catch (err) {
_d = true;
_e = err;
} finally {
try {
if (!_n && _i["return"] != null) _i["return"]();
} finally {
if (_d) throw _e;
}
}
return _arr;
}
function _nonIterableRest() {
throw new TypeError("Invalid attempt to destructure non-iterable instance");
}
var gestureTriggers = (function () {
let _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
_ref$touchMenuHoldTim = _ref.touchMenuHoldTime,
touchMenuHoldTime = _ref$touchMenuHoldTim === void 0 ? 600 : _ref$touchMenuHoldTim,
_ref$touchDragDelay = _ref.touchDragDelay,
touchDragDelay = _ref$touchDragDelay === void 0 ? 500 : _ref$touchDragDelay;
return {
onClick: {
onClick: (fireEvent, event) => fireEvent(event)
},
onDblclick: {
onDblclick: (fireEvent, event) => fireEvent(event)
},
onMenu: {
onMousedown(fireEvent, event) {
if (event.button === 2) fireEvent(event);
},
onTouchstart(fireEvent, event) {
const target = event.target;
let canFire = false;
const holdTimer = setTimeout(() => canFire = true, touchMenuHoldTime);
const handleCancel = () => {
target.removeEventListener('touchend', handleEnd);
target.removeEventListener('touchcancel', handleCancel);
};
const handleEnd = () => {
if (canFire) {
fireEvent(event);
} else {
clearTimeout(holdTimer);
}
handleCancel();
};
target.addEventListener('touchend', handleEnd);
target.addEventListener('touchcancel', handleCancel);
}
},
onDrag: {
onMousedown(fireEvent, startEvent) {
if (startEvent.button !== 0) return;
const target = startEvent.target;
let isFirstMove = true;
const handleMove = moveEvent => {
if (isFirstMove) {
fireEvent(startEvent, 'start');
isFirstMove = false;
}
fireEvent(moveEvent, 'move');
};
const handleEnd = endEvent => {
fireEvent(endEvent, 'end');
target.removeEventListener('mousemove', handleMove);
target.removeEventListener('mouseup', handleEnd);
};
target.addEventListener('mousemove', handleMove);
target.addEventListener('mouseup', handleEnd);
},
onTouchstart(fireEvent, startEvent) {
const target = startEvent.target;
const startingTouch = startEvent.changedTouches[0];
let isDragging = false;
let isFirstMove = true;
const handleMove = moveEvent => {
if (isFirstMove) {
fireEvent(startEvent, 'start');
isFirstMove = false;
}
for (let i = 0; i < moveEvent.changedTouches.length; i++) {
const touch = moveEvent.changedTouches[i];
if (touch.identifier === startingTouch.identifier) {
fireEvent(moveEvent, 'move');
}
}
};
const handleStart = () => {
isDragging = true;
target.addEventListener('touchmove', handleMove);
};
const holdTimer = setTimeout(handleStart, touchDragDelay);
const handleEnd = endEvent => {
debugger;
if (isDragging) {
fireEvent(endEvent, 'end');
target.removeEventListener('touchmove', handleMove);
} else {
clearTimeout(holdTimer);
}
isDragging = false;
target.removeEventListener('touchend', handleEnd);
target.removeEventListener('touchcancel', handleEnd);
};
target.addEventListener('touchend', handleEnd);
target.addEventListener('touchcancel', handleEnd);
}
}
};
});
function series(funcs) {
return function () {
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
return funcs.forEach(f => f(...args));
};
}
function splitCamelCase(string) {
return string.split(/(?=[A-Z])/g);
}
function safeAppend(target, value) {
if (Array.isArray(target)) return target.concat([value]);else return [target, value];
}
function mergeWith(resolver, objA, objB) {
let result = Object.assign({}, objA);
Object.entries(objB).forEach((_ref) => {
let _ref2 = _slicedToArray(_ref, 2),
key = _ref2[0],
value = _ref2[1];
if (key in result) {
result[key] = safeAppend(result[key], value);
} else {
result[key] = [value];
}
});
Object.entries(result).forEach((_ref3) => {
let _ref4 = _slicedToArray(_ref3, 2),
key = _ref4[0],
values = _ref4[1];
if (values.length === 1) result[key] = values[0];else result[key] = resolver(values, key);
});
return result;
} // export function filterObj (obj, pred) {
// let result = {}
// Object.entries(obj)
// .forEach(([key, val]) => pred(key, val) ? result[key] = val : undefined)
// return result
// }
function mapObj(obj, mapper) {
let result = {};
Object.entries(obj).forEach((_ref5) => {
let _ref6 = _slicedToArray(_ref5, 2),
key = _ref6[0],
val = _ref6[1];
return result[key] = mapper(key, val);
});
return result;
}
function setPath(obj, path, value) {
const pathParts = path.slice();
const propToSet = pathParts.pop();
let current = obj;
pathParts.forEach(p => {
if (typeof current[p] !== 'object') current[p] = {};
current = current[p];
});
current[propToSet] = value;
}
const splitHandlerName = name => {
let _splitCamelCase = splitCamelCase(name),
_splitCamelCase2 = _toArray(_splitCamelCase),
on = _splitCamelCase2[0],
gestureName = _splitCamelCase2[1],
eventName = _splitCamelCase2.slice(2);
eventName = eventName.length === 0 ? ['default'] : eventName;
return [on + gestureName, eventName.join('')];
};
function withGestures(Wrapped, config) {
const handledEvents = [];
const configuredTriggers = gestureTriggers(config);
const bindTriggerToCallbacks = (triggerFunc, callbacks) => {
const fireEvent = function fireEvent(event) {
let name = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'default';
if (!handledEvents.includes(event)) {
handledEvents.push(event);
if (name in callbacks) callbacks[name](event);
}
};
return triggerFunc.bind(null, fireEvent);
};
return function (props) {
let passtroughProps = {};
let gestureTriggers = {};
let gestureCallbacks = {};
Object.entries(props).forEach((_ref) => {
let _ref2 = _slicedToArray(_ref, 2),
propName = _ref2[0],
prop = _ref2[1];
let _splitHandlerName = splitHandlerName(propName),
_splitHandlerName2 = _slicedToArray(_splitHandlerName, 2),
gestureName = _splitHandlerName2[0],
gestureEventName = _splitHandlerName2[1];
gestureEventName = gestureEventName.toLowerCase();
if (gestureName in configuredTriggers) {
let gestureCallback = prop;
setPath(gestureCallbacks, [gestureName, gestureEventName], gestureCallback);
} else {
passtroughProps[propName] = prop;
}
});
Object.entries(gestureCallbacks).forEach((_ref3) => {
let _ref4 = _slicedToArray(_ref3, 2),
gestureName = _ref4[0],
callbacks = _ref4[1];
let currentTriggers = mapObj(configuredTriggers[gestureName], (name, triggerFunc) => bindTriggerToCallbacks(triggerFunc, callbacks));
gestureTriggers = mergeWith(series, gestureTriggers, currentTriggers);
});
return h("span", gestureTriggers, h(Wrapped, passtroughProps));
};
}
export { withGestures };