96 lines
3.0 KiB
TypeScript
96 lines
3.0 KiB
TypeScript
|
|
'use client'
|
||
|
|
|
||
|
|
import { useRef, useCallback, useState } from 'react';
|
||
|
|
|
||
|
|
interface UseContextMenuTriggersProps {
|
||
|
|
longPressDelay?: number;
|
||
|
|
doubleClickDelay?: number;
|
||
|
|
}
|
||
|
|
|
||
|
|
export const useContextMenuTriggers = ({
|
||
|
|
longPressDelay = 500, // Default long press delay in ms
|
||
|
|
doubleClickDelay = 300, // Default double click delay in ms
|
||
|
|
}: UseContextMenuTriggersProps = {}) => {
|
||
|
|
const [isLongPressing, setIsLongPressing] = useState(false);
|
||
|
|
const longPressTimerRef = useRef<NodeJS.Timeout | null>(null);
|
||
|
|
const lastClickTimeRef = useRef<number>(0);
|
||
|
|
const targetRef = useRef<HTMLElement | null>(null);
|
||
|
|
|
||
|
|
// Function to simulate a right-click event
|
||
|
|
const simulateRightClick = useCallback((element: HTMLElement) => {
|
||
|
|
// Create and dispatch a custom contextmenu event
|
||
|
|
const contextMenuEvent = new MouseEvent('contextmenu', {
|
||
|
|
bubbles: true,
|
||
|
|
cancelable: true,
|
||
|
|
clientX: element.getBoundingClientRect().left + element.offsetWidth / 2,
|
||
|
|
clientY: element.getBoundingClientRect().top + element.offsetHeight / 2,
|
||
|
|
});
|
||
|
|
|
||
|
|
element.dispatchEvent(contextMenuEvent);
|
||
|
|
}, []);
|
||
|
|
|
||
|
|
// Handle touch start (for long press)
|
||
|
|
const handleTouchStart = useCallback((e: React.TouchEvent) => {
|
||
|
|
if (e.touches.length === 1) {
|
||
|
|
const element = e.currentTarget as HTMLElement;
|
||
|
|
targetRef.current = element;
|
||
|
|
|
||
|
|
// Clear any existing timer
|
||
|
|
if (longPressTimerRef.current) {
|
||
|
|
clearTimeout(longPressTimerRef.current);
|
||
|
|
}
|
||
|
|
|
||
|
|
setIsLongPressing(true);
|
||
|
|
|
||
|
|
// Start a timer for long press
|
||
|
|
longPressTimerRef.current = setTimeout(() => {
|
||
|
|
if (isLongPressing && targetRef.current) {
|
||
|
|
simulateRightClick(targetRef.current);
|
||
|
|
}
|
||
|
|
}, longPressDelay);
|
||
|
|
}
|
||
|
|
}, [longPressDelay, isLongPressing, simulateRightClick]);
|
||
|
|
|
||
|
|
// Handle touch end (cancel long press)
|
||
|
|
const handleTouchEnd = useCallback(() => {
|
||
|
|
if (longPressTimerRef.current) {
|
||
|
|
clearTimeout(longPressTimerRef.current);
|
||
|
|
}
|
||
|
|
setIsLongPressing(false);
|
||
|
|
}, []);
|
||
|
|
|
||
|
|
// Handle touch move (cancel long press if moving)
|
||
|
|
const handleTouchMove = useCallback(() => {
|
||
|
|
if (longPressTimerRef.current) {
|
||
|
|
clearTimeout(longPressTimerRef.current);
|
||
|
|
}
|
||
|
|
setIsLongPressing(false);
|
||
|
|
}, []);
|
||
|
|
|
||
|
|
// Handle double click
|
||
|
|
const handleClick = useCallback((e: React.MouseEvent) => {
|
||
|
|
const currentTime = new Date().getTime();
|
||
|
|
const element = e.currentTarget as HTMLElement;
|
||
|
|
|
||
|
|
// Check if this is a double click
|
||
|
|
if (currentTime - lastClickTimeRef.current < doubleClickDelay) {
|
||
|
|
// Double click detected
|
||
|
|
simulateRightClick(element);
|
||
|
|
lastClickTimeRef.current = 0; // Reset to prevent triple-click issues
|
||
|
|
} else {
|
||
|
|
// First click
|
||
|
|
lastClickTimeRef.current = currentTime;
|
||
|
|
}
|
||
|
|
}, [doubleClickDelay, simulateRightClick]);
|
||
|
|
|
||
|
|
return {
|
||
|
|
handlers: {
|
||
|
|
onTouchStart: handleTouchStart,
|
||
|
|
onTouchEnd: handleTouchEnd,
|
||
|
|
onTouchMove: handleTouchMove,
|
||
|
|
onClick: handleClick,
|
||
|
|
},
|
||
|
|
simulateRightClick,
|
||
|
|
};
|
||
|
|
};
|