"use client"; import { useEffect, useState } from "react"; import { createPortal } from "react-dom"; import { Swiper, SwiperSlide } from "swiper/react"; import { Navigation, Pagination, Autoplay } from "swiper/modules"; import "swiper/css"; import "swiper/css/navigation"; import "swiper/css/pagination"; import "../globals.css"; import "../assets/main.css"; /** * Carousel "fiche détail" — refonte Stitch (étape 7.a). * * Conserve l'API publique (`images`, `className`) pour ne rien casser côté * consommateurs (`ContentSection`, `ModalGlossaire`). Changements vs version * historique : * * - Pagination bullets teintée `primary` via surcharge inline des variables * Swiper (pas de pollution `globals.css`, cf. même approche que `VignetteCarousel`). * - Flèches Swiper par défaut recolorées `primary` — laissées en chevrons * Swiper pour garder l'empreinte tactile native (le Material Symbol `chevron` * demandait un remplacement complet du markup via slots). * - Conteneur en `rounded-tile overflow-hidden shadow-ambient-sm` plutôt que * `rounded-md shadow-md` — cohérence avec les tokens de la refonte. * - **Lightbox refaite en Stitch** : voile `bg-on-surface/80 backdrop-blur-sm` * (vs `bg-black/10 backdrop-blur-2xl` qui dépixellisait l'image), image en * `object-contain` (ne déforme plus les photos verticales), bouton close rond * Material Symbol, fermeture Esc + clic voile. */ interface CarouselProps { images: Array<{ url: string; alt: string }>; className?: string; } export default function Carousel({ images, className }: CarouselProps) { const [selectedImage, setSelectedImage] = useState(null); useEffect(() => { if (!selectedImage) return; document.body.classList.add("overflow-hidden"); const handleKey = (e: KeyboardEvent) => { if (e.key === "Escape") setSelectedImage(null); }; window.addEventListener("keydown", handleKey); return () => { document.body.classList.remove("overflow-hidden"); window.removeEventListener("keydown", handleKey); }; }, [selectedImage]); return ( <>
1} className={`w-full ${className || "h-64"}`} style={ { "--swiper-navigation-color": "#26445d", "--swiper-navigation-size": "28px", "--swiper-pagination-color": "#26445d", "--swiper-pagination-bullet-inactive-color": "#ffffff", "--swiper-pagination-bullet-inactive-opacity": "0.6", "--swiper-pagination-bullet-size": "8px", "--swiper-pagination-bullet-horizontal-gap": "4px", } as React.CSSProperties } > {images.map((img, index) => ( {img.alt} setSelectedImage(img.url)} loading="lazy" /> ))}
{selectedImage && createPortal(
setSelectedImage(null)} role="dialog" aria-modal="true" aria-label="Image agrandie" >
e.stopPropagation()} > Aperçu en taille réelle
, document.body )} ); }