"use client"; import { useEffect, useState } from "react"; import { createPortal } from "react-dom"; import { NodeViewWrapper } from "@tiptap/react"; import type { NodeViewProps } from "@tiptap/react"; import { Maximize2, Download, Link as LinkIcon, Trash2, } from "lucide-react"; import { toast } from "sonner"; import { cn } from "@multica/ui/lib/utils"; // --------------------------------------------------------------------------- // Lightbox — full-screen image preview (ESC or click backdrop to close) // --------------------------------------------------------------------------- function ImageLightbox({ src, alt, onClose, }: { src: string; alt: string; onClose: () => void; }) { useEffect(() => { const handler = (e: KeyboardEvent) => { if (e.key === "Escape") onClose(); }; document.addEventListener("keydown", handler); return () => document.removeEventListener("keydown", handler); }, [onClose]); return createPortal(
{alt} e.stopPropagation()} />
, document.body, ); } // --------------------------------------------------------------------------- // Image NodeView — renders img with hover toolbar + lightbox // --------------------------------------------------------------------------- function ImageView({ node, editor, selected, deleteNode }: NodeViewProps) { const src = node.attrs.src as string; const alt = (node.attrs.alt as string) || ""; const title = node.attrs.title as string | undefined; const uploading = node.attrs.uploading as boolean; const [lightbox, setLightbox] = useState(false); const isEditable = editor.isEditable; const handleView = () => setLightbox(true); const handleDownload = () => { // Cross-origin CDN images can't be fetched as blob (CORS), // and is ignored for cross-origin URLs. // Open in new tab — user can right-click → Save As. window.open(src, "_blank", "noopener,noreferrer"); }; const handleCopyLink = async () => { try { await navigator.clipboard.writeText(src); toast.success("Link copied"); } catch { toast.error("Failed to copy link"); } }; return (
{alt} {!uploading && (
e.stopPropagation()} onClick={(e) => e.stopPropagation()} > {isEditable && ( )}
)}
{lightbox && ( setLightbox(false)} /> )}
); } export { ImageView, ImageLightbox };