diff --git a/CHANGELOG.md b/CHANGELOG.md
index 047d98b..a2c83d9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,12 @@
# Changelog
+## 0.1.4
+
+#### New Functions
+- `use_supported`
+- `use_resize_observer`
+- `watch`
+
## 0.1.3
#### New Functions
diff --git a/Cargo.toml b/Cargo.toml
index 180bc0c..cedd725 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -14,10 +14,23 @@ repository = "https://github.com/Synphonyte/leptos-use"
[dependencies]
leptos = "0.3.0"
-web-sys = { version = "0.3.63", features = ["ScrollToOptions", "ScrollBehavior", "CssStyleDeclaration"] }
wasm-bindgen = "0.2.86"
js-sys = "0.3.63"
default-struct-builder = "0.1.0"
+[dependencies.web-sys]
+version = "0.3.63"
+features = [
+ "ScrollToOptions",
+ "ScrollBehavior",
+ "CssStyleDeclaration",
+ "ResizeObserver",
+ "ResizeObserverOptions",
+ "ResizeObserverBoxOptions",
+ "ResizeObserverSize",
+ "ResizeObserverEntry",
+ "Navigator",
+]
+
[features]
docs = []
\ No newline at end of file
diff --git a/docs/book/src/SUMMARY.md b/docs/book/src/SUMMARY.md
index 2b5eb9f..8d86c14 100644
--- a/docs/book/src/SUMMARY.md
+++ b/docs/book/src/SUMMARY.md
@@ -16,7 +16,12 @@
- [use_scroll](sensors/use_scroll.md)
+# Watch
+
+- [watch](watch/watch.md)
+
# Utilities
- [use_debounce_fn](utilities/use_debounce_fn.md)
+- [use_supported](utilities/use_supported.md)
- [use_throttle_fn](utilities/use_throttle_fn.md)
\ No newline at end of file
diff --git a/docs/book/src/custom.css b/docs/book/src/custom.css
index 78faaf9..e3844ec 100644
--- a/docs/book/src/custom.css
+++ b/docs/book/src/custom.css
@@ -20,4 +20,17 @@
pre > code {
border-radius: 5px;
padding: 1.5rem;
-}
\ No newline at end of file
+}
+
+.meta-data {
+ font-size: 87.5%;
+ display: grid;
+ grid-template-columns: 70px auto;
+ gap: 2rem;
+ align-items: flex-start;
+ margin: 2rem 0 4rem;
+}
+
+.meta-data > div:first-child {
+ opacity: 0.5;
+}
diff --git a/docs/book/src/extract_doc_comment.py b/docs/book/src/extract_doc_comment.py
index 436fb18..7215c4d 100644
--- a/docs/book/src/extract_doc_comment.py
+++ b/docs/book/src/extract_doc_comment.py
@@ -1,8 +1,18 @@
import sys
import re
+import os
def main():
+ category = os.path.split(os.getcwd())[-1]
+
+ print(f"""
+
+""")
+
name = sys.argv[1]
file_name = f"../../../../src/{name}.rs"
@@ -49,8 +59,11 @@ def add_source_paragraph(name):
demo_url = f"https://github.com/Synphonyte/leptos-use/tree/main/examples/{name}"
docs_url = f"https://docs.rs/leptos-use/latest/leptos_use/fn.{name}.html"
+ demo_link = " • Demo" if os.path.isdir(
+ os.path.join("..", "..", "..", "..", "examples", name)) else ""
+
print(
- f"Source • Demo • Docs")
+ f"Source{demo_link} • Docs")
interal_doc_link_pattern = re.compile(r"\[`([^]]+)\`](?!\()")
diff --git a/docs/book/src/utilities/use_supported.md b/docs/book/src/utilities/use_supported.md
new file mode 100644
index 0000000..581e8bd
--- /dev/null
+++ b/docs/book/src/utilities/use_supported.md
@@ -0,0 +1,3 @@
+# use_supported
+
+
diff --git a/docs/book/src/watch/watch.md b/docs/book/src/watch/watch.md
new file mode 100644
index 0000000..6087512
--- /dev/null
+++ b/docs/book/src/watch/watch.md
@@ -0,0 +1,3 @@
+# watch
+
+
diff --git a/src/lib.rs b/src/lib.rs
index a9a3552..3d44d29 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,18 +1,25 @@
//! Collection of essential Leptos utilities inspired by SolidJS USE / VueUse
pub mod core;
-mod use_debounce_fn;
-mod use_event_listener;
-mod use_scroll;
-mod use_throttle_fn;
-pub mod utils;
-
#[cfg(feature = "docs")]
pub mod docs;
+mod use_debounce_fn;
+mod use_event_listener;
+#[cfg(web_sys_unstable_apis)]
+mod use_resize_observer;
+mod use_scroll;
+mod use_supported;
+mod use_throttle_fn;
+pub mod utils;
+mod watch;
pub use use_debounce_fn::*;
pub use use_event_listener::*;
+#[cfg(web_sys_unstable_apis)]
+pub use use_resize_observer::*;
pub use use_scroll::*;
+pub use use_supported::*;
pub use use_throttle_fn::*;
+pub use watch::*;
extern crate self as leptos_use;
diff --git a/src/use_debounce_fn.rs b/src/use_debounce_fn.rs
index dece312..ed1184a 100644
--- a/src/use_debounce_fn.rs
+++ b/src/use_debounce_fn.rs
@@ -64,7 +64,7 @@ use leptos::MaybeSignal;
/// ## Recommended Reading
///
/// - [**Debounce vs Throttle**: Definitive Visual Guide](https://redd.one/blog/debounce-vs-throttle)
-pub fn use_debounce_fn(func: F, ms: impl Into>) -> impl Fn()
+pub fn use_debounce_fn(func: F, ms: impl Into>) -> impl Fn() + Clone
where
F: FnOnce() + Clone + 'static,
{
@@ -76,7 +76,7 @@ pub fn use_debounce_fn_with_options(
func: F,
ms: impl Into>,
options: DebounceOptions,
-) -> impl Fn()
+) -> impl Fn() + Clone
where
F: FnOnce() + Clone + 'static,
{
diff --git a/src/use_resize_observer.rs b/src/use_resize_observer.rs
new file mode 100644
index 0000000..8c980c7
--- /dev/null
+++ b/src/use_resize_observer.rs
@@ -0,0 +1,59 @@
+use crate::core::ElementMaybeSignal;
+use crate::use_supported;
+use default_struct_builder::DefaultBuilder;
+use leptos::*;
+use std::cell::RefCell;
+use std::rc::Rc;
+use wasm_bindgen::{JsCast, JsValue};
+
+pub fn use_resize_observer_with_options(
+ cx: Scope,
+ target: El, // TODO : multiple elements?
+ callback: F,
+ options: web_sys::ResizeObserverOptions,
+) where
+ (Scope, El): Into>,
+ T: Into + Clone + 'static,
+ F: FnMut(Vec, web_sys::ResizeObserver) + 'static,
+{
+ let observer: Rc>> = Rc::new(RefCell::new(None));
+
+ let is_supported = use_supported(cx, || JsValue::from("ResizeObserver").js_in(&window()));
+
+ let obs = Rc::clone(&observer);
+ let cleanup = move || {
+ let observer = obs.borrow_mut();
+ if let Some(o) = *observer {
+ o.disconnect();
+ *observer = None;
+ }
+ };
+
+ let target = target.into();
+
+ let clean = cleanup.clone();
+ create_effect(cx, move |_| {
+ clean();
+
+ if is_supported() {
+ let obs = web_sys::ResizeObserver::new(move |entries: &js_sys::Array, observer| {
+ callback(
+ entries
+ .to_vec()
+ .into_iter()
+ .map(|v| v.unchecked_into::(&options)),
+ observer,
+ );
+ })
+ .expect("failed to create ResizeObserver");
+
+ observer.observe(&target.get());
+
+ observer.replace(obs);
+ }
+ });
+
+ let stop = move || {
+ cleanup();
+ };
+}
diff --git a/src/use_supported.rs b/src/use_supported.rs
new file mode 100644
index 0000000..f8dc29c
--- /dev/null
+++ b/src/use_supported.rs
@@ -0,0 +1,26 @@
+use leptos::*;
+
+/// SSR compatibe `is_supported`
+///
+/// ## Usage
+///
+/// ```
+/// # use leptos::*;
+/// # use leptos_use::use_supported;
+/// # use wasm_bindgen::JsValue;
+/// #
+/// # pub fn Demo(cx: Scope) -> impl IntoView {
+/// let is_supported = use_supported(
+/// cx,
+/// || JsValue::from("getBattery").js_in(&window().navigator())
+/// );
+///
+/// if is_supported() {
+/// // do something
+/// }
+/// # view! { cx, }
+/// # }
+/// ```
+pub fn use_supported(cx: Scope, callback: impl Fn() -> bool + 'static) -> Signal {
+ Signal::derive(cx, callback)
+}
diff --git a/src/watch.rs b/src/watch.rs
new file mode 100644
index 0000000..30a4c24
--- /dev/null
+++ b/src/watch.rs
@@ -0,0 +1,84 @@
+use leptos::*;
+use std::cell::RefCell;
+
+/// A version of `create_effect` that listens to any dependency that is accessed inside `deps`.
+/// Also a stop handler is returned.
+/// If `immediate` is false, the `callback` will not run immediately but only after
+/// the first change is detected of any signal that is accessed in `deps`.
+/// The return value of `deps` is passed into `callback` as an argument together with the previous value
+/// and the previous value that the `callback` itself returned last time.
+///
+/// # Usage
+///
+/// ```
+/// # use std::time::Duration;
+/// # use leptos::*;
+/// # use leptos_use::watch;
+/// #
+/// # pub fn Demo(cx: Scope) -> impl IntoView {
+/// let (num, set_num) = create_signal(cx, 0);
+///
+/// let stop = watch(
+/// cx,
+/// num,
+/// move |num, _, _| {
+/// log!("number {}", num);
+/// },
+/// true,
+/// );
+///
+/// set_num(1); // > "number 1"
+///
+/// set_timeout_with_handle(move || {
+/// stop(); // stop watching
+///
+/// set_num(2); // nothing happens
+/// }, Duration::from_millis(1000));
+/// # view! { cx, }
+/// # }
+/// ```
+pub fn watch(
+ cx: Scope,
+ deps: DFn,
+ callback: CFn,
+ immediate: bool,
+) -> impl Fn() + Clone
+where
+ DFn: Fn() -> W + 'static,
+ CFn: Fn(&W, Option<&W>, Option) -> T + 'static,
+ W: 'static,
+ T: 'static,
+{
+ let (is_active, set_active) = create_signal(cx, true);
+
+ let prev_deps_value: RefCell