Merge pull request 'hot-fix/adding-mobile-responsiveness' (#3) from hot-fix/adding-mobile-responsiveness into main
Reviewed-on: http://35.207.46.142/Wodey/woedii/pulls/3
This commit is contained in:
commit
ea65d655dc
@ -63,7 +63,6 @@ export default function Reader() {
|
||||
|
||||
content: (
|
||||
<>
|
||||
<h1 className="text-4xl font-bold mb-8 text-center">BRUTAL</h1>
|
||||
<div className="space-y-6 text-center max-w-3xl">
|
||||
<p className="text-lg">
|
||||
"Wade Harper! What is up, old friend! It's been too long,
|
||||
@ -94,7 +93,6 @@ export default function Reader() {
|
||||
videoSrc: "/videos/background2.mp4",
|
||||
content: (
|
||||
<>
|
||||
<h1 className="text-4xl font-bold mb-8 text-center">BRUTAL</h1>
|
||||
<div className="space-y-6 text-center max-w-3xl">
|
||||
<p className="text-lg">
|
||||
Man, yes! Didn't I tell you not to question this man! I knew
|
||||
|
||||
@ -130,8 +130,6 @@
|
||||
background-size: cover;
|
||||
background-position: center center;
|
||||
background-blend-mode: multiply;
|
||||
height: 170px;
|
||||
width: 1045px;
|
||||
border-radius: 10px;
|
||||
padding: 8px;
|
||||
display: flex;
|
||||
@ -165,23 +163,7 @@
|
||||
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 {
|
||||
display: flex;
|
||||
|
||||
@ -31,25 +31,35 @@ const Page: React.FC = () => {
|
||||
const [selectedCategory, setSelectedCategory] = useState<string>(
|
||||
categories[0]
|
||||
);
|
||||
|
||||
const handleCategoryClick = (category: string) => {
|
||||
setSelectedCategory(category);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="w-full h-full flex flex-col py-4 items-center justify-start bg-white">
|
||||
<div className="w-[1280px] h-[282px] bg-slate-400 rounded-md">
|
||||
<div className="w-full flex flex-col py-4 items-center justify-start bg-white">
|
||||
{/* Banner - responsive width */}
|
||||
<div className="w-full max-w-7xl px-4 sm:px-6">
|
||||
<div className="w-full bg-slate-400 rounded-md overflow-hidden">
|
||||
<Image
|
||||
src="/images/Banner.png"
|
||||
alt="advertizers"
|
||||
width={1280}
|
||||
height={282}
|
||||
className="w-full h-auto object-cover"
|
||||
priority
|
||||
/>
|
||||
</div>
|
||||
<div className="w-full px-4 bg-[#F3F3F3] mt-6 p-4 flex justify-center items-center">
|
||||
<div className="flex flex-wrap gap-4">
|
||||
</div>
|
||||
|
||||
{/* Categories - scrollable on mobile */}
|
||||
<div className="w-full bg-[#F3F3F3] mt-6 py-4 px-2">
|
||||
<div className="overflow-x-auto max-w-7xl mx-auto">
|
||||
<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) => (
|
||||
<button
|
||||
key={category}
|
||||
className={`px-5 py-2 rounded-md cursor-pointer duration-300 ${
|
||||
className={`px-3 md:px-5 py-2 rounded-md whitespace-nowrap text-sm md:text-base cursor-pointer transition duration-300 ${
|
||||
selectedCategory === category
|
||||
? "bg-black text-white"
|
||||
: "bg-white text-black"
|
||||
@ -61,26 +71,33 @@ const Page: React.FC = () => {
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-[1280px] m-6 grid sm:grid-cols-1 md:grid-cols-5 gap-2">
|
||||
</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 w-full h-full flex-col items-center justify-start rounded-md"
|
||||
className="flex flex-col items-center justify-start rounded-md"
|
||||
>
|
||||
<div className="w-full mb-2 rounded-md overflow-hidden">
|
||||
<Image
|
||||
key={index}
|
||||
src={image}
|
||||
alt="advertizers"
|
||||
width={1290}
|
||||
height={280}
|
||||
alt={`advertizers-${index}`}
|
||||
width={300}
|
||||
height={200}
|
||||
className="w-full h-auto object-cover"
|
||||
/>
|
||||
<button className="px-15 py-1 border border-slate-800 rounded cursor-pointer">
|
||||
</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>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
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,18 +2,18 @@ import React from "react";
|
||||
|
||||
export const Home_Banner: React.FC = () => {
|
||||
return (
|
||||
<div className="hero_img">
|
||||
<div className="hero_text">
|
||||
<h3>Bring Your Story to Life</h3>
|
||||
<p>
|
||||
<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 w-full md:w-1/2 flex flex-col justify-center">
|
||||
<h3 className="text-2xl md:text-3xl font-bold mb-2 text-white">Bring Your Story to Life</h3>
|
||||
<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
|
||||
readers, and publish to the world in just a few clicks.
|
||||
</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 className="hero_cards">
|
||||
<div className="card_1"></div>
|
||||
<div className="card_2"></div>
|
||||
<div className="hero_cards w-full md:w-1/2">
|
||||
<div className="card_1 "></div>
|
||||
<div className="card_2 "></div>
|
||||
<div className="card_3"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,31 +1,64 @@
|
||||
import React, { forwardRef } from "react";
|
||||
import Image from "next/image";
|
||||
import React from "react";
|
||||
|
||||
interface RecentCardProps {
|
||||
export interface Recent_CardProps {
|
||||
image: string;
|
||||
title: string;
|
||||
tag: 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 (
|
||||
<div className="flex flex-col justify-start items-start">
|
||||
<div className="recent_card flex justify-center items-center">
|
||||
<div
|
||||
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
|
||||
src={image}
|
||||
alt="image"
|
||||
className="card_img"
|
||||
width={100}
|
||||
height={100}
|
||||
alt={title}
|
||||
fill
|
||||
className="object-cover"
|
||||
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>
|
||||
<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>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
Recent_Card.displayName = "Recent_Card";
|
||||
|
||||
export default Recent_Card;
|
||||
@ -1,3 +1,4 @@
|
||||
'use client'
|
||||
import React from "react";
|
||||
import { FaSlidersH } from "react-icons/fa";
|
||||
import Recent_Card from "./Recent_Card";
|
||||
@ -8,17 +9,22 @@ import {
|
||||
ContextMenuTrigger,
|
||||
} from "@/components/ui/context-menu"
|
||||
import Link from "next/link";
|
||||
|
||||
|
||||
import { useContextMenuTriggers } from "@/hooks/useContextMenuTriggers";
|
||||
|
||||
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 (
|
||||
<div className="recent_container">
|
||||
<div className="recent_title">
|
||||
<div className="recent_container w-full">
|
||||
<div className="recent_title w-full">
|
||||
<h3 className="text-[16px] font-[700]">Recent creations</h3>
|
||||
<FaSlidersH className="text-[20px] recent_icon" />
|
||||
</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>
|
||||
<ContextMenuTrigger asChild>
|
||||
<Recent_Card
|
||||
@ -26,6 +32,8 @@ const Recent_Creation: React.FC = () => {
|
||||
title="Good morning Gabe Hager!"
|
||||
tag="Ebook"
|
||||
description="Edited 13 mins ago"
|
||||
// Add our event handlers for mobile support
|
||||
{...handlers}
|
||||
/>
|
||||
</ContextMenuTrigger>
|
||||
<ContextMenuContent>
|
||||
|
||||
@ -29,7 +29,7 @@ export default function HoverCards({
|
||||
return (
|
||||
<HoverCard>
|
||||
<HoverCardTrigger asChild>
|
||||
<p className="text-blue-400 cursor-pointer underline">{triggerText}</p>
|
||||
<span className="text-blue-400 cursor-pointer underline inline">{triggerText}</span>
|
||||
</HoverCardTrigger>
|
||||
<HoverCardContent className="w-80">
|
||||
<div className="max-w-xs bg-white rounded-lg overflow-hidden shadow-lg">
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import Image from "next/image";
|
||||
import { ExternalLink, Ellipsis, Trash2 } from 'lucide-react';
|
||||
import { Trash2 } from 'lucide-react';
|
||||
import Link from "next/link";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import React from "react";
|
||||
@ -76,7 +76,7 @@ export default function RecentDesign({ drawerOpen }: { drawerOpen?: boolean, set
|
||||
{design.title}
|
||||
</span>
|
||||
{/* Action Buttons */}
|
||||
<div className="ml-auto">
|
||||
{/* <div className="ml-auto">
|
||||
<Button variant="ghost" size="icon" className="h-8 sm:h-9 md:h-10 w-8 sm:w-9 md:w-10 p-1">
|
||||
<div className="flex items-center gap-0.5 sm:gap-1 md:gap-1.5 pr-2 sm:pr-3 md:pr-5">
|
||||
<Button variant="ghost" size="icon" className="h-6 w-6 sm:h-7 sm:w-7 md:h-8 md:w-8 p-0 hover:bg-[#e6e6e8]">
|
||||
@ -87,7 +87,7 @@ export default function RecentDesign({ drawerOpen }: { drawerOpen?: boolean, set
|
||||
</Button>
|
||||
</div>
|
||||
</Button>
|
||||
</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