Refactor Reader component by removing hardcoded title; enhance layout responsiveness in Marketplace and Home_Banner components; add context menu functionality with hooks; improve Recent_Card interactivity; clean up Recent_Creation and Recent_Design components; update HoverCards for better accessibility.
This commit is contained in:
parent
7edddfb532
commit
6dda74f583
@ -63,7 +63,6 @@ export default function Reader() {
|
|||||||
|
|
||||||
content: (
|
content: (
|
||||||
<>
|
<>
|
||||||
<h1 className="text-4xl font-bold mb-8 text-center">BRUTAL</h1>
|
|
||||||
<div className="space-y-6 text-center max-w-3xl">
|
<div className="space-y-6 text-center max-w-3xl">
|
||||||
<p className="text-lg">
|
<p className="text-lg">
|
||||||
"Wade Harper! What is up, old friend! It's been too long,
|
"Wade Harper! What is up, old friend! It's been too long,
|
||||||
@ -94,7 +93,6 @@ export default function Reader() {
|
|||||||
videoSrc: "/videos/background2.mp4",
|
videoSrc: "/videos/background2.mp4",
|
||||||
content: (
|
content: (
|
||||||
<>
|
<>
|
||||||
<h1 className="text-4xl font-bold mb-8 text-center">BRUTAL</h1>
|
|
||||||
<div className="space-y-6 text-center max-w-3xl">
|
<div className="space-y-6 text-center max-w-3xl">
|
||||||
<p className="text-lg">
|
<p className="text-lg">
|
||||||
Man, yes! Didn't I tell you not to question this man! I knew
|
Man, yes! Didn't I tell you not to question this man! I knew
|
||||||
|
|||||||
@ -130,8 +130,6 @@
|
|||||||
background-size: cover;
|
background-size: cover;
|
||||||
background-position: center center;
|
background-position: center center;
|
||||||
background-blend-mode: multiply;
|
background-blend-mode: multiply;
|
||||||
height: 170px;
|
|
||||||
width: 1045px;
|
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -165,23 +163,7 @@
|
|||||||
font-family: Arial, Helvetica, sans-serif;
|
font-family: Arial, Helvetica, sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hero_img {
|
|
||||||
background-color: #010313D9;
|
|
||||||
background-image: url("/images/89f1cacd4041e59eb162ffcb0f8080dc179fe415.png");
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-size: cover;
|
|
||||||
background-position: center center;
|
|
||||||
background-blend-mode: multiply;
|
|
||||||
height: 170px;
|
|
||||||
width: 100%;
|
|
||||||
border-radius: 10px;
|
|
||||||
padding: 8px;
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
padding-left: 40px;
|
|
||||||
padding-right: 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hero_text {
|
.hero_text {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@ -31,57 +31,74 @@ const Page: React.FC = () => {
|
|||||||
const [selectedCategory, setSelectedCategory] = useState<string>(
|
const [selectedCategory, setSelectedCategory] = useState<string>(
|
||||||
categories[0]
|
categories[0]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleCategoryClick = (category: string) => {
|
const handleCategoryClick = (category: string) => {
|
||||||
setSelectedCategory(category);
|
setSelectedCategory(category);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full h-full flex flex-col py-4 items-center justify-start bg-white">
|
<div className="w-full flex flex-col py-4 items-center justify-start bg-white">
|
||||||
<div className="w-[1280px] h-[282px] bg-slate-400 rounded-md">
|
{/* Banner - responsive width */}
|
||||||
<Image
|
<div className="w-full max-w-7xl px-4 sm:px-6">
|
||||||
src="/images/Banner.png"
|
<div className="w-full bg-slate-400 rounded-md overflow-hidden">
|
||||||
alt="advertizers"
|
<Image
|
||||||
width={1280}
|
src="/images/Banner.png"
|
||||||
height={282}
|
alt="advertizers"
|
||||||
/>
|
width={1280}
|
||||||
</div>
|
height={282}
|
||||||
<div className="w-full px-4 bg-[#F3F3F3] mt-6 p-4 flex justify-center items-center">
|
className="w-full h-auto object-cover"
|
||||||
<div className="flex flex-wrap gap-4">
|
priority
|
||||||
{categories.map((category) => (
|
/>
|
||||||
<button
|
|
||||||
key={category}
|
|
||||||
className={`px-5 py-2 rounded-md cursor-pointer duration-300 ${
|
|
||||||
selectedCategory === category
|
|
||||||
? "bg-black text-white"
|
|
||||||
: "bg-white text-black"
|
|
||||||
}`}
|
|
||||||
onClick={() => handleCategoryClick(category)}
|
|
||||||
>
|
|
||||||
{category}
|
|
||||||
</button>
|
|
||||||
))}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-[1280px] m-6 grid sm:grid-cols-1 md:grid-cols-5 gap-2">
|
|
||||||
{images.map((image, index) => (
|
{/* Categories - scrollable on mobile */}
|
||||||
<div
|
<div className="w-full bg-[#F3F3F3] mt-6 py-4 px-2">
|
||||||
key={index}
|
<div className="overflow-x-auto max-w-7xl mx-auto">
|
||||||
className="flex w-full h-full flex-col items-center justify-start rounded-md"
|
<div className="flex flex-nowrap md:flex-wrap min-w-max md:min-w-0 gap-2 md:gap-4 px-2">
|
||||||
>
|
{categories.map((category) => (
|
||||||
<Image
|
<button
|
||||||
key={index}
|
key={category}
|
||||||
src={image}
|
className={`px-3 md:px-5 py-2 rounded-md whitespace-nowrap text-sm md:text-base cursor-pointer transition duration-300 ${
|
||||||
alt="advertizers"
|
selectedCategory === category
|
||||||
width={1290}
|
? "bg-black text-white"
|
||||||
height={280}
|
: "bg-white text-black"
|
||||||
/>
|
}`}
|
||||||
<button className="px-15 py-1 border border-slate-800 rounded cursor-pointer">
|
onClick={() => handleCategoryClick(category)}
|
||||||
List in Ebook
|
>
|
||||||
</button>
|
{category}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
))}
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Image grid - responsive columns */}
|
||||||
|
<div className="w-full max-w-7xl px-4 sm:px-6 my-6">
|
||||||
|
<div className="grid grid-cols-2 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-3 md:gap-4">
|
||||||
|
{images.map((image, index) => (
|
||||||
|
<div
|
||||||
|
key={index}
|
||||||
|
className="flex flex-col items-center justify-start rounded-md"
|
||||||
|
>
|
||||||
|
<div className="w-full mb-2 rounded-md overflow-hidden">
|
||||||
|
<Image
|
||||||
|
src={image}
|
||||||
|
alt={`advertizers-${index}`}
|
||||||
|
width={300}
|
||||||
|
height={200}
|
||||||
|
className="w-full h-auto object-cover"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<button className="px-3 py-1 text-sm md:text-base border border-slate-800 rounded cursor-pointer hover:bg-slate-100 transition-colors">
|
||||||
|
List in Ebook
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Page;
|
export default Page;
|
||||||
54
components/ContextMenuExample.tsx
Normal file
54
components/ContextMenuExample.tsx
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import React from "react";
|
||||||
|
import {
|
||||||
|
ContextMenu,
|
||||||
|
ContextMenuContent,
|
||||||
|
ContextMenuItem,
|
||||||
|
ContextMenuTrigger,
|
||||||
|
} from "@/components/ui/context-menu";
|
||||||
|
import { useContextMenuTriggers } from "@/hooks/useContextMenuTriggers";
|
||||||
|
|
||||||
|
const ContextMenuExample: React.FC = () => {
|
||||||
|
const { handlers } = useContextMenuTriggers({
|
||||||
|
longPressDelay: 500, // 500ms for long press
|
||||||
|
doubleClickDelay: 300 // 300ms for double click detection
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="p-4">
|
||||||
|
<h2 className="text-xl font-bold mb-4">Context Menu Example</h2>
|
||||||
|
<p className="mb-4">
|
||||||
|
This component demonstrates how to use the context menu with:
|
||||||
|
</p>
|
||||||
|
<ul className="list-disc pl-6 mb-4">
|
||||||
|
<li>Right-click (desktop)</li>
|
||||||
|
<li>Long press (mobile - 500ms)</li>
|
||||||
|
<li>Double click (mobile and desktop)</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div className="mt-6">
|
||||||
|
<ContextMenu>
|
||||||
|
<ContextMenuTrigger asChild>
|
||||||
|
<div
|
||||||
|
className="bg-blue-100 p-6 rounded-lg shadow-md cursor-pointer text-center"
|
||||||
|
{...handlers}
|
||||||
|
>
|
||||||
|
<p className="font-medium">
|
||||||
|
Try right-click, long press, or double click me!
|
||||||
|
</p>
|
||||||
|
<p className="text-sm text-gray-500 mt-2">
|
||||||
|
Works on both desktop and mobile devices
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</ContextMenuTrigger>
|
||||||
|
<ContextMenuContent>
|
||||||
|
<ContextMenuItem>Option 1</ContextMenuItem>
|
||||||
|
<ContextMenuItem>Option 2</ContextMenuItem>
|
||||||
|
<ContextMenuItem>Option 3</ContextMenuItem>
|
||||||
|
</ContextMenuContent>
|
||||||
|
</ContextMenu>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ContextMenuExample;
|
||||||
@ -2,20 +2,20 @@ import React from "react";
|
|||||||
|
|
||||||
export const Home_Banner: React.FC = () => {
|
export const Home_Banner: React.FC = () => {
|
||||||
return (
|
return (
|
||||||
<div className="hero_img">
|
<div className="hero_img flex flex-wrap gap-3 md:flex-nowrap h-fit md:h-[170px] w-full px-4 md:px-6 py-6 md:py-0 bg-gradient-to-r from-blue-500 to-purple-600">
|
||||||
<div className="hero_text">
|
<div className="hero_text w-full md:w-1/2 flex flex-col justify-center">
|
||||||
<h3>Bring Your Story to Life</h3>
|
<h3 className="text-2xl md:text-3xl font-bold mb-2 text-white">Bring Your Story to Life</h3>
|
||||||
<p>
|
<p className="text-sm md:text-base mb-4 text-white opacity-90 max-w-md">
|
||||||
Start creating your own ebook today — share your voice, inspire
|
Start creating your own ebook today — share your voice, inspire
|
||||||
readers, and publish to the world in just a few clicks.
|
readers, and publish to the world in just a few clicks.
|
||||||
</p>
|
</p>
|
||||||
<button>Create an ebook</button>
|
<button className="bg-white text-blue-600 hover:bg-blue-50 transition-colors rounded-md py-2 px-4 text-sm md:text-base font-medium w-fit">Create an ebook</button>
|
||||||
</div>
|
</div>
|
||||||
<div className="hero_cards">
|
<div className="hero_cards w-full md:w-1/2">
|
||||||
<div className="card_1"></div>
|
<div className="card_1 "></div>
|
||||||
<div className="card_2"></div>
|
<div className="card_2 "></div>
|
||||||
<div className="card_3"></div>
|
<div className="card_3"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@ -1,31 +1,64 @@
|
|||||||
|
import React, { forwardRef } from "react";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import React from "react";
|
|
||||||
|
|
||||||
interface RecentCardProps {
|
export interface Recent_CardProps {
|
||||||
image: string;
|
image: string;
|
||||||
title: string;
|
title: string;
|
||||||
tag: string;
|
tag: string;
|
||||||
description: string;
|
description: string;
|
||||||
|
// Add additional props for event handlers
|
||||||
|
onClick?: React.MouseEventHandler<HTMLDivElement>;
|
||||||
|
onTouchStart?: React.TouchEventHandler<HTMLDivElement>;
|
||||||
|
onTouchEnd?: React.TouchEventHandler<HTMLDivElement>;
|
||||||
|
onTouchMove?: React.TouchEventHandler<HTMLDivElement>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Recent_Card: React.FC<RecentCardProps> = ({ image, title, tag, description }) => {
|
const Recent_Card = forwardRef<HTMLDivElement, Recent_CardProps>(({
|
||||||
|
image,
|
||||||
|
title,
|
||||||
|
tag,
|
||||||
|
description,
|
||||||
|
onClick,
|
||||||
|
onTouchStart,
|
||||||
|
onTouchEnd,
|
||||||
|
onTouchMove,
|
||||||
|
...props
|
||||||
|
}, ref) => {
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col justify-start items-start">
|
<div
|
||||||
<div className="recent_card flex justify-center items-center">
|
ref={ref}
|
||||||
|
className="w-full h-full flex flex-col bg-white rounded-lg overflow-hidden shadow-sm hover:shadow-md transition-shadow cursor-pointer"
|
||||||
|
onClick={onClick}
|
||||||
|
onTouchStart={onTouchStart}
|
||||||
|
onTouchEnd={onTouchEnd}
|
||||||
|
onTouchMove={onTouchMove}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{/* Image container with consistent aspect ratio */}
|
||||||
|
<div className="relative w-full pt-[75%]">
|
||||||
<Image
|
<Image
|
||||||
src={image}
|
src={image}
|
||||||
alt="image"
|
alt={title}
|
||||||
className="card_img"
|
fill
|
||||||
width={100}
|
className="object-cover"
|
||||||
height={100}
|
sizes="(max-width: 640px) 100vw, (max-width: 768px) 50vw, (max-width: 1024px) 33vw, 20vw"
|
||||||
/>
|
/>
|
||||||
|
<div className="absolute top-2 left-2">
|
||||||
|
<span className="bg-black text-white text-xs px-2 py-1 rounded">
|
||||||
|
{tag}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Content section */}
|
||||||
|
<div className="p-3 flex flex-col flex-grow">
|
||||||
|
<h4 className="font-medium text-sm mb-1 line-clamp-1">{title}</h4>
|
||||||
|
<p className="text-xs text-gray-500">{description}</p>
|
||||||
</div>
|
</div>
|
||||||
<h3 className="text-[14px] font-[400] text-slate-900">{title}</h3>
|
|
||||||
<p className="text-[10px] font-[400] text-slate-400">
|
|
||||||
{tag} <span> {description}</span>
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
});
|
||||||
|
|
||||||
export default Recent_Card;
|
Recent_Card.displayName = "Recent_Card";
|
||||||
|
|
||||||
|
export default Recent_Card;
|
||||||
@ -1,3 +1,4 @@
|
|||||||
|
'use client'
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { FaSlidersH } from "react-icons/fa";
|
import { FaSlidersH } from "react-icons/fa";
|
||||||
import Recent_Card from "./Recent_Card";
|
import Recent_Card from "./Recent_Card";
|
||||||
@ -8,17 +9,22 @@ import {
|
|||||||
ContextMenuTrigger,
|
ContextMenuTrigger,
|
||||||
} from "@/components/ui/context-menu"
|
} from "@/components/ui/context-menu"
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
import { useContextMenuTriggers } from "@/hooks/useContextMenuTriggers";
|
||||||
|
|
||||||
|
|
||||||
const Recent_Creation: React.FC = () => {
|
const Recent_Creation: React.FC = () => {
|
||||||
|
// Use our custom hook for the first card with context menu
|
||||||
|
const { handlers } = useContextMenuTriggers({
|
||||||
|
longPressDelay: 500, // 500ms for long press
|
||||||
|
doubleClickDelay: 300 // 300ms for double click detection
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="recent_container">
|
<div className="recent_container w-full">
|
||||||
<div className="recent_title">
|
<div className="recent_title w-full">
|
||||||
<h3 className="text-[16px] font-[700]">Recent creations</h3>
|
<h3 className="text-[16px] font-[700]">Recent creations</h3>
|
||||||
<FaSlidersH className="text-[20px] recent_icon" />
|
<FaSlidersH className="text-[20px] recent_icon" />
|
||||||
</div>
|
</div>
|
||||||
<div className="grid sm:grid-cols-2 md:grid-cols-5 gap-4 mt-2">
|
<div className="grid sm:grid-cols-2 md:grid-cols-5 gap-4 mt-2 w-full">
|
||||||
<ContextMenu>
|
<ContextMenu>
|
||||||
<ContextMenuTrigger asChild>
|
<ContextMenuTrigger asChild>
|
||||||
<Recent_Card
|
<Recent_Card
|
||||||
@ -26,6 +32,8 @@ const Recent_Creation: React.FC = () => {
|
|||||||
title="Good morning Gabe Hager!"
|
title="Good morning Gabe Hager!"
|
||||||
tag="Ebook"
|
tag="Ebook"
|
||||||
description="Edited 13 mins ago"
|
description="Edited 13 mins ago"
|
||||||
|
// Add our event handlers for mobile support
|
||||||
|
{...handlers}
|
||||||
/>
|
/>
|
||||||
</ContextMenuTrigger>
|
</ContextMenuTrigger>
|
||||||
<ContextMenuContent>
|
<ContextMenuContent>
|
||||||
|
|||||||
@ -29,7 +29,7 @@ export default function HoverCards({
|
|||||||
return (
|
return (
|
||||||
<HoverCard>
|
<HoverCard>
|
||||||
<HoverCardTrigger asChild>
|
<HoverCardTrigger asChild>
|
||||||
<p className="text-blue-400 cursor-pointer underline">{triggerText}</p>
|
<span className="text-blue-400 cursor-pointer underline inline">{triggerText}</span>
|
||||||
</HoverCardTrigger>
|
</HoverCardTrigger>
|
||||||
<HoverCardContent className="w-80">
|
<HoverCardContent className="w-80">
|
||||||
<div className="max-w-xs bg-white rounded-lg overflow-hidden shadow-lg">
|
<div className="max-w-xs bg-white rounded-lg overflow-hidden shadow-lg">
|
||||||
@ -65,4 +65,4 @@ export default function HoverCards({
|
|||||||
</HoverCardContent>
|
</HoverCardContent>
|
||||||
</HoverCard>
|
</HoverCard>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -73,7 +73,7 @@ export default function RecentDesign() {
|
|||||||
</span>
|
</span>
|
||||||
|
|
||||||
{/* Action Buttons */}
|
{/* Action Buttons */}
|
||||||
<div className="ml-auto">
|
{/* <div className="ml-auto">
|
||||||
<Button variant="ghost" size="icon" className="h-10 w-10 p-1">
|
<Button variant="ghost" size="icon" className="h-10 w-10 p-1">
|
||||||
<div className="flex items-center gap-0.5 md:gap-1 pr-5">
|
<div className="flex items-center gap-0.5 md:gap-1 pr-5">
|
||||||
<Button variant="ghost" size="icon" className="h-7 w-7 md:h-8 md:w-8 p-0 hover:bg-[#e6e6e8]">
|
<Button variant="ghost" size="icon" className="h-7 w-7 md:h-8 md:w-8 p-0 hover:bg-[#e6e6e8]">
|
||||||
@ -84,7 +84,7 @@ export default function RecentDesign() {
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div> */}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
95
hooks/useContextMenuTriggers.ts
Normal file
95
hooks/useContextMenuTriggers.ts
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
'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,
|
||||||
|
};
|
||||||
|
};
|
||||||
Loading…
Reference in New Issue
Block a user