"use client"; import * as React from "react"; import { cn } from "../../lib/utils"; import { motion, AnimatePresence, HTMLMotionProps } from "framer-motion"; interface MenubarContextValue { openMenu: string | null; setOpenMenu: React.Dispatch>; } const MenubarContext = React.createContext(undefined); function useMenubarContext() { const context = React.useContext(MenubarContext); if (!context) { throw new Error("useMenubarContext must be used within a MenubarProvider"); } return context; } interface MenubarProps extends React.HTMLAttributes {} function Menubar({ className, children, ...props }: MenubarProps) { const [openMenu, setOpenMenu] = React.useState(null); React.useEffect(() => { const handleClickOutside = (e: MouseEvent) => { const menubarElement = e.target as HTMLElement; const isClickInsideMenubarOrDropdown = menubarElement.closest('[role="menubar"]') || menubarElement.closest('[role="menu"]'); if (openMenu && !isClickInsideMenubarOrDropdown) { setOpenMenu(null); } }; document.addEventListener("mousedown", handleClickOutside); return () => { document.removeEventListener("mousedown", handleClickOutside); }; }, [openMenu]); return (
{children}
); } interface MenubarMenuProps { value?: string; children: React.ReactNode; } function MenubarMenu({ value, children }: MenubarMenuProps) { // Always call useId unconditionally const generatedId = React.useId(); // Then, use the provided value if it exists, otherwise use the generatedId const menuId = value ?? generatedId; // Use nullish coalescing operator for clarity return (
{children}
); } interface MenubarTriggerProps extends React.ButtonHTMLAttributes {} function MenubarTrigger({ className, children, ...props }: MenubarTriggerProps) { const { openMenu, setOpenMenu } = useMenubarContext(); const triggerRef = React.useRef(null); const [menuId, setMenuId] = React.useState(""); React.useEffect(() => { if (triggerRef.current) { setMenuId(triggerRef.current.parentElement?.getAttribute("data-value") || ""); } }, []); // Empty dependency array means this runs once on mount const isOpen = openMenu === menuId; const handleClick = (e: React.MouseEvent) => { e.stopPropagation(); setOpenMenu(isOpen ? null : menuId); }; return ( ); } interface MenubarContentProps extends HTMLMotionProps<"div"> { // Add any specific props for MenubarContent here if needed, e.g., side, align } function MenubarContent({ className, children, ...props }: MenubarContentProps) { const { openMenu } = useMenubarContext(); const menuContentRef = React.useRef(null); const [currentMenuId, setCurrentMenuId] = React.useState(null); React.useEffect(() => { if (menuContentRef.current) { const parentDataValue = menuContentRef.current.parentElement?.getAttribute("data-value"); setCurrentMenuId(parentDataValue || null); } }, []); const shouldBeOpen = openMenu === currentMenuId; return ( {shouldBeOpen && ( {children} )} ); } interface MenubarItemProps extends React.HTMLAttributes { inset?: boolean; } function MenubarItem({ className, inset, children, ...props }: MenubarItemProps) { const { setOpenMenu } = useMenubarContext(); const handleClick = (e: React.MouseEvent) => { e.stopPropagation(); setOpenMenu(null); if (props.onClick) { props.onClick(e); } }; return (
{children}
); } export { Menubar, MenubarMenu, MenubarTrigger, MenubarContent, MenubarItem, };