feat: Complete Corporate Site Deployment Checkpoint
This commit is contained in:
95
src/components/public/hero-carousel.tsx
Normal file
95
src/components/public/hero-carousel.tsx
Normal file
@@ -0,0 +1,95 @@
|
||||
"use client"
|
||||
|
||||
import { useState, useEffect } from "react"
|
||||
import { GalleryItem } from "@/types/cms"
|
||||
import Image from "next/image"
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
interface HeroCarouselProps {
|
||||
images: GalleryItem[]
|
||||
children?: React.ReactNode
|
||||
}
|
||||
|
||||
export function HeroCarousel({ images, children }: HeroCarouselProps) {
|
||||
const [current, setCurrent] = useState(0)
|
||||
|
||||
useEffect(() => {
|
||||
if (images.length <= 1) return
|
||||
|
||||
const timer = setInterval(() => {
|
||||
setCurrent((prev) => (prev + 1) % images.length)
|
||||
}, 5000)
|
||||
|
||||
return () => clearInterval(timer)
|
||||
}, [images.length])
|
||||
|
||||
const getImageUrl = (item: GalleryItem) => {
|
||||
if (item.image_url) return item.image_url
|
||||
if (item.video_url) {
|
||||
const regExp = /^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|&v=)([^#&?]*).*/
|
||||
const match = item.video_url.match(regExp)
|
||||
const id = (match && match[2].length === 11) ? match[2] : null
|
||||
if (id) return `https://img.youtube.com/vi/${id}/maxresdefault.jpg`
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
// Fallback if no images
|
||||
if (!images || images.length === 0) {
|
||||
return (
|
||||
<div className="absolute inset-0 bg-slate-900">
|
||||
<div
|
||||
className="absolute inset-0 bg-cover bg-center opacity-50"
|
||||
style={{ backgroundImage: 'url("https://images.unsplash.com/photo-1519167758481-83f550bb49b3?q=80&w=2098&auto=format&fit=crop")' }}
|
||||
/>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{images.map((img, idx) => {
|
||||
const displayImage = getImageUrl(img)
|
||||
if (!displayImage) return null;
|
||||
|
||||
return (
|
||||
<div
|
||||
key={img.id}
|
||||
className={cn(
|
||||
"absolute inset-0 transition-opacity duration-1000",
|
||||
idx === current ? "opacity-100 z-0" : "opacity-0 -z-10"
|
||||
)}
|
||||
>
|
||||
<Image
|
||||
src={displayImage}
|
||||
alt="Hero Image"
|
||||
fill
|
||||
className="object-cover"
|
||||
priority={idx === 0}
|
||||
/>
|
||||
<div className="absolute inset-0 bg-black/40" />
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
|
||||
{/* Indicators */}
|
||||
{images.length > 1 && (
|
||||
<div className="absolute bottom-8 left-1/2 -translate-x-1/2 z-30 flex space-x-2">
|
||||
{images.map((_, idx) => (
|
||||
<button
|
||||
key={idx}
|
||||
onClick={() => setCurrent(idx)}
|
||||
className={cn(
|
||||
"w-2 h-2 rounded-full transition-all",
|
||||
idx === current ? "bg-white w-4" : "bg-white/50 hover:bg-white/80"
|
||||
)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{children}
|
||||
</>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user