Rocky_Mountain_Vending/components/image-carousel.tsx

137 lines
3.9 KiB
TypeScript

"use client"
import React, { useState, useEffect, useRef } from "react"
import { ChevronLeft, ChevronRight } from "lucide-react"
import { Card, CardContent } from "@/components/ui/card"
import Image from "next/image"
interface ImageCarouselProps {
images: Array<{
src: string
alt?: string
title?: string
}>
autoScrollInterval?: number
className?: string
}
export function ImageCarousel({
images,
autoScrollInterval = 3000,
className = "",
}: ImageCarouselProps) {
const [currentIndex, setCurrentIndex] = useState(0)
const scrollRef = useRef<HTMLDivElement>(null)
const itemWidth = 280
const gap = 16
// Auto-scroll functionality
useEffect(() => {
const timer = setInterval(() => {
setCurrentIndex((prevIndex) => {
const nextIndex = prevIndex + 1
return nextIndex >= images.length ? 0 : nextIndex
})
}, autoScrollInterval)
return () => clearInterval(timer)
}, [images.length, autoScrollInterval])
// Scroll to current index
useEffect(() => {
if (scrollRef.current) {
scrollRef.current.scrollTo({
left: currentIndex * (itemWidth + gap),
behavior: "smooth",
})
}
}, [currentIndex, itemWidth, gap])
const handlePrevious = () => {
setCurrentIndex((prevIndex) => {
const nextIndex = prevIndex - 1
return nextIndex < 0 ? images.length - 1 : nextIndex
})
}
const handleNext = () => {
setCurrentIndex((prevIndex) => {
const nextIndex = prevIndex + 1
return nextIndex >= images.length ? 0 : nextIndex
})
}
if (images.length === 0) {
return null
}
return (
<div className={`relative ${className}`}>
{/* Navigation buttons */}
<button
onClick={handlePrevious}
className="absolute left-0 top-1/2 -translate-y-1/2 z-10 bg-white/90 hover:bg-white border border-border/50 rounded-full p-2 shadow-lg transition-all hover:scale-110"
aria-label="Previous image"
>
<ChevronLeft className="h-5 w-5" />
</button>
<button
onClick={handleNext}
className="absolute right-0 top-1/2 -translate-y-1/2 z-10 bg-white/90 hover:bg-white border border-border/50 rounded-full p-2 shadow-lg transition-all hover:scale-110"
aria-label="Next image"
>
<ChevronRight className="h-5 w-5" />
</button>
{/* Carousel container */}
<div
ref={scrollRef}
className="flex overflow-x-auto gap-4 scroll-smooth snap-x snap-mandatory"
style={{
scrollbarWidth: "none",
msOverflowStyle: "none",
padding: "0.5rem 2.5rem",
}}
>
{images.map((img, index) => (
<div
key={index}
className="flex-shrink-0 snap-start"
style={{ width: `${itemWidth}px` }}
>
<Card className="overflow-hidden border-border/50 hover:border-secondary/50 transition-all">
<CardContent className="p-0">
<div className="relative aspect-square overflow-hidden">
<Image
src={img.src}
alt={img.alt || img.title || ""}
fill
className="object-cover"
sizes="(max-width: 640px) 280px, 280px"
/>
</div>
</CardContent>
</Card>
</div>
))}
</div>
{/* Dots indicator */}
<div className="flex justify-center gap-2 mt-4">
{images.map((_, index) => (
<button
key={index}
onClick={() => setCurrentIndex(index)}
className={`w-2 h-2 rounded-full transition-all ${
index === currentIndex
? "bg-primary w-6"
: "bg-muted-foreground/30"
}`}
aria-label={`Go to image ${index + 1}`}
/>
))}
</div>
</div>
)
}