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