import { useCallback, useRef, useState } from "react" const DEFAULT_DELAY_MS = 500 const ACK_MS = 280 /** Applied briefly when a long-press action fires (touch feedback). */ export const LONG_PRESS_ACK_CLASS = "long-press-ack" export function useLongPress( onLongPress: () => void, options?: { delay?: number; disabled?: boolean; ack?: boolean } ) { const timerRef = useRef | null>(null) const ackTimerRef = useRef | null>(null) const firedRef = useRef(false) const delay = options?.delay ?? DEFAULT_DELAY_MS const disabled = options?.disabled ?? false const withAck = options?.ack ?? true const [ack, setAck] = useState(false) const clearAck = useCallback(() => { if (ackTimerRef.current) { clearTimeout(ackTimerRef.current) ackTimerRef.current = null } setAck(false) }, []) const clear = useCallback(() => { if (timerRef.current) { clearTimeout(timerRef.current) timerRef.current = null } }, []) const pulseAck = useCallback(() => { if (!withAck) return clearAck() setAck(true) ackTimerRef.current = setTimeout(() => { setAck(false) ackTimerRef.current = null }, ACK_MS) }, [clearAck, withAck]) const onPointerDown = useCallback( (e: React.PointerEvent) => { if (disabled) return if (e.pointerType === "mouse" && e.button !== 0) return firedRef.current = false clear() timerRef.current = setTimeout(() => { firedRef.current = true pulseAck() onLongPress() }, delay) }, [clear, delay, disabled, onLongPress, pulseAck] ) const onClickCapture = useCallback( (e: React.MouseEvent) => { if (!firedRef.current) return e.preventDefault() e.stopPropagation() firedRef.current = false }, [] ) return { onPointerDown, onPointerUp: clear, onPointerLeave: clear, onPointerCancel: clear, onClickCapture, ackActive: ack, ackClassName: ack ? LONG_PRESS_ACK_CLASS : undefined, } }