@cordelia273/react-image-grid
Version:
Facebook-like image grid for react
213 lines (198 loc) • 5.6 kB
JavaScript
import React, { useEffect, useState } from "react";
import PropTypes from "prop-types";
import styled, { css } from "styled-components";
import "react-image-gallery/styles/css/image-gallery.css";
import ImageGallery from "react-image-gallery";
import "react-responsive-modal/styles.css";
import { Modal } from "react-responsive-modal";
import "./style.css";
const Overlay = styled.div`
background-color: rgba(0, 0, 0, 0.5);
position: absolute;
width: 100%;
height: 100%;
color: white;
display: flex;
justify-content: center;
align-items: center;
font-size: 2.5rem;
`;
const ImgCell = styled.div`
cursor: pointer;
`;
const Img = styled.img`
width: 100%;
height: 100%;
object-fit: cover;
`;
const Container = styled.div`
display: grid;
${(props) => `gap: ${props.gap};`}
${(props) => props.count === 2 && `grid-template-columns: repeat(2, 1fr);`}
${(props) =>
props.isHorizontal &&
props.count === 3 &&
`grid-template-columns: repeat(2, 1fr);`}
${(props) =>
props.isHorizontal &&
props.count >= 4 &&
`grid-template-columns: repeat(3, 1fr);`}
${(props) =>
!props.isHorizontal &&
props.count === 3 &&
`grid-template-columns: 2fr 1fr; grid-template-rows: repeat(2, 1fr);`}
${(props) =>
!props.isHorizontal &&
props.count >= 4 &&
`grid-template-columns: 2fr 1fr; grid-template-rows: repeat(3, 1fr);`}
${ImgCell}:nth-child(1) {
grid-column-start: 1;
grid-row-start: 1;
${(props) =>
props.isHorizontal && props.count === 3 && `grid-column-end: 3;`}
${(props) =>
props.isHorizontal && props.count >= 4 && `grid-column-end: 4;`}
${(props) => !props.isHorizontal && props.count === 3 && `grid-row-end: 3;`}
${(props) => !props.isHorizontal && props.count >= 4 && `grid-row-end: 4;`}
}
${(props) =>
props.count === 2 ? `${ImgCell} {` : `${ImgCell}:not(:nth-child(1)) {`}
position: relative;
&:after {
content: '';
display: block;
padding-bottom: 100%;
}
${Img} {
width: 100%;
height: 100%;
left: 0;
top: 0;
position: absolute;
}
}
`;
const ImageGrid = ({ images, gap, className, onClick, modal }) => {
const count = images.length;
const [imageLoaded, setImageLoaded] = useState(false);
const [isHorizontal, setIsHorizontal] = useState(true);
const [open, setOpen] = useState(false);
const [galleryImages, setGalleryImages] = useState([]);
const [imageIndex, setImageIndex] = useState(0);
const handleClick = (clickedImage) => {
setImageIndex(images.indexOf(clickedImage));
setOpen(true);
onClick(clickedImage);
};
useEffect(() => {
const img = new Image();
const [firstImage] = images;
img.src = firstImage;
img.onload = () => {
setImageLoaded(true);
if (img.width > img.height) setIsHorizontal(true);
else setIsHorizontal(false);
};
setGalleryImages(
images.map((image) => ({
original: image,
}))
);
}, [images]);
return (
imageLoaded && (
<>
<Container
isHorizontal={isHorizontal}
count={count}
gap={gap}
className={className}
>
{images.map(
(image, index) =>
index <= 3 && (
<ImgCell
key={image}
onClick={() => handleClick(image)}
>
<Img src={image} alt={image} />
{count > 4 && index === 3 && (
<Overlay>
<h3>+{count - 3}</h3>
</Overlay>
)}
</ImgCell>
)
)}
</Container>
{modal && (
<Modal
open={open}
onClose={() => setOpen(false)}
closeIcon={
<div style={{color: "white"}}>
<svg
style={{width: '2rem'}}
xmlns="http://www.w3.org/2000/svg"
className="h-6 w-6"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M6 18L18 6M6 6l12 12"
/>
</svg>
</div>
}
center
styles={{
modalContainer: {
overflow: "hidden",
},
modal: {
maxWidth: "100vw",
width: "100vw",
height: "100vh",
top: 0,
left: 0,
margin: 0,
padding: 0,
background: "black",
display: "flex",
alignItems: "center",
justifyContent: "center",
},
}}
animationDuration={0}
>
<ImageGallery
items={galleryImages}
showPlayButton={false}
showFullscreenButton={false}
showThumbnails={false}
startIndex={imageIndex}
/>
</Modal>
)}
</>
)
);
};
ImageGrid.defaultProps = {
className: "",
gap: "0.2rem",
modal: true,
onClick: () => {},
};
ImageGrid.propTypes = {
images: PropTypes.array.isRequired,
gap: PropTypes.string,
className: PropTypes.string,
modal: PropTypes.bool,
onClick: PropTypes.func,
};
export default ImageGrid;