UNPKG

wgp-next-shop-ui-lib

Version:

Библиотека UI-компонентов для Next-Shop с поддержкой e-commerce функций

156 lines (141 loc) 4.98 kB
'use client'; import React, { useState, useEffect, useRef } from 'react'; import { ChevronLeft, ChevronRight } from 'lucide-react'; const Slider = ({ slides = [], autoplay = false, autoplayInterval = 3000, showArrows = true, showDots = true, infinite = true, customStyles = {}, renderSlide = null, onSlideChange = null, className = '' }) => { const [currentSlide, setCurrentSlide] = useState(0); const [isAutoplay, setIsAutoplay] = useState(autoplay); const intervalRef = useRef(null); // Автопроигрывание useEffect(() => { if (isAutoplay && slides.length > 1) { intervalRef.current = setInterval(() => { setCurrentSlide(prev => infinite ? (prev + 1) % slides.length : Math.min(prev + 1, slides.length - 1) ); }, autoplayInterval); } return () => { if (intervalRef.current) { clearInterval(intervalRef.current); } }; }, [isAutoplay, slides.length, autoplayInterval, infinite]); // Обработчики навигации const goToSlide = (index) => { setCurrentSlide(index); onSlideChange?.(index); }; const nextSlide = () => { const newIndex = infinite ? (currentSlide + 1) % slides.length : Math.min(currentSlide + 1, slides.length - 1); goToSlide(newIndex); }; const prevSlide = () => { const newIndex = infinite ? currentSlide === 0 ? slides.length - 1 : currentSlide - 1 : Math.max(currentSlide - 1, 0); goToSlide(newIndex); }; // Остановка автопроигрывания при наведении const handleMouseEnter = () => setIsAutoplay(false); const handleMouseLeave = () => setIsAutoplay(autoplay); if (!slides.length) return null; const defaultStyles = { container: "relative w-full overflow-hidden rounded-lg bg-gray-100", slideContainer: "flex transition-transform duration-500 ease-in-out", slide: "w-full flex-shrink-0", arrow: "absolute top-1/2 -translate-y-1/2 z-10 bg-white/80 hover:bg-white text-gray-800 rounded-full p-2 shadow-lg transition-all duration-200 hover:scale-110", leftArrow: "left-4", rightArrow: "right-4", dotsContainer: "absolute bottom-4 left-1/2 -translate-x-1/2 flex gap-2 z-10", dot: "w-3 h-3 rounded-full transition-all duration-200 cursor-pointer", dotActive: "bg-white shadow-lg", dotInactive: "bg-white/50 hover:bg-white/75" }; const styles = { ...defaultStyles, ...customStyles }; return ( <div className={`${styles.container} ${className}`} onMouseEnter={handleMouseEnter} onMouseLeave={handleMouseLeave} > {/* Слайды */} <div className={styles.slideContainer} style={{ transform: `translateX(-${currentSlide * 100}%)` }} > {slides.map((slide, index) => ( <div key={index} className={styles.slide}> {renderSlide ? renderSlide(slide, index) : ( <div className="w-full h-full flex items-center justify-center"> {slide.image && ( <img src={slide.image} alt={slide.title || `Slide ${index + 1}`} className="w-full h-full object-cover" /> )} {slide.content && ( <div className="absolute inset-0 flex items-center justify-center"> <div className="text-center text-white"> {slide.title && <h2 className="text-2xl md:text-4xl font-bold mb-2">{slide.title}</h2>} {slide.description && <p className="text-lg md:text-xl">{slide.description}</p>} </div> </div> )} </div> )} </div> ))} </div> {/* Стрелки навигации */} {showArrows && slides.length > 1 && ( <> <button onClick={prevSlide} className={`${styles.arrow} ${styles.leftArrow}`} disabled={!infinite && currentSlide === 0} > <ChevronLeft size={20} /> </button> <button onClick={nextSlide} className={`${styles.arrow} ${styles.rightArrow}`} disabled={!infinite && currentSlide === slides.length - 1} > <ChevronRight size={20} /> </button> </> )} {/* Точки индикации */} {showDots && slides.length > 1 && ( <div className={styles.dotsContainer}> {slides.map((_, index) => ( <button key={index} onClick={() => goToSlide(index)} className={`${styles.dot} ${ index === currentSlide ? styles.dotActive : styles.dotInactive }`} /> ))} </div> )} </div> ); }; export default Slider;