UNPKG

@carbon/ibm-products

Version:
228 lines (226 loc) 10.9 kB
/** * Copyright IBM Corp. 2020, 2026 * * This source code is licensed under the Apache-2.0 license found in the * LICENSE file in the root directory of this source tree. */ const require_runtime = require("../../_virtual/_rolldown/runtime.js"); const require_index = require("../../node_modules/classnames/index.js"); const require_settings = require("../../settings.js"); require("../../global/js/utils/props-helper.js"); const require_uuidv4 = require("../../global/js/utils/uuidv4.js"); const require_useResizeObserver = require("../../global/js/hooks/useResizeObserver.js"); const require_TooltipTrigger = require("../TooltipTrigger/TooltipTrigger.js"); let react = require("react"); react = require_runtime.__toESM(react); let prop_types = require("prop-types"); prop_types = require_runtime.__toESM(prop_types); let _carbon_react = require("@carbon/react"); let _carbon_react_icons = require("@carbon/react/icons"); //#region src/components/BreadcrumbWithOverflow/BreadcrumbWithOverflow.jsx var import_classnames = /* @__PURE__ */ require_runtime.__toESM(require_index.default); const blockClass = `${require_settings.pkg.prefix}--breadcrumb-with-overflow`; const componentName = "BreadcrumbWithOverflow"; /** * The BreadcrumbWithOverflow is used internally by the PageHeader to wrap BreadcrumbItems. */ let BreadcrumbWithOverflow = ({ breadcrumbs, className, label, maxVisible, noTrailingSlash, overflowAriaLabel, overflowTooltipAlign, ...other }) => { const carbonPrefix = (0, _carbon_react.usePrefix)(); const [displayCount, setDisplayCount] = (0, react.useState)(3); const [displayedBreadcrumbItems, setDisplayedBreadcrumbItems] = (0, react.useState)([]); const breadcrumbItemWithOverflow = (0, react.useRef)(null); const sizingContainerRef = (0, react.useRef)(null); const internalId = (0, react.useRef)(require_uuidv4.default()); const [hiddenSizingItems, setHiddenSizingItems] = (0, react.useState)([]); const BreadcrumbOverflowMenu = ({ overflowItems }) => { const handleClick = (evt, item) => { if (item?.props?.href) window.location.href = item.props.href; item?.props?.onClick?.(evt); }; return /* @__PURE__ */ react.default.createElement(_carbon_react.BreadcrumbItem, { key: `breadcrumb-overflow-${internalId.current}` }, /* @__PURE__ */ react.default.createElement(_carbon_react.unstable_FeatureFlags, { enableV12Overflowmenu: true }, /* @__PURE__ */ react.default.createElement(_carbon_react.OverflowMenu, { role: "navigation", "aria-label": overflowAriaLabel, label: overflowAriaLabel, renderIcon: (props) => /* @__PURE__ */ react.default.createElement(_carbon_react_icons.OverflowMenuHorizontal, { size: 32, ...props }), className: `${blockClass}__overflow-menu`, tooltipAlignment: overflowTooltipAlign }, overflowItems.map((item, index) => /* @__PURE__ */ react.default.createElement(_carbon_react.MenuItem, { key: `breadcrumb-overflow-menu-item-${internalId.current}-${index}`, onClick: (evt) => handleClick(evt, item), label: item.props.children }))))); }; (0, react.useEffect)(() => { setHiddenSizingItems(/* @__PURE__ */ react.default.createElement("div", { className: `${blockClass}__breadcrumb-container ${blockClass}__breadcrumb-container--hidden`, "aria-hidden": true, ref: sizingContainerRef }, /* @__PURE__ */ react.default.createElement(_carbon_react.Breadcrumb, { "aria-label": `${label}-hidden` }, /* @__PURE__ */ react.default.createElement(_carbon_react.BreadcrumbItem, { key: `${blockClass}-hidden-overflow-${internalId}` }, /* @__PURE__ */ react.default.createElement(_carbon_react.OverflowMenu, { role: "navigation", "aria-label": overflowAriaLabel, renderIcon: (props) => /* @__PURE__ */ react.default.createElement(_carbon_react_icons.OverflowMenuHorizontal, { size: 32, ...props }) })), breadcrumbs.map(({ label, key, title, id, shortTitle, ...rest }) => /* @__PURE__ */ react.default.createElement(_carbon_react.BreadcrumbItem, { key, ...rest, "data-original-id": id, title: title ?? label }, label))))); }, [ breadcrumbs, label, overflowAriaLabel ]); (0, react.useEffect)(() => { /* istanbul ignore if */ if (breadcrumbs.length === 0) { setDisplayedBreadcrumbItems([]); return; } const newDisplayedBreadcrumbItems = breadcrumbs.map(({ className, key, label, shortTitle, title, ...rest }, index) => /* @__PURE__ */ react.default.createElement(_carbon_react.BreadcrumbItem, { key, className: index > 0 || displayCount > 1 ? (0, import_classnames.default)([className, `${blockClass}__displayed-breadcrumb`]) : className, title: index + 1 === breadcrumbs.length ? title : null, ...rest }, shortTitle || label)); const overflowPosition = displayCount > 1 ? 1 : 0; let newOverflowBreadcrumbItems = newDisplayedBreadcrumbItems.splice(overflowPosition, breadcrumbs.length - displayCount); if (newOverflowBreadcrumbItems.length) newDisplayedBreadcrumbItems.splice(overflowPosition, 0, /* @__PURE__ */ react.default.createElement(BreadcrumbOverflowMenu, { overflowItems: newOverflowBreadcrumbItems, key: `displayed-breadcrumb-${internalId}-overflow` })); setDisplayedBreadcrumbItems(newDisplayedBreadcrumbItems); }, [breadcrumbs, displayCount]); const checkFullyVisibleBreadcrumbItems = () => { if (!breadcrumbItemWithOverflow.current) return; const displayItemIndex = (itemCount, index) => { if (index === 0) return itemCount - 1; else if (index === 1) return 1; else return itemCount - index; }; if (maxVisible <= 1) setDisplayCount(1); else { let willFit = 0; let spaceAvailable = breadcrumbItemWithOverflow.current.offsetWidth; /* istanbul ignore next */ if (sizingContainerRef.current) { const sizingBreadcrumbItems = sizingContainerRef.current.querySelectorAll(`.${carbonPrefix}--breadcrumb-item`); const breadcrumbWidthsIncludingMargin = []; for (let item of sizingBreadcrumbItems) { const computedStyle = window ? window.getComputedStyle(sizingBreadcrumbItems[0]) : null; const marginWidths = computedStyle ? parseFloat(computedStyle.marginLeft || 0, 10) + parseFloat(computedStyle.marginRight || 0, 10) : 0; breadcrumbWidthsIncludingMargin.push(item.offsetWidth + marginWidths); } let overflowWidth = breadcrumbWidthsIncludingMargin[0]; for (let i = 0; i < breadcrumbWidthsIncludingMargin.length - 1; i++) { const index = displayItemIndex(breadcrumbWidthsIncludingMargin.length, i); if (spaceAvailable >= breadcrumbWidthsIncludingMargin[index]) { spaceAvailable -= breadcrumbWidthsIncludingMargin[index]; willFit += 1; } else break; } if (willFit < breadcrumbWidthsIncludingMargin.length - 1) { if (spaceAvailable < overflowWidth) while (willFit > 0 && spaceAvailable < overflowWidth) { willFit -= 1; const itemToRemove = displayItemIndex(breadcrumbWidthsIncludingMargin.length, willFit); spaceAvailable += breadcrumbWidthsIncludingMargin[itemToRemove]; } } } if (willFit <= 1) setDisplayCount(1); else setDisplayCount(maxVisible ? Math.min(willFit, maxVisible) : willFit); } }; (0, react.useEffect)(() => { checkFullyVisibleBreadcrumbItems(); }, [hiddenSizingItems, maxVisible]); /* istanbul ignore next */ const handleResize = () => { /* istanbul ignore next */ checkFullyVisibleBreadcrumbItems(); }; let backItem = breadcrumbs[breadcrumbs.length - 1]; /* istanbul ignore if */ if (backItem.isCurrentPage) backItem = breadcrumbs[breadcrumbs.length - 2]; require_useResizeObserver.useResizeObserver(sizingContainerRef, handleResize); require_useResizeObserver.useResizeObserver(breadcrumbItemWithOverflow, handleResize); return /* @__PURE__ */ react.default.createElement("div", { className: (0, import_classnames.default)(blockClass, className, { [`${blockClass}__with-items`]: displayedBreadcrumbItems.length > 1 }), ref: breadcrumbItemWithOverflow }, /* @__PURE__ */ react.default.createElement("div", { className: (0, import_classnames.default)([`${blockClass}__space`]) }, hiddenSizingItems, /* @__PURE__ */ react.default.createElement(_carbon_react.Breadcrumb, { "aria-label": label, className: (0, import_classnames.default)(`${blockClass}__breadcrumb-container`, { [`${blockClass}__breadcrumb-container-with-items`]: displayedBreadcrumbItems.length > 1 }), noTrailingSlash, ...other }, backItem?.href && (backItem?.label || backItem?.title) && /* @__PURE__ */ react.default.createElement(_carbon_react.BreadcrumbItem, { className: (0, import_classnames.default)(`${blockClass}__breadcrumb-back`) }, /* @__PURE__ */ react.default.createElement(_carbon_react.Link, { href: backItem.href, renderIcon: () => /* @__PURE__ */ react.default.createElement(_carbon_react.Tooltip, { align: "right", label: backItem.title || backItem.label, className: `${blockClass}__back__button ${carbonPrefix}--icon-tooltip` }, /* @__PURE__ */ react.default.createElement(require_TooltipTrigger.TooltipTrigger, null, /* @__PURE__ */ react.default.createElement(_carbon_react_icons.ArrowLeft, { size: 16 }))) })), displayedBreadcrumbItems))); }; BreadcrumbWithOverflow = require_settings.pkg.checkComponentEnabled(BreadcrumbWithOverflow, componentName); BreadcrumbWithOverflow.propTypes = { breadcrumbs: prop_types.default.arrayOf(prop_types.default.shape({ /** * Optional string representing the link location for the BreadcrumbItem */ href: prop_types.default.string, /** * Provide if this breadcrumb item represents the current page */ isCurrentPage: prop_types.default.bool, /** * Key required to render array efficiently */ key: prop_types.default.string.isRequired, /** * Pass in content that will be inside of the BreadcrumbItem */ label: prop_types.default.node, /** * An optional title label for extra long breadcrumb */ shortTitle: prop_types.default.string, /** * A string based alternative to the children, required only if children is not of type string */ title: prop_types.default.string })), /** * className */ className: prop_types.default.string, /** * Label for the Breadcrumb component */ label: prop_types.default.string, /** * maxVisible: maximum visible breadcrumb-items before overflow is used (values less than 1 are treated as 1) */ maxVisible: prop_types.default.number, /** * noTrailing slash - same as for Carbon */ noTrailingSlash: prop_types.default.bool, /** * overflowAriaLabel label for open close button overflow used for breadcrumb items that do not fit. */ overflowAriaLabel: prop_types.default.string, /** * overflowTooltipAlign: align tooltip position */ overflowTooltipAlign: _carbon_react.Tooltip.propTypes.align }; BreadcrumbWithOverflow.displayName = componentName; //#endregion Object.defineProperty(exports, "BreadcrumbWithOverflow", { enumerable: true, get: function() { return BreadcrumbWithOverflow; } });