Skip to content

Commit 11a952c

Browse files
fix(dom): account for space left by scrollbar-gutter: stable in get_viewport_rect
1 parent 69565f5 commit 11a952c

5 files changed

Lines changed: 68 additions & 25 deletions

File tree

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,5 +63,6 @@ features = [
6363
"ResizeObserverEntry",
6464
"Selection",
6565
"ShadowRoot",
66+
"VisualViewport",
6667
"Window",
6768
]

packages/dom/src/platform/convert_offset_parent_relative_rect_to_viewport_relative_rect.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ pub fn convert_offset_parent_relative_rect_to_viewport_relative_rect(
6969
}
7070

7171
let html_offset = if !is_offset_parent_an_element && !is_fixed {
72-
get_html_offset(&document_element, &scroll, Some(true))
72+
get_html_offset(&document_element, &scroll)
7373
} else {
7474
Coords::new(0.0)
7575
};

packages/dom/src/utils/get_html_offset.rs

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,10 @@ use web_sys::Element;
33

44
use crate::utils::get_window_scroll_bar_x::get_window_scroll_bar_x;
55

6-
pub fn get_html_offset(
7-
document_element: &Element,
8-
scroll: &NodeScroll,
9-
ignore_scrollbar_x: Option<bool>,
10-
) -> Coords {
11-
let ignore_scrollbar_x = ignore_scrollbar_x.unwrap_or(false);
12-
6+
pub fn get_html_offset(document_element: &Element, scroll: &NodeScroll) -> Coords {
137
let html_rect = document_element.get_bounding_client_rect();
148
let x = html_rect.left() + scroll.scroll_left
15-
- if ignore_scrollbar_x {
16-
0.0
17-
} else {
18-
// RTL <body> scrollbar.
19-
get_window_scroll_bar_x(document_element, Some(&html_rect))
20-
};
9+
- get_window_scroll_bar_x(document_element, Some(&html_rect));
2110
let y = html_rect.top() + scroll.scroll_top;
2211

2312
Coords { x, y }

packages/dom/src/utils/get_rect_relative_to_offset_parent.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ pub fn get_rect_relative_to_offset_parent(
6868
}
6969

7070
let html_offset = if !is_offset_parent_an_element && !is_fixed {
71-
get_html_offset(&document_element, &scroll, None)
71+
get_html_offset(&document_element, &scroll)
7272
} else {
7373
Coords::new(0.0)
7474
};

packages/dom/src/utils/get_viewport_rect.rs

Lines changed: 63 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,71 @@
1-
use floating_ui_utils::{Rect, Strategy, dom::get_document_element};
1+
use floating_ui_utils::{
2+
Rect, Strategy,
3+
dom::{get_computed_style, get_document_element, get_window, is_web_kit},
4+
};
25
use web_sys::Element;
36

4-
pub fn get_viewport_rect(element: &Element, _strategy: Strategy) -> Rect {
5-
// let window = get_window(Some(element));
7+
use crate::utils::get_window_scroll_bar_x::get_window_scroll_bar_x;
8+
9+
// Safety check: ensure the scrollbar space is reasonable in case this calculation is affected by unusual styles.
10+
// Most scrollbars leave 15-18px of space.
11+
const SCROLLBAR_MAX: f64 = 25.0;
12+
13+
pub fn get_viewport_rect(element: &Element, strategy: Strategy) -> Rect {
14+
let window = get_window(Some(element));
615
let html = get_document_element(Some(element.into()));
7-
// TODO
8-
// let visual_viewport = window.visual_viewport;
16+
let visual_viewport = window.visual_viewport();
917

10-
let x = 0.0;
11-
let y = 0.0;
12-
let width = html.client_width() as f64;
13-
let height = html.client_height() as f64;
18+
let mut x = 0.0;
19+
let mut y = 0.0;
20+
let mut width = html.client_width() as f64;
21+
let mut height = html.client_height() as f64;
1422

15-
// TODO: visual viewport
23+
if let Some(visual_viewport) = visual_viewport {
24+
width = visual_viewport.width();
25+
height = visual_viewport.height();
26+
27+
let visual_viewport_based = is_web_kit();
28+
if !visual_viewport_based || strategy == Strategy::Fixed {
29+
x = visual_viewport.offset_left();
30+
y = visual_viewport.offset_top();
31+
}
32+
}
33+
34+
let window_scrollbar_x = get_window_scroll_bar_x(&html, None);
35+
// <html> `overflow: hidden` + `scrollbar-gutter: stable` reduces the visual width of the <html>,
36+
// but this is not considered in the size of `html.client_width`.
37+
if window_scrollbar_x <= 0.0 {
38+
let doc = html
39+
.owner_document()
40+
.expect("Element should have owner document.");
41+
let body = doc.body().expect("Document should have body.");
42+
let body_styles = get_computed_style(&body);
43+
let body_margin_inline = if doc.compat_mode() == "CSS1Compat" {
44+
body_styles
45+
.get_property_value("margin-left")
46+
.expect("Computed style should have margin left.")
47+
.parse::<f64>()
48+
.unwrap_or(0.0)
49+
+ body_styles
50+
.get_property_value("margin-right")
51+
.expect("Computed style should have margin right.")
52+
.parse::<f64>()
53+
.unwrap_or(0.0)
54+
} else {
55+
0.0
56+
};
57+
let clipping_stable_scrollbar_width =
58+
((html.client_width() as f64) - (body.client_width() as f64) - body_margin_inline)
59+
.abs();
60+
61+
if clipping_stable_scrollbar_width <= SCROLLBAR_MAX {
62+
width -= clipping_stable_scrollbar_width;
63+
}
64+
} else if window_scrollbar_x <= SCROLLBAR_MAX {
65+
// If the <body> scrollbar is on the left, the width needs to be extended
66+
// by the scrollbar amount so there isn't extra space on the right.
67+
width += window_scrollbar_x;
68+
}
1669

1770
Rect {
1871
x,

0 commit comments

Comments
 (0)