diff --git a/.cargo/config.toml b/.cargo/config.toml
deleted file mode 100644
index b891103..0000000
--- a/.cargo/config.toml
+++ /dev/null
@@ -1,3 +0,0 @@
-[unstable]
-rustflags = ["--cfg=web_sys_unstable_apis"]
-rustdocflags = ["--cfg=web_sys_unstable_apis"]
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 82633c2..e1c38c4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,15 @@
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [0.8.2] - 2023-11-09
+
+### Fixes 🍕
+
+- Fixed SSR for
+ - use_timestamp
+ - use_raf_fn
+ - use_idle
+
## [0.8.1] - 2023-10-28
### Fixes 🍕
diff --git a/Cargo.toml b/Cargo.toml
index 05ce484..03e168e 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "leptos-use"
-version = "0.8.1"
+version = "0.8.2"
edition = "2021"
authors = ["Marc-Stefan Cassola"]
categories = ["gui", "web-programming"]
@@ -27,7 +27,7 @@ prost = { version = "0.12", optional = true }
serde = { version = "1", optional = true }
serde_json = { version = "1", optional = true }
thiserror = "1.0"
-wasm-bindgen = "0.2"
+wasm-bindgen = "0.2.87"
wasm-bindgen-futures = "0.4"
[dependencies.web-sys]
@@ -102,5 +102,3 @@ ssr = []
[package.metadata.docs.rs]
all-features = true
-rustdoc-args = ["--cfg=web_sys_unstable_apis"]
-rustc-args = ["--cfg=web_sys_unstable_apis"]
diff --git a/examples/.cargo/config.toml b/examples/.cargo/config.toml
deleted file mode 100644
index c87f326..0000000
--- a/examples/.cargo/config.toml
+++ /dev/null
@@ -1,2 +0,0 @@
-[build]
-rustflags = ["--cfg=web_sys_unstable_apis", "--cfg=has_std"]
diff --git a/examples/ssr/src/app.rs b/examples/ssr/src/app.rs
index 20f3b91..2d5da01 100644
--- a/examples/ssr/src/app.rs
+++ b/examples/ssr/src/app.rs
@@ -5,8 +5,8 @@ use leptos_meta::*;
use leptos_router::*;
use leptos_use::storage::use_local_storage;
use leptos_use::{
- use_color_mode, use_debounce_fn, use_event_listener, use_intl_number_format, use_window,
- ColorMode, UseColorModeReturn, UseIntlNumberFormatOptions,
+ use_color_mode, use_debounce_fn, use_event_listener, use_intl_number_format, use_timestamp,
+ use_window, ColorMode, UseColorModeReturn, UseIntlNumberFormatOptions,
};
#[component]
@@ -65,6 +65,8 @@ fn HomePage() -> impl IntoView {
let UseColorModeReturn { mode, set_mode, .. } = use_color_mode();
+ let timestamp = use_timestamp();
+
view! {
Leptos-Use SSR Example
@@ -75,5 +77,6 @@ fn HomePage() -> impl IntoView {
+ {timestamp}
}
}
diff --git a/examples/use_element_size/.cargo/config.toml b/examples/use_element_size/.cargo/config.toml
deleted file mode 100644
index 8467175..0000000
--- a/examples/use_element_size/.cargo/config.toml
+++ /dev/null
@@ -1,2 +0,0 @@
-[build]
-rustflags = ["--cfg=web_sys_unstable_apis"]
diff --git a/src/core/datetime.rs b/src/core/datetime.rs
new file mode 100644
index 0000000..66e72fb
--- /dev/null
+++ b/src/core/datetime.rs
@@ -0,0 +1,16 @@
+use cfg_if::cfg_if;
+
+/// SSR safe `Date.now()`.
+#[inline(always)]
+pub(crate) fn now() -> f64 {
+ cfg_if! { if #[cfg(feature = "ssr")] {
+ use std::time::{SystemTime, UNIX_EPOCH};
+
+ SystemTime::now()
+ .duration_since(UNIX_EPOCH)
+ .expect("Time went backwards")
+ .as_millis() as f64
+ } else {
+ js_sys::Date::now()
+ }}
+}
diff --git a/src/core/element_maybe_signal.rs b/src/core/element_maybe_signal.rs
index 20dd423..a100b7e 100644
--- a/src/core/element_maybe_signal.rs
+++ b/src/core/element_maybe_signal.rs
@@ -179,6 +179,7 @@ where
{
fn from(target: &'a str) -> Self {
cfg_if! { if #[cfg(feature = "ssr")] {
+ let _ = target;
Self::Static(None)
} else {
Self::Static(document().query_selector(target).unwrap_or_default())
@@ -201,6 +202,7 @@ where
{
fn from(signal: Signal) -> Self {
cfg_if! { if #[cfg(feature = "ssr")] {
+ let _ = signal;
Self::Dynamic(Signal::derive(|| None))
} else {
Self::Dynamic(
diff --git a/src/core/mod.rs b/src/core/mod.rs
index 25d7d04..9abe354 100644
--- a/src/core/mod.rs
+++ b/src/core/mod.rs
@@ -1,4 +1,5 @@
mod connection_ready_state;
+mod datetime;
mod direction;
mod element_maybe_signal;
mod elements_maybe_signal;
@@ -10,6 +11,7 @@ mod ssr_safe_method;
mod storage;
pub use connection_ready_state::*;
+pub(crate) use datetime::*;
pub use direction::*;
pub use element_maybe_signal::*;
pub use elements_maybe_signal::*;
diff --git a/src/lib.rs b/src/lib.rs
index 7ca96a3..caf6dd5 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,8 +1,6 @@
// #![feature(doc_cfg)]
//! Collection of essential Leptos utilities inspired by SolidJS USE / VueUse
-use cfg_if::cfg_if;
-
pub mod core;
#[cfg(feature = "docs")]
pub mod docs;
@@ -11,14 +9,6 @@ pub mod math;
pub mod storage;
pub mod utils;
-cfg_if! { if #[cfg(web_sys_unstable_apis)] {
- mod use_element_size;
- mod use_resize_observer;
-
- pub use use_element_size::*;
- pub use use_resize_observer::*;
-}}
-
mod is_err;
mod is_none;
mod is_ok;
@@ -37,6 +27,7 @@ mod use_document_visibility;
mod use_draggable;
mod use_drop_zone;
mod use_element_hover;
+mod use_element_size;
mod use_element_visibility;
mod use_event_listener;
mod use_favicon;
@@ -53,6 +44,7 @@ mod use_mutation_observer;
mod use_preferred_contrast;
mod use_preferred_dark;
mod use_raf_fn;
+mod use_resize_observer;
mod use_scroll;
mod use_service_worker;
mod use_sorted;
@@ -89,6 +81,7 @@ pub use use_document_visibility::*;
pub use use_draggable::*;
pub use use_drop_zone::*;
pub use use_element_hover::*;
+pub use use_element_size::*;
pub use use_element_visibility::*;
pub use use_event_listener::*;
pub use use_favicon::*;
@@ -105,6 +98,7 @@ pub use use_mutation_observer::*;
pub use use_preferred_contrast::*;
pub use use_preferred_dark::*;
pub use use_raf_fn::*;
+pub use use_resize_observer::*;
pub use use_scroll::*;
pub use use_service_worker::*;
pub use use_sorted::*;
diff --git a/src/use_element_size.rs b/src/use_element_size.rs
index 8831505..bc6a226 100644
--- a/src/use_element_size.rs
+++ b/src/use_element_size.rs
@@ -12,9 +12,6 @@ cfg_if! { if #[cfg(not(feature = "ssr"))] {
/// Reactive size of an HTML element.
///
-/// > This function requires `--cfg=web_sys_unstable_apis` to be activated as
-/// [described in the wasm-bindgen guide](https://rustwasm.github.io/docs/wasm-bindgen/web-sys/unstable-apis.html).
-///
/// Please refer to [ResizeObserver on MDN](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver)
/// for more details.
///
@@ -25,12 +22,12 @@ cfg_if! { if #[cfg(not(feature = "ssr"))] {
/// ## Usage
///
/// ```
-/// # use leptos::*;
+/// # use leptos::{html::Div, *};
/// # use leptos_use::{use_element_size, UseElementSizeReturn};
/// #
/// # #[component]
/// # fn Demo() -> impl IntoView {
-/// let el = create_node_ref();
+/// let el = create_node_ref::();
///
/// let UseElementSizeReturn { width, height } = use_element_size(el);
///
@@ -175,7 +172,7 @@ where
}
}
-#[derive(DefaultBuilder)]
+#[derive(DefaultBuilder, Default)]
/// Options for [`use_element_size_with_options`].
pub struct UseElementSizeOptions {
/// Initial size returned before any measurements on the `target` are done. Also the value reported
@@ -187,15 +184,6 @@ pub struct UseElementSizeOptions {
pub box_: Option
,
}
-impl Default for UseElementSizeOptions {
- fn default() -> Self {
- Self {
- initial_size: Size::default(),
- box_: None,
- }
- }
-}
-
/// The return value of [`use_element_size`].
pub struct UseElementSizeReturn {
/// The width of the element.
diff --git a/src/use_idle.rs b/src/use_idle.rs
index 247c867..8a9cc96 100644
--- a/src/use_idle.rs
+++ b/src/use_idle.rs
@@ -1,8 +1,10 @@
+use crate::core::now;
use crate::utils::{create_filter_wrapper, DebounceOptions, FilterOptions, ThrottleOptions};
use crate::{
filter_builder_methods, use_document, use_event_listener, use_event_listener_with_options,
UseEventListenerOptions,
};
+use cfg_if::cfg_if;
use default_struct_builder::DefaultBuilder;
use leptos::ev::{visibilitychange, Custom};
use leptos::leptos_dom::helpers::TimeoutHandle;
@@ -54,6 +56,18 @@ use std::time::Duration;
/// # view! { }
/// # }
/// ```
+///
+/// ## Server-Side Rendering
+///
+/// On the server this will always return static signals
+///
+/// ```ignore
+/// UseIdleReturn{
+/// idle: Signal(initial_state),
+/// last_active: Signal(now),
+/// reset: || {}
+/// }
+/// ```
pub fn use_idle(timeout: u64) -> UseIdleReturn {
use_idle_with_options(timeout, UseIdleOptions::default())
}
@@ -71,57 +85,65 @@ pub fn use_idle_with_options(
} = options;
let (idle, set_idle) = create_signal(initial_state);
- let (last_active, set_last_active) = create_signal(js_sys::Date::now());
+ let (last_active, set_last_active) = create_signal(now());
- let reset = {
- let timer = Cell::new(None::);
+ cfg_if! { if #[cfg(feature = "ssr")] {
+ let reset = || ();
+ let _ = timeout;
+ let _ = events;
+ let _ = listen_for_visibility_change;
+ let _ = filter;
+ } else {
+ let reset = {
+ let timer = Cell::new(None::);
- move || {
- set_idle.set(false);
- if let Some(timer) = timer.take() {
- timer.clear();
+ move || {
+ set_idle.set(false);
+ if let Some(timer) = timer.take() {
+ timer.clear();
+ }
+ timer.replace(
+ set_timeout_with_handle(move || set_idle.set(true), Duration::from_millis(timeout))
+ .ok(),
+ );
}
- timer.replace(
- set_timeout_with_handle(move || set_idle.set(true), Duration::from_millis(timeout))
- .ok(),
+ };
+
+ let on_event = {
+ let reset = reset.clone();
+
+ let filtered_callback = create_filter_wrapper(filter.filter_fn(), move || {
+ set_last_active.set(js_sys::Date::now());
+ reset();
+ });
+
+ move |_: web_sys::Event| {
+ filtered_callback();
+ }
+ };
+
+ let listener_options = UseEventListenerOptions::default().passive(true);
+ for event in events {
+ let _ = use_event_listener_with_options(
+ use_document(),
+ Custom::new(event),
+ on_event.clone(),
+ listener_options,
);
}
- };
- let on_event = {
- let reset = reset.clone();
+ if listen_for_visibility_change {
+ let on_event = on_event.clone();
- let filtered_callback = create_filter_wrapper(filter.filter_fn(), move || {
- set_last_active.set(js_sys::Date::now());
- reset();
- });
-
- move |_: web_sys::Event| {
- filtered_callback();
+ let _ = use_event_listener(use_document(), visibilitychange, move |evt| {
+ if !document().hidden() {
+ on_event(evt);
+ }
+ });
}
- };
- let listener_options = UseEventListenerOptions::default().passive(true);
- for event in events {
- let _ = use_event_listener_with_options(
- use_document(),
- Custom::new(event),
- on_event.clone(),
- listener_options,
- );
- }
-
- if listen_for_visibility_change {
- let on_event = on_event.clone();
-
- let _ = use_event_listener(use_document(), visibilitychange, move |evt| {
- if !document().hidden() {
- on_event(evt);
- }
- });
- }
-
- reset.clone()();
+ reset.clone()();
+ }}
UseIdleReturn {
idle: idle.into(),
diff --git a/src/use_raf_fn.rs b/src/use_raf_fn.rs
index c44a00a..b18503c 100644
--- a/src/use_raf_fn.rs
+++ b/src/use_raf_fn.rs
@@ -1,10 +1,9 @@
use crate::utils::Pausable;
+use cfg_if::cfg_if;
use default_struct_builder::DefaultBuilder;
use leptos::*;
use std::cell::{Cell, RefCell};
use std::rc::Rc;
-use wasm_bindgen::closure::Closure;
-use wasm_bindgen::JsCast;
/// Call function on every requestAnimationFrame.
/// With controls of pausing and resuming.
@@ -34,6 +33,10 @@ use wasm_bindgen::JsCast;
///
/// You can use `use_raf_fn_with_options` and set `immediate` to `false`. In that case
/// you have to call `resume()` before the `callback` is executed.
+///
+/// ## Server-Side Rendering
+///
+/// On the server this does basically nothing. The provided closure will never be called.
pub fn use_raf_fn(
callback: impl Fn(UseRafFnCallbackArgs) + 'static,
) -> Pausable {
@@ -54,24 +57,31 @@ pub fn use_raf_fn_with_options(
let loop_ref = Rc::new(RefCell::new(Box::new(|_: f64| {}) as Box));
let request_next_frame = {
- let loop_ref = Rc::clone(&loop_ref);
- let raf_handle = Rc::clone(&raf_handle);
+ cfg_if! { if #[cfg(feature = "ssr")] {
+ move || ()
+ } else {
+ use wasm_bindgen::JsCast;
+ use wasm_bindgen::closure::Closure;
- move || {
let loop_ref = Rc::clone(&loop_ref);
+ let raf_handle = Rc::clone(&raf_handle);
- raf_handle.set(
- window()
- .request_animation_frame(
- Closure::once_into_js(move |timestamp: f64| {
- loop_ref.borrow()(timestamp);
- })
- .as_ref()
- .unchecked_ref(),
- )
- .ok(),
- );
- }
+ move || {
+ let loop_ref = Rc::clone(&loop_ref);
+
+ raf_handle.set(
+ window()
+ .request_animation_frame(
+ Closure::once_into_js(move |timestamp: f64| {
+ loop_ref.borrow()(timestamp);
+ })
+ .as_ref()
+ .unchecked_ref(),
+ )
+ .ok(),
+ );
+ }
+ }}
};
let loop_fn = {
diff --git a/src/use_resize_observer.rs b/src/use_resize_observer.rs
index 2e35e85..07e5dd4 100644
--- a/src/use_resize_observer.rs
+++ b/src/use_resize_observer.rs
@@ -12,9 +12,6 @@ cfg_if! { if #[cfg(not(feature = "ssr"))] {
/// Reports changes to the dimensions of an Element's content or the border-box.
///
-/// > This function requires `--cfg=web_sys_unstable_apis` to be activated as
-/// [described in the wasm-bindgen guide](https://rustwasm.github.io/docs/wasm-bindgen/web-sys/unstable-apis.html).
-///
/// Please refer to [ResizeObserver on MDN](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver)
/// for more details.
///
@@ -25,12 +22,12 @@ cfg_if! { if #[cfg(not(feature = "ssr"))] {
/// ## Usage
///
/// ```
-/// # use leptos::*;
+/// # use leptos::{html::Div, *};
/// # use leptos_use::use_resize_observer;
/// #
/// # #[component]
/// # fn Demo() -> impl IntoView {
-/// let el = create_node_ref();
+/// let el = create_node_ref::();
/// let (text, set_text) = create_signal("".to_string());
///
/// use_resize_observer(
diff --git a/src/use_timestamp.rs b/src/use_timestamp.rs
index 1718e4f..cdc2576 100644
--- a/src/use_timestamp.rs
+++ b/src/use_timestamp.rs
@@ -1,3 +1,4 @@
+use crate::core::now;
use crate::utils::Pausable;
use crate::{
use_interval_fn_with_options, use_raf_fn_with_options, UseIntervalFnOptions, UseRafFnOptions,
@@ -47,7 +48,8 @@ use std::rc::Rc;
///
/// ## Server-Side Rendering
///
-/// On the server this function will simply be ignored.
+/// On the server this function will return a signal with the milliseconds since the Unix epoch.
+/// But the signal will never update (as there's no `request_animation_frame` on the server).
pub fn use_timestamp() -> Signal {
use_timestamp_with_controls().timestamp
}
@@ -71,10 +73,10 @@ pub fn use_timestamp_with_controls_and_options(options: UseTimestampOptions) ->
callback,
} = options;
- let (ts, set_ts) = create_signal(js_sys::Date::now() + offset);
+ let (ts, set_ts) = create_signal(now() + offset);
let update = move || {
- set_ts.set(js_sys::Date::now() + offset);
+ set_ts.set(now() + offset);
};
let cb = {
diff --git a/src/utils/filters/throttle.rs b/src/utils/filters/throttle.rs
index caaa15e..ceda428 100644
--- a/src/utils/filters/throttle.rs
+++ b/src/utils/filters/throttle.rs
@@ -1,8 +1,8 @@
#![cfg_attr(feature = "ssr", allow(unused_variables, unused_imports))]
+use crate::core::now;
use cfg_if::cfg_if;
use default_struct_builder::DefaultBuilder;
-use js_sys::Date;
use leptos::leptos_dom::helpers::TimeoutHandle;
use leptos::{set_timeout_with_handle, MaybeSignal, SignalGetUntracked};
use std::cell::{Cell, RefCell};
@@ -51,7 +51,7 @@ where
move |mut _invoke: Rc R>| {
let duration = ms.get_untracked();
- let elapsed = Date::now() - last_exec.get();
+ let elapsed = now() - last_exec.get();
let last_return_val = Rc::clone(&last_return_value);
let invoke = move || {
@@ -65,13 +65,13 @@ where
clear();
if duration <= 0.0 {
- last_exec.set(Date::now());
+ last_exec.set(now());
invoke();
return Rc::clone(&last_return_value);
}
if elapsed > duration && (options.leading || !is_leading.get()) {
- last_exec.set(Date::now());
+ last_exec.set(now());
invoke();
} else if options.trailing {
cfg_if! { if #[cfg(not(feature = "ssr"))] {
@@ -80,7 +80,7 @@ where
timer.set(
set_timeout_with_handle(
move || {
- last_exec.set(Date::now());
+ last_exec.set(now());
is_leading.set(true);
invoke();
clear();