mirror of
https://github.com/adoyle0/thaw.git
synced 2025-01-23 06:19:22 -05:00
fix: Binder component display position
This commit is contained in:
parent
360343e8cf
commit
3e59f506dd
3 changed files with 151 additions and 300 deletions
|
@ -1,4 +1,4 @@
|
||||||
.thaw-binder-follower-container {
|
.thaw-binder-follower {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use leptos::prelude::window;
|
use leptos::prelude::window;
|
||||||
use web_sys::DomRect;
|
use web_sys::DomRect;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone, PartialEq)]
|
||||||
pub enum FollowerPlacement {
|
pub enum FollowerPlacement {
|
||||||
Top,
|
Top,
|
||||||
Bottom,
|
Bottom,
|
||||||
|
@ -66,310 +66,152 @@ pub fn get_follower_placement_offset(
|
||||||
placement: FollowerPlacement,
|
placement: FollowerPlacement,
|
||||||
target_rect: DomRect,
|
target_rect: DomRect,
|
||||||
follower_rect: DomRect,
|
follower_rect: DomRect,
|
||||||
|
content_rect: DomRect,
|
||||||
) -> Option<FollowerPlacementOffset> {
|
) -> Option<FollowerPlacementOffset> {
|
||||||
match placement {
|
let (left, placement, top, transform) = match placement {
|
||||||
FollowerPlacement::Top => {
|
FollowerPlacement::Top | FollowerPlacement::TopStart | FollowerPlacement::TopEnd => {
|
||||||
let left = target_rect.x() + target_rect.width() / 2.0;
|
let Some(window_inner_height) = window_inner_height() else {
|
||||||
let (top, placement) = {
|
|
||||||
let follower_height = follower_rect.height();
|
|
||||||
let target_y = target_rect.y();
|
|
||||||
let target_height = target_rect.height();
|
|
||||||
let top = target_y - follower_height;
|
|
||||||
|
|
||||||
let Some(inner_height) = window_inner_height() else {
|
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
|
let content_height = content_rect.height();
|
||||||
if top < 0.0 && target_y + target_height + follower_height <= inner_height {
|
let target_top = target_rect.top();
|
||||||
(target_y + target_height, FollowerPlacement::Bottom)
|
let target_bottom = target_rect.bottom();
|
||||||
|
let top = target_top - content_height;
|
||||||
|
let (top, new_placement) =
|
||||||
|
if top < 0.0 && target_bottom + content_height <= window_inner_height {
|
||||||
|
(target_bottom, FollowerPlacement::Bottom)
|
||||||
} else {
|
} else {
|
||||||
(top, FollowerPlacement::Top)
|
(top, FollowerPlacement::Top)
|
||||||
}
|
|
||||||
};
|
|
||||||
Some(FollowerPlacementOffset {
|
|
||||||
top,
|
|
||||||
left,
|
|
||||||
transform: String::from("translateX(-50%)"),
|
|
||||||
placement,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
FollowerPlacement::TopStart => {
|
|
||||||
let left = target_rect.x();
|
|
||||||
let (top, placement) = {
|
|
||||||
let follower_height = follower_rect.height();
|
|
||||||
let target_y = target_rect.y();
|
|
||||||
let target_height = target_rect.height();
|
|
||||||
let top = target_y - follower_height;
|
|
||||||
|
|
||||||
let Some(inner_height) = window_inner_height() else {
|
|
||||||
return None;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if top < 0.0 && target_y + target_height + follower_height <= inner_height {
|
if placement == FollowerPlacement::Top {
|
||||||
(target_y + target_height, FollowerPlacement::BottomStart)
|
let left = target_rect.left() + target_rect.width() / 2.0;
|
||||||
|
let transform = String::from("translateX(-50%)");
|
||||||
|
(left, new_placement, top, transform)
|
||||||
|
} else if placement == FollowerPlacement::TopStart {
|
||||||
|
let left = target_rect.left();
|
||||||
|
let transform = String::new();
|
||||||
|
(left, new_placement, top, transform)
|
||||||
|
} else if placement == FollowerPlacement::TopEnd {
|
||||||
|
let left = target_rect.right();
|
||||||
|
let transform = String::from("translateX(-100%)");
|
||||||
|
(left, new_placement, top, transform)
|
||||||
} else {
|
} else {
|
||||||
(top, FollowerPlacement::TopStart)
|
unreachable!()
|
||||||
}
|
}
|
||||||
};
|
|
||||||
Some(FollowerPlacementOffset {
|
|
||||||
top,
|
|
||||||
left,
|
|
||||||
transform: String::new(),
|
|
||||||
placement,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
FollowerPlacement::TopEnd => {
|
FollowerPlacement::Bottom
|
||||||
let left = target_rect.x() + target_rect.width();
|
| FollowerPlacement::BottomStart
|
||||||
let (top, placement) = {
|
| FollowerPlacement::BottomEnd => {
|
||||||
let follower_height = follower_rect.height();
|
let Some(window_inner_height) = window_inner_height() else {
|
||||||
let target_y = target_rect.y();
|
|
||||||
let target_height = target_rect.height();
|
|
||||||
let top = target_y - follower_height;
|
|
||||||
|
|
||||||
let Some(inner_height) = window_inner_height() else {
|
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
|
let content_height = content_rect.height();
|
||||||
if top < 0.0 && target_y + target_height + follower_height <= inner_height {
|
let target_top = target_rect.top();
|
||||||
(target_y + target_height, FollowerPlacement::BottomEnd)
|
let target_bottom = target_rect.bottom();
|
||||||
} else {
|
let top = target_bottom;
|
||||||
(top, FollowerPlacement::TopEnd)
|
let (top, new_placement) = if top + content_height > window_inner_height
|
||||||
}
|
&& target_top - content_height >= 0.0
|
||||||
};
|
{
|
||||||
Some(FollowerPlacementOffset {
|
(target_top - content_height, FollowerPlacement::Top)
|
||||||
top,
|
|
||||||
left,
|
|
||||||
transform: String::from("translateX(-100%)"),
|
|
||||||
placement,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
FollowerPlacement::Left => {
|
|
||||||
let top = target_rect.y() + target_rect.height() / 2.0;
|
|
||||||
let (left, placement) = {
|
|
||||||
let follower_width = follower_rect.width();
|
|
||||||
let target_x = target_rect.x();
|
|
||||||
let target_width = target_rect.width();
|
|
||||||
let left = target_x - follower_width;
|
|
||||||
|
|
||||||
let Some(inner_width) = window_inner_width() else {
|
|
||||||
return None;
|
|
||||||
};
|
|
||||||
|
|
||||||
if left < 0.0 && target_x + target_width + follower_width > inner_width {
|
|
||||||
(target_x + follower_width, FollowerPlacement::Right)
|
|
||||||
} else {
|
|
||||||
(left, FollowerPlacement::Left)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Some(FollowerPlacementOffset {
|
|
||||||
top,
|
|
||||||
left,
|
|
||||||
transform: String::from("translateY(-50%)"),
|
|
||||||
placement,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
FollowerPlacement::LeftStart => {
|
|
||||||
let top = target_rect.y();
|
|
||||||
let (left, placement) = {
|
|
||||||
let follower_width = follower_rect.width();
|
|
||||||
let target_x = target_rect.x();
|
|
||||||
let target_width = target_rect.width();
|
|
||||||
let left = target_x - follower_width;
|
|
||||||
|
|
||||||
let Some(inner_width) = window_inner_width() else {
|
|
||||||
return None;
|
|
||||||
};
|
|
||||||
|
|
||||||
if left < 0.0 && target_x + target_width + follower_width > inner_width {
|
|
||||||
(target_x + follower_width, FollowerPlacement::RightStart)
|
|
||||||
} else {
|
|
||||||
(left, FollowerPlacement::LeftStart)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Some(FollowerPlacementOffset {
|
|
||||||
top,
|
|
||||||
left,
|
|
||||||
transform: String::new(),
|
|
||||||
placement,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
FollowerPlacement::LeftEnd => {
|
|
||||||
let top = target_rect.y() + target_rect.height();
|
|
||||||
let (left, placement) = {
|
|
||||||
let follower_width = follower_rect.width();
|
|
||||||
let target_x = target_rect.x();
|
|
||||||
let target_width = target_rect.width();
|
|
||||||
let left = target_x - follower_width;
|
|
||||||
|
|
||||||
let Some(inner_width) = window_inner_width() else {
|
|
||||||
return None;
|
|
||||||
};
|
|
||||||
|
|
||||||
if left < 0.0 && target_x + target_width + follower_width > inner_width {
|
|
||||||
(target_x + follower_width, FollowerPlacement::RightEnd)
|
|
||||||
} else {
|
|
||||||
(left, FollowerPlacement::LeftEnd)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Some(FollowerPlacementOffset {
|
|
||||||
top,
|
|
||||||
left,
|
|
||||||
transform: String::from("translateY(-100%)"),
|
|
||||||
placement,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
FollowerPlacement::Right => {
|
|
||||||
let top = target_rect.y() + target_rect.height() / 2.0;
|
|
||||||
let (left, placement) = {
|
|
||||||
let follower_width = follower_rect.width();
|
|
||||||
let target_x = target_rect.x();
|
|
||||||
let target_width = target_rect.width();
|
|
||||||
let left = target_x + target_width;
|
|
||||||
|
|
||||||
let Some(inner_width) = window_inner_width() else {
|
|
||||||
return None;
|
|
||||||
};
|
|
||||||
|
|
||||||
if left + follower_width > inner_width && target_x - follower_width >= 0.0 {
|
|
||||||
(target_x - follower_width, FollowerPlacement::Left)
|
|
||||||
} else {
|
|
||||||
(left, FollowerPlacement::Right)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Some(FollowerPlacementOffset {
|
|
||||||
top,
|
|
||||||
left,
|
|
||||||
transform: String::from("translateY(-50%)"),
|
|
||||||
placement,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
FollowerPlacement::RightStart => {
|
|
||||||
let top = target_rect.y();
|
|
||||||
let (left, placement) = {
|
|
||||||
let follower_width = follower_rect.width();
|
|
||||||
let target_x = target_rect.x();
|
|
||||||
let target_width = target_rect.width();
|
|
||||||
let left = target_x + target_width;
|
|
||||||
|
|
||||||
let Some(inner_width) = window_inner_width() else {
|
|
||||||
return None;
|
|
||||||
};
|
|
||||||
|
|
||||||
if left + follower_width > inner_width && target_x - follower_width >= 0.0 {
|
|
||||||
(target_x - follower_width, FollowerPlacement::LeftStart)
|
|
||||||
} else {
|
|
||||||
(left, FollowerPlacement::RightStart)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Some(FollowerPlacementOffset {
|
|
||||||
top,
|
|
||||||
left,
|
|
||||||
transform: String::new(),
|
|
||||||
placement,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
FollowerPlacement::RightEnd => {
|
|
||||||
let top = target_rect.y() + target_rect.height();
|
|
||||||
let (left, placement) = {
|
|
||||||
let follower_width = follower_rect.width();
|
|
||||||
let target_x = target_rect.x();
|
|
||||||
let target_width = target_rect.width();
|
|
||||||
let left = target_x + target_width;
|
|
||||||
|
|
||||||
let Some(inner_width) = window_inner_width() else {
|
|
||||||
return None;
|
|
||||||
};
|
|
||||||
|
|
||||||
if left + follower_width > inner_width && target_x - follower_width >= 0.0 {
|
|
||||||
(target_x - follower_width, FollowerPlacement::LeftEnd)
|
|
||||||
} else {
|
|
||||||
(left, FollowerPlacement::RightEnd)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Some(FollowerPlacementOffset {
|
|
||||||
top,
|
|
||||||
left,
|
|
||||||
transform: String::from("translateY(-100%)"),
|
|
||||||
placement,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
FollowerPlacement::Bottom => {
|
|
||||||
let left = target_rect.x() + target_rect.width() / 2.0;
|
|
||||||
let (top, placement) = {
|
|
||||||
let follower_height = follower_rect.height();
|
|
||||||
let target_y = target_rect.y();
|
|
||||||
let target_height = target_rect.height();
|
|
||||||
let top = target_y + target_height;
|
|
||||||
|
|
||||||
let Some(inner_height) = window_inner_height() else {
|
|
||||||
return None;
|
|
||||||
};
|
|
||||||
|
|
||||||
if top + follower_height > inner_height && target_y - follower_height >= 0.0 {
|
|
||||||
(target_y - follower_height, FollowerPlacement::Top)
|
|
||||||
} else {
|
} else {
|
||||||
(top, FollowerPlacement::Bottom)
|
(top, FollowerPlacement::Bottom)
|
||||||
}
|
|
||||||
};
|
};
|
||||||
Some(FollowerPlacementOffset {
|
if placement == FollowerPlacement::Bottom {
|
||||||
top,
|
let left = target_rect.left() + target_rect.width() / 2.0;
|
||||||
left,
|
let transform = String::from("translateX(-50%)");
|
||||||
transform: String::from("translateX(-50%)"),
|
(left, new_placement, top, transform)
|
||||||
placement,
|
} else if placement == FollowerPlacement::BottomStart {
|
||||||
})
|
let left = target_rect.left();
|
||||||
|
let transform = String::new();
|
||||||
|
(left, new_placement, top, transform)
|
||||||
|
} else if placement == FollowerPlacement::BottomEnd {
|
||||||
|
let left = target_rect.right();
|
||||||
|
let transform = String::from("translateX(-100%)");
|
||||||
|
(left, new_placement, top, transform)
|
||||||
|
} else {
|
||||||
|
unreachable!()
|
||||||
}
|
}
|
||||||
FollowerPlacement::BottomStart => {
|
}
|
||||||
let left = target_rect.x();
|
FollowerPlacement::Left | FollowerPlacement::LeftStart | FollowerPlacement::LeftEnd => {
|
||||||
let (top, placement) = {
|
let Some(window_inner_width) = window_inner_width() else {
|
||||||
let follower_height = follower_rect.height();
|
return None;
|
||||||
let target_y = target_rect.y();
|
};
|
||||||
let target_height = target_rect.height();
|
let content_width = content_rect.width();
|
||||||
let top = target_y + target_height;
|
let target_left = target_rect.left();
|
||||||
|
let target_right = target_rect.right();
|
||||||
let Some(inner_height) = window_inner_height() else {
|
let left = target_left - content_width;
|
||||||
|
leptos::logging::log!(
|
||||||
|
"{}-{} {} {}",
|
||||||
|
left,
|
||||||
|
target_right,
|
||||||
|
content_width,
|
||||||
|
window_inner_width
|
||||||
|
);
|
||||||
|
let (left, new_placement) =
|
||||||
|
if left < 0.0 && target_right + content_width <= window_inner_width {
|
||||||
|
(target_right, FollowerPlacement::Right)
|
||||||
|
} else {
|
||||||
|
(left, FollowerPlacement::Left)
|
||||||
|
};
|
||||||
|
if placement == FollowerPlacement::Left {
|
||||||
|
let top = target_rect.top() + target_rect.height() / 2.0;
|
||||||
|
let transform = String::from("translateY(-50%)");
|
||||||
|
(left, new_placement, top, transform)
|
||||||
|
} else if placement == FollowerPlacement::LeftStart {
|
||||||
|
let top = target_rect.top();
|
||||||
|
let transform = String::new();
|
||||||
|
(left, new_placement, top, transform)
|
||||||
|
} else if placement == FollowerPlacement::LeftEnd {
|
||||||
|
let top = target_rect.bottom();
|
||||||
|
let transform = String::from("translateY(-100%)");
|
||||||
|
(left, new_placement, top, transform)
|
||||||
|
} else {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FollowerPlacement::Right | FollowerPlacement::RightStart | FollowerPlacement::RightEnd => {
|
||||||
|
let Some(window_inner_width) = window_inner_width() else {
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
|
|
||||||
if top + follower_height > inner_height && target_y - follower_height >= 0.0 {
|
let content_width = content_rect.width();
|
||||||
(target_y - follower_height, FollowerPlacement::TopStart)
|
let target_left = target_rect.left();
|
||||||
|
let target_right = target_rect.right();
|
||||||
|
let left = target_right;
|
||||||
|
let (left, new_placement) = if left + content_width > window_inner_width
|
||||||
|
&& target_left - content_width >= 0.0
|
||||||
|
{
|
||||||
|
(target_left - content_width, FollowerPlacement::Left)
|
||||||
} else {
|
} else {
|
||||||
(top, FollowerPlacement::BottomStart)
|
(left, FollowerPlacement::Right)
|
||||||
}
|
|
||||||
};
|
|
||||||
Some(FollowerPlacementOffset {
|
|
||||||
top,
|
|
||||||
left,
|
|
||||||
transform: String::new(),
|
|
||||||
placement,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
FollowerPlacement::BottomEnd => {
|
|
||||||
let left = target_rect.x() + target_rect.width();
|
|
||||||
let (top, placement) = {
|
|
||||||
let follower_height = follower_rect.height();
|
|
||||||
let target_y = target_rect.y();
|
|
||||||
let target_height = target_rect.height();
|
|
||||||
let top = target_y + target_height;
|
|
||||||
|
|
||||||
let Some(inner_height) = window_inner_height() else {
|
|
||||||
return None;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if top + follower_height > inner_height && target_y - follower_height >= 0.0 {
|
if placement == FollowerPlacement::Right {
|
||||||
(target_y - follower_height, FollowerPlacement::TopEnd)
|
let top = target_rect.top() + target_rect.height() / 2.0;
|
||||||
|
let transform = String::from("translateY(-50%)");
|
||||||
|
(left, new_placement, top, transform)
|
||||||
|
} else if placement == FollowerPlacement::RightStart {
|
||||||
|
let top = target_rect.top();
|
||||||
|
let transform = String::new();
|
||||||
|
(left, new_placement, top, transform)
|
||||||
|
} else if placement == FollowerPlacement::RightEnd {
|
||||||
|
let top = target_rect.bottom();
|
||||||
|
let transform = String::from("translateY(-100%)");
|
||||||
|
(left, new_placement, top, transform)
|
||||||
} else {
|
} else {
|
||||||
(top, FollowerPlacement::BottomEnd)
|
unreachable!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Some(FollowerPlacementOffset {
|
Some(FollowerPlacementOffset {
|
||||||
top,
|
top: top - follower_rect.top(),
|
||||||
left,
|
left: left - follower_rect.left(),
|
||||||
transform: String::from("translateX(-100%)"),
|
|
||||||
placement,
|
placement,
|
||||||
|
transform,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn window_inner_width() -> Option<f64> {
|
fn window_inner_width() -> Option<f64> {
|
||||||
let Ok(inner_width) = window().inner_width() else {
|
let Ok(inner_width) = window().inner_width() else {
|
||||||
|
|
|
@ -82,16 +82,21 @@ where
|
||||||
|
|
||||||
let scrollable_element_handle_vec = StoredValue::<Vec<EventListenerHandle>>::new(vec![]);
|
let scrollable_element_handle_vec = StoredValue::<Vec<EventListenerHandle>>::new(vec![]);
|
||||||
let resize_handle = StoredValue::new(None::<WindowListenerHandle>);
|
let resize_handle = StoredValue::new(None::<WindowListenerHandle>);
|
||||||
|
let follower_ref = NodeRef::<html::Div>::new();
|
||||||
let content_ref = NodeRef::<html::Div>::new();
|
let content_ref = NodeRef::<html::Div>::new();
|
||||||
let content_style = RwSignal::new(String::new());
|
let content_style = RwSignal::new(String::new());
|
||||||
let placement_str = RwSignal::new(follower_placement.as_str());
|
let placement_str = RwSignal::new(follower_placement.as_str());
|
||||||
let sync_position = move || {
|
let sync_position = move || {
|
||||||
|
let Some(follower_el) = follower_ref.get_untracked() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
let Some(content_ref) = content_ref.get_untracked() else {
|
let Some(content_ref) = content_ref.get_untracked() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let Some(target_ref) = target_ref.get_untracked() else {
|
let Some(target_ref) = target_ref.get_untracked() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
let follower_rect = follower_el.get_bounding_client_rect();
|
||||||
let target_rect = target_ref.get_bounding_client_rect();
|
let target_rect = target_ref.get_bounding_client_rect();
|
||||||
let content_rect = content_ref.get_bounding_client_rect();
|
let content_rect = content_ref.get_bounding_client_rect();
|
||||||
let mut style = String::new();
|
let mut style = String::new();
|
||||||
|
@ -108,8 +113,12 @@ where
|
||||||
left,
|
left,
|
||||||
transform,
|
transform,
|
||||||
placement,
|
placement,
|
||||||
}) = get_follower_placement_offset(follower_placement, target_rect, content_rect)
|
}) = get_follower_placement_offset(
|
||||||
{
|
follower_placement,
|
||||||
|
target_rect,
|
||||||
|
follower_rect,
|
||||||
|
content_rect,
|
||||||
|
) {
|
||||||
placement_str.set(placement.as_str());
|
placement_str.set(placement.as_str());
|
||||||
style.push_str(&format!(
|
style.push_str(&format!(
|
||||||
"transform-origin: {};",
|
"transform-origin: {};",
|
||||||
|
@ -197,18 +206,18 @@ where
|
||||||
view! {
|
view! {
|
||||||
{children()}
|
{children()}
|
||||||
<Teleport immediate=follower_show>
|
<Teleport immediate=follower_show>
|
||||||
<Provider value=follower_injection>
|
<div class="thaw-binder-follower" node_ref=follower_ref>
|
||||||
<div class="thaw-binder-follower-container">
|
|
||||||
<div
|
<div
|
||||||
class="thaw-binder-follower-content"
|
class="thaw-binder-follower-content"
|
||||||
data-thaw-placement=move || placement_str.get()
|
data-thaw-placement=move || placement_str.get()
|
||||||
node_ref=content_ref
|
node_ref=content_ref
|
||||||
style=move || content_style.get()
|
style=move || content_style.get()
|
||||||
>
|
>
|
||||||
|
<Provider value=follower_injection>
|
||||||
{follower_children()}
|
{follower_children()}
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Provider>
|
</Provider>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</Teleport>
|
</Teleport>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue