feat: binder component add placement

This commit is contained in:
luoxiao 2023-11-12 01:58:26 +08:00
parent f5e6eae558
commit c57910948f
3 changed files with 91 additions and 10 deletions

View file

@ -1,7 +1,7 @@
mod theme;
use crate::{
components::{Binder, Follower},
components::{Binder, Follower, FollowerPlacement},
mount_style, use_theme,
utils::StoredMaybeSignal,
Input, Theme,
@ -61,7 +61,7 @@ pub fn AutoComplete(
allow_value
/>
</div>
<Follower slot show=is_show_menu>
<Follower slot show=is_show_menu placement=FollowerPlacement::BottomStart>
<div
class="thaw-auto-complete__menu"
style=move || menu_css_vars.get()

View file

@ -0,0 +1,65 @@
use leptos::window;
use web_sys::DomRect;
#[derive(Clone)]
pub enum FollowerPlacement {
// Top,
// Bottom,
// Left,
// Right,
// TopStart,
// TopEnd,
// LeftStart,
// LeftEnd,
// RightStart,
// RightEnd,
BottomStart,
// BottomEnd,
}
impl Copy for FollowerPlacement {}
pub fn get_follower_placement_style(
placement: FollowerPlacement,
target_rect: DomRect,
follower_rect: DomRect,
) -> Option<String> {
// TODO: Implements FollowerPlacement more properties
_ = placement;
let mut style = String::new();
let left = target_rect.x();
let top = {
let follower_height = follower_rect.height();
let target_y = target_rect.y();
let target_height = target_rect.height();
let mut top = target_y + target_height;
let Some(inner_height) = window_inner_height() else {
return None;
};
if top + follower_height > inner_height {
if target_y - follower_height >= 0.0 {
top = target_y - follower_height
}
}
top
};
style.push_str(&format!(
"transform: translateX({left}px) translateY({top}px);"
));
Some(style)
}
fn window_inner_height() -> Option<f64> {
let Ok(inner_height) = window().inner_height() else {
return None;
};
let Some(inner_height) = inner_height.as_f64() else {
return None;
};
Some(inner_height)
}

View file

@ -1,8 +1,12 @@
mod get_placement_style;
use crate::{
mount_style,
teleport::Teleport,
utils::{add_event_listener, EventListenerHandle},
};
use get_placement_style::get_follower_placement_style;
pub use get_placement_style::FollowerPlacement;
use leptos::{
html::{AnyElement, ElementDescriptor, ToHtmlElement},
leptos_dom::helpers::WindowListenerHandle,
@ -13,6 +17,7 @@ use leptos::{
pub struct Follower {
#[prop(into)]
show: MaybeSignal<bool>,
placement: FollowerPlacement,
children: Children,
}
@ -25,6 +30,7 @@ pub fn Binder<El: ElementDescriptor + Clone + 'static>(
mount_style("binder", include_str!("./binder.css"));
let Follower {
show: follower_show,
placement: follower_placement,
children: follower_children,
} = follower;
@ -102,6 +108,7 @@ pub fn Binder<El: ElementDescriptor + Clone + 'static>(
<FollowerContainer
show=follower_show
target_ref
placement=follower_placement
add_scroll_listener
remove_scroll_listener
add_resize_listener
@ -116,6 +123,7 @@ pub fn Binder<El: ElementDescriptor + Clone + 'static>(
fn FollowerContainer<El: ElementDescriptor + Clone + 'static>(
show: MaybeSignal<bool>,
target_ref: NodeRef<El>,
placement: FollowerPlacement,
#[prop(into)] add_scroll_listener: Callback<Callback<()>>,
#[prop(into)] remove_scroll_listener: Callback<()>,
#[prop(into)] add_resize_listener: Callback<Callback<()>>,
@ -125,18 +133,24 @@ fn FollowerContainer<El: ElementDescriptor + Clone + 'static>(
let content_ref = create_node_ref::<html::Div>();
let content_style = create_rw_signal(String::new());
let sync_position: Callback<()> = Callback::new(move |_| {
let Some(target_ref) = target_ref.get().map(|target| target.into_any()) else {
let Some(content_ref) = content_ref.get_untracked() else {
return;
};
let Some(target_ref) = target_ref.get_untracked().map(|target| target.into_any()) else {
return;
};
let target_rect = target_ref.get_bounding_client_rect();
let content_rect = content_ref.get_bounding_client_rect();
let mut style = String::new();
style.push_str(&format!("width: {}px;", target_rect.width()));
style.push_str(&format!(
"transform: translateX({}px) translateY({}px);",
target_rect.x(),
target_rect.y() + target_rect.height()
));
if let Some(placement_style) =
get_follower_placement_style(placement, target_rect, content_rect)
{
style.push_str(&placement_style);
} else {
logging::error!("Thaw-Binder: get_follower_placement_style return None");
}
content_style.set(style);
});
@ -149,7 +163,9 @@ fn FollowerContainer<El: ElementDescriptor + Clone + 'static>(
}
let is_show = show.get();
if is_show {
sync_position.call(());
request_animation_frame(move || {
sync_position.call(());
});
add_scroll_listener.call(sync_position);
add_resize_listener.call(sync_position);
} else {