UNPKG

@grafana/ui

Version:
306 lines (299 loc) • 9.47 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var jsxRuntime = require('react/jsx-runtime'); var css = require('@emotion/css'); var React = require('react'); var i18n = require('@grafana/i18n'); var ThemeContext = require('../../themes/ThemeContext.cjs'); var mixins = require('../../themes/mixins.cjs'); var Icon = require('../Icon/Icon.cjs'); var Stack = require('../Layout/Stack/Stack.cjs'); var SubMenu = require('./SubMenu.cjs'); function _interopNamespaceCompat(e) { if (e && typeof e === 'object' && 'default' in e) return e; var n = Object.create(null); if (e) { Object.keys(e).forEach(function (k) { if (k !== 'default') { var d = Object.getOwnPropertyDescriptor(e, k); Object.defineProperty(n, k, d.get ? d : { enumerable: true, get: function () { return e[k]; } }); } }); } n.default = e; return Object.freeze(n); } var React__namespace = /*#__PURE__*/_interopNamespaceCompat(React); "use strict"; const MenuItem = React__namespace.memo( React__namespace.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 = ThemeContext.useStyles2(getStyles); const resolvedIconColor = iconColor && !destructive && !disabled ? iconColor : void 0; const [isActive, setIsActive] = React.useState(active); const [isSubMenuOpen, setIsSubMenuOpen] = React.useState(false); const onMouseEnter = React.useCallback(() => { if (disabled) { return; } setIsSubMenuOpen(true); setIsActive(true); }, [disabled]); const onMouseLeave = React.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 = css.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 = React.useRef(null); React.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__ */ jsxRuntime.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__ */ jsxRuntime.jsxs(Stack.Stack, { direction: "row", justifyContent: "flex-start", alignItems: "center", children: [ icon && /* @__PURE__ */ jsxRuntime.jsx( Icon.Icon, { name: icon, className: css.cx(styles.icon, resolvedIconColor && css.css({ color: resolvedIconColor })), "aria-hidden": true } ), /* @__PURE__ */ jsxRuntime.jsx("span", { className: css.cx(styles.ellipsis, styles.label), children: label }), /* @__PURE__ */ jsxRuntime.jsxs("div", { className: css.cx(styles.rightWrapper, { [styles.withShortcut]: hasShortcut }), children: [ hasShortcut && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles.shortcut, children: [ /* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { name: "keyboard", title: i18n.t("grafana-ui.menu-item.keyboard-shortcut-label", "Keyboard shortcut") }), shortcut ] }), hasSubMenu && /* @__PURE__ */ jsxRuntime.jsx( SubMenu.SubMenu, { parentItemRef: localRef, items: childItems, isOpen: isSubMenuOpen, close: closeSubMenu, customStyle: customSubMenuContainerStyles } ) ] }) ] }), description && /* @__PURE__ */ jsxRuntime.jsx( "div", { className: css.cx(styles.description, styles.ellipsis, { [styles.descriptionWithIcon]: icon !== void 0 }), children: description } ), props.component ? /* @__PURE__ */ jsxRuntime.jsx(props.component, {}) : null ] } ); }) ); MenuItem.displayName = "MenuItem"; const getStyles = (theme) => { const menuPadding = theme.components.menu.padding * theme.spacing.gridSize; return { item: css.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: mixins.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": mixins.getFocusStyles(theme) }), label: css.css({ color: theme.colors.text.primary }), active: css.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.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.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.css({ opacity: 0.7 }), rightWrapper: css.css({ display: "flex", alignItems: "center", marginLeft: "auto" }), withShortcut: css.css({ minWidth: theme.spacing(10.5) }), shortcut: css.css({ display: "flex", alignItems: "center", gap: theme.spacing(1), marginLeft: theme.spacing(2) }), description: css.css({ ...theme.typography.bodySmall, textAlign: "start" }), descriptionWithIcon: css.css({ marginLeft: theme.spacing(3) }), ellipsis: css.css({ overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }) }; }; exports.MenuItem = MenuItem; //# sourceMappingURL=MenuItem.cjs.map