UNPKG

@trail-ui/react

Version:
105 lines (87 loc) 2.88 kB
import { useInterval } from '@trail-ui/hooks'; import { useCallback, useEffect, useRef, useState } from 'react'; /** * When click and hold on a button - the speed of auto changing the value. */ const CONTINUOUS_CHANGE_INTERVAL = 50; /** * When click and hold on a button - the delay before auto changing the value. */ const CONTINUOUS_CHANGE_DELAY = 300; type Action = 'increment' | 'decrement'; /** * React hook used in the number input to spin its * value on long press of the spin buttons * * @param increment the function to increment * @param decrement the function to decrement */ export function useSpinner(increment: () => void, decrement: () => void) { /** * To keep incrementing/decrementing on press, we call that `spinning` */ const [isSpinning, setIsSpinning] = useState(false); // This state keeps track of the action ("increment" or "decrement") const [action, setAction] = useState<Action | null>(null); // To increment the value the first time you mousedown, we call that `runOnce` const [runOnce, setRunOnce] = useState(true); // Store the timeout instance id in a ref, so we can clear the timeout later const timeoutRef = useRef<any>(null); // Clears the timeout from memory const removeTimeout = () => clearTimeout(timeoutRef.current); /** * useInterval hook provides a performant way to * update the state value at specific interval */ useInterval( () => { if (action === 'increment') { increment(); } if (action === 'decrement') { decrement(); } }, isSpinning ? CONTINUOUS_CHANGE_INTERVAL : null, ); // Function to activate the spinning and increment the value const up = useCallback(() => { // increment the first time if (runOnce) { increment(); } // after a delay, keep incrementing at interval ("spinning up") timeoutRef.current = setTimeout(() => { setRunOnce(false); setIsSpinning(true); setAction('increment'); }, CONTINUOUS_CHANGE_DELAY); }, [increment, runOnce]); // Function to activate the spinning and increment the value const down = useCallback(() => { // decrement the first time if (runOnce) { decrement(); } // after a delay, keep decrementing at interval ("spinning down") timeoutRef.current = setTimeout(() => { setRunOnce(false); setIsSpinning(true); setAction('decrement'); }, CONTINUOUS_CHANGE_DELAY); }, [decrement, runOnce]); // Function to stop spinning (useful for mouseup, keyup handlers) const stop = useCallback(() => { setRunOnce(true); setIsSpinning(false); removeTimeout(); }, []); /** * If the component unmounts while spinning, * let's clear the timeout as well */ useEffect(() => { return () => removeTimeout(); }, []); return { up, down, stop, isSpinning }; }