UNPKG

@carbon/ibm-products

Version:

Carbon for IBM Products

898 lines (872 loc) 41 kB
/** * Copyright IBM Corp. 2020, 2025 * * This source code is licensed under the Apache-2.0 license found in the * LICENSE file in the root directory of this source tree. */ 'use strict'; var _rollupPluginBabelHelpers = require('../../_virtual/_rollupPluginBabelHelpers.js'); var react = require('@carbon/react'); var React = require('react'); var TagSet = require('../TagSet/TagSet.js'); var layout = require('@carbon/layout'); var PageHeaderUtils = require('./PageHeaderUtils.js'); var propsHelper = require('../../global/js/utils/props-helper.js'); var icons = require('@carbon/react/icons'); var PageHeaderTitle = require('./PageHeaderTitle.js'); var index = require('../../_virtual/index.js'); var cx = require('classnames'); var devtools = require('../../global/js/utils/devtools.js'); var settings = require('../../settings.js'); var useResizeObserver = require('../../global/js/hooks/useResizeObserver.js'); var useOverflowString = require('../../global/js/hooks/useOverflowString.js'); var useWindowScroll = require('../../global/js/hooks/useWindowScroll.js'); var useWindowResize = require('../../global/js/hooks/useWindowResize.js'); var useIsomorphicEffect = require('../../global/js/hooks/useIsomorphicEffect.js'); var BreadcrumbWithOverflow = require('../BreadcrumbWithOverflow/BreadcrumbWithOverflow.js'); var ActionBar = require('../ActionBar/ActionBar.js'); var ButtonSetWithOverflow = require('../ButtonSetWithOverflow/ButtonSetWithOverflow.js'); const componentName = 'PageHeader'; settings.pkg._silenceWarnings(true); settings.pkg.component.ActionBar = true; // Default values for props const defaults = { fullWidthGrid: false, narrowGrid: false, breadcrumbOverflowTooltipAlign: 'right' }; exports.PageHeader = /*#__PURE__*/React.forwardRef((_ref, ref) => { let { // The component props, in alphabetical order (for consistency). actionBarItems, actionBarMenuOptionsClass, actionBarOverflowAriaLabel, allTagsModalSearchLabel, allTagsModalSearchPlaceholderText, allTagsModalTitle, hasBackgroundAlways: deprecated_hasBackgroundAlways, breadcrumbOverflowAriaLabel, breadcrumbLabel, breadcrumbs, children, className, collapseHeader, collapseHeaderIconDescription, collapseTitle, disableBreadcrumbScroll: deprecated_disableBreadcrumbScroll, enableBreadcrumbScroll, expandHeaderIconDescription, fullWidthGrid = defaults.fullWidthGrid, hasCollapseHeaderToggle, narrowGrid = defaults.narrowGrid, navigation, pageActions, pageActionsOverflowLabel, pageActionsMenuOptionsClass, showAllTagsLabel, subtitle, tags, title, withoutBackground, breadcrumbOverflowTooltipAlign = defaults.breadcrumbOverflowTooltipAlign, // Collect any other property values passed in. ...rest } = _ref; // handle deprecated props - START // if withoutBackground is nullish check deprecated_hasBackgroundAlways and default false withoutBackground ??= !(deprecated_hasBackgroundAlways ?? true); // prefer enabled if nullish check deprecated_disableBreadcrumbScroll and default false enableBreadcrumbScroll ??= !(deprecated_disableBreadcrumbScroll ?? true); // handle deprecated props - END const [metrics, setMetrics] = React.useState({}); const [pageHeaderStyles, setPageHeaderStyles] = React.useState({ ...rest?.style }); // refs const localHeaderRef = React.useRef(null); const headerRef = ref || localHeaderRef; const sizingContainerRef = React.useRef(null); const offsetTopMeasuringRef = React.useRef(null); const overflowMenuRef = React.useRef(null); // state based on props only const hasActionBar = actionBarItems && actionBarItems.length > 0; const hasBreadcrumbRow = !!breadcrumbs || !!actionBarItems; if (tags && tags?.length > 10) { if (!allTagsModalSearchLabel) { throw new Error("allTagsModalSearchLabel' is required."); } if (!allTagsModalSearchPlaceholderText) { throw new Error("'allTagsModalSearchPlaceholderText' is required."); } if (!allTagsModalTitle) { throw new Error("'allTagsModalTitle' is required."); } if (!showAllTagsLabel) { throw new Error("'showAllTagsLabel' is required."); } } // utility functions const checkUpdateVerticalSpace = function () { return PageHeaderUtils.utilCheckUpdateVerticalSpace(headerRef, offsetTopMeasuringRef, navigation, enableBreadcrumbScroll, hasActionBar || false, widthIsNarrow, setMetrics); }; // NOTE: The buffer is used to add space between the bottom of the header and the last content // Not pre-collapsed and (subtitle or children) const lastRowBufferActive = (title || pageActions) && !collapseTitle || subtitle || children; // state based on scroll/resize based effects const [pageActionsInBreadcrumbRow, setPageActionsInBreadcrumbRow] = React.useState(false); const [scrollYValue, setScrollYValue] = React.useState(0); const [hasCollapseButton, setHasCollapseButton] = React.useState(false); const [spaceForCollapseButton, setSpaceForCollapseButton] = React.useState(false); const [actionBarMaxWidth, setActionBarMaxWidth] = React.useState(0); const [actionBarMinWidth, setActionBarMinWidth] = React.useState(0); const [pageActionInBreadcrumbMaxWidth, setPageActionInBreadcrumbMaxWidth] = React.useState(0); const [pageActionInBreadcrumbMinWidth, setPageActionInBreadcrumbMinWidth] = React.useState(0); const [actionBarColumnWidth, setActionBarColumnWidth] = React.useState(0); const [fullyCollapsed, setFullyCollapsed] = React.useState(false); const [widthIsNarrow, setWidthIsNarrow] = React.useState(false); const prefix = react.usePrefix(); // handlers const handleActionBarWidthChange = _ref2 => { let { minWidth, maxWidth } = _ref2; if (minWidth !== actionBarMinWidth || maxWidth !== actionBarMaxWidth) { let overflowMenuWidth = 0; const overflowMenu = overflowMenuRef?.current?.querySelector(`.${prefix}--overflow-menu`); if (overflowMenu) { overflowMenuWidth = overflowMenu.offsetWidth; } /* don't know how to test resize */ /* istanbul ignore next */ setActionBarMaxWidth(maxWidth + overflowMenuWidth); /* don't know how to test resize */ /* istanbul ignore next */ setActionBarMinWidth(minWidth); } }; const handlePageActionWidthChange = _ref3 => { let { minWidth, maxWidth } = _ref3; /* don't know how to test resize */ /* istanbul ignore next */ setPageActionInBreadcrumbMaxWidth(maxWidth); /* don't know how to test resize */ /* istanbul ignore next */ setPageActionInBreadcrumbMinWidth(minWidth); }; /* istanbul ignore next */ const handleResizeActionBarColumn = _ref4 => { let { width } = _ref4; /* don't know how to test resize */ /* istanbul ignore next */ setActionBarColumnWidth(width); }; /* istanbul ignore next */ const handleResize = () => { // receives width and height parameters if needed /* don't know how to test resize */ /* istanbul ignore next */ checkUpdateVerticalSpace(); }; const handleCollapseToggle = () => { PageHeaderUtils.utilSetCollapsed(!fullyCollapsed, headerRef, metrics?.headerOffset, metrics?.headerTopValue); }; // use effects React.useEffect(() => { /* istanbul ignore else */ if (pageActions?.content) { const { minWidth, maxWidth } = pageActions; handlePageActionWidthChange({ minWidth, maxWidth }); } }, [pageActions]); React.useEffect(() => { // Determine the location of the pageAction buttons setPageActionsInBreadcrumbRow(collapseTitle || hasActionBar && !!metrics?.titleRowSpaceAbove && scrollYValue > metrics?.titleRowSpaceAbove || widthIsNarrow && !!metrics?.pageActionsSpaceAbove && scrollYValue > metrics?.pageActionsSpaceAbove); }, [hasActionBar, metrics.breadcrumbRowSpaceBelow, metrics.titleRowSpaceAbove, metrics.pageActionsSpaceAbove, collapseTitle, scrollYValue, widthIsNarrow]); React.useEffect(() => { // Assesses the size of the action bar and page action area and their required // space before setting their sizes // let newActionBarWidth = 'initial'; let newPageActionInBreadcrumbWidth = 'initial'; /* don't know how to test resize */ /* istanbul ignore if */ if (actionBarColumnWidth > 0) { if (pageActionInBreadcrumbMaxWidth > 0 && actionBarColumnWidth > actionBarMaxWidth + pageActionInBreadcrumbMaxWidth) { newPageActionInBreadcrumbWidth = `${pageActionInBreadcrumbMaxWidth}px`; } else if (pageActionInBreadcrumbMinWidth > 0) { newPageActionInBreadcrumbWidth = `${pageActionInBreadcrumbMinWidth}px`; } if (actionBarMaxWidth > 0 && actionBarColumnWidth > pageActionInBreadcrumbMinWidth + actionBarMaxWidth) { newActionBarWidth = `${actionBarMaxWidth}px`; } else { if (actionBarMinWidth > 0) { newActionBarWidth = `${actionBarColumnWidth - pageActionInBreadcrumbMinWidth}px`; } } } setPageHeaderStyles(prev => ({ ...prev, [`--${PageHeaderUtils.blockClass}--max-action-bar-width-px`]: newActionBarWidth, [`--${PageHeaderUtils.blockClass}--button-set-in-breadcrumb-width-px`]: `${newPageActionInBreadcrumbWidth}` })); }, [actionBarColumnWidth, actionBarMaxWidth, actionBarMinWidth, pageActionInBreadcrumbMaxWidth, pageActionInBreadcrumbMinWidth, headerRef]); React.useEffect(() => { // Updates custom CSS props used to manage scroll behavior /* istanbul ignore next */ setPageHeaderStyles(prev => ({ ...prev, [`--${PageHeaderUtils.blockClass}--height-px`]: `${metrics.headerHeight}px`, [`--${PageHeaderUtils.blockClass}--width-px`]: `${metrics.headerWidth}px`, [`--${PageHeaderUtils.blockClass}--header-top`]: `${(metrics?.headerTopValue || 0) + (metrics?.headerOffset || 0)}px`, [`--${PageHeaderUtils.blockClass}--breadcrumb-title-visibility`]: scrollYValue > 0 ? 'visible' : 'hidden', [`--${PageHeaderUtils.blockClass}--scroll`]: `${scrollYValue}`, [`--${PageHeaderUtils.blockClass}--breadcrumb-title-top`]: `${metrics.breadcrumbTitleHeight && metrics.titleRowSpaceAbove && Math.max(0, metrics.breadcrumbTitleHeight + metrics.titleRowSpaceAbove - scrollYValue)}px`, [`--${PageHeaderUtils.blockClass}--breadcrumb-title-opacity`]: `${Math.min(1, Math.max(0, (scrollYValue - (metrics.titleRowSpaceAbove || 0)) / (metrics.breadcrumbTitleHeight || 1) // don't want to divide by zero ))}`, [`--${PageHeaderUtils.blockClass}--breadcrumb-row-width-px`]: `${metrics?.breadcrumbRowWidth}px` })); }, [headerRef, enableBreadcrumbScroll, metrics, metrics.breadcrumbRowHeight, metrics.breadcrumbRowSpaceBelow, metrics.breadcrumbTitleHeight, metrics.breadcrumbRowWidth, metrics.headerHeight, metrics.headerWidth, metrics.headerOffset, metrics.headerTopValue, metrics.navigationRowHeight, navigation, scrollYValue, tags]); useWindowScroll.useNearestScroll(headerRef, // on scroll or various layout changes check updates if needed // istanbul ignore next _ref5 => { let { current } = _ref5; setPageHeaderStyles(prev => ({ ...prev, [`--${PageHeaderUtils.blockClass}--breadcrumb-top`]: `${metrics.headerOffset}px` })); const fullyCollapsed = current.scrollY + metrics.headerTopValue + metrics.headerOffset >= 0; setFullyCollapsed(fullyCollapsed); // set offset for tagset tooltip /* istanbul ignore next */ const tagsetTooltipOffset = fullyCollapsed && metrics?.headerHeight && metrics?.headerTopValue && metrics?.headerOffset ? metrics.headerHeight + metrics.headerTopValue + metrics.headerOffset : (metrics.headerHeight || 0) + (metrics.headerOffset || 0); /* istanbul ignore next */ document.documentElement.style.setProperty(`--${PageHeaderUtils.blockClass}--tagset-tooltip-position`, fullyCollapsed ? 'fixed' : 'absolute'); document.documentElement.style.setProperty(`--${PageHeaderUtils.blockClass}--tagset-tooltip-offset`, `${tagsetTooltipOffset}px`); setScrollYValue(current.scrollY); }, [metrics.headerHeight, metrics.headerTopValue, metrics.headerOffset, enableBreadcrumbScroll]); useWindowResize.useWindowResize(_ref6 => { let { current } = _ref6; // on window resize and other updates some values may have changed checkUpdateVerticalSpace(); setWidthIsNarrow(current.innerWidth / 16 < parseInt(layout.breakpoints.md.width)); // small (below medium) media query }, [actionBarItems, children, breadcrumbs, enableBreadcrumbScroll, navigation, pageActions, subtitle, tags, title]); React.useEffect(() => { checkUpdateVerticalSpace(); // eslint-disable-next-line react-hooks/exhaustive-deps }, [fullWidthGrid, narrowGrid]); React.useEffect(() => { // Determines the appropriate header background opacity based on the header config/height/scroll and the withoutBackground setting let result = withoutBackground ? 0 : 1; if (!result && metrics?.headerHeight && metrics.headerHeight > 0 && (breadcrumbs || actionBarItems || tags || navigation)) { const startAddingAt = parseFloat(`${layout.spacing[9]}`) * parseInt(`${layout.baseFontSize}`); const scrollRemaining = metrics.headerHeight - scrollYValue; /* don't know how to test resize */ /* istanbul ignore if */ if (scrollRemaining < startAddingAt) { const distanceAddingOver = startAddingAt - (metrics?.breadcrumbRowHeight || 0); result = Math.min(1, (startAddingAt - scrollRemaining) / distanceAddingOver); } } setPageHeaderStyles(prev => ({ ...prev, [`--${PageHeaderUtils.blockClass}--background-opacity`]: result })); }, [actionBarItems, withoutBackground, breadcrumbs, headerRef, metrics.breadcrumbRowHeight, metrics.headerHeight, navigation, scrollYValue, hasCollapseHeaderToggle, tags]); React.useEffect(() => { // only has toggle if requested and withoutBackground is unset/falsy // NOTE: prop-types isRequired.if for the expand and collapse // icon descriptions depends on the this. setHasCollapseButton(hasCollapseHeaderToggle && !withoutBackground || false); }, [withoutBackground, hasCollapseHeaderToggle]); React.useEffect(() => { // Determine if space is needed in the breadcrumb for a collapse button if (hasCollapseButton && !(navigation || tags) && metrics?.headerHeight) { setSpaceForCollapseButton(true); } else { setSpaceForCollapseButton(false); } }, [hasCollapseButton, navigation, tags, metrics.headerHeight]); const nextToTabsCheck = () => { /* istanbul ignore next */ return enableBreadcrumbScroll && !actionBarItems && metrics.headerTopValue && scrollYValue + metrics.headerTopValue >= 0; }; React.useEffect(() => { if (collapseHeader === true) { PageHeaderUtils.utilSetCollapsed(collapseHeader, headerRef, metrics.headerOffset, metrics.headerTopValue); } }, [collapseHeader, metrics.headerOffset, metrics.headerTopValue, headerRef]); useResizeObserver.useResizeObserver(sizingContainerRef, handleResizeActionBarColumn); useResizeObserver.useResizeObserver(headerRef, handleResize); // Determine what form of title to display in the breadcrumb const breadcrumbItemForTitle = PageHeaderUtils.utilGetBreadcrumbItemForTitle(PageHeaderUtils.blockClass, collapseTitle, title); const getBreadcrumbs = () => { if (breadcrumbs && breadcrumbItemForTitle) { return breadcrumbs.concat(breadcrumbItemForTitle); } else { if (breadcrumbItemForTitle) { return [breadcrumbItemForTitle]; } else { return breadcrumbs; } } }; const displayedBreadcrumbs = getBreadcrumbs(); useIsomorphicEffect.useIsomorphicEffect(() => { Object.keys(pageHeaderStyles).forEach(key => { // check if style is a css var if (key.startsWith('--')) { headerRef.current.style.setProperty(key, pageHeaderStyles[key]); } else { headerRef.current.style[key] = pageHeaderStyles[key]; } }); }, [headerRef, pageHeaderStyles]); const subtitleRef = React.useRef(null); const isOverflowing = useOverflowString.useOverflowStringHeight(subtitleRef); const subtitleContent = /*#__PURE__*/React.createElement("span", { ref: subtitleRef, className: `${PageHeaderUtils.blockClass}__subtitle-text` }, subtitle); return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", { className: `${PageHeaderUtils.blockClass}--offset-top-measuring-element`, ref: offsetTopMeasuringRef }), /*#__PURE__*/React.createElement("section", _rollupPluginBabelHelpers.extends({}, rest, { className: cx([PageHeaderUtils.blockClass, `${PageHeaderUtils.blockClass}--no-margins-below-row`, className, { [`${PageHeaderUtils.blockClass}--has-navigation`]: navigation || tags, [`${PageHeaderUtils.blockClass}--has-navigation-tags-only`]: !navigation && tags, [`${PageHeaderUtils.blockClass}--without-background`]: withoutBackground }]), ref: headerRef }, devtools.getDevtoolsProps(componentName)), /*#__PURE__*/React.createElement(react.FlexGrid, { fullWidth: fullWidthGrid === true || fullWidthGrid === 'xl', narrow: narrowGrid, className: cx({ [`${PageHeaderUtils.blockClass}--width--xl`]: fullWidthGrid === 'xl' }) }, /*#__PURE__*/React.createElement("div", { className: `${PageHeaderUtils.blockClass}__non-navigation-row-content` }, hasBreadcrumbRow ? /*#__PURE__*/React.createElement(react.Row, { className: cx(`${PageHeaderUtils.blockClass}__breadcrumb-row`, { [`${PageHeaderUtils.blockClass}__breadcrumb-row--next-to-tabs`]: nextToTabsCheck(), [`${PageHeaderUtils.blockClass}__breadcrumb-row--has-breadcrumbs`]: breadcrumbs || breadcrumbItemForTitle, [`${PageHeaderUtils.blockClass}__breadcrumb-row--has-action-bar`]: hasActionBar || widthIsNarrow, [`${PageHeaderUtils.blockClass}__has-page-actions-without-action-bar`]: !hasActionBar && !widthIsNarrow && pageActions, [`${PageHeaderUtils.blockClass}__has-page-actions-with-title-collapsed`]: collapseTitle && pageActions }) }, /*#__PURE__*/React.createElement("div", { className: `${PageHeaderUtils.blockClass}__breadcrumb-row--container` }, /*#__PURE__*/React.createElement(react.Column, { className: cx(`${PageHeaderUtils.blockClass}__breadcrumb-column`, { [`${PageHeaderUtils.blockClass}__breadcrumb-column--background`]: !!breadcrumbs || hasActionBar || widthIsNarrow }) }, (breadcrumbs || breadcrumbItemForTitle) && /*#__PURE__*/React.createElement(BreadcrumbWithOverflow.BreadcrumbWithOverflow, { className: `${PageHeaderUtils.blockClass}__breadcrumb`, noTrailingSlash: !!title, overflowAriaLabel: breadcrumbOverflowAriaLabel, breadcrumbs: displayedBreadcrumbs, overflowTooltipAlign: breadcrumbOverflowTooltipAlign, maxVisible: undefined, label: breadcrumbLabel })), /*#__PURE__*/React.createElement(react.Column, { className: cx([`${PageHeaderUtils.blockClass}__action-bar-column ${PageHeaderUtils.blockClass}__action-bar-column--background`, { [`${PageHeaderUtils.blockClass}__action-bar-column--has-page-actions`]: pageActions, [`${PageHeaderUtils.blockClass}__action-bar-column--influenced-by-collapse-button`]: spaceForCollapseButton }]) }, /*#__PURE__*/React.createElement("div", { className: `${PageHeaderUtils.blockClass}__action-bar-column-content`, ref: sizingContainerRef }, hasActionBar ? /*#__PURE__*/ // Investigate the responsive behavior or this and the title also fix the ActionBar Item and PageAction story css React.createElement(React.Fragment, null, thePageActions(true, pageActionsInBreadcrumbRow), /*#__PURE__*/React.createElement(ActionBar.ActionBar, { actions: actionBarItems, className: `${PageHeaderUtils.blockClass}__action-bar`, menuOptionsClass: `${cx(actionBarMenuOptionsClass, `${PageHeaderUtils.blockClass}__action-bar-menu-options`)}`, onWidthChange: handleActionBarWidthChange, overflowAriaLabel: actionBarOverflowAriaLabel, overflowMenuRef, rightAlign: true })) : (widthIsNarrow || pageActions) && thePageActions(true, pageActionsInBreadcrumbRow))))) : null, !collapseTitle && (title || pageActions) ? /*#__PURE__*/React.createElement(react.Row, { className: cx(`${PageHeaderUtils.blockClass}__title-row`, { [`${PageHeaderUtils.blockClass}__title-row--no-breadcrumb-row`]: !hasBreadcrumbRow, [`${PageHeaderUtils.blockClass}__title-row--under-action-bar`]: hasActionBar || widthIsNarrow, [`${PageHeaderUtils.blockClass}__title-row--has-page-actions`]: !!pageActions, [`${PageHeaderUtils.blockClass}__title-row--sticky`]: !!pageActions && !actionBarItems && hasBreadcrumbRow }) }, /*#__PURE__*/React.createElement(react.Column, { className: `${PageHeaderUtils.blockClass}__title-column` }, title ? /*#__PURE__*/React.createElement(PageHeaderTitle.PageHeaderTitle, { blockClass: PageHeaderUtils.blockClass, hasBreadcrumbRow: hasBreadcrumbRow, title: title }) : null), thePageActions(false, pageActionsInBreadcrumbRow)) : null, subtitle && /*#__PURE__*/React.createElement(react.Row, { className: `${PageHeaderUtils.blockClass}__subtitle-row` }, /*#__PURE__*/React.createElement(react.Column, { className: `${PageHeaderUtils.blockClass}__subtitle` }, isOverflowing ? /*#__PURE__*/React.createElement(react.DefinitionTooltip, { definition: subtitle, className: `${PageHeaderUtils.blockClass}__subtitle-tooltip` }, subtitleContent) : subtitleContent)), children ? /*#__PURE__*/React.createElement(react.Row, { className: `${PageHeaderUtils.blockClass}__available-row` }, /*#__PURE__*/React.createElement(react.Column, { className: `${PageHeaderUtils.blockClass}__available-column` }, children)) : null, (breadcrumbs || actionBarItems || title || pageActions || children || subtitle) && /*#__PURE__*/React.createElement("div", { className: cx([`${PageHeaderUtils.blockClass}__last-row-buffer`, { [`${PageHeaderUtils.blockClass}__last-row-buffer--active`]: lastRowBufferActive }]) }), // this navigation row scrolls under the breadcrumb if there is one tags && !navigation ? /*#__PURE__*/React.createElement(react.Row, { className: cx(`${PageHeaderUtils.blockClass}__navigation-row`, { [`${PageHeaderUtils.blockClass}__navigation-row--has-tags`]: tags }) }, /*#__PURE__*/React.createElement(react.Column, { className: cx(`${PageHeaderUtils.blockClass}__navigation-tags`, { [`${PageHeaderUtils.blockClass}__navigation-tags--tags-only`]: !navigation }) }, /*#__PURE__*/React.createElement(TagSet.TagSet, { overflowAlign: "bottom-right", allTagsModalSearchLabel, allTagsModalSearchPlaceholderText, allTagsModalTitle, tags, overflowClassName: `${PageHeaderUtils.blockClass}__navigation-tags-overflow`, showAllTagsLabel: showAllTagsLabel || '' }))) : null), // this navigation pushes the breadcrumb off or settles underneath it depending on enableBreadcrumbScroll navigation ? /*#__PURE__*/React.createElement(react.Row, { className: cx(`${PageHeaderUtils.blockClass}__navigation-row`, { [`${PageHeaderUtils.blockClass}__navigation-row--spacing-above-06`]: !!navigation, [`${PageHeaderUtils.blockClass}__navigation-row--has-tags`]: tags }) }, /*#__PURE__*/React.createElement(react.Column, { className: `${PageHeaderUtils.blockClass}__navigation-tabs` }, navigation), tags ? /*#__PURE__*/React.createElement(react.Column, { className: cx(`${PageHeaderUtils.blockClass}__navigation-tags`, { [`${PageHeaderUtils.blockClass}__navigation-tags--tags-only`]: !navigation }) }, /*#__PURE__*/React.createElement(TagSet.TagSet, { overflowAlign: "bottom-right", allTagsModalSearchLabel, allTagsModalSearchPlaceholderText, allTagsModalTitle, showAllTagsLabel: showAllTagsLabel || '', tags, overflowClassName: `${PageHeaderUtils.blockClass}__navigation-tags-overflow` })) : null) : null), hasCollapseButton ? /*#__PURE__*/React.createElement("div", { className: cx(`${PageHeaderUtils.blockClass}__collapse-expand-toggle`, { [`${PageHeaderUtils.blockClass}__collapse-expand-toggle--collapsed`]: fullyCollapsed }) }, /*#__PURE__*/React.createElement(react.Button, { hasIconOnly: true, iconDescription: /* istanbul ignore next */ fullyCollapsed ? expandHeaderIconDescription : collapseHeaderIconDescription, kind: "ghost", onClick: handleCollapseToggle, renderIcon: props => /*#__PURE__*/React.createElement(icons.ChevronUp, _rollupPluginBabelHelpers.extends({ size: 16 }, props)), size: "md", tooltipPosition: "bottom", tooltipAlignment: "end", type: "button" })) : null)); function thePageActions(isBreadcrumbRow, inBreadcrumbRow) { if (pageActions) { const Tag = isBreadcrumbRow ? 'div' : react.Column; // Only report size change of version action bar is rendered as part of the breadcrumb row. // and when there is an actionBar const handleWidthChange = isBreadcrumbRow && hasBreadcrumbRow ? handlePageActionWidthChange : () => {}; return /*#__PURE__*/React.createElement(Tag, { className: cx(`${PageHeaderUtils.blockClass}__page-actions`, { [`${PageHeaderUtils.blockClass}__page-actions--in-breadcrumb`]: inBreadcrumbRow }) }, /*#__PURE__*/React.createElement("div", { className: cx(`${PageHeaderUtils.blockClass}__page-actions-content`) }, pageActions?.content ?? /*#__PURE__*/React.createElement(ButtonSetWithOverflow.ButtonSetWithOverflow, { className: `${PageHeaderUtils.blockClass}__button-set-with-overflow`, menuOptionsClass: cx(pageActionsMenuOptionsClass, `${PageHeaderUtils.blockClass}__button-set-menu-options`), onWidthChange: handleWidthChange, buttons: pageActions, buttonSetOverflowLabel: pageActionsOverflowLabel, rightAlign: !widthIsNarrow }))); } } }); // Return a placeholder if not released and not enabled by feature flag exports.PageHeader = settings.pkg.checkComponentEnabled(exports.PageHeader, componentName); // copied from carbon-components-react/src/components/Tag/Tag.js for DocGen const TYPES = { red: 'Red', magenta: 'Magenta', purple: 'Purple', blue: 'Blue', cyan: 'Cyan', teal: 'Teal', green: 'Green', gray: 'Gray', 'cool-gray': 'Cool-Gray', 'warm-gray': 'Warm-Gray', 'high-contrast': 'High-Contrast', outline: 'Outline' }; const tagTypes = Object.keys(TYPES); const deprecatedProps = { /** * **Deprecated** see property `enableBreadcrumbScroll` */ disableBreadcrumbScroll: propsHelper.deprecateProp(index.default.bool, 'Property replaced by `enableBreadcrumbScroll`'), /** * **Deprecated** see property `withoutBackground` */ hasBackgroundAlways: propsHelper.deprecateProp(index.default.bool, 'Property replaced by `withoutBackground`') }; /**@ts-ignore */ exports.PageHeader.tagTypes = tagTypes; exports.PageHeader.propTypes = { /** * Specifies the action bar items which are the final items in the row top of the PageHeader. * Each item is specified as an object with the properties of a Carbon Button in icon only form. * Button kind, size, tooltipPosition, tooltipAlignment and type are ignored. */ /**@ts-ignore */ actionBarItems: index.default.arrayOf(index.default.shape({ /**@ts-ignore*/ ...propsHelper.prepareProps(react.Button.propTypes, ['kind', 'size', 'tooltipPosition', 'tooltipAlignment']), iconDescription: index.default.string.isRequired, /**@ts-ignore*/ onClick: react.Button.propTypes.onClick, /**@ts-ignore*/ renderIcon: react.Button.propTypes.renderIcon.isRequired })), /** * class name applied to the action bar overflow options */ actionBarMenuOptionsClass: index.default.string, /** * When there is insufficient space for all actionBarItems to be displayed this * aria label is used for the action bar overflow menu * * NOTE: This prop is required if actionBarItems are supplied */ /**@ts-ignore */ actionBarOverflowAriaLabel: index.default.string.isRequired.if(_ref7 => { let { actionBarItems } = _ref7; return actionBarItems && actionBarItems.length > 0; }), /** * When tags are supplied there may not be sufficient space to display all of the tags. This results in an overflow * menu being shown. If in the overflow menu there is still insufficient space this label is used in a dialog showing * all tags. * * **Note: Required if more than 10 tags** */ allTagsModalSearchLabel: TagSet.string_required_if_more_than_10_tags, /** * When tags are supplied there may not be sufficient space to display all of the tags. This results in an overflow * menu being shown. If in the overflow menu there is still insufficient space this placeholder is used in a dialog * showing all tags. * * **Note: Required if more than 10 tags** */ allTagsModalSearchPlaceholderText: TagSet.string_required_if_more_than_10_tags, /** * When tags are supplied there may not be sufficient space to display all of the tags. This results in an overflow * menu being shown. If in the overflow menu there is still insufficient space this title is used in a dialog showing * all tags. * * **Note: Required if more than 10 tags** */ allTagsModalTitle: TagSet.string_required_if_more_than_10_tags, /** * If the user supplies breadcrumbs then this property is required. * It is used in an overflow menu when there is insufficient space to display all breadcrumbs inline. */ /**@ts-ignore */ breadcrumbOverflowAriaLabel: index.default.string.isRequired.if(_ref8 => { let { breadcrumbs } = _ref8; return breadcrumbs && breadcrumbs.length > 0; }), /** * align breadcrumb overflow tooltip */ breadcrumbOverflowTooltipAlign: react.Tooltip.propTypes.align, /** * Specifies the breadcrumb components to be shown in the breadcrumb area of * the page header. Each item is specified as an object with optional fields * 'label' to supply the breadcrumb label, 'href' to supply the link location, * and 'isCurrentPage' to specify whether this breadcrumb component represents * the current page. Each item should also include a unique 'key' field to * enable efficient rendering, and if the label is not a string then a 'title' * field is required to provide a text alternative for display. Any other * fields in the object will be passed through to the breadcrumb element as * HTML attributes. */ /**@ts-ignore */ breadcrumbs: index.default.arrayOf(index.default.shape({ /** * Optional string representing the link location for the BreadcrumbItem */ href: index.default.string, /** * Provide if this breadcrumb item represents the current page */ isCurrentPage: index.default.bool, /** * Key required to render array efficiently */ key: index.default.string.isRequired, /** * Pass in content that will be inside of the BreadcrumbItem */ label: index.default.node, /** * A text version of the `label` for display, required if `label` is not a string. */ /**@ts-ignore */ title: index.default.string.isRequired.if(_ref9 => { let { label } = _ref9; return typeof label !== 'string'; }) })), /** * A zone for placing high-level, client content above the page tabs. * Accepts arbitrary renderable content as a React node. Optional. */ children: index.default.node, /** * Specifies class(es) to be applied to the top-level PageHeader node. * Optional. */ className: index.default.string, /** * The header can as a whole be collapsed, expanded or somewhere in between. * This setting controls the initial value, but also takes effect on change * * NOTE: The header is collapsed by setting the scroll position to hide part of the header. * Collapsing has no effect if there is insufficient content to scroll. */ collapseHeader: index.default.bool, /** * If `hasCollapseHeaderToggle` is set and `withoutBackground` is unset/falsy then assistive text is * required for both the expend and collapse states of the button component used. */ /**@ts-ignore */ collapseHeaderIconDescription: index.default.string.isRequired.if(_ref10 => { let { withoutBackground, hasCollapseHeaderToggle } = _ref10; return !withoutBackground && hasCollapseHeaderToggle; }), /** * The title row typically starts below the breadcrumb row. This option * preCollapses it into the breadcrumb row. */ collapseTitle: index.default.bool, /** * Standard keeps the breadcrumb on the page. This option allows the breadcrumb * to scroll off */ enableBreadcrumbScroll: index.default.bool, /** * If `hasCollapseHeaderToggle` is set and `withoutBackground` is unset/falsy then assistive text is * required for both the expend and collapse states of the button component used. */ /**@ts-ignore */ expandHeaderIconDescription: index.default.string.isRequired.if(_ref11 => { let { withoutBackground, hasCollapseHeaderToggle } = _ref11; return !withoutBackground && hasCollapseHeaderToggle; }), /** * The PageHeader is hosted in a Carbon grid, this value is passed through to the Carbon grid fullWidth prop. * 'xl' is used to override the grid width setting. Can be used with narrowGrid: true to get the largest size. */ /**@ts-ignore */ fullWidthGrid: index.default.oneOfType([index.default.bool, index.default.oneOf(['xl'])]), /** * Adds a button as the last element of the bottom row which collapses and expands the header. * * NOTE: The header is collapsed by setting the scroll position to hide part of the header. * Collapsing has no effect if there is insufficient content to scroll. */ /**@ts-ignore */ hasCollapseHeaderToggle: index.default.bool, /** * The PageHeader is hosted in a Carbon grid, this value is passed through to the Carbon grid narrow prop */ narrowGrid: index.default.bool, /** * Content for the navigation area in the PageHeader. Should * be a React element that is normally a Carbon Tabs component. Optional. */ navigation: index.default.element, // Supports Tabs /** * Specifies the primary page actions which are placed at the same level in the page as the title. * * Either a set of actions, each specified as an object with the properties of a Carbon Button plus: * * - label: node * * Or a single object * * - content: content to be rendered. NOTE: must be capable of restricting itself to the space provided. This 2.5rem height ($spacing-08) * and the width not used by action bar items when scrolled into toolbar. * - minWidth: smallest number of pixel width the content would like. NOTE: This is not guaranteed and may be less on small viewports. * - maxWidth: maximum number of pixels the content will grow to * Carbon Button API https://react.carbondesignsystem.com/?path=/docs/components-button--default#component-api */ /**@ts-ignore */ pageActions: index.default.oneOfType([index.default.arrayOf(index.default.shape({ /**@ts-ignore*/ ...react.Button.propTypes, key: index.default.string.isRequired, /**@ts-ignore*/ kind: react.Button.propTypes.kind, label: index.default.node, onClick: index.default.func })), index.default.shape({ /** * minWidth should not be more than 180 * The content is expected to adjust itself to fit in */ content: index.default.node.isRequired, minWidth: index.default.number.isRequired, maxWidth: index.default.number.isRequired })]), /** * class name applied to the page actions overflow options */ pageActionsMenuOptionsClass: index.default.string, /** * When there is insufficient space to display all of hte page actions inline a dropdown button menu is shown, * containing the page actions. This label is used as the display content of the dropdown button menu. * * NOTE: This prop is required if pageActions are supplied */ /**@ts-ignore */ pageActionsOverflowLabel: index.default.node.isRequired.if(_ref12 => { let { pageActions } = _ref12; return pageActions && pageActions.length > 0 && !pageActions.content; }), /** * When tags are supplied there may not be sufficient space to display all of the tags. This results in an overflow * menu being shown. If in the overflow menu there is still insufficient space this label is used to offer a * "View all tags" option. * * **Note: Required if more than 10 tags** */ showAllTagsLabel: TagSet.string_required_if_more_than_10_tags, /** * Sitting just below the title is this optional subtitle that provides additional context to * identify the current page. */ subtitle: index.default.node, /** * An array of tags to be shown as the final content in the PageHeader. * * Each tag is specified as an object with the following properties * **label**\* (required) to supply the tag content, and properties of the the Carbon Tag component, * such as **type**, **disabled**, **ref**, **className** , and any other Tag props. * * NOTE: **filter** is not supported. Any remaining fields in the object will be passed through to the HTML element * as HTML attributes. * * See https://react.carbondesignsystem.com/?path=/docs/components-tag--default */ /**@ts-ignore */ tags: index.default.arrayOf(index.default.shape({ ...propsHelper.prepareProps(react.Tag.propTypes, 'filter'), label: index.default.string.isRequired, // we duplicate this prop to improve the DocGen type: index.default.oneOf(tagTypes) })), /** * An optional page title supplied as a string or object with the following attributes: text, icon, loading * * Can be supplied either as: * - String * - Object containing * - text: title string * - shortTitle: alternative title for exceptionally long titles * - icon: optional icon * - loading: boolean shows loading indicator if true * - onChange: function to process the live value (React change === HTML Input) * - onSave: function to process a confirmed change * - editableLabel: label for edit required if onChange supplied * - cancelDescription: label for edit cancel button * - saveDescription: label for edit save button * - tooltipAlignment: position for tooltip displayed for large titles. Default to "bottom". * - Object containing user defined contents. These must fit within the area defined for the title in both main part of the header and the breadcrumb. * - content: title or name of current location shown in main part of page header * - breadcrumbContent: version of content used in the breadcrumb on scroll. If not supplied * - asText: String based representation of the title */ /**@ts-ignore */ title: index.default.oneOfType([index.default.shape({ // Update docgen if changed text: index.default.string.isRequired, shortTitle: index.default.string, icon: index.default.oneOfType([index.default.func, index.default.object]), loading: index.default.bool, // inline edit version properties editableLabel: index.default.string, // .isRequired.if(editInPlaceRequired), id: index.default.string, // .isRequired.if(editInPlaceRequired), onCancel: index.default.func, onChange: index.default.func, onSave: index.default.func, cancelDescription: index.default.string, //.isRequired.if(editInPlaceRequired), editDescription: index.default.string, // .isRequired.if(editInPlaceRequired), saveDescription: index.default.string, //.isRequired.if(editInPlaceRequired), tooltipAlignment: index.default.oneOf(['top', 'top-left', 'top-right', 'bottom', 'bottom-left', 'bottom-right', 'left', 'right']) // Update docgen if changed }), index.default.string, index.default.shape({ content: index.default.node.isRequired, breadcrumbContent: index.default.node, asText: index.default.string.isRequired })]), /** * Specifies if the PageHeader should appear without a background color, and defaults to the preferred `false` (a background color is shown). * Note that when `true` some parts of the header still gain a background if and when they stick to the top of the PageHeader on scroll. */ /**@ts-ignore */ withoutBackground: index.default.bool, ...deprecatedProps }; exports.PageHeader.displayName = componentName; exports.deprecatedProps = deprecatedProps;