313 lines
7.8 KiB
TypeScript
313 lines
7.8 KiB
TypeScript
"use client";
|
|
|
|
|
|
|
|
import { useState } from "react";
|
|
|
|
import Image from "next/image";
|
|
|
|
|
|
|
|
interface PropertyGalleryProps {
|
|
|
|
images: string[];
|
|
|
|
title: string;
|
|
|
|
}
|
|
|
|
|
|
|
|
export default function PropertyGallery({ images, title }: PropertyGalleryProps) {
|
|
|
|
const [activeImage, setActiveImage] = useState(0);
|
|
|
|
const [showLightbox, setShowLightbox] = useState(false);
|
|
|
|
const [lightboxIndex, setLightboxIndex] = useState(0);
|
|
|
|
|
|
|
|
const openLightbox = (index: number) => {
|
|
|
|
setLightboxIndex(index);
|
|
|
|
setShowLightbox(true);
|
|
|
|
document.body.style.overflow = 'hidden';
|
|
|
|
};
|
|
|
|
|
|
|
|
const closeLightbox = () => {
|
|
|
|
setShowLightbox(false);
|
|
|
|
document.body.style.overflow = 'unset';
|
|
|
|
};
|
|
|
|
|
|
|
|
// Ensure we have at least 5 images for the gallery (1 big + 4 small)
|
|
|
|
const displayImages = [...images];
|
|
|
|
while (displayImages.length < 5) {
|
|
|
|
displayImages.push("/assets/images/image.png");
|
|
|
|
}
|
|
|
|
|
|
|
|
const nextImage = () => {
|
|
|
|
setLightboxIndex((prev) => (prev + 1) % displayImages.length);
|
|
|
|
};
|
|
|
|
|
|
|
|
const prevImage = () => {
|
|
|
|
setLightboxIndex((prev) => (prev - 1 + displayImages.length) % displayImages.length);
|
|
|
|
};
|
|
|
|
|
|
|
|
return (
|
|
|
|
<>
|
|
|
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 mb-8">
|
|
|
|
<div className="md:col-span-2 relative h-[500px] rounded-2xl overflow-hidden group cursor-pointer" onClick={() => openLightbox(activeImage)}>
|
|
|
|
<Image
|
|
|
|
src={displayImages[activeImage]}
|
|
|
|
alt={title}
|
|
|
|
fill
|
|
|
|
className="object-cover transition-transform duration-300 group-hover:scale-105"
|
|
|
|
/>
|
|
|
|
<div className="absolute inset-0 bg-black/0 group-hover:bg-black/10 transition-colors duration-300" />
|
|
|
|
<div className="absolute bottom-4 right-4 bg-black/70 text-white px-3 py-1.5 rounded-lg text-sm opacity-0 group-hover:opacity-100 transition-opacity">
|
|
|
|
Click to enlarge
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div className="grid grid-cols-2 grid-rows-2 gap-4 h-[500px]">
|
|
|
|
{displayImages.slice(0, 3).map((img, idx) => (
|
|
|
|
<div
|
|
|
|
key={idx}
|
|
|
|
onClick={() => setActiveImage(idx)}
|
|
|
|
className={`relative w-full h-full rounded-xl overflow-hidden cursor-pointer transition-all duration-300 ${activeImage === idx ? 'ring-4 ring-primary scale-95' : 'hover:scale-95'
|
|
|
|
}`}
|
|
|
|
>
|
|
|
|
<Image src={img} alt={`View ${idx + 1}`} fill className="object-cover" />
|
|
|
|
</div>
|
|
|
|
))}
|
|
|
|
{/* View All Photos Button (4th slot) */}
|
|
|
|
<div
|
|
|
|
onClick={() => openLightbox(0)}
|
|
|
|
className="relative w-full h-full rounded-xl overflow-hidden cursor-pointer group"
|
|
|
|
>
|
|
|
|
<Image
|
|
|
|
src={displayImages[3]}
|
|
|
|
alt="View all photos"
|
|
|
|
fill
|
|
|
|
className="object-cover"
|
|
|
|
/>
|
|
|
|
<div className="absolute inset-0 bg-black/60 group-hover:bg-black/70 transition-colors flex flex-col items-center justify-center text-white">
|
|
|
|
<svg className="w-10 h-10 mb-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" />
|
|
|
|
</svg>
|
|
|
|
<span className="font-semibold text-lg">View All Photos</span>
|
|
|
|
<span className="text-sm opacity-90">({displayImages.length} images)</span>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
{/* Lightbox Modal */}
|
|
|
|
{showLightbox && (
|
|
|
|
<div className="fixed inset-0 z-50 bg-black/95 flex items-center justify-center">
|
|
|
|
{/* Close Button */}
|
|
|
|
<button
|
|
|
|
onClick={closeLightbox}
|
|
|
|
className="absolute top-4 right-4 text-white hover:text-gray-300 transition-colors z-10"
|
|
|
|
aria-label="Close gallery"
|
|
|
|
>
|
|
|
|
<svg className="w-10 h-10" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
|
|
|
|
</svg>
|
|
|
|
</button>
|
|
|
|
|
|
|
|
{/* Image Counter */}
|
|
|
|
<div className="absolute top-4 left-1/2 -translate-x-1/2 text-white text-lg font-medium bg-black/50 px-4 py-2 rounded-full">
|
|
|
|
{lightboxIndex + 1} / {displayImages.length}
|
|
|
|
</div>
|
|
|
|
|
|
|
|
{/* Previous Button */}
|
|
|
|
<button
|
|
|
|
onClick={prevImage}
|
|
|
|
className="absolute left-4 text-white hover:text-gray-300 transition-colors p-2 bg-black/50 rounded-full hover:bg-black/70"
|
|
|
|
aria-label="Previous image"
|
|
|
|
>
|
|
|
|
<svg className="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
|
|
|
|
</svg>
|
|
|
|
</button>
|
|
|
|
|
|
|
|
{/* Main Image */}
|
|
|
|
<div className="relative w-full h-full max-w-6xl max-h-[90vh] mx-4">
|
|
|
|
<Image
|
|
|
|
src={displayImages[lightboxIndex]}
|
|
|
|
alt={`${title} - Image ${lightboxIndex + 1}`}
|
|
|
|
fill
|
|
|
|
className="object-contain"
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
{/* Next Button */}
|
|
|
|
<button
|
|
|
|
onClick={nextImage}
|
|
|
|
className="absolute right-4 text-white hover:text-gray-300 transition-colors p-2 bg-black/50 rounded-full hover:bg-black/70"
|
|
|
|
aria-label="Next image"
|
|
|
|
>
|
|
|
|
<svg className="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
|
|
|
|
</svg>
|
|
|
|
</button>
|
|
|
|
|
|
|
|
{/* Thumbnail Strip */}
|
|
|
|
<div className="absolute bottom-4 left-1/2 -translate-x-1/2 flex gap-2 overflow-x-auto max-w-[90vw] px-4 py-2 bg-black/50 rounded-lg hide-scrollbar">
|
|
|
|
{displayImages.map((img, idx) => (
|
|
|
|
<div
|
|
|
|
key={idx}
|
|
|
|
onClick={() => setLightboxIndex(idx)}
|
|
|
|
className={`relative w-20 h-20 flex-shrink-0 rounded-lg overflow-hidden cursor-pointer transition-all ${lightboxIndex === idx ? 'ring-4 ring-white scale-110' : 'opacity-60 hover:opacity-100'
|
|
|
|
}`}
|
|
|
|
>
|
|
|
|
<Image src={img} alt={`Thumbnail ${idx + 1}`} fill className="object-cover" />
|
|
|
|
</div>
|
|
|
|
))}
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
)}
|
|
|
|
</>
|
|
|
|
);
|
|
|
|
} |