keen-slider
unknown
javascript
2 years ago
5.0 kB
399
Indexable
Never
import Image from 'next/image' import React, { MutableRefObject, useEffect, useRef, useState } from 'react' import cn from 'clsx' import { useKeenSlider, KeenSliderInstance, KeenSliderPlugin, } from 'keen-slider/react' import 'keen-slider/keen-slider.min.css' import ProductSliderModal from './ProductSliderModal' import { ProductImage } from '@commerce/types/product' interface ProductSliderProps { productName: string media: ProductImage[] } function ThumbnailPlugin( mainRef: MutableRefObject<KeenSliderInstance | null> ): KeenSliderPlugin { return (slider) => { function removeActive() { slider.slides.forEach((slide) => { slide.classList.remove('active') }) } function addActive(idx: number) { slider.slides[idx].classList.add('active') } function addClickEvents() { slider.slides.forEach((slide, idx) => { slide.addEventListener('click', () => { if (mainRef.current) mainRef.current.moveToIdx(idx) }) }) } slider.on('created', () => { if (!mainRef.current) return addActive(slider.track.details.rel) addClickEvents() mainRef.current.on('animationStarted', (main) => { removeActive() const next = main.animator.targetIdx || 0 addActive(main.track.absToRel(next)) slider.moveToIdx(next) }) }) } } const ProductSlider: React.FC<ProductSliderProps> = ({ media, productName, }) => { const [isMounted, setIsMounted] = useState(false) const [currentSlideIndex, setCurrentSlideIndex] = useState(0) const [isModalOpen, setModalOpen] = useState(false) const sliderContainerRef = useRef<HTMLDivElement>(null) const [sliderRef, slider] = useKeenSlider<HTMLDivElement>({ renderMode: 'performance', created: () => setIsMounted(true), initial: 0, }) const [thumbnailRef, thumbnail] = useKeenSlider<HTMLDivElement>( { initial: 0, slides: { perView: 5, spacing: 10, }, }, [ThumbnailPlugin(slider)] ) const hasThumbnail = media.length > 1 const handleSlideClick = (idx: number) => { setCurrentSlideIndex(idx) setModalOpen(true) } // // Stop the history navigation gesture on touch devices useEffect(() => { const preventNavigation = (event: TouchEvent) => { // Center point of the touch area const touchXPosition = event.touches[0].pageX // Size of the touch area const touchXRadius = event.touches[0].radiusX || 0 // We set a threshold (10px) on both sizes of the screen, // if the touch area overlaps with the screen edges // it's likely to trigger the navigation. We prevent the // touchstart event in that case. if ( touchXPosition - touchXRadius < 10 || touchXPosition + touchXRadius > window.innerWidth - 10 ) event.preventDefault() } const slider = sliderContainerRef.current! slider.addEventListener('touchstart', preventNavigation) return () => { if (slider) { console.log('removing touchstart') slider.removeEventListener('touchstart', preventNavigation) } } }, []) return ( <> <div className="md:w-3/4 lg:w-full mb-8 md:mb-10 lg:mb-0" ref={sliderContainerRef} > <div ref={sliderRef} className={cn('keen-slider', { 'mb-3': hasThumbnail, })} > {media.map(({ url, label }, index) => ( <div key={url} className="keen-slider__slide" onClick={() => handleSlideClick(index)} > <div className="relative overflow-hidden rounded-lg h-[365px] lg:h-[582px]"> <Image src={url} layout="fill" objectFit="cover" alt={label} quality={100} /> </div> </div> ))} </div> <div ref={thumbnailRef} className={cn('keen-slider thumbnail', { '!hidden': !hasThumbnail, })} > {hasThumbnail && media.map(({ url, label }) => ( <div key={url} className="keen-slider__slide rounded-lg"> <div className="relative rounded-lg overflow-hidden h-14"> <Image src={url} layout="fill" objectFit="cover" alt={label} quality={100} /> </div> </div> ))} </div> </div> {isModalOpen && ( <ProductSliderModal productName={productName} isOpen={isModalOpen} onClose={() => setModalOpen(false)} media={media} initialSlide={currentSlideIndex} /> )} </> ) } export default ProductSlider