refactor: unlock dev flow, dedupe EcommercePage, extract shell UI components
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
import { useEffect } from "react";
|
||||
import { createPortal } from "react-dom";
|
||||
import { CloseOutlined, DeleteOutlined, DownloadOutlined } from "@ant-design/icons";
|
||||
|
||||
export interface ProductSetPreviewSelection {
|
||||
src: string;
|
||||
label: string;
|
||||
nodeId?: string;
|
||||
cardId?: string;
|
||||
removable?: boolean;
|
||||
}
|
||||
|
||||
interface ProductSetPreviewModalProps {
|
||||
preview: ProductSetPreviewSelection | null;
|
||||
onClose: () => void;
|
||||
onDownload: (preview: ProductSetPreviewSelection) => void;
|
||||
onRemove: (preview: ProductSetPreviewSelection) => void;
|
||||
}
|
||||
|
||||
// 商品套图大图预览弹窗。通过 portal 挂到 body,支持下载/移除。
|
||||
export default function ProductSetPreviewModal({ preview, onClose, onDownload, onRemove }: ProductSetPreviewModalProps) {
|
||||
// Esc 关闭
|
||||
useEffect(() => {
|
||||
if (!preview) return;
|
||||
const handleKey = (event: KeyboardEvent) => {
|
||||
if (event.key === "Escape") onClose();
|
||||
};
|
||||
window.addEventListener("keydown", handleKey);
|
||||
return () => window.removeEventListener("keydown", handleKey);
|
||||
}, [preview, onClose]);
|
||||
|
||||
if (!preview || typeof document === "undefined") return null;
|
||||
|
||||
return createPortal(
|
||||
<div className="product-set-preview-backdrop" role="presentation" onClick={onClose}>
|
||||
<section
|
||||
className="product-set-preview-modal"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-label={preview.label}
|
||||
onClick={(event) => event.stopPropagation()}
|
||||
>
|
||||
<button type="button" className="product-set-preview-close" onClick={onClose} aria-label="关闭预览">
|
||||
<CloseOutlined />
|
||||
</button>
|
||||
<img src={preview.src} alt={preview.label} />
|
||||
<div className="product-set-preview-footer">
|
||||
<strong>{preview.label}</strong>
|
||||
<div className="product-set-preview-actions" aria-label="图片操作">
|
||||
<button
|
||||
type="button"
|
||||
className="product-set-preview-action"
|
||||
onClick={() => {
|
||||
onDownload(preview);
|
||||
}}
|
||||
>
|
||||
<DownloadOutlined />
|
||||
<span>下载</span>
|
||||
</button>
|
||||
{preview.removable ? (
|
||||
<button
|
||||
type="button"
|
||||
className="product-set-preview-action product-set-preview-action--danger"
|
||||
onClick={() => onRemove(preview)}
|
||||
>
|
||||
<DeleteOutlined />
|
||||
<span>移除</span>
|
||||
</button>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>,
|
||||
document.body,
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user