UNPKG

@grafana/ui

Version:
283 lines (280 loc) • 8.66 kB
import { jsxs, jsx } from 'react/jsx-runtime'; import { cx, css } from '@emotion/css'; import * as React from 'react'; import { useState, useCallback, useRef, useImperativeHandle } from 'react'; import { t } from '@grafana/i18n'; import { useStyles2 } from '../../themes/ThemeContext.mjs'; import { getFocusStyles, getInternalRadius } from '../../themes/mixins.mjs'; import { Icon } from '../Icon/Icon.mjs'; import { Stack } from '../Layout/Stack/Stack.mjs'; import { SubMenu } from './SubMenu.mjs'; "use strict"; const MenuItem = React.memo( React.forwardRef((props, ref) => { const { url, icon, label, description, ariaLabel, ariaChecked, target, onClick, className, active, disabled, destructive, childItems, role, tabIndex = -1, customSubMenuContainerStyles, shortcut, testId, iconColor } = props; const styles = useStyles2(getStyles); const resolvedIconColor = iconColor && !destructive && !disabled ? iconColor : void 0; const [isActive, setIsActive] = useState(active); const [isSubMenuOpen, setIsSubMenuOpen] = useState(false); const onMouseEnter = useCallback(() => { if (disabled) { return; } setIsSubMenuOpen(true); setIsActive(true); }, [disabled]); const onMouseLeave = useCallback(() => { if (disabled) { return; } setIsSubMenuOpen(false); setIsActive(false); }, [disabled]); const hasSubMenu = childItems && childItems.length > 0; const ItemElement = hasSubMenu ? "div" : url === void 0 ? "button" : "a"; const itemStyle = cx( { [styles.item]: true, [styles.active]: isActive, [styles.disabled]: disabled, [styles.destructive]: destructive && !disabled }, className ); const disabledProps = { [ItemElement === "button" ? "disabled" : "aria-disabled"]: disabled, ...ItemElement === "a" && disabled && { href: void 0, onClick: void 0 }, ...disabled && { tabIndex: -1, ["data-disabled"]: disabled // used to identify disabled items in Menu.tsx } }; const localRef = useRef(null); useImperativeHandle(ref, () => localRef.current); const handleKeys = (event) => { var _a; switch (event.key) { case " ": if (ItemElement === "a" && url) { event.preventDefault(); (_a = localRef.current) == null ? void 0 : _a.click(); } if (hasSubMenu && !isSubMenuOpen) { event.preventDefault(); event.stopPropagation(); setIsSubMenuOpen(true); setIsActive(true); return; } break; case "ArrowRight": event.preventDefault(); event.stopPropagation(); if (hasSubMenu) { setIsSubMenuOpen(true); setIsActive(true); } break; case "Enter": if (hasSubMenu && !isSubMenuOpen) { event.preventDefault(); event.stopPropagation(); setIsSubMenuOpen(true); setIsActive(true); return; } break; default: break; } }; const closeSubMenu = () => { var _a; setIsSubMenuOpen(false); setIsActive(false); (_a = localRef == null ? void 0 : localRef.current) == null ? void 0 : _a.focus(); }; const hasShortcut = Boolean(shortcut && shortcut.length > 0); return /* @__PURE__ */ jsxs( ItemElement, { target, className: itemStyle, rel: target === "_blank" ? "noopener noreferrer" : void 0, href: url, onClick: (event) => { if (hasSubMenu && !isSubMenuOpen) { event.preventDefault(); event.stopPropagation(); } onClick == null ? void 0 : onClick(event); }, onMouseEnter, onMouseLeave, onKeyDown: handleKeys, role: role != null ? role : "menuitem", "data-role": "menuitem", ref: localRef, "data-testid": testId, "aria-label": ariaLabel, "aria-checked": ariaChecked, "aria-haspopup": hasSubMenu ? "menu" : void 0, "aria-expanded": hasSubMenu ? isSubMenuOpen : void 0, tabIndex, ...disabledProps, children: [ /* @__PURE__ */ jsxs(Stack, { direction: "row", justifyContent: "flex-start", alignItems: "center", children: [ icon && /* @__PURE__ */ jsx( Icon, { name: icon, className: cx(styles.icon, resolvedIconColor && css({ color: resolvedIconColor })), "aria-hidden": true } ), /* @__PURE__ */ jsx("span", { className: cx(styles.ellipsis, styles.label), children: label }), /* @__PURE__ */ jsxs("div", { className: cx(styles.rightWrapper, { [styles.withShortcut]: hasShortcut }), children: [ hasShortcut && /* @__PURE__ */ jsxs("div", { className: styles.shortcut, children: [ /* @__PURE__ */ jsx(Icon, { name: "keyboard", title: t("grafana-ui.menu-item.keyboard-shortcut-label", "Keyboard shortcut") }), shortcut ] }), hasSubMenu && /* @__PURE__ */ jsx( SubMenu, { parentItemRef: localRef, items: childItems, isOpen: isSubMenuOpen, close: closeSubMenu, customStyle: customSubMenuContainerStyles } ) ] }) ] }), description && /* @__PURE__ */ jsx( "div", { className: cx(styles.description, styles.ellipsis, { [styles.descriptionWithIcon]: icon !== void 0 }), children: description } ), props.component ? /* @__PURE__ */ jsx(props.component, {}) : null ] } ); }) ); MenuItem.displayName = "MenuItem"; const getStyles = (theme) => { const menuPadding = theme.components.menu.padding * theme.spacing.gridSize; return { item: css({ background: "none", cursor: "pointer", whiteSpace: "nowrap", color: theme.colors.text.secondary, display: "flex", flexDirection: "column", alignItems: "stretch", justifyContent: "center", padding: theme.spacing(0.5, 1.5), minHeight: theme.spacing(4), borderRadius: getInternalRadius(theme, menuPadding, { parentBorderWidth: 0, parentBorderRadius: "lg" }), margin: 0, border: "none", width: "100%", position: "relative", "&:hover, &:focus-visible": { background: theme.colors.action.hover, color: theme.colors.text.primary, textDecoration: "none" }, "&:focus-visible": getFocusStyles(theme) }), label: css({ color: theme.colors.text.primary }), active: css({ background: theme.colors.action.hover, // needed to show active items in forced colors mode "@media (forced-colors: active)": { outline: "1px solid transparent" } }), destructive: css({ color: theme.colors.error.text, svg: { color: theme.colors.error.text }, "&:hover, &:focus, &:focus-visible": { background: theme.colors.error.main, color: theme.colors.error.contrastText, svg: { color: theme.colors.error.contrastText } } }), disabled: css({ color: theme.colors.action.disabledText, label: "menu-item-disabled", "&:hover, &:focus, &:focus-visible": { cursor: "not-allowed", background: "none", color: theme.colors.action.disabledText } }), icon: css({ opacity: 0.7 }), rightWrapper: css({ display: "flex", alignItems: "center", marginLeft: "auto" }), withShortcut: css({ minWidth: theme.spacing(10.5) }), shortcut: css({ display: "flex", alignItems: "center", gap: theme.spacing(1), marginLeft: theme.spacing(2) }), description: css({ ...theme.typography.bodySmall, textAlign: "start" }), descriptionWithIcon: css({ marginLeft: theme.spacing(3) }), ellipsis: css({ overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }) }; }; export { MenuItem }; //# sourceMappingURL=MenuItem.mjs.map