import { NodeViewWrapper } from "@tiptap/react"; import { useState, useEffect, useRef, useCallback } from "react"; import type { NodeViewProps } from "@tiptap/react"; export function FootnoteView({ node, editor, getPos, updateAttributes }: NodeViewProps) { const content = node.attrs.content as string; const [index, setIndex] = useState(0); const [showTooltip, setShowTooltip] = useState(false); const [editing, setEditing] = useState(false); const [draft, setDraft] = useState(content); const tooltipTimer = useRef>(); const inputRef = useRef(null); // Auto-number: count all footnotes in document order useEffect(() => { const computeIndex = () => { let i = 0; const pos = getPos(); editor.state.doc.descendants((n, p) => { if (n.type.name === "footnote") { i++; if (p === pos) setIndex(i); } }); }; computeIndex(); editor.on("update", computeIndex); return () => { editor.off("update", computeIndex); }; }, [editor, getPos]); useEffect(() => { if (editing && inputRef.current) { inputRef.current.focus(); inputRef.current.select(); } }, [editing]); const handleMouseEnter = useCallback(() => { if (!editing) tooltipTimer.current = setTimeout(() => setShowTooltip(true), 300); }, [editing]); const handleMouseLeave = useCallback(() => { clearTimeout(tooltipTimer.current); if (!editing) setShowTooltip(false); }, [editing]); const startEdit = useCallback(() => { setDraft(content); setEditing(true); setShowTooltip(true); }, [content]); const commitEdit = useCallback(() => { updateAttributes({ content: draft }); setEditing(false); setShowTooltip(false); }, [draft, updateAttributes]); const remove = useCallback(() => { const pos = getPos(); if (typeof pos === "number") { editor.chain().focus().deleteRange({ from: pos, to: pos + 1 }).run(); } }, [editor, getPos]); return ( {index} {showTooltip && (
{editing ? (