@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
147 lines • 8.64 kB
JavaScript
import { __rest } from "tslib";
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
import React, { useEffect, useRef, useState } from 'react';
import clsx from 'clsx';
import { useContainerQuery } from '@awsui/component-toolkit';
import { getLogicalBoundingClientRect } from '@awsui/component-toolkit/internal';
import { getAnalyticsMetadataAttribute } from '@awsui/component-toolkit/internal/analytics-metadata';
import { InternalButton } from '../button/internal';
import InternalButtonDropdown from '../button-dropdown/internal';
import { useInternalI18n } from '../i18n/context';
import InternalIcon from '../icon/internal';
import { getBaseProps } from '../internal/base-component';
import { fireCancelableEvent } from '../internal/events';
import { useMergeRefs } from '../internal/hooks/use-merge-refs';
import { checkSafeUrl } from '../internal/utils/check-safe-url';
import { createWidgetizedComponent } from '../internal/widgets';
import { AllItemsDropdown } from './all-items-dropdown';
import { BreadcrumbItem } from './item/item';
import { BreadcrumbGroupSkeleton } from './skeleton';
import { getEventDetail, getItemsDisplayProperties } from './utils';
import analyticsSelectors from './analytics-metadata/styles.css.js';
import styles from './styles.css.js';
/**
* Provided for backwards compatibility
*/
const DEFAULT_EXPAND_ARIA_LABEL = 'Show path';
const getEllipsisDropdownTrigger = ({ ariaLabel, triggerRef, testUtilsClass, isOpen, onClick }) => {
return (React.createElement(InternalButton, { ref: triggerRef, className: testUtilsClass, onClick: event => {
event.preventDefault();
onClick();
}, ariaExpanded: isOpen, "aria-haspopup": true, ariaLabel: ariaLabel, variant: "breadcrumb-group", formAction: "none" }, "..."));
};
const EllipsisDropdown = ({ ariaLabel, dropdownItems, onDropdownItemClick, onDropdownItemFollow, visible, }) => {
var _a;
const i18n = useInternalI18n('breadcrumb-group');
return (React.createElement("li", { className: clsx(styles.ellipsis, visible && styles.visible) },
React.createElement(InternalButtonDropdown, { ariaLabel: (_a = i18n('expandAriaLabel', ariaLabel)) !== null && _a !== void 0 ? _a : DEFAULT_EXPAND_ARIA_LABEL, items: dropdownItems, onItemClick: onDropdownItemClick, onItemFollow: onDropdownItemFollow, customTriggerBuilder: getEllipsisDropdownTrigger, linkStyle: true, analyticsMetadataTransformer: metadata => {
var _a, _b;
if ((_a = metadata.detail) === null || _a === void 0 ? void 0 : _a.id) {
delete metadata.detail.id;
}
if ((_b = metadata.detail) === null || _b === void 0 ? void 0 : _b.position) {
metadata.detail.position = `${parseInt(metadata.detail.position, 10) + 1}`;
}
return metadata;
} }),
React.createElement("span", { className: styles.icon },
React.createElement(InternalIcon, { name: "angle-right" }))));
};
const areArrayEqual = (first, second) => {
if (first.length !== second.length) {
return false;
}
return first.every((item, index) => item === second[index]);
};
export function BreadcrumbGroupImplementation(_a) {
var { items = [], ariaLabel, expandAriaLabel, onClick, onFollow, __internalRootRef, __injectAnalyticsComponentMetadata } = _a, props = __rest(_a, ["items", "ariaLabel", "expandAriaLabel", "onClick", "onFollow", "__internalRootRef", "__injectAnalyticsComponentMetadata"]);
for (const item of items) {
checkSafeUrl('BreadcrumbGroup', item.href);
}
const baseProps = getBaseProps(props);
const [navWidth, navRef] = useContainerQuery(rect => rect.borderBoxWidth);
const mergedRef = useMergeRefs(navRef, __internalRootRef);
const itemsRefs = useRef({ ghost: {}, real: {} });
const setBreadcrumb = (type, index, node) => {
if (node) {
itemsRefs.current[type][index] = node;
}
else {
delete itemsRefs.current[type][index];
}
};
const [itemsWidths, setItemsWidths] = useState({ ghost: [], real: [] });
useEffect(() => {
if (itemsRefs.current) {
const newItemsWidths = { ghost: [], real: [] };
for (const node of Object.values(itemsRefs.current.ghost)) {
const width = getLogicalBoundingClientRect(node).inlineSize;
newItemsWidths.ghost.push(width);
}
for (const node of Object.values(itemsRefs.current.real)) {
const width = getLogicalBoundingClientRect(node).inlineSize;
newItemsWidths.real.push(width);
}
setItemsWidths(oldWidths => {
if (!areArrayEqual(newItemsWidths.ghost, oldWidths.ghost) ||
!areArrayEqual(newItemsWidths.real, oldWidths.real)) {
return newItemsWidths;
}
else {
return oldWidths;
}
});
}
}, [items, navWidth]);
const { collapsed } = getItemsDisplayProperties(itemsWidths.ghost, navWidth);
let breadcrumbItems = items.map((item, index) => {
const isLast = index === items.length - 1;
const isDisplayed = index === 0 || index > collapsed;
const clickAnalyticsMetadata = {
action: 'click',
detail: {
position: `${index + 1}`,
label: `.${analyticsSelectors['breadcrumb-item']}`,
href: item.href || '',
},
};
return (React.createElement("li", Object.assign({ className: clsx(styles.item, !isDisplayed && styles.hide), key: index }, (isLast ? {} : getAnalyticsMetadataAttribute(clickAnalyticsMetadata)), { ref: node => setBreadcrumb('real', `${index}`, node) }),
React.createElement(BreadcrumbItem, { item: item, onClick: onClick, onFollow: onFollow, itemIndex: index, totalCount: items.length, isTruncated: itemsWidths.ghost[index] - itemsWidths.real[index] > 0 })));
});
const hiddenBreadcrumbItems = items.map((item, index) => (React.createElement("li", { className: styles['ghost-item'], key: index, ref: node => setBreadcrumb('ghost', `${index}`, node) },
React.createElement(BreadcrumbItem, { item: item, itemIndex: index, totalCount: items.length, isGhost: true }))));
const getEventItem = (e) => {
const { id } = e.detail;
return items[parseInt(id)];
};
// Add ellipsis
if (breadcrumbItems.length >= 2) {
const dropdownItems = items
.slice(1, 1 + collapsed)
.map((item, index) => ({
id: (index + 1).toString(),
text: item.text,
href: item.href || '#',
}));
breadcrumbItems = [
breadcrumbItems[0],
React.createElement(EllipsisDropdown, { key: 'ellipsis', visible: collapsed > 0, ariaLabel: expandAriaLabel, dropdownItems: dropdownItems, onDropdownItemClick: e => fireCancelableEvent(onClick, getEventDetail(getEventItem(e)), e), onDropdownItemFollow: e => fireCancelableEvent(onFollow, getEventDetail(getEventItem(e)), e) }),
...breadcrumbItems.slice(1),
];
}
const componentAnalyticsMetadata = {
name: 'awsui.BreadcrumbGroup',
label: { root: 'self' },
};
return (React.createElement("nav", Object.assign({}, baseProps, { className: clsx(styles['breadcrumb-group'], baseProps.className), "aria-label": ariaLabel || undefined, ref: mergedRef }, (__injectAnalyticsComponentMetadata
? Object.assign({}, getAnalyticsMetadataAttribute({
component: componentAnalyticsMetadata,
})) : {})),
collapsed > 0 && collapsed === items.length - 1 ? (React.createElement(AllItemsDropdown, { items: items, onItemClick: e => e.detail.id !== (items.length - 1).toString() &&
fireCancelableEvent(onClick, getEventDetail(getEventItem(e)), e), onItemFollow: e => e.detail.id !== (items.length - 1).toString() &&
fireCancelableEvent(onFollow, getEventDetail(getEventItem(e)), e) })) : (React.createElement("ol", { className: styles['breadcrumb-group-list'] }, breadcrumbItems)),
React.createElement("ol", { className: clsx(styles['breadcrumb-group-list'], styles.ghost), "aria-hidden": true, tabIndex: -1 }, hiddenBreadcrumbItems)));
}
export const createWidgetizedBreadcrumbGroup = createWidgetizedComponent(BreadcrumbGroupImplementation, BreadcrumbGroupSkeleton);
//# sourceMappingURL=implementation.js.map