x
This commit is contained in:
1 parent
5d111b2866
commit
62dec90f82
2 files changed
+103
-17
No files matched your search
@@ -1,4 +1,4 @@
|
||||
import { useState } from 'react'
|
||||
import { useState, useRef, useEffect } from 'react'
|
||||
import type { ProductImage } from '../types'
|
||||
|
||||
interface ProductImageGalleryProps {
|
||||
@@ -10,6 +10,29 @@ interface ProductImageGalleryProps {
|
||||
|
||||
export default function ProductImageGallery({ images, fallback, alt = 'Product Image', badgeText }: ProductImageGalleryProps) {
|
||||
const [currentIndex, setCurrentIndex] = useState(0)
|
||||
const scrollRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
useEffect(() => {
|
||||
if (scrollRef.current) {
|
||||
const container = scrollRef.current
|
||||
const child = container.children[currentIndex] as HTMLElement
|
||||
if (child) {
|
||||
container.scrollTo({ left: child.offsetLeft, behavior: 'smooth' })
|
||||
}
|
||||
}
|
||||
}, [currentIndex])
|
||||
|
||||
const handleScroll = (e: React.UIEvent<HTMLDivElement>) => {
|
||||
const container = e.currentTarget
|
||||
const scrollLeft = container.scrollLeft
|
||||
const width = container.clientWidth
|
||||
if (width > 0) {
|
||||
const newIndex = Math.round(scrollLeft / width)
|
||||
if (newIndex !== currentIndex && newIndex >= 0 && newIndex < (images?.length || 0)) {
|
||||
setCurrentIndex(newIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort images by order if they exist, else just use the array
|
||||
const sortedImages = images?.length ? [...images].sort((a, b) => (a.order || 0) - (b.order || 0)) : []
|
||||
@@ -60,18 +83,38 @@ export default function ProductImageGallery({ images, fallback, alt = 'Product I
|
||||
|
||||
{/* Main Image */}
|
||||
<div className="relative aspect-square w-full flex-1 overflow-hidden rounded-3xl bg-pop-bg shadow-sm border border-pop-black/10 flex items-center justify-center">
|
||||
<img
|
||||
key={currentIndex}
|
||||
src={sortedImages[currentIndex].url}
|
||||
className="w-full h-full object-cover animate-fade-in"
|
||||
alt={alt}
|
||||
onError={(e) => { (e.target as HTMLImageElement).src = fallback }}
|
||||
/>
|
||||
<div
|
||||
ref={scrollRef}
|
||||
onScroll={handleScroll}
|
||||
className="flex w-full h-full overflow-x-auto snap-x snap-mandatory hide-scrollbar"
|
||||
>
|
||||
{sortedImages.map((img, idx) => (
|
||||
<div key={idx} className="relative w-full h-full flex-none snap-center snap-always block">
|
||||
<img
|
||||
src={img.url}
|
||||
alt={`${alt} ${idx + 1}`}
|
||||
className="absolute inset-0 w-full h-full object-cover"
|
||||
onError={(e) => { (e.target as HTMLImageElement).src = fallback }}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{badgeText && (
|
||||
<div className="absolute top-6 left-6 bg-pop-bg backdrop-blur-sm px-3 py-1.5 rounded-full text-[10px] font-bold uppercase tracking-widest text-pop-pink shadow-sm pointer-events-none z-10">
|
||||
{badgeText}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Mobile Dots */}
|
||||
<div className="md:hidden absolute bottom-4 left-0 right-0 flex justify-center gap-2 z-10 pointer-events-none">
|
||||
{sortedImages.map((_, idx) => (
|
||||
<div
|
||||
key={idx}
|
||||
className={`w-1.5 h-1.5 rounded-full shadow-[0_1px_2px_rgba(0,0,0,0.3)] transition-all ${currentIndex === idx ? 'bg-white scale-125' : 'bg-white/50'}`}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
Reference in new issue
Block a user