Files
mip-website/pages/Gallery.js
T

357 lines
9.7 KiB
JavaScript

import fs from 'fs'
import path from 'path'
import React, { useEffect, useState } from 'react'
import Image from 'next/image'
import AppContainer from './appContainer'
const shimmer = (width, height) => `
<svg width="${width}" height="${height}" viewBox="0 0 ${width} ${height}" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="gradient">
<stop stop-color="#e5e7eb" offset="20%" />
<stop stop-color="#f9fafb" offset="50%" />
<stop stop-color="#e5e7eb" offset="70%" />
</linearGradient>
</defs>
<rect width="${width}" height="${height}" fill="#e5e7eb" />
<rect width="${width}" height="${height}" fill="url(#gradient)" />
</svg>
`
const toBase64 = value =>
typeof window === 'undefined' ? Buffer.from(value).toString('base64') : window.btoa(value)
const galleryBannerLoader = () => '/resource/parallax/Gallery.jpg'
const Gallery = ({ images }) => {
const [selectedImage, setSelectedImage] = useState(null)
const closeLightbox = () => setSelectedImage(null)
const showPreviousImage = () => {
setSelectedImage(current => (current === 0 ? images.length - 1 : current - 1))
}
const showNextImage = () => {
setSelectedImage(current => (current === images.length - 1 ? 0 : current + 1))
}
useEffect(() => {
if (selectedImage === null) return undefined
const handleKeyDown = event => {
if (event.key === 'Escape') setSelectedImage(null)
if (event.key === 'ArrowRight') {
setSelectedImage(current => (current === images.length - 1 ? 0 : current + 1))
}
if (event.key === 'ArrowLeft') {
setSelectedImage(current => (current === 0 ? images.length - 1 : current - 1))
}
}
const previousOverflow = document.body.style.overflow
document.body.style.overflow = 'hidden'
window.addEventListener('keydown', handleKeyDown)
return () => {
document.body.style.overflow = previousOverflow
window.removeEventListener('keydown', handleKeyDown)
}
}, [selectedImage, images.length])
return (
<AppContainer title='گالری بیمارستان مهر پرند'>
<style jsx>{`
.gallery-page {
direction: rtl;
padding: 3rem 1rem 5rem;
background: #fff;
}
.gallery-header {
max-width: 1200px;
margin: 0 auto 2rem;
text-align: right;
}
.gallery-header h2 {
margin: 0 0 0.5rem;
color: #263238;
font-size: 1.75rem;
font-weight: 700;
}
.gallery-header p {
margin: 0;
color: #6b7280;
font-size: 0.95rem;
}
.gallery-header::after {
content: '';
display: block;
width: 54px;
height: 3px;
margin-top: 0.9rem;
border-radius: 999px;
background: #009688;
}
.gallery-grid {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 1rem;
max-width: 1200px;
margin: 0 auto;
}
.gallery-card {
position: relative;
aspect-ratio: 16 / 9;
padding: 0;
overflow: hidden;
border: 1px solid #e5e7eb;
border-radius: 0.85rem;
background: #f3f4f6;
cursor: zoom-in;
box-shadow: 0 4px 14px rgba(15, 23, 42, 0.07);
transition: transform 180ms ease, box-shadow 180ms ease;
}
.gallery-card:hover,
.gallery-card:focus-visible {
transform: translateY(-3px);
box-shadow: 0 10px 24px rgba(15, 23, 42, 0.15);
outline: none;
}
.gallery-card :global(img) {
transition: transform 250ms ease;
}
.gallery-card:hover :global(img) {
transform: scale(1.035);
}
.lightbox {
position: fixed;
z-index: 1500;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
padding: 4.5rem 5rem 3rem;
background: rgba(0, 0, 0, 0.92);
}
.lightbox-image {
max-width: 100%;
max-height: calc(100vh - 7.5rem);
object-fit: contain;
border-radius: 0.4rem;
}
.lightbox-close,
.lightbox-navigation {
position: absolute;
display: flex;
align-items: center;
justify-content: center;
border: 0;
color: #fff;
background: rgba(255, 255, 255, 0.13);
cursor: pointer;
transition: background 150ms ease;
}
.lightbox-close:hover,
.lightbox-navigation:hover {
background: rgba(255, 255, 255, 0.25);
}
.lightbox-close {
top: 1.25rem;
right: 1.25rem;
width: 44px;
height: 44px;
border-radius: 50%;
font-size: 1.9rem;
line-height: 1;
}
.lightbox-navigation {
top: 50%;
width: 46px;
height: 58px;
border-radius: 0.5rem;
font-size: 2rem;
transform: translateY(-50%);
}
.lightbox-previous {
left: 1.25rem;
}
.lightbox-next {
right: 1.25rem;
}
.lightbox-counter {
position: absolute;
bottom: 1rem;
left: 50%;
margin: 0;
color: #fff;
font-size: 0.9rem;
transform: translateX(-50%);
}
@media (max-width: 992px) {
.gallery-grid {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
}
@media (max-width: 768px) {
.gallery-page {
padding: 2rem 1rem 4rem;
}
.gallery-grid {
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 0.75rem;
}
.lightbox {
padding: 4rem 1rem;
}
.lightbox-navigation {
top: auto;
bottom: 0.8rem;
transform: none;
}
.lightbox-previous {
left: 1rem;
}
.lightbox-next {
right: 1rem;
}
}
@media (max-width: 480px) {
.gallery-header h2 {
font-size: 1.4rem;
}
.gallery-grid {
grid-template-columns: 1fr;
}
}
`}</style>
<div className='parallax-container'>
<h1 className='center-align text-white header_back service-text dana font-weight-bold'>گالری بیمارستان</h1>
<Image
loader={galleryBannerLoader}
src='gallery-banner.jpg'
className='responsive-img shadow-5'
alt='گالری بیمارستان مهر پرند'
width={3000}
height={1300}
priority
/>
</div>
<main className='gallery-page'>
<header className='gallery-header'>
<h2>گالری تصاویر</h2>
<p>تصاویر بیمارستان مهر پرند</p>
</header>
<section className='gallery-grid' aria-label='تصاویر گالری بیمارستان'>
{images.map((image, index) => (
<button
type='button'
className='gallery-card'
key={image.id}
aria-label={`نمایش تصویر ${index + 1} از گالری`}
onClick={() => setSelectedImage(index)}
>
<Image
src={image.image}
alt={image.title}
layout='fill'
objectFit='cover'
placeholder='blur'
blurDataURL={`data:image/svg+xml;base64,${toBase64(shimmer(640, 360))}`}
/>
</button>
))}
</section>
</main>
{selectedImage !== null && (
<div className='lightbox' role='dialog' aria-modal='true' aria-label='نمایش تصویر گالری' onClick={closeLightbox}>
<button type='button' className='lightbox-close' aria-label='بستن تصویر' onClick={closeLightbox}>
&times;
</button>
<button
type='button'
className='lightbox-navigation lightbox-previous'
aria-label='تصویر قبلی'
onClick={event => {
event.stopPropagation()
showPreviousImage()
}}
>
&#8249;
</button>
<img
className='lightbox-image'
src={images[selectedImage].image}
alt={images[selectedImage].title}
onClick={event => event.stopPropagation()}
/>
<button
type='button'
className='lightbox-navigation lightbox-next'
aria-label='تصویر بعدی'
onClick={event => {
event.stopPropagation()
showNextImage()
}}
>
&#8250;
</button>
<p className='lightbox-counter'>
{selectedImage + 1} از {images.length}
</p>
</div>
)}
</AppContainer>
)
}
export async function getStaticProps() {
const galleryDirectory = path.join(process.cwd(), 'public', 'resource', 'images', 'gallery')
const fileNames = fs
.readdirSync(galleryDirectory)
.filter(fileName => /\.(jpe?g|png|webp)$/i.test(fileName))
.sort((first, second) => first.localeCompare(second, undefined, { numeric: true }))
const images = fileNames.map((fileName, index) => ({
id: fileName,
image: `/resource/images/gallery/${encodeURIComponent(fileName)}`,
title: `تصویر ${index + 1} از بیمارستان مهر پرند`
}))
return {
props: { images }
}
}
export default Gallery