import { useCallback, useRef } from 'react';
/**
* Long-Press-Erkennung über Pointer-Events.
*
* Liefert Handler die direkt auf `
` etc. gespreaded werden können.
* Ein "Long-Press" feuert nach `thresholdMs` (Default 500 ms) wenn der Pointer
* sich nicht weiter als `moveTolerance` Pixel bewegt hat.
*/
interface LongPressOptions {
thresholdMs?: number;
moveTolerance?: number;
/** Wenn ``true``, werden auch Maus-Events behandelt (für Desktop-Smoke-Tests). */
includeMouse?: boolean;
}
interface LongPressHandlers {
onPointerDown: (e: React.PointerEvent) => void;
onPointerMove: (e: React.PointerEvent) => void;
onPointerUp: (e: React.PointerEvent) => void;
onPointerCancel: (e: React.PointerEvent) => void;
onPointerLeave: (e: React.PointerEvent) => void;
}
export function usePointerLongPress(
callback: (e: React.PointerEvent) => void,
options: LongPressOptions = {},
): LongPressHandlers {
const { thresholdMs = 500, moveTolerance = 8, includeMouse = false } = options;
const timerRef = useRef(null);
const startPosRef = useRef<{ x: number; y: number } | null>(null);
const firedRef = useRef(false);
const _clear = useCallback(() => {
if (timerRef.current !== null) {
window.clearTimeout(timerRef.current);
timerRef.current = null;
}
startPosRef.current = null;
firedRef.current = false;
}, []);
const onPointerDown = useCallback(
(e: React.PointerEvent) => {
if (!includeMouse && e.pointerType === 'mouse') return;
_clear();
startPosRef.current = { x: e.clientX, y: e.clientY };
firedRef.current = false;
timerRef.current = window.setTimeout(() => {
firedRef.current = true;
callback(e);
}, thresholdMs);
},
[callback, includeMouse, thresholdMs, _clear],
);
const onPointerMove = useCallback(
(e: React.PointerEvent) => {
if (timerRef.current === null || !startPosRef.current) return;
const dx = e.clientX - startPosRef.current.x;
const dy = e.clientY - startPosRef.current.y;
if (Math.abs(dx) > moveTolerance || Math.abs(dy) > moveTolerance) _clear();
},
[moveTolerance, _clear],
);
return {
onPointerDown,
onPointerMove,
onPointerUp: _clear,
onPointerCancel: _clear,
onPointerLeave: _clear,
};
}