import type { JSX } from "solid-js"; type PointerHandler = NonNullable["onPointerDown"]>; type LongPressGestureOptions = { onLongPress: () => void; delay?: number; movementThreshold?: number; }; type LongPressGestureHandlers = { onPointerDown: PointerHandler; onPointerMove: PointerHandler; onPointerUp: PointerHandler; onPointerCancel: PointerHandler; onPointerLeave: PointerHandler; }; export const createLongPressGesture = (options: LongPressGestureOptions): LongPressGestureHandlers => { let timeoutId: number | undefined; let originX = 0; let originY = 0; const delay = options.delay ?? 420; const movementThreshold = options.movementThreshold ?? 10; const clearPendingLongPress = (): void => { if (typeof timeoutId === "number") { window.clearTimeout(timeoutId); timeoutId = undefined; } }; const onPointerDown: PointerHandler = (event): void => { // Mobile long-press should only respond to the primary touch/pen pointer. if (event.pointerType === "mouse" || !event.isPrimary) { return; } originX = event.clientX; originY = event.clientY; clearPendingLongPress(); timeoutId = window.setTimeout(() => { timeoutId = undefined; options.onLongPress(); }, delay); }; const onPointerMove: PointerHandler = (event): void => { if (typeof timeoutId !== "number") { return; } const deltaX = Math.abs(event.clientX - originX); const deltaY = Math.abs(event.clientY - originY); if (deltaX > movementThreshold || deltaY > movementThreshold) { clearPendingLongPress(); } }; const onPointerUp: PointerHandler = (): void => { clearPendingLongPress(); }; return { onPointerDown, onPointerMove, onPointerUp, onPointerCancel: onPointerUp, onPointerLeave: onPointerUp, }; };