mirror of
https://github.com/adoyle0/thaw.git
synced 2025-01-22 22:09:22 -05:00
feat(lepots-v0.7): Binder
This commit is contained in:
parent
54b23447ee
commit
4262493f61
4 changed files with 83 additions and 138 deletions
|
@ -1,5 +1,5 @@
|
|||
use super::switch_version::SwitchVersion;
|
||||
use leptos::{prelude::*, ev};
|
||||
use leptos::{ev, prelude::*};
|
||||
use leptos_meta::Style;
|
||||
use leptos_router::hooks::use_navigate;
|
||||
// use leptos_use::{storage::use_local_storage, utils::FromToStringCodec};
|
||||
|
@ -7,6 +7,7 @@ use thaw::*;
|
|||
|
||||
#[component]
|
||||
pub fn SiteHeader() -> impl IntoView {
|
||||
let navigate = use_navigate();
|
||||
let theme = Theme::use_rw_theme();
|
||||
let theme_name = Memo::new(move |_| {
|
||||
theme.with(|theme| {
|
||||
|
@ -65,9 +66,11 @@ pub fn SiteHeader() -> impl IntoView {
|
|||
.collect()
|
||||
})
|
||||
});
|
||||
let on_search_select = move |path: String| {
|
||||
let navigate = use_navigate();
|
||||
let on_search_select = {
|
||||
let navigate = navigate.clone();
|
||||
move |path: String| {
|
||||
navigate(&path, Default::default());
|
||||
}
|
||||
};
|
||||
let auto_complete_ref = ComponentRef::<AutoCompleteRef>::new();
|
||||
let handle = window_event_listener(ev::keydown, move |event| {
|
||||
|
@ -132,7 +135,6 @@ pub fn SiteHeader() -> impl IntoView {
|
|||
</Style>
|
||||
<LayoutHeader attr:class=("demo-header", true)>
|
||||
<Space on:click=move |_| {
|
||||
let navigate = use_navigate();
|
||||
navigate("/", Default::default());
|
||||
}>
|
||||
<img src="/logo.svg" style="width: 36px"/>
|
||||
|
@ -274,5 +276,3 @@ fn gen_search_all_options() -> Vec<AutoCompleteOption> {
|
|||
|
||||
// menu_value
|
||||
// }
|
||||
|
||||
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
use leptos::prelude::*;
|
||||
use leptos_router::hooks::{use_navigate, use_query_map};
|
||||
// use leptos_router::{use_navigate, use_query_map};
|
||||
use thaw::*;
|
||||
|
||||
#[component]
|
||||
pub fn Home() -> impl IntoView {
|
||||
let query_map = use_query_map().get_untracked();
|
||||
let navigate = use_navigate();
|
||||
|
||||
// mobile page
|
||||
if let Some(path) = query_map.get("path") {
|
||||
let navigate = use_navigate();
|
||||
navigate(&path, Default::default());
|
||||
}
|
||||
view! {
|
||||
|
@ -22,7 +22,6 @@ pub fn Home() -> impl IntoView {
|
|||
<Button
|
||||
appearance=ButtonAppearance::Primary
|
||||
on_click=move |_| {
|
||||
let navigate = use_navigate();
|
||||
navigate("/components/button", Default::default());
|
||||
}
|
||||
>
|
||||
|
|
|
@ -3,7 +3,7 @@ mod auto_complete_option;
|
|||
pub use auto_complete_option::AutoCompleteOption;
|
||||
|
||||
use crate::{ComponentRef, ConfigInjection, Input, InputPrefix, InputRef, InputSuffix};
|
||||
use leptos::{context::Provider, html, prelude::*};
|
||||
use leptos::{context::Provider, either::Either, html, prelude::*};
|
||||
use thaw_components::{
|
||||
Binder, CSSTransition, Follower, FollowerPlacement, FollowerWidth, OptionComp,
|
||||
};
|
||||
|
@ -194,10 +194,13 @@ pub fn AutoComplete(
|
|||
node_ref=menu_ref
|
||||
role="listbox"
|
||||
>
|
||||
|
||||
<OptionComp value=children let:children>
|
||||
{children()}
|
||||
</OptionComp>
|
||||
{
|
||||
if let Some(children) = children {
|
||||
Either::Left(children())
|
||||
} else {
|
||||
Either::Right(())
|
||||
}
|
||||
}
|
||||
</div>
|
||||
</CSSTransition>
|
||||
</Provider>
|
||||
|
|
|
@ -81,117 +81,12 @@ where
|
|||
children: follower_children,
|
||||
} = follower;
|
||||
|
||||
let scroll_listener = StoredValue::new(None::<Callback<()>>);
|
||||
let scrollable_element_handle_vec = StoredValue::<Vec<EventListenerHandle>>::new(vec![]);
|
||||
let resize_handle = StoredValue::new(None::<WindowListenerHandle>);
|
||||
|
||||
let ensure_scroll_listener = Callback::new(move |_: ()| {
|
||||
let target_ref = target_ref.get_untracked();
|
||||
let Some(el) = target_ref.as_deref() else {
|
||||
return;
|
||||
};
|
||||
|
||||
let mut handle_vec = vec![];
|
||||
let mut cursor = get_scroll_parent(&el);
|
||||
loop {
|
||||
if let Some(el) = cursor.take() {
|
||||
cursor = get_scroll_parent(&el);
|
||||
|
||||
let handle = add_event_listener(el, ev::scroll, move |_| {
|
||||
if let Some(scroll_listener) = scroll_listener.get_value() {
|
||||
scroll_listener.call(());
|
||||
}
|
||||
});
|
||||
handle_vec.push(handle);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
scrollable_element_handle_vec.set_value(handle_vec);
|
||||
});
|
||||
|
||||
let add_scroll_listener = Callback::new(move |listener: Callback<()>| {
|
||||
scroll_listener.update_value(|scroll_listener| {
|
||||
if scroll_listener.is_none() {
|
||||
ensure_scroll_listener.call(());
|
||||
}
|
||||
*scroll_listener = Some(listener);
|
||||
})
|
||||
});
|
||||
|
||||
let remove_scroll_listener = Callback::new(move |_| {
|
||||
scrollable_element_handle_vec.update_value(|vec| {
|
||||
vec.drain(..).for_each(|handle| handle.remove());
|
||||
});
|
||||
scroll_listener.set_value(None);
|
||||
});
|
||||
|
||||
let add_resize_listener = Callback::new(move |listener: Callback<()>| {
|
||||
resize_handle.update_value(move |resize_handle| {
|
||||
if let Some(handle) = resize_handle.take() {
|
||||
handle.remove();
|
||||
}
|
||||
let handle = window_event_listener(ev::resize, move |_| {
|
||||
listener.call(());
|
||||
});
|
||||
|
||||
*resize_handle = Some(handle);
|
||||
});
|
||||
});
|
||||
|
||||
let remove_resize_listener = Callback::new(move |_| {
|
||||
resize_handle.update_value(move |handle| {
|
||||
if let Some(handle) = handle.take() {
|
||||
handle.remove();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
on_cleanup({
|
||||
let remove_scroll_listener = remove_scroll_listener.clone();
|
||||
let remove_resize_listener = remove_resize_listener.clone();
|
||||
move || {
|
||||
remove_scroll_listener.call(());
|
||||
remove_resize_listener.call(());
|
||||
}
|
||||
});
|
||||
view! {
|
||||
{children()}
|
||||
<FollowerContainer
|
||||
show=follower_show
|
||||
target_ref
|
||||
width=follower_width
|
||||
placement=follower_placement
|
||||
add_scroll_listener
|
||||
remove_scroll_listener
|
||||
add_resize_listener
|
||||
remove_resize_listener
|
||||
>
|
||||
{follower_children()}
|
||||
</FollowerContainer>
|
||||
}
|
||||
}
|
||||
|
||||
#[component]
|
||||
fn FollowerContainer<E>(
|
||||
show: MaybeSignal<bool>,
|
||||
target_ref: NodeRef<E>,
|
||||
width: Option<FollowerWidth>,
|
||||
placement: FollowerPlacement,
|
||||
#[prop(into)] add_scroll_listener: Callback<Callback<()>>,
|
||||
#[prop(into)] remove_scroll_listener: Callback<()>,
|
||||
#[prop(into)] add_resize_listener: Callback<Callback<()>>,
|
||||
#[prop(into)] remove_resize_listener: Callback<()>,
|
||||
children: Children,
|
||||
) -> impl IntoView
|
||||
where
|
||||
E: ElementType + 'static,
|
||||
E::Output: JsCast + Clone + Deref<Target = web_sys::HtmlElement> + 'static,
|
||||
{
|
||||
let content_ref = NodeRef::<html::Div>::new();
|
||||
let content_style = RwSignal::new(String::new());
|
||||
let placement_str = RwSignal::new(placement.as_str());
|
||||
let sync_position: Callback<()> = Callback::new(move |_| {
|
||||
let placement_str = RwSignal::new(follower_placement.as_str());
|
||||
let sync_position = move || {
|
||||
let Some(content_ref) = content_ref.get_untracked() else {
|
||||
return;
|
||||
};
|
||||
|
@ -201,7 +96,7 @@ where
|
|||
let target_rect = target_ref.get_bounding_client_rect();
|
||||
let content_rect = content_ref.get_bounding_client_rect();
|
||||
let mut style = String::new();
|
||||
if let Some(width) = width {
|
||||
if let Some(width) = follower_width {
|
||||
let width = match width {
|
||||
FollowerWidth::Target => format!("width: {}px;", target_rect.width()),
|
||||
FollowerWidth::MinTarget => format!("min-width: {}px;", target_rect.width()),
|
||||
|
@ -214,7 +109,7 @@ where
|
|||
left,
|
||||
transform,
|
||||
placement,
|
||||
}) = get_follower_placement_offset(placement, target_rect, content_rect)
|
||||
}) = get_follower_placement_offset(follower_placement, target_rect, content_rect)
|
||||
{
|
||||
placement_str.set(placement.as_str());
|
||||
style.push_str(&format!(
|
||||
|
@ -229,7 +124,51 @@ where
|
|||
}
|
||||
|
||||
content_style.set(style);
|
||||
};
|
||||
|
||||
let ensure_listener = move || {
|
||||
let target_ref = target_ref.get_untracked();
|
||||
let Some(el) = target_ref.as_deref() else {
|
||||
return;
|
||||
};
|
||||
|
||||
let mut handle_vec = vec![];
|
||||
let mut cursor = get_scroll_parent(&el);
|
||||
loop {
|
||||
if let Some(el) = cursor.take() {
|
||||
cursor = get_scroll_parent(&el);
|
||||
|
||||
let handle = add_event_listener(el, ev::scroll, move |_| {
|
||||
sync_position();
|
||||
});
|
||||
handle_vec.push(handle);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
scrollable_element_handle_vec.set_value(handle_vec);
|
||||
|
||||
resize_handle.update_value(move |resize_handle| {
|
||||
if let Some(handle) = resize_handle.take() {
|
||||
handle.remove();
|
||||
}
|
||||
let handle = window_event_listener(ev::resize, move |_| {
|
||||
sync_position();
|
||||
});
|
||||
*resize_handle = Some(handle);
|
||||
});
|
||||
};
|
||||
|
||||
let remove_listener = move || {
|
||||
scrollable_element_handle_vec.update_value(|vec| {
|
||||
vec.drain(..).for_each(|handle| handle.remove());
|
||||
});
|
||||
resize_handle.update_value(move |handle| {
|
||||
if let Some(handle) = handle.take() {
|
||||
handle.remove();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
Effect::new(move |_| {
|
||||
if target_ref.get().is_none() {
|
||||
|
@ -238,31 +177,35 @@ where
|
|||
if content_ref.get().is_none() {
|
||||
return;
|
||||
}
|
||||
if show.get() {
|
||||
request_animation_frame({
|
||||
let sync_position = sync_position.clone();
|
||||
move || {
|
||||
sync_position.call(());
|
||||
}
|
||||
if follower_show.get() {
|
||||
request_animation_frame(move || {
|
||||
sync_position();
|
||||
});
|
||||
add_scroll_listener.call(sync_position.clone());
|
||||
add_resize_listener.call(sync_position.clone());
|
||||
|
||||
remove_listener();
|
||||
ensure_listener();
|
||||
} else {
|
||||
remove_scroll_listener.call(());
|
||||
remove_resize_listener.call(());
|
||||
remove_listener();
|
||||
}
|
||||
});
|
||||
|
||||
let children = with_hydration_off(|| {
|
||||
on_cleanup(move || {
|
||||
remove_listener();
|
||||
});
|
||||
|
||||
let teleport_children = with_hydration_off(|| {
|
||||
html::div().class("thaw-binder-follower-container").child(
|
||||
html::div()
|
||||
.class("thaw-binder-follower-content")
|
||||
.attr("data-thaw-placement", move || placement_str.get())
|
||||
.node_ref(content_ref)
|
||||
.attr("style", move || content_style.get())
|
||||
.child(children()),
|
||||
.child(follower_children()),
|
||||
)
|
||||
});
|
||||
|
||||
view! { <Teleport element=children.into_any() immediate=show/> }
|
||||
view! {
|
||||
{children()}
|
||||
<Teleport element=teleport_children.into_any() immediate=follower_show/>
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue