"use client"; import { useEffect, useRef, useState } from "react"; import { createPortal } from "react-dom"; import { X } from "lucide-react"; import { cn } from "@/lib/utils"; export interface RadialMenuItem { id: string; icon: React.ReactNode; label: string; onClick: () => void; disabled?: boolean; destructive?: boolean; } interface RadialMenuProps { items: RadialMenuItem[]; isOpen: boolean; position: { x: number; y: number }; onClose: () => void; size?: number; } export function RadialMenu({ items, isOpen, position, onClose, size = 200, }: RadialMenuProps) { const [mounted, setMounted] = useState(false); const [activeIndex, setActiveIndex] = useState(-1); const [animatingIn, setAnimatingIn] = useState(false); const menuRef = useRef(null); useEffect(() => { setMounted(true); }, []); useEffect(() => { if (isOpen) { requestAnimationFrame(() => requestAnimationFrame(() => setAnimatingIn(true))); } else { setAnimatingIn(false); } }, [isOpen]); useEffect(() => { if (!isOpen) return; setActiveIndex(-1); const handleKeyDown = (e: KeyboardEvent) => { if (e.key === "Escape") { e.preventDefault(); onClose(); return; } if (e.key === "Enter" && activeIndex >= 0 && activeIndex < items.length) { e.preventDefault(); const item = items[activeIndex]; if (!item.disabled) { item.onClick(); onClose(); } return; } if (e.key === "ArrowRight" || e.key === "ArrowDown") { e.preventDefault(); setActiveIndex((prev) => { let next = prev + 1; if (next >= items.length) next = 0; let loops = 0; while (items[next]?.disabled && loops < items.length) { next = next + 1 >= items.length ? 0 : next + 1; loops++; } return next; }); return; } if (e.key === "ArrowLeft" || e.key === "ArrowUp") { e.preventDefault(); setActiveIndex((prev) => { let next = prev - 1; if (next < 0) next = items.length - 1; let loops = 0; while (items[next]?.disabled && loops < items.length) { next = next - 1 < 0 ? items.length - 1 : next - 1; loops++; } return next; }); return; } }; document.addEventListener("keydown", handleKeyDown); return () => document.removeEventListener("keydown", handleKeyDown); }, [isOpen, activeIndex, items, onClose]); const radius = size / 2 - 28; const center = size / 2; if (!mounted) return null; return createPortal( <>
{items.map((item, index) => { const angle = (index / items.length) * 2 * Math.PI - Math.PI / 2; const x = center + radius * Math.cos(angle); const y = center + radius * Math.sin(angle); const itemSize = 40; return (
); })}
, document.body ); }