114 lines
3.4 KiB
TypeScript
114 lines
3.4 KiB
TypeScript
"use client";
|
|
|
|
import { useEffect, useRef } from "react";
|
|
import Image from "next/image";
|
|
import { ChevronLeft, ChevronRight, X } from "lucide-react";
|
|
|
|
import type { PrivateAlbum } from "@/data/dto/private-room";
|
|
|
|
import styles from "../private-room-screen.module.css";
|
|
|
|
export interface PrivateAlbumGalleryProps {
|
|
album: PrivateAlbum;
|
|
imageIndex: number;
|
|
onClose: () => void;
|
|
onImageIndexChange: (index: number) => void;
|
|
}
|
|
|
|
export function PrivateAlbumGallery({
|
|
album,
|
|
imageIndex,
|
|
onClose,
|
|
onImageIndexChange,
|
|
}: PrivateAlbumGalleryProps) {
|
|
const touchStartXRef = useRef<number | null>(null);
|
|
const currentImage = album.images[imageIndex];
|
|
const hasPrevious = imageIndex > 0;
|
|
const hasNext = imageIndex < album.images.length - 1;
|
|
|
|
useEffect(() => {
|
|
const handleKeyDown = (event: KeyboardEvent) => {
|
|
if (event.key === "Escape") onClose();
|
|
if (event.key === "ArrowLeft" && hasPrevious) {
|
|
onImageIndexChange(imageIndex - 1);
|
|
}
|
|
if (event.key === "ArrowRight" && hasNext) {
|
|
onImageIndexChange(imageIndex + 1);
|
|
}
|
|
};
|
|
document.addEventListener("keydown", handleKeyDown);
|
|
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
}, [hasNext, hasPrevious, imageIndex, onClose, onImageIndexChange]);
|
|
|
|
if (!currentImage?.url) return null;
|
|
|
|
const handleTouchEnd = (event: React.TouchEvent) => {
|
|
const touchStartX = touchStartXRef.current;
|
|
touchStartXRef.current = null;
|
|
if (touchStartX === null) return;
|
|
const delta = event.changedTouches[0]?.clientX ?? touchStartX;
|
|
const distance = delta - touchStartX;
|
|
if (distance > 48 && hasPrevious) onImageIndexChange(imageIndex - 1);
|
|
if (distance < -48 && hasNext) onImageIndexChange(imageIndex + 1);
|
|
};
|
|
|
|
return (
|
|
<div
|
|
className={styles.galleryOverlay}
|
|
role="dialog"
|
|
aria-modal="true"
|
|
aria-label={album.title || "Private album gallery"}
|
|
onTouchStart={(event) => {
|
|
touchStartXRef.current = event.touches[0]?.clientX ?? null;
|
|
}}
|
|
onTouchEnd={handleTouchEnd}
|
|
>
|
|
<button
|
|
type="button"
|
|
className={styles.galleryClose}
|
|
onClick={onClose}
|
|
aria-label="Close private album gallery"
|
|
>
|
|
<X size={24} aria-hidden="true" />
|
|
</button>
|
|
|
|
<div className={styles.galleryImageFrame}>
|
|
<Image
|
|
src={currentImage.url}
|
|
alt={`${album.title || "Private album"} photo ${imageIndex + 1}`}
|
|
fill
|
|
priority
|
|
draggable={false}
|
|
sizes="(max-width: 540px) 100vw, 540px"
|
|
className={styles.galleryImage}
|
|
/>
|
|
</div>
|
|
|
|
<span className={styles.galleryCounter}>
|
|
{imageIndex + 1} / {album.images.length}
|
|
</span>
|
|
|
|
{hasPrevious ? (
|
|
<button
|
|
type="button"
|
|
className={`${styles.galleryNav} ${styles.galleryPrevious}`}
|
|
onClick={() => onImageIndexChange(imageIndex - 1)}
|
|
aria-label="Previous photo"
|
|
>
|
|
<ChevronLeft size={30} aria-hidden="true" />
|
|
</button>
|
|
) : null}
|
|
{hasNext ? (
|
|
<button
|
|
type="button"
|
|
className={`${styles.galleryNav} ${styles.galleryNext}`}
|
|
onClick={() => onImageIndexChange(imageIndex + 1)}
|
|
aria-label="Next photo"
|
|
>
|
|
<ChevronRight size={30} aria-hidden="true" />
|
|
</button>
|
|
) : null}
|
|
</div>
|
|
);
|
|
}
|