UNPKG

@grafana/ui

Version:
244 lines (241 loc) • 7.41 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 } = props; const styles = useStyles2(getStyles); 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) => { switch (event.key) { case "ArrowRight": event.preventDefault(); event.stopPropagation(); if (hasSubMenu) { setIsSubMenuOpen(true); setIsActive(true); } 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: !url ? role || "menuitem" : role, "data-role": "menuitem", ref: localRef, "data-testid": testId, "aria-label": ariaLabel, "aria-checked": ariaChecked, tabIndex, ...disabledProps, children: [ /* @__PURE__ */ jsxs(Stack, { direction: "row", justifyContent: "flex-start", alignItems: "center", children: [ icon && /* @__PURE__ */ jsx(Icon, { name: icon, className: styles.icon, "aria-hidden": true }), /* @__PURE__ */ jsx("span", { className: styles.ellipsis, 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, { 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.primary, 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 }), 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) }), active: css({ background: theme.colors.action.hover }), 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, color: theme.colors.text.secondary }), 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), color: theme.colors.text.secondary }), description: css({ ...theme.typography.bodySmall, color: theme.colors.text.secondary, textAlign: "start" }), descriptionWithIcon: css({ marginLeft: theme.spacing(3) }), ellipsis: css({ overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }) }; }; export { MenuItem }; //# sourceMappingURL=MenuItem.mjs.map