UNPKG

@itwin/itwinui-react

Version:

A react component library for iTwinUI

155 lines (154 loc) 4 kB
import * as React from 'react'; import { SvgCaretRightSmall, useMergedRefs, useId, useWarningLogger, } from '../../utils/index.js'; import { Menu, MenuContext } from './Menu.js'; import { ListItem } from '../List/ListItem.js'; import cx from 'classnames'; import { DropdownMenuCloseOnClickContext, DropdownMenuContext, } from '../DropdownMenu/DropdownMenu.js'; export const MenuItem = React.forwardRef((props, forwardedRef) => { let { className, children, isSelected, disabled, value, onClick: onClickProp, sublabel, size = !!sublabel ? 'large' : 'default', icon, startIcon = icon, badge, endIcon = badge, role = 'menuitem', subMenuItems = [], ...rest } = props; let logWarning = useWarningLogger(); let hasSubMenu = React.useMemo( () => subMenuItems.length > 0, [subMenuItems.length], ); if ( 'development' === process.env.NODE_ENV && null != onClickProp && hasSubMenu ) logWarning( 'Passing a non-empty submenuItems array and onClick to MenuItem at the same time is not supported. This is because when a non empty submenuItems array is passed, clicking the MenuItem toggles the submenu visibility.', ); let parentMenu = React.useContext(MenuContext); let dropdownMenu = React.useContext(DropdownMenuContext); let shouldCloseMenuOnClick = React.useContext(DropdownMenuCloseOnClickContext) && !hasSubMenu; let menuItemRef = React.useRef(null); let submenuId = useId(); let popoverProps = React.useMemo( () => ({ placement: 'right-start', interactions: { click: true, hover: true, listNavigation: { nested: hasSubMenu, openOnArrowKeyDown: true, }, }, }), [hasSubMenu], ); let onClick = (event) => { if (disabled) return; if (shouldCloseMenuOnClick) dropdownMenu?.close(); onClickProp?.(value ?? event); }; let focusableItemIndex = parentMenu?.focusableElements.findIndex( (el) => el === menuItemRef.current, ); let trigger = React.createElement( ListItem, { as: 'button', type: 'button', className: cx('iui-button-base', className), actionable: true, size: size, active: isSelected, disabled: disabled, ref: useMergedRefs(menuItemRef, forwardedRef), role: role, tabIndex: isSelected ? 0 : -1, 'aria-selected': isSelected, 'aria-haspopup': hasSubMenu ? 'true' : void 0, 'aria-controls': hasSubMenu ? submenuId : void 0, 'aria-disabled': disabled, ...(parentMenu?.popoverGetItemProps != null ? parentMenu.popoverGetItemProps({ focusableItemIndex, userProps: { onClick, }, }) : { onClick, }), ...rest, }, startIcon && React.createElement( ListItem.Icon, { as: 'span', 'aria-hidden': true, }, startIcon, ), React.createElement( ListItem.Content, null, React.createElement('div', null, children), sublabel && React.createElement(ListItem.Description, null, sublabel), ), !endIcon && hasSubMenu && React.createElement( ListItem.Icon, { as: 'span', 'aria-hidden': true, }, React.createElement(SvgCaretRightSmall, null), ), endIcon && React.createElement( ListItem.Icon, { as: 'span', 'aria-hidden': true, }, endIcon, ), ); return React.createElement( React.Fragment, null, hasSubMenu && !disabled ? React.createElement( Menu, { id: submenuId, trigger: trigger, popoverProps: popoverProps, }, subMenuItems, ) : trigger, ); }); if ('development' === process.env.NODE_ENV) MenuItem.displayName = 'MenuItem';