From 7f5ee51e49b04097ad62ed616daea59f68b646c0 Mon Sep 17 00:00:00 2001 From: Maccesch Date: Wed, 13 Sep 2023 00:17:16 +0100 Subject: [PATCH] added use_window and use_document --- CHANGELOG.md | 2 + docs/book/src/SUMMARY.md | 2 + docs/book/src/elements/use_document.md | 3 ++ docs/book/src/elements/use_window.md | 3 ++ examples/Cargo.toml | 2 + src/lib.rs | 4 ++ src/use_document.rs | 51 ++++++++++++++++++++++ src/use_geolocation.rs | 5 ++- src/use_window.rs | 59 ++++++++++++++++++++++++++ 9 files changed, 129 insertions(+), 2 deletions(-) create mode 100644 docs/book/src/elements/use_document.md create mode 100644 docs/book/src/elements/use_window.md create mode 100644 src/use_document.rs create mode 100644 src/use_window.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index ecaaa65..43f0a19 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### New Functions 🚀 +- `use_document` +- `use_window` - `use_geolocation` - `signal_debounced` - `signal_throttled` diff --git a/docs/book/src/SUMMARY.md b/docs/book/src/SUMMARY.md index 1a82b6e..2beba1e 100644 --- a/docs/book/src/SUMMARY.md +++ b/docs/book/src/SUMMARY.md @@ -15,6 +15,7 @@ # Elements - [use_active_element](elements/use_active_element.md) +- [use_document](elements/use_document.md) - [use_document_visibility](elements/use_document_visibility.md) - [use_draggable](elements/use_draggable.md) - [use_drop_zone](elements/use_drop_zone.md) @@ -23,6 +24,7 @@ - [use_intersection_observer](elements/use_intersection_observer.md) - [use_mutation_observer](elements/use_mutation_observer.md) - [use_resize_observer](elements/use_resize_observer.md) +- [use_window](elements/use_window.md) - [use_window_focus](elements/use_window_focus.md) - [use_window_scroll](elements/use_window_scroll.md) diff --git a/docs/book/src/elements/use_document.md b/docs/book/src/elements/use_document.md new file mode 100644 index 0000000..838ae90 --- /dev/null +++ b/docs/book/src/elements/use_document.md @@ -0,0 +1,3 @@ +# use_document + + diff --git a/docs/book/src/elements/use_window.md b/docs/book/src/elements/use_window.md new file mode 100644 index 0000000..ee5e14a --- /dev/null +++ b/docs/book/src/elements/use_window.md @@ -0,0 +1,3 @@ +# use_window + + diff --git a/examples/Cargo.toml b/examples/Cargo.toml index ee3b98e..e1fe9e5 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -13,6 +13,7 @@ members = [ "use_css_var", "use_cycle_list", "use_debounce_fn", + "use_document", "use_document_visibility", "use_draggable", "use_drop_zone", @@ -37,6 +38,7 @@ members = [ "use_storage", "use_throttle_fn", "use_websocket", + "use_window", "use_window_focus", "use_window_scroll", "watch_debounced", diff --git a/src/lib.rs b/src/lib.rs index aac6d68..3fde831 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -25,6 +25,8 @@ mod is_none; mod is_ok; mod is_some; mod on_click_outside; +mod use_document; +mod use_window; mod use_geolocation; mod signal_debounced; mod signal_throttled; @@ -69,6 +71,8 @@ pub use is_none::*; pub use is_ok::*; pub use is_some::*; pub use on_click_outside::*; +pub use use_document::*; +pub use use_window::*; pub use use_geolocation::*; pub use signal_debounced::*; pub use signal_throttled::*; diff --git a/src/use_document.rs b/src/use_document.rs new file mode 100644 index 0000000..aa84862 --- /dev/null +++ b/src/use_document.rs @@ -0,0 +1,51 @@ +use cfg_if::cfg_if; +use default_struct_builder::DefaultBuilder; +use leptos::*; +use std::ops::Deref; + +/// SSR safe `document()`. +/// This returns just a new-type wrapper around `Option`. +/// Calling this amounts to `None` on the server and `Some(Document)` on the client. +/// +/// It provides some convenient methods for working with the document like `body()`. +/// +/// ## Usage +/// +/// ``` +/// # use leptos::*; +/// # use leptos_use::use_document; +/// # +/// # #[component] +/// # fn Demo() -> impl IntoView { +/// let document = use_document(); +/// +/// // Returns `None` on the server but will not panic. +/// let body = document.body(); +/// # +/// # view! { } +/// # } +/// ``` +pub fn use_document() -> UseDocument { + cfg_if! { if #[cfg(feature = "ssr")] { + UseDocument(None) + } else { + UseDocument(Some(document())) + }} +} + +/// Return type of [`use_document`]. +pub struct UseDocument(Option); + +impl Deref for UseDocument { + type Target = Option; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl UseDocument { + /// Returns `Some(HtmlElement)` in the Browser. `None` otherwise. + pub fn body(&self) -> Option { + self.0.as_ref().and_then(|d| d.body()) + } +} diff --git a/src/use_geolocation.rs b/src/use_geolocation.rs index 8388f03..034828f 100644 --- a/src/use_geolocation.rs +++ b/src/use_geolocation.rs @@ -1,3 +1,4 @@ +use crate::use_window; use default_struct_builder::DefaultBuilder; use leptos::*; use std::cell::Cell; @@ -60,7 +61,7 @@ pub fn use_geolocation_with_options( let position_options = options.as_position_options(); move || { - let navigator = Some(window().navigator()); + let navigator = use_window().navigator(); if let Some(navigator) = navigator { if let Ok(geolocation) = navigator.geolocation() { let update_position = @@ -93,7 +94,7 @@ pub fn use_geolocation_with_options( let watch_handle = Rc::clone(&watch_handle); move || { - let navigator = Some(window().navigator()); + let navigator = use_window().navigator(); if let Some(navigator) = navigator { if let Some(handle) = watch_handle.take() { if let Ok(geolocation) = navigator.geolocation() { diff --git a/src/use_window.rs b/src/use_window.rs new file mode 100644 index 0000000..58d9428 --- /dev/null +++ b/src/use_window.rs @@ -0,0 +1,59 @@ +use crate::{use_document, UseDocument}; +use cfg_if::cfg_if; +use default_struct_builder::DefaultBuilder; +use leptos::*; +use std::ops::Deref; + +/// SSR safe `window()`. +/// This returns just a new-type wrapper around `Option`. +/// Calling this amounts to `None` on the server and `Some(Window)` on the client. +/// +/// It provides some convenient methods for working with the window like `document()` and `navigator()`. +/// These will all return `None` on the server. +/// +/// ## Usage +/// +/// ``` +/// # use leptos::*; +/// # use leptos_use::use_window; +/// # +/// # #[component] +/// # fn Demo() -> impl IntoView { +/// let window = use_window(); +/// +/// // Returns `None` on the server but will not panic. +/// let navigator = window.navigator(); +/// # +/// # view! { } +/// # } +/// ``` +pub fn use_window() -> UseWindow { + cfg_if! { if #[cfg(feature = "ssr")] { + UseWindow(None) + } else { + UseWindow(Some(window())) + }} +} + +/// Return type of [`use_window`]. +pub struct UseWindow(Option); + +impl Deref for UseWindow { + type Target = Option; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl UseWindow { + /// Returns the `Some(Navigator)` in the Browser. `None` otherwise. + pub fn navigator(&self) -> Option { + self.0.as_ref().map(|w| w.navigator()) + } + + /// Returns the same as [`use_document`]. + #[inline(always)] + pub fn document(&self) -> UseDocument { + use_document() + } +}