Untitled
unknown
plain_text
10 months ago
5.6 kB
16
Indexable
// components/hero-designs/CarouselHero.tsx
import { useState, useEffect } from "react";
import { motion, AnimatePresence } from "framer-motion";
import { ChevronDown, ChevronLeft, ChevronRight } from "lucide-react";
import { ClientConfig } from "@/lib/client-config";
import { getR2ImageUrl } from "@/lib/r2";
interface CarouselHeroProps {
config: ClientConfig;
}
export function CarouselHero({ config }: CarouselHeroProps) {
const heroConfig = config.welcomeHero || {};
const images = heroConfig.images || [config.highlightImage];
// Get processed image URLs
const imageUrls = images.map(img =>
img.startsWith('http')
? img
: getR2ImageUrl(config.id, img.split('/').pop() || '')
);
const [currentIndex, setCurrentIndex] = useState(0);
const overlayOpacity = heroConfig.overlayOpacity ?? 0.4;
const textColor = heroConfig.textColor === 'dark' ? 'text-gray-800' : 'text-white';
// Auto advance carousel
useEffect(() => {
if (imageUrls.length <= 1) return;
const interval = setInterval(() => {
setCurrentIndex((prevIndex) => (prevIndex + 1) % imageUrls.length);
}, 5000);
return () => clearInterval(interval);
}, [imageUrls.length]);
const nextSlide = () => {
setCurrentIndex((prevIndex) => (prevIndex + 1) % imageUrls.length);
};
const prevSlide = () => {
setCurrentIndex((prevIndex) =>
prevIndex === 0 ? imageUrls.length - 1 : prevIndex - 1
);
};
return (
<div className="relative h-screen w-full overflow-hidden">
{/* Carousel Images */}
<AnimatePresence mode="wait">
<motion.div
key={currentIndex}
className="absolute inset-0"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 1 }}
>
<img
src={imageUrls[currentIndex]}
alt={`Slide ${currentIndex + 1}`}
className="h-full w-full object-cover"
/>
<div
className="absolute inset-0 bg-black"
style={{ opacity: overlayOpacity }}
/>
</motion.div>
</AnimatePresence>
{/* Navigation Arrows (only if multiple images) */}
{imageUrls.length > 1 && (
<>
<button
className="absolute left-4 top-1/2 z-10 -translate-y-1/2 rounded-full bg-black/30 p-2 text-white backdrop-blur-sm transition-all hover:bg-black/50"
onClick={prevSlide}
>
<ChevronLeft className="h-6 w-6" />
</button>
<button
className="absolute right-4 top-1/2 z-10 -translate-y-1/2 rounded-full bg-black/30 p-2 text-white backdrop-blur-sm transition-all hover:bg-black/50"
onClick={nextSlide}
>
<ChevronRight className="h-6 w-6" />
</button>
</>
)}
{/* Content */}
<div className="relative z-10 flex h-full flex-col items-center justify-center px-4 text-center">
{heroConfig.showLogo && heroConfig.logoUrl && (
<motion.img
src={heroConfig.logoUrl}
alt="Logo"
className="mb-8 h-16 w-auto"
initial={{ opacity: 0, y: -20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.8, delay: 0.2 }}
/>
)}
<motion.h1
className={`${textColor} mb-4 max-w-4xl text-4xl font-bold sm:text-5xl md:text-6xl lg:text-7xl`}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.8, delay: 0.4 }}
>
{config.title}
</motion.h1>
{heroConfig.subtitle && (
<motion.p
className={`${textColor} max-w-xl text-lg sm:text-xl`}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.8, delay: 0.6 }}
>
{heroConfig.subtitle}
</motion.p>
)}
<motion.div
className={`${textColor} mt-6 text-lg sm:text-xl`}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.8, delay: 0.8 }}
>
{config.date}
</motion.div>
{/* Indicators */}
{imageUrls.length > 1 && (
<div className="absolute bottom-16 left-0 right-0 flex justify-center gap-2">
{imageUrls.map((_, index) => (
<button
key={index}
className={`h-2 w-2 rounded-full ${
index === currentIndex ? 'bg-white' : 'bg-white/50'
}`}
onClick={() => setCurrentIndex(index)}
/>
))}
</div>
)}
</div>
{/* Scroll Indicator */}
{(heroConfig.scrollIndicator ?? true) && (
<motion.div
className="absolute bottom-8 left-0 right-0 flex justify-center"
initial={{ opacity: 0, y: -20 }}
animate={{ opacity: 1, y: 0 }}
transition={{
duration: 0.8,
delay: 1,
repeat: Infinity,
repeatType: "reverse",
repeatDelay: 0.5
}}
>
<ChevronDown className={`${textColor} h-8 w-8`} />
</motion.div>
)}
</div>
);
}Editor is loading...
Leave a Comment