UNPKG

clicknhold

Version:

React hook for handling click and hold events

112 lines (90 loc) 2.64 kB
import { useState, useRef, useCallback, useEffect } from 'react'; interface ClicknHoldOptions { duration?: number; onComplete?: () => void; onStart?: () => void; onCancel?: () => void; onProgress?: (progress: number) => void; disabled?: boolean; } type EventHandlers = { onMouseDown: (event: React.MouseEvent) => void; onMouseUp: () => void; onMouseLeave: () => void; onTouchStart: (event: React.TouchEvent) => void; onTouchEnd: () => void; onKeyDown: (event: React.KeyboardEvent) => void; onKeyUp: (event: React.KeyboardEvent) => void; } export function useClicknHold({ duration = 2000, onComplete, onStart, onCancel, onProgress, disabled = false }: ClicknHoldOptions = {}) { const [isHolding, setIsHolding] = useState(false); const timerRef = useRef<number>(); const startTimeRef = useRef<number>(); const cleanup = useCallback(() => { if (timerRef.current) { window.clearInterval(timerRef.current); timerRef.current = undefined; } startTimeRef.current = undefined; setIsHolding(false); }, []); const startHolding = useCallback((event: React.MouseEvent | React.TouchEvent | React.KeyboardEvent) => { if ('preventDefault' in event) { event.preventDefault(); } if (disabled) return; startTimeRef.current = Date.now(); setIsHolding(true); onStart?.(); timerRef.current = window.setInterval(() => { const elapsedTime = Date.now() - startTimeRef.current!; const progress = Math.min(elapsedTime / duration, 1); onProgress?.(progress); if (elapsedTime >= duration) { cleanup(); onComplete?.(); } }, 16); }, [duration, onComplete, onStart, onProgress, cleanup, disabled]); const stopHolding = useCallback(() => { if (isHolding) { cleanup(); onCancel?.(); } }, [isHolding, cleanup, onCancel]); const handleKeyDown = useCallback((event: React.KeyboardEvent) => { if (event.key === 'Enter' || event.key === ' ') { startHolding(event); } }, [startHolding]); const handleKeyUp = useCallback((event: React.KeyboardEvent) => { if (event.key === 'Enter' || event.key === ' ') { stopHolding(); } }, [stopHolding]); useEffect(() => { return cleanup; }, [cleanup]); const handlers: EventHandlers = { onMouseDown: startHolding, onMouseUp: stopHolding, onMouseLeave: stopHolding, onTouchStart: startHolding, onTouchEnd: stopHolding, onKeyDown: handleKeyDown, onKeyUp: handleKeyUp, }; return { isHolding, handlers, startHolding, stopHolding }; }