All files / src/components/PhotosCarousel usePhotosCarousel.ts

0% Statements 0/46
0% Branches 0/14
0% Functions 0/12
0% Lines 0/42

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86                                                                                                                                                                           
import { type KeyboardEvent, useEffect, useState } from 'react';
 
import useEmblaCarousel from 'embla-carousel-react';
 
import type { PhotosCarouselProps } from './index';
 
const usePhotosCarousel = ({
  photos,
  selectedSlide,
  onSlideChange,
}: Pick<PhotosCarouselProps, 'photos' | 'selectedSlide' | 'onSlideChange'>) => {
  const [internalSlide, setInternalSlide] = useState(0);
  const currentSlide = Math.max(0, Math.min(selectedSlide ?? internalSlide, photos.length - 1));
  const [startIndex] = useState(currentSlide);
  const [emblaRef, emblaApi] = useEmblaCarousel({ loop: false, startIndex });
  const [scrollSnaps, setScrollSnaps] = useState<number[]>([]);
  const [prevButtonDisabled, setPrevButtonDisabled] = useState(true);
  const [nextButtonDisabled, setNextButtonDisabled] = useState(true);
  const photoKey = JSON.stringify(photos.map((photo) => photo.url));
 
  const scrollPrev = () => emblaApi?.scrollPrev();
  const scrollNext = () => emblaApi?.scrollNext();
  const scrollTo = (index: number) => emblaApi?.scrollTo(index);
 
  useEffect(() => {
    emblaApi?.reInit();
  }, [emblaApi, photoKey]);
 
  useEffect(() => {
    if (!emblaApi) {
      return;
    }
    // Synchronize external selection before subscribing so it does not echo a change callback.
    if (emblaApi.selectedScrollSnap() !== currentSlide) {
      emblaApi.scrollTo(currentSlide);
    }
    const updateControls = () => {
      setPrevButtonDisabled(!emblaApi.canScrollPrev());
      setNextButtonDisabled(!emblaApi.canScrollNext());
    };
    const handleInit = () => {
      setScrollSnaps(emblaApi.scrollSnapList());
      updateControls();
    };
    const handleSelect = () => {
      updateControls();
      const slide = emblaApi.selectedScrollSnap();
      setInternalSlide(slide);
      if (slide !== currentSlide) {
        onSlideChange?.(slide);
      }
    };
    handleInit();
    emblaApi.on('select', handleSelect).on('reInit', handleInit);
 
    return () => {
      emblaApi.off('select', handleSelect).off('reInit', handleInit);
    };
  }, [emblaApi, currentSlide, onSlideChange, photoKey]);
 
  const onKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {
    if (event.key === 'ArrowLeft' || event.key === 'ArrowRight') {
      event.preventDefault();
      if (event.key === 'ArrowLeft') {
        scrollPrev();
      } else {
        scrollNext();
      }
    }
  };
 
  return {
    emblaRef,
    currentSlide,
    scrollSnaps,
    prevButtonDisabled,
    nextButtonDisabled,
    scrollPrev,
    scrollNext,
    scrollTo,
    onKeyDown,
  };
};
 
export default usePhotosCarousel;