Present architecture as a plain click-to-enlarge image
Verify and deploy Tabularium / verify-and-deploy (push) Successful in 5m3s

This commit is contained in:
2026-10-27 21:17:12 +08:00
parent 36840ef1d4
commit 0a4057ac2a
6 changed files with 141 additions and 224 deletions
+39 -77
View File
@@ -1,104 +1,66 @@
import { useRef, useState } from "react";
const source = "/figures/endor-architecture.svg";
const figureWidth = 2000;
export function ArchitectureFigure({ alt }: { alt: string }) {
const dialog = useRef<HTMLDialogElement>(null);
const trigger = useRef<HTMLButtonElement>(null);
const pan = useRef<HTMLDivElement>(null);
const [fit, setFit] = useState(true);
const [zoom, setZoom] = useState(1);
const stage = useRef<HTMLDivElement>(null);
const [originalSize, setOriginalSize] = useState(false);
function open() {
setFit(true);
setZoom(1);
setOriginalSize(false);
dialog.current?.showModal();
}
function changeZoom(delta: number) {
const current = fit
? ((pan.current?.clientWidth ?? figureWidth + 16) - 16) / figureWidth
: zoom;
setFit(false);
setZoom(
Math.max(0.1, Math.min(2, Math.round((current + delta) * 100) / 100)),
);
}
return (
<figure className="architecture-figure">
<span className="figure-toolbar">
<span>ENDOR / 架构拓扑</span>
<button ref={trigger} onClick={open}>
放大查看 ↗
</button>
</span>
<>
<button
className="figure-preview"
ref={trigger}
className="architecture-preview"
onClick={open}
aria-label="放大 Endor 架构图"
>
{/* eslint-disable-next-line @next/next/no-img-element */}
<img src={source} alt={alt} />
</button>
<span className="figure-caption">
多区域拓扑 · 独立分片群与统一备份 · 点击放大查看
</span>
<dialog
ref={dialog}
className="figure-dialog"
aria-label="Endor 架构图查看器"
className="image-lightbox"
aria-label="Endor 架构图大图"
onClick={(event) => {
if (
event.target === dialog.current ||
event.target === stage.current
) {
dialog.current?.close();
}
}}
onClose={() => trigger.current?.focus()}
>
<div className="figure-viewer-toolbar">
<strong>Endor 架构拓扑</strong>
<div className="figure-controls">
<button
onClick={() => changeZoom(-0.25)}
aria-label="缩小架构图"
disabled={!fit && zoom <= 0.1}
>
−
</button>
<span aria-live="polite">
{fit ? "适应宽度" : `${Math.round(zoom * 100)}%`}
</span>
<button
onClick={() => changeZoom(0.25)}
aria-label="放大架构图"
disabled={!fit && zoom >= 2}
>
+
</button>
<button onClick={() => setFit(true)}>适应</button>
<a href={source} download="endor-architecture.svg">
下载 SVG
</a>
<button
onClick={() => dialog.current?.close()}
aria-label="关闭架构图"
>
关闭 ×
</button>
</div>
</div>
<div
ref={pan}
className="figure-pan"
tabIndex={0}
aria-label="架构图滚动区域,可使用方向键滚动"
<button
className="image-lightbox-close"
aria-label="关闭图片"
onClick={() => dialog.current?.close()}
>
{/* eslint-disable-next-line @next/next/no-img-element */}
<img
src={source}
alt={alt}
style={{ width: fit ? "100%" : figureWidth * zoom }}
/>
×
</button>
<div
ref={stage}
className="image-lightbox-stage"
tabIndex={0}
aria-label="图片滚动区域"
>
<button
className={`image-lightbox-picture${originalSize ? " original-size" : ""}`}
onClick={() => setOriginalSize((value) => !value)}
aria-label={originalSize ? "图片适应屏幕" : "查看图片原尺寸"}
>
{/* eslint-disable-next-line @next/next/no-img-element */}
<img src={source} alt={alt} />
</button>
</div>
<p className="figure-viewer-hint">
实线:请求 /
依赖 · 紫色:身份 · 蓝色点线:回源 · 金色:统一备份。放大后可横向、纵向滚动,Esc
关闭。
</p>
</dialog>
</figure>
</>
);
}