Skip to content

Instantly share code, notes, and snippets.

@omargfh
Created July 24, 2022 22:42
Show Gist options
  • Save omargfh/42d9ecd243c19b064832b13d13b57a8f to your computer and use it in GitHub Desktop.
Save omargfh/42d9ecd243c19b064832b13d13b57a8f to your computer and use it in GitHub Desktop.
Next.js Bootstrap Gallery with Image Viewer
import { useEffect, useState } from "react";
import Image from 'next/image';
import { gsap } from "gsap/dist/gsap";
import { ScrollTrigger } from "gsap/dist/ScrollTrigger";
export default function ImageViewer({images, invokedImage, imageViewerActive, close}) {
const root = images.root;
images = images.paths;
const [activeImage, setActiveImage] = useState(invokedImage);
useEffect(() => {
setActiveImage(invokedImage);
}, [invokedImage]);
const getAllPaths = () => {
const ret = [];
Object.entries(images).forEach(([dir, paths]) => {
paths.forEach((path) => {
ret.push(root + '/' + dir + '/' + path);
})
});
return ret;
}
const handleClick = (paths, direction) => {
const increment = direction == 'left' ? 1 : -1;
const index = paths.indexOf(activeImage) + increment;
if (index < 0) {
index = paths.length - 1;
}
else if (index >= paths.length) {
index = 0;
}
setActiveImage(paths[index]);
}
// Cleanup
useEffect(() => {
return () => {
setActiveImage(null);
};
}, []);
const getOffset = (pathsForViewer) => {
const index = pathsForViewer.indexOf(activeImage);
const screenWidth = window.innerWidth;
const width = 100;
return -1 * index * width + (0.5 * (screenWidth - width));
}
const constructImageViewer = () => {
const pathsForViewer = getAllPaths();
return (
<div className="image-viewer-container">
<div className="image-viewer-app">
<div className="activeImage">
<img src={pathsForViewer.includes(activeImage) ? activeImage : 'images/error.jpg'} />
</div>
<div className="close" onClick={() => close()}><i className="fa-solid fa-x"></i></div>
<div className="right-arrow" onClick={()=>handleClick(pathsForViewer, 'right')}><i className="fa-solid fa-chevron-left"></i></div>
<div className="left-arrow" onClick={()=>handleClick(pathsForViewer, 'left')}><i className="fa-solid fa-chevron-right"></i></div>
<div className="images" style={{
transform: `translate3d(${(getOffset(pathsForViewer))}px, 0, 0)`
}}>
{
pathsForViewer.map((src) =>
<div key={src} className={"image-thumbnail" + (activeImage == src ? " active" : "")}>
<Image width="100" height="100" src={src} className={activeImage == src ? "active" : ""} onClick={()=>setActiveImage(src)} loading="lazy" alt={`A gallery thumbnail.`}/>
</div>
)
}
</div>
</div>
</div>
)
}
return (
<>
{imageViewerActive ? constructImageViewer() : <></>}
</>
)
}
import {useRef, useState} from 'react';
import fs from 'fs';
import Gallery from '../components/gallery/gallery';
import ImageViewer from '../components/gallery/imageViewer';
export default function Home({images}) {
// Image viewer and Gallery
const [invokedImage, setInvokedImage] = useState(null);
const [imageViewerActive, setImageViewerActive] = useState(false);
const handleImageClick = (fullPathName) => {
setInvokedImage(fullPathName);
setImageViewerActive(true);
}
const closeViewer = () => {
setImageViewerActive(false);
}
return (
<div className="app">
<ImageViewer images={images} invokedImage={invokedImage} imageViewerActive={imageViewerActive} close={closeViewer} />
<main>
<Gallery images={images} viewerHandler={handleImageClick} />
</main>
</div>
)
}
export async function getStaticProps(context) {
const root = './public';
const path = '/images/landing';
const fileList = await fs.readdirSync(root + path);
let images = {
"root": path,
"paths": {}
};
for (const file of fileList) {
const stat = fs.statSync(root+path+'/'+file);
console.log(path+'/'+file);
// If it is a directory
if (!stat.isFile()) {
const subDirList = await fs.readdirSync(root + path + '/' + file);
images.paths[file] = subDirList;
}
}
return {
props: {
images
},
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment