@itwin/itwinui-react
Version:
A react component library for iTwinUI
155 lines (154 loc) • 4 kB
JavaScript
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';