hot-fix/adding-mobile-responsiveness #3

Merged
Yussif merged 3 commits from hot-fix/adding-mobile-responsiveness into main 2025-04-29 14:06:26 +00:00
10 changed files with 284 additions and 97 deletions

View File

@ -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">
&quot;Wade Harper! What is up, old friend! It&#39;s been too long, &quot;Wade Harper! What is up, old friend! It&#39;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&#39;t I tell you not to question this man! I knew Man, yes! Didn&#39;t I tell you not to question this man! I knew

View File

@ -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;

View File

@ -31,25 +31,35 @@ 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 */}
<div className="w-full max-w-7xl px-4 sm:px-6">
<div className="w-full bg-slate-400 rounded-md overflow-hidden">
<Image <Image
src="/images/Banner.png" src="/images/Banner.png"
alt="advertizers" alt="advertizers"
width={1280} width={1280}
height={282} height={282}
className="w-full h-auto object-cover"
priority
/> />
</div> </div>
<div className="w-full px-4 bg-[#F3F3F3] mt-6 p-4 flex justify-center items-center"> </div>
<div className="flex flex-wrap gap-4">
{/* 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) => ( {categories.map((category) => (
<button <button
key={category} 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 selectedCategory === category
? "bg-black text-white" ? "bg-black text-white"
: "bg-white text-black" : "bg-white text-black"
@ -61,26 +71,33 @@ const Page: React.FC = () => {
))} ))}
</div> </div>
</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) => ( {images.map((image, index) => (
<div <div
key={index} 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 <Image
key={index}
src={image} src={image}
alt="advertizers" alt={`advertizers-${index}`}
width={1290} width={300}
height={280} 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 List in Ebook
</button> </button>
</div> </div>
))} ))}
</div> </div>
</div> </div>
</div>
); );
}; };

View 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;

View File

@ -2,16 +2,16 @@ 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>

View File

@ -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>
); );
}; });
Recent_Card.displayName = "Recent_Card";
export default Recent_Card; export default Recent_Card;

View File

@ -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>

View File

@ -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">

View File

@ -1,5 +1,5 @@
import Image from "next/image"; import Image from "next/image";
import { ExternalLink, Ellipsis, Trash2 } from 'lucide-react'; import { Trash2 } from 'lucide-react';
import Link from "next/link"; import Link from "next/link";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import React from "react"; import React from "react";
@ -76,7 +76,7 @@ export default function RecentDesign({ drawerOpen }: { drawerOpen?: boolean, set
{design.title} {design.title}
</span> </span>
{/* Action Buttons */} {/* 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"> <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"> <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]"> <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> </Button>
</div> </div>
</Button> </Button>
</div> </div> */}
</div> </div>
))} ))}
</div> </div>

View 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,
};
};