UNPKG

@carbon/react

Version:

React components for the Carbon Design System

122 lines (120 loc) 5.04 kB
/** * Copyright IBM Corp. 2016, 2026 * * This source code is licensed under the Apache-2.0 license found in the * LICENSE file in the root directory of this source tree. */ const require_runtime = require("../../_virtual/_rolldown/runtime.js"); const require_usePrefix = require("../../internal/usePrefix.js"); const require_keys = require("../../internal/keyboard/keys.js"); const require_match = require("../../internal/keyboard/match.js"); const require_SideNavContext = require("./SideNavContext.js"); const require_SideNavIcon = require("./SideNavIcon.js"); let classnames = require("classnames"); classnames = require_runtime.__toESM(classnames); let react = require("react"); react = require_runtime.__toESM(react); let prop_types = require("prop-types"); prop_types = require_runtime.__toESM(prop_types); let react_jsx_runtime = require("react/jsx-runtime"); let _carbon_icons_react = require("@carbon/icons-react"); //#region src/components/UIShell/SideNavMenu.tsx /** * Copyright IBM Corp. 2016, 2026 * * This source code is licensed under the Apache-2.0 license found in the * LICENSE file in the root directory of this source tree. */ const SideNavMenu = (0, react.forwardRef)(({ className: customClassName, children, defaultExpanded = false, isActive = false, large = false, renderIcon: IconElement, isSideNavExpanded, tabIndex, title }, ref) => { const { isRail, isSideNavExpanded: contextIsSideNavExpanded } = (0, react.useContext)(require_SideNavContext.SideNavContext); const currentIsSideNavExpanded = isSideNavExpanded ?? contextIsSideNavExpanded; const prefix = require_usePrefix.usePrefix(); const [isExpanded, setIsExpanded] = (0, react.useState)(defaultExpanded); const prevExpandedRef = (0, react.useRef)(false); const className = (0, classnames.default)({ [`${prefix}--side-nav__item`]: true, [`${prefix}--side-nav__item--active`]: isActive || hasActiveDescendant(children) && !isExpanded, [`${prefix}--side-nav__item--icon`]: IconElement, [`${prefix}--side-nav__item--large`]: large, [customClassName]: !!customClassName }); (0, react.useEffect)(() => { if (!isRail) return; if (!currentIsSideNavExpanded && isExpanded) { setIsExpanded(false); prevExpandedRef.current = true; } else if (currentIsSideNavExpanded && prevExpandedRef.current) { setIsExpanded(true); prevExpandedRef.current = false; } }, [ currentIsSideNavExpanded, isExpanded, isRail ]); return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", { className, onKeyDown: (event) => { if (require_match.match(event, require_keys.Escape)) setIsExpanded(false); }, children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", { "aria-expanded": isExpanded, className: `${prefix}--side-nav__submenu`, onClick: () => { setIsExpanded(!isExpanded); }, ref, type: "button", tabIndex: tabIndex === void 0 ? !currentIsSideNavExpanded && !isRail ? -1 : 0 : tabIndex, children: [ IconElement && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_SideNavIcon.default, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconElement, {}) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: `${prefix}--side-nav__submenu-title`, children: title }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_SideNavIcon.default, { className: `${prefix}--side-nav__submenu-chevron`, small: true, children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_carbon_icons_react.ChevronDown, { size: 20 }) }) ] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", { className: `${prefix}--side-nav__menu`, children })] }); }); SideNavMenu.displayName = "SideNavMenu"; SideNavMenu.propTypes = { children: prop_types.default.node, className: prop_types.default.string, defaultExpanded: prop_types.default.bool, isActive: prop_types.default.bool, isSideNavExpanded: prop_types.default.bool, large: prop_types.default.bool, renderIcon: prop_types.default.oneOfType([prop_types.default.func, prop_types.default.object]), tabIndex: prop_types.default.number, title: prop_types.default.string.isRequired }; /** Defining the children parameter with the type ReactNode | ReactNode[]. This allows for various possibilities: a single element, an array of elements, or null or undefined. **/ function hasActiveDescendant(children) { if (Array.isArray(children)) return children.some((child) => { if (!react.default.isValidElement(child)) return false; /** Explicitly defining the expected prop types (isActive and 'aria-current) for the children to ensure type safety when accessing their props. **/ const props = child.props; if (props.isActive === true || props["aria-current"] || props.children instanceof Array && hasActiveDescendant(props.children)) return true; return false; }); if (react.default.isValidElement(children)) { const props = children.props; if (props.isActive === true || props["aria-current"]) return true; } return false; } //#endregion exports.default = SideNavMenu;