mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-11 10:23:54 +00:00
96 lines
2.8 KiB
TypeScript
96 lines
2.8 KiB
TypeScript
import React, { useEffect, useMemo, useRef } from 'react';
|
|
|
|
import { throttle } from '../utils/throttle';
|
|
|
|
export interface UseLongPressOptions {
|
|
/** Time in milliseconds to trigger the long press, default is 400ms */
|
|
threshold?: number;
|
|
|
|
/** Callback triggered when the long press starts */
|
|
onStart?: (event: React.MouseEvent | React.TouchEvent) => void;
|
|
|
|
/** Callback triggered when the long press finishes */
|
|
onFinish?: (event: React.MouseEvent | React.TouchEvent) => void;
|
|
|
|
/** Callback triggered when the long press is canceled */
|
|
onCancel?: (event: React.MouseEvent | React.TouchEvent) => void;
|
|
}
|
|
|
|
export interface UseLongPressReturnValue {
|
|
onMouseDown: (event: React.MouseEvent) => void;
|
|
onMouseUp: (event: React.MouseEvent) => void;
|
|
onMouseLeave: (event: React.MouseEvent) => void;
|
|
onTouchStart: (event: React.TouchEvent) => void;
|
|
onTouchEnd: (event: React.TouchEvent) => void;
|
|
}
|
|
|
|
export function useLongPress(
|
|
onLongPress: (event: React.MouseEvent | React.TouchEvent) => void,
|
|
options: UseLongPressOptions = {},
|
|
): UseLongPressReturnValue {
|
|
const { threshold = 700, onStart, onFinish, onCancel } = options;
|
|
const isLongPressActive = useRef(false);
|
|
const isPressed = useRef(false);
|
|
const timeout = useRef<number>(-1);
|
|
|
|
useEffect(() => () => window.clearTimeout(timeout.current), []);
|
|
|
|
return useMemo(() => {
|
|
if (typeof onLongPress !== 'function') {
|
|
return {} as UseLongPressReturnValue;
|
|
}
|
|
|
|
const start = (event: React.MouseEvent | React.TouchEvent) => {
|
|
if (!isMouseEvent(event) && !isTouchEvent(event)) {
|
|
return;
|
|
}
|
|
|
|
if (onStart) {
|
|
onStart(event);
|
|
}
|
|
|
|
isPressed.current = true;
|
|
timeout.current = window.setTimeout(() => {
|
|
onLongPress(event);
|
|
isLongPressActive.current = true;
|
|
}, threshold);
|
|
};
|
|
|
|
const cancel = (event: React.MouseEvent | React.TouchEvent) => {
|
|
if (!isMouseEvent(event) && !isTouchEvent(event)) {
|
|
return;
|
|
}
|
|
|
|
if (isLongPressActive.current) {
|
|
onFinish?.(event);
|
|
} else if (isPressed.current) {
|
|
onCancel?.(event);
|
|
}
|
|
|
|
isLongPressActive.current = false;
|
|
isPressed.current = false;
|
|
|
|
if (timeout.current) {
|
|
window.clearTimeout(timeout.current);
|
|
}
|
|
};
|
|
|
|
return {
|
|
onMouseDown: start,
|
|
onMouseUp: cancel,
|
|
onMouseLeave: cancel,
|
|
onTouchStart: start,
|
|
onTouchEnd: cancel,
|
|
onTouchMove: throttle(cancel, 150),
|
|
};
|
|
}, [onLongPress, threshold, onCancel, onFinish, onStart]);
|
|
}
|
|
|
|
function isTouchEvent(event: React.MouseEvent | React.TouchEvent): event is React.TouchEvent {
|
|
return window.TouchEvent ? event.nativeEvent instanceof TouchEvent : 'touches' in event.nativeEvent;
|
|
}
|
|
|
|
function isMouseEvent(event: React.MouseEvent | React.TouchEvent): event is React.MouseEvent {
|
|
return event.nativeEvent instanceof MouseEvent;
|
|
}
|