UNPKG

@ui5/webcomponents-react

Version:

React Wrapper for UI5 Web Components and additional components

674 lines (667 loc) 28 kB
'use client'; import AvatarSize from '@ui5/webcomponents/dist/types/AvatarSize.js'; import { debounce, enrichEventWithDetails, ThemingParameters, useStylesheet, useSyncRef } from '@ui5/webcomponents-react-base'; import { clsx } from 'clsx'; import { cloneElement, forwardRef, isValidElement, useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { ObjectPageMode } from '../../enums/index.js'; import { addCustomCSSWithScoping } from '../../internal/addCustomCSSWithScoping.js'; import { safeGetChildrenArray } from '../../internal/safeGetChildrenArray.js'; import { useObserveHeights } from '../../internal/useObserveHeights.js'; import { Tab, TabContainer } from '../../webComponents/index.js'; import { ObjectPageAnchorBar } from '../ObjectPageAnchorBar/index.js'; import { CollapsedAvatar } from './CollapsedAvatar.js'; import { classNames, styleData } from './ObjectPage.module.css.js'; import { extractSectionIdFromHtmlId, getSectionById } from './ObjectPageUtils.js'; import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; addCustomCSSWithScoping('ui5-tabcontainer', // todo: the additional text span adds 3px to the container - needs to be investigated why ` :host([data-component-name="ObjectPageTabContainer"]) [id$="additionalText"] { display: none; } `); const ObjectPageCssVariables = { headerDisplay: '--_ui5wcr_ObjectPage_header_display', titleFontSize: '--_ui5wcr_ObjectPage_title_fontsize' }; const TAB_CONTAINER_HEADER_HEIGHT = 48; /** * A component that allows apps to easily display information related to a business object. * * The `ObjectPage` is composed of a header (title and content) and block content wrapped in sections and subsections that structure the information. */ const ObjectPage = /*#__PURE__*/forwardRef((props, ref) => { const { titleArea, image, footerArea, mode = ObjectPageMode.Default, imageShapeCircle, className, style, slot, children, selectedSectionId, headerPinned: headerPinnedProp, headerArea, hidePinButton, preserveHeaderStateOnClick, accessibilityAttributes, placeholder, onSelectedSectionChange, onToggleHeaderArea, onPinButtonToggle, onBeforeNavigate, ...rest } = props; useStylesheet(styleData, ObjectPage.displayName); const firstSectionId = safeGetChildrenArray(children)[0]?.props?.id; const [internalSelectedSectionId, setInternalSelectedSectionId] = useState(selectedSectionId ?? firstSectionId); const [selectedSubSectionId, setSelectedSubSectionId] = useState(props.selectedSubSectionId); const [headerPinned, setHeaderPinned] = useState(headerPinnedProp); const isProgrammaticallyScrolled = useRef(false); const prevSelectedSectionId = useRef(undefined); const [componentRef, objectPageRef] = useSyncRef(ref); const topHeaderRef = useRef(null); const scrollEvent = useRef(undefined); const prevTopHeaderHeight = useRef(0); // @ts-expect-error: useSyncRef will create a ref if not present const [componentRefHeaderContent, headerContentRef] = useSyncRef(headerArea?.ref); const anchorBarRef = useRef(null); const objectPageContentRef = useRef(null); const selectionScrollTimeout = useRef(null); const [isAfterScroll, setIsAfterScroll] = useState(false); const isToggledRef = useRef(false); const [headerCollapsedInternal, setHeaderCollapsedInternal] = useState(undefined); const [scrolledHeaderExpanded, setScrolledHeaderExpanded] = useState(false); const scrollTimeout = useRef(0); const [sectionSpacer, setSectionSpacer] = useState(0); const [currentTabModeSection, setCurrentTabModeSection] = useState(null); const sections = mode === ObjectPageMode.IconTabBar ? currentTabModeSection : children; useEffect(() => { const currentSection = mode === ObjectPageMode.IconTabBar ? getSectionById(children, internalSelectedSectionId) : null; setCurrentTabModeSection(currentSection); }, [mode, children, internalSelectedSectionId]); const prevInternalSelectedSectionId = useRef(internalSelectedSectionId); const fireOnSelectedChangedEvent = (targetEvent, index, id, section) => { if (typeof onSelectedSectionChange === 'function' && prevInternalSelectedSectionId.current !== id) { onSelectedSectionChange(enrichEventWithDetails(targetEvent, { selectedSectionIndex: parseInt(index, 10), selectedSectionId: id, section })); prevInternalSelectedSectionId.current = id; } }; const debouncedOnSectionChange = useRef(debounce(fireOnSelectedChangedEvent, 500)).current; useEffect(() => { return () => { debouncedOnSectionChange.cancel(); clearTimeout(selectionScrollTimeout.current); }; }, []); // observe heights of header parts const { topHeaderHeight, headerContentHeight, anchorBarHeight, totalHeaderHeight, headerCollapsed } = useObserveHeights(objectPageRef, topHeaderRef, headerContentRef, anchorBarRef, [headerCollapsedInternal, setHeaderCollapsedInternal], { noHeader: !titleArea && !headerArea, fixedHeader: headerPinned, scrollTimeout }); useEffect(() => { if (typeof onToggleHeaderArea === 'function' && isToggledRef.current) { onToggleHeaderArea(headerCollapsed !== true); } }, [headerCollapsed]); useEffect(() => { const objectPageNode = objectPageRef.current; if (objectPageNode) { Object.assign(objectPageNode, { toggleHeaderArea(snapped) { if (typeof snapped === 'boolean') { onToggleHeaderContentVisibility({ detail: { visible: !snapped } }); } else { onToggleHeaderContentVisibility({ detail: { visible: !!headerCollapsed } }); } } }); } }, [headerCollapsed]); const avatar = useMemo(() => { if (!image) { return null; } if (typeof image === 'string') { return /*#__PURE__*/_jsx("span", { className: classNames.headerImage, style: { borderRadius: imageShapeCircle ? '50%' : 0, overflow: 'hidden' }, children: /*#__PURE__*/_jsx("img", { src: image, className: classNames.image, alt: "Company Logo" }) }); } else { return /*#__PURE__*/cloneElement(image, { size: AvatarSize.L, className: clsx(classNames.headerImage, image.props?.className) }); } }, [image, classNames.headerImage, classNames.image, imageShapeCircle]); const scrollToSectionById = (id, isSubSection = false) => { const section = objectPageRef.current?.querySelector(`#${isSubSection ? 'ObjectPageSubSection' : 'ObjectPageSection'}-${CSS.escape(id)}`); scrollTimeout.current = performance.now() + 500; if (section) { const safeTopHeaderHeight = topHeaderHeight || prevTopHeaderHeight.current; section.style.scrollMarginBlockStart = safeTopHeaderHeight + anchorBarHeight + TAB_CONTAINER_HEADER_HEIGHT + (headerPinned ? headerContentHeight : 0) + 'px'; section.focus(); section.scrollIntoView({ behavior: 'smooth' }); section.style.scrollMarginBlockStart = '0px'; } }; const scrollToSection = sectionId => { if (!sectionId) { return; } if (firstSectionId === sectionId) { objectPageRef.current?.scrollTo({ top: 0, behavior: 'smooth' }); } else { scrollToSectionById(sectionId); } isProgrammaticallyScrolled.current = false; }; const programmaticallySetSection = () => { const currentId = selectedSectionId ?? firstSectionId; if (currentId !== prevSelectedSectionId.current) { debouncedOnSectionChange.cancel(); isProgrammaticallyScrolled.current = true; setInternalSelectedSectionId(currentId); prevSelectedSectionId.current = currentId; const sectionNodes = objectPageRef.current?.querySelectorAll('section[data-component-name="ObjectPageSection"]'); const currentIndex = safeGetChildrenArray(children).findIndex(objectPageSection => { return /*#__PURE__*/isValidElement(objectPageSection) && objectPageSection.props?.id === currentId; }); fireOnSelectedChangedEvent({}, currentIndex, currentId, sectionNodes[0]); } }; // change selected section when prop is changed (external change) const [timeStamp, setTimeStamp] = useState(0); const requestAnimationFrameRef = useRef(undefined); useEffect(() => { if (selectedSectionId) { if (mode === ObjectPageMode.Default) { // wait for DOM draw, otherwise initial scroll won't work as intended if (timeStamp < 750 && timeStamp !== undefined) { requestAnimationFrameRef.current = requestAnimationFrame(internalTimestamp => { setTimeStamp(internalTimestamp); }); } else { setTimeStamp(undefined); programmaticallySetSection(); } } else { programmaticallySetSection(); } } return () => { cancelAnimationFrame(requestAnimationFrameRef.current); }; }, [timeStamp, selectedSectionId, firstSectionId, debouncedOnSectionChange]); // section was selected by clicking on the tab bar buttons const handleOnSectionSelected = (targetEvent, newSelectionSectionId, index, section) => { isProgrammaticallyScrolled.current = true; debouncedOnSectionChange.cancel(); setInternalSelectedSectionId(prevSelectedSection => { if (prevSelectedSection === newSelectionSectionId) { scrollToSection(newSelectionSectionId); } return newSelectionSectionId; }); scrollEvent.current = targetEvent; fireOnSelectedChangedEvent(targetEvent, index, newSelectionSectionId, section); }; // do internal scrolling useEffect(() => { if (mode === ObjectPageMode.Default && isProgrammaticallyScrolled.current === true && !selectedSubSectionId) { scrollToSection(internalSelectedSectionId); } }, [internalSelectedSectionId, mode, isProgrammaticallyScrolled, scrollToSection, selectedSubSectionId]); // Scrolling for Sub Section Selection useEffect(() => { if (selectedSubSectionId && isProgrammaticallyScrolled.current === true && sectionSpacer) { scrollToSectionById(selectedSubSectionId, true); isProgrammaticallyScrolled.current = false; } }, [selectedSubSectionId, isProgrammaticallyScrolled.current, sectionSpacer]); useEffect(() => { if (headerPinnedProp !== undefined) { setHeaderPinned(headerPinnedProp); } if (headerPinnedProp) { onToggleHeaderContentVisibility({ detail: { visible: true } }); } }, [headerPinnedProp]); const prevHeaderPinned = useRef(headerPinned); useEffect(() => { if (prevHeaderPinned.current && !headerPinned && objectPageRef.current.scrollTop > topHeaderHeight) { onToggleHeaderContentVisibility({ detail: { visible: false } }); prevHeaderPinned.current = false; } if (!prevHeaderPinned.current && headerPinned) { prevHeaderPinned.current = true; } }, [headerPinned, topHeaderHeight]); useEffect(() => { setSelectedSubSectionId(props.selectedSubSectionId); if (props.selectedSubSectionId) { isProgrammaticallyScrolled.current = true; if (mode === ObjectPageMode.IconTabBar) { let sectionId; safeGetChildrenArray(children).forEach(section => { if ( /*#__PURE__*/isValidElement(section) && section.props && section.props.children) { safeGetChildrenArray(section.props.children).forEach(subSection => { if ( /*#__PURE__*/isValidElement(subSection) && subSection.props && subSection.props.id === props.selectedSubSectionId) { sectionId = section.props?.id; } }); } }); if (sectionId) { setInternalSelectedSectionId(sectionId); } } } }, [props.selectedSubSectionId, children, mode]); const tabContainerContainerRef = useRef(null); useEffect(() => { const objectPage = objectPageRef.current; const sectionNodes = objectPage.querySelectorAll('[id^="ObjectPageSection"]'); const lastSectionNode = sectionNodes[sectionNodes.length - 1]; const tabContainerContainer = tabContainerContainerRef.current; const observer = new ResizeObserver(([sectionElement]) => { const subSections = lastSectionNode.querySelectorAll('[id^="ObjectPageSubSection"]'); const lastSubSection = subSections[subSections.length - 1]; const lastSubSectionOrSection = lastSubSection ?? sectionElement.target; if (currentTabModeSection && !lastSubSection || sectionNodes.length === 1 && !lastSubSection) { setSectionSpacer(0); } else if (!!tabContainerContainer) { setSectionSpacer(objectPage.getBoundingClientRect().bottom - tabContainerContainer.getBoundingClientRect().bottom - lastSubSectionOrSection.getBoundingClientRect().height - TAB_CONTAINER_HEADER_HEIGHT); } }); if (objectPage && lastSectionNode) { observer.observe(lastSectionNode, { box: 'border-box' }); } return () => { observer.disconnect(); }; }, [headerCollapsed, topHeaderHeight, headerContentHeight, currentTabModeSection, children]); const onToggleHeaderContentVisibility = useCallback(e => { isToggledRef.current = true; scrollTimeout.current = performance.now() + 500; if (!e.detail.visible) { setHeaderCollapsedInternal(true); objectPageRef.current?.classList.add(classNames.headerCollapsed); } else { setHeaderCollapsedInternal(false); setScrolledHeaderExpanded(true); objectPageRef.current?.classList.remove(classNames.headerCollapsed); } }, []); const handleOnSubSectionSelected = useCallback(e => { isProgrammaticallyScrolled.current = true; if (mode === ObjectPageMode.IconTabBar) { const sectionId = e.detail.sectionId; setInternalSelectedSectionId(sectionId); const sectionNodes = objectPageRef.current?.querySelectorAll('section[data-component-name="ObjectPageSection"]'); const currentIndex = safeGetChildrenArray(children).findIndex(objectPageSection => { return /*#__PURE__*/isValidElement(objectPageSection) && objectPageSection.props?.id === sectionId; }); debouncedOnSectionChange(e, currentIndex, sectionId, sectionNodes[currentIndex]); } const subSectionId = e.detail.subSectionId; scrollTimeout.current = performance.now() + 200; setSelectedSubSectionId(subSectionId); }, [mode, setInternalSelectedSectionId, setSelectedSubSectionId, isProgrammaticallyScrolled, children]); const objectPageClasses = clsx(classNames.objectPage, className, mode === ObjectPageMode.IconTabBar && classNames.iconTabBarMode); const { onScroll: _0, selectedSubSectionId: _1, ...propsWithoutOmitted } = rest; useEffect(() => { const sectionNodes = objectPageRef.current?.querySelectorAll('section[data-component-name="ObjectPageSection"]'); const objectPageHeight = objectPageRef.current?.clientHeight ?? 1000; const marginBottom = objectPageHeight - totalHeaderHeight - /*TabContainer*/TAB_CONTAINER_HEADER_HEIGHT; const rootMargin = `-${totalHeaderHeight}px 0px -${marginBottom < 0 ? 0 : marginBottom}px 0px`; const observer = new IntersectionObserver(([section]) => { if (section.isIntersecting && isProgrammaticallyScrolled.current === false) { if (objectPageRef.current.getBoundingClientRect().top + totalHeaderHeight + TAB_CONTAINER_HEADER_HEIGHT <= section.target.getBoundingClientRect().bottom) { const currentId = extractSectionIdFromHtmlId(section.target.id); setInternalSelectedSectionId(currentId); const currentIndex = safeGetChildrenArray(children).findIndex(objectPageSection => { return /*#__PURE__*/isValidElement(objectPageSection) && objectPageSection.props?.id === currentId; }); debouncedOnSectionChange(scrollEvent.current, currentIndex, currentId, section.target); } } }, { root: objectPageRef.current, rootMargin, threshold: [0] }); sectionNodes.forEach(el => { observer.observe(el); }); return () => { observer.disconnect(); }; }, [children, totalHeaderHeight, setInternalSelectedSectionId, isProgrammaticallyScrolled]); // Fallback when scrolling faster than the IntersectionObserver can observe (in most cases faster than 60fps) useEffect(() => { const sectionNodes = objectPageRef.current?.querySelectorAll('section[data-component-name="ObjectPageSection"]'); if (isAfterScroll) { let currentSection = sectionNodes[sectionNodes.length - 1]; let currentIndex; for (let i = 0; i <= sectionNodes.length - 1; i++) { const sectionNode = sectionNodes[i]; if (objectPageRef.current.getBoundingClientRect().top + totalHeaderHeight + TAB_CONTAINER_HEADER_HEIGHT <= sectionNode.getBoundingClientRect().bottom) { currentSection = sectionNode; currentIndex = i; break; } } const currentSectionId = extractSectionIdFromHtmlId(currentSection?.id); if (currentSectionId !== internalSelectedSectionId) { setInternalSelectedSectionId(currentSectionId); debouncedOnSectionChange(scrollEvent.current, currentIndex ?? sectionNodes.length - 1, currentSectionId, currentSection); } setIsAfterScroll(false); } }, [isAfterScroll]); const onTitleClick = e => { e.stopPropagation(); if (!preserveHeaderStateOnClick) { onToggleHeaderContentVisibility(enrichEventWithDetails(e, { visible: headerCollapsed })); } }; const snappedHeaderInObjPage = titleArea && titleArea.props.snappedContent && headerCollapsed === true && !!image; const isInitial = useRef(true); useEffect(() => { if (!isInitial.current) { scrollTimeout.current = performance.now() + 200; } else { isInitial.current = false; } }, [snappedHeaderInObjPage]); const renderHeaderContentSection = () => { if (headerArea?.props) { return /*#__PURE__*/cloneElement(headerArea, { ...headerArea.props, topHeaderHeight, style: headerCollapsed === true ? { position: 'absolute', visibility: 'hidden', flexShrink: 0 } : { ...headerArea.props.style, flexShrink: 0 }, headerPinned: headerPinned || scrolledHeaderExpanded, //@ts-expect-error: todo remove me when forwardref has been replaced ref: componentRefHeaderContent, children: /*#__PURE__*/_jsxs("div", { className: classNames.headerContainer, "data-component-name": "ObjectPageHeaderContainer", children: [avatar, headerArea.props.children && /*#__PURE__*/_jsx("div", { "data-component-name": "ObjectPageHeaderContent", children: headerArea.props.children })] }) }); } }; const onTabItemSelect = event => { if (typeof onBeforeNavigate === 'function') { const selectedTabDataset = event.detail.tab.dataset; const sectionIndex = parseInt(selectedTabDataset.index, 10); const sectionId = selectedTabDataset.parentId ?? selectedTabDataset.sectionId; const subSectionId = selectedTabDataset.hasOwnProperty('isSubTab') ? selectedTabDataset.sectionId : undefined; onBeforeNavigate(enrichEventWithDetails(event, { sectionIndex, sectionId, subSectionId })); if (event.defaultPrevented) { return; } } event.preventDefault(); const { sectionId, index, isSubTab, parentId } = event.detail.tab.dataset; if (isSubTab !== undefined) { handleOnSubSectionSelected(enrichEventWithDetails(event, { sectionId: parentId, subSectionId: sectionId })); } else { const section = safeGetChildrenArray(children).find(el => { return el.props.id == sectionId; }); handleOnSectionSelected(event, section?.props?.id, index, section); } }; const prevScrollTop = useRef(undefined); const onObjectPageScroll = useCallback(e => { if (!isToggledRef.current) { isToggledRef.current = true; } if (scrollTimeout.current >= performance.now()) { return; } scrollEvent.current = e; if (typeof props.onScroll === 'function') { props.onScroll(e); } if (selectedSubSectionId) { setSelectedSubSectionId(undefined); } if (selectionScrollTimeout.current) { clearTimeout(selectionScrollTimeout.current); } selectionScrollTimeout.current = setTimeout(() => { setIsAfterScroll(true); }, 100); if (!headerPinned || e.target.scrollTop === 0) { objectPageRef.current?.classList.remove(classNames.headerCollapsed); } if (scrolledHeaderExpanded && e.target.scrollTop !== prevScrollTop.current) { if (e.target.scrollHeight - e.target.scrollTop === e.target.clientHeight) { return; } prevScrollTop.current = e.target.scrollTop; if (!headerPinned) { setHeaderCollapsedInternal(true); } setScrolledHeaderExpanded(false); } }, [topHeaderHeight, headerPinned, props.onScroll, scrolledHeaderExpanded, selectedSubSectionId]); const onHoverToggleButton = useCallback(e => { if (e?.type === 'mouseover') { topHeaderRef.current?.classList.add(classNames.headerHoverStyles); } else { topHeaderRef.current?.classList.remove(classNames.headerHoverStyles); } }, [classNames.headerHoverStyles]); const objectPageStyles = { ...style }; if (headerCollapsed === true && headerArea) { objectPageStyles[ObjectPageCssVariables.titleFontSize] = ThemingParameters.sapObjectHeader_Title_SnappedFontSize; } return /*#__PURE__*/_jsxs("div", { "data-component-name": "ObjectPage", slot: slot, className: objectPageClasses, style: objectPageStyles, ref: componentRef, onScroll: onObjectPageScroll, ...propsWithoutOmitted, children: [/*#__PURE__*/_jsxs("header", { onMouseOver: onHoverToggleButton, onMouseLeave: onHoverToggleButton, "data-component-name": "ObjectPageTopHeader", ref: topHeaderRef, role: accessibilityAttributes?.objectPageTopHeader?.role, "data-not-clickable": !!preserveHeaderStateOnClick, "aria-roledescription": accessibilityAttributes?.objectPageTopHeader?.ariaRoledescription ?? 'Object Page header', className: classNames.header, style: { gridAutoColumns: `min-content ${titleArea && image && headerCollapsed === true ? `calc(100% - 3rem - 1rem)` : '100%'}` }, children: [/*#__PURE__*/_jsx("span", { className: classNames.clickArea, onClick: onTitleClick, "data-component-name": "ObjectPageTitleAreaClickElement" }), titleArea && image && headerCollapsed === true && /*#__PURE__*/_jsx(CollapsedAvatar, { image: image, imageShapeCircle: imageShapeCircle }), titleArea && /*#__PURE__*/cloneElement(titleArea, { className: clsx(titleArea?.props?.className), onToggleHeaderContentVisibility: onTitleClick, 'data-not-clickable': !!preserveHeaderStateOnClick, 'data-header-content-visible': headerArea && headerCollapsed !== true, 'data-is-snapped-rendered-outside': snappedHeaderInObjPage }), snappedHeaderInObjPage && /*#__PURE__*/_jsx("div", { className: classNames.snappedContent, "data-component-name": "ATwithImageSnappedContentContainer", children: titleArea.props.snappedContent })] }), renderHeaderContentSection(), headerArea && titleArea && /*#__PURE__*/_jsx("div", { "data-component-name": "ObjectPageAnchorBar", ref: anchorBarRef, className: classNames.anchorBar, style: { top: scrolledHeaderExpanded || headerPinned ? `${topHeaderHeight + (headerCollapsed === true ? 0 : headerContentHeight)}px` : `${topHeaderHeight + 5}px` }, children: /*#__PURE__*/_jsx(ObjectPageAnchorBar, { headerContentVisible: headerArea && headerCollapsed !== true, hidePinButton: !!hidePinButton, headerPinned: headerPinned, accessibilityAttributes: accessibilityAttributes, onToggleHeaderContentVisibility: onToggleHeaderContentVisibility, setHeaderPinned: setHeaderPinned, onHoverToggleButton: onHoverToggleButton, onPinButtonToggle: onPinButtonToggle }) }), !placeholder && /*#__PURE__*/_jsx("div", { ref: tabContainerContainerRef, className: classNames.tabContainer, "data-component-name": "ObjectPageTabContainer", style: { top: headerPinned || scrolledHeaderExpanded ? `${topHeaderHeight + (headerCollapsed === true ? 0 : headerContentHeight)}px` : `${topHeaderHeight}px` }, children: /*#__PURE__*/_jsx(TabContainer, { collapsed: true, onTabSelect: onTabItemSelect, "data-component-name": "ObjectPageTabContainer", className: classNames.tabContainerComponent, children: safeGetChildrenArray(children).map((section, index) => { if (! /*#__PURE__*/isValidElement(section) || !section.props) return null; const subTabs = safeGetChildrenArray(section.props.children).filter(subSection => // @ts-expect-error: if the `ObjectPageSubSection` component is passed as children, the `displayName` is available. Otherwise, the default children should be rendered w/o additional logic. /*#__PURE__*/isValidElement(subSection) && subSection?.type?.displayName === 'ObjectPageSubSection'); return /*#__PURE__*/_jsx(Tab, { "data-index": index, "data-section-id": section.props.id, text: section.props.titleText, selected: internalSelectedSectionId === section.props?.id || undefined, items: subTabs.map(item => { if (! /*#__PURE__*/isValidElement(item)) { return null; } return /*#__PURE__*/_jsx(Tab, { "data-parent-id": section.props.id, "data-is-sub-tab": true, "data-section-id": item.props.id, text: item.props.titleText, selected: item.props.id === selectedSubSectionId || undefined, "data-index": index, children: /*#__PURE__*/_jsx("span", { style: { display: 'none' } }) }, item.props.id); }), children: /*#__PURE__*/_jsx("span", { style: { display: 'none' } }) }, `Anchor-${section.props?.id}`); }) }) }), /*#__PURE__*/_jsxs("div", { "data-component-name": "ObjectPageContent", className: classNames.content, ref: objectPageContentRef, children: [/*#__PURE__*/_jsx("div", { style: { height: headerCollapsed ? `${headerContentHeight}px` : 0 }, "aria-hidden": true }), placeholder ? placeholder : sections, /*#__PURE__*/_jsx("div", { style: { height: `${sectionSpacer}px` }, "aria-hidden": true })] }), footerArea && mode === ObjectPageMode.IconTabBar && !sectionSpacer && /*#__PURE__*/_jsx("div", { className: classNames.footerSpacer, "data-component-name": "ObjectPageFooterSpacer", "aria-hidden": true }), footerArea && /*#__PURE__*/_jsx("footer", { className: classNames.footer, "data-component-name": "ObjectPageFooter", children: footerArea })] }); }); ObjectPage.displayName = 'ObjectPage'; export { ObjectPage };