SPB Git

Repository Public

Lightbox.tsx 2.8 KB 89 lines tsx

1// -----------------------------------------------------------------------------2// Auto-Ka — Agrégateur de voitures usagées à vendre (province de Québec)3// Auteur : Simon-Pierre Boucher — contact@spboucher.ai4// Lightbox.tsx : visionneuse photos plein écran — clavier, gestes, compteur5// -----------------------------------------------------------------------------6import { useCallback, useEffect, useRef } from "react";78interface Props {9  images: string[];10  index: number;11  onClose: () => void;12  onIndex: (i: number) => void;13  alt: string;14}1516export default function Lightbox({ images, index, onClose, onIndex, alt }: Props) {17  const touchX = useRef<number | null>(null);1819  const prev = useCallback(20    () => onIndex((index - 1 + images.length) % images.length),21    [index, images.length, onIndex]22  );23  const next = useCallback(24    () => onIndex((index + 1) % images.length),25    [index, images.length, onIndex]26  );2728  useEffect(() => {29    const onKey = (e: KeyboardEvent) => {30      if (e.key === "Escape") onClose();31      else if (e.key === "ArrowLeft") prev();32      else if (e.key === "ArrowRight") next();33    };34    window.addEventListener("keydown", onKey);35    document.body.style.overflow = "hidden";36    return () => {37      window.removeEventListener("keydown", onKey);38      document.body.style.overflow = "";39    };40  }, [onClose, prev, next]);4142  // précharger les voisines pour une navigation instantanée43  useEffect(() => {44    [index - 1, index + 1].forEach((i) => {45      const u = images[(i + images.length) % images.length];46      if (u) new Image().src = u;47    });48  }, [index, images]);4950  return (51    <div52      className="lightbox"53      role="dialog"54      aria-label={`Photos — ${alt}`}55      onClick={onClose}56      onTouchStart={(e) => { touchX.current = e.touches[0].clientX; }}57      onTouchEnd={(e) => {58        if (touchX.current == null) return;59        const dx = e.changedTouches[0].clientX - touchX.current;60        touchX.current = null;61        if (dx > 48) prev();62        else if (dx < -48) next();63      }}64    >65      <button className="lb-close" aria-label="Fermer" onClick={onClose}>✕</button>66      <div className="lb-count mono">{index + 1} / {images.length}</div>67      {images.length > 1 && (68        <>69          <button70            className="lb-nav lb-prev"71            aria-label="Photo précédente"72            onClick={(e) => { e.stopPropagation(); prev(); }}73          >←</button>74          <button75            className="lb-nav lb-next"76            aria-label="Photo suivante"77            onClick={(e) => { e.stopPropagation(); next(); }}78          >→</button>79        </>80      )}81      <img82        src={images[index]}83        alt={`${alt} — photo ${index + 1}`}84        onClick={(e) => e.stopPropagation()}85      />86    </div>87  );88}89
Files main
Menu

Theme