diff --git a/components/Lightbox.tsx b/components/Lightbox.tsx index 03480181..861d298c 100644 --- a/components/Lightbox.tsx +++ b/components/Lightbox.tsx @@ -3,6 +3,8 @@ import React, { useEffect, useState, useCallback } from 'react'; import Image from 'next/image'; import { createPortal } from 'react-dom'; +import { motion, AnimatePresence } from 'framer-motion'; +import { useRouter, useSearchParams, usePathname } from 'next/navigation'; interface LightboxProps { isOpen: boolean; @@ -12,6 +14,9 @@ interface LightboxProps { } export default function Lightbox({ isOpen, images, initialIndex, onClose }: LightboxProps) { + const router = useRouter(); + const searchParams = useSearchParams(); + const pathname = usePathname(); const [currentIndex, setCurrentIndex] = useState(initialIndex); const [mounted, setMounted] = useState(false); @@ -20,19 +25,53 @@ export default function Lightbox({ isOpen, images, initialIndex, onClose }: Ligh return () => setMounted(false); }, []); + const updateUrl = useCallback((index: number | null) => { + const params = new URLSearchParams(searchParams.toString()); + if (index !== null) { + params.set('photo', index.toString()); + } else { + params.delete('photo'); + } + router.replace(`${pathname}?${params.toString()}`, { scroll: false }); + }, [pathname, router, searchParams]); + const prevImage = useCallback(() => { - setCurrentIndex((prev) => (prev === 0 ? images.length - 1 : prev - 1)); - }, [images.length]); + setCurrentIndex((prev) => { + const next = prev === 0 ? images.length - 1 : prev - 1; + updateUrl(next); + return next; + }); + }, [images.length, updateUrl]); const nextImage = useCallback(() => { - setCurrentIndex((prev) => (prev === images.length - 1 ? 0 : prev + 1)); - }, [images.length]); + setCurrentIndex((prev) => { + const next = prev === images.length - 1 ? 0 : prev + 1; + updateUrl(next); + return next; + }); + }, [images.length, updateUrl]); + + useEffect(() => { + const photoParam = searchParams.get('photo'); + if (photoParam !== null) { + const index = parseInt(photoParam, 10); + if (!isNaN(index) && index >= 0 && index < images.length) { + setCurrentIndex(index); + } + } + }, [searchParams, images.length]); + + useEffect(() => { + if (isOpen) { + updateUrl(currentIndex); + } + }, [isOpen, currentIndex, updateUrl]); useEffect(() => { if (!isOpen) return; const handleKeyDown = (e: KeyboardEvent) => { - if (e.key === 'Escape') onClose(); + if (e.key === 'Escape') handleClose(); if (e.key === 'ArrowLeft') prevImage(); if (e.key === 'ArrowRight') nextImage(); }; @@ -47,59 +86,119 @@ export default function Lightbox({ isOpen, images, initialIndex, onClose }: Ligh document.body.style.overflow = originalStyle; window.removeEventListener('keydown', handleKeyDown); }; - }, [isOpen, onClose, prevImage, nextImage]); + }, [isOpen, prevImage, nextImage]); - if (!isOpen || !mounted) return null; + if (!mounted) return null; + + const handleClose = () => { + updateUrl(null); + onClose(); + }; return createPortal( -
- - - - - + + {isOpen && ( +
+ -
- {`Gallery -
- {currentIndex + 1} / {images.length} + +
+ × +
+
+ + + + + + + + + + +
+
+ + + {`Gallery + + + + {/* Technical Detail: Subtle grid overlay to reinforce industrial precision */} +
+ + {/* Premium Reflection: Subtle gradient to give material feel */} +
+
+ + +
+
+ {currentIndex + 1} / {images.length} +
+
+ +
+
-
- - {/* Backdrop click to close */} -
-
, + )} + , document.body ); -} \ No newline at end of file +} diff --git a/components/home/GallerySection.tsx b/components/home/GallerySection.tsx index 77601fea..4eb7b3c7 100644 --- a/components/home/GallerySection.tsx +++ b/components/home/GallerySection.tsx @@ -1,13 +1,15 @@ 'use client'; -import React, { useState } from 'react'; +import React, { useState, useEffect } from 'react'; import Image from 'next/image'; import { useTranslations } from 'next-intl'; import { Section, Container, Heading } from '../../components/ui'; import Lightbox from '../Lightbox'; +import { useSearchParams } from 'next/navigation'; export default function GallerySection() { const t = useTranslations('Home.gallery'); + const searchParams = useSearchParams(); const images = [ '/uploads/2024/12/DSC07433-Large-600x400.webp', '/uploads/2024/12/DSC07460-Large-600x400.webp', @@ -20,6 +22,17 @@ export default function GallerySection() { const [lightboxOpen, setLightboxOpen] = useState(false); const [lightboxIndex, setLightboxIndex] = useState(0); + useEffect(() => { + const photoParam = searchParams.get('photo'); + if (photoParam !== null) { + const index = parseInt(photoParam, 10); + if (!isNaN(index) && index >= 0 && index < images.length) { + setLightboxIndex(index); + setLightboxOpen(true); + } + } + }, [searchParams, images.length]); + return (