mirror of
https://github.com/adoyle0/thaw.git
synced 2025-03-13 05:59:49 -04:00
feat: binder component add placement
This commit is contained in:
parent
f5e6eae558
commit
c57910948f
3 changed files with 91 additions and 10 deletions
|
@ -1,7 +1,7 @@
|
||||||
mod theme;
|
mod theme;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
components::{Binder, Follower},
|
components::{Binder, Follower, FollowerPlacement},
|
||||||
mount_style, use_theme,
|
mount_style, use_theme,
|
||||||
utils::StoredMaybeSignal,
|
utils::StoredMaybeSignal,
|
||||||
Input, Theme,
|
Input, Theme,
|
||||||
|
@ -61,7 +61,7 @@ pub fn AutoComplete(
|
||||||
allow_value
|
allow_value
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<Follower slot show=is_show_menu>
|
<Follower slot show=is_show_menu placement=FollowerPlacement::BottomStart>
|
||||||
<div
|
<div
|
||||||
class="thaw-auto-complete__menu"
|
class="thaw-auto-complete__menu"
|
||||||
style=move || menu_css_vars.get()
|
style=move || menu_css_vars.get()
|
||||||
|
|
65
src/components/binder/get_placement_style.rs
Normal file
65
src/components/binder/get_placement_style.rs
Normal 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)
|
||||||
|
}
|
|
@ -1,8 +1,12 @@
|
||||||
|
mod get_placement_style;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
mount_style,
|
mount_style,
|
||||||
teleport::Teleport,
|
teleport::Teleport,
|
||||||
utils::{add_event_listener, EventListenerHandle},
|
utils::{add_event_listener, EventListenerHandle},
|
||||||
};
|
};
|
||||||
|
use get_placement_style::get_follower_placement_style;
|
||||||
|
pub use get_placement_style::FollowerPlacement;
|
||||||
use leptos::{
|
use leptos::{
|
||||||
html::{AnyElement, ElementDescriptor, ToHtmlElement},
|
html::{AnyElement, ElementDescriptor, ToHtmlElement},
|
||||||
leptos_dom::helpers::WindowListenerHandle,
|
leptos_dom::helpers::WindowListenerHandle,
|
||||||
|
@ -13,6 +17,7 @@ use leptos::{
|
||||||
pub struct Follower {
|
pub struct Follower {
|
||||||
#[prop(into)]
|
#[prop(into)]
|
||||||
show: MaybeSignal<bool>,
|
show: MaybeSignal<bool>,
|
||||||
|
placement: FollowerPlacement,
|
||||||
children: Children,
|
children: Children,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,6 +30,7 @@ pub fn Binder<El: ElementDescriptor + Clone + 'static>(
|
||||||
mount_style("binder", include_str!("./binder.css"));
|
mount_style("binder", include_str!("./binder.css"));
|
||||||
let Follower {
|
let Follower {
|
||||||
show: follower_show,
|
show: follower_show,
|
||||||
|
placement: follower_placement,
|
||||||
children: follower_children,
|
children: follower_children,
|
||||||
} = follower;
|
} = follower;
|
||||||
|
|
||||||
|
@ -102,6 +108,7 @@ pub fn Binder<El: ElementDescriptor + Clone + 'static>(
|
||||||
<FollowerContainer
|
<FollowerContainer
|
||||||
show=follower_show
|
show=follower_show
|
||||||
target_ref
|
target_ref
|
||||||
|
placement=follower_placement
|
||||||
add_scroll_listener
|
add_scroll_listener
|
||||||
remove_scroll_listener
|
remove_scroll_listener
|
||||||
add_resize_listener
|
add_resize_listener
|
||||||
|
@ -116,6 +123,7 @@ pub fn Binder<El: ElementDescriptor + Clone + 'static>(
|
||||||
fn FollowerContainer<El: ElementDescriptor + Clone + 'static>(
|
fn FollowerContainer<El: ElementDescriptor + Clone + 'static>(
|
||||||
show: MaybeSignal<bool>,
|
show: MaybeSignal<bool>,
|
||||||
target_ref: NodeRef<El>,
|
target_ref: NodeRef<El>,
|
||||||
|
placement: FollowerPlacement,
|
||||||
#[prop(into)] add_scroll_listener: Callback<Callback<()>>,
|
#[prop(into)] add_scroll_listener: Callback<Callback<()>>,
|
||||||
#[prop(into)] remove_scroll_listener: Callback<()>,
|
#[prop(into)] remove_scroll_listener: Callback<()>,
|
||||||
#[prop(into)] add_resize_listener: Callback<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_ref = create_node_ref::<html::Div>();
|
||||||
let content_style = create_rw_signal(String::new());
|
let content_style = create_rw_signal(String::new());
|
||||||
let sync_position: Callback<()> = Callback::new(move |_| {
|
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;
|
return;
|
||||||
};
|
};
|
||||||
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 mut style = String::new();
|
let mut style = String::new();
|
||||||
style.push_str(&format!("width: {}px;", target_rect.width()));
|
style.push_str(&format!("width: {}px;", target_rect.width()));
|
||||||
style.push_str(&format!(
|
if let Some(placement_style) =
|
||||||
"transform: translateX({}px) translateY({}px);",
|
get_follower_placement_style(placement, target_rect, content_rect)
|
||||||
target_rect.x(),
|
{
|
||||||
target_rect.y() + target_rect.height()
|
style.push_str(&placement_style);
|
||||||
));
|
} else {
|
||||||
|
logging::error!("Thaw-Binder: get_follower_placement_style return None");
|
||||||
|
}
|
||||||
|
|
||||||
content_style.set(style);
|
content_style.set(style);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -149,7 +163,9 @@ fn FollowerContainer<El: ElementDescriptor + Clone + 'static>(
|
||||||
}
|
}
|
||||||
let is_show = show.get();
|
let is_show = show.get();
|
||||||
if is_show {
|
if is_show {
|
||||||
sync_position.call(());
|
request_animation_frame(move || {
|
||||||
|
sync_position.call(());
|
||||||
|
});
|
||||||
add_scroll_listener.call(sync_position);
|
add_scroll_listener.call(sync_position);
|
||||||
add_resize_listener.call(sync_position);
|
add_resize_listener.call(sync_position);
|
||||||
} else {
|
} else {
|
||||||
|
|
Loading…
Add table
Reference in a new issue