import { NodeViewWrapper } from "@tiptap/react"; import { useCallback, useRef, useState } from "react"; import type { NodeViewProps } from "@tiptap/react"; import { uploadImage } from "./upload"; export function ImageUploadView({ editor, getPos }: NodeViewProps) { const [dragging, setDragging] = useState(false); const [uploading, setUploading] = useState(false); const [urlMode, setUrlMode] = useState(false); const [urlValue, setUrlValue] = useState(""); const fileRef = useRef(null); const replaceWithImage = useCallback( (src: string) => { const pos = getPos(); if (pos === undefined) return; editor .chain() .focus() .deleteRange({ from: pos, to: pos + 1 }) .insertContentAt(pos, { type: "image", attrs: { src }, }) .run(); }, [editor, getPos], ); const handleFiles = useCallback( (files: FileList | File[]) => { const file = Array.from(files).find((f) => f.type.startsWith("image/")); if (!file) return; setUploading(true); uploadImage(file) .then((url) => replaceWithImage(url)) .catch((err) => { console.error("[image-upload]", err); setUploading(false); }); }, [replaceWithImage], ); const onDrop = useCallback( (e: React.DragEvent) => { e.preventDefault(); setDragging(false); if (e.dataTransfer.files.length) handleFiles(e.dataTransfer.files); }, [handleFiles], ); const onDragOver = useCallback((e: React.DragEvent) => { e.preventDefault(); setDragging(true); }, []); const onDragLeave = useCallback(() => setDragging(false), []); const submitUrl = useCallback(() => { const src = urlValue.trim(); if (src) replaceWithImage(src); }, [urlValue, replaceWithImage]); return (
{ if (e.target.files?.length) handleFiles(e.target.files); }} /> {uploading ? ( <>

Uploading...

) : !urlMode ? ( <>

Drag & drop an image, or click to upload

) : (
setUrlValue(e.target.value)} onKeyDown={(e) => { if (e.key === "Enter") submitUrl(); if (e.key === "Escape") setUrlMode(false); }} autoFocus />
)}
); }