mirror of
https://github.com/adoyle0/thaw.git
synced 2025-01-22 22:09:22 -05:00
thaw_utils: upgrade leptosv0.7
This commit is contained in:
parent
99f585440b
commit
42d76917d0
20 changed files with 362 additions and 355 deletions
|
@ -12,7 +12,7 @@ license = "MIT"
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
leptos = { version = "0.6.10" }
|
||||
leptos = { workspace = true }
|
||||
thaw_utils = { workspace = true }
|
||||
web-sys = { version = "0.3.69", features = ["DomRect"] }
|
||||
cfg-if = "1.0.0"
|
||||
|
|
|
@ -18,6 +18,7 @@ web-sys = "0.3.69"
|
|||
wasm-bindgen = "0.2.92"
|
||||
cfg-if = "1.0.0"
|
||||
chrono = "0.4.35"
|
||||
send_wrapper = "0.6"
|
||||
|
||||
[features]
|
||||
csr = ["leptos/csr"]
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
#[cfg(not(feature = "ssr"))]
|
||||
use leptos::create_render_effect;
|
||||
use leptos::prelude::RenderEffect;
|
||||
use leptos::{
|
||||
Attribute, IntoAttribute, MaybeProp, Memo, Oco, RwSignal, SignalGet, SignalUpdate, SignalWith,
|
||||
prelude::{MaybeProp, Memo, Oco, RwSignal},
|
||||
reactive_graph::traits::{Get, Update, With, WithUntracked},
|
||||
tachys::renderer::DomRenderer,
|
||||
};
|
||||
use std::{collections::HashSet, rc::Rc};
|
||||
use std::collections::HashSet;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ClassList(RwSignal<HashSet<Oco<'static, str>>>);
|
||||
|
||||
impl ClassList {
|
||||
|
@ -30,7 +33,7 @@ impl ClassList {
|
|||
});
|
||||
}
|
||||
#[cfg(not(feature = "ssr"))]
|
||||
create_render_effect(move |old_name| {
|
||||
RenderEffect::new(move |old_name| {
|
||||
let name = f();
|
||||
if let Some(old_name) = old_name {
|
||||
if old_name != name {
|
||||
|
@ -57,7 +60,7 @@ impl ClassList {
|
|||
}
|
||||
}
|
||||
#[cfg(not(feature = "ssr"))]
|
||||
create_render_effect(move |old_name| {
|
||||
RenderEffect::new(move |old_name| {
|
||||
let name = f();
|
||||
if let Some(old_name) = old_name {
|
||||
if old_name != name {
|
||||
|
@ -96,7 +99,7 @@ impl ClassList {
|
|||
});
|
||||
}
|
||||
#[cfg(not(feature = "ssr"))]
|
||||
create_render_effect(move |old| {
|
||||
RenderEffect::new(move |old| {
|
||||
let name = name.clone();
|
||||
let new = f();
|
||||
if old.is_none() {
|
||||
|
@ -121,13 +124,9 @@ impl ClassList {
|
|||
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoAttribute for ClassList {
|
||||
fn into_attribute(self) -> Attribute {
|
||||
Attribute::Fn(Rc::new(move || {
|
||||
fn to_class_string(self, class: &mut String) {
|
||||
self.0.with(|set| {
|
||||
let mut class = String::new();
|
||||
set.iter().enumerate().for_each(|(index, name)| {
|
||||
if name.is_empty() {
|
||||
return;
|
||||
|
@ -137,13 +136,77 @@ impl IntoAttribute for ClassList {
|
|||
}
|
||||
class.push_str(name)
|
||||
});
|
||||
class.into_attribute()
|
||||
})
|
||||
}))
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn into_attribute_boxed(self: Box<Self>) -> Attribute {
|
||||
self.into_attribute()
|
||||
impl<'a, R> leptos::tachys::html::class::IntoClass<R> for ClassList
|
||||
where
|
||||
R: DomRenderer,
|
||||
{
|
||||
type State = (R::ClassList, String);
|
||||
type Cloneable = Self;
|
||||
type CloneableOwned = Self;
|
||||
|
||||
fn html_len(&self) -> usize {
|
||||
self.0.with_untracked(|set| {
|
||||
let mut len = 0;
|
||||
set.iter().enumerate().for_each(|(index, name)| {
|
||||
if name.is_empty() {
|
||||
return;
|
||||
}
|
||||
if index != 0 {
|
||||
len += 1;
|
||||
}
|
||||
len += name.len();
|
||||
});
|
||||
|
||||
len
|
||||
})
|
||||
}
|
||||
|
||||
fn to_html(self, class: &mut String) {
|
||||
self.to_class_string(class);
|
||||
}
|
||||
|
||||
fn hydrate<const FROM_SERVER: bool>(self, el: &R::Element) -> Self::State {
|
||||
let class_list = R::class_list(el);
|
||||
let mut class = String::new();
|
||||
self.to_class_string(&mut class);
|
||||
|
||||
if !FROM_SERVER {
|
||||
R::add_class(&class_list, &class);
|
||||
}
|
||||
(class_list, class)
|
||||
}
|
||||
|
||||
fn build(self, el: &R::Element) -> Self::State {
|
||||
let class_list = R::class_list(el);
|
||||
let mut class = String::new();
|
||||
self.to_class_string(&mut class);
|
||||
if !class.is_empty() {
|
||||
R::add_class(&class_list, &class);
|
||||
}
|
||||
(class_list, class)
|
||||
}
|
||||
|
||||
fn rebuild(self, state: &mut Self::State) {
|
||||
let mut class = String::new();
|
||||
self.to_class_string(&mut class);
|
||||
let (class_list, prev_class) = state;
|
||||
if class != *prev_class {
|
||||
R::remove_class(class_list, prev_class);
|
||||
R::add_class(class_list, &class);
|
||||
}
|
||||
*prev_class = class;
|
||||
}
|
||||
|
||||
fn into_cloneable(self) -> Self::Cloneable {
|
||||
self
|
||||
}
|
||||
|
||||
fn into_cloneable_owned(self) -> Self::CloneableOwned {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -245,21 +308,22 @@ macro_rules! class_list {
|
|||
};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use leptos::{create_runtime, Attribute, IntoAttribute};
|
||||
// TODO
|
||||
// #[cfg(test)]
|
||||
// mod tests {
|
||||
// use leptos::reactive_graph::Run;
|
||||
|
||||
#[test]
|
||||
fn macro_class_list() {
|
||||
let rt = create_runtime();
|
||||
let class_list = class_list!("aa", ("bb", || true), move || "cc");
|
||||
if let Attribute::Fn(f) = class_list.into_attribute() {
|
||||
if let Attribute::String(class) = f() {
|
||||
assert!(class.contains("aa"));
|
||||
assert!(class.contains("bb"));
|
||||
assert!(class.contains("cc"));
|
||||
}
|
||||
}
|
||||
rt.dispose();
|
||||
}
|
||||
}
|
||||
// #[test]
|
||||
// fn macro_class_list() {
|
||||
// let rt = create_runtime();
|
||||
// let class_list = class_list!("aa", ("bb", || true), move || "cc");
|
||||
// if let Attribute::Fn(f) = class_list.into_attribute() {
|
||||
// if let Attribute::String(class) = f() {
|
||||
// assert!(class.contains("aa"));
|
||||
// assert!(class.contains("bb"));
|
||||
// assert!(class.contains("cc"));
|
||||
// }
|
||||
// }
|
||||
// rt.dispose();
|
||||
// }
|
||||
// }
|
||||
|
|
|
@ -1,55 +1,55 @@
|
|||
use leptos::{
|
||||
html::{AnyElement, ToHtmlElement},
|
||||
*,
|
||||
};
|
||||
// use leptos::{
|
||||
// html::{AnyElement, ToHtmlElement},
|
||||
// *,
|
||||
// };
|
||||
|
||||
pub fn get_scroll_parent(element: &HtmlElement<AnyElement>) -> Option<HtmlElement<AnyElement>> {
|
||||
let Some(parent_element) = get_parent_element(element) else {
|
||||
return None;
|
||||
};
|
||||
// pub fn get_scroll_parent(element: &HtmlElement<AnyElement>) -> Option<HtmlElement<AnyElement>> {
|
||||
// let Some(parent_element) = get_parent_element(element) else {
|
||||
// return None;
|
||||
// };
|
||||
|
||||
if parent_element.node_type() == 9 {
|
||||
return Some(parent_element);
|
||||
}
|
||||
// if parent_element.node_type() == 9 {
|
||||
// return Some(parent_element);
|
||||
// }
|
||||
|
||||
if parent_element.node_type() == 1 {
|
||||
if let Some((overflow, overflow_x, overflow_y)) = get_overflow(&parent_element) {
|
||||
let overflow = format!("{overflow}{overflow_x}{overflow_y}");
|
||||
if overflow.contains("auto") {
|
||||
return Some(parent_element);
|
||||
}
|
||||
if overflow.contains("scroll") {
|
||||
return Some(parent_element);
|
||||
}
|
||||
if overflow.contains("overlay") {
|
||||
return Some(parent_element);
|
||||
}
|
||||
}
|
||||
}
|
||||
// if parent_element.node_type() == 1 {
|
||||
// if let Some((overflow, overflow_x, overflow_y)) = get_overflow(&parent_element) {
|
||||
// let overflow = format!("{overflow}{overflow_x}{overflow_y}");
|
||||
// if overflow.contains("auto") {
|
||||
// return Some(parent_element);
|
||||
// }
|
||||
// if overflow.contains("scroll") {
|
||||
// return Some(parent_element);
|
||||
// }
|
||||
// if overflow.contains("overlay") {
|
||||
// return Some(parent_element);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
get_scroll_parent(&parent_element)
|
||||
}
|
||||
// get_scroll_parent(&parent_element)
|
||||
// }
|
||||
|
||||
fn get_parent_element(element: &HtmlElement<AnyElement>) -> Option<HtmlElement<AnyElement>> {
|
||||
if element.node_type() == 9 {
|
||||
None
|
||||
} else {
|
||||
element.parent_element().map(|ele| ele.to_leptos_element())
|
||||
}
|
||||
}
|
||||
// fn get_parent_element(element: &HtmlElement<AnyElement>) -> Option<HtmlElement<AnyElement>> {
|
||||
// if element.node_type() == 9 {
|
||||
// None
|
||||
// } else {
|
||||
// element.parent_element().map(|ele| ele.to_leptos_element())
|
||||
// }
|
||||
// }
|
||||
|
||||
fn get_overflow(parent_element: &HtmlElement<AnyElement>) -> Option<(String, String, String)> {
|
||||
let Ok(Some(css_style_declaration)) = window().get_computed_style(parent_element) else {
|
||||
return None;
|
||||
};
|
||||
let Ok(overflow) = css_style_declaration.get_property_value("overflow") else {
|
||||
return None;
|
||||
};
|
||||
let Ok(overflow_x) = css_style_declaration.get_property_value("overflowX") else {
|
||||
return None;
|
||||
};
|
||||
let Ok(overflow_y) = css_style_declaration.get_property_value("overflowY") else {
|
||||
return None;
|
||||
};
|
||||
Some((overflow, overflow_x, overflow_y))
|
||||
}
|
||||
// fn get_overflow(parent_element: &HtmlElement<AnyElement>) -> Option<(String, String, String)> {
|
||||
// let Ok(Some(css_style_declaration)) = window().get_computed_style(parent_element) else {
|
||||
// return None;
|
||||
// };
|
||||
// let Ok(overflow) = css_style_declaration.get_property_value("overflow") else {
|
||||
// return None;
|
||||
// };
|
||||
// let Ok(overflow_x) = css_style_declaration.get_property_value("overflowX") else {
|
||||
// return None;
|
||||
// };
|
||||
// let Ok(overflow_y) = css_style_declaration.get_property_value("overflowY") else {
|
||||
// return None;
|
||||
// };
|
||||
// Some((overflow, overflow_x, overflow_y))
|
||||
// }
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
mod get_scroll_parent;
|
||||
mod mount_style;
|
||||
|
||||
pub use get_scroll_parent::get_scroll_parent;
|
||||
// pub use get_scroll_parent::get_scroll_parent;
|
||||
pub use mount_style::{mount_dynamic_style, mount_style};
|
||||
|
|
|
@ -9,7 +9,7 @@ pub fn mount_style(id: &str, content: &'static str) {
|
|||
let style_el = style().attr("data-thaw-id", id).child(content);
|
||||
meta.tags.register(format!("leptos-thaw-{id}").into(), style_el.into_any());
|
||||
} else {
|
||||
use leptos::document;
|
||||
use leptos::prelude::document;
|
||||
let head = document().head().expect("head no exist");
|
||||
let style = head
|
||||
.query_selector(&format!("style[data-thaw-id=\"{id}\"]"))
|
||||
|
@ -32,7 +32,7 @@ pub fn mount_style(id: &str, content: &'static str) {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn mount_dynamic_style<T: Fn() -> String + 'static>(id: String, f: T) {
|
||||
pub fn mount_dynamic_style<T: Fn() -> String + Send + Sync + 'static>(id: String, f: T) {
|
||||
cfg_if! {
|
||||
if #[cfg(feature = "ssr")] {
|
||||
use leptos::html::style;
|
||||
|
@ -42,35 +42,30 @@ pub fn mount_dynamic_style<T: Fn() -> String + 'static>(id: String, f: T) {
|
|||
let style_el = style().attr("data-thaw-id", id).child(content);
|
||||
meta.tags.register(format!("leptos-thaw-{id}").into(), style_el.into_any());
|
||||
} else {
|
||||
use leptos::document;
|
||||
use leptos::prelude::document;
|
||||
use send_wrapper::SendWrapper;
|
||||
|
||||
let head = document().head().expect("head no exist");
|
||||
let style = head
|
||||
.query_selector(&format!("style[data-thaw-id=\"{id}\"]"))
|
||||
.expect("query style element error");
|
||||
|
||||
#[cfg(feature = "hydrate")]
|
||||
let _ = leptos::leptos_dom::HydrationCtx::id();
|
||||
|
||||
|
||||
leptos::Effect::new_isomorphic(move |prev: Option<Option<web_sys::Element>>| {
|
||||
let content = f();
|
||||
|
||||
if let Some(style) = style.as_ref() {
|
||||
style.set_text_content(Some(&content));
|
||||
None
|
||||
} else if let Some(style) = prev.flatten() {
|
||||
style.set_text_content(Some(&content));
|
||||
Some(style)
|
||||
} else {
|
||||
.expect("query style element error").unwrap_or_else(|| {
|
||||
let style = document()
|
||||
.create_element("style")
|
||||
.expect("create style element error");
|
||||
_ = style.set_attribute("data-thaw-id", &id);
|
||||
style.set_text_content(Some(&content));
|
||||
_ = head.prepend_with_node_1(&style);
|
||||
|
||||
Some(style)
|
||||
}
|
||||
style
|
||||
});
|
||||
|
||||
#[cfg(feature = "hydrate")]
|
||||
let _ = leptos::leptos_dom::HydrationCtx::id();
|
||||
|
||||
let style = SendWrapper::new(style);
|
||||
leptos::prelude::Effect::new_isomorphic(move |_| {
|
||||
let content = f();
|
||||
|
||||
style.set_text_content(Some(&content));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,5 @@
|
|||
use ::wasm_bindgen::{prelude::Closure, JsCast};
|
||||
use leptos::{
|
||||
ev,
|
||||
tachys::{renderer::DomRenderer, view::any_view::AnyView},
|
||||
};
|
||||
use std::ops::Deref;
|
||||
use leptos::{ev, tachys::renderer::DomRenderer};
|
||||
use web_sys::EventTarget;
|
||||
|
||||
pub fn add_event_listener<E>(
|
||||
|
@ -15,9 +11,10 @@ where
|
|||
E: ev::EventDescriptor + 'static,
|
||||
E::EventType: JsCast,
|
||||
{
|
||||
add_event_listener_untyped(target, &event.name(), move |e| {
|
||||
cb(e.unchecked_into::<E::EventType>())
|
||||
})
|
||||
todo!()
|
||||
// add_event_listener_untyped(target, &event.name(), move |e| {
|
||||
// cb(e.unchecked_into::<E::EventType>())
|
||||
// })
|
||||
}
|
||||
|
||||
pub struct EventListenerHandle(Box<dyn FnOnce()>);
|
||||
|
@ -34,26 +31,26 @@ impl EventListenerHandle {
|
|||
}
|
||||
}
|
||||
|
||||
fn add_event_listener_untyped(
|
||||
target: impl DomRenderer,
|
||||
event_name: &str,
|
||||
cb: impl Fn(web_sys::Event) + 'static,
|
||||
) -> EventListenerHandle {
|
||||
fn wel(
|
||||
target: impl DomRenderer,
|
||||
cb: Box<dyn FnMut(web_sys::Event)>,
|
||||
event_name: &str,
|
||||
) -> EventListenerHandle {
|
||||
let cb = Closure::wrap(cb).into_js_value();
|
||||
_ = target.add_event_listener_with_callback(event_name, cb.unchecked_ref());
|
||||
let event_name = event_name.to_string();
|
||||
EventListenerHandle(Box::new(move || {
|
||||
_ = target.remove_event_listener_with_callback(&event_name, cb.unchecked_ref());
|
||||
}))
|
||||
}
|
||||
// fn add_event_listener_untyped(
|
||||
// target: impl DomRenderer,
|
||||
// event_name: &str,
|
||||
// cb: impl Fn(web_sys::Event) + 'static,
|
||||
// ) -> EventListenerHandle {
|
||||
// fn wel(
|
||||
// target: impl DomRenderer,
|
||||
// cb: Box<dyn FnMut(web_sys::Event)>,
|
||||
// event_name: &str,
|
||||
// ) -> EventListenerHandle {
|
||||
// let cb = Closure::wrap(cb).into_js_value();
|
||||
// _ = target.add_event_listener_with_callback(event_name, cb.unchecked_ref());
|
||||
// let event_name = event_name.to_string();
|
||||
// EventListenerHandle(Box::new(move || {
|
||||
// _ = target.remove_event_listener_with_callback(&event_name, cb.unchecked_ref());
|
||||
// }))
|
||||
// }
|
||||
|
||||
wel(target, Box::new(cb), event_name)
|
||||
}
|
||||
// wel(target, Box::new(cb), event_name)
|
||||
// }
|
||||
|
||||
pub fn add_event_listener_with_bool<E: ev::EventDescriptor + 'static>(
|
||||
target: impl IntoEventTarget,
|
||||
|
@ -119,8 +116,8 @@ impl IntoEventTarget for web_sys::Document {
|
|||
}
|
||||
}
|
||||
|
||||
impl IntoEventTarget for HtmlElement<AnyElement> {
|
||||
fn into_event_target(self) -> EventTarget {
|
||||
self.deref().deref().deref().deref().clone()
|
||||
}
|
||||
}
|
||||
// impl IntoEventTarget for HtmlElement<AnyElement> {
|
||||
// fn into_event_target(self) -> EventTarget {
|
||||
// self.deref().deref().deref().deref().clone()
|
||||
// }
|
||||
// }
|
||||
|
|
|
@ -4,4 +4,4 @@ mod use_next_frame;
|
|||
|
||||
pub use use_click_position::use_click_position;
|
||||
pub use use_lock_html_scroll::use_lock_html_scroll;
|
||||
pub use use_next_frame::{use_next_frame, NextFrame};
|
||||
pub use use_next_frame::NextFrame;
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
use leptos::{ReadSignal, RwSignal};
|
||||
use leptos::reactive_graph::signal::{ReadSignal, RwSignal};
|
||||
|
||||
pub fn use_click_position() -> ReadSignal<Option<(i32, i32)>> {
|
||||
let mouse_position = RwSignal::new(None);
|
||||
#[cfg(any(feature = "csr", feature = "hydrate"))]
|
||||
{
|
||||
use leptos::{ev, on_cleanup, window_event_listener, SignalSet};
|
||||
use leptos::{
|
||||
ev,
|
||||
prelude::{on_cleanup, window_event_listener, Set},
|
||||
};
|
||||
use wasm_bindgen::JsCast;
|
||||
use web_sys::MouseEvent;
|
||||
|
||||
|
|
|
@ -1,20 +1,25 @@
|
|||
use leptos::MaybeSignal;
|
||||
use leptos::reactive_graph::wrappers::read::MaybeSignal;
|
||||
|
||||
pub fn use_lock_html_scroll(is_lock: MaybeSignal<bool>) {
|
||||
#[cfg(any(feature = "csr", feature = "hydrate"))]
|
||||
{
|
||||
use leptos::{create_render_effect, document, on_cleanup, SignalGet, StoredValue};
|
||||
let style_el = StoredValue::new(None::<web_sys::Element>);
|
||||
// use leptos::{create_render_effect, document, on_cleanup, SignalGet, StoredValue};
|
||||
use leptos::prelude::{
|
||||
document, effect::RenderEffect, on_cleanup, traits::Get, StoredValue,
|
||||
};
|
||||
use send_wrapper::SendWrapper;
|
||||
|
||||
let style_el = StoredValue::new(SendWrapper::new(None::<web_sys::Element>));
|
||||
let remove_style_el = move || {
|
||||
style_el.update_value(move |el| {
|
||||
if let Some(el) = el.take() {
|
||||
if let Some(el) = Option::take(el) {
|
||||
let head = document().head().expect("head no exist");
|
||||
_ = head.remove_child(&el);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
create_render_effect(move |_| {
|
||||
RenderEffect::new(move |_| {
|
||||
if is_lock.get() {
|
||||
let head = document().head().expect("head no exist");
|
||||
let style = document()
|
||||
|
@ -23,7 +28,9 @@ pub fn use_lock_html_scroll(is_lock: MaybeSignal<bool>) {
|
|||
_ = style.set_attribute("data-id", &format!("thaw-lock-html-scroll"));
|
||||
style.set_text_content(Some("html { overflow: hidden; }"));
|
||||
_ = head.append_child(&style);
|
||||
style_el.set_value(Some(style));
|
||||
style_el.update_value(move |el| {
|
||||
*el = SendWrapper::new(Some(style));
|
||||
});
|
||||
} else {
|
||||
remove_style_el();
|
||||
}
|
||||
|
|
|
@ -1,9 +1,26 @@
|
|||
// use leptos::{
|
||||
// leptos_dom::helpers::AnimationFrameRequestHandle, on_cleanup,
|
||||
// request_animation_frame_with_handle, StoredValue,
|
||||
// };
|
||||
|
||||
use leptos::{
|
||||
leptos_dom::helpers::AnimationFrameRequestHandle, on_cleanup,
|
||||
request_animation_frame_with_handle, StoredValue,
|
||||
prelude::{request_animation_frame_with_handle, AnimationFrameRequestHandle},
|
||||
reactive_graph::owner::{on_cleanup, StoredValue},
|
||||
};
|
||||
|
||||
pub fn use_next_frame() -> NextFrame {
|
||||
#[derive(Clone)]
|
||||
pub struct NextFrame(StoredValue<Option<AnimationFrameRequestHandle>>);
|
||||
|
||||
impl Default for NextFrame {
|
||||
fn default() -> Self {
|
||||
Self(StoredValue::new(None))
|
||||
}
|
||||
}
|
||||
|
||||
impl Copy for NextFrame {}
|
||||
|
||||
impl NextFrame {
|
||||
pub fn use_() -> Self {
|
||||
let next_frame = NextFrame::default();
|
||||
|
||||
on_cleanup(move || {
|
||||
|
@ -13,12 +30,6 @@ pub fn use_next_frame() -> NextFrame {
|
|||
next_frame
|
||||
}
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
pub struct NextFrame(StoredValue<Option<AnimationFrameRequestHandle>>);
|
||||
|
||||
impl Copy for NextFrame {}
|
||||
|
||||
impl NextFrame {
|
||||
pub fn run(&self, cb: impl FnOnce() + 'static) {
|
||||
self.cancel();
|
||||
|
||||
|
|
|
@ -7,15 +7,11 @@ mod signals;
|
|||
mod throttle;
|
||||
mod time;
|
||||
|
||||
pub use dom::{get_scroll_parent, mount_dynamic_style, mount_style};
|
||||
pub use event_listener::{
|
||||
add_event_listener, add_event_listener_with_bool, EventListenerHandle, IntoEventTarget,
|
||||
};
|
||||
pub use hooks::{use_click_position, use_lock_html_scroll, use_next_frame, NextFrame};
|
||||
pub use dom::{mount_dynamic_style, mount_style};
|
||||
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;
|
||||
pub use signals::{
|
||||
create_component_ref, ComponentRef, Model, OptionalMaybeSignal, SignalWatch, StoredMaybeSignal,
|
||||
};
|
||||
pub use signals::{ComponentRef, Model, OptionalMaybeSignal, SignalWatch, StoredMaybeSignal};
|
||||
pub use throttle::throttle;
|
||||
pub use time::now_date;
|
||||
|
||||
|
|
|
@ -66,19 +66,19 @@ impl From<String> for OptionalProp<MaybeSignal<String>> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> From<ReadSignal<T>> for OptionalProp<MaybeSignal<T>> {
|
||||
impl<T: Send + Sync> From<ReadSignal<T>> for OptionalProp<MaybeSignal<T>> {
|
||||
fn from(value: ReadSignal<T>) -> Self {
|
||||
Self(Some(MaybeSignal::from(value)))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<RwSignal<T>> for OptionalProp<MaybeSignal<T>> {
|
||||
impl<T: Send + Sync> From<RwSignal<T>> for OptionalProp<MaybeSignal<T>> {
|
||||
fn from(value: RwSignal<T>) -> Self {
|
||||
Self(Some(MaybeSignal::from(value)))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<Memo<T>> for OptionalProp<MaybeSignal<T>> {
|
||||
impl<T: Send + Sync> From<Memo<T>> for OptionalProp<MaybeSignal<T>> {
|
||||
fn from(value: Memo<T>) -> Self {
|
||||
Self(Some(MaybeSignal::from(value)))
|
||||
}
|
||||
|
|
|
@ -1,14 +1,18 @@
|
|||
use leptos::{
|
||||
create_render_effect, create_rw_signal, logging::debug_warn, RwSignal, SignalGet,
|
||||
SignalGetUntracked, SignalUpdate,
|
||||
logging::debug_warn,
|
||||
reactive_graph::{
|
||||
effect::RenderEffect,
|
||||
signal::RwSignal,
|
||||
traits::{Get, GetUntracked, Update},
|
||||
},
|
||||
};
|
||||
use std::cell::Cell;
|
||||
|
||||
pub struct ComponentRef<T: 'static>(RwSignal<Option<T>>);
|
||||
|
||||
impl<T> Default for ComponentRef<T> {
|
||||
impl<T: Send + Sync> Default for ComponentRef<T> {
|
||||
fn default() -> Self {
|
||||
Self(create_rw_signal(None))
|
||||
Self(RwSignal::new(None))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -20,11 +24,14 @@ impl<T> Clone for ComponentRef<T> {
|
|||
|
||||
impl<T: 'static> Copy for ComponentRef<T> {}
|
||||
|
||||
impl<T> ComponentRef<T> {
|
||||
// TODO
|
||||
impl<T: Send + Sync> ComponentRef<T> {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ComponentRef<T> {
|
||||
pub fn get(&self) -> Option<T>
|
||||
where
|
||||
T: Clone,
|
||||
|
@ -58,14 +65,10 @@ impl<T> ComponentRef<T> {
|
|||
{
|
||||
let f = Cell::new(Some(f));
|
||||
|
||||
create_render_effect(move |_| {
|
||||
RenderEffect::new(move |_| {
|
||||
if let Some(comp) = self.get() {
|
||||
f.take().unwrap()(comp);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_component_ref<T>() -> ComponentRef<T> {
|
||||
ComponentRef::default()
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ mod optional_maybe_signal;
|
|||
mod signal_watch;
|
||||
mod stored_maybe_signal;
|
||||
|
||||
pub use component_ref::{create_component_ref, ComponentRef};
|
||||
pub use component_ref::ComponentRef;
|
||||
pub use model::Model;
|
||||
pub use optional_maybe_signal::OptionalMaybeSignal;
|
||||
pub use signal_watch::SignalWatch;
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
use leptos::{
|
||||
Memo, ReadSignal, RwSignal, Signal, SignalGet, SignalGetUntracked, SignalSet, SignalUpdate,
|
||||
SignalWith, SignalWithUntracked, WriteSignal,
|
||||
use leptos::reactive_graph::{
|
||||
computed::Memo,
|
||||
signal::{ReadSignal, RwSignal, WriteSignal},
|
||||
traits::{DefinedAt, IsDisposed, Set, Update, With, WithUntracked},
|
||||
wrappers::read::Signal,
|
||||
};
|
||||
|
||||
pub struct Model<T>
|
||||
|
@ -12,7 +14,7 @@ where
|
|||
on_write: Option<WriteSignal<T>>,
|
||||
}
|
||||
|
||||
impl<T: Default> Default for Model<T> {
|
||||
impl<T: Default + Send + Sync> Default for Model<T> {
|
||||
fn default() -> Self {
|
||||
RwSignal::new(Default::default()).into()
|
||||
}
|
||||
|
@ -26,7 +28,7 @@ impl<T> Clone for Model<T> {
|
|||
|
||||
impl<T> Copy for Model<T> {}
|
||||
|
||||
impl<T> Model<T> {
|
||||
impl<T: Send + Sync> Model<T> {
|
||||
fn new(value: T) -> Self {
|
||||
let rw_signal = RwSignal::new(value);
|
||||
rw_signal.into()
|
||||
|
@ -37,91 +39,56 @@ impl<T> Model<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Clone> SignalGet for Model<T> {
|
||||
impl<T> DefinedAt for Model<T> {
|
||||
fn defined_at(&self) -> Option<&'static std::panic::Location<'static>> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Send + Sync> With for Model<T> {
|
||||
type Value = T;
|
||||
|
||||
fn get(&self) -> Self::Value {
|
||||
self.read.get()
|
||||
}
|
||||
|
||||
fn try_get(&self) -> Option<Self::Value> {
|
||||
self.read.try_get()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone> SignalGetUntracked for Model<T> {
|
||||
type Value = T;
|
||||
|
||||
fn get_untracked(&self) -> Self::Value {
|
||||
self.read.get_untracked()
|
||||
}
|
||||
|
||||
fn try_get_untracked(&self) -> Option<Self::Value> {
|
||||
self.read.try_get_untracked()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone> SignalSet for Model<T> {
|
||||
type Value = T;
|
||||
|
||||
fn set(&self, new_value: Self::Value) {
|
||||
if let Some(on_write) = self.on_write.as_ref() {
|
||||
on_write.set(new_value.clone());
|
||||
}
|
||||
self.write.set(new_value);
|
||||
}
|
||||
|
||||
fn try_set(&self, new_value: Self::Value) -> Option<Self::Value> {
|
||||
if let Some(on_write) = self.on_write.as_ref() {
|
||||
on_write.try_set(new_value.clone());
|
||||
}
|
||||
self.write.try_set(new_value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> SignalWith for Model<T> {
|
||||
type Value = T;
|
||||
|
||||
fn with<O>(&self, f: impl FnOnce(&Self::Value) -> O) -> O {
|
||||
self.read.with(f)
|
||||
}
|
||||
|
||||
fn try_with<O>(&self, f: impl FnOnce(&Self::Value) -> O) -> Option<O> {
|
||||
self.read.try_with(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> SignalWithUntracked for Model<T> {
|
||||
impl<T: Send + Sync> WithUntracked for Model<T> {
|
||||
type Value = T;
|
||||
|
||||
fn with_untracked<O>(&self, f: impl FnOnce(&Self::Value) -> O) -> O {
|
||||
self.read.with_untracked(f)
|
||||
}
|
||||
|
||||
fn try_with_untracked<O>(&self, f: impl FnOnce(&Self::Value) -> O) -> Option<O> {
|
||||
self.read.try_with_untracked(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> SignalUpdate for Model<T> {
|
||||
// TODO
|
||||
impl<T: Send + Sync + Clone> Update for Model<T> {
|
||||
type Value = T;
|
||||
|
||||
fn update(&self, f: impl FnOnce(&mut Self::Value)) {
|
||||
self.write.update(f);
|
||||
fn try_maybe_update<U>(&self, fun: impl FnOnce(&mut Self::Value) -> (bool, U)) -> Option<U> {
|
||||
let value = self.write.try_maybe_update(fun);
|
||||
|
||||
if let Some(on_write) = self.on_write.as_ref() {
|
||||
on_write.set(self.read.with_untracked(|read| read.clone()));
|
||||
}
|
||||
|
||||
fn try_update<O>(&self, f: impl FnOnce(&mut Self::Value) -> O) -> Option<O> {
|
||||
self.write.try_update(f)
|
||||
value
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<T> for Model<T> {
|
||||
impl<T> IsDisposed for Model<T> {
|
||||
fn is_disposed(&self) -> bool {
|
||||
self.write.is_disposed()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Send + Sync> From<T> for Model<T> {
|
||||
fn from(value: T) -> Self {
|
||||
Self::new(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<RwSignal<T>> for Model<T> {
|
||||
impl<T: Send + Sync> From<RwSignal<T>> for Model<T> {
|
||||
fn from(rw_signal: RwSignal<T>) -> Self {
|
||||
let (read, write) = rw_signal.split();
|
||||
Self {
|
||||
|
@ -142,7 +109,7 @@ impl<T> From<(Signal<T>, WriteSignal<T>)> for Model<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> From<(ReadSignal<T>, WriteSignal<T>)> for Model<T> {
|
||||
impl<T: Send + Sync> From<(ReadSignal<T>, WriteSignal<T>)> for Model<T> {
|
||||
fn from((read, write): (ReadSignal<T>, WriteSignal<T>)) -> Self {
|
||||
Self {
|
||||
read: read.into(),
|
||||
|
@ -152,7 +119,7 @@ impl<T> From<(ReadSignal<T>, WriteSignal<T>)> for Model<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> From<(Memo<T>, WriteSignal<T>)> for Model<T> {
|
||||
impl<T: Send + Sync> From<(Memo<T>, WriteSignal<T>)> for Model<T> {
|
||||
fn from((read, write): (Memo<T>, WriteSignal<T>)) -> Self {
|
||||
Self {
|
||||
read: read.into(),
|
||||
|
@ -162,7 +129,7 @@ impl<T> From<(Memo<T>, WriteSignal<T>)> for Model<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Default> From<(Option<T>, WriteSignal<T>)> for Model<T> {
|
||||
impl<T: Default + Send + Sync> From<(Option<T>, WriteSignal<T>)> for Model<T> {
|
||||
fn from((read, write): (Option<T>, WriteSignal<T>)) -> Self {
|
||||
let mut model = Self::new(read.unwrap_or_default());
|
||||
model.on_write = Some(write);
|
||||
|
@ -170,35 +137,36 @@ impl<T: Default> From<(Option<T>, WriteSignal<T>)> for Model<T> {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::Model;
|
||||
use leptos::*;
|
||||
// TODO
|
||||
// #[cfg(test)]
|
||||
// mod test {
|
||||
// use super::Model;
|
||||
// use leptos::*;
|
||||
|
||||
#[test]
|
||||
fn from() {
|
||||
let runtime = create_runtime();
|
||||
// #[test]
|
||||
// fn from() {
|
||||
// let runtime = create_runtime();
|
||||
|
||||
// T
|
||||
let model: Model<i32> = 0.into();
|
||||
assert_eq!(model.get_untracked(), 0);
|
||||
model.set(1);
|
||||
assert_eq!(model.get_untracked(), 1);
|
||||
// // T
|
||||
// let model: Model<i32> = 0.into();
|
||||
// assert_eq!(model.get_untracked(), 0);
|
||||
// model.set(1);
|
||||
// assert_eq!(model.get_untracked(), 1);
|
||||
|
||||
// RwSignal
|
||||
let rw_signal = RwSignal::new(0);
|
||||
let model: Model<i32> = rw_signal.into();
|
||||
assert_eq!(model.get_untracked(), 0);
|
||||
model.set(1);
|
||||
assert_eq!(model.get_untracked(), 1);
|
||||
// // RwSignal
|
||||
// let rw_signal = RwSignal::new(0);
|
||||
// let model: Model<i32> = rw_signal.into();
|
||||
// assert_eq!(model.get_untracked(), 0);
|
||||
// model.set(1);
|
||||
// assert_eq!(model.get_untracked(), 1);
|
||||
|
||||
// Read Write
|
||||
let (read, write) = create_signal(0);
|
||||
let model: Model<i32> = (read, write).into();
|
||||
assert_eq!(model.get_untracked(), 0);
|
||||
model.set(1);
|
||||
assert_eq!(model.get_untracked(), 1);
|
||||
// // Read Write
|
||||
// let (read, write) = create_signal(0);
|
||||
// let model: Model<i32> = (read, write).into();
|
||||
// assert_eq!(model.get_untracked(), 0);
|
||||
// model.set(1);
|
||||
// assert_eq!(model.get_untracked(), 1);
|
||||
|
||||
runtime.dispose();
|
||||
}
|
||||
}
|
||||
// runtime.dispose();
|
||||
// }
|
||||
// }
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use leptos::*;
|
||||
use leptos::prelude::*;
|
||||
use std::ops::Deref;
|
||||
|
||||
pub struct OptionalMaybeSignal<T: 'static>(MaybeSignal<Option<T>>);
|
||||
|
@ -37,19 +37,19 @@ impl<T> From<Option<T>> for OptionalMaybeSignal<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> From<ReadSignal<Option<T>>> for OptionalMaybeSignal<T> {
|
||||
impl<T: Send + Sync> From<ReadSignal<Option<T>>> for OptionalMaybeSignal<T> {
|
||||
fn from(value: ReadSignal<Option<T>>) -> Self {
|
||||
Self(MaybeSignal::Dynamic(value.into()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<RwSignal<Option<T>>> for OptionalMaybeSignal<T> {
|
||||
impl<T: Send + Sync> From<RwSignal<Option<T>>> for OptionalMaybeSignal<T> {
|
||||
fn from(value: RwSignal<Option<T>>) -> Self {
|
||||
Self(MaybeSignal::Dynamic(value.into()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<Memo<Option<T>>> for OptionalMaybeSignal<T> {
|
||||
impl<T: Send + Sync> From<Memo<Option<T>>> for OptionalMaybeSignal<T> {
|
||||
fn from(value: Memo<Option<T>>) -> Self {
|
||||
Self(MaybeSignal::Dynamic(value.into()))
|
||||
}
|
||||
|
@ -67,19 +67,20 @@ impl<T> From<MaybeSignal<Option<T>>> for OptionalMaybeSignal<T> {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::OptionalMaybeSignal;
|
||||
use leptos::{create_runtime, MaybeSignal};
|
||||
// TODO
|
||||
// #[cfg(test)]
|
||||
// mod test {
|
||||
// use super::OptionalMaybeSignal;
|
||||
// use leptos::{create_runtime, MaybeSignal};
|
||||
|
||||
#[test]
|
||||
fn into() {
|
||||
let runtime = create_runtime();
|
||||
// #[test]
|
||||
// fn into() {
|
||||
// let runtime = create_runtime();
|
||||
|
||||
let _: MaybeSignal<i32> = 12.into();
|
||||
let _: OptionalMaybeSignal<i32> = Some(12).into();
|
||||
let _: OptionalMaybeSignal<i32> = MaybeSignal::Static(Some(12)).into();
|
||||
// let _: MaybeSignal<i32> = 12.into();
|
||||
// let _: OptionalMaybeSignal<i32> = Some(12).into();
|
||||
// let _: OptionalMaybeSignal<i32> = MaybeSignal::Static(Some(12)).into();
|
||||
|
||||
runtime.dispose();
|
||||
}
|
||||
}
|
||||
// runtime.dispose();
|
||||
// }
|
||||
// }
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use leptos::{create_effect, untrack, RwSignal, SignalDispose, SignalWith};
|
||||
use leptos::reactive_graph::{effect::Effect, signal::RwSignal, traits::{Dispose, With}, untrack};
|
||||
|
||||
pub trait SignalWatch {
|
||||
type Value;
|
||||
|
@ -6,7 +6,7 @@ pub trait SignalWatch {
|
|||
fn watch(&self, f: impl Fn(&Self::Value) + 'static) -> Box<dyn FnOnce()>;
|
||||
}
|
||||
|
||||
impl<T> SignalWatch for RwSignal<T> {
|
||||
impl<T: 'static> SignalWatch for RwSignal<T> {
|
||||
type Value = T;
|
||||
|
||||
/// Listens for RwSignal changes and is not executed immediately
|
||||
|
@ -31,7 +31,7 @@ impl<T> SignalWatch for RwSignal<T> {
|
|||
fn watch(&self, f: impl Fn(&Self::Value) + 'static) -> Box<dyn FnOnce()> {
|
||||
let signal = *self;
|
||||
|
||||
let effect = create_effect(move |prev| {
|
||||
let effect = Effect::new(move |prev| {
|
||||
signal.with(|value| {
|
||||
if prev.is_some() {
|
||||
untrack(|| f(value));
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use leptos::{
|
||||
MaybeSignal, Signal, SignalGet, SignalGetUntracked, SignalWith, SignalWithUntracked,
|
||||
StoredValue,
|
||||
use leptos::reactive_graph::{
|
||||
owner::StoredValue,
|
||||
traits::{DefinedAt, With, WithUntracked},
|
||||
wrappers::read::{MaybeSignal, Signal},
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
|
@ -14,52 +15,18 @@ where
|
|||
|
||||
impl<T: Clone> Copy for StoredMaybeSignal<T> {}
|
||||
|
||||
impl<T: Clone> SignalGet for StoredMaybeSignal<T> {
|
||||
impl<T> DefinedAt for StoredMaybeSignal<T> {
|
||||
fn defined_at(&self) -> Option<&'static std::panic::Location<'static>> {
|
||||
match self {
|
||||
StoredMaybeSignal::StoredValue(value) => value.defined_at(),
|
||||
StoredMaybeSignal::Signal(signal) => signal.defined_at(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Send + Sync> With for StoredMaybeSignal<T> {
|
||||
type Value = T;
|
||||
|
||||
fn get(&self) -> Self::Value {
|
||||
match self {
|
||||
StoredMaybeSignal::StoredValue(value) => value.get_value(),
|
||||
StoredMaybeSignal::Signal(signal) => signal.get(),
|
||||
}
|
||||
}
|
||||
|
||||
fn try_get(&self) -> Option<Self::Value> {
|
||||
match self {
|
||||
StoredMaybeSignal::StoredValue(value) => value.try_get_value(),
|
||||
StoredMaybeSignal::Signal(signal) => signal.try_get(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone> SignalGetUntracked for StoredMaybeSignal<T> {
|
||||
type Value = T;
|
||||
|
||||
fn get_untracked(&self) -> Self::Value {
|
||||
match self {
|
||||
StoredMaybeSignal::StoredValue(value) => value.get_value(),
|
||||
StoredMaybeSignal::Signal(signal) => signal.get_untracked(),
|
||||
}
|
||||
}
|
||||
|
||||
fn try_get_untracked(&self) -> Option<Self::Value> {
|
||||
match self {
|
||||
StoredMaybeSignal::StoredValue(value) => value.try_get_value(),
|
||||
StoredMaybeSignal::Signal(signal) => signal.try_get_untracked(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> SignalWith for StoredMaybeSignal<T> {
|
||||
type Value = T;
|
||||
|
||||
fn with<O>(&self, f: impl FnOnce(&Self::Value) -> O) -> O {
|
||||
match self {
|
||||
StoredMaybeSignal::StoredValue(value) => value.with_value(f),
|
||||
StoredMaybeSignal::Signal(signal) => signal.with(f),
|
||||
}
|
||||
}
|
||||
|
||||
fn try_with<O>(&self, f: impl FnOnce(&Self::Value) -> O) -> Option<O> {
|
||||
match self {
|
||||
StoredMaybeSignal::StoredValue(value) => value.try_with_value(f),
|
||||
|
@ -68,16 +35,9 @@ impl<T> SignalWith for StoredMaybeSignal<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> SignalWithUntracked for StoredMaybeSignal<T> {
|
||||
impl<T: Send + Sync> WithUntracked for StoredMaybeSignal<T> {
|
||||
type Value = T;
|
||||
|
||||
fn with_untracked<O>(&self, f: impl FnOnce(&Self::Value) -> O) -> O {
|
||||
match self {
|
||||
StoredMaybeSignal::StoredValue(value) => value.with_value(f),
|
||||
StoredMaybeSignal::Signal(signal) => signal.with_untracked(f),
|
||||
}
|
||||
}
|
||||
|
||||
fn try_with_untracked<O>(&self, f: impl FnOnce(&Self::Value) -> O) -> Option<O> {
|
||||
match self {
|
||||
StoredMaybeSignal::StoredValue(value) => value.try_with_value(f),
|
||||
|
@ -86,7 +46,7 @@ impl<T> SignalWithUntracked for StoredMaybeSignal<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> From<MaybeSignal<T>> for StoredMaybeSignal<T> {
|
||||
impl<T: Send + Sync> From<MaybeSignal<T>> for StoredMaybeSignal<T> {
|
||||
fn from(value: MaybeSignal<T>) -> Self {
|
||||
match value {
|
||||
MaybeSignal::Static(value) => Self::StoredValue(StoredValue::new(value)),
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use leptos::{leptos_dom::helpers::TimeoutHandle, prelude::*};
|
||||
use std::time::Duration;
|
||||
|
||||
pub fn throttle(cb: impl Fn() + 'static, duration: Duration) -> impl Fn() -> () {
|
||||
pub fn throttle(cb: impl Fn() + Send + Sync + 'static, duration: Duration) -> impl Fn() -> () {
|
||||
let cb = Callback::new(move |_| cb());
|
||||
let timeout_handle = StoredValue::new(None::<TimeoutHandle>);
|
||||
on_cleanup(move || {
|
||||
|
@ -16,6 +16,7 @@ pub fn throttle(cb: impl Fn() + 'static, duration: Duration) -> impl Fn() -> ()
|
|||
if timeout_handle.with_value(|handle| handle.is_some()) {
|
||||
return;
|
||||
}
|
||||
let cb = cb.clone();
|
||||
let handle = set_timeout_with_handle(
|
||||
move || {
|
||||
cb.call(());
|
||||
|
|
Loading…
Add table
Reference in a new issue