/** * Copyright (c) 2023-present Plane Software, Inc. and contributors * SPDX-License-Identifier: AGPL-3.0-only * See the LICENSE file for details. */ import * as React from "react"; import { Menu as BaseMenu } from "@base-ui-components/react/menu"; import { MoreHorizontal } from "lucide-react"; import { ChevronDownIcon, ChevronRightIcon } from "../icons"; import { cn } from "../utils/classname"; import type { TMenuProps, TSubMenuProps, TMenuItemProps } from "./types"; // Context for main menu to communicate with submenus const MenuContext = React.createContext<{ closeAllSubmenus: () => void; registerSubmenu: (closeSubmenu: () => void) => () => void; } | null>(null); // SubMenu context for closing submenu from nested items const SubMenuContext = React.createContext<{ closeSubmenu: () => void } | null>(null); // Hook to use submenu context const useSubMenu = () => React.useContext(SubMenuContext); // SubMenu implementation function SubMenu(props: TSubMenuProps) { const { children, trigger, disabled = false, className = "" } = props; return ( {trigger} {children} ); } function MenuItem(props: TMenuItemProps) { const { children, disabled = false, onClick, className } = props; const submenuContext = useSubMenu(); return ( { close(); onClick?.(e); submenuContext?.closeSubmenu(); }} > {children} ); } function Menu(props: TMenuProps) { const { ariaLabel, buttonClassName = "", customButtonClassName = "", customButtonTabIndex = 0, children, customButton, disabled = false, ellipsis = false, label, maxHeight = "md", noBorder = false, noChevron = false, optionsClassName = "", menuItemsClassName = "", verticalEllipsis = false, menuButtonOnClick, onMenuClose, tabIndex, openOnHover = false, handleOpenChange = () => {}, } = props; const [isOpen, setIsOpen] = React.useState(false); // refs const submenuClosersRef = React.useRef void>>(new Set()); const closeAllSubmenus = React.useCallback(() => { submenuClosersRef.current.forEach((closeSubmenu) => closeSubmenu()); }, []); const registerSubmenu = React.useCallback((closeSubmenu: () => void) => { submenuClosersRef.current.add(closeSubmenu); return () => { submenuClosersRef.current.delete(closeSubmenu); }; }, []); const openDropdown = () => { setIsOpen(true); }; const closeDropdown = React.useCallback(() => { if (isOpen) { closeAllSubmenus(); onMenuClose?.(); } setIsOpen(false); }, [isOpen, closeAllSubmenus, onMenuClose]); const handleMenuButtonClick = (e: React.MouseEvent) => { e.stopPropagation(); e.preventDefault(); if (isOpen) { closeDropdown(); } else { openDropdown(); } if (menuButtonOnClick) menuButtonOnClick(); }; return ( {customButton ? ( {customButton} ) : ( <> {ellipsis || verticalEllipsis ? ( ) : ( {label} {!noChevron && } )} )} {children} ); } Menu.MenuItem = MenuItem; Menu.SubMenu = SubMenu; export { Menu };