Fixed and refactored carousel

This commit is contained in:
TitiLaPierre
2024-10-09 11:31:56 +02:00
parent 3a83854afb
commit 356bdd46d8
2 changed files with 179 additions and 64 deletions

View File

@@ -0,0 +1,107 @@
.carousel {
display: flex;
flex-direction: column;
align-items: center;
gap: 1rem;
border-radius: 15px;
position: relative;
}
.carousel-wrapper {
overflow: hidden;
width: 100%;
position: relative;
}
.carousel-slider {
display: flex;
gap: 25px;
position: relative;
padding-block: 1rem;
transition: translate 0.3s;
}
.slide {
background: var(--white);
border-radius: 15px;
box-shadow: var(--shadow);
}
.slide-image {
display: block;
color: var(--darkGray);
font-size: 0.8rem;
transition: opacity 0.5s ease-in-out;
height: 15rem;
object-fit: contain;
border-radius: 15px;
}
.slide-content {
padding: 1rem;
border-bottom-left-radius: 15px;
border-bottom-right-radius: 15px;
}
.slide-title {
font-weight: 800;
font-size: 1rem;
margin-bottom: 0.5rem;
}
.slide-text {
font-size: 0.8rem;
color: var(--darkGray);
margin-bottom: 0.5rem;
}
.carousel-button {
display: flex;
align-items: center;
justify-content: center;
background: var(--gray);
border: none;
width: 2em;
height: 2em;
font-size: 1em;
border-radius: 999px;
cursor: pointer;
}
.carousel-image.fade-out {
opacity: 0;
}
.carousel-image.fade-in {
opacity: 1;
}
.carousel-buttons {
width: 100%;
display: flex;
justify-content: space-between;
transform: translateY(-50%);
}
.carousel-buttons button {
background-color: rgba(0, 0, 0, 0.5);
color: white;
border: none;
padding: 1em;
cursor: pointer;
}
.carousel-bar {
display: flex;
align-items: center;
justify-content: center;
gap: 1rem;
width: 100%;
}
.carousel-dot {
width: 8px;
height: 8px;
border-radius: 999px;
background: var(--gray);
cursor: pointer;
border: none;
transition: width .3s, background .3s;
&.dot_active { width: 24px; }
&:hover { background: var(--darkGray); }
}

View File

@@ -1,84 +1,92 @@
import React, { useState, useEffect } from "react";
import React from "react";
import { NavArrowLeft, NavArrowRight } from "iconoir-react";
import "./carousel.css";
interface CarouselProps {
images: string[];
carousel: CarouselImageProps[];
}
interface CarouselClickablesProps {
images: string[];
links: string[];
interface CarouselImageProps {
image: string;
title: string;
text: string;
link?: string;
alt?: string;
}
export const Carousel: React.FC<CarouselProps> = ({ images }) => {
const [currentImage, setCurrentImage] = React.useState(0);
export const Carousel: React.FC<CarouselProps> = ({ carousel }) => {
const [currentIndex, setCurrentImage] = React.useState(0);
const [imagesSize, setImagesSize] = React.useState<number[]>([]);
const carouselSlider = React.useRef<HTMLDivElement>(null);
const nextImage = () => {
setCurrentImage((currentImage + 1) % images.length);
setCurrentImage((currentIndex + 1) % carousel.length);
}
const previousImage = () => {
setCurrentImage((currentImage - 1 + images.length) % images.length);
setCurrentImage((currentIndex - 1 + carousel.length) % carousel.length);
}
React.useEffect(() => {
const interval = setTimeout(() => {
nextImage();
}, 3000);
if (carouselSlider.current) {
const translate = imagesSize.reduce((acc, size, index) => index < currentIndex ? acc + size : acc, 0);
const gap = 25; // TODO: Find a way to get this value (HARDCODED)
carouselSlider.current.style.translate = `-${translate+gap*currentIndex}px`;
}
return () => clearInterval(interval);
}, [currentIndex, imagesSize, carouselSlider]);
function handleImageLoad(i: number, event: React.SyntheticEvent<HTMLImageElement>) {
setImagesSize((old) => {
const newSizes = [...old];
newSizes[i] = (event.target as HTMLImageElement).clientWidth;
return newSizes;
});
}
return (
<div className={"carousel"}>
<div className={"carousel-image"}>
<img src={images[currentImage]} alt={"carousel"}/>
<div className={"carousel-wrapper"}>
<div className={"carousel-slider"} ref={carouselSlider}>
{
carousel.map((image, index) => (
<div key={index} className={'slide'}>
<img
key={index}
className={`slide-image ${index === currentIndex ? "active" : ""}`}
src={image.image}
alt={image.alt ?? "Image Carousel"}
onLoad={(e) => handleImageLoad(index, e)}
/>
<div className={'slide-content'}>
<h3 className={'slide-title'}>{image.title}</h3>
<p className={'slide-text'}>{image.text}</p>
</div>
</div>
))
}
</div>
</div>
<div className={"carousel-buttons"}>
<button onClick={previousImage}>Previous</button>
<button onClick={nextImage}>Next</button>
<div className="carousel-bar">
<button className={"carousel-button button_left"} onClick={previousImage}><NavArrowLeft /></button>
{
carousel.map((_, index) => (
<button
key={index}
className={`carousel-dot ${index === currentIndex ? "dot_active" : ""}`}
onClick={() => setCurrentImage(index)}
/>
))
}
<button className={"carousel-button button_right"} onClick={nextImage}><NavArrowRight /></button>
</div>
</div>
)
}
export const CarouselClickables: React.FC<CarouselClickablesProps> = ({ images, links }) => {
const [currentIndex, setCurrentIndex] = useState(0);
const [fade, setFade] = useState("fade-in");
useEffect(() => {
const interval = setInterval(() => {
handleNextImage();
}, 3000);
return () => clearInterval(interval);
}, [images.length]);
const handleNextImage = () => {
setFade("fade-out");
setTimeout(() => {
setCurrentIndex((prevIndex) => (prevIndex + 1) % images.length);
setFade("fade-in");
}, 500);
};
const handlePreviousImage = () => {
setFade("fade-out");
setTimeout(() => {
setCurrentIndex((prevIndex) => (prevIndex - 1 + images.length) % images.length);
setFade("fade-in");
}, 500);
};
return (
<div className="carousel-container">
<div className="carousel-slide" style={{ transform: `translateX(-${currentIndex * 100}%)` }}>
{images.map((image, index) => (
<a key={index} href={links[index]} target="_blank" rel="noopener noreferrer">
<img
src={image}
alt={`Slide ${index}`}
className={`carousel-image ${fade}`}
style={{ display: index === currentIndex ? 'block' : 'none' }}
/>
</a>
))}
</div>
<div className="carousel-buttons">
<button onClick={handlePreviousImage}>Previous</button>
<button onClick={handleNextImage}>Next</button>
</div>
</div>
);
};