Skip to content

Commit 36ea35a

Browse files
Update to upstream @floating-ui/dom@1.6.13 (#89)
* Update to upstream @floating-ui/dom@1.6.13 * Fix auto update Work around `IntersectionObserver` issue that sometimes fails to detect movement of reference element. --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Daniëlle Huisman <danielle@huisman.me>
1 parent bfc480f commit 36ea35a

4 files changed

Lines changed: 69 additions & 34 deletions

File tree

packages/dom/src/auto_update.rs

Lines changed: 62 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use web_sys::{
1212

1313
use crate::{
1414
types::{ElementOrVirtual, OwnedElementOrVirtual},
15-
utils::get_bounding_client_rect::get_bounding_client_rect,
15+
utils::{get_bounding_client_rect::get_bounding_client_rect, rects_are_equal::rects_are_equal},
1616
};
1717

1818
fn request_animation_frame(callback: &Closure<dyn FnMut()>) -> i32 {
@@ -63,20 +63,26 @@ fn observe_move(element: Element, on_move: Rc<dyn Fn()>) -> Box<dyn Fn()> {
6363
*refresh_closure_clone.borrow_mut() = Some(Box::new(move |skip: bool, threshold: f64| {
6464
refresh_cleanup();
6565

66-
let rect = element.get_bounding_client_rect();
66+
let element_rect_for_root_margin = element.get_bounding_client_rect();
6767

6868
if !skip {
6969
on_move();
7070
}
7171

72-
if rect.width() == 0.0 || rect.height() == 0.0 {
72+
if element_rect_for_root_margin.width() == 0.0
73+
|| element_rect_for_root_margin.height() == 0.0
74+
{
7375
return;
7476
}
7577

76-
let inset_top = rect.top().floor();
77-
let inset_right = (root.client_width() as f64 - (rect.left() + rect.width())).floor();
78-
let inset_bottom = (root.client_height() as f64 - (rect.top() + rect.height())).floor();
79-
let inset_left = rect.left().floor();
78+
let inset_top = element_rect_for_root_margin.top().floor();
79+
let inset_right = (root.client_width() as f64
80+
- (element_rect_for_root_margin.left() + element_rect_for_root_margin.width()))
81+
.floor();
82+
let inset_bottom = (root.client_height() as f64
83+
- (element_rect_for_root_margin.top() + element_rect_for_root_margin.height()))
84+
.floor();
85+
let inset_left = element_rect_for_root_margin.left().floor();
8086
let root_margin = format!(
8187
"{}px {}px {}px {}px",
8288
-inset_top, -inset_right, -inset_bottom, -inset_left
@@ -95,33 +101,60 @@ fn observe_move(element: Element, on_move: Rc<dyn Fn()>) -> Box<dyn Fn()> {
95101
let observe_timeout_id = timeout_id.clone();
96102
let observe_window = window.clone();
97103
let observe_refresh = refresh_closure.clone();
98-
let local_observe_closure = Closure::new(move |entries: Vec<IntersectionObserverEntry>| {
99-
let ratio = entries[0].intersection_ratio();
104+
let local_observe_closure = Closure::new({
105+
let element = element.clone();
106+
107+
move |entries: Vec<IntersectionObserverEntry>| {
108+
let ratio = entries[0].intersection_ratio();
109+
110+
if ratio != threshold {
111+
if !*is_first_update.borrow() {
112+
observe_refresh
113+
.borrow()
114+
.as_ref()
115+
.expect("Refresh closure should exist.")(
116+
false, 1.0
117+
);
118+
return;
119+
}
100120

101-
if ratio != threshold {
102-
if !*is_first_update.borrow() {
103-
observe_refresh
104-
.borrow()
105-
.as_ref()
106-
.expect("Refresh closure should exist.")(false, 1.0);
107-
return;
121+
if ratio == 0.0 {
122+
// If the reference is clipped, the ratio is 0. Throttle the refresh to prevent an infinite loop of updates.
123+
observe_timeout_id.replace(Some(
124+
observe_window
125+
.set_timeout_with_callback_and_timeout_and_arguments_0(
126+
(*timeout_closure).as_ref().unchecked_ref(),
127+
1000,
128+
)
129+
.expect("Set timeout should be successful."),
130+
));
131+
} else {
132+
observe_refresh
133+
.borrow()
134+
.as_ref()
135+
.expect("Refresh closure should exist.")(
136+
false, ratio
137+
);
138+
}
108139
}
109140

110-
if ratio == 0.0 {
111-
// If the reference is clipped, the ratio is 0. Throttle the refresh to prevent an infinite loop of updates.
112-
observe_timeout_id.replace(Some(
113-
observe_window
114-
.set_timeout_with_callback_and_timeout_and_arguments_0(
115-
(*timeout_closure).as_ref().unchecked_ref(),
116-
1000,
117-
)
118-
.expect("Set timeout should be successful."),
119-
));
120-
} else {
141+
if ratio == 1.0
142+
&& !rects_are_equal(
143+
&element_rect_for_root_margin.clone().into(),
144+
&element.get_bounding_client_rect().into(),
145+
)
146+
{
147+
// It's possible that even though the ratio is reported as 1, the
148+
// element is not actually fully within the IntersectionObserver's root
149+
// area anymore. This can happen under performance constraints. This may
150+
// be a bug in the browser's IntersectionObserver implementation. To
151+
// work around this, we compare the element's bounding rect now with
152+
// what it was at the time we created the IntersectionObserver. If they
153+
// are not equal then the element moved, so we refresh.
121154
observe_refresh
122155
.borrow()
123156
.as_ref()
124-
.expect("Refresh closure should exist.")(false, ratio);
157+
.expect("Refresh closure should exist.")(false, 1.0);
125158
}
126159

127160
is_first_update.replace(false);
@@ -373,11 +406,7 @@ pub fn auto_update(
373406
get_bounding_client_rect((&owned_reference).into(), false, false, None);
374407

375408
if let Some(prev_ref_rect) = prev_ref_rect.borrow().as_ref() {
376-
if next_ref_rect.x != prev_ref_rect.x
377-
|| next_ref_rect.y != prev_ref_rect.y
378-
|| next_ref_rect.width != prev_ref_rect.width
379-
|| next_ref_rect.height != prev_ref_rect.height
380-
{
409+
if !rects_are_equal(prev_ref_rect, &next_ref_rect) {
381410
update();
382411
}
383412
}

packages/dom/src/utils.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ pub mod get_viewport_rect;
77
pub mod get_visual_offsets;
88
pub mod get_window_scroll_bar_x;
99
pub mod is_static_positioned;
10+
pub mod rects_are_equal;
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
use floating_ui_utils::ClientRectObject;
2+
3+
pub fn rects_are_equal(a: &ClientRectObject, b: &ClientRectObject) -> bool {
4+
a.x == b.x && a.y == b.y && a.width == b.width && a.height == b.height
5+
}

upstream.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[releases]
22
core = "1.6.9"
3-
dom = "1.6.12"
3+
dom = "1.6.13"
44
utils = "0.2.9"
55
vue = "1.1.5"

0 commit comments

Comments
 (0)