"use client"; import { useState, useRef, useEffect } from "react"; import type { GraphNode } from "@/lib/types"; interface ContextMenuProps { node: GraphNode; x: number; y: number; onShowDescription: () => void; onAddComment: () => void; onClaimTask?: () => void; onUnclaimTask?: () => void; onCollapseEpic?: () => void; onUncollapseEpic?: () => void; onFocusEpic?: () => void; onExitFocusEpic?: () => void; onClose: () => void; } export function ContextMenu({ node, x, y, onShowDescription, onAddComment, onClaimTask, onUnclaimTask, onCollapseEpic, onUncollapseEpic, onFocusEpic, onExitFocusEpic, onClose, }: ContextMenuProps) { const menuRef = useRef(null); const [pos, setPos] = useState({ x: 0, y: 0 }); const [visible, setVisible] = useState(false); // Position + clamp to viewport useEffect(() => { if (!menuRef.current) return; const rect = menuRef.current.getBoundingClientRect(); const vw = window.innerWidth; const vh = window.innerHeight; let nx = x + 4; let ny = y + 4; if (nx + rect.width > vw - 16) nx = vw - rect.width - 16; if (nx < 16) nx = 16; if (ny + rect.height > vh - 16) ny = vh - rect.height - 16; if (ny < 16) ny = 16; setPos({ x: nx, y: ny }); setVisible(true); }, [x, y]); // Escape key useEffect(() => { const handler = (e: KeyboardEvent) => { if (e.key === "Escape") onClose(); }; window.addEventListener("keydown", handler); return () => window.removeEventListener("keydown", handler); }, [onClose]); // Click outside (with delay so triggering right-click doesn't immediately close) useEffect(() => { const handler = (e: MouseEvent) => { if (menuRef.current && !menuRef.current.contains(e.target as Node)) { onClose(); } }; const timer = setTimeout( () => window.addEventListener("mousedown", handler), 50 ); return () => { clearTimeout(timer); window.removeEventListener("mousedown", handler); }; }, [onClose]); return (
e.preventDefault()} >
{node.description && ( )} {onClaimTask && ( )} {onUnclaimTask && ( )} {onCollapseEpic && ( )} {onUncollapseEpic && ( )} {onFocusEpic && ( )} {onExitFocusEpic && ( )}
); }