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 super::switch_version::SwitchVersion;
|
||||||
use leptos::{prelude::*, ev};
|
use leptos::{ev, prelude::*};
|
||||||
use leptos_meta::Style;
|
use leptos_meta::Style;
|
||||||
use leptos_router::hooks::use_navigate;
|
use leptos_router::hooks::use_navigate;
|
||||||
// use leptos_use::{storage::use_local_storage, utils::FromToStringCodec};
|
// use leptos_use::{storage::use_local_storage, utils::FromToStringCodec};
|
||||||
|
@ -7,6 +7,7 @@ use thaw::*;
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn SiteHeader() -> impl IntoView {
|
pub fn SiteHeader() -> impl IntoView {
|
||||||
|
let navigate = use_navigate();
|
||||||
let theme = Theme::use_rw_theme();
|
let theme = Theme::use_rw_theme();
|
||||||
let theme_name = Memo::new(move |_| {
|
let theme_name = Memo::new(move |_| {
|
||||||
theme.with(|theme| {
|
theme.with(|theme| {
|
||||||
|
@ -65,9 +66,11 @@ pub fn SiteHeader() -> impl IntoView {
|
||||||
.collect()
|
.collect()
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
let on_search_select = move |path: String| {
|
let on_search_select = {
|
||||||
let navigate = use_navigate();
|
let navigate = navigate.clone();
|
||||||
navigate(&path, Default::default());
|
move |path: String| {
|
||||||
|
navigate(&path, Default::default());
|
||||||
|
}
|
||||||
};
|
};
|
||||||
let auto_complete_ref = ComponentRef::<AutoCompleteRef>::new();
|
let auto_complete_ref = ComponentRef::<AutoCompleteRef>::new();
|
||||||
let handle = window_event_listener(ev::keydown, move |event| {
|
let handle = window_event_listener(ev::keydown, move |event| {
|
||||||
|
@ -132,7 +135,6 @@ pub fn SiteHeader() -> impl IntoView {
|
||||||
</Style>
|
</Style>
|
||||||
<LayoutHeader attr:class=("demo-header", true)>
|
<LayoutHeader attr:class=("demo-header", true)>
|
||||||
<Space on:click=move |_| {
|
<Space on:click=move |_| {
|
||||||
let navigate = use_navigate();
|
|
||||||
navigate("/", Default::default());
|
navigate("/", Default::default());
|
||||||
}>
|
}>
|
||||||
<img src="/logo.svg" style="width: 36px"/>
|
<img src="/logo.svg" style="width: 36px"/>
|
||||||
|
@ -274,5 +276,3 @@ fn gen_search_all_options() -> Vec<AutoCompleteOption> {
|
||||||
|
|
||||||
// menu_value
|
// menu_value
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
use leptos::prelude::*;
|
use leptos::prelude::*;
|
||||||
use leptos_router::hooks::{use_navigate, use_query_map};
|
use leptos_router::hooks::{use_navigate, use_query_map};
|
||||||
// use leptos_router::{use_navigate, use_query_map};
|
|
||||||
use thaw::*;
|
use thaw::*;
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn Home() -> impl IntoView {
|
pub fn Home() -> impl IntoView {
|
||||||
let query_map = use_query_map().get_untracked();
|
let query_map = use_query_map().get_untracked();
|
||||||
|
let navigate = use_navigate();
|
||||||
|
|
||||||
// mobile page
|
// mobile page
|
||||||
if let Some(path) = query_map.get("path") {
|
if let Some(path) = query_map.get("path") {
|
||||||
let navigate = use_navigate();
|
|
||||||
navigate(&path, Default::default());
|
navigate(&path, Default::default());
|
||||||
}
|
}
|
||||||
view! {
|
view! {
|
||||||
|
@ -22,7 +22,6 @@ pub fn Home() -> impl IntoView {
|
||||||
<Button
|
<Button
|
||||||
appearance=ButtonAppearance::Primary
|
appearance=ButtonAppearance::Primary
|
||||||
on_click=move |_| {
|
on_click=move |_| {
|
||||||
let navigate = use_navigate();
|
|
||||||
navigate("/components/button", Default::default());
|
navigate("/components/button", Default::default());
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
|
|
@ -3,7 +3,7 @@ mod auto_complete_option;
|
||||||
pub use auto_complete_option::AutoCompleteOption;
|
pub use auto_complete_option::AutoCompleteOption;
|
||||||
|
|
||||||
use crate::{ComponentRef, ConfigInjection, Input, InputPrefix, InputRef, InputSuffix};
|
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::{
|
use thaw_components::{
|
||||||
Binder, CSSTransition, Follower, FollowerPlacement, FollowerWidth, OptionComp,
|
Binder, CSSTransition, Follower, FollowerPlacement, FollowerWidth, OptionComp,
|
||||||
};
|
};
|
||||||
|
@ -194,10 +194,13 @@ pub fn AutoComplete(
|
||||||
node_ref=menu_ref
|
node_ref=menu_ref
|
||||||
role="listbox"
|
role="listbox"
|
||||||
>
|
>
|
||||||
|
{
|
||||||
<OptionComp value=children let:children>
|
if let Some(children) = children {
|
||||||
{children()}
|
Either::Left(children())
|
||||||
</OptionComp>
|
} else {
|
||||||
|
Either::Right(())
|
||||||
|
}
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</CSSTransition>
|
</CSSTransition>
|
||||||
</Provider>
|
</Provider>
|
||||||
|
|
|
@ -81,117 +81,12 @@ where
|
||||||
children: follower_children,
|
children: follower_children,
|
||||||
} = follower;
|
} = follower;
|
||||||
|
|
||||||
let scroll_listener = StoredValue::new(None::<Callback<()>>);
|
|
||||||
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 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_ref = NodeRef::<html::Div>::new();
|
||||||
let content_style = RwSignal::new(String::new());
|
let content_style = RwSignal::new(String::new());
|
||||||
let placement_str = RwSignal::new(placement.as_str());
|
let placement_str = RwSignal::new(follower_placement.as_str());
|
||||||
let sync_position: Callback<()> = Callback::new(move |_| {
|
let sync_position = move || {
|
||||||
let Some(content_ref) = content_ref.get_untracked() else {
|
let Some(content_ref) = content_ref.get_untracked() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
@ -201,7 +96,7 @@ where
|
||||||
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();
|
||||||
if let Some(width) = width {
|
if let Some(width) = follower_width {
|
||||||
let width = match width {
|
let width = match width {
|
||||||
FollowerWidth::Target => format!("width: {}px;", target_rect.width()),
|
FollowerWidth::Target => format!("width: {}px;", target_rect.width()),
|
||||||
FollowerWidth::MinTarget => format!("min-width: {}px;", target_rect.width()),
|
FollowerWidth::MinTarget => format!("min-width: {}px;", target_rect.width()),
|
||||||
|
@ -214,7 +109,7 @@ where
|
||||||
left,
|
left,
|
||||||
transform,
|
transform,
|
||||||
placement,
|
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());
|
placement_str.set(placement.as_str());
|
||||||
style.push_str(&format!(
|
style.push_str(&format!(
|
||||||
|
@ -229,7 +124,51 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
content_style.set(style);
|
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 |_| {
|
Effect::new(move |_| {
|
||||||
if target_ref.get().is_none() {
|
if target_ref.get().is_none() {
|
||||||
|
@ -238,31 +177,35 @@ where
|
||||||
if content_ref.get().is_none() {
|
if content_ref.get().is_none() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if show.get() {
|
if follower_show.get() {
|
||||||
request_animation_frame({
|
request_animation_frame(move || {
|
||||||
let sync_position = sync_position.clone();
|
sync_position();
|
||||||
move || {
|
|
||||||
sync_position.call(());
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
add_scroll_listener.call(sync_position.clone());
|
|
||||||
add_resize_listener.call(sync_position.clone());
|
remove_listener();
|
||||||
|
ensure_listener();
|
||||||
} else {
|
} else {
|
||||||
remove_scroll_listener.call(());
|
remove_listener();
|
||||||
remove_resize_listener.call(());
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
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-container").child(
|
||||||
html::div()
|
html::div()
|
||||||
.class("thaw-binder-follower-content")
|
.class("thaw-binder-follower-content")
|
||||||
.attr("data-thaw-placement", move || placement_str.get())
|
.attr("data-thaw-placement", move || placement_str.get())
|
||||||
.node_ref(content_ref)
|
.node_ref(content_ref)
|
||||||
.attr("style", move || content_style.get())
|
.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