neroshitron/components/ui/gallery.tsx

218 lines
8.7 KiB
TypeScript
Raw Permalink Normal View History

2024-05-26 02:14:59 -04:00
import { use, useState } from 'react';
import { useEffect } from 'react';
2024-05-26 14:09:30 -04:00
import { render } from 'react-dom';
2024-05-26 02:14:59 -04:00
import Masonry from 'react-masonry-css';
2024-05-26 19:49:20 -04:00
import PanZoom from 'react-easy-panzoom';
2024-05-26 02:14:59 -04:00
interface GalleryProps {
2024-05-26 13:08:59 -04:00
id: string;
2024-05-26 16:58:48 -04:00
columns: number;
2024-05-26 02:14:59 -04:00
closeMenu: () => void;
}
2024-05-26 16:58:48 -04:00
const Gallery = ({ id, columns, closeMenu }: GalleryProps) => {
2024-05-26 02:14:59 -04:00
const [isSingle, setIsSingle] = useState<boolean>(false);
const [loaded, setLoaded] = useState({})
const [selectedImage, setSelectedImage] = useState<string | null>(null);
const [images, setImages] = useState<string[]>([]);
2024-05-26 13:08:59 -04:00
const [galleryId, setGalleryId] = useState(id as string);
2024-05-26 16:36:45 -04:00
const [currentIndex, setCurrentIndex] = useState(0);
2024-05-26 02:14:59 -04:00
const getData = async () => {
2024-05-26 16:45:17 -04:00
const thumbnailResponse = await fetch('/api/galleries/' + String(galleryId) + '/images');
const thumbnailUrl = await thumbnailResponse.json() as string[];
setImages(thumbnailUrl);
2024-05-26 02:14:59 -04:00
}
2024-05-26 16:45:17 -04:00
const next = () => {
if (currentIndex < images.length - 1) {
setCurrentIndex(currentIndex + 1);
} else {
setCurrentIndex(0);
}
2024-05-26 16:36:45 -04:00
}
2024-05-26 16:45:17 -04:00
const previous = () => {
if (currentIndex > 0) {
setCurrentIndex(currentIndex - 1);
} else {
setCurrentIndex(images.length - 1);
}
2024-05-26 16:36:45 -04:00
}
2024-05-26 16:45:17 -04:00
const renderButtons = () => {
return (
<>
<div className="z-20 fixed left-6 bottom-4 w-100 h-20 bg-purple-900 bg-opacity-40 animate-in rounded-3xl" style={{ backdropFilter: 'blur(10px)' }}>
<div className='grid grid-cols-4 gap-4 pl-32 pr-4 pt-4 m-1 '>
<button
className={`justify-center text-center w-full animate-in animate-once animate-duration-1000 animate-ease-out animate-reverse mb-4 py-2 px-4 rounded-lg no-underline flex items-center z-50 ${!selectedImage ? 'opacity-50 cursor-not-allowed bg-gray-800' : 'bg-pink-700 hover:bg-pink-600'}`}
onClick={() => previous()}
disabled={!selectedImage}
>
Previous
</button>
<button
className={`justify-center text-center w-full animate-in animate-once animate-duration-1000 animate-ease-out animate-reverse mb-4 py-2 px-4 rounded-lg no-underline flex items-center z-50 ${!selectedImage ? 'opacity-50 cursor-not-allowed bg-gray-800' : 'bg-pink-700 hover:bg-pink-600'}`}
onClick={() => next()}
disabled={!selectedImage}
>
Next
</button>
<button
className={`justify-center text-center w-full animate-in animate-once animate-duration-1000 animate-ease-out animate-reverse mb-4 py-2 px-4 rounded-lg no-underline flex items-center z-50 ${!selectedImage ? 'opacity-50 cursor-not-allowed bg-gray-800' : 'bg-purple-700 hover:bg-purple-600'}`}
onClick={() => selectedImage && handleDownload(selectedImage)}
disabled={!selectedImage}
>
Download
</button>
<button
className={`justify-center text-center w-full animate-in animate-once animate-duration-1000 animate-ease-out animate-reverse mb-4 py-2 px-4 rounded-lg no-underline flex items-center z-50 ${!selectedImage ? 'opacity-50 cursor-not-allowed bg-gray-800' : 'bg-purple-700 hover:bg-purple-600'}`}
onClick={() => selectedImage && open()}
disabled={!selectedImage}
>
Open
</button>
</div>
</div>
</>
);
};
2024-05-26 02:14:59 -04:00
const handleDownload = (image: string) => {
const link = document.createElement('a');
link.href = image;
link.download = 'image.jpg'; // or any other filename
link.style.display = 'none';
2024-05-26 16:45:17 -04:00
2024-05-26 02:14:59 -04:00
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
};
useEffect(() => {
2024-05-26 16:45:17 -04:00
getData();
const handleKeyDown = (event: KeyboardEvent) => {
switch (event.key) {
case 'ArrowLeft':
case 'a':
case 'A':
previous();
break;
case 'ArrowRight':
case 'd':
case 'D':
next();
break;
case 'Escape':
close();
break;
default:
break;
}
};
setSelectedImage(images[currentIndex]);
window.addEventListener('keydown', handleKeyDown);
// Clean up the event listener when the component is unmounted
return () => {
window.removeEventListener('keydown', handleKeyDown);
};
}, [selectedImage,currentIndex]);
2024-05-26 02:14:59 -04:00
const handleClick = (image: string) => {
2024-05-26 16:45:17 -04:00
setSelectedImage(image);
setCurrentIndex(images.indexOf(image));
2024-05-26 02:14:59 -04:00
};
2024-05-26 03:46:00 -04:00
const open = () => {
2024-05-26 16:45:17 -04:00
if (selectedImage === null) return;
2024-05-26 03:46:00 -04:00
console.log(selectedImage)
let base64Image = selectedImage.split(';base64,').pop();
2024-05-26 16:45:17 -04:00
if (!base64Image) return;
let blob = new Blob([Uint8Array.from(atob(base64Image), c => c.charCodeAt(0))], { type: 'image/jpeg' }); // adjust the type as needed
2024-05-26 03:46:00 -04:00
let url = URL.createObjectURL(blob);
window.open(url, '_blank');
}
2024-05-26 16:45:17 -04:00
2024-05-26 14:36:20 -04:00
const close = () => {
2024-05-26 16:45:17 -04:00
if (selectedImage != null) {
2024-05-26 14:36:20 -04:00
setSelectedImage(null);
2024-05-26 16:45:17 -04:00
setImages([]);
2024-05-26 14:36:20 -04:00
}
2024-05-26 16:45:17 -04:00
else {
2024-05-26 14:36:20 -04:00
closeMenu();
}
}
2024-05-26 02:14:59 -04:00
const breakpointColumnsObj = {
2024-05-26 16:45:17 -04:00
default: 3
2024-05-26 02:14:59 -04:00
};
return (
2024-05-26 16:36:45 -04:00
<div className="z-20">
<button
className="fixed bg-purple-800 hover:bg-purple-700 left-10 bottom-5 animate-in animate-once animate-duration-1000 animate-ease-out animate-reverse mb-4 py-2 px-4 rounded-lg no-underline flex items-center z-50"
onClick={() => close()}
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
className="mr-2 h-4 w-4 transition-transform group-hover:-translate-x-1"
2024-05-26 02:14:59 -04:00
>
2024-05-26 16:36:45 -04:00
<polyline points="15 18 9 12 15 6" />
</svg>{" "}
Back
</button>
{renderButtons()}
2024-05-26 19:49:20 -04:00
{selectedImage ? (<PanZoom>
2024-05-26 16:36:45 -04:00
<img
src={images[currentIndex]}
2024-05-26 19:49:20 -04:00
style={{ objectFit: "contain", maxWidth: "100%", maxHeight: "calc(100vh - 20px)", pointerEvents:"none" }}
2024-05-26 16:36:45 -04:00
className="cursor-pointer animate-in w-full h-auto"
2024-05-26 16:59:55 -04:00
onClick={() => close()}
2024-05-26 16:36:45 -04:00
/>
2024-05-26 19:49:20 -04:00
</PanZoom>
2024-05-26 16:36:45 -04:00
) : (
2024-05-26 19:49:20 -04:00
<div
className="z-30 pb-10"
style={{
display: selectedImage ? "flex" : "block",
alignItems: "flex-start",
}}
>
2024-05-26 16:36:45 -04:00
<Masonry
2024-05-26 16:58:48 -04:00
breakpointCols={columns}
2024-05-26 16:36:45 -04:00
className="my-masonry-grid"
style={{ width: selectedImage ? "50%" : "100%" }}
>
{images
.filter((img) => img !== selectedImage)
.map((image, index) => (
<img
src={image}
onClick={() => handleClick(image)}
className={`animate-in animate-once animate-duration-1000 animate-ease-out animate-reverse hover:scale-105 p-2 cursor-pointer my-2 transition-all opacity-100 duration-500 ease-in-out transform`}
/>
))}
</Masonry>
2024-05-26 19:49:20 -04:00
<>
</>
</div>
2024-05-26 16:36:45 -04:00
)}
2024-05-26 02:14:59 -04:00
</div>
);
}
export default Gallery;