UNPKG

@kiwicom/orbit-components

Version:

Orbit-components is a React component library which provides developers with the easiest possible way of building Kiwi.com’s products.

113 lines (94 loc) 3.81 kB
import { useState, useEffect, useCallback } from "react"; import { throttle } from "./helpers"; const timing = 1 / 60 * 1000; // eslint-disable-next-line no-bitwise const decay = v => -0.1 * (1 / timing ^ 4) + v; const TRIGGER_OFFSET = 50; const useScroll = ref => { const [clickStartX, setClickStartX] = useState(); const [scrollStartX, setScrollStartX] = useState(); const [isDragging, setIsDragging] = useState(false); const [direction, setDirection] = useState(0); const [momentum, setMomentum] = useState(0); const [lastScrollX, setLastScrollX] = useState(0); const [speed, setSpeed] = useState(0); const [reachedStart, setReachedStart] = useState(false); const [reachedEnd, setReachedEnd] = useState(false); const scrollWrapperCurrent = ref.current; const handleLastScrollX = useCallback(x => setLastScrollX(x), []); const handleMomentum = throttle(nextMomentum => { setMomentum(nextMomentum); if (scrollWrapperCurrent) { scrollWrapperCurrent.scrollLeft += nextMomentum * timing * direction; } }, timing); useEffect(() => { if (direction !== 0) { if (momentum > 0.1 && !isDragging) { handleMomentum(decay(momentum)); } else if (isDragging) { setMomentum(speed); } else { setDirection(0); } } }, [momentum, isDragging, speed, direction, handleMomentum]); useEffect(() => { if (ref.current) { const handleDragStart = e => { var _ref$current; setClickStartX(e.screenX); setScrollStartX((_ref$current = ref.current) == null ? void 0 : _ref$current.scrollLeft); setDirection(0); }; const handleDragMove = e => { e.preventDefault(); e.stopPropagation(); if (clickStartX !== undefined && scrollStartX !== undefined && ref.current) { const touchDelta = clickStartX - e.screenX; // eslint-disable-next-line no-param-reassign ref.current.scrollLeft = scrollStartX + touchDelta; if (Math.abs(touchDelta) > 1) { setIsDragging(true); setDirection(touchDelta / Math.abs(touchDelta)); setSpeed(Math.abs((lastScrollX - e.screenX) / timing)); handleLastScrollX(e.screenX); } } }; const handleDragEnd = () => { if (isDragging && clickStartX !== undefined) { var _ref$current2, _ref$current3; setClickStartX(undefined); setScrollStartX(undefined); setIsDragging(false); if (scrollStartX !== undefined && (_ref$current2 = ref.current) != null && _ref$current2.scrollLeft) { if (ref.current.scrollLeft <= scrollStartX + TRIGGER_OFFSET) { setReachedEnd(true); } else { setReachedEnd(false); } } if (((_ref$current3 = ref.current) == null ? void 0 : _ref$current3.scrollLeft) === 0) setReachedStart(true);else setReachedStart(false); } }; // $FlowFixMe: on mobile browser is null, on desktop is undefined if (ref.current && ref.current.ontouchstart === undefined) { // eslint-disable-next-line no-param-reassign ref.current.onmousedown = handleDragStart; // eslint-disable-next-line no-param-reassign ref.current.onmousemove = handleDragMove; // eslint-disable-next-line no-param-reassign ref.current.onmouseup = handleDragEnd; // eslint-disable-next-line no-param-reassign ref.current.onmouseleave = handleDragEnd; } } }, [scrollWrapperCurrent, clickStartX, isDragging, scrollStartX, handleLastScrollX, lastScrollX]); return { clickStartX, scrollStartX, isDragging, direction, momentum, lastScrollX, speed, reachedEnd, reachedStart }; }; export default useScroll;