diff --git a/thaw_components/Cargo.toml b/thaw_components/Cargo.toml index ae140ba..174974b 100644 --- a/thaw_components/Cargo.toml +++ b/thaw_components/Cargo.toml @@ -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" diff --git a/thaw_utils/Cargo.toml b/thaw_utils/Cargo.toml index 8a60343..584f4ea 100644 --- a/thaw_utils/Cargo.toml +++ b/thaw_utils/Cargo.toml @@ -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"] diff --git a/thaw_utils/src/class_list.rs b/thaw_utils/src/class_list.rs index 0b24669..6f452ae 100644 --- a/thaw_utils/src/class_list.rs +++ b/thaw_utils/src/class_list.rs @@ -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>>); 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,29 +124,89 @@ impl ClassList { self } + + fn to_class_string(self, class: &mut String) { + self.0.with(|set| { + set.iter().enumerate().for_each(|(index, name)| { + if name.is_empty() { + return; + } + if index != 0 { + class.push(' '); + } + class.push_str(name) + }); + }); + } } -impl IntoAttribute for ClassList { - fn into_attribute(self) -> Attribute { - Attribute::Fn(Rc::new(move || { - self.0.with(|set| { - let mut class = String::new(); - set.iter().enumerate().for_each(|(index, name)| { - if name.is_empty() { - return; - } - if index != 0 { - class.push(' '); - } - class.push_str(name) - }); - class.into_attribute() - }) - })) +impl<'a, R> leptos::tachys::html::class::IntoClass 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 into_attribute_boxed(self: Box) -> Attribute { - self.into_attribute() + fn to_html(self, class: &mut String) { + self.to_class_string(class); + } + + fn hydrate(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(); +// } +// } diff --git a/thaw_utils/src/dom/get_scroll_parent.rs b/thaw_utils/src/dom/get_scroll_parent.rs index 29dd9d6..203c4de 100644 --- a/thaw_utils/src/dom/get_scroll_parent.rs +++ b/thaw_utils/src/dom/get_scroll_parent.rs @@ -1,55 +1,55 @@ -use leptos::{ - html::{AnyElement, ToHtmlElement}, - *, -}; +// use leptos::{ +// html::{AnyElement, ToHtmlElement}, +// *, +// }; -pub fn get_scroll_parent(element: &HtmlElement) -> Option> { - let Some(parent_element) = get_parent_element(element) else { - return None; - }; +// pub fn get_scroll_parent(element: &HtmlElement) -> Option> { +// 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) -> Option> { - if element.node_type() == 9 { - None - } else { - element.parent_element().map(|ele| ele.to_leptos_element()) - } -} +// fn get_parent_element(element: &HtmlElement) -> Option> { +// if element.node_type() == 9 { +// None +// } else { +// element.parent_element().map(|ele| ele.to_leptos_element()) +// } +// } -fn get_overflow(parent_element: &HtmlElement) -> 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) -> 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)) +// } diff --git a/thaw_utils/src/dom/mod.rs b/thaw_utils/src/dom/mod.rs index c5e3111..5bfba60 100644 --- a/thaw_utils/src/dom/mod.rs +++ b/thaw_utils/src/dom/mod.rs @@ -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}; diff --git a/thaw_utils/src/dom/mount_style.rs b/thaw_utils/src/dom/mount_style.rs index 461b972..4a88735 100644 --- a/thaw_utils/src/dom/mount_style.rs +++ b/thaw_utils/src/dom/mount_style.rs @@ -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 String + 'static>(id: String, f: T) { +pub fn mount_dynamic_style 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 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>| { - 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)); }); } } diff --git a/thaw_utils/src/event_listener.rs b/thaw_utils/src/event_listener.rs index 9afdc19..9b1d1f4 100644 --- a/thaw_utils/src/event_listener.rs +++ b/thaw_utils/src/event_listener.rs @@ -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( @@ -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::()) - }) + todo!() + // add_event_listener_untyped(target, &event.name(), move |e| { + // cb(e.unchecked_into::()) + // }) } pub struct EventListenerHandle(Box); @@ -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, - 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, +// 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( target: impl IntoEventTarget, @@ -119,8 +116,8 @@ impl IntoEventTarget for web_sys::Document { } } -impl IntoEventTarget for HtmlElement { - fn into_event_target(self) -> EventTarget { - self.deref().deref().deref().deref().clone() - } -} +// impl IntoEventTarget for HtmlElement { +// fn into_event_target(self) -> EventTarget { +// self.deref().deref().deref().deref().clone() +// } +// } diff --git a/thaw_utils/src/hooks/mod.rs b/thaw_utils/src/hooks/mod.rs index 315230d..64db224 100644 --- a/thaw_utils/src/hooks/mod.rs +++ b/thaw_utils/src/hooks/mod.rs @@ -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; diff --git a/thaw_utils/src/hooks/use_click_position.rs b/thaw_utils/src/hooks/use_click_position.rs index 49df597..29130fc 100644 --- a/thaw_utils/src/hooks/use_click_position.rs +++ b/thaw_utils/src/hooks/use_click_position.rs @@ -1,10 +1,13 @@ -use leptos::{ReadSignal, RwSignal}; +use leptos::reactive_graph::signal::{ReadSignal, RwSignal}; pub fn use_click_position() -> ReadSignal> { 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; diff --git a/thaw_utils/src/hooks/use_lock_html_scroll.rs b/thaw_utils/src/hooks/use_lock_html_scroll.rs index 091178c..cd67f7e 100644 --- a/thaw_utils/src/hooks/use_lock_html_scroll.rs +++ b/thaw_utils/src/hooks/use_lock_html_scroll.rs @@ -1,20 +1,25 @@ -use leptos::MaybeSignal; +use leptos::reactive_graph::wrappers::read::MaybeSignal; pub fn use_lock_html_scroll(is_lock: MaybeSignal) { #[cfg(any(feature = "csr", feature = "hydrate"))] { - use leptos::{create_render_effect, document, on_cleanup, SignalGet, StoredValue}; - let style_el = StoredValue::new(None::); + // 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::)); 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) { _ = 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(); } diff --git a/thaw_utils/src/hooks/use_next_frame.rs b/thaw_utils/src/hooks/use_next_frame.rs index 7b1a60b..c2af79d 100644 --- a/thaw_utils/src/hooks/use_next_frame.rs +++ b/thaw_utils/src/hooks/use_next_frame.rs @@ -1,24 +1,35 @@ +// 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 { - let next_frame = NextFrame::default(); - - on_cleanup(move || { - next_frame.cancel(); - }); - - next_frame -} - -#[derive(Default, Clone)] +#[derive(Clone)] pub struct NextFrame(StoredValue>); +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 || { + next_frame.cancel(); + }); + + next_frame + } + pub fn run(&self, cb: impl FnOnce() + 'static) { self.cancel(); diff --git a/thaw_utils/src/lib.rs b/thaw_utils/src/lib.rs index 3a80f60..2bdd211 100644 --- a/thaw_utils/src/lib.rs +++ b/thaw_utils/src/lib.rs @@ -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; diff --git a/thaw_utils/src/optional_prop.rs b/thaw_utils/src/optional_prop.rs index ffb5daf..78c0007 100644 --- a/thaw_utils/src/optional_prop.rs +++ b/thaw_utils/src/optional_prop.rs @@ -66,19 +66,19 @@ impl From for OptionalProp> { } } -impl From> for OptionalProp> { +impl From> for OptionalProp> { fn from(value: ReadSignal) -> Self { Self(Some(MaybeSignal::from(value))) } } -impl From> for OptionalProp> { +impl From> for OptionalProp> { fn from(value: RwSignal) -> Self { Self(Some(MaybeSignal::from(value))) } } -impl From> for OptionalProp> { +impl From> for OptionalProp> { fn from(value: Memo) -> Self { Self(Some(MaybeSignal::from(value))) } diff --git a/thaw_utils/src/signals/component_ref.rs b/thaw_utils/src/signals/component_ref.rs index 8baf1c1..e371617 100644 --- a/thaw_utils/src/signals/component_ref.rs +++ b/thaw_utils/src/signals/component_ref.rs @@ -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(RwSignal>); -impl Default for ComponentRef { +impl Default for ComponentRef { fn default() -> Self { - Self(create_rw_signal(None)) + Self(RwSignal::new(None)) } } @@ -20,11 +24,14 @@ impl Clone for ComponentRef { impl Copy for ComponentRef {} -impl ComponentRef { +// TODO +impl ComponentRef { pub fn new() -> Self { Self::default() } +} +impl ComponentRef { pub fn get(&self) -> Option where T: Clone, @@ -58,14 +65,10 @@ impl ComponentRef { { 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() -> ComponentRef { - ComponentRef::default() -} diff --git a/thaw_utils/src/signals/mod.rs b/thaw_utils/src/signals/mod.rs index d1f6fc5..a491aee 100644 --- a/thaw_utils/src/signals/mod.rs +++ b/thaw_utils/src/signals/mod.rs @@ -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; diff --git a/thaw_utils/src/signals/model.rs b/thaw_utils/src/signals/model.rs index 87f6356..6d072c3 100644 --- a/thaw_utils/src/signals/model.rs +++ b/thaw_utils/src/signals/model.rs @@ -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 @@ -12,7 +14,7 @@ where on_write: Option>, } -impl Default for Model { +impl Default for Model { fn default() -> Self { RwSignal::new(Default::default()).into() } @@ -26,7 +28,7 @@ impl Clone for Model { impl Copy for Model {} -impl Model { +impl Model { fn new(value: T) -> Self { let rw_signal = RwSignal::new(value); rw_signal.into() @@ -37,91 +39,56 @@ impl Model { } } -impl SignalGet for Model { - type Value = T; - - fn get(&self) -> Self::Value { - self.read.get() - } - - fn try_get(&self) -> Option { - self.read.try_get() +impl DefinedAt for Model { + fn defined_at(&self) -> Option<&'static std::panic::Location<'static>> { + todo!() } } -impl SignalGetUntracked for Model { +impl With for Model { type Value = T; - fn get_untracked(&self) -> Self::Value { - self.read.get_untracked() - } - - fn try_get_untracked(&self) -> Option { - self.read.try_get_untracked() - } -} - -impl SignalSet for Model { - 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 { - if let Some(on_write) = self.on_write.as_ref() { - on_write.try_set(new_value.clone()); - } - self.write.try_set(new_value) - } -} - -impl SignalWith for Model { - type Value = T; - - fn with(&self, f: impl FnOnce(&Self::Value) -> O) -> O { - self.read.with(f) - } - fn try_with(&self, f: impl FnOnce(&Self::Value) -> O) -> Option { self.read.try_with(f) } } -impl SignalWithUntracked for Model { +impl WithUntracked for Model { type Value = T; - fn with_untracked(&self, f: impl FnOnce(&Self::Value) -> O) -> O { - self.read.with_untracked(f) - } - fn try_with_untracked(&self, f: impl FnOnce(&Self::Value) -> O) -> Option { self.read.try_with_untracked(f) } } -impl SignalUpdate for Model { +// TODO +impl Update for Model { type Value = T; - fn update(&self, f: impl FnOnce(&mut Self::Value)) { - self.write.update(f); - } + fn try_maybe_update(&self, fun: impl FnOnce(&mut Self::Value) -> (bool, U)) -> Option { + let value = self.write.try_maybe_update(fun); - fn try_update(&self, f: impl FnOnce(&mut Self::Value) -> O) -> Option { - self.write.try_update(f) + if let Some(on_write) = self.on_write.as_ref() { + on_write.set(self.read.with_untracked(|read| read.clone())); + } + + value } } -impl From for Model { +impl IsDisposed for Model { + fn is_disposed(&self) -> bool { + self.write.is_disposed() + } +} + +impl From for Model { fn from(value: T) -> Self { Self::new(value) } } -impl From> for Model { +impl From> for Model { fn from(rw_signal: RwSignal) -> Self { let (read, write) = rw_signal.split(); Self { @@ -142,7 +109,7 @@ impl From<(Signal, WriteSignal)> for Model { } } -impl From<(ReadSignal, WriteSignal)> for Model { +impl From<(ReadSignal, WriteSignal)> for Model { fn from((read, write): (ReadSignal, WriteSignal)) -> Self { Self { read: read.into(), @@ -152,7 +119,7 @@ impl From<(ReadSignal, WriteSignal)> for Model { } } -impl From<(Memo, WriteSignal)> for Model { +impl From<(Memo, WriteSignal)> for Model { fn from((read, write): (Memo, WriteSignal)) -> Self { Self { read: read.into(), @@ -162,7 +129,7 @@ impl From<(Memo, WriteSignal)> for Model { } } -impl From<(Option, WriteSignal)> for Model { +impl From<(Option, WriteSignal)> for Model { fn from((read, write): (Option, WriteSignal)) -> Self { let mut model = Self::new(read.unwrap_or_default()); model.on_write = Some(write); @@ -170,35 +137,36 @@ impl From<(Option, WriteSignal)> for Model { } } -#[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 = 0.into(); - assert_eq!(model.get_untracked(), 0); - model.set(1); - assert_eq!(model.get_untracked(), 1); +// // T +// let model: Model = 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 = 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 = 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 = (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 = (read, write).into(); +// assert_eq!(model.get_untracked(), 0); +// model.set(1); +// assert_eq!(model.get_untracked(), 1); - runtime.dispose(); - } -} +// runtime.dispose(); +// } +// } diff --git a/thaw_utils/src/signals/optional_maybe_signal.rs b/thaw_utils/src/signals/optional_maybe_signal.rs index 70b782f..d43b3be 100644 --- a/thaw_utils/src/signals/optional_maybe_signal.rs +++ b/thaw_utils/src/signals/optional_maybe_signal.rs @@ -1,4 +1,4 @@ -use leptos::*; +use leptos::prelude::*; use std::ops::Deref; pub struct OptionalMaybeSignal(MaybeSignal>); @@ -37,19 +37,19 @@ impl From> for OptionalMaybeSignal { } } -impl From>> for OptionalMaybeSignal { +impl From>> for OptionalMaybeSignal { fn from(value: ReadSignal>) -> Self { Self(MaybeSignal::Dynamic(value.into())) } } -impl From>> for OptionalMaybeSignal { +impl From>> for OptionalMaybeSignal { fn from(value: RwSignal>) -> Self { Self(MaybeSignal::Dynamic(value.into())) } } -impl From>> for OptionalMaybeSignal { +impl From>> for OptionalMaybeSignal { fn from(value: Memo>) -> Self { Self(MaybeSignal::Dynamic(value.into())) } @@ -67,19 +67,20 @@ impl From>> for OptionalMaybeSignal { } } -#[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 = 12.into(); - let _: OptionalMaybeSignal = Some(12).into(); - let _: OptionalMaybeSignal = MaybeSignal::Static(Some(12)).into(); +// let _: MaybeSignal = 12.into(); +// let _: OptionalMaybeSignal = Some(12).into(); +// let _: OptionalMaybeSignal = MaybeSignal::Static(Some(12)).into(); - runtime.dispose(); - } -} +// runtime.dispose(); +// } +// } diff --git a/thaw_utils/src/signals/signal_watch.rs b/thaw_utils/src/signals/signal_watch.rs index 51f37fd..0da02e8 100644 --- a/thaw_utils/src/signals/signal_watch.rs +++ b/thaw_utils/src/signals/signal_watch.rs @@ -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; } -impl SignalWatch for RwSignal { +impl SignalWatch for RwSignal { type Value = T; /// Listens for RwSignal changes and is not executed immediately @@ -31,7 +31,7 @@ impl SignalWatch for RwSignal { fn watch(&self, f: impl Fn(&Self::Value) + 'static) -> Box { 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)); diff --git a/thaw_utils/src/signals/stored_maybe_signal.rs b/thaw_utils/src/signals/stored_maybe_signal.rs index fedbe40..b191509 100644 --- a/thaw_utils/src/signals/stored_maybe_signal.rs +++ b/thaw_utils/src/signals/stored_maybe_signal.rs @@ -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 Copy for StoredMaybeSignal {} -impl SignalGet for StoredMaybeSignal { - type Value = T; - - fn get(&self) -> Self::Value { +impl DefinedAt for StoredMaybeSignal { + fn defined_at(&self) -> Option<&'static std::panic::Location<'static>> { match self { - StoredMaybeSignal::StoredValue(value) => value.get_value(), - StoredMaybeSignal::Signal(signal) => signal.get(), - } - } - - fn try_get(&self) -> Option { - match self { - StoredMaybeSignal::StoredValue(value) => value.try_get_value(), - StoredMaybeSignal::Signal(signal) => signal.try_get(), + StoredMaybeSignal::StoredValue(value) => value.defined_at(), + StoredMaybeSignal::Signal(signal) => signal.defined_at(), } } } -impl SignalGetUntracked for StoredMaybeSignal { +impl With for StoredMaybeSignal { 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 { - match self { - StoredMaybeSignal::StoredValue(value) => value.try_get_value(), - StoredMaybeSignal::Signal(signal) => signal.try_get_untracked(), - } - } -} - -impl SignalWith for StoredMaybeSignal { - type Value = T; - - fn with(&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(&self, f: impl FnOnce(&Self::Value) -> O) -> Option { match self { StoredMaybeSignal::StoredValue(value) => value.try_with_value(f), @@ -68,16 +35,9 @@ impl SignalWith for StoredMaybeSignal { } } -impl SignalWithUntracked for StoredMaybeSignal { +impl WithUntracked for StoredMaybeSignal { type Value = T; - fn with_untracked(&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(&self, f: impl FnOnce(&Self::Value) -> O) -> Option { match self { StoredMaybeSignal::StoredValue(value) => value.try_with_value(f), @@ -86,7 +46,7 @@ impl SignalWithUntracked for StoredMaybeSignal { } } -impl From> for StoredMaybeSignal { +impl From> for StoredMaybeSignal { fn from(value: MaybeSignal) -> Self { match value { MaybeSignal::Static(value) => Self::StoredValue(StoredValue::new(value)), diff --git a/thaw_utils/src/throttle.rs b/thaw_utils/src/throttle.rs index a71e061..a637b35 100644 --- a/thaw_utils/src/throttle.rs +++ b/thaw_utils/src/throttle.rs @@ -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::); 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(());