ERP-node/frontend/components/dataflow/node-editor/NodeContextMenu.tsx

68 lines
1.8 KiB
TypeScript

"use client";
import { useCallback, useEffect, useRef } from "react";
import { Pencil, Copy, Trash2, Scissors } from "lucide-react";
interface ContextMenuItem {
label: string;
icon: React.ReactNode;
onClick: () => void;
danger?: boolean;
disabled?: boolean;
}
interface NodeContextMenuProps {
x: number;
y: number;
items: ContextMenuItem[];
onClose: () => void;
}
export function NodeContextMenu({ x, y, items, onClose }: NodeContextMenuProps) {
const ref = useRef<HTMLDivElement>(null);
useEffect(() => {
const handleClick = (e: MouseEvent) => {
if (ref.current && !ref.current.contains(e.target as Node)) {
onClose();
}
};
const handleEsc = (e: KeyboardEvent) => {
if (e.key === "Escape") onClose();
};
document.addEventListener("mousedown", handleClick);
document.addEventListener("keydown", handleEsc);
return () => {
document.removeEventListener("mousedown", handleClick);
document.removeEventListener("keydown", handleEsc);
};
}, [onClose]);
return (
<div
ref={ref}
className="fixed z-[200] min-w-[160px] rounded-lg border border-zinc-700 bg-zinc-900 py-1 shadow-xl shadow-black/40"
style={{ left: x, top: y }}
>
{items.map((item, i) => (
<button
key={i}
onClick={() => {
item.onClick();
onClose();
}}
disabled={item.disabled}
className={`flex w-full items-center gap-2.5 px-3 py-2 text-left text-xs transition-colors disabled:opacity-30 ${
item.danger
? "text-pink-400 hover:bg-pink-500/10"
: "text-zinc-300 hover:bg-zinc-800"
}`}
>
{item.icon}
{item.label}
</button>
))}
</div>
);
}