UNPKG

@gravity-ui/uikit

Version:

Gravity UI base styling and components

136 lines (135 loc) 5.7 kB
'use client'; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.MenuItem = void 0; const tslib_1 = require("tslib"); const jsx_runtime_1 = require("react/jsx-runtime"); const React = tslib_1.__importStar(require("react")); const react_1 = require("@floating-ui/react"); const icons_1 = require("@gravity-ui/icons"); const hooks_1 = require("../../../hooks/index.js"); const constants_1 = require("../../Button/constants.js"); const Icon_1 = require("../../Icon/index.js"); const theme_1 = require("../../theme/index.js"); const cn_1 = require("../../utils/cn.js"); const mergeProps_1 = require("../../utils/mergeProps.js"); const ListItemView_1 = require("../ListItemView/ListItemView.js"); const MenuContext_1 = require("./MenuContext.js"); const MenuItemContext_1 = require("./MenuItemContext.js"); const utils_1 = require("./utils.js"); require("./MenuItem.css"); function isMenuItemComponentProps(p) { return p.component !== undefined; } function isMenuItemLinkProps(p) { return p.href !== undefined; } const b = (0, cn_1.block)('lab-menu-item'); exports.MenuItem = React.forwardRef((props, ref) => { const { theme, selected, disabled, icon, arrow, children, className, qa, ...restComponentProps } = props; const [submenuOpen, setSubmenuOpen] = React.useState(false); const [hasFocusInside, setHasFocusInside] = React.useState(false); const isRTL = (0, theme_1.useDirection)() === 'rtl'; const menuContext = React.useContext(MenuContext_1.MenuContext); const menuItemContext = React.useContext(MenuItemContext_1.MenuItemContext); if (!menuContext) { throw new Error('<MenuItem> must be used within <Menu>'); } const item = (0, react_1.useListItem)(); const tree = (0, react_1.useFloatingTree)(); const isActive = item.index === menuContext.activeIndex; const tabIndex = (menuContext.inline && item.index === 0) || isActive ? 0 : -1; let submenu = null; const preparedChildren = []; if (icon) { preparedChildren.push((0, jsx_runtime_1.jsx)("div", { className: b('icon'), "aria-hidden": true, children: icon }, "icon")); } for (const child of React.Children.toArray(children)) { if (!menuContext.inline && (0, utils_1.isComponentType)(child, 'Menu')) { submenu = child; } else { preparedChildren.push(child); } } if (arrow || submenu) { preparedChildren.push((0, jsx_runtime_1.jsx)("div", { className: b('arrow'), "aria-hidden": true, children: arrow ? (arrow) : ((0, jsx_runtime_1.jsx)(Icon_1.Icon, { data: isRTL ? icons_1.ChevronLeft : icons_1.ChevronRight, size: constants_1.BUTTON_ICON_SIZE_MAP[menuContext.size] })) }, "arrow")); } const handleRef = (0, hooks_1.useForkRef)(ref, item.ref); const handleClick = (event) => { props.onClick?.(event); if (!submenu) { tree?.events.emit('click'); } }; const handleFocus = (event) => { props.onFocus?.(event); setHasFocusInside(false); menuItemContext?.setHasFocusInside(true); }; let component; let componentProps; const commonComponentProps = { role: 'menuitem', tabIndex, className: b({ theme, size: menuContext.size, }, className), 'data-qa': qa, ...menuContext.getItemProps({ ...restComponentProps, onClick: handleClick, onFocus: handleFocus, }), }; if (isMenuItemComponentProps(props)) { component = props.component; componentProps = { ...commonComponentProps, 'aria-disabled': disabled ?? undefined, }; } else if (isMenuItemLinkProps(props)) { component = 'a'; componentProps = { ...commonComponentProps, rel: props.target === '_blank' && !props.rel ? 'noopener noreferrer' : props.rel, 'aria-disabled': disabled ?? undefined, }; } else { component = 'button'; componentProps = { ...commonComponentProps, type: 'button', disabled, 'aria-disabled': disabled ?? undefined, 'aria-pressed': selected ?? undefined, }; } const content = ((0, jsx_runtime_1.jsx)(ListItemView_1.ListItemView, { isContainer: true, component: component, componentProps: componentProps, ref: handleRef, size: menuContext.size, disabled: disabled, active: isActive && !hasFocusInside, hovered: hasFocusInside || (!isActive && submenuOpen), selected: selected, selectionStyle: "highlight", children: preparedChildren })); const contextValue = React.useMemo(() => ({ setHasFocusInside, }), []); if (submenu) { return ((0, jsx_runtime_1.jsx)(MenuItemContext_1.MenuItemContext.Provider, { value: contextValue, children: React.cloneElement(submenu, { trigger: (triggerProps, triggerRef) => { return React.cloneElement(content, { ref: (0, hooks_1.mergeRefs)(triggerRef, handleRef), componentProps: (0, mergeProps_1.mergeProps)(triggerProps, componentProps), }); }, onOpenChange: (open, event, reason) => { submenu.props.onOpenChange?.(open, event, reason); setSubmenuOpen(open); if (!open) { setHasFocusInside(false); } }, }) })); } return content; }); exports.MenuItem.displayName = 'Menu.Item'; //# sourceMappingURL=MenuItem.js.map