@awsui/components-react
Version:
On July 19th, 2022, we launched [Cloudscape Design System](https://cloudscape.design). Cloudscape is an evolution of AWS-UI. It consists of user interface guidelines, front-end components, design resources, and development tools for building intuitive, en
117 lines • 7.61 kB
JavaScript
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
import React, { useEffect, useRef } from 'react';
import clsx from 'clsx';
import { getAnalyticsMetadataAttribute, } from '@awsui/component-toolkit/internal/analytics-metadata';
import InternalIcon from '../../icon/internal';
import { useDropdownContext } from '../../internal/components/dropdown/context';
import useHiddenDescription from '../../internal/hooks/use-hidden-description';
import { useVisualRefresh } from '../../internal/hooks/use-visual-mode';
import Tooltip from '../tooltip';
import { getMenuItemCheckboxProps, getMenuItemProps } from '../utils/menu-item';
import { isCheckboxItem, isLinkItem } from '../utils/utils';
import { getItemTarget } from '../utils/utils';
import analyticsLabels from '../analytics-metadata/styles.css.js';
import styles from './styles.css.js';
const ItemElement = ({ position = '1', item, disabled, onItemActivate, highlighted, highlightItem, showDivider, hasCategoryHeader, isKeyboardHighlighted = false, analyticsMetadataTransformer = (metadata) => metadata, variant = 'normal', linkStyle, }) => {
const isLink = isLinkItem(item);
const isCheckbox = isCheckboxItem(item);
const onClick = (event) => {
// Stop propagation to parent node and handle event exclusively in here. This ensures
// that no group will interfere with the default behavior of links
event.stopPropagation();
if (!isLink) {
event.preventDefault();
}
if (!disabled) {
onItemActivate(item, event);
}
};
const onHover = () => {
highlightItem(item);
};
const isVisualRefresh = useVisualRefresh();
return (React.createElement("li", { className: clsx(styles['item-element'], styles[`variant-${variant}`], {
[styles.highlighted]: highlighted,
[styles.disabled]: disabled,
[styles['has-category-header']]: hasCategoryHeader,
[styles['has-checkmark']]: isCheckbox,
[styles['show-divider']]: showDivider,
[styles['is-focused']]: isKeyboardHighlighted,
[styles['visual-refresh']]: isVisualRefresh,
}), role: "presentation", "data-testid": item.id, "data-description": item.description, onClick: onClick, onMouseEnter: onHover, onTouchStart: onHover, ...getAnalyticsMetadataAttribute(disabled
? {}
: analyticsMetadataTransformer({
action: 'click',
detail: {
position,
id: item.id,
label: `.${analyticsLabels['menu-item']}`,
href: item.href || '',
},
})) },
React.createElement(MenuItem, { item: item, disabled: disabled, highlighted: highlighted, linkStyle: linkStyle })));
};
function MenuItem({ item, disabled, highlighted, linkStyle }) {
const menuItemRef = useRef(null);
const isCheckbox = isCheckboxItem(item);
const isCurrentBreadcrumb = !isCheckbox && item.isCurrentBreadcrumb;
useEffect(() => {
if (highlighted && menuItemRef.current) {
menuItemRef.current.focus();
}
}, [highlighted]);
const isDisabledWithReason = disabled && item.disabledReason;
const { targetProps, descriptionEl } = useHiddenDescription(item.disabledReason);
const menuItemProps = {
'aria-label': item.ariaLabel,
className: clsx(styles['menu-item'], analyticsLabels['menu-item'], linkStyle && styles['link-style'], linkStyle && highlighted && styles['link-style-highlighted'], isCurrentBreadcrumb && styles['current-breadcrumb']),
'aria-current': isCurrentBreadcrumb,
lang: item.lang,
ref: menuItemRef,
// We are using the roving tabindex technique to manage the focus state of the dropdown.
// The current element will always have tabindex=0 which means that it can be tabbed to,
// while all other items have tabindex=-1 so we can focus them when necessary.
tabIndex: highlighted ? 0 : -1,
...(isCheckbox ? getMenuItemCheckboxProps({ disabled, checked: item.checked }) : getMenuItemProps({ disabled })),
...(isDisabledWithReason ? targetProps : {}),
};
const menuItem = isLinkItem(item) ? (React.createElement("a", { ...menuItemProps, href: !disabled ? item.href : undefined, download: !disabled && item.download ? item.download : undefined, target: getItemTarget(item), rel: item.external ? 'noopener noreferrer' : undefined },
React.createElement(MenuItemContent, { item: item, disabled: disabled, highlighted: highlighted }))) : (React.createElement("span", { ...menuItemProps },
React.createElement(MenuItemContent, { item: item, disabled: disabled, highlighted: highlighted })));
const { position } = useDropdownContext();
const tooltipPosition = position === 'bottom-left' || position === 'top-left' ? 'left' : 'right';
return isDisabledWithReason ? (React.createElement(Tooltip, { content: item.disabledReason, position: tooltipPosition, className: styles['item-tooltip-wrapper'] },
menuItem,
descriptionEl)) : (menuItem);
}
const MenuItemContent = ({ item, disabled, highlighted, }) => {
const hasIcon = !!(item.iconName || item.iconUrl || item.iconSvg);
const hasExternal = isLinkItem(item) && item.external;
const isCheckbox = isCheckboxItem(item);
return (React.createElement(React.Fragment, null,
isCheckbox && React.createElement(MenuItemCheckmark, { checked: item.checked, disabled: disabled }),
hasIcon && (React.createElement(MenuItemIcon, { name: item.iconName, url: item.iconUrl, svg: item.iconSvg, alt: item.iconAlt, badge: item.badge })),
React.createElement("div", { className: styles['content-wrapper'] },
React.createElement("div", { className: styles['main-row'] },
React.createElement("div", null,
item.text,
hasExternal && React.createElement(ExternalIcon, { disabled: disabled, ariaLabel: item.externalIconAriaLabel })),
item.labelTag && (React.createElement("div", { className: clsx(styles['label-tag'], disabled && styles.disabled) }, item.labelTag))),
item.secondaryText && (React.createElement("div", { className: clsx(styles['secondary-text'], highlighted && styles.highlighted, disabled && styles.disabled) }, item.secondaryText)))));
};
const MenuItemIcon = (props) => (React.createElement("span", { className: styles.icon },
React.createElement(InternalIcon, { ...props })));
// Toggle has aria-hidden set because it's just used as a graphical element,
// a11y attributes for the checkmark are communicated through the role and aria-checked state
// of the menu element item.
const MenuItemCheckmark = ({ disabled, checked }) => {
const checkmark = React.createElement(InternalIcon, { variant: disabled ? 'disabled' : 'normal', name: "check" });
return (React.createElement("span", { className: clsx(styles.icon, styles.checkmark, { [styles.disabled]: disabled }), "aria-hidden": "true", style: { visibility: checked ? 'visible' : 'hidden' } }, checkmark));
};
const ExternalIcon = ({ disabled, ariaLabel }) => {
const icon = React.createElement(InternalIcon, { variant: disabled ? 'disabled' : 'normal', name: "external" });
return (React.createElement("span", { className: styles['external-icon'], role: ariaLabel ? 'img' : undefined, "aria-label": ariaLabel }, icon));
};
export default ItemElement;
//# sourceMappingURL=index.js.map