Skip to content

Commit 8b34295

Browse files
authored
fix(getViewportRect): account for scrollbar-gutter: stable space (#3387)
1 parent 38f8b5a commit 8b34295

File tree

4 files changed

+39
-6
lines changed

4 files changed

+39
-6
lines changed

.changeset/sour-numbers-press.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@floating-ui/dom': patch
3+
---
4+
5+
fix(getViewportRect): account for space left by `scrollbar-gutter: stable`

packages/dom/src/platform/convertOffsetParentRelativeRectToViewportRelativeRect.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ export function convertOffsetParentRelativeRectToViewportRelativeRect({
5555

5656
const htmlOffset =
5757
documentElement && !isOffsetParentAnElement && !isFixed
58-
? getHTMLOffset(documentElement, scroll, true)
58+
? getHTMLOffset(documentElement, scroll)
5959
: createCoords(0);
6060

6161
return {

packages/dom/src/utils/getHTMLOffset.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,12 @@ import {getWindowScrollBarX} from './getWindowScrollBarX';
44
export function getHTMLOffset(
55
documentElement: HTMLElement,
66
scroll: NodeScroll,
7-
ignoreScrollbarX = false,
87
) {
98
const htmlRect = documentElement.getBoundingClientRect();
109
const x =
1110
htmlRect.left +
1211
scroll.scrollLeft -
13-
(ignoreScrollbarX
14-
? 0
15-
: // RTL <body> scrollbar.
16-
getWindowScrollBarX(documentElement, htmlRect));
12+
getWindowScrollBarX(documentElement, htmlRect);
1713
const y = htmlRect.top + scroll.scrollTop;
1814

1915
return {

packages/dom/src/utils/getViewportRect.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@ import type {Rect, Strategy} from '@floating-ui/core';
22
import {getWindow, isWebKit} from '@floating-ui/utils/dom';
33

44
import {getDocumentElement} from '../platform/getDocumentElement';
5+
import {getWindowScrollBarX} from './getWindowScrollBarX';
6+
7+
// Safety check: ensure the scrollbar space is reasonable in case this
8+
// calculation is affected by unusual styles.
9+
// Most scrollbars leave 15-18px of space.
10+
const SCROLLBAR_MAX = 25;
511

612
export function getViewportRect(element: Element, strategy: Strategy): Rect {
713
const win = getWindow(element);
@@ -25,6 +31,32 @@ export function getViewportRect(element: Element, strategy: Strategy): Rect {
2531
}
2632
}
2733

34+
const windowScrollbarX = getWindowScrollBarX(html);
35+
// <html> `overflow: hidden` + `scrollbar-gutter: stable` reduces the
36+
// visual width of the <html> but this is not considered in the size
37+
// of `html.clientWidth`.
38+
if (windowScrollbarX <= 0) {
39+
const doc = html.ownerDocument;
40+
const body = doc.body;
41+
const bodyStyles = getComputedStyle(body);
42+
const bodyMarginInline =
43+
doc.compatMode === 'CSS1Compat'
44+
? parseFloat(bodyStyles.marginLeft) +
45+
parseFloat(bodyStyles.marginRight) || 0
46+
: 0;
47+
const clippingStableScrollbarWidth = Math.abs(
48+
html.clientWidth - body.clientWidth - bodyMarginInline,
49+
);
50+
51+
if (clippingStableScrollbarWidth <= SCROLLBAR_MAX) {
52+
width -= clippingStableScrollbarWidth;
53+
}
54+
} else if (windowScrollbarX <= SCROLLBAR_MAX) {
55+
// If the <body> scrollbar is on the left, the width needs to be extended
56+
// by the scrollbar amount so there isn't extra space on the right.
57+
width += windowScrollbarX;
58+
}
59+
2860
return {
2961
width,
3062
height,

0 commit comments

Comments
 (0)