UNPKG

@octopusdeploy/design-system-components

Version:
59 lines (58 loc) 3.55 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.NestedMenu = NestedMenu; const jsx_runtime_1 = require("react/jsx-runtime"); const css_1 = require("@emotion/css"); const design_system_icons_1 = require("@octopusdeploy/design-system-icons"); const react_1 = require("react"); const CustomMenu_1 = require("../..//Menu/CustomMenu"); const OctopusRoutingContext_1 = require("../../../routing/OctopusRoutingContext"); const useMenuState_1 = require("../../Menu/useMenuState"); const MenuItemButton_1 = require("../MenuItemButton/MenuItemButton"); const SimpleMenuItems_1 = require("../SimpleMenuItems"); function NestedMenu({ childMenuItems, label, onClose, autoFocus, compact }) { const isUrlActive = (0, OctopusRoutingContext_1.useIsUrlActive)(); const isActive = hasActiveDescendants(childMenuItems, isUrlActive); const [openMenu, menuState, buttonAriaAttributes] = (0, useMenuState_1.useMenuState)(); const closeMenu = menuState.onClose; const close = (0, react_1.useCallback)(() => { onClose?.(); closeMenu(); }, [closeMenu, onClose]); const onKeyDown = (0, react_1.useCallback)((ev) => { // We want to stop propagation so that navigation keydown events (like arrow up/down or home/end) // don't bubble up to the parent menu, causing focus to change for that menu too. // Normally, the modal also handles escape events, but we also don't want these to bubble up, // and instead want to let our onClose call chain handle closing of all the nested menus, // so that we have a single way of closing menus ev.stopPropagation(); if (ev.key === "Escape") { close(); } }, [close]); return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(MenuItemButton_1.MenuItemButton, { isSelected: isActive, onClick: openMenu, autoFocus: autoFocus, compact: compact, menuButtonAttributes: buttonAriaAttributes, children: (0, jsx_runtime_1.jsxs)("div", { className: buttonContainerStyles, children: [(0, jsx_runtime_1.jsx)("div", { className: buttonLabelStyles, children: label }), (0, jsx_runtime_1.jsx)(design_system_icons_1.CaretRightIcon, { size: 20 })] }) }), (0, jsx_runtime_1.jsx)(CustomMenu_1.CustomMenu, { menuId: menuState.menuId, onClose: close, isOpen: menuState.isOpen, anchorElement: menuState.anchorElement, anchorOrigin: anchorOrigin, onKeyDown: onKeyDown, accessibleName: label, children: (0, jsx_runtime_1.jsx)(SimpleMenuItems_1.SimpleMenuItems, { items: childMenuItems, onClose: close, autoFocus: true, compact: compact }) })] })); } NestedMenu.displayName = "NestedMenu"; const anchorOrigin = { horizontal: "right", vertical: "top" }; function hasActiveDescendants(children, isUrlActive) { const allLinks = children .map((childMenuItem) => { if (childMenuItem.type === "internal-link") { return [childMenuItem]; } return []; //Use Infinity here when typescript properly supports it, until then we are assuming 20 levels of nested items // is deep enough. Please see https://stackoverflow.com/questions/61420027/proper-typing-for-array-flatinfinity }) .flat(20); return allLinks.some((l) => isUrlActive(l.path, l.showAsActive ?? "if path partially matches")); } const buttonContainerStyles = (0, css_1.css)({ display: "flex", alignItems: "center", width: "100%", textAlign: "left", }); const buttonLabelStyles = (0, css_1.css)({ flexGrow: 1, });