import { useEffect, useRef, useState } from "react"; import type { BodyElement } from "../../lib/api"; interface ElementContextMenuProps { x: number; y: number; element: BodyElement; elementIndex: number; selectedText?: string; onClose: () => void; onFocus: (index: number) => void; onDelete: (index: number) => void; onDuplicate: (index: number) => void; onConvert: (index: number, toType: string) => void; onGenerate: (index: number, action: string) => void; onCreateTask: (index: number, selectedText?: string) => void; } export function ElementContextMenu({ x, y, element, elementIndex, selectedText, onClose, onFocus, onDelete, onDuplicate, onConvert, onGenerate, onCreateTask, }: ElementContextMenuProps) { const menuRef = useRef(null); const [activeSubmenu, setActiveSubmenu] = useState<"generate" | "convert" | null>(null); // Close on click outside useEffect(() => { const handleClickOutside = (e: MouseEvent) => { if (menuRef.current && !menuRef.current.contains(e.target as Node)) { onClose(); } }; const handleKeyDown = (e: KeyboardEvent) => { if (e.key === "Escape") { onClose(); } }; document.addEventListener("mousedown", handleClickOutside); document.addEventListener("keydown", handleKeyDown); return () => { document.removeEventListener("mousedown", handleClickOutside); document.removeEventListener("keydown", handleKeyDown); }; }, [onClose]); // Adjust position if menu would overflow viewport useEffect(() => { if (menuRef.current) { const rect = menuRef.current.getBoundingClientRect(); const viewportWidth = window.innerWidth; const viewportHeight = window.innerHeight; if (rect.right > viewportWidth) { menuRef.current.style.left = `${x - rect.width}px`; } if (rect.bottom > viewportHeight) { menuRef.current.style.top = `${y - rect.height}px`; } } }, [x, y]); const getElementTypeLabel = () => { switch (element.type) { case "heading": return `Heading ${element.level}`; case "paragraph": return "Paragraph"; case "code": return element.language ? `Code (${element.language})` : "Code"; case "list": return element.ordered ? "Ordered List" : "Bullet List"; case "chart": return `Chart (${element.chartType})`; case "image": return "Image"; default: return "Element"; } }; const menuItemClass = "w-full px-3 py-1.5 text-left text-xs font-mono text-[#9bc3ff] hover:bg-[rgba(117,170,252,0.1)] flex items-center gap-2"; const submenuTriggerClass = "w-full px-3 py-1.5 text-left text-xs font-mono text-[#9bc3ff] hover:bg-[rgba(117,170,252,0.1)] flex items-center justify-between"; const dividerClass = "border-t border-[rgba(117,170,252,0.2)] my-1"; return (
{/* Header showing element type */}
{getElementTypeLabel()} [{elementIndex}]
{/* Focus action */} {/* Create task action */}
{/* Generate submenu */}
setActiveSubmenu("generate")} onMouseLeave={() => setActiveSubmenu(null)} > {activeSubmenu === "generate" && (
)}
{/* Convert submenu */}
setActiveSubmenu("convert")} onMouseLeave={() => setActiveSubmenu(null)} > {activeSubmenu === "convert" && (
{element.type !== "paragraph" && ( )} {element.type !== "list" && ( <> )} {element.type !== "code" && ( )} {element.type !== "heading" && ( <>
{[1, 2, 3, 4, 5, 6].map((level) => ( ))} )}
)}
{/* Duplicate */} {/* Delete */}
); }