diff --git a/packages/hooks/src/useLongPress/index.ts b/packages/hooks/src/useLongPress/index.ts index caa3aee13a..87cd6e1d0e 100644 --- a/packages/hooks/src/useLongPress/index.ts +++ b/packages/hooks/src/useLongPress/index.ts @@ -13,11 +13,6 @@ export interface Options { onLongPressEnd?: (event: EventType) => void; } -const touchSupported = - isBrowser && - // @ts-ignore - ('ontouchstart' in window || (window.DocumentTouch && document instanceof DocumentTouch)); - function useLongPress( onLongPress: (event: EventType) => void, target: BasicTarget, @@ -30,6 +25,8 @@ function useLongPress( const timerRef = useRef>(); const isTriggeredRef = useRef(false); const pervPositionRef = useRef({ x: 0, y: 0 }); + const mousePressed = useRef(false); + const touchPressed = useRef(false); const hasMoveThreshold = !!( (moveThreshold?.x && moveThreshold.x > 0) || (moveThreshold?.y && moveThreshold.y > 0) @@ -60,7 +57,6 @@ function useLongPress( clientY: event.touches[0].clientY, }; } - if (event instanceof MouseEvent) { return { clientX: event.clientX, @@ -73,64 +69,119 @@ function useLongPress( return { clientX: 0, clientY: 0 }; } - const onStart = (event: EventType) => { + const createTimer = (event: EventType) => { + timerRef.current = setTimeout(() => { + onLongPressRef.current(event); + isTriggeredRef.current = true; + }, delay); + }; + + const onTouchStart = (event: TouchEvent) => { + if (touchPressed.current) return; + touchPressed.current = true; + if (hasMoveThreshold) { const { clientX, clientY } = getClientPosition(event); pervPositionRef.current.x = clientX; pervPositionRef.current.y = clientY; } - timerRef.current = setTimeout(() => { - onLongPressRef.current(event); - isTriggeredRef.current = true; - }, delay); + createTimer(event); }; - const onMove = (event: TouchEvent) => { + const onMouseDown = (event: MouseEvent) => { + if ((event as any)?.sourceCapabilities?.firesTouchEvents) return; + + mousePressed.current = true; + + if (hasMoveThreshold) { + pervPositionRef.current.x = event.clientX; + pervPositionRef.current.y = event.clientY; + } + createTimer(event); + }; + + const onMove = (event: EventType) => { if (timerRef.current && overThreshold(event)) { clearTimeout(timerRef.current); timerRef.current = undefined; } }; - const onEnd = (event: EventType, shouldTriggerClick: boolean = false) => { + const onTouchEnd = (event: TouchEvent) => { + if (!touchPressed.current) return; + touchPressed.current = false; + if (timerRef.current) { clearTimeout(timerRef.current); + timerRef.current = undefined; } + if (isTriggeredRef.current) { onLongPressEndRef.current?.(event); + } else if (onClickRef.current) { + onClickRef.current(event); + } + isTriggeredRef.current = false; + }; + + const onMouseUp = (event: MouseEvent) => { + if ((event as any)?.sourceCapabilities?.firesTouchEvents) return; + if (!mousePressed.current) return; + mousePressed.current = false; + + if (timerRef.current) { + clearTimeout(timerRef.current); + timerRef.current = undefined; } - if (shouldTriggerClick && !isTriggeredRef.current && onClickRef.current) { + + if (isTriggeredRef.current) { + onLongPressEndRef.current?.(event); + } else if (onClickRef.current) { onClickRef.current(event); } isTriggeredRef.current = false; }; - const onEndWithClick = (event: EventType) => onEnd(event, true); - - if (!touchSupported) { - targetElement.addEventListener('mousedown', onStart); - targetElement.addEventListener('mouseup', onEndWithClick); - targetElement.addEventListener('mouseleave', onEnd); - if (hasMoveThreshold) targetElement.addEventListener('mousemove', onMove); - } else { - targetElement.addEventListener('touchstart', onStart); - targetElement.addEventListener('touchend', onEndWithClick); - if (hasMoveThreshold) targetElement.addEventListener('touchmove', onMove); + const onMouseLeave = (event: MouseEvent) => { + if (!mousePressed.current) return; + mousePressed.current = false; + + if (timerRef.current) { + clearTimeout(timerRef.current); + timerRef.current = undefined; + } + if (isTriggeredRef.current) { + onLongPressEndRef.current?.(event); + isTriggeredRef.current = false; + } + }; + + targetElement.addEventListener('mousedown', onMouseDown); + targetElement.addEventListener('mouseup', onMouseUp); + targetElement.addEventListener('mouseleave', onMouseLeave); + targetElement.addEventListener('touchstart', onTouchStart); + targetElement.addEventListener('touchend', onTouchEnd); + + if (hasMoveThreshold) { + targetElement.addEventListener('mousemove', onMove); + targetElement.addEventListener('touchmove', onMove); } + return () => { if (timerRef.current) { clearTimeout(timerRef.current); isTriggeredRef.current = false; } - if (!touchSupported) { - targetElement.removeEventListener('mousedown', onStart); - targetElement.removeEventListener('mouseup', onEndWithClick); - targetElement.removeEventListener('mouseleave', onEnd); - if (hasMoveThreshold) targetElement.removeEventListener('mousemove', onMove); - } else { - targetElement.removeEventListener('touchstart', onStart); - targetElement.removeEventListener('touchend', onEndWithClick); - if (hasMoveThreshold) targetElement.removeEventListener('touchmove', onMove); + + targetElement.removeEventListener('mousedown', onMouseDown); + targetElement.removeEventListener('mouseup', onMouseUp); + targetElement.removeEventListener('mouseleave', onMouseLeave); + targetElement.removeEventListener('touchstart', onTouchStart); + targetElement.removeEventListener('touchend', onTouchEnd); + + if (hasMoveThreshold) { + targetElement.removeEventListener('mousemove', onMove); + targetElement.removeEventListener('touchmove', onMove); } }; },