story-az-card-kit
Version:
A flexible and customizable story card component with multiple layouts
153 lines (137 loc) • 4.01 kB
JavaScript
import React, { useState, useRef, useEffect } from 'react';
import PropTypes from 'prop-types';
import StoryCard from '../components/StoryCard';
const CarouselCard = ({
stories = [],
theme = 'default',
onCardClick,
className = '',
autoPlay = false,
autoPlayInterval = 3000,
showArrows = true,
showDots = true,
children,
...props
}) => {
const [currentIndex, setCurrentIndex] = useState(0);
const [isAutoPlaying, setIsAutoPlaying] = useState(autoPlay);
const carouselRef = useRef(null);
const intervalRef = useRef(null);
useEffect(() => {
if (isAutoPlaying && autoPlay) {
intervalRef.current = setInterval(() => {
setCurrentIndex((prev) => (prev + 1) % stories.length);
}, autoPlayInterval);
}
return () => {
if (intervalRef.current) {
clearInterval(intervalRef.current);
}
};
}, [isAutoPlaying, autoPlay, autoPlayInterval, stories.length]);
const goToSlide = (index) => {
setCurrentIndex(index);
};
const goToPrevious = () => {
setCurrentIndex((prev) => (prev - 1 + stories.length) % stories.length);
};
const goToNext = () => {
setCurrentIndex((prev) => (prev + 1) % stories.length);
};
const handleMouseEnter = () => {
if (autoPlay) {
setIsAutoPlaying(false);
if (intervalRef.current) {
clearInterval(intervalRef.current);
}
}
};
const handleMouseLeave = () => {
if (autoPlay) {
setIsAutoPlaying(true);
}
};
const carouselClass = 'story-card-carousel';
const carouselClasses = [
carouselClass,
className
].filter(Boolean).join(' ');
return (
<div
ref={carouselRef}
className={carouselClasses}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
{...props}
>
<div className="story-card-carousel__container">
<div
className="story-card-carousel__track"
style={{
transform: `translateX(-${currentIndex * 100}%)`
}}
>
{stories.map((story, index) => (
<div key={index} className="story-card-carousel__slide">
<StoryCard
{...story}
theme={theme}
onClick={onCardClick ? (e, data) => onCardClick(e, data, index) : undefined}
/>
</div>
))}
</div>
</div>
{showArrows && stories.length > 1 && (
<>
<button
className="story-card-carousel__arrow story-card-carousel__arrow--prev"
onClick={goToPrevious}
aria-label="Previous slide"
>
‹
</button>
<button
className="story-card-carousel__arrow story-card-carousel__arrow--next"
onClick={goToNext}
aria-label="Next slide"
>
›
</button>
</>
)}
{showDots && stories.length > 1 && (
<div className="story-card-carousel__dots">
{stories.map((_, index) => (
<button
key={index}
className={`story-card-carousel__dot ${index === currentIndex ? 'story-card-carousel__dot--active' : ''}`}
onClick={() => goToSlide(index)}
aria-label={`Go to slide ${index + 1}`}
/>
))}
</div>
)}
{children}
</div>
);
};
CarouselCard.propTypes = {
stories: PropTypes.arrayOf(PropTypes.shape({
title: PropTypes.string,
description: PropTypes.string,
image: PropTypes.string,
author: PropTypes.string,
date: PropTypes.string,
tags: PropTypes.arrayOf(PropTypes.string)
})),
theme: PropTypes.oneOf(['default', 'dark', 'minimal', 'elegant', 'modern']),
onCardClick: PropTypes.func,
className: PropTypes.string,
autoPlay: PropTypes.bool,
autoPlayInterval: PropTypes.number,
showArrows: PropTypes.bool,
showDots: PropTypes.bool,
children: PropTypes.node
};
export default CarouselCard;