react-advanced-gallery
Version:
A customizable React image gallery component with Bootstrap styling, fullscreen mode, animations, and support for various media types
144 lines (143 loc) • 5.67 kB
JavaScript
import { useState, useCallback, useEffect } from 'react';
export const useGallery = ({ initialImages, initialLayout = 'grid', initialAnimation = 'fade', initialThumbnailsPosition = 'bottom', initialEnableZoom = true }) => {
const [images, setImages] = useState(initialImages);
const [selectedImage, setSelectedImage] = useState(null);
const [currentIndex, setCurrentIndex] = useState(0);
const [isFullscreenOpen, setIsFullscreenOpen] = useState(false);
const [zoomLevel, setZoomLevel] = useState(1);
const [layout, setLayout] = useState(initialLayout);
const [animation, setAnimation] = useState(initialAnimation);
const [thumbnailsPosition, setThumbnailsPosition] = useState(initialThumbnailsPosition);
const [enableZoom, setEnableZoom] = useState(initialEnableZoom);
// Reset zoom level when closing fullscreen or changing images
useEffect(() => {
if (!isFullscreenOpen) {
setZoomLevel(1);
}
}, [isFullscreenOpen, selectedImage]);
const openFullscreen = useCallback((image) => {
const index = images.findIndex(img => img.id === image.id);
setSelectedImage(image);
setCurrentIndex(index !== -1 ? index : 0);
setIsFullscreenOpen(true);
}, [images]);
const closeFullscreen = useCallback(() => {
setIsFullscreenOpen(false);
setSelectedImage(null);
}, []);
const toggleFavorite = useCallback((image) => {
setImages(prevImages => prevImages.map(img => img.id === image.id ? { ...img, isFavorite: !img.isFavorite } : img));
if (selectedImage && selectedImage.id === image.id) {
setSelectedImage({ ...selectedImage, isFavorite: !selectedImage.isFavorite });
}
}, [selectedImage]);
const removeImage = useCallback((image) => {
// Get the new array of images before setting state to use for navigation
const newImages = images.filter(img => img.id !== image.id);
if (isFullscreenOpen && selectedImage && selectedImage.id === image.id) {
if (newImages.length > 0) {
// If we're removing the last image, go to the previous one
// Otherwise, stay at the same index (which will now show the next image)
const newIndex = currentIndex >= newImages.length ? newImages.length - 1 : currentIndex;
setCurrentIndex(newIndex);
setSelectedImage(newImages[newIndex]);
}
else {
closeFullscreen();
}
}
// Update the images state after handling navigation
setImages(newImages);
}, [isFullscreenOpen, selectedImage, images, currentIndex, closeFullscreen]);
const navigateToImage = useCallback((index) => {
if (index >= 0 && index < images.length) {
setCurrentIndex(index);
setSelectedImage(images[index]);
}
}, [images]);
const navigatePrev = useCallback(() => {
const newIndex = (currentIndex - 1 + images.length) % images.length;
navigateToImage(newIndex);
}, [currentIndex, images.length, navigateToImage]);
const navigateNext = useCallback(() => {
const newIndex = (currentIndex + 1) % images.length;
navigateToImage(newIndex);
}, [currentIndex, images.length, navigateToImage]);
const zoomIn = useCallback(() => {
setZoomLevel(prevZoom => Math.min(prevZoom + 0.5, 3));
}, []);
const zoomOut = useCallback(() => {
setZoomLevel(prevZoom => Math.max(prevZoom - 0.5, 0.5));
}, []);
const resetZoom = useCallback(() => {
setZoomLevel(1);
}, []);
const addImages = useCallback((newImages) => {
setImages(prevImages => [...prevImages, ...newImages]);
}, []);
const updateConfig = useCallback((config) => {
if (config.layout)
setLayout(config.layout);
if (config.animation)
setAnimation(config.animation);
if (config.thumbnailsPosition)
setThumbnailsPosition(config.thumbnailsPosition);
if (config.enableZoom !== undefined)
setEnableZoom(config.enableZoom);
}, []);
useEffect(() => {
const handleKeyDown = (e) => {
if (!isFullscreenOpen)
return;
switch (e.key) {
case 'Escape':
closeFullscreen();
break;
case 'ArrowLeft':
navigatePrev();
break;
case 'ArrowRight':
navigateNext();
break;
case '+':
zoomIn();
break;
case '-':
zoomOut();
break;
case '0':
resetZoom();
break;
default:
break;
}
};
window.addEventListener('keydown', handleKeyDown);
return () => {
window.removeEventListener('keydown', handleKeyDown);
};
}, [isFullscreenOpen, closeFullscreen, navigatePrev, navigateNext, zoomIn, zoomOut, resetZoom]);
return {
images,
selectedImage,
currentIndex,
isFullscreenOpen,
zoomLevel,
layout,
animation,
thumbnailsPosition,
enableZoom,
openFullscreen,
closeFullscreen,
toggleFavorite,
removeImage,
navigateToImage,
navigatePrev,
navigateNext,
zoomIn,
zoomOut,
resetZoom,
addImages,
updateConfig,
};
};