74 lines
2.2 KiB
TypeScript
74 lines
2.2 KiB
TypeScript
'use client';
|
|
|
|
import React, { useState } from 'react';
|
|
import { RiImageLine } from '@remixicon/react';
|
|
import { cn } from '@/lib/utils';
|
|
import { Dialog, DialogContent, DialogTitle } from '@/components/ui/dialog';
|
|
|
|
interface ProductImage {
|
|
url: string;
|
|
altText?: string;
|
|
}
|
|
|
|
interface ProductDetailGalleryProps {
|
|
images: ProductImage[];
|
|
}
|
|
|
|
const ProductDetailGallery: React.FC<ProductDetailGalleryProps> = ({ images }) => {
|
|
const [zoomedIndex, setZoomedIndex] = useState<number | null>(null);
|
|
|
|
if (images.length === 0) {
|
|
return (
|
|
<div className="aspect-square bg-gray-100 flex items-center justify-center text-gray-400">
|
|
<RiImageLine size={60} />
|
|
</div>
|
|
);
|
|
}
|
|
|
|
const zoomedImage = zoomedIndex !== null ? images[zoomedIndex] : null;
|
|
|
|
return (
|
|
<>
|
|
<div className="grid grid-cols-2 gap-4">
|
|
{images.map((image, index) => (
|
|
<button
|
|
key={index}
|
|
onClick={() => setZoomedIndex(index)}
|
|
className={cn(
|
|
'aspect-square bg-gray-100 overflow-hidden cursor-zoom-in group',
|
|
// A lone image (or the last of an odd count) spans both columns to avoid a gap
|
|
images.length % 2 === 1 && index === images.length - 1 && 'col-span-2'
|
|
)}
|
|
aria-label={`Zoom image ${index + 1}`}
|
|
>
|
|
<img
|
|
src={image.url}
|
|
alt={image.altText || `Product image ${index + 1}`}
|
|
className="w-full h-full object-cover transition-transform duration-300 group-hover:scale-105"
|
|
/>
|
|
</button>
|
|
))}
|
|
</div>
|
|
|
|
{/* Zoom Lightbox */}
|
|
<Dialog
|
|
open={zoomedIndex !== null}
|
|
onOpenChange={(open) => !open && setZoomedIndex(null)}
|
|
>
|
|
<DialogContent className="max-w-4xl p-0 gap-0 overflow-hidden">
|
|
<DialogTitle className="sr-only">Product image</DialogTitle>
|
|
{zoomedImage && (
|
|
<img
|
|
src={zoomedImage.url}
|
|
alt={zoomedImage.altText || 'Product image'}
|
|
className="w-full h-auto max-h-[85vh] object-contain bg-gray-100"
|
|
/>
|
|
)}
|
|
</DialogContent>
|
|
</Dialog>
|
|
</>
|
|
);
|
|
};
|
|
|
|
export default ProductDetailGallery;
|