@carbon/react
Version:
React components for the Carbon Design System
345 lines (343 loc) • 15.2 kB
JavaScript
/**
* Copyright IBM Corp. 2016, 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_usePrefix = require("../../internal/usePrefix.js");
const require_Text = require("../Text/Text.js");
const require_useIsomorphicEffect = require("../../internal/useIsomorphicEffect.js");
const require_useId = require("../../internal/useId.js");
const require_AspectRatio = require("../AspectRatio/AspectRatio.js");
const require_index = require("../Popover/index.js");
const require_DefinitionTooltip = require("../Tooltip/DefinitionTooltip.js");
const require_MenuItem = require("../Menu/MenuItem.js");
const require_Grid = require("../Grid/Grid.js");
const require_Column = require("../Grid/Column.js");
const require_index$1 = require("../MenuButton/index.js");
const require_useMatchMedia = require("../../internal/useMatchMedia.js");
const require_Tag = require("../Tag/Tag.js");
const require_OperationalTag = require("../Tag/OperationalTag.js");
const require_useOverflowItems = require("../../internal/useOverflowItems.js");
let classnames = require("classnames");
classnames = require_runtime.__toESM(classnames);
let react = require("react");
react = require_runtime.__toESM(react);
let prop_types = require("prop-types");
prop_types = require_runtime.__toESM(prop_types);
let react_jsx_runtime = require("react/jsx-runtime");
let _carbon_utilities = require("@carbon/utilities");
let _carbon_layout = require("@carbon/layout");
//#region src/components/PageHeader/PageHeader.tsx
/**
* Copyright IBM Corp. 2025, 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 PageHeader = react.default.forwardRef(({ className, children, ...other }, ref) => {
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: (0, classnames.default)({ [`${require_usePrefix.usePrefix()}--page-header`]: true }, className),
ref,
...other,
children
});
});
PageHeader.displayName = "PageHeader";
const PageHeaderBreadcrumbBar = react.default.forwardRef(({ border = true, className, children, renderIcon: IconElement, contentActions, contentActionsFlush, pageActions, pageActionsFlush, ...other }, ref) => {
const prefix = require_usePrefix.usePrefix();
const classNames = (0, classnames.default)({
[`${prefix}--page-header__breadcrumb-bar`]: true,
[`${prefix}--page-header__breadcrumb-bar-border`]: border,
[`${prefix}--page-header__breadcrumb__actions-flush`]: pageActionsFlush
}, className);
const contentActionsClasses = (0, classnames.default)({ [`${prefix}--page-header__breadcrumb__content-actions`]: !contentActionsFlush });
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: classNames,
ref,
...other,
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_Grid.GridAsGridComponent, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_Column.default, {
lg: 16,
md: 8,
sm: 4,
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: `${prefix}--page-header__breadcrumb-container`,
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: `${prefix}--page-header__breadcrumb-wrapper`,
children: [IconElement && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: `${prefix}--page-header__breadcrumb__icon`,
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconElement, {})
}), children]
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: `${prefix}--page-header__breadcrumb__actions`,
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: contentActionsClasses,
children: contentActions
}), pageActions]
})]
})
}) })
});
});
PageHeaderBreadcrumbBar.displayName = "PageHeaderBreadcrumbBar";
const PageHeaderContent = react.default.forwardRef(({ className, children, title, renderIcon: IconElement, contextualActions, pageActions, ...other }, ref) => {
const prefix = require_usePrefix.usePrefix();
const classNames = (0, classnames.default)({ [`${prefix}--page-header__content`]: true }, className);
const titleRef = (0, react.useRef)(null);
const [isEllipsisApplied, setIsEllipsisApplied] = (0, react.useState)(false);
const isEllipsisActive = (element) => {
setIsEllipsisApplied(element.offsetHeight < element.scrollHeight);
return element.offsetHeight < element.scrollHeight;
};
require_useIsomorphicEffect.default(() => {
if (titleRef.current) isEllipsisActive(titleRef.current);
}, [title]);
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: classNames,
ref,
...other,
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_Grid.GridAsGridComponent, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_Column.default, {
lg: 16,
md: 8,
sm: 4,
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: `${prefix}--page-header__content__title-wrapper`,
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: `${prefix}--page-header__content__start`,
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: `${prefix}--page-header__content__title-container`,
children: [IconElement && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: `${prefix}--page-header__content__icon`,
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconElement, {})
}), isEllipsisApplied ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_DefinitionTooltip.DefinitionTooltip, {
definition: title,
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_Text.Text, {
ref: titleRef,
as: "h4",
className: `${prefix}--page-header__content__title`,
children: title
})
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_Text.Text, {
ref: titleRef,
as: "h4",
className: `${prefix}--page-header__content__title`,
children: title
})]
}), contextualActions && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: `${prefix}--page-header__content__contextual-actions`,
children: contextualActions
})]
}), pageActions]
}), children]
}) })
});
});
PageHeaderContent.displayName = "PageHeaderContent";
PageHeaderContent.propTypes = {
children: prop_types.default.node,
className: prop_types.default.string,
renderIcon: prop_types.default.oneOfType([prop_types.default.func, prop_types.default.object]),
title: prop_types.default.string.isRequired,
subtitle: prop_types.default.string,
contextualActions: prop_types.default.node,
pageActions: prop_types.default.node
};
const PageHeaderContentPageActions = ({ className, children, menuButtonLabel = "Actions", actions, ...other }) => {
const classNames = (0, classnames.default)({ [`${require_usePrefix.usePrefix()}--page-header__content__page-actions`]: true }, className);
const containerRef = (0, react.useRef)(null);
const offsetRef = (0, react.useRef)(null);
const [menuButtonVisibility, setMenuButtonVisibility] = (0, react.useState)(false);
const [hiddenItems, setHiddenItems] = (0, react.useState)([]);
require_useIsomorphicEffect.default(() => {
if (menuButtonVisibility && offsetRef.current) {
const width = offsetRef.current.offsetWidth;
document.documentElement.style.setProperty("--pageheader-title-grid-width", `${width}px`);
}
}, [menuButtonVisibility]);
(0, react.useEffect)(() => {
if (!containerRef.current || !Array.isArray(actions)) return;
(0, _carbon_utilities.createOverflowHandler)({
container: containerRef.current,
maxVisibleItems: containerRef.current.children.length - 1,
onChange: (visible, hidden) => {
setHiddenItems(actions?.slice(visible.length));
if (hidden.length > 0) setMenuButtonVisibility(true);
}
});
}, []);
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: classNames,
ref: containerRef,
...other,
children: actions && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: Array.isArray(actions) && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [actions.map((action) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: react.default.cloneElement(action.body, {
...action.body.props,
onClick: action.onClick
}) }, action.id)), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
"data-offset": true,
"data-hidden": true,
ref: offsetRef,
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_index$1.MenuButton, {
menuAlignment: "bottom-end",
label: menuButtonLabel,
size: "md",
children: [...hiddenItems].reverse().map((item) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_MenuItem.MenuItem, {
onClick: item.onClick,
...item.menuItem
}, item.id))
})
})] }) })
});
};
PageHeaderContentPageActions.displayName = "PageHeaderContentPageActions";
PageHeaderContentPageActions.propTypes = {
children: prop_types.default.node,
className: prop_types.default.string,
menuButtonLabel: prop_types.default.string,
actions: prop_types.default.oneOfType([prop_types.default.node, prop_types.default.array])
};
const PageHeaderContentText = ({ className, children, subtitle, ...other }) => {
const prefix = require_usePrefix.usePrefix();
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: (0, classnames.default)({ [`${prefix}--page-header__content__body`]: true }, className),
...other,
children: [subtitle && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_Text.Text, {
as: "h3",
className: `${prefix}--page-header__content__subtitle`,
children: subtitle
}), children]
});
};
PageHeaderContentText.displayName = "PageHeaderContentText";
PageHeaderContentText.propTypes = {
children: prop_types.default.node,
className: prop_types.default.string,
subtitle: prop_types.default.string
};
const PageHeaderHeroImage = ({ className, children, ...other }) => {
const classNames = (0, classnames.default)({ [`${require_usePrefix.usePrefix()}--page-header__hero-image`]: true }, className);
const isLg = require_useMatchMedia.useMatchMedia(`(min-width: ${_carbon_layout.breakpoints.lg.width})`);
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_AspectRatio.default, {
className: classNames,
...other,
ratio: isLg ? "2x1" : "3x2",
children
});
};
PageHeaderHeroImage.displayName = "PageHeaderHeroImage";
PageHeaderHeroImage.propTypes = {
children: prop_types.default.node,
className: prop_types.default.string
};
const PageHeaderTabBar = react.default.forwardRef(({ className, children, tags = [], ...other }, ref) => {
const prefix = require_usePrefix.usePrefix();
const classNames = (0, classnames.default)({ [`${prefix}--page-header__tab-bar`]: true }, className);
const [openPopover, setOpenPopover] = (0, react.useState)(false);
const tagSize = tags[0]?.size || "md";
const instanceId = require_useId.useId("PageHeaderTabBar");
const tagsWithIds = (0, react.useMemo)(() => {
return tags.map((tag, index) => ({
...tag,
id: tag.id || `tag-${index}-${instanceId}`
}));
}, [instanceId, tags]);
const tagsContainerRef = (0, react.useRef)(null);
const offsetRef = (0, react.useRef)(null);
(0, react.useEffect)(() => {
const handleResize = () => {
setOpenPopover(false);
};
window.addEventListener("resize", handleResize);
return () => {
window.removeEventListener("resize", handleResize);
};
}, []);
const { visibleItems = [], hiddenItems = [], itemRefHandler = () => {} } = require_useOverflowItems.default(tagsWithIds, tagsContainerRef, offsetRef) || {
visibleItems: [],
hiddenItems: [],
itemRefHandler: () => {}
};
const handleOverflowClick = (0, react.useCallback)((event) => {
event.stopPropagation();
setOpenPopover((prev) => !prev);
}, []);
const renderTags = () => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: `${prefix}--page-header__tags`,
ref: tagsContainerRef,
children: [visibleItems.map((tag) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_Tag.default, {
ref: (node) => itemRefHandler(tag.id, node),
type: tag.type,
size: tag.size,
className: `${prefix}--page-header__tag-item`,
children: tag.text
}, tag.id)), hiddenItems.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_index.Popover, {
open: openPopover,
onRequestClose: () => setOpenPopover(false),
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_OperationalTag.default, {
onClick: handleOverflowClick,
"aria-expanded": openPopover,
text: `+${hiddenItems.length}`,
size: tagSize
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_index.PopoverContent, {
className: "tag-popover-content",
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: `${prefix}--page-header__tags-popover-list`,
children: hiddenItems.map((tag) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_Tag.default, {
type: tag.type,
size: tag.size,
children: tag.text
}, tag.id))
})
})]
})]
});
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: classNames,
ref,
...other,
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_Grid.GridAsGridComponent, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_Column.default, {
lg: 16,
md: 8,
sm: 4,
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
className: `${prefix}--page-header__tab-bar--tablist`,
children: [children, tags.length > 0 && renderTags()]
})
}) })
});
});
PageHeaderTabBar.displayName = "PageHeaderTabBar";
/**
* -------
* Exports
* -------
*/
const Root = PageHeader;
Root.displayName = "PageHeader.Root";
const BreadcrumbBar = PageHeaderBreadcrumbBar;
BreadcrumbBar.displayName = "PageHeaderBreadcrumbBar";
const Content = PageHeaderContent;
Content.displayName = "PageHeaderContent";
const ContentPageActions = PageHeaderContentPageActions;
ContentPageActions.displayName = "PageHeaderContentPageActions";
const ContentText = PageHeaderContentText;
ContentText.displayName = "PageHeaderContentText";
const HeroImage = PageHeaderHeroImage;
HeroImage.displayName = "PageHeaderHeroImage";
const TabBar = PageHeaderTabBar;
TabBar.displayName = "PageHeaderTabBar";
//#endregion
exports.BreadcrumbBar = BreadcrumbBar;
exports.Content = Content;
exports.ContentPageActions = ContentPageActions;
exports.ContentText = ContentText;
exports.HeroImage = HeroImage;
exports.PageHeader = PageHeader;
exports.PageHeaderBreadcrumbBar = PageHeaderBreadcrumbBar;
exports.PageHeaderContent = PageHeaderContent;
exports.PageHeaderContentPageActions = PageHeaderContentPageActions;
exports.PageHeaderContentText = PageHeaderContentText;
exports.PageHeaderHeroImage = PageHeaderHeroImage;
exports.PageHeaderTabBar = PageHeaderTabBar;
exports.Root = Root;
exports.TabBar = TabBar;