refactor: Callback

This commit is contained in:
luoxiao 2024-07-19 17:30:46 +08:00
parent 769f69c069
commit c186c8b352
26 changed files with 241 additions and 117 deletions

View file

@ -19,7 +19,7 @@ pub fn SiteHeader() -> impl IntoView {
})
});
// let (_, write_theme, _) = use_local_storage::<String, FromToStringCodec>("theme");
let change_theme = Callback::new(move |_| {
let change_theme = move |_| {
if theme_name.get_untracked() == "Light" {
theme.set(Theme::light());
// write_theme.set("light".to_string());
@ -27,7 +27,7 @@ pub fn SiteHeader() -> impl IntoView {
theme.set(Theme::dark());
// write_theme.set("dark".to_string());
}
});
};
let search_value = RwSignal::new(String::new());
let search_all_options = StoredValue::new(gen_search_all_options());
@ -186,7 +186,7 @@ pub fn SiteHeader() -> impl IntoView {
<Space class="demo-header__right-btn" align=SpaceAlign::Center>
<Button
appearance=ButtonAppearance::Subtle
on_click=Callback::new(move |_| change_theme.call(()))
on_click=change_theme
>
{move || theme_name.get()}
</Button>

View file

@ -4,19 +4,19 @@
let open = RwSignal::new(false);
let position = RwSignal::new(DrawerPosition::Top);
let open_f = Callback::new(move |new_position: DrawerPosition| {
let open_f = move |new_position: DrawerPosition| {
// Note: Since `show` changes are made in real time,
// please put it in front of `show.set(true)` when changing `placement`.
position.set(new_position);
open.set(true);
});
};
view! {
<ButtonGroup>
<Button on_click=Callback::new(move |_| open_f.call(DrawerPosition::Top))>"Top"</Button>
<Button on_click=Callback::new(move |_| open_f.call(DrawerPosition::Right))>"Right"</Button>
<Button on_click=Callback::new(move |_| open_f.call(DrawerPosition::Bottom))>"Bottom"</Button>
<Button on_click=Callback::new(move |_| open_f.call(DrawerPosition::Left))>"Left"</Button>
<Button on_click=move |_| open_f(DrawerPosition::Top)>"Top"</Button>
<Button on_click=move |_| open_f(DrawerPosition::Right)>"Right"</Button>
<Button on_click=move |_| open_f(DrawerPosition::Bottom)>"Bottom"</Button>
<Button on_click=move |_| open_f(DrawerPosition::Left)>"Left"</Button>
</ButtonGroup>
<OverlayDrawer open position>
<DrawerHeader>

View file

@ -70,13 +70,13 @@ view! {
let value = RwSignal::new(String::from("o"));
let input_ref = ComponentRef::<InputRef>::new();
let focus = Callback::new(move |_| {
let focus = move |_| {
input_ref.get_untracked().unwrap().focus()
});
};
let blur = Callback::new(move |_| {
let blur = move |_| {
input_ref.get_untracked().unwrap().blur()
});
};
view! {
<Space vertical=true>

View file

@ -9,9 +9,8 @@ view! {
### Closable
```rust demo
use send_wrapper::SendWrapper;
// let message = use_message();
let success = move |_: SendWrapper<ev::MouseEvent>| {
let success = move |_: ev::MouseEvent| {
// message.create(
// "tag close".into(),
// MessageVariant::Success,

View file

@ -24,10 +24,9 @@ view!{
### Drag to upload
```rust demo
use send_wrapper::SendWrapper;
// let message = use_message();
let custom_request = move |file_list: SendWrapper<FileList>| {
let custom_request = move |file_list: FileList| {
// message.create(
// format!("Number of uploaded files: {}", file_list.length()),
// MessageVariant::Success,

View file

@ -6,7 +6,8 @@ use thaw_utils::{mount_style, update, with, StoredMaybeSignal};
#[component]
pub fn AccordionItem(
/// Required value that identifies this item inside an Accordion component.
#[prop(into)] value: MaybeSignal<String>,
#[prop(into)]
value: MaybeSignal<String>,
accordion_header: AccordionHeader,
children: Children,
) -> impl IntoView {
@ -57,10 +58,10 @@ pub fn AccordionItem(
aria-hidden="true"
width="1em"
height="1em"
viewBox="0 0 20 20"
style=move || if is_show_panel.get() {
viewBox="0 0 20 20"
style=move || if is_show_panel.get() {
"transform: rotate(90deg)"
} else {
} else {
"transform: rotate(0deg)"
}
>

View file

@ -5,7 +5,7 @@ pub use auto_complete_option::AutoCompleteOption;
use crate::{ComponentRef, ConfigInjection, Input, InputPrefix, InputRef, InputSuffix};
use leptos::{context::Provider, either::Either, html, prelude::*};
use thaw_components::{Binder, CSSTransition, Follower, FollowerPlacement, FollowerWidth};
use thaw_utils::{class_list, mount_style, Model, OptionalProp};
use thaw_utils::{class_list, mount_style, BoxOneCallback, Model, OptionalProp};
#[slot]
pub struct AutoCompletePrefix {
@ -23,7 +23,7 @@ pub fn AutoComplete(
#[prop(optional, into)] placeholder: OptionalProp<MaybeSignal<String>>,
#[prop(optional, into)] clear_after_select: MaybeSignal<bool>,
#[prop(optional, into)] blur_after_select: MaybeSignal<bool>,
#[prop(optional, into)] on_select: Option<Callback<String>>,
#[prop(optional, into)] on_select: Option<BoxOneCallback<String>>,
#[prop(optional, into)] disabled: MaybeSignal<bool>,
#[prop(optional, into)] allow_free_input: bool,
#[prop(optional, into)] invalid: MaybeSignal<bool>,
@ -60,8 +60,8 @@ pub fn AutoComplete(
} else {
value.set(option_value.clone());
}
if let Some(on_select) = on_select {
on_select.call(option_value);
if let Some(on_select) = on_select.as_ref() {
on_select(option_value);
}
if allow_free_input {
select_option_index.set(None);

View file

@ -2,8 +2,8 @@ use crate::{ConfigInjection, Icon};
use leptos::{either::Either, ev, html, prelude::*};
use thaw_components::{CSSTransition, Teleport};
use thaw_utils::{
add_event_listener, class_list, get_scroll_parent, mount_style, EventListenerHandle,
OptionalProp,
add_event_listener, class_list, get_scroll_parent, mount_style, BoxCallback,
EventListenerHandle, OptionalProp,
};
#[component]
@ -28,7 +28,7 @@ pub fn BackTop(
}
});
let scroll_to_top = StoredValue::new(None::<Callback<()>>);
let scroll_to_top = StoredValue::new(None::<BoxCallback>);
let scroll_handle = StoredValue::new(None::<EventListenerHandle>);
Effect::new(move |_| {
@ -42,7 +42,7 @@ pub fn BackTop(
{
let scroll_el = send_wrapper::SendWrapper::new(scroll_el.clone());
scroll_to_top.set_value(Some(Callback::new(move |_| {
scroll_to_top.set_value(Some(BoxCallback::new(move || {
scroll_el.scroll_to_with_scroll_to_options(
web_sys::ScrollToOptions::new()
.top(0.0)
@ -69,7 +69,7 @@ pub fn BackTop(
let on_click = move |_| {
scroll_to_top.with_value(|scroll_to_top| {
if let Some(scroll_to_top) = scroll_to_top {
scroll_to_top.call(());
scroll_to_top();
}
});
};

View file

@ -4,8 +4,7 @@ pub use button_group::ButtonGroup;
use crate::icon::Icon;
use leptos::{either::Either, ev, prelude::*};
use send_wrapper::SendWrapper;
use thaw_utils::{class_list, mount_style, OptionalMaybeSignal, OptionalProp};
use thaw_utils::{class_list, mount_style, BoxOneCallback, OptionalMaybeSignal, OptionalProp};
#[derive(Default, PartialEq, Clone, Copy)]
pub enum ButtonAppearance {
@ -73,7 +72,7 @@ pub fn Button(
#[prop(optional, into)] icon: OptionalMaybeSignal<icondata_core::Icon>,
#[prop(optional, into)] disabled: MaybeSignal<bool>,
#[prop(optional, into)] disabled_focusable: MaybeSignal<bool>,
#[prop(optional, into)] on_click: Option<Callback<SendWrapper<ev::MouseEvent>>>,
#[prop(optional, into)] on_click: Option<BoxOneCallback<ev::MouseEvent>>,
#[prop(optional)] children: Option<Children>,
) -> impl IntoView {
mount_style("button", include_str!("./button.css"));
@ -87,10 +86,10 @@ pub fn Button(
return;
}
let Some(callback) = on_click.as_ref() else {
let Some(on_click) = on_click.as_ref() else {
return;
};
callback.call(SendWrapper::new(e));
on_click(e);
};
view! {

View file

@ -34,7 +34,7 @@ pub fn DatePicker(
show_date_text.set(text);
});
let on_input_blur = Callback::new(move |_| {
let on_input_blur = move |_| {
if let Ok(date) =
NaiveDate::parse_from_str(&show_date_text.get_untracked(), show_date_format)
{
@ -45,7 +45,7 @@ pub fn DatePicker(
} else {
update_show_date_text();
}
});
};
let close_panel = Callback::new(move |date: Option<NaiveDate>| {
if value.get_untracked() != date {
@ -57,13 +57,13 @@ pub fn DatePicker(
is_show_panel.set(false);
});
let open_panel = Callback::new(move |_| {
let open_panel = move |_| {
panel_selected_date.set(value.get_untracked());
if let Some(panel_ref) = panel_ref.get_untracked() {
panel_ref.init_panel(value.get_untracked().unwrap_or(now_date()));
}
is_show_panel.set(true);
});
};
view! {
<Binder target_ref=date_picker_ref>

View file

@ -80,9 +80,9 @@ pub fn DatePanel(
*date = *date + Months::new(1);
});
};
let now = Callback::new(move |_| {
let now = move |_| {
close_panel.call(Some(now_date()));
});
};
view! {
<div>
<div class="thaw-date-picker-date-panel__calendar">

View file

@ -5,7 +5,7 @@ mod year_panel;
use crate::ConfigInjection;
use chrono::NaiveDate;
use date_panel::DatePanel;
use leptos::{ev, html, prelude::*};
use leptos::{html, prelude::*};
use month_panel::MonthPanel;
use thaw_components::CSSTransition;
use thaw_utils::{now_date, ComponentRef};
@ -24,7 +24,7 @@ pub fn Panel(
#[cfg(any(feature = "csr", feature = "hydrate"))]
{
use leptos::wasm_bindgen::__rt::IntoJsResult;
let handle = window_event_listener(ev::click, move |ev| {
let handle = window_event_listener(leptos::ev::click, move |ev| {
let el = ev.target();
let mut el: Option<web_sys::Element> =
el.into_js_result().map_or(None, |el| Some(el.into()));

View file

@ -19,9 +19,9 @@ pub fn Dialog(
open.set(false);
}
};
let on_esc = Callback::new(move |_: ev::KeyboardEvent| {
let on_esc = move |_: ev::KeyboardEvent| {
open.set(false);
});
};
view! {
<Teleport immediate=open.signal()>

View file

@ -28,7 +28,7 @@ pub fn OverlayDrawer(
open_drawer.set(is_open);
});
use_lock_html_scroll(is_lock.into());
let on_after_leave = move |_| {
let on_after_leave = move || {
is_lock.set(false);
};
@ -38,9 +38,9 @@ pub fn OverlayDrawer(
open.set(false);
}
};
let on_esc = Callback::new(move |_: ev::KeyboardEvent| {
let on_esc = move |_: ev::KeyboardEvent| {
open.set(false);
});
};
view! {
<Teleport immediate=open.signal()>

View file

@ -1,7 +1,7 @@
// copy https://github.com/Carlosted/leptos-icons
// leptos updated version causes leptos_icons error
use leptos::{ev, prelude::*};
use thaw_utils::{class_list, mount_style};
use thaw_utils::{class_list, mount_style, BoxOneCallback};
/// The Icon component.
#[component]
@ -23,7 +23,7 @@ pub fn Icon(
style: Option<MaybeSignal<String>>,
/// Callback when clicking on the icon.
#[prop(optional, into)]
on_click: Option<Callback<ev::MouseEvent>>,
on_click: Option<BoxOneCallback<ev::MouseEvent>>,
) -> impl IntoView {
mount_style("icon", include_str!("./icon.css"));
@ -41,7 +41,7 @@ pub fn Icon(
let icon_data = RwSignal::new(None);
let on_click = move |ev| {
if let Some(click) = on_click.as_ref() {
click.call(ev);
click(ev);
}
};

View file

@ -1,6 +1,5 @@
use leptos::{ev, html, prelude::*};
use send_wrapper::SendWrapper;
use thaw_utils::{class_list, mount_style, ComponentRef, Model, OptionalProp};
use thaw_utils::{class_list, mount_style, BoxOneCallback, ComponentRef, Model, OptionalProp};
#[derive(Default, Clone)]
pub enum InputVariant {
@ -35,11 +34,11 @@ pub struct InputSuffix {
#[component]
pub fn Input(
#[prop(optional, into)] value: Model<String>,
#[prop(optional, into)] allow_value: Option<Callback<String, bool>>,
#[prop(optional, into)] allow_value: Option<BoxOneCallback<String, bool>>,
#[prop(optional, into)] variant: MaybeSignal<InputVariant>,
#[prop(optional, into)] placeholder: OptionalProp<MaybeSignal<String>>,
#[prop(optional, into)] on_focus: Option<Callback<SendWrapper<ev::FocusEvent>>>,
#[prop(optional, into)] on_blur: Option<Callback<SendWrapper<ev::FocusEvent>>>,
#[prop(optional, into)] on_focus: Option<BoxOneCallback<ev::FocusEvent>>,
#[prop(optional, into)] on_blur: Option<BoxOneCallback<ev::FocusEvent>>,
#[prop(optional, into)] disabled: MaybeSignal<bool>,
#[prop(optional, into)] invalid: MaybeSignal<bool>,
#[prop(optional)] input_prefix: Option<InputPrefix>,
@ -56,7 +55,7 @@ pub fn Input(
move |ev| {
let input_value = event_target_value(&ev);
if let Some(allow_value) = allow_value.as_ref() {
if !allow_value.call(input_value.clone()) {
if !allow_value(input_value.clone()) {
value_trigger.trigger();
return;
}
@ -68,13 +67,13 @@ pub fn Input(
let on_internal_focus = move |ev| {
is_focus.set(true);
if let Some(on_focus) = on_focus.as_ref() {
on_focus.call(SendWrapper::new(ev));
on_focus(ev);
}
};
let on_internal_blur = move |ev| {
is_focus.set(false);
if let Some(on_blur) = on_blur.as_ref() {
on_blur.call(SendWrapper::new(ev));
on_blur(ev);
}
};

View file

@ -1,23 +1,15 @@
use leptos::{either::Either, ev, prelude::*};
use send_wrapper::SendWrapper;
use thaw_utils::{class_list, mount_style, OptionalProp};
use thaw_utils::{class_list, mount_style, ArcOneCallback, OptionalProp};
#[component]
pub fn Tag(
#[prop(optional, into)] class: OptionalProp<MaybeSignal<String>>,
#[prop(optional, into)] closable: MaybeSignal<bool>,
#[prop(optional, into)] on_close: Option<Callback<SendWrapper<ev::MouseEvent>>>,
#[prop(optional, into)] on_close: Option<ArcOneCallback<ev::MouseEvent>>,
children: Children,
) -> impl IntoView {
mount_style("tag", include_str!("./tag.css"));
let on_close = move |event| {
let Some(callback) = on_close.as_ref() else {
return;
};
callback.call(SendWrapper::new(event));
};
view! {
<span
class=class_list!["thaw-tag", ("thaw-tag--closable", move || closable.get()), class.map(| c | move || c.get())]
@ -26,6 +18,13 @@ pub fn Tag(
<span class="thaw-tag__primary-text">{children()}</span>
{move || {
let on_close = on_close.clone();
let on_close = move |event| {
let Some(on_close) = on_close.as_ref() else {
return;
};
on_close(event);
};
if closable.get() {
Either::Left(view! {
<button class="thaw-tag__close" on:click=on_close>

View file

@ -1,13 +1,13 @@
use leptos::{ev, html, prelude::*};
use thaw_utils::{class_list, mount_style, ComponentRef, Model};
use thaw_utils::{class_list, mount_style, BoxOneCallback, ComponentRef, Model};
#[component]
pub fn Textarea(
#[prop(optional, into)] value: Model<String>,
#[prop(optional, into)] allow_value: Option<Callback<String, bool>>,
#[prop(optional, into)] allow_value: Option<BoxOneCallback<String, bool>>,
#[prop(optional, into)] placeholder: MaybeProp<String>,
#[prop(optional, into)] on_focus: Option<Callback<ev::FocusEvent>>,
#[prop(optional, into)] on_blur: Option<Callback<ev::FocusEvent>>,
#[prop(optional, into)] on_focus: Option<BoxOneCallback<ev::FocusEvent>>,
#[prop(optional, into)] on_blur: Option<BoxOneCallback<ev::FocusEvent>>,
#[prop(optional, into)] disabled: MaybeSignal<bool>,
/// Which direction the Textarea is allowed to be resized.
#[prop(optional, into)]
@ -24,7 +24,7 @@ pub fn Textarea(
move |ev| {
let input_value = event_target_value(&ev);
if let Some(allow_value) = allow_value.as_ref() {
if !allow_value.call(input_value.clone()) {
if !allow_value(input_value.clone()) {
value_trigger.trigger();
return;
}
@ -34,12 +34,12 @@ pub fn Textarea(
};
let on_internal_focus = move |ev| {
if let Some(on_focus) = on_focus.as_ref() {
on_focus.call(ev);
on_focus(ev);
}
};
let on_internal_blur = move |ev| {
if let Some(on_blur) = on_blur.as_ref() {
on_blur.call(ev);
on_blur(ev);
}
};

View file

@ -3,7 +3,7 @@ use crate::{
SignalWatch,
};
use chrono::{Local, NaiveTime, Timelike};
use leptos::{ev, html, prelude::*};
use leptos::{html, prelude::*};
use thaw_components::{Binder, CSSTransition, Follower, FollowerPlacement};
use thaw_utils::{mount_style, ComponentRef, Model, OptionalProp};
@ -36,7 +36,7 @@ pub fn TimePicker(
show_time_text.set(text);
});
let on_input_blur = Callback::new(move |_| {
let on_input_blur = move |_| {
if let Ok(time) =
NaiveTime::parse_from_str(&show_time_text.get_untracked(), show_time_format)
{
@ -47,7 +47,7 @@ pub fn TimePicker(
} else {
update_show_time_text();
}
});
};
let close_panel = Callback::new(move |time: Option<NaiveTime>| {
if value.get_untracked() != time {
if time.is_some() {
@ -58,7 +58,7 @@ pub fn TimePicker(
is_show_panel.set(false);
});
let open_panel = Callback::new(move |_| {
let open_panel = move |_| {
panel_selected_time.set(value.get_untracked());
is_show_panel.set(true);
request_animation_frame(move || {
@ -66,7 +66,7 @@ pub fn TimePicker(
panel_ref.scroll_into_view();
}
});
});
};
view! {
<Binder target_ref=time_picker_ref>
@ -99,18 +99,18 @@ fn Panel(
comp_ref: ComponentRef<PanelRef>,
) -> impl IntoView {
let config_provider = ConfigInjection::use_();
let now = Callback::new(move |_| {
let now = move |_| {
close_panel.call(Some(now_time()));
});
let ok = Callback::new(move |_| {
};
let ok = move |_| {
close_panel.call(selected_time.get_untracked());
});
};
let panel_ref = NodeRef::<html::Div>::new();
#[cfg(any(feature = "csr", feature = "hydrate"))]
{
use leptos::wasm_bindgen::__rt::IntoJsResult;
let handle = window_event_listener(ev::click, move |ev| {
let handle = window_event_listener(leptos::ev::click, move |ev| {
let el = ev.target();
let mut el: Option<web_sys::Element> =
el.into_js_result().map_or(None, |el| Some(el.into()));

View file

@ -190,13 +190,13 @@ fn ToasterContainer(
);
}
let on_before_leave = move |_| {
let on_before_leave = move || {
let Some(el) = container_ref.get_untracked() else {
return;
};
el.style(("max-height", format!("{}px", el.offset_height())));
};
let on_after_leave = move |_| {
let on_after_leave = move || {
request_animation_frame(move || on_close.call((id, position)));
};

View file

@ -4,14 +4,13 @@ pub use upload_dragger::UploadDragger;
pub use web_sys::FileList;
use leptos::{ev, html, prelude::*};
use send_wrapper::SendWrapper;
use thaw_utils::{add_event_listener, mount_style};
use thaw_utils::{add_event_listener, mount_style, ArcOneCallback};
#[component]
pub fn Upload(
#[prop(optional, into)] accept: MaybeSignal<String>,
#[prop(optional, into)] multiple: MaybeSignal<bool>,
#[prop(optional, into)] custom_request: Option<Callback<SendWrapper<FileList>>>,
#[prop(optional, into)] custom_request: Option<ArcOneCallback<FileList>>,
children: Children,
) -> impl IntoView {
mount_style("upload", include_str!("./upload.css"));
@ -34,17 +33,20 @@ pub fn Upload(
});
let on_file_addition = move |files: FileList| {
if let Some(custom_request) = custom_request {
custom_request.call(SendWrapper::new(files));
if let Some(custom_request) = custom_request.as_ref() {
custom_request(files);
}
};
let on_change = move |_| {
if let Some(input_ref) = input_ref.get_untracked() {
if let Some(files) = input_ref.files() {
on_file_addition(files);
let on_change = {
let on_file_addition = on_file_addition.clone();
move |_| {
if let Some(input_ref) = input_ref.get_untracked() {
if let Some(files) = input_ref.files() {
on_file_addition(files);
}
input_ref.set_value("");
}
input_ref.set_value("");
}
};

View file

@ -1,6 +1,6 @@
use leptos::{ev, html::ElementType, prelude::*};
use std::{ops::Deref, time::Duration};
use thaw_utils::{add_event_listener, EventListenerHandle, NextFrame};
use thaw_utils::{add_event_listener, ArcCallback, EventListenerHandle, NextFrame};
use web_sys::wasm_bindgen::JsCast;
/// # CSS Transition
@ -12,12 +12,12 @@ pub fn CSSTransition<E, CF, IV>(
#[prop(into)] show: MaybeSignal<bool>,
#[prop(into)] name: MaybeSignal<String>,
#[prop(optional)] appear: bool,
#[prop(optional, into)] on_before_enter: Option<Callback<()>>,
#[prop(optional, into)] on_enter: Option<Callback<()>>,
#[prop(optional, into)] on_after_enter: Option<Callback<()>>,
#[prop(optional, into)] on_before_leave: Option<Callback<()>>,
#[prop(optional, into)] on_leave: Option<Callback<()>>,
#[prop(optional, into)] on_after_leave: Option<Callback<()>>,
#[prop(optional, into)] on_before_enter: Option<ArcCallback>,
#[prop(optional, into)] on_enter: Option<ArcCallback>,
#[prop(optional, into)] on_after_enter: Option<ArcCallback>,
#[prop(optional, into)] on_before_leave: Option<ArcCallback>,
#[prop(optional, into)] on_leave: Option<ArcCallback>,
#[prop(optional, into)] on_after_leave: Option<ArcCallback>,
children: CF,
) -> impl IntoView
where
@ -114,6 +114,12 @@ where
};
let name = name.clone();
let on_before_enter = on_before_enter.clone();
let on_enter = on_enter.clone();
let on_after_enter = on_after_enter.clone();
let on_before_leave = on_before_leave.clone();
let on_leave = on_leave.clone();
let on_after_leave = on_after_leave.clone();
let effect = RenderEffect::new(move |prev: Option<bool>| {
let show = show.get();
let prev = if let Some(prev) = prev {
@ -131,7 +137,7 @@ where
{
// on_enter
if let Some(on_before_enter) = on_before_enter.as_ref() {
on_before_enter.call(());
on_before_enter();
}
let enter_from = format!("{name}-enter-from");
let enter_active = format!("{name}-enter-active");
@ -142,6 +148,8 @@ where
let class_list = class_list.clone();
let on_end = on_end.clone();
let on_enter = on_enter.clone();
let on_after_enter = on_after_enter.clone();
next_frame.run(move || {
let _ = class_list.remove_1(&enter_from);
let _ = class_list.add_1(&enter_to);
@ -150,13 +158,13 @@ where
let remove = Box::new(move || {
let _ = class_list.remove_2(&enter_active, &enter_to);
if let Some(on_after_enter) = on_after_enter.as_ref() {
on_after_enter.call(());
on_after_enter();
}
});
on_end(remove);
if let Some(on_enter) = on_enter.as_ref() {
on_enter.call(());
on_enter();
}
});
}
@ -165,7 +173,7 @@ where
{
// on_leave
if let Some(on_before_leave) = on_before_leave.as_ref() {
on_before_leave.call(());
on_before_leave();
}
let leave_from = format!("{name}-leave-from");
let leave_active = format!("{name}-leave-active");
@ -174,9 +182,9 @@ where
let _ = class_list.add_2(&leave_from, &leave_active);
let class_list = class_list.clone();
let on_after_leave = on_after_leave.clone();
let on_end = on_end.clone();
let on_leave = on_leave.clone();
let on_after_leave = on_after_leave.clone();
next_frame.run(move || {
let _ = class_list.remove_1(&leave_from);
let _ = class_list.add_1(&leave_to);
@ -186,12 +194,12 @@ where
let _ = class_list.remove_2(&leave_active, &leave_to);
display.set(Some("display: none;"));
if let Some(on_after_leave) = on_after_leave.as_ref() {
on_after_leave.call(());
on_after_leave();
}
});
on_end(remove);
if let Some(on_leave) = on_leave {
on_leave.call(());
on_leave();
}
});
}

View file

@ -1,4 +1,5 @@
use leptos::{ev, prelude::*};
use thaw_utils::ArcOneCallback;
#[cfg(any(feature = "csr", feature = "hydrate"))]
thread_local! {
@ -9,7 +10,7 @@ thread_local! {
pub fn FocusTrap(
disabled: bool,
#[prop(into)] active: MaybeSignal<bool>,
#[prop(into)] on_esc: Callback<ev::KeyboardEvent>,
#[prop(into)] on_esc: ArcOneCallback<ev::KeyboardEvent>,
children: Children,
) -> impl IntoView {
#[cfg(any(feature = "csr", feature = "hydrate"))]
@ -36,7 +37,7 @@ pub fn FocusTrap(
let handle = window_event_listener(ev::keydown, move |e| {
if &e.code() == "Escape" {
if is_current_active() {
on_esc.call(e);
on_esc(e);
}
}
});

115
thaw_utils/src/callback.rs Normal file
View file

@ -0,0 +1,115 @@
use std::{ops::Deref, sync::Arc};
pub struct BoxCallback(Box<dyn Fn() + Send + Sync + 'static>);
impl BoxCallback {
pub fn new<F>(f: F) -> Self
where
F: Fn() + Send + Sync + 'static,
{
Self(Box::new(f))
}
}
impl Deref for BoxCallback {
type Target = Box<dyn Fn() + Send + Sync + 'static>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<F> From<F> for BoxCallback
where
F: Fn() + Send + Sync + 'static,
{
fn from(value: F) -> Self {
Self::new(value)
}
}
pub struct BoxOneCallback<A, Return = ()>(Box<dyn Fn(A) -> Return + Send + Sync + 'static>);
impl<A, Return> BoxOneCallback<A, Return> {
pub fn new<F>(f: F) -> Self
where
F: Fn(A) -> Return + Send + Sync + 'static,
{
Self(Box::new(f))
}
}
impl<A, Return> Deref for BoxOneCallback<A, Return> {
type Target = Box<dyn Fn(A) -> Return + Send + Sync + 'static>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<F, A, Return> From<F> for BoxOneCallback<A, Return>
where
F: Fn(A) -> Return + Send + Sync + 'static,
{
fn from(value: F) -> Self {
Self::new(value)
}
}
#[derive(Clone)]
pub struct ArcCallback(Arc<dyn Fn() + Send + Sync + 'static>);
impl ArcCallback {
pub fn new<F>(f: F) -> Self
where
F: Fn() + Send + Sync + 'static,
{
Self(Arc::new(f))
}
}
impl Deref for ArcCallback {
type Target = Arc<dyn Fn() + Send + Sync + 'static>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<F> From<F> for ArcCallback
where
F: Fn() + Send + Sync + 'static,
{
fn from(value: F) -> Self {
Self::new(value)
}
}
#[derive(Clone)]
pub struct ArcOneCallback<A>(Arc<dyn Fn(A) + Send + Sync + 'static>);
impl<A> ArcOneCallback<A> {
pub fn new<F>(f: F) -> Self
where
F: Fn(A) + Send + Sync + 'static,
{
Self(Arc::new(f))
}
}
impl<A> Deref for ArcOneCallback<A> {
type Target = Arc<dyn Fn(A) + Send + Sync + 'static>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<F, A> From<F> for ArcOneCallback<A>
where
F: Fn(A) + Send + Sync + 'static,
{
fn from(value: F) -> Self {
Self::new(value)
}
}

View file

@ -7,8 +7,10 @@ mod optional_prop;
mod signals;
mod throttle;
mod time;
mod callback;
pub use dom::*;
pub use callback::*;
pub use event_listener::{add_event_listener, add_event_listener_with_bool, EventListenerHandle};
pub use hooks::{use_click_position, use_lock_html_scroll, NextFrame};
pub use optional_prop::OptionalProp;

View file

@ -1,8 +1,8 @@
use leptos::{leptos_dom::helpers::TimeoutHandle, prelude::*};
use std::time::Duration;
use std::{sync::Arc, time::Duration};
pub fn throttle(cb: impl Fn() + Send + Sync + 'static, duration: Duration) -> impl Fn() -> () {
let cb = Callback::new(move |_| cb());
let cb = Arc::new(cb);
let timeout_handle = StoredValue::new(None::<TimeoutHandle>);
on_cleanup(move || {
timeout_handle.update_value(move |handle| {
@ -19,7 +19,7 @@ pub fn throttle(cb: impl Fn() + Send + Sync + 'static, duration: Duration) -> im
let cb = cb.clone();
let handle = set_timeout_with_handle(
move || {
cb.call(());
cb();
timeout_handle.update_value(move |handle| {
*handle = None;
});