Files
endorphin-tabularium-docs/apps/docs/src/components/FigureGallery.tsx
T
codex-bot bd932c36b2
Verify and deploy Tabularium / verify-and-deploy (pull_request) Failing after 5m44s
Add a compressed illustration carousel with slide indicators
2026-10-28 18:16:17 +08:00

67 lines
2.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { useId, useState } from "react";
import { ArchitectureFigure } from "./ArchitectureFigure";
type Slide = { source: string; alt: string };
export function FigureGallery({ slides }: { slides: Slide[] }) {
const [active, setActive] = useState(0);
const viewport = useId();
const slide = slides[active];
if (!slide) return null;
function move(offset: number) {
setActive((current) => (current + offset + slides.length) % slides.length);
}
return (
<section
className="figure-gallery"
role="region"
aria-roledescription="轮播"
aria-label="文档配图"
onKeyDown={(event) => {
if ((event.target as HTMLElement).closest("dialog")) return;
if (event.key === "ArrowLeft" || event.key === "ArrowRight") {
event.preventDefault();
move(event.key === "ArrowLeft" ? -1 : 1);
}
}}
>
<div
id={viewport}
role="group"
aria-roledescription="幻灯片"
aria-label={`第 ${active + 1} 张,共 ${slides.length} 张`}
>
<ArchitectureFigure key={slide.source} source={slide.source} alt={slide.alt} />
</div>
<div className="figure-gallery-controls">
<button type="button" className="figure-gallery-arrow" aria-label="上一张图片" aria-controls={viewport} onClick={() => move(-1)}>
<span aria-hidden="true">‹</span>
</button>
<div className="figure-gallery-dots" role="group" aria-label="选择图片">
{slides.map((item, index) => (
<button
key={item.source}
type="button"
className="figure-gallery-dot"
aria-label={`显示第 ${index + 1} 张图片`}
aria-current={index === active ? "true" : undefined}
aria-controls={viewport}
onClick={() => setActive(index)}
>
<span aria-hidden="true" />
</button>
))}
</div>
<button type="button" className="figure-gallery-arrow" aria-label="下一张图片" aria-controls={viewport} onClick={() => move(1)}>
<span aria-hidden="true">›</span>
</button>
</div>
<p className="sr-only" role="status" aria-live="polite" aria-atomic="true">
第 {active + 1} 张,共 {slides.length} 张:{slide.alt}
</p>
</section>
);
}