diff --git a/packages/main/src/components/ObjectPage/CollapsedAvatar.module.css b/packages/main/src/components/ObjectPage/CollapsedAvatar.module.css
new file mode 100644
index 00000000000..fc7e2faa09c
--- /dev/null
+++ b/packages/main/src/components/ObjectPage/CollapsedAvatar.module.css
@@ -0,0 +1,27 @@
+.base {
+ align-self: center;
+ opacity: 0;
+ padding-inline-end: 1rem;
+}
+
+.hidden {
+ opacity: 0;
+}
+
+.visible {
+ transition: opacity 0.5s;
+ opacity: 1;
+}
+
+.imageContainer {
+ display: inline-block;
+ vertical-align: middle;
+ max-height: 3rem;
+ width: 3rem;
+ max-width: 3rem;
+}
+
+.image {
+ width: 100%;
+ height: 100%;
+}
diff --git a/packages/main/src/components/ObjectPage/CollapsedAvatar.tsx b/packages/main/src/components/ObjectPage/CollapsedAvatar.tsx
index dc446f60ffc..f2333afcd97 100644
--- a/packages/main/src/components/ObjectPage/CollapsedAvatar.tsx
+++ b/packages/main/src/components/ObjectPage/CollapsedAvatar.tsx
@@ -1,38 +1,9 @@
+import { useStylesheet } from '@ui5/webcomponents-react-base';
import { clsx } from 'clsx';
import type { CSSProperties, ReactElement } from 'react';
import React, { cloneElement, useEffect, useMemo, useRef, useState } from 'react';
-import { createUseStyles } from 'react-jss';
import { AvatarSize } from '../../enums/index.js';
-
-const styles = {
- base: {
- alignSelf: 'center',
- opacity: 0,
- paddingInlineEnd: '1rem'
- },
- hidden: {
- opacity: 0
- },
- visible: {
- transition: 'opacity 0.5s',
- opacity: 1
- },
- imageContainer: {
- display: 'inline-block',
- verticalAlign: 'middle',
- maxHeight: '3rem',
- width: '3rem',
- maxWidth: '3rem'
- },
- image: {
- width: '100%',
- height: '100%'
- }
-};
-
-const useStyles = createUseStyles(styles, {
- name: 'CollapsedAvatar'
-});
+import { classNames, styleData } from './CollapsedAvatar.module.css.js';
export interface CollapsedAvatarPropTypes {
image?: string | ReactElement;
@@ -42,7 +13,7 @@ export interface CollapsedAvatarPropTypes {
export const CollapsedAvatar = (props: CollapsedAvatarPropTypes) => {
const { image, imageShapeCircle, style } = props;
- const classes = useStyles();
+ useStylesheet(styleData, CollapsedAvatar.displayName);
const [isMounted, setIsMounted] = useState(false);
const domRef = useRef(null);
@@ -52,18 +23,18 @@ export const CollapsedAvatar = (props: CollapsedAvatarPropTypes) => {
if (typeof image === 'string') {
return (
-
+
);
} else {
return cloneElement(image, {
size: AvatarSize.S,
className: image.props?.className
- ? `${classes.imageContainer} ${image.props?.className}`
- : classes.imageContainer
+ ? `${classNames.imageContainer} ${image.props?.className}`
+ : classNames.imageContainer
} as unknown);
}
}, [image, imageShapeCircle]);
@@ -72,7 +43,7 @@ export const CollapsedAvatar = (props: CollapsedAvatarPropTypes) => {
setIsMounted(true);
}, []);
- const containerClasses = clsx(classes.base, isMounted ? classes.visible : classes.hidden);
+ const containerClasses = clsx(classNames.base, isMounted ? classNames.visible : classNames.hidden);
return (
@@ -80,3 +51,5 @@ export const CollapsedAvatar = (props: CollapsedAvatarPropTypes) => {
);
};
+
+CollapsedAvatar.displayName = 'CollapsedAvatar';
diff --git a/packages/main/src/components/ObjectPage/ObjectPage.jss.ts b/packages/main/src/components/ObjectPage/ObjectPage.jss.ts
deleted file mode 100644
index 1f724624c21..00000000000
--- a/packages/main/src/components/ObjectPage/ObjectPage.jss.ts
+++ /dev/null
@@ -1,153 +0,0 @@
-import { ThemingParameters } from '@ui5/webcomponents-react-base';
-import { ResponsiveContainerPadding } from '../../internal/ContainerQueries.js';
-import { DynamicPageCssVariables } from '../DynamicPage/utils.js';
-
-export const ObjectPageCssVariables = {
- anchorFloat: '--_ui5wcr_ObjectPage_actions_float',
- anchorLeft: '--_ui5wcr_ObjectPage_actions_left',
- anchorRight: '--_ui5wcr_ObjectPage_actions_right'
-};
-
-export const styles = {
- objectPage: {
- [DynamicPageCssVariables.headerDisplay]: 'block',
- [DynamicPageCssVariables.titleFontSize]: ThemingParameters.sapObjectHeader_Title_FontSize,
- container: 'objectPage / inline-size',
- boxSizing: 'border-box',
- display: 'flex',
- flexDirection: 'column',
- width: '100%',
- height: '100%',
- maxHeight: '100vh',
- position: 'relative',
- whiteSpace: 'normal',
- fontFamily: ThemingParameters.sapFontFamily,
- backgroundColor: ThemingParameters.sapBackgroundColor,
- overflowX: 'hidden',
- overflowY: 'auto',
- scrollBehavior: 'smooth',
- '& section[id*="ObjectPageSection-"] > div[role="heading"]': {
- display: 'none'
- },
- // explanation why first-child selector is not sufficient here:
- // https://stackoverflow.com/questions/7128406/css-select-the-first-child-from-elements-with-particular-attribute
- '& section[id*="ObjectPageSection-"] ~ section[id*="ObjectPageSection-"] > div[role="heading"]': {
- display: 'block'
- }
- },
- '@global html': {
- [ObjectPageCssVariables.anchorFloat]: 'right',
- [ObjectPageCssVariables.anchorRight]: '1.25rem',
- [ObjectPageCssVariables.anchorLeft]: 'unset'
- },
- '@global [dir="rtl"]': {
- [ObjectPageCssVariables.anchorFloat]: 'left',
- [ObjectPageCssVariables.anchorRight]: 'unset',
- [ObjectPageCssVariables.anchorLeft]: '1.25rem'
- },
- iconTabBarMode: {
- '& section[data-component-name="ObjectPageSection"] > div[role="heading"]': {
- display: 'none'
- }
- },
- headerCollapsed: {
- [DynamicPageCssVariables.headerDisplay]: 'none',
- [DynamicPageCssVariables.titleFontSize]: ThemingParameters.sapObjectHeader_Title_SnappedFontSize
- },
- headerContainer: {
- extend: ResponsiveContainerPadding,
- marginBottom: '0.25rem',
- backgroundColor: ThemingParameters.sapObjectHeader_Background,
- display: 'grid',
- gridAutoColumns: 'min-content calc(100% - 5rem - 2rem)' /*avatar size - padding */,
- '& [data-component-name="ObjectPageHeaderContent"]': {
- gridColumn: 2
- }
- },
- headerHoverStyles: {
- '&[data-not-clickable="true"]': {
- cursor: 'unset'
- },
- '&[data-not-clickable="false"]': {
- backgroundColor: `${ThemingParameters.sapObjectHeader_Hover_Background}`,
- '& [data-component-name="DynamicPageTitle"]': {
- backgroundColor: ThemingParameters.sapObjectHeader_Hover_Background
- }
- }
- },
- header: {
- extend: ResponsiveContainerPadding,
- [DynamicPageCssVariables.headerDisplay]: 'block',
- boxSizing: 'border-box',
- backgroundColor: ThemingParameters.sapObjectHeader_Background,
- position: 'sticky',
- top: 0,
- zIndex: 2,
- '& [data-component-name="DynamicPageTitle"]': {
- gridColumn: 2,
- paddingInline: 0
- },
- cursor: 'pointer'
- },
- headerImage: {
- minWidth: '5rem',
- maxWidth: '5rem',
- maxHeight: '5rem',
- display: 'inline-block',
- marginInlineEnd: '2rem'
- },
- image: {
- width: '100%',
- height: '100%'
- },
- anchorBar: {
- position: 'sticky',
- zIndex: 2
- },
- tabContainer: {
- position: 'sticky',
- zIndex: 1,
- // each tab has inline padding of 1rem, so it needs to be subtracted from the default responsive padding
- '@container objectPage (max-width: 599px)': {
- '--_ui5wcr_ObjectPage_tab_bar_inline_padding': 0
- },
- '@container objectPage (min-width: 600px) and (max-width: 1023px)': {
- '--_ui5wcr_ObjectPage_tab_bar_inline_padding': '1rem'
- },
- '@container objectPage (min-width: 1024px) and (max-width: 1439px)': {
- '--_ui5wcr_ObjectPage_tab_bar_inline_padding': '1rem'
- },
- '@container objectPage (min-width: 1440px)': {
- '--_ui5wcr_ObjectPage_tab_bar_inline_padding': '2rem'
- }
- },
- tabContainerComponent: {
- '&::part(content)': {
- display: 'none'
- },
- '&::part(tabstrip)': {
- // padding-inline is used here to ensure the same responsive padding behavior as for the rest of the component
- padding: 0,
- paddingInline: 'var(--_ui5wcr_ObjectPage_tab_bar_inline_padding)',
- boxShadow: `inset 0 -0.0625rem ${ThemingParameters.sapPageHeader_BorderColor}, 0 0.125rem 0.25rem 0 rgb(0 0 0 / 8%)`
- }
- },
- content: {
- extend: ResponsiveContainerPadding,
- flexGrow: 1,
- position: 'relative'
- },
- footer: {
- position: 'sticky',
- bottom: '0.5rem',
- margin: '0 0.5rem'
- },
- footerSpacer: { height: '1rem', flexShrink: 0 },
- subSectionPopover: {
- '&::part(content)': {
- padding: 0
- }
- },
- titleInHeader: { padding: 0 },
- snappedContent: { gridColumn: '1 / span 2' }
-};
diff --git a/packages/main/src/components/ObjectPage/ObjectPage.module.css b/packages/main/src/components/ObjectPage/ObjectPage.module.css
new file mode 100644
index 00000000000..4c21ecc9700
--- /dev/null
+++ b/packages/main/src/components/ObjectPage/ObjectPage.module.css
@@ -0,0 +1,180 @@
+.objectPage {
+ container: objectPage / inline-size;
+ --_ui5wcr_DynamicPage_header_display: block;
+ --_ui5wcr_DynamicPage_title_fontsize: var(--sapObjectHeader_Title_FontSize);
+
+ box-sizing: border-box;
+ display: flex;
+ flex-direction: column;
+ width: 100%;
+ height: 100%;
+ max-height: 100vh;
+ position: relative;
+ white-space: normal;
+ font-family: var(--sapFontFamily);
+ background-color: var(--sapBackgroundColor);
+ overflow-x: hidden;
+ overflow-y: auto;
+ scroll-behavior: smooth;
+ section[id*='ObjectPageSection-'] > div[role='heading'] {
+ display: none;
+ }
+
+ /* explanation why first-child selector is not sufficient here:
+ https://stackoverflow.com/questions/7128406/css-select-the-first-child-from-elements-with-particular-attribute
+ */
+ section[id*='ObjectPageSection-'] ~ section[id*='ObjectPageSection-'] > div[role='heading'] {
+ display: block;
+ }
+}
+
+.iconTabBarMode section[data-component-name='ObjectPageSection'] > div[role='heading'] {
+ display: none;
+}
+
+/* Header */
+.header {
+ --_ui5wcr_DynamicPage_header_display: block;
+ box-sizing: border-box;
+ background-color: var(--sapObjectHeader_Background);
+ position: sticky;
+ inset-block-start: 0;
+ z-index: 2;
+ cursor: pointer;
+
+ [data-component-name='DynamicPageTitle'] {
+ grid-column: 2;
+ padding-inline: 0;
+ }
+}
+
+.headerCollapsed {
+ --_ui5wcr_DynamicPage_header_display: none;
+ --_ui5wcr_DynamicPage_title_fontsize: var(--sapObjectHeader_Title_SnappedFontSize);
+}
+
+.headerContainer {
+ margin-block-end: 0.25rem;
+ background-color: var(--sapObjectHeader_Background);
+ display: grid;
+ grid-auto-columns: min-content calc(100% - 5rem - 2rem);
+
+ [data-component-name='ObjectPageHeaderContent'] {
+ grid-column: 2;
+ }
+}
+
+.headerHoverStyles {
+ &[data-not-clickable='true'] {
+ cursor: unset;
+ }
+
+ &[data-not-clickable='false'] {
+ background-color: var(--sapObjectHeader_Hover_Background);
+
+ [data-component-name='DynamicPageTitle'] {
+ background-color: var(--sapObjectHeader_Hover_Background);
+ }
+ }
+}
+
+.headerImage {
+ min-width: 5rem;
+ max-width: 5rem;
+ max-height: 5rem;
+ display: inline-block;
+ margin-inline-end: 2rem;
+}
+
+.image {
+ width: 100%;
+ height: 100%;
+}
+
+.anchorBar {
+ position: sticky;
+ z-index: 2;
+}
+
+.tabContainer {
+ position: sticky;
+ z-index: 1;
+}
+
+.tabContainerComponent {
+ &::part(content) {
+ display: none;
+ }
+
+ &::part(tabstrip) {
+ padding: 0;
+ padding-inline: var(--_ui5wcr_ObjectPage_tab_bar_inline_padding);
+ box-shadow:
+ inset 0 -0.0625rem var(--sapPageHeader_BorderColor),
+ 0 0.125rem 0.25rem 0 rgb(0 0 0 / 8%);
+ }
+}
+
+.content {
+ flex-grow: 1;
+ position: relative;
+}
+
+@container (max-width: 599px) {
+ .header,
+ .headerContainer,
+ .content {
+ padding-inline: 1rem;
+ }
+
+ .tabContainer {
+ --_ui5wcr_ObjectPage_tab_bar_inline_padding: 0;
+ }
+}
+
+@container (min-width: 600px) and (max-width: 1439px) {
+ .header,
+ .headerContainer,
+ .content {
+ padding-inline: 2rem;
+ }
+
+ .tabContainer {
+ --_ui5wcr_ObjectPage_tab_bar_inline_padding: 1rem;
+ }
+}
+
+@container (min-width: 1440px) {
+ .header,
+ .headerContainer,
+ .content {
+ padding-inline: 3rem;
+ }
+
+ .tabContainer {
+ --_ui5wcr_ObjectPage_tab_bar_inline_padding: 2rem;
+ }
+}
+
+.footer {
+ position: sticky;
+ inset-block-end: 0.5rem;
+ margin: 0 0.5rem;
+}
+
+.footerSpacer {
+ height: 1rem;
+ flex-shrink: 0;
+}
+
+.subSectionPopover::part(content) {
+ padding: 0;
+}
+
+.titleInHeader {
+ padding: 0;
+}
+
+.snappedContent {
+ grid-column: 1 / span 2;
+}
diff --git a/packages/main/src/components/ObjectPage/index.tsx b/packages/main/src/components/ObjectPage/index.tsx
index 1300e0a7eae..7948a428a27 100644
--- a/packages/main/src/components/ObjectPage/index.tsx
+++ b/packages/main/src/components/ObjectPage/index.tsx
@@ -6,6 +6,7 @@ import {
deprecationNotice,
enrichEventWithDetails,
ThemingParameters,
+ useStylesheet,
useSyncRef
} from '@ui5/webcomponents-react-base';
import { clsx } from 'clsx';
@@ -20,7 +21,6 @@ import React, {
useRef,
useState
} from 'react';
-import { createUseStyles } from 'react-jss';
import { AvatarSize, GlobalStyleClasses, ObjectPageMode } from '../../enums/index.js';
import { addCustomCSSWithScoping } from '../../internal/addCustomCSSWithScoping.js';
import { safeGetChildrenArray } from '../../internal/safeGetChildrenArray.js';
@@ -33,7 +33,7 @@ import { DynamicPageAnchorBar } from '../DynamicPageAnchorBar/index.js';
import { DynamicPageHeader } from '../DynamicPageHeader/index.js';
import type { ObjectPageSectionPropTypes } from '../ObjectPageSection/index.js';
import { CollapsedAvatar } from './CollapsedAvatar.js';
-import { styles } from './ObjectPage.jss.js';
+import { classNames, styleData } from './ObjectPage.module.css.js';
import { extractSectionIdFromHtmlId, getSectionById } from './ObjectPageUtils.js';
addCustomCSSWithScoping(
@@ -173,8 +173,6 @@ export interface ObjectPagePropTypes extends Omit {
onPinnedStateChange?: (pinned: boolean) => void;
}
-const useStyles = createUseStyles(styles, { name: 'ObjectPage' });
-
/**
* A component that allows apps to easily display information related to a business object.
*
@@ -206,7 +204,7 @@ const ObjectPage = forwardRef((props, ref)
...rest
} = props;
- const classes = useStyles();
+ useStylesheet(styleData, ObjectPage.displayName);
const firstSectionId: string | undefined = safeGetChildrenArray(children)[0]?.props?.id;
@@ -304,19 +302,19 @@ const ObjectPage = forwardRef((props, ref)
if (typeof image === 'string') {
return (
-
+
);
} else {
return cloneElement(image, {
size: AvatarSize.L,
- className: clsx(classes.headerImage, image.props?.className)
+ className: clsx(classNames.headerImage, image.props?.className)
} as AvatarPropTypes);
}
- }, [image, classes.headerImage, classes.image, imageShapeCircle]);
+ }, [image, classNames.headerImage, classNames.image, imageShapeCircle]);
const scrollToSectionById = (id?: string, isSubSection = false) => {
const section = objectPageRef.current?.querySelector(
@@ -500,11 +498,11 @@ const ObjectPage = forwardRef((props, ref)
scrollTimeout.current = performance.now() + 500;
if (!e.detail.visible) {
setHeaderCollapsedInternal(true);
- objectPageRef.current?.classList.add(classes.headerCollapsed);
+ objectPageRef.current?.classList.add(classNames.headerCollapsed);
} else {
setHeaderCollapsedInternal(false);
setScrolledHeaderExpanded(true);
- objectPageRef.current?.classList.remove(classes.headerCollapsed);
+ objectPageRef.current?.classList.remove(classNames.headerCollapsed);
}
}, []);
@@ -530,10 +528,10 @@ const ObjectPage = forwardRef((props, ref)
);
const objectPageClasses = clsx(
- classes.objectPage,
+ classNames.objectPage,
GlobalStyleClasses.sapScrollBar,
className,
- mode === ObjectPageMode.IconTabBar && classes.iconTabBarMode
+ mode === ObjectPageMode.IconTabBar && classNames.iconTabBarMode
);
const { onScroll: _0, selectedSubSectionId: _1, ...propsWithoutOmitted } = rest;
@@ -626,7 +624,7 @@ const ObjectPage = forwardRef((props, ref)
const hasHeaderContent = !!headerContent;
const renderTitleSection = useCallback(
(inHeader = false) => {
- const titleInHeaderClass = inHeader ? classes.titleInHeader : undefined;
+ const titleInHeaderClass = inHeader ? classNames.titleInHeader : undefined;
if (headerTitle?.props && headerTitle.props?.showSubHeaderRight === undefined) {
return cloneElement(headerTitle, {
@@ -670,7 +668,7 @@ const ObjectPage = forwardRef((props, ref)
headerPinned: headerPinned || scrolledHeaderExpanded,
ref: componentRefHeaderContent,
children: (
-
+
{avatar}
{(headerContent.props.children || titleInHeader) && (
@@ -689,7 +687,7 @@ const ObjectPage = forwardRef
((props, ref)
headerPinned={headerPinned || scrolledHeaderExpanded}
ref={componentRefHeaderContent}
>
-
+
{avatar}
{titleInHeader && renderTitleSection(true)}
@@ -759,7 +757,7 @@ const ObjectPage = forwardRef
((props, ref)
setIsAfterScroll(true);
}, 100);
if (!headerPinned || e.target.scrollTop === 0) {
- objectPageRef.current?.classList.remove(classes.headerCollapsed);
+ objectPageRef.current?.classList.remove(classNames.headerCollapsed);
}
if (scrolledHeaderExpanded && e.target.scrollTop !== prevScrollTop.current) {
if (e.target.scrollHeight - e.target.scrollTop === e.target.clientHeight) {
@@ -778,12 +776,12 @@ const ObjectPage = forwardRef((props, ref)
const onHoverToggleButton = useCallback(
(e) => {
if (e?.type === 'mouseover') {
- topHeaderRef.current?.classList.add(classes.headerHoverStyles);
+ topHeaderRef.current?.classList.add(classNames.headerHoverStyles);
} else {
- topHeaderRef.current?.classList.remove(classes.headerHoverStyles);
+ topHeaderRef.current?.classList.remove(classNames.headerHoverStyles);
}
},
- [classes.headerHoverStyles]
+ [classNames.headerHoverStyles]
);
const objectPageStyles: CSSProperties = {
@@ -811,7 +809,7 @@ const ObjectPage = forwardRef((props, ref)
role={a11yConfig?.objectPageTopHeader?.role}
data-not-clickable={titleHeaderNotClickable}
aria-roledescription={a11yConfig?.objectPageTopHeader?.ariaRoledescription ?? 'Object Page header'}
- className={classes.header}
+ className={classNames.header}
onClick={onTitleClick}
style={{
gridAutoColumns: `min-content ${
@@ -825,7 +823,7 @@ const ObjectPage = forwardRef((props, ref)
)}
{headerTitle && renderTitleSection()}
{snappedHeaderInObjPage && (
-
+
{headerTitle.props.snappedContent}
)}
@@ -835,7 +833,7 @@ const ObjectPage = forwardRef
((props, ref)
((props, ref)
{!placeholder && (
((props, ref)
fixed
onTabSelect={onTabItemSelect}
data-component-name="ObjectPageTabContainer"
- className={classes.tabContainerComponent}
+ className={classNames.tabContainerComponent}
>
{safeGetChildrenArray(children).map((section, index) => {
if (!isValidElement(section) || !section.props) return null;
@@ -917,16 +915,16 @@ const ObjectPage = forwardRef((props, ref)
)}
-
+
{placeholder ? placeholder : sections}
{footer && mode === ObjectPageMode.IconTabBar && !sectionSpacer && (
-
+
)}
{footer && (
-