0
votes

I am building a project with Nextjs, React and using Sanity as a CMS.

One of the main components will be a gallery of images that, when you click on of the images you will open an image slider gallery. The images are an array passed in from the CMS.

It's loosely based on the way this website works.. https://www.garrodkirkwood.com/projects/

At the moment, I'm using a simple setState toggle to hide and show the image gallery and the slideshow. The code is far from perfect as there's a lot going on I realise.

WHAT I NEED HELP WITH....

I need to find a way to pass the index of the clicked on image to the event listener so that when you click on the image you open the slideshow on the SAME IMAGE.

I really hope this makes sense.

The user will then exit the slideshow by clicking on 'show thumbnails'

import React, { useState, useEffect, useRef } from "react";
import { css, jsx } from "@emotion/core";
import ImageSliderContent from "./ImageSliderContent";
import styles from "./ImageGrid.module.css";
import ImageGrid from "./ImageGrid";
import ImageSlide from "./ImageSlide";
import Arrow from "./Arrow";
import Dots from "./Dots";
import imageUrlBuilder from "@sanity/image-url";
import client from "../../client";

const builder = imageUrlBuilder(client);

const LocationsImageGallery = (props) => {
  const { caption, image } = props;

  const images = props.image;

  const [state, setState] = useState({
    translate: 0,
    transition: 0.45,
    activeSlide: 0,
  });

  const [showSlider, setShowSlider] = useState(false);

  const { translate, transition, activeSlide, _slides } = state;

  const size = useWindowSize();
  const transitionRef = useRef();

  function useWindowSize() {
    const isClient = typeof window === "object";

    function getSize() {
      return {
        width: isClient ? window.innerWidth : undefined,
      };
    }

    const [windowSize, setWindowSize] = useState(getSize);

    useEffect(() => {
      if (!isClient) {
        return false;
      }

      function handleResize() {
        setWindowSize(getSize());
      }

      window.addEventListener("resize", handleResize);
      return () => window.removeEventListener("resize", handleResize);
    }, []);

    return windowSize;
  }

  const nextSlide = () => {
    if (activeSlide === images.length - 1) {
      return setState({
        ...state,
        translate: 0,
        activeSlide: 0,
      });
    }

    setState({
      ...state,
      activeSlide: activeSlide + 1,
      translate: (activeSlide + 1) * size.width,
    });
  };

  const prevSlide = () => {
    if (activeSlide === 0) {
      return setState({
        ...state,
        translate: (images.length - 1) * size.width,
        activeSlide: images.length - 1,
      });
    }

    setState({
      ...state,
      activeSlide: activeSlide - 1,
      translate: (activeSlide - 1) * size.width,
    });
  };

  const show = () => {
    setShowSlider(true);
  };

  const hide = () => {
    setShowSlider(false);
  };

  return (
    <div className={styles.root}>
      <div className={styles.imageGridContainer}>
        {images.map((image, index) => (
          <div className={styles.imageContainer} onClick={show}>
            <img
              src={builder.image(image).auto("format").width(2000).url()}
              className={styles.image}
              alt={image.caption}
              key={index}
            />
            <p className={styles.caption}>{image.caption}</p>
          </div>
        ))}
      </div>

      {showSlider && (
        <div className={styles.imageGalleryContainer}>
          <div css={ImageSliderCSS}>
            <ImageSliderContent
              translate={translate}
              transition={transition}
              width={size.width * images.length}
            >
              {images.map((image, index) => (
                <ImageSlide
                  key={image + index}
                  content={builder.image(image).auto("format").width(2000).url()}
                ></ImageSlide>
              ))}
            </ImageSliderContent>
            <Arrow direction="left" handleClick={prevSlide} />
            <Arrow direction="right" handleClick={nextSlide} />
            <Dots slides={images} activeSlide={activeSlide} />
          </div>
          <a href="" onClick={hide}>
            Show Thumbnails
          </a>
        </div>
      )}
    </div>
  );
};

const ImageSliderCSS = css`
  position: relative;
  height: 500px;
  width: 750px;
  margin: 0 auto;
  overflow: hidden;
`;
export default LocationsImageGallery;

UPDATED WITH PASSING INDEX TO CLICK EVENT

import React, { useState, useEffect, useRef } from "react";
import { css, jsx } from "@emotion/core";
import ImageSliderContent from "./ImageSliderContent";
import styles from "./LocationsImageGallery.module.css";
import ImageGrid from "./ImageGrid";
import ImageSlide from "./ImageSlide";
import Arrow from "./Arrow";
import Dots from "./Dots";
import imageUrlBuilder from "@sanity/image-url";
import client from "../../client";

const builder = imageUrlBuilder(client);

const LocationsImageGallery = (props) => {
  const { caption, image } = props;

  const images = props.image;

  const [state, setState] = useState({
    translate: 0,
    transition: 0.45,
    activeSlide: 0,
  });

  const [showSlider, setShowSlider] = useState(false);
  const [showGrid, setShowGrid] = useState(true);

  const { translate, transition, activeSlide, _slides } = state;

  const size = useWindowSize();
  const transitionRef = useRef();

  function useWindowSize() {
    const isClient = typeof window === "object";

    function getSize() {
      return {
        width: isClient ? window.innerWidth : undefined,
      };
    }

    const [windowSize, setWindowSize] = useState(getSize);

    useEffect(() => {
      if (!isClient) {
        return false;
      }

      function handleResize() {
        setWindowSize(getSize());
      }

      window.addEventListener("resize", handleResize);
      return () => window.removeEventListener("resize", handleResize);
    }, []);

    return windowSize;
  }

  const nextSlide = () => {
    if (activeSlide === images.length - 1) {
      return setState({
        ...state,
        translate: 0,
        activeSlide: 0,
      });
    }

    setState({
      ...state,
      activeSlide: activeSlide + 1,
      translate: (activeSlide + 1) * size.width,
    });
  };

  const prevSlide = () => {
    if (activeSlide === 0) {
      return setState({
        ...state,
        translate: (images.length - 1) * size.width,
        activeSlide: images.length - 1,
      });
    }

    setState({
      ...state,
      activeSlide: activeSlide - 1,
      translate: (activeSlide - 1) * size.width,
    });
  };

  const show = (index) => {
    setShowGrid(false);
    setShowSlider(true);
    setState({ activeSlide: index });
  };

  const hide = () => {
    setShowSlider(false);
    setShowGrid(true);
  };

  return (
    <div className={styles.root}>
      <div className={styles.header}>
        <a href="/locations">X</a>
      </div>
      {showGrid && (
        <div className={styles.imageGrid}>
          <div className={styles.imageGridContainer}>
            {images.map((image, index, caption) => (
              <div className={styles.imageContainer} onClick={() => show(index)}>
                <img
                  src={builder.image(image).auto("format").width(2000).url()}
                  className={styles.image}
                  alt={image.caption}
                  key={index}
                />
                <p className={styles.caption}>{image.caption}</p>
              </div>
            ))}
          </div>
        </div>
      )}

      {showSlider && (
        <div className={styles.imageGalleryContainer}>
          <div className={styles.imageSlider}>
            <ImageSliderContent
              translate={translate}
              transition={transition}
              width={size.width * images.length}
            >
              {images.map((image, index, caption) => (
                <>
                  <ImageSlide
                    key={image + index}
                    content={builder.image(image).auto("format").url()}
                  ></ImageSlide>
                </>
              ))}
            </ImageSliderContent>
            <Arrow direction="left" handleClick={prevSlide} />
            <Arrow direction="right" handleClick={nextSlide} />
          </div>
          <div className={styles.infoBar}>
            <p className={styles.infoCaption}>
              Locations / <span>{image.caption}</span>
            </p>
            <a href="" onClick={hide} className={styles.infoThumbnails}>
              Show Thumbnails
            </a>
          </div>
        </div>
      )}
    </div>
  );
};

export default LocationsImageGallery;

This version, doesn't seem to be updating the state, console.log of activeSlide and state...

LocationsImageGallery.js?0692:94 {activeSlide: 8, state: {…}} activeSlide: 8 state: activeSlide: 0 transition: 0.45 translate: 0 proto: Object proto: Object

2

2 Answers

0
votes

You can change the show function to take slide index as an argument:

  const show = (index) => {
    // set activeSlide accordingly
    setState(...)
    setShowSlider(true);
  }

Then, in the onClick function, pass in the index:

onClick={() => show(index)}

If I were you, I might remove the showSlider state and use activeSlide: -1 or activeSlide: null to represent not showing slider.

0
votes

Could you try adding a custom attribute to the img element with the index as the value like

data-index = {index}

You can then go back to your original way of doing the onClick handler

onClick={show}

Your show function can then grab that custom attribute like so:

  const show = (e) => {
   setShowGrid(false);
   setShowSlider(true);
   setState({ activeSlide: e.target.getAttribute("data-index") });
 };