"use client" import { useState, useRef, useEffect } from "react" import { createPortal } from "react-dom" import Link from "next/link" import { cn } from "@/lib/utils" interface MenuItem { label?: string href?: string onClick?: () => void disabled?: boolean danger?: boolean divider?: boolean } interface DropdownMenuProps { items: MenuItem[] align?: "left" | "right" } export function DropdownMenu({ items, align = "right" }: DropdownMenuProps) { const [open, setOpen] = useState(false) const [position, setPosition] = useState({ top: 0, left: 0 }) const triggerRef = useRef(null) const dropdownRef = useRef(null) // Calculate dropdown position useEffect(() => { if (open && triggerRef.current) { const rect = triggerRef.current.getBoundingClientRect() const dropdownWidth = 192 // w-48 = 12rem = 192px setPosition({ top: rect.bottom + 4, left: align === "right" ? rect.right - dropdownWidth : rect.left, }) } }, [open, align]) // Click outside handler useEffect(() => { function handleClickOutside(event: MouseEvent) { const target = event.target as Node if ( triggerRef.current && !triggerRef.current.contains(target) && dropdownRef.current && !dropdownRef.current.contains(target) ) { setOpen(false) } } if (open) { document.addEventListener("mousedown", handleClickOutside) } return () => document.removeEventListener("mousedown", handleClickOutside) }, [open]) return ( <> {/* Dropdown - rendered via portal */} {open && typeof document !== "undefined" && createPortal(
{items.map((item, idx) => { if (item.divider) { return
} const className = cn( "w-full px-3 py-2 text-sm text-left transition-colors flex items-center gap-2 cursor-pointer", item.disabled ? "text-text-muted cursor-not-allowed" : item.danger ? "text-status-error hover:bg-[#222222]" : "text-text-secondary hover:bg-[#222222] hover:text-text-primary" ) if (item.href && !item.disabled) { return ( setOpen(false)} > {item.label} ) } return ( ) })}
, document.body )} ) }