diff --git a/packages/main/src/components/AnalyticalTable/AnalyticalTable.module.css b/packages/main/src/components/AnalyticalTable/AnalyticalTable.module.css
index 82b8a23822b..5f5477a7399 100644
--- a/packages/main/src/components/AnalyticalTable/AnalyticalTable.module.css
+++ b/packages/main/src/components/AnalyticalTable/AnalyticalTable.module.css
@@ -661,15 +661,16 @@
}
/* ==========================================================================
- Firefox scrollbar styles
+ Native scrollbar styles
========================================================================== */
-.firefoxCell {
+.nativeScrollbarCell {
&:last-child {
- padding-inline-end: 18px;
+ /* keep the cell's base inline padding and additionally reserve the scrollbar width */
+ padding-inline-end: calc(0.5rem + var(--_ui5wcr-AnalyticalTable-ScrollbarWidth, 18px));
}
}
-.firefoxNativeScrollbar {
+.nativeScrollbar {
scrollbar-width: inherit;
}
diff --git a/packages/main/src/components/AnalyticalTable/TableBody/VirtualTableBodyContainer.tsx b/packages/main/src/components/AnalyticalTable/TableBody/VirtualTableBodyContainer.tsx
index 11b17cdc822..081e0bd646d 100644
--- a/packages/main/src/components/AnalyticalTable/TableBody/VirtualTableBodyContainer.tsx
+++ b/packages/main/src/components/AnalyticalTable/TableBody/VirtualTableBodyContainer.tsx
@@ -21,7 +21,7 @@ interface VirtualTableBodyContainerProps {
rowCollapsedFlag?: boolean;
dispatch: (e: { type: string; payload?: any }) => void;
isGrouped: boolean;
- isFirefox: boolean;
+ nativeScrollbar: boolean;
}
export const VirtualTableBodyContainer = (props: VirtualTableBodyContainerProps) => {
@@ -41,7 +41,7 @@ export const VirtualTableBodyContainer = (props: VirtualTableBodyContainerProps)
popInRowHeight,
rowCollapsedFlag,
isGrouped,
- isFirefox,
+ nativeScrollbar,
dispatch,
} = props;
const [isMounted, setIsMounted] = useState(false);
@@ -119,7 +119,7 @@ export const VirtualTableBodyContainer = (props: VirtualTableBodyContainerProps)
return (
{
- // safe here because we only update state after mount for SSR hydration
- // eslint-disable-next-line react-hooks/set-state-in-effect
- setIsFirefox(isFirefoxFn());
- }, []);
-
- return isFirefox;
-}
diff --git a/packages/main/src/components/AnalyticalTable/hooks/useNativeScrollbar.ts b/packages/main/src/components/AnalyticalTable/hooks/useNativeScrollbar.ts
new file mode 100644
index 00000000000..90edc2e5959
--- /dev/null
+++ b/packages/main/src/components/AnalyticalTable/hooks/useNativeScrollbar.ts
@@ -0,0 +1,43 @@
+// When reused, move to base pkg
+import { isFirefox as isFirefoxFn } from '@ui5/webcomponents-react-base/Device';
+import { useEffect, useState } from 'react';
+
+/**
+ * Creates an invisible element for measuring scrollbar width.
+ *
+ * __Note:__ Overlay scrollbars don't have a width.
+ */
+function getScrollbarWidth() {
+ const el = document.createElement('div');
+ el.style.cssText = 'overflow:scroll;position:absolute;top:-9999px;width:100px;height:100px';
+ document.body.appendChild(el);
+ const width = el.offsetWidth - el.clientWidth;
+ el.remove();
+ return width;
+}
+
+interface NativeScrollbarInfo {
+ nativeScrollbar: boolean;
+ scrollbarWidth: number;
+}
+
+/**
+ * SSR ready hook that determines whether the table uses the browser's native scrollbar.
+ *
+ * The native scrollbar is used for overlay scrollbars and Firefox, otherwise the custom `VerticalScrollbar` replaces the classic scrollbar with the styled one.
+ *
+ * __Note:__ Measured once on mount, a mode change (OS setting or (dis)connecting a mouse) requires a page refresh.
+ */
+export function useNativeScrollbar(): NativeScrollbarInfo {
+ const [scrollbarWidth, setScrollbarWidth] = useState(0);
+ const [isFirefox, setIsFirefox] = useState(false);
+
+ useEffect(() => {
+ // only update state after mount for SSR hydration
+ // eslint-disable-next-line react-hooks/set-state-in-effect
+ setIsFirefox(isFirefoxFn());
+ setScrollbarWidth(getScrollbarWidth());
+ }, []);
+
+ return { nativeScrollbar: isFirefox || scrollbarWidth === 0, scrollbarWidth };
+}
diff --git a/packages/main/src/components/AnalyticalTable/hooks/usePopIn.ts b/packages/main/src/components/AnalyticalTable/hooks/usePopIn.ts
index d3ccf4e49f6..03b07e9a58e 100644
--- a/packages/main/src/components/AnalyticalTable/hooks/usePopIn.ts
+++ b/packages/main/src/components/AnalyticalTable/hooks/usePopIn.ts
@@ -7,11 +7,10 @@ const popInVisibleColumnsDeps: ReactTableHooks['visibleColumnsDeps'][0] = (deps,
];
const popInVisibleColumns: ReactTableHooks['visibleColumns'][0] = (cols, { instance }) => {
- const { state, dispatch } = instance;
+ const { state, dispatch, webComponentsReactProperties } = instance;
+ const { scrollbarWidth } = webComponentsReactProperties;
- const tableClientWidth = state.isScrollable
- ? state?.tableClientWidth + 13 /*scrollbar width*/
- : state?.tableClientWidth;
+ const tableClientWidth = state.isScrollable ? state?.tableClientWidth + scrollbarWidth : state?.tableClientWidth;
const popInColumns: AnalyticalTableState['popInColumns'] = cols
.filter((item) => {
diff --git a/packages/main/src/components/AnalyticalTable/hooks/useStyling.ts b/packages/main/src/components/AnalyticalTable/hooks/useStyling.ts
index ccfb9f915e6..84ba356fb60 100644
--- a/packages/main/src/components/AnalyticalTable/hooks/useStyling.ts
+++ b/packages/main/src/components/AnalyticalTable/hooks/useStyling.ts
@@ -87,7 +87,7 @@ const getRowProps = (
const getCellProps = (cellProps, { cell: { column }, instance }) => {
const { webComponentsReactProperties, state } = instance;
- const { classes, isFirefox } = webComponentsReactProperties;
+ const { classes, nativeScrollbar } = webComponentsReactProperties;
const style: CSSProperties = { width: `${column.totalWidth}px`, ...resolveCellAlignment(column) };
if (
@@ -105,7 +105,7 @@ const getCellProps = (cellProps, { cell: { column }, instance }) => {
cellProps.className,
classes.tableCell,
column.className,
- isFirefox && state.isScrollable && classes.firefoxCell,
+ nativeScrollbar && state.isScrollable && classes.nativeScrollbarCell,
),
style,
tabIndex: -1,
diff --git a/packages/main/src/components/AnalyticalTable/index.tsx b/packages/main/src/components/AnalyticalTable/index.tsx
index e8d52a2cb00..fb0fd8a9e86 100644
--- a/packages/main/src/components/AnalyticalTable/index.tsx
+++ b/packages/main/src/components/AnalyticalTable/index.tsx
@@ -59,8 +59,8 @@ import { useColumnsDeps } from './hooks/useColumnsDeps.js';
import { useColumnDragAndDrop } from './hooks/useDragAndDrop.js';
import { useDynamicColumnWidths } from './hooks/useDynamicColumnWidths.js';
import { useFontsReady } from './hooks/useFontsReady.js';
-import { useIsFirefox } from './hooks/useIsFirefox.js';
import { useKeyboardNavigation } from './hooks/useKeyboardNavigation.js';
+import { useNativeScrollbar } from './hooks/useNativeScrollbar.js';
import { usePopIn } from './hooks/usePopIn.js';
import { useRowHighlight } from './hooks/useRowHighlight.js';
import { useRowNavigationIndicators } from './hooks/useRowNavigationIndicator.js';
@@ -201,7 +201,7 @@ const AnalyticalTable = forwardRef
0) {
return {
...tableStyles,
@@ -667,7 +670,7 @@ const AnalyticalTable = forwardRef {
if (retainColumnWidth && tableState.columnResizing?.isResizingColumn && tableState.tableColResized == null) {
@@ -678,7 +681,7 @@ const AnalyticalTable = forwardRef {
columnVirtualizer.measure();
@@ -890,7 +893,7 @@ const AnalyticalTable = forwardRef
)}
- {!isFirefox && (additionalEmptyRowsCount || tableState.isScrollable) && (
+ {!nativeScrollbar && (additionalEmptyRowsCount || tableState.isScrollable) && (