Files

41 lines
2.5 KiB
TypeScript
Raw Permalink 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.
"use client";
import { useEffect, useState } from "react";
type Attachment = { name?: string; filename?: string; url?: string; externalLink?: string; type?: string; size?: string | number };
function resourceUrl(attachment: Attachment, sourceBaseUrl?: string) {
if (attachment.url) return attachment.url;
if (attachment.externalLink) return attachment.externalLink;
if (!sourceBaseUrl || !attachment.name || !attachment.filename) return null;
const resourceName = attachment.name.split("/").map(encodeURIComponent).join("/");
return `${sourceBaseUrl.replace(/\/$/, "")}/file/${resourceName}/${encodeURIComponent(attachment.filename)}`;
}
export function Attachments({ json, sourceBaseUrl, compact = false }: { json: string; sourceBaseUrl?: string | null; compact?: boolean }) {
const [activeImage, setActiveImage] = useState<{ href: string; label: string } | null>(null);
useEffect(() => {
const closeOnEscape = (event: KeyboardEvent) => { if (event.key === "Escape") setActiveImage(null); };
window.addEventListener("keydown", closeOnEscape);
return () => window.removeEventListener("keydown", closeOnEscape);
}, []);
let attachments: Attachment[] = [];
try { attachments = JSON.parse(json); } catch { return null; }
const displayable = attachments.map((attachment) => ({ attachment, href: resourceUrl(attachment, sourceBaseUrl || undefined) })).filter((item): item is { attachment: Attachment; href: string } => Boolean(item.href));
if (!displayable.length) return null;
return <>
<section className={`attachments${compact ? " attachments-compact" : ""}`} aria-label="附件">
{displayable.map(({ attachment, href }) => {
const label = attachment.filename || attachment.name || "附件";
if (attachment.type?.startsWith("image/")) return <button type="button" className="attachment-image" onClick={() => setActiveImage({ href, label })} key={href} aria-label={`放大檢視:${label}`}><img src={href} alt={label} /></button>;
return <a className="attachment-file" href={href} target="_blank" rel="noreferrer" key={href}>📎 {label}</a>;
})}
</section>
{activeImage && <div className="image-lightbox" role="dialog" aria-modal="true" aria-label={activeImage.label} onClick={() => setActiveImage(null)}>
<button type="button" className="image-lightbox-close" onClick={() => setActiveImage(null)} aria-label="關閉圖片檢視">×</button>
<img src={activeImage.href} alt={activeImage.label} onClick={(event) => event.stopPropagation()} />
</div>}
</>;
}