Added images to the first step

This commit is contained in:
cagsun 2025-06-27 00:41:25 +02:00
parent e3cd2c7f00
commit 08cac5f022
20 changed files with 154 additions and 89 deletions

View file

@ -1,44 +1,77 @@
// src/components/ImageCarousel.js
import React from 'react';
import clsx from 'clsx';
import React, { useState, useEffect, useCallback } from 'react';
import styles from './ImageCarousel.module.css';
export default function ImageCarousel({ images }) {
const [current, setCurrent] = React.useState(0);
const [currentIndex, setCurrentIndex] = useState(0);
const [isOpen, setIsOpen] = useState(false);
if (!images || images.length === 0) return null;
const currentImage = images[currentIndex];
const closeLightbox = () => setIsOpen(false);
const handleKeyDown = useCallback((e) => {
if (!isOpen) return;
switch (e.key) {
case 'Escape':
closeLightbox();
break;
case 'ArrowRight':
setCurrentIndex((prev) => (prev + 1) % images.length);
break;
case 'ArrowLeft':
setCurrentIndex((prev) => (prev - 1 + images.length) % images.length);
break;
}
}, [isOpen, images.length]);
useEffect(() => {
document.addEventListener('keydown', handleKeyDown);
return () => document.removeEventListener('keydown', handleKeyDown);
}, [handleKeyDown]);
return (
<div className={styles.carousel}>
<div className={styles.viewport}>
{images.map((img, idx) => (
<div
key={idx}
className={clsx(styles.slide, { [styles.active]: idx === current })}
>
<img src={img.src} alt={img.alt || `Slide ${idx + 1}`} />
{img.caption && <div className={styles.caption}>{img.caption}</div>}
</div>
))}
<>
<div className={styles.carousel}>
<img
src={currentImage.src}
alt={currentImage.caption}
className={styles.mainImage}
onClick={() => setIsOpen(true)}
/>
<div className={styles.thumbnails}>
{images.map((img, i) => (
<img
key={i}
src={img.src}
alt={img.caption}
className={`${styles.thumbnail} ${i === currentIndex ? styles.active : ''}`}
onClick={() => setCurrentIndex(i)}
/>
))}
</div>
</div>
<div className={styles.thumbnails}>
{images.map((img, idx) => (
{isOpen && (
<div className={styles.lightbox} onClick={closeLightbox}>
<button className={styles.closeButton} onClick={closeLightbox}>&times;</button>
<button
key={idx}
onClick={() => setCurrent(idx)}
className={clsx(styles.thumbnailWrapper, {
[styles.activeThumbnail]: idx === current,
})}
className={`${styles.navButton} ${styles.left}`}
onClick={(e) => { e.stopPropagation(); setCurrentIndex((prev) => (prev - 1 + images.length) % images.length); }}
>
<img
src={img.src}
alt={`Thumbnail ${idx + 1}`}
className={styles.thumbnail}
/>
&#10094;
</button>
))}
</div>
</div>
<img src={currentImage.src} alt={currentImage.caption} className={styles.lightboxImage} />
<button
className={`${styles.navButton} ${styles.right}`}
onClick={(e) => { e.stopPropagation(); setCurrentIndex((prev) => (prev + 1) % images.length); }}
>
&#10095;
</button>
</div>
)}
</>
);
}

View file

@ -1,66 +1,97 @@
.carousel {
max-width: 600px;
margin: 2rem auto;
text-align: center;
display: flex;
flex-direction: column;
align-items: center;
}
.viewport {
position: relative;
min-height: 300px;
}
.slide {
display: none;
}
.slide.active {
display: block;
}
.slide img {
width: 100%;
height: auto;
border-radius: 0.5rem;
}
.caption {
margin-top: 0.5rem;
font-size: 0.9rem;
color: #666;
.mainImage {
max-width: 100%;
max-height: 300px;
cursor: zoom-in;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.thumbnails {
margin-top: 1rem;
display: flex;
justify-content: center;
flex-wrap: wrap;
gap: 0.5rem;
}
.thumbnailWrapper {
border: none;
background: none;
padding: 0;
cursor: pointer;
outline: none;
border-radius: 0.375rem;
transition: box-shadow 0.2s ease;
margin-top: 1rem;
flex-wrap: wrap;
justify-content: center;
}
.thumbnail {
width: 60px;
height: 40px;
height: 60px;
object-fit: cover;
border-radius: 0.375rem;
opacity: 0.8;
transition: opacity 0.2s ease;
opacity: 0.6;
border-radius: 4px;
cursor: pointer;
transition: 0.2s;
border: 2px solid transparent;
}
.thumbnailWrapper:hover .thumbnail,
.thumbnailWrapper.activeThumbnail .thumbnail {
.thumbnail:hover,
.thumbnail.active {
opacity: 1;
border-color: #0078e7;
}
.activeThumbnail {
box-shadow: 0 0 0 2px #333;
/* Lightbox overlay */
.lightbox {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background-color: rgba(0, 0, 0, 0.85);
display: flex;
justify-content: center;
align-items: center;
z-index: 9999;
cursor: default;
overflow: hidden;
}
.lightboxImage {
max-width: 90vw;
max-height: 90vh;
border-radius: 8px;
z-index: 1;
}
/* Close (×) button */
.closeButton {
position: absolute;
top: 20px;
right: 30px;
font-size: 2.5rem;
color: white;
background: none;
border: none;
cursor: pointer;
z-index: 2;
}
/* Left/right arrows */
.navButton {
position: absolute;
top: 50%;
font-size: 3rem;
color: white;
background: none;
border: none;
cursor: pointer;
padding: 0 1rem;
z-index: 2;
transform: translateY(-50%);
user-select: none;
}
.left {
left: 20px;
}
.right {
right: 20px;
}