@gravity-ui/uikit
Version:
Gravity UI base styling and components
136 lines (135 loc) • 5.7 kB
JavaScript
'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