use dioxus::prelude::*;
use dioxus_signals::use_signal;
use dioxus_signals::Signal;
use dioxus_use_mounted::UseMounted;
use js_sys::Array;
use web_sys::DomRectReadOnly;
use std::rc::Rc;
use wasm_bindgen::closure::Closure;
use wasm_bindgen::JsCast;
use web_sys::ResizeObserver;
use web_sys::ResizeObserverEntry;
pub type Rect = DomRectReadOnly;
pub fn use_size<T>(cx: Scope<T>, mounted: UseMounted) -> Rect {
let resize = use_resize(cx, mounted);
let resize_ref = resize.read();
resize_ref.clone().unwrap_or_else(|| DomRectReadOnly::new().unwrap())
}
pub fn use_resize<T>(cx: Scope<T>, mounted: UseMounted) -> Signal<Option<Rect>> {
let state_ref: Signal<Option<State>> = use_signal(cx, || None);
let size_ref = use_signal(cx, || None);
dioxus_signals::use_effect(cx, move || {
if let Some(mounted) = mounted.signal.read().clone() {
maybe_unobserve(state_ref);
let on_resize = Closure::<dyn FnMut(Array)>::new(move |entries: Array| {
let entry = entries.at(0);
let entry: ResizeObserverEntry = entry.dyn_into().unwrap();
size_ref.set(Some(entry.content_rect()));
});
let resize_observer = ResizeObserver::new(on_resize.as_ref().unchecked_ref()).unwrap();
let raw_elem = get_raw_element(&mounted);
resize_observer.observe(raw_elem);
state_ref.set(Some(State {
resize_observer,
mounted,
_on_resize: on_resize,
}));
} else {
maybe_unobserve(state_ref);
}
});
size_ref
}
struct State {
resize_observer: ResizeObserver,
mounted: Rc<MountedData>,
_on_resize: Closure<dyn FnMut(Array)>,
}
fn get_raw_element(mounted: &MountedData) -> &web_sys::Element {
mounted
.get_raw_element()
.unwrap()
.downcast_ref::<web_sys::Element>()
.unwrap()
}
fn maybe_unobserve(state_ref: Signal<Option<State>>) {
if let Some(state) = state_ref.write().take() {
let raw_elem = get_raw_element(&state.mounted);
state.resize_observer.unobserve(raw_elem);
}
}