diff --git a/packages/core/src/compute_coords_from_placement.rs b/packages/core/src/compute_coords_from_placement.rs index 0fec589..5525a93 100644 --- a/packages/core/src/compute_coords_from_placement.rs +++ b/packages/core/src/compute_coords_from_placement.rs @@ -42,18 +42,18 @@ pub fn compute_coords_from_placement( }; let rtl = rtl.unwrap_or(false); - match get_alignment(placement) { - Some(Alignment::Start) => { - coords.update_axis(alignment_axis, |value| { - value - common_align * (if rtl && is_vertical { -1.0 } else { 1.0 }) - }); - } - Some(Alignment::End) => { - coords.update_axis(alignment_axis, |value| { - value + common_align * (if rtl && is_vertical { -1.0 } else { 1.0 }) - }); - } - None => {} + + if let Some(alignment) = get_alignment(placement) { + coords.update_axis(alignment_axis, |value| { + value + - common_align + * (if alignment == Alignment::End { + 1.0 + } else { + -1.0 + }) + * (if rtl && is_vertical { -1.0 } else { 1.0 }) + }); } coords diff --git a/packages/core/src/middleware/auto_placement.rs b/packages/core/src/middleware/auto_placement.rs index 102de9c..c7da6e4 100644 --- a/packages/core/src/middleware/auto_placement.rs +++ b/packages/core/src/middleware/auto_placement.rs @@ -236,23 +236,12 @@ impl Middleware Middleware .get_client_rects(elements.reference) .unwrap_or(vec![]); + // No rects (e.g. a hidden or detached reference, or a collapsed range) - + // keep the existing reference rect rather than resetting to an invalid + // one with non-finite values. + if native_client_rects.is_empty() { + return MiddlewareReturn { + x: None, + y: None, + data: None, + reset: None, + }; + } + let client_rects = get_rects_by_line(native_client_rects.clone()); let fallback = rect_to_client_rect(get_bounding_rect(native_client_rects)); let padding_object = get_padding_object(padding); @@ -187,7 +199,8 @@ impl let get_bounding_client_rect = move || { // There are two rects and they are disjoined. if client_rects.len() == 2 - && client_rects[0].left > client_rects[1].right + && (client_rects[0].left > client_rects[1].right + || client_rects[1].left > client_rects[0].right) && let Some(x) = options.x && let Some(y) = options.y { @@ -222,19 +235,13 @@ impl } else { last_rect.right }; - let width = right - left; - let height = bottom - top; - return ClientRectObject { + return rect_to_client_rect(Rect { x: left, y: top, - width, - height, - top, - right, - bottom, - left, - }; + width: right - left, + height: bottom - top, + }); } let is_left_side = placement.side() == Side::Left; @@ -261,21 +268,13 @@ impl let top = measure_rects.first().expect("Enough elements exist.").top; let bottom = measure_rects.last().expect("Enough elements exist.").bottom; - let left = min_left; - let right = max_right; - let width = right - left; - let height = bottom - top; - return ClientRectObject { - x: left, + return rect_to_client_rect(Rect { + x: min_left, y: top, - width, - height, - top, - right, - bottom, - left, - }; + width: max_right - min_left, + height: bottom - top, + }); } fallback.clone() diff --git a/packages/core/src/middleware/shift.rs b/packages/core/src/middleware/shift.rs index 370a215..410c235 100644 --- a/packages/core/src/middleware/shift.rs +++ b/packages/core/src/middleware/shift.rs @@ -192,34 +192,30 @@ impl let mut main_axis_coord = coords.axis(main_axis); let mut cross_axis_coord = coords.axis(cross_axis); - if check_main_axis { - let min_side = match main_axis { - Axis::X => Side::Left, - Axis::Y => Side::Top, - }; - let max_side = match main_axis { - Axis::X => Side::Right, - Axis::Y => Side::Bottom, - }; - let min = main_axis_coord + overflow.side(min_side); - let max = main_axis_coord - overflow.side(max_side); + let clamp_coord = |axis: Axis, coord: f64| -> f64 { + clamp( + coord + + overflow.side(if axis == Axis::Y { + Side::Top + } else { + Side::Left + }), + coord, + coord + - overflow.side(if axis == Axis::Y { + Side::Bottom + } else { + Side::Right + }), + ) + }; - main_axis_coord = clamp(min, main_axis_coord, max); + if check_main_axis { + main_axis_coord = clamp_coord(main_axis, main_axis_coord); } if check_cross_axis { - let min_side = match cross_axis { - Axis::X => Side::Left, - Axis::Y => Side::Top, - }; - let max_side = match cross_axis { - Axis::X => Side::Right, - Axis::Y => Side::Bottom, - }; - let min = cross_axis_coord + overflow.side(min_side); - let max = cross_axis_coord - overflow.side(max_side); - - cross_axis_coord = clamp(min, cross_axis_coord, max); + cross_axis_coord = clamp_coord(cross_axis, cross_axis_coord); } let limited_coords = limiter.compute(MiddlewareState { diff --git a/packages/core/src/middleware/size.rs b/packages/core/src/middleware/size.rs index 634c362..e4a9d8f 100644 --- a/packages/core/src/middleware/size.rs +++ b/packages/core/src/middleware/size.rs @@ -207,27 +207,10 @@ impl Middleware { #[derive(Clone, Debug, PartialEq)] pub enum RootBoundary { Viewport, + LayoutViewport, Document, Rect(Rect), } diff --git a/packages/dom/src/platform/get_clipping_rect.rs b/packages/dom/src/platform/get_clipping_rect.rs index ce8f396..fafd89a 100644 --- a/packages/dom/src/platform/get_clipping_rect.rs +++ b/packages/dom/src/platform/get_clipping_rect.rs @@ -4,7 +4,7 @@ use floating_ui_utils::{ dom::{ OverflowAncestor, get_computed_style, get_document_element, get_node_name, get_overflow_ancestors, get_parent_node, is_containing_block, is_last_traversable_node, - is_overflow_element, is_top_layer, + is_top_layer, }, rect_to_client_rect, }; @@ -49,7 +49,8 @@ fn get_client_rect_from_clipping_ancestor( ElementOrRootBoundary::Element(element) => { get_inner_bounding_client_rect(&element, strategy) } - ElementOrRootBoundary::RootBoundary(RootBoundary::Viewport) => { + ElementOrRootBoundary::RootBoundary(RootBoundary::Viewport) + | ElementOrRootBoundary::RootBoundary(RootBoundary::LayoutViewport) => { get_viewport_rect(&get_document_element(Some(element.into())), strategy) } ElementOrRootBoundary::RootBoundary(RootBoundary::Document) => { @@ -69,23 +70,6 @@ fn get_client_rect_from_clipping_ancestor( rect_to_client_rect(rect) } -fn has_fixed_position_ancestor(element: &Element, stop_node: &Node) -> bool { - let parent_node = get_parent_node(element); - if &parent_node == stop_node - || !parent_node.is_instance_of::() - || is_last_traversable_node(&parent_node) - { - false - } else { - let element = parent_node.unchecked_into::(); - get_computed_style(&element) - .get_property_value("position") - .expect("Computed style should have position.") - == "fixed" - || has_fixed_position_ancestor(&element, stop_node) - } -} - fn get_clipping_element_ancestors(element: &Element) -> Vec { // TODO: cache @@ -99,7 +83,7 @@ fn get_clipping_element_ancestors(element: &Element) -> Vec { OverflowAncestor::VisualViewport(_) => None, }) .collect(); - let mut current_containing_block_computed_style: Option = None; + let mut last_kept_computed_style: Option = None; let element_is_fixed = get_computed_style(element) .get_property_value("position") .expect("Computed style should have position.") @@ -116,37 +100,34 @@ fn get_clipping_element_ancestors(element: &Element) -> Vec { let computed_style = get_computed_style(current_element); let current_node_is_containing = is_containing_block(current_element.into()); - let position = computed_style - .get_property_value("position") - .expect("Computed style should have position"); - - if !current_node_is_containing && position == "fixed" { - current_containing_block_computed_style = None; - } - - let should_drop_current_node = if element_is_fixed { - !current_node_is_containing && current_containing_block_computed_style.is_none() - } else { - (!current_node_is_containing - && position == "static" - && current_containing_block_computed_style - .as_ref() - .is_some_and(|style| { - let positon = style - .get_property_value("position") - .expect("Computed style should have position"); - - positon == "absolute" || positon == "fixed" - })) - || (is_overflow_element(current_element) - && !current_node_is_containing - && has_fixed_position_ancestor(element, current_element)) - }; + // Position of the containing block chain below the current node. A fixed + // element whose containing block hasn't been found yet is a fixed chain. + let last_position = + if let Some(last_kept_computed_style) = last_kept_computed_style.as_ref() { + last_kept_computed_style + .get_property_value("position") + .expect("Computed style should have position") + } else { + if element_is_fixed { "fixed" } else { "" }.to_owned() + }; + + // A non-containing ancestor does not clip the element when the chain + // below it escapes it: a fixed chain escapes all ancestors up to the + // next containing block, an absolute chain escapes static ancestors. + let should_drop_current_node = !current_node_is_containing + && (last_position == "fixed" + || (last_position == "absolute" + && computed_style + .get_property_value("position") + .expect("Computed style should have position") + == "static")); if should_drop_current_node { + // Drop non-containing blocks. result.retain(|ancestor| ancestor != current_element); } else { - current_containing_block_computed_style = Some(computed_style); + // The kept node carries the chain position for the next iteration. + last_kept_computed_style = Some(computed_style); } current_node = get_parent_node(¤t_node); diff --git a/packages/dom/src/utils/get_css_dimensions.rs b/packages/dom/src/utils/get_css_dimensions.rs index 7b2a575..ee0ae1a 100644 --- a/packages/dom/src/utils/get_css_dimensions.rs +++ b/packages/dom/src/utils/get_css_dimensions.rs @@ -26,16 +26,15 @@ pub fn get_css_dimensions(element: &Element) -> CssDimensions { .parse::() .unwrap_or(0.0); - let offset_width; - let offset_height; - if is_html_element(element) { + let (offset_width, offset_height) = if is_html_element(element) { let element = element.unchecked_ref::(); - offset_width = element.offset_width() as f64; - offset_height = element.offset_height() as f64; + ( + element.offset_width() as f64, + element.offset_height() as f64, + ) } else { - offset_width = width; - offset_height = height; - } + (width, height) + }; let should_fallback = width.round() != offset_width || height.round() != offset_height; CssDimensions { diff --git a/upstream.toml b/upstream.toml index ef002fc..9664e46 100644 --- a/upstream.toml +++ b/upstream.toml @@ -1,5 +1,5 @@ [releases] -core = "1.7.5" +core = "1.8.0" dom = "1.7.6" utils = "0.2.12" vue = "2.0.0"