All files / src/components/PhotosCarousel index.tsx

0% Statements 0/7
0% Branches 0/15
0% Functions 0/4
0% Lines 0/7

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 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101                                                                                                                                                                                                         
import React, { type JSX } from 'react';
 
import { Box, Typography } from '@mui/material';
 
import Icon from '../Icon/Icon';
import PhotoSlide from './PhotoSlide';
import './styles.scss';
import usePhotosCarousel from './usePhotosCarousel';
 
export type PhotoItem = {
  url: string;
  alt?: string;
  decoration?: React.ReactNode;
};
 
export interface PhotosCarouselProps {
  photos: PhotoItem[];
  selectedSlide?: number;
  onSlideChange?: (index: number) => void;
  showArrows?: boolean;
  numbered?: boolean;
  dots?: boolean;
}
 
const PhotosCarousel = (props: PhotosCarouselProps): JSX.Element => {
  const { photos, showArrows = false, numbered, dots = true } = props;
  const {
    emblaRef,
    currentSlide,
    scrollSnaps,
    prevButtonDisabled,
    nextButtonDisabled,
    scrollPrev,
    scrollNext,
    scrollTo,
    onKeyDown,
  } = usePhotosCarousel(props);
 
  return (
    <Box className='photos-carousel' role='region' aria-roledescription='carousel' aria-label='Photos'>
      <div className='embla'>
        <div className='embla__viewport' ref={emblaRef} onKeyDown={onKeyDown}>
          <div className='embla__container'>
            {photos.map((photo, index) => (
              <PhotoSlide
                key={`${photo.url}-${index}`}
                photo={photo}
                index={index}
                total={photos.length}
                selected={index === currentSlide}
              />
            ))}
          </div>
        </div>
        {showArrows && photos.length > 1 && (
          <>
            <button
              type='button'
              className='embla__prev'
              aria-label='Previous photo'
              disabled={prevButtonDisabled}
              onClick={scrollPrev}
            >
              <Icon name='caretLeft' fillColor='currentColor' />
            </button>
            <button
              type='button'
              className='embla__next'
              aria-label='Next photo'
              disabled={nextButtonDisabled}
              onClick={scrollNext}
            >
              <Icon name='caretRight' fillColor='currentColor' />
            </button>
          </>
        )}
        {dots && (
          <div className='embla__dots'>
            {scrollSnaps.map((_, index) => (
              <button
                key={index}
                type='button'
                className={`embla__dot${index === currentSlide ? ' embla__dot--selected' : ''}`}
                aria-label={`Go to photo ${index + 1}`}
                aria-current={index === currentSlide ? 'true' : undefined}
                onClick={() => scrollTo(index)}
              />
            ))}
          </div>
        )}
      </div>
      {numbered && (
        <Typography className='photo-numbering'>{`${photos.length ? currentSlide + 1 : 0}/${photos.length}`}</Typography>
      )}
      {photos[currentSlide]?.decoration}
    </Box>
  );
};
 
export default PhotosCarousel;