beautiful-react-hooks
Version:
A collection of beautiful (and hopefully useful) React hooks to speed-up your components and hooks development
132 lines (131 loc) • 5.08 kB
JavaScript
import { useEffect, useRef, useState } from 'react';
import createHandlerSetter from './factory/createHandlerSetter';
import useMouseEvents from './useMouseEvents';
import useTouchEvents from './useTouchEvents';
import { getDirection, getPointerCoordinates } from './shared/swipeUtils';
const defaultOptions = {
threshold: 15,
preventDefault: true,
};
/* eslint-disable @typescript-eslint/default-param-last */
/**
* Very similar to useSwipe but doesn't cause re-rendering during swipe
*/
const useSilentSwipeState = (targetRef = null, options = defaultOptions, onSwipeStart, onSwipeMove, onSwipeEnd) => {
const startingPointRef = useRef([-1, -1]);
const directionRef = useRef(null);
const isDraggingRef = useRef(false);
const alphaRef = useRef([]);
const opts = Object.assign(Object.assign({}, defaultOptions), (options || {}));
const { onMouseDown, onMouseMove, onMouseLeave, onMouseUp } = useMouseEvents(targetRef);
const { onTouchStart, onTouchMove, onTouchEnd, onTouchCancel } = useTouchEvents(targetRef);
const [state, setState] = useState();
const startSwipe = (event) => {
const [clientX, clientY] = getPointerCoordinates(event);
startingPointRef.current = [clientX, clientY];
directionRef.current = null;
if (onSwipeStart) {
onSwipeStart({ clientX, clientY });
}
if (opts.preventDefault) {
event.preventDefault();
event.stopPropagation();
}
};
const continueSwipe = (event) => {
const [clientX, clientY] = getPointerCoordinates(event);
if (opts.preventDefault) {
event.preventDefault();
event.stopPropagation();
}
if (startingPointRef.current[0] !== -1 && startingPointRef.current[1] !== -1) {
const alpha = [startingPointRef.current[0] - clientX, startingPointRef.current[1] - clientY];
if (Math.abs(alpha[0]) > opts.threshold || Math.abs(alpha[1]) > opts.threshold) {
isDraggingRef.current = true;
directionRef.current = getDirection([clientX, clientY], startingPointRef.current, alpha);
alphaRef.current = alpha;
if (onSwipeMove) {
onSwipeMove({
clientX,
clientY,
direction: directionRef.current,
alphaX: alphaRef.current[0],
alphaY: alphaRef.current[1],
});
}
}
}
};
const endSwipe = (event) => {
if (isDraggingRef.current && directionRef.current) {
if (opts.preventDefault) {
event.preventDefault();
event.stopPropagation();
}
setState({
direction: directionRef.current,
alphaX: alphaRef.current[0],
alphaY: alphaRef.current[1],
});
if (onSwipeEnd) {
onSwipeEnd({
direction: directionRef.current,
alphaX: alphaRef.current[0],
alphaY: alphaRef.current[1],
});
}
}
startingPointRef.current = [-1, -1];
isDraggingRef.current = false;
directionRef.current = null;
};
onMouseDown(startSwipe);
onTouchStart(startSwipe);
onMouseMove(continueSwipe);
onTouchMove(continueSwipe);
onMouseUp(endSwipe);
onTouchEnd(endSwipe);
onMouseLeave(endSwipe);
onTouchCancel(endSwipe);
return state;
};
/**
* useSwipeEvents
* @param ref
* @param options
*/
const useSwipeEvents = (ref = null, options = defaultOptions) => {
const opts = Object.assign(Object.assign({}, defaultOptions), (options || {}));
const [onSwipeLeft, setOnSwipeLeft] = createHandlerSetter();
const [onSwipeRight, setOnSwipeRight] = createHandlerSetter();
const [onSwipeUp, setOnSwipeUp] = createHandlerSetter();
const [onSwipeDown, setOnSwipeDown] = createHandlerSetter();
const [onSwipeStart, setOnSwipeStart] = createHandlerSetter();
const [onSwipeMove, setOnSwipeMove] = createHandlerSetter();
const [onSwipeEnd, setOnSwipeEnd] = createHandlerSetter();
const state = useSilentSwipeState(ref, opts, onSwipeStart.current, onSwipeMove.current, onSwipeEnd.current);
const fnMap = {
right: onSwipeRight,
left: onSwipeLeft,
up: onSwipeUp,
down: onSwipeDown,
};
useEffect(() => {
if (state && state.direction) {
const cb = fnMap[state.direction].current;
if (cb && typeof cb === 'function') {
cb(state);
}
}
}, [state]);
return Object.freeze({
onSwipeLeft: setOnSwipeLeft,
onSwipeRight: setOnSwipeRight,
onSwipeUp: setOnSwipeUp,
onSwipeDown: setOnSwipeDown,
onSwipeMove: setOnSwipeMove,
onSwipeStart: setOnSwipeStart,
onSwipeEnd: setOnSwipeEnd,
});
};
export default useSwipeEvents;