Files
jiang13-forum/frontend/src/components/ImageLightbox.tsx
freefire 060b7707cb 支持公开用户主页、帖子图缩略图与编辑器图组排版。
新增用户签名与活动统计、图片灯箱;正文按需生成缩略图;TipTap 支持多图分组与环绕排版,并注入站点标题避免刷新闪烁。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-01 12:29:50 +08:00

59 lines
1.6 KiB
TypeScript

import { useEffect } from 'react';
import { X } from 'lucide-react';
import { createPortal } from 'react-dom';
interface Props {
src: string | null;
alt?: string;
open: boolean;
onClose: () => void;
}
/** 帖子正文图片灯箱:展示原图,点击遮罩 / Esc / 关闭按钮退出 */
export default function ImageLightbox({ src, alt = '', open, onClose }: Props) {
useEffect(() => {
if (!open) return;
const onKey = (e: KeyboardEvent) => {
if (e.key === 'Escape') onClose();
};
const prev = document.body.style.overflow;
document.body.style.overflow = 'hidden';
window.addEventListener('keydown', onKey);
return () => {
document.body.style.overflow = prev;
window.removeEventListener('keydown', onKey);
};
}, [open, onClose]);
if (!open || !src) return null;
return createPortal(
<div className="image-lightbox" role="dialog" aria-modal="true" aria-label="查看原图">
<button
type="button"
className="image-lightbox-backdrop"
aria-label="关闭"
onClick={onClose}
/>
<button
type="button"
className="image-lightbox-close"
aria-label="关闭"
onClick={onClose}
>
<X size={20} aria-hidden />
</button>
<div className="image-lightbox-stage">
<img
src={src}
alt={alt || '原图'}
className="image-lightbox-img"
decoding="async"
/>
</div>
<p className="image-lightbox-hint"></p>
</div>,
document.body,
);
}