use crate::core::ElementsMaybeSignal; use cfg_if::cfg_if; use default_struct_builder::DefaultBuilder; use leptos::*; cfg_if! { if #[cfg(not(feature = "ssr"))] { use crate::use_supported; use std::cell::RefCell; use std::rc::Rc; use wasm_bindgen::prelude::*; }} /// Reports changes to the dimensions of an Element's content or the border-box. /// /// Please refer to [ResizeObserver on MDN](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver) /// for more details. /// /// ## Demo /// /// [Link to Demo](https://github.com/Synphonyte/leptos-use/tree/main/examples/use_resize_observer) /// /// ## Usage /// /// ``` /// # use leptos::{html::Div, *}; /// # use leptos_use::use_resize_observer; /// # /// # #[component] /// # fn Demo() -> impl IntoView { /// let el = create_node_ref::
(); /// let (text, set_text) = create_signal("".to_string()); /// /// use_resize_observer( /// el, /// move |entries, observer| { /// let rect = entries[0].content_rect(); /// set_text.set(format!("width: {}\nheight: {}", rect.width(), rect.height())); /// }, /// ); /// /// view! { ///
{ move || text.get() }
/// } /// # } /// ``` /// /// ## Server-Side Rendering /// /// On the server this amounts to a no-op. /// /// ## See also /// /// * [`fn@crate::use_element_size`] pub fn use_resize_observer( target: El, // TODO : multiple elements? callback: F, ) -> UseResizeObserverReturn where El: Into>, T: Into + Clone + 'static, F: FnMut(Vec, web_sys::ResizeObserver) + 'static, { use_resize_observer_with_options(target, callback, UseResizeObserverOptions::default()) } /// Version of [`use_resize_observer`] that takes a `web_sys::ResizeObserverOptions`. See [`use_resize_observer`] for how to use. #[cfg_attr(feature = "ssr", allow(unused_variables, unused_mut))] pub fn use_resize_observer_with_options( target: El, // TODO : multiple elements? mut callback: F, options: UseResizeObserverOptions, ) -> UseResizeObserverReturn where El: Into>, T: Into + Clone + 'static, F: FnMut(Vec, web_sys::ResizeObserver) + 'static, { #[cfg(feature = "ssr")] { UseResizeObserverReturn { is_supported: Signal::derive(|| true), stop: || {}, } } #[cfg(not(feature = "ssr"))] { use crate::js; let closure_js = Closure::::new( move |entries: js_sys::Array, observer| { #[cfg(debug_assertions)] let prev = SpecialNonReactiveZone::enter(); callback( entries .to_vec() .into_iter() .map(|v| v.unchecked_into::()) .collect(), observer, ); #[cfg(debug_assertions)] SpecialNonReactiveZone::exit(prev); }, ) .into_js_value(); let observer: Rc>> = Rc::new(RefCell::new(None)); let is_supported = use_supported(|| js!("ResizeObserver" in &window())); let cleanup = { let observer = Rc::clone(&observer); move || { let mut observer = observer.borrow_mut(); if let Some(o) = observer.as_ref() { o.disconnect(); *observer = None; } } }; let targets = target.into(); let stop_watch = { let cleanup = cleanup.clone(); watch( move || targets.get(), move |targets, _, _| { cleanup(); if is_supported.get_untracked() && !targets.is_empty() { let obs = web_sys::ResizeObserver::new( closure_js.clone().as_ref().unchecked_ref(), ) .expect("failed to create ResizeObserver"); for target in targets.iter().flatten() { let target: web_sys::Element = target.clone().into(); obs.observe_with_options(&target, &options.clone().into()); } observer.replace(Some(obs)); } }, true, ) }; let stop = move || { cleanup(); stop_watch(); }; on_cleanup(stop.clone()); UseResizeObserverReturn { is_supported, stop } } } /// Options for [`use_resize_observer_with_options`]. #[derive(DefaultBuilder, Clone, Default)] pub struct UseResizeObserverOptions { /// The box that is used to determine the dimensions of the target. Defaults to `ContentBox`. #[builder(into)] pub box_: Option, } impl From for web_sys::ResizeObserverOptions { fn from(val: UseResizeObserverOptions) -> Self { let mut options = web_sys::ResizeObserverOptions::new(); options.box_( val.box_ .unwrap_or(web_sys::ResizeObserverBoxOptions::ContentBox), ); options } } /// The return value of [`use_resize_observer`]. pub struct UseResizeObserverReturn { /// Whether the browser supports the ResizeObserver API pub is_supported: Signal, /// A function to stop and detach the ResizeObserver pub stop: F, }