-
- {isImage ?
-
- :
-
- }
-
-
-
+
+
+
+
+ {triggerText}
+
+
+
+
+ {isMobile && (
+
+ )}
+
+ {isImage ? (
+
+ ) : (
+
+ )}
+
-
-
- {description}
-
+
+
+ {description}
+
-
-
- {linkText}
-
-
+
+
+ {linkText}
+
+
+
-
-
-
- )
-}
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/components/ui/tooltip.tsx b/components/ui/tooltip.tsx
new file mode 100644
index 0000000..4ee26b3
--- /dev/null
+++ b/components/ui/tooltip.tsx
@@ -0,0 +1,61 @@
+"use client"
+
+import * as React from "react"
+import * as TooltipPrimitive from "@radix-ui/react-tooltip"
+
+import { cn } from "@/lib/utils"
+
+function TooltipProvider({
+ delayDuration = 0,
+ ...props
+}: React.ComponentProps
) {
+ return (
+
+ )
+}
+
+function Tooltip({
+ ...props
+}: React.ComponentProps) {
+ return (
+
+
+
+ )
+}
+
+function TooltipTrigger({
+ ...props
+}: React.ComponentProps) {
+ return
+}
+
+function TooltipContent({
+ className,
+ sideOffset = 0,
+ children,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+
+ {children}
+
+
+
+ )
+}
+
+export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }
diff --git a/hooks/useContextMenuTriggers.ts b/hooks/useContextMenuTriggers.ts
index 322a270..25ef214 100644
--- a/hooks/useContextMenuTriggers.ts
+++ b/hooks/useContextMenuTriggers.ts
@@ -5,19 +5,26 @@ import { useRef, useCallback, useState } from 'react';
interface UseContextMenuTriggersProps {
longPressDelay?: number;
doubleClickDelay?: number;
+ onLongPress?: () => void;
+ onDoubleTap?: () => void;
}
export const useContextMenuTriggers = ({
longPressDelay = 500, // Default long press delay in ms
doubleClickDelay = 300, // Default double click delay in ms
+ onLongPress,
+ onDoubleTap
}: UseContextMenuTriggersProps = {}) => {
const [isLongPressing, setIsLongPressing] = useState(false);
const longPressTimerRef = useRef(null);
const lastClickTimeRef = useRef(0);
const targetRef = useRef(null);
+ const preventClickRef = useRef(false);
// Function to simulate a right-click event
const simulateRightClick = useCallback((element: HTMLElement) => {
+ console.log("Simulating right click");
+
// Create and dispatch a custom contextmenu event
const contextMenuEvent = new MouseEvent('contextmenu', {
bubbles: true,
@@ -31,6 +38,7 @@ export const useContextMenuTriggers = ({
// Handle touch start (for long press)
const handleTouchStart = useCallback((e: React.TouchEvent) => {
+ console.log("Touch start detected");
if (e.touches.length === 1) {
const element = e.currentTarget as HTMLElement;
targetRef.current = element;
@@ -41,18 +49,27 @@ export const useContextMenuTriggers = ({
}
setIsLongPressing(true);
+ preventClickRef.current = false;
// Start a timer for long press
longPressTimerRef.current = setTimeout(() => {
- if (isLongPressing && targetRef.current) {
- simulateRightClick(targetRef.current);
+ if (targetRef.current) {
+ console.log("Long press detected");
+ preventClickRef.current = true;
+
+ if (onLongPress) {
+ onLongPress();
+ } else {
+ simulateRightClick(targetRef.current);
+ }
}
}, longPressDelay);
}
- }, [longPressDelay, isLongPressing, simulateRightClick]);
+ }, [longPressDelay, simulateRightClick, onLongPress]);
// Handle touch end (cancel long press)
const handleTouchEnd = useCallback(() => {
+ console.log("Touch end detected");
if (longPressTimerRef.current) {
clearTimeout(longPressTimerRef.current);
}
@@ -61,27 +78,41 @@ export const useContextMenuTriggers = ({
// Handle touch move (cancel long press if moving)
const handleTouchMove = useCallback(() => {
+ console.log("Touch move detected");
if (longPressTimerRef.current) {
clearTimeout(longPressTimerRef.current);
}
setIsLongPressing(false);
}, []);
- // Handle double click
+ // Handle click and double click/tap
const handleClick = useCallback((e: React.MouseEvent) => {
+ if (preventClickRef.current) {
+ preventClickRef.current = false;
+ return;
+ }
+
const currentTime = new Date().getTime();
const element = e.currentTarget as HTMLElement;
// Check if this is a double click
if (currentTime - lastClickTimeRef.current < doubleClickDelay) {
+ console.log("Double tap detected");
+
// Double click detected
- simulateRightClick(element);
+ if (onDoubleTap) {
+ onDoubleTap();
+ } else {
+ simulateRightClick(element);
+ }
+
lastClickTimeRef.current = 0; // Reset to prevent triple-click issues
} else {
// First click
+ console.log("Single tap detected");
lastClickTimeRef.current = currentTime;
}
- }, [doubleClickDelay, simulateRightClick]);
+ }, [doubleClickDelay, simulateRightClick, onDoubleTap]);
return {
handlers: {
@@ -92,4 +123,4 @@ export const useContextMenuTriggers = ({
},
simulateRightClick,
};
-};
+};
\ No newline at end of file
diff --git a/package.json b/package.json
index b06ecc8..480f77f 100644
--- a/package.json
+++ b/package.json
@@ -10,11 +10,12 @@
},
"dependencies": {
"@radix-ui/react-avatar": "^1.1.7",
+ "@radix-ui/react-checkbox": "^1.2.3",
"@radix-ui/react-context-menu": "^2.2.12",
"@radix-ui/react-hover-card": "^1.1.11",
- "@radix-ui/react-checkbox": "^1.2.3",
"@radix-ui/react-slot": "^1.2.0",
"@radix-ui/react-tabs": "^1.1.9",
+ "@radix-ui/react-tooltip": "^1.2.4",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"iconsax-react": "^0.0.8",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 8fae87f..2da3413 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -26,6 +26,9 @@ importers:
'@radix-ui/react-tabs':
specifier: ^1.1.9
version: 1.1.9(@types/react-dom@19.1.2(@types/react@19.1.2))(@types/react@19.1.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@radix-ui/react-tooltip':
+ specifier: ^1.2.4
+ version: 1.2.4(@types/react-dom@19.1.2(@types/react@19.1.2))(@types/react@19.1.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
class-variance-authority:
specifier: ^0.7.1
version: 0.7.1
@@ -608,6 +611,19 @@ packages:
'@types/react-dom':
optional: true
+ '@radix-ui/react-tooltip@1.2.4':
+ resolution: {integrity: sha512-DyW8VVeeMSSLFvAmnVnCwvI3H+1tpJFHT50r+tdOoMse9XqYDBCcyux8u3G2y+LOpt7fPQ6KKH0mhs+ce1+Z5w==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
'@radix-ui/react-use-callback-ref@1.1.1':
resolution: {integrity: sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==}
peerDependencies:
@@ -689,6 +705,19 @@ packages:
'@types/react':
optional: true
+ '@radix-ui/react-visually-hidden@1.2.0':
+ resolution: {integrity: sha512-rQj0aAWOpCdCMRbI6pLQm8r7S2BM3YhTa0SzOYD55k+hJA8oo9J+H+9wLM9oMlZWOX/wJWPTzfDfmZkf7LvCfg==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
'@radix-ui/rect@1.1.1':
resolution: {integrity: sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==}
@@ -2671,6 +2700,26 @@ snapshots:
'@types/react': 19.1.2
'@types/react-dom': 19.1.2(@types/react@19.1.2)
+ '@radix-ui/react-tooltip@1.2.4(@types/react-dom@19.1.2(@types/react@19.1.2))(@types/react@19.1.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.2
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.2)(react@19.1.0)
+ '@radix-ui/react-context': 1.1.2(@types/react@19.1.2)(react@19.1.0)
+ '@radix-ui/react-dismissable-layer': 1.1.7(@types/react-dom@19.1.2(@types/react@19.1.2))(@types/react@19.1.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@radix-ui/react-id': 1.1.1(@types/react@19.1.2)(react@19.1.0)
+ '@radix-ui/react-popper': 1.2.4(@types/react-dom@19.1.2(@types/react@19.1.2))(@types/react@19.1.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@radix-ui/react-portal': 1.1.6(@types/react-dom@19.1.2(@types/react@19.1.2))(@types/react@19.1.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.2(@types/react@19.1.2))(@types/react@19.1.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@radix-ui/react-primitive': 2.1.0(@types/react-dom@19.1.2(@types/react@19.1.2))(@types/react@19.1.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@radix-ui/react-slot': 1.2.0(@types/react@19.1.2)(react@19.1.0)
+ '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.2)(react@19.1.0)
+ '@radix-ui/react-visually-hidden': 1.2.0(@types/react-dom@19.1.2(@types/react@19.1.2))(@types/react@19.1.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ react: 19.1.0
+ react-dom: 19.1.0(react@19.1.0)
+ optionalDependencies:
+ '@types/react': 19.1.2
+ '@types/react-dom': 19.1.2(@types/react@19.1.2)
+
'@radix-ui/react-use-callback-ref@1.1.1(@types/react@19.1.2)(react@19.1.0)':
dependencies:
react: 19.1.0
@@ -2732,6 +2781,15 @@ snapshots:
optionalDependencies:
'@types/react': 19.1.2
+ '@radix-ui/react-visually-hidden@1.2.0(@types/react-dom@19.1.2(@types/react@19.1.2))(@types/react@19.1.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
+ dependencies:
+ '@radix-ui/react-primitive': 2.1.0(@types/react-dom@19.1.2(@types/react@19.1.2))(@types/react@19.1.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ react: 19.1.0
+ react-dom: 19.1.0(react@19.1.0)
+ optionalDependencies:
+ '@types/react': 19.1.2
+ '@types/react-dom': 19.1.2(@types/react@19.1.2)
+
'@radix-ui/rect@1.1.1': {}
'@rtsao/scc@1.1.0': {}