SPB Git forge

spb/toveli

Public
14commits 1branches 0releases
1.1 MBsize
maindefault branch
2 h agolast push
TypeScript 50.6% JavaScript 46.1% SQL 2.1% Shell 0.6% CSS 0.5%
2.4 KB · 33 lines tsx
Raw Blame History
1"use client";2import { useState } from "react";3import { IconChevron } from "./icons";45export function Gallery({ images, credit }: { images: string[]; credit: string }) {6  const [i, setI] = useState(0); const [bad, setBad] = useState<Set<number>>(new Set());7  const imgs = images.filter((_, k) => !bad.has(k));8  if (!imgs.length) return <div className="aspect-[16/10] md:aspect-[21/9] rounded-card bg-surface-2 grid place-items-center text-ink-3 text-sm">Aucune photo disponible pour cette annonce</div>;9  const cur = Math.min(i, imgs.length - 1);10  return (11    <figure className="relative">12      <div className="aspect-[16/10] md:aspect-[21/9] rounded-card overflow-hidden bg-surface-2 relative">13        {/* eslint-disable-next-line @next/next/no-img-element */}14        <img key={imgs[cur]} src={imgs[cur]} alt={`Photo ${cur + 1} sur ${imgs.length}`} className="absolute inset-0 w-full h-full object-cover" referrerPolicy="no-referrer" onError={() => setBad((b) => new Set(b).add(images.indexOf(imgs[cur])))} />15        {imgs.length > 1 && (<>16          <button type="button" onClick={() => setI((cur - 1 + imgs.length) % imgs.length)} className="absolute left-3 top-1/2 -translate-y-1/2 p-2 rounded-full bg-white/90 shadow rotate-180" aria-label="Photo précédente"><IconChevron /></button>17          <button type="button" onClick={() => setI((cur + 1) % imgs.length)} className="absolute right-3 top-1/2 -translate-y-1/2 p-2 rounded-full bg-white/90 shadow" aria-label="Photo suivante"><IconChevron /></button>18          <span className="absolute bottom-3 right-3 tv-badge bg-black/55 text-white">{cur + 1} / {imgs.length}</span>19        </>)}20      </div>21      <figcaption className="mt-1.5 text-[11px] text-ink-3">Photos : {credit} (affichées depuis le site d'origine, droits réservés à leur propriétaire).</figcaption>22      {imgs.length > 1 && (23        <div className="mt-2 flex gap-2 overflow-x-auto no-scrollbar">24          {imgs.slice(0, 12).map((src, k) => (25            // eslint-disable-next-line @next/next/no-img-element26            <button key={src} type="button" onClick={() => setI(k)} className={`shrink-0 w-20 h-14 rounded-sm overflow-hidden border-2 ${k === cur ? "border-brand" : "border-transparent"}`} aria-label={`Voir la photo ${k + 1}`}><img src={src} alt="" className="w-full h-full object-cover" loading="lazy" referrerPolicy="no-referrer" /></button>27          ))}28        </div>29      )}30    </figure>31  );32}33