first commit
This commit is contained in:
@@ -0,0 +1,188 @@
|
||||
'use client';
|
||||
|
||||
import { ImageMeta } from '@/interfaces/image';
|
||||
import styles from '@/styles/Gallery.module.scss';
|
||||
import Image from 'next/image';
|
||||
import { useRouter as useNavigationRouter, useSearchParams } from 'next/navigation';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
interface GalleryProps {
|
||||
initialImages: ImageMeta[];
|
||||
}
|
||||
|
||||
export default function Gallery({ initialImages }: GalleryProps) {
|
||||
const HORIZONTAL_ASPECT_RATIO = 1.5;
|
||||
const VERTICAL_ASPECT_RATIO = 0.9;
|
||||
|
||||
const navigationRouter = useNavigationRouter();
|
||||
const searchParams = useSearchParams();
|
||||
const [images, setImages] = useState<ImageMeta[]>([]);
|
||||
const [columnCount, setColumnCount] = useState(0);
|
||||
const [fullScreenImage, setFullScreenImage] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const updateColumns = () => {
|
||||
const newImages = [...initialImages];
|
||||
|
||||
if (newImages.length > 0) {
|
||||
let usedColumnsInRow = 0;
|
||||
let usedRowsInColumn = 0;
|
||||
let index = 0;
|
||||
|
||||
for (const image of newImages) {
|
||||
usedColumnsInRow += image.aspect_ratio > HORIZONTAL_ASPECT_RATIO ? 2 : 1;
|
||||
usedRowsInColumn += image.aspect_ratio < VERTICAL_ASPECT_RATIO ? 2 : 1;
|
||||
|
||||
if (usedColumnsInRow > columnCount) {
|
||||
const nextViableImage: ImageMeta | undefined = newImages.slice(index).find((img) => {
|
||||
const imgAspectRatio = img.aspect_ratio;
|
||||
return imgAspectRatio <= HORIZONTAL_ASPECT_RATIO && image.aspect_ratio >= VERTICAL_ASPECT_RATIO;
|
||||
});
|
||||
|
||||
if (nextViableImage) {
|
||||
newImages.splice(index, 1);
|
||||
newImages.splice(index - 1, 0, nextViableImage);
|
||||
|
||||
usedColumnsInRow -= nextViableImage.aspect_ratio > HORIZONTAL_ASPECT_RATIO ? 2 : 1;
|
||||
usedRowsInColumn -= nextViableImage.aspect_ratio < VERTICAL_ASPECT_RATIO ? 2 : 1;
|
||||
}
|
||||
} else if (usedColumnsInRow === columnCount) {
|
||||
usedColumnsInRow = usedRowsInColumn;
|
||||
usedRowsInColumn = 0;
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
setImages(newImages);
|
||||
}
|
||||
};
|
||||
|
||||
const updateColumnCount = () => {
|
||||
const container = document.querySelector(`.${styles.images}`);
|
||||
if (!container) return;
|
||||
|
||||
const newColumnCount = window
|
||||
.getComputedStyle(container)
|
||||
.getPropertyValue('grid-template-columns')
|
||||
.split(' ').length;
|
||||
|
||||
if (newColumnCount !== columnCount) {
|
||||
setColumnCount(newColumnCount);
|
||||
}
|
||||
};
|
||||
|
||||
setImages([...initialImages]);
|
||||
updateColumns();
|
||||
updateColumnCount();
|
||||
window.addEventListener('resize', updateColumnCount);
|
||||
return () => window.removeEventListener('resize', updateColumnCount);
|
||||
}, [columnCount, initialImages]);
|
||||
|
||||
useEffect(() => {
|
||||
const imagePath = searchParams.get('image');
|
||||
|
||||
if (imagePath) {
|
||||
setFullScreenImage(`/images/${imagePath}`);
|
||||
}
|
||||
}, [searchParams]);
|
||||
|
||||
useEffect(() => {
|
||||
if (fullScreenImage) {
|
||||
const params = new URLSearchParams(searchParams.toString());
|
||||
const relativePath = fullScreenImage.replace('/images/', '');
|
||||
params.set('image', relativePath);
|
||||
navigationRouter.replace(`?${params.toString()}`, { scroll: false });
|
||||
|
||||
document.body.style.overflow = 'hidden';
|
||||
} else {
|
||||
const params = new URLSearchParams(searchParams.toString());
|
||||
params.delete('image');
|
||||
navigationRouter.replace(`?${params.toString()}`, { scroll: false });
|
||||
|
||||
document.body.style.overflow = '';
|
||||
}
|
||||
|
||||
return () => {
|
||||
document.body.style.overflow = '';
|
||||
};
|
||||
}, [fullScreenImage, searchParams, navigationRouter]);
|
||||
|
||||
useEffect(() => {
|
||||
const params = new URLSearchParams(searchParams.toString());
|
||||
navigationRouter.replace(`?${params.toString()}`, { scroll: false });
|
||||
}, [searchParams, navigationRouter]);
|
||||
|
||||
return (
|
||||
<div className={styles.gallery}>
|
||||
<div className={styles.images}>
|
||||
{images.map((image: ImageMeta) => (
|
||||
<div
|
||||
key={uuidv4()}
|
||||
className={`${styles.imagesContainer} ${
|
||||
image.aspect_ratio > HORIZONTAL_ASPECT_RATIO
|
||||
? styles.horizontal
|
||||
: image.aspect_ratio < VERTICAL_ASPECT_RATIO
|
||||
? styles.vertical
|
||||
: ''
|
||||
}`}>
|
||||
<Image
|
||||
width={image.aspect_ratio > HORIZONTAL_ASPECT_RATIO ? image.width : 700}
|
||||
height={image.aspect_ratio < VERTICAL_ASPECT_RATIO ? image.height : 700}
|
||||
loading="lazy"
|
||||
src={`/images/${image.relative_path}`}
|
||||
alt={image.aspect_ratio?.toString()}
|
||||
onClick={() => setFullScreenImage(`/images/${image.relative_path}`)}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{fullScreenImage && (
|
||||
<div className={styles.fullscreenModal} onClick={() => setFullScreenImage(null)}>
|
||||
<Image
|
||||
src={fullScreenImage}
|
||||
alt="Full Screen"
|
||||
width={1920}
|
||||
height={1080}
|
||||
style={{ objectFit: 'contain' }}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
/>
|
||||
<button
|
||||
className={styles.closeButton}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
setFullScreenImage(null);
|
||||
}}>
|
||||
×
|
||||
</button>
|
||||
|
||||
<button
|
||||
className={`${styles.arrowButton} ${styles.arrowButtonLeft}`}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
const currentIndex = images.findIndex((image) => `/images/${image.relative_path}` === fullScreenImage);
|
||||
if (currentIndex > 0) {
|
||||
setFullScreenImage(`/images/${images[currentIndex - 1].relative_path}`);
|
||||
}
|
||||
}}>
|
||||
‹
|
||||
</button>
|
||||
|
||||
<button
|
||||
className={`${styles.arrowButton} ${styles.arrowButtonRight}`}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
const currentIndex = images.findIndex((image) => `/images/${image.relative_path}` === fullScreenImage);
|
||||
if (currentIndex < images.length - 1) {
|
||||
setFullScreenImage(`/images/${images[currentIndex + 1].relative_path}`);
|
||||
}
|
||||
}}>
|
||||
›
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user