diff --git a/Cargo.toml b/Cargo.toml index 61e0dbd..bcbb25d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -63,5 +63,6 @@ features = [ "ResizeObserverEntry", "Selection", "ShadowRoot", + "VisualViewport", "Window", ] diff --git a/packages/dom/src/platform/convert_offset_parent_relative_rect_to_viewport_relative_rect.rs b/packages/dom/src/platform/convert_offset_parent_relative_rect_to_viewport_relative_rect.rs index 279283f..4e874d6 100644 --- a/packages/dom/src/platform/convert_offset_parent_relative_rect_to_viewport_relative_rect.rs +++ b/packages/dom/src/platform/convert_offset_parent_relative_rect_to_viewport_relative_rect.rs @@ -69,7 +69,7 @@ pub fn convert_offset_parent_relative_rect_to_viewport_relative_rect( } let html_offset = if !is_offset_parent_an_element && !is_fixed { - get_html_offset(&document_element, &scroll, Some(true)) + get_html_offset(&document_element, &scroll) } else { Coords::new(0.0) }; diff --git a/packages/dom/src/utils/get_html_offset.rs b/packages/dom/src/utils/get_html_offset.rs index babc348..5c7a107 100644 --- a/packages/dom/src/utils/get_html_offset.rs +++ b/packages/dom/src/utils/get_html_offset.rs @@ -3,21 +3,10 @@ use web_sys::Element; use crate::utils::get_window_scroll_bar_x::get_window_scroll_bar_x; -pub fn get_html_offset( - document_element: &Element, - scroll: &NodeScroll, - ignore_scrollbar_x: Option, -) -> Coords { - let ignore_scrollbar_x = ignore_scrollbar_x.unwrap_or(false); - +pub fn get_html_offset(document_element: &Element, scroll: &NodeScroll) -> Coords { let html_rect = document_element.get_bounding_client_rect(); let x = html_rect.left() + scroll.scroll_left - - if ignore_scrollbar_x { - 0.0 - } else { - // RTL scrollbar. - get_window_scroll_bar_x(document_element, Some(&html_rect)) - }; + - get_window_scroll_bar_x(document_element, Some(&html_rect)); let y = html_rect.top() + scroll.scroll_top; Coords { x, y } diff --git a/packages/dom/src/utils/get_rect_relative_to_offset_parent.rs b/packages/dom/src/utils/get_rect_relative_to_offset_parent.rs index fe3251a..9a9e7bc 100644 --- a/packages/dom/src/utils/get_rect_relative_to_offset_parent.rs +++ b/packages/dom/src/utils/get_rect_relative_to_offset_parent.rs @@ -68,7 +68,7 @@ pub fn get_rect_relative_to_offset_parent( } let html_offset = if !is_offset_parent_an_element && !is_fixed { - get_html_offset(&document_element, &scroll, None) + get_html_offset(&document_element, &scroll) } else { Coords::new(0.0) }; diff --git a/packages/dom/src/utils/get_viewport_rect.rs b/packages/dom/src/utils/get_viewport_rect.rs index 046c52a..fc284de 100644 --- a/packages/dom/src/utils/get_viewport_rect.rs +++ b/packages/dom/src/utils/get_viewport_rect.rs @@ -1,18 +1,71 @@ -use floating_ui_utils::{Rect, Strategy, dom::get_document_element}; +use floating_ui_utils::{ + Rect, Strategy, + dom::{get_computed_style, get_document_element, get_window, is_web_kit}, +}; use web_sys::Element; -pub fn get_viewport_rect(element: &Element, _strategy: Strategy) -> Rect { - // let window = get_window(Some(element)); +use crate::utils::get_window_scroll_bar_x::get_window_scroll_bar_x; + +// Safety check: ensure the scrollbar space is reasonable in case this calculation is affected by unusual styles. +// Most scrollbars leave 15-18px of space. +const SCROLLBAR_MAX: f64 = 25.0; + +pub fn get_viewport_rect(element: &Element, strategy: Strategy) -> Rect { + let window = get_window(Some(element)); let html = get_document_element(Some(element.into())); - // TODO - // let visual_viewport = window.visual_viewport; + let visual_viewport = window.visual_viewport(); - let x = 0.0; - let y = 0.0; - let width = html.client_width() as f64; - let height = html.client_height() as f64; + let mut x = 0.0; + let mut y = 0.0; + let mut width = html.client_width() as f64; + let mut height = html.client_height() as f64; - // TODO: visual viewport + if let Some(visual_viewport) = visual_viewport { + width = visual_viewport.width(); + height = visual_viewport.height(); + + let visual_viewport_based = is_web_kit(); + if !visual_viewport_based || strategy == Strategy::Fixed { + x = visual_viewport.offset_left(); + y = visual_viewport.offset_top(); + } + } + + let window_scrollbar_x = get_window_scroll_bar_x(&html, None); + // `overflow: hidden` + `scrollbar-gutter: stable` reduces the visual width of the , + // but this is not considered in the size of `html.client_width`. + if window_scrollbar_x <= 0.0 { + let doc = html + .owner_document() + .expect("Element should have owner document."); + let body = doc.body().expect("Document should have body."); + let body_styles = get_computed_style(&body); + let body_margin_inline = if doc.compat_mode() == "CSS1Compat" { + body_styles + .get_property_value("margin-left") + .expect("Computed style should have margin left.") + .parse::() + .unwrap_or(0.0) + + body_styles + .get_property_value("margin-right") + .expect("Computed style should have margin right.") + .parse::() + .unwrap_or(0.0) + } else { + 0.0 + }; + let clipping_stable_scrollbar_width = + ((html.client_width() as f64) - (body.client_width() as f64) - body_margin_inline) + .abs(); + + if clipping_stable_scrollbar_width <= SCROLLBAR_MAX { + width -= clipping_stable_scrollbar_width; + } + } else if window_scrollbar_x <= SCROLLBAR_MAX { + // If the scrollbar is on the left, the width needs to be extended + // by the scrollbar amount so there isn't extra space on the right. + width += window_scrollbar_x; + } Rect { x, diff --git a/upstream.toml b/upstream.toml index 20b008f..2857db0 100644 --- a/upstream.toml +++ b/upstream.toml @@ -1,5 +1,5 @@ [releases] core = "1.7.3" -dom = "1.7.3" +dom = "1.7.4" utils = "0.2.10" vue = "1.1.8"