feat: add responsive carousel with mobile-optimized layout and controls

This commit is contained in:
2025-09-19 01:01:46 +02:00
parent 0dedde3592
commit 43d995bbc2
2 changed files with 73 additions and 17 deletions

View File

@@ -1,6 +1,7 @@
'use client'
import { useEffect, useMemo, useState, useRef } from 'react'
import { useEffect, useMemo, useState } from 'react'
import { useResponsiveCarousel } from '@/hooks/useResponsiveCarousel';
import Image from 'next/image'
import { motion, AnimatePresence } from 'framer-motion'
import { wrap } from 'popmotion'
@@ -24,16 +25,13 @@ const galleryItems = [
]
// 🔧 Carousel Config
const VISIBLE = 4
const GAP = 300 // spacing for larger cards
const ROT_Y = 18
const DEPTH = 210
const SCALE_DROP = 0.12
const AUTOPLAY_MS = 3200
const VISIBLE = 4;
const AUTOPLAY_MS = 3200;
export function ClickableGallery() {
const [active, setActive] = useState(0)
const [hovering, setHovering] = useState(false)
const [active, setActive] = useState(0);
const [hovering, setHovering] = useState(false);
const { GAP, ROT_Y, DEPTH, SCALE_DROP } = useResponsiveCarousel();
// autoplay
useEffect(() => {
@@ -59,7 +57,7 @@ export function ClickableGallery() {
</div>
</FadeIn>
<FadeIn transition={{ duration: 0.8, delay: 0.2 }}>
<div className="mx-auto max-w-4xl mt-6">
<div className="mx-auto max-w-4xl mt-6 lg:px-0 px-4">
<P className="text-center" color="primary">
The future isnt about more tools. Its about one intelligent partner that can do it all. This is your gateway to creativity, automation, and discovery.
</P>
@@ -72,7 +70,7 @@ export function ClickableGallery() {
onMouseEnter={() => setHovering(true)}
onMouseLeave={() => setHovering(false)}
>
<div className="relative w-full max-w-[1800px] h-[500px]" style={{ perspective: '1600px' }}>
<div className="relative w-full max-w-[1800px] h-[300px] md:h-[500px]" style={{ perspective: '1600px' }}>
<div className="absolute inset-0" style={{ transformStyle: 'preserve-3d' }}>
<AnimatePresence initial={false}>
{indices.map((idx, i) => {
@@ -119,7 +117,8 @@ export function ClickableGallery() {
</div>
{/* Arrows */}
<div className="absolute inset-y-0 left-8 flex items-center z-50">
{/* Arrows */}
<div className="absolute inset-y-0 left-8 hidden md:flex items-center z-50">
<button
onClick={prev}
className="bg-transparent rounded-full p-2 shadow-lg backdrop-blur-md"
@@ -128,7 +127,7 @@ export function ClickableGallery() {
<svg className="size-8" viewBox="0 0 24 24" fill="none" dangerouslySetInnerHTML={{ __html: '<path d="M15 19L8 12l7-7" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>' }} />
</button>
</div>
<div className="absolute inset-y-0 right-8 flex items-center z-50">
<div className="absolute inset-y-0 right-8 hidden md:flex items-center z-50">
<button
onClick={next}
className="bg-transparent rounded-full p-2 shadow-lg backdrop-blur-md"
@@ -138,8 +137,8 @@ export function ClickableGallery() {
</button>
</div>
{/* Foreground pill */}
<div className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 z-[60]">
{/* Foreground pill (Desktop) */}
<div className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 z-[60] hidden md:block">
<div className="flex items-center justify-between w-[1040px] gap-6 rounded-2xl bg-black/30 shadow-[0_8px_40px_rgba(0,0,0,0.15)] px-12 backdrop-blur">
<CT as="h4" className="max-w-[820px] h-[72px] text-white flex items-center">
<TypeAnimation
@@ -150,12 +149,30 @@ export function ClickableGallery() {
repeat={0}
/>
</CT>
<Button href="#" color="cyan" className="text-sm px-4 py-2 lg:text-base">
Start
<Button href="#" color="cyan" className="text-sm px-4 py-2 lg:text-base whitespace-nowrap">
Start
</Button>
</div>
</div>
</section>
{/* Text box (Mobile) */}
<div className="md:hidden w-full px-4 -mt-12 mb-16">
<div className="flex flex-row items-center justify-between w-full gap-x-4 rounded-2xl bg-white/10 bg-opacity-80 p-4 backdrop-blur-md">
<CT as="h4" className="w-full text-left h-[72px] text-white leading-tight flex items-center">
<TypeAnimation
key={active}
sequence={[galleryItems[active].text]}
wrapper="span"
speed={50}
repeat={0}
/>
</CT>
<Button href="#" color="cyan" className="text-xs px-3 py-1.5 whitespace-nowrap">
Start
</Button>
</div>
</div>
</FadeIn>
</div>
);

View File

@@ -0,0 +1,39 @@
'use client'
import { useState, useEffect } from 'react';
// 🔧 Carousel Config
const desktopConfig = {
GAP: 300,
ROT_Y: 18,
DEPTH: 210,
SCALE_DROP: 0.12,
};
const mobileConfig = {
GAP: 100, // Smaller gap for mobile
ROT_Y: 0, // Flatter view on mobile
DEPTH: 150, // Less depth
SCALE_DROP: 0.1, // Less aggressive scaling
};
export const useResponsiveCarousel = () => {
const [config, setConfig] = useState(desktopConfig);
useEffect(() => {
const checkScreenSize = () => {
if (window.innerWidth < 768) {
setConfig(mobileConfig);
} else {
setConfig(desktopConfig);
}
};
checkScreenSize();
window.addEventListener('resize', checkScreenSize);
return () => window.removeEventListener('resize', checkScreenSize);
}, []);
return config;
};