use perspective_js::utils::ApiFuture;
use wasm_bindgen::JsCast;
use wasm_bindgen::prelude::*;
use web_sys::*;
use yew::prelude::*;
use crate::components::viewer::{PerspectiveViewer, PerspectiveViewerMsg};
use crate::js::*;
use crate::presentation::Presentation;
use crate::renderer::*;
use crate::root::Root;
use crate::session::{Session, TableLoadState};
use crate::tasks::*;
use crate::utils::*;
pub struct ResizeObserverHandle {
elem: HtmlElement,
observer: ResizeObserver,
_callback: Closure<dyn FnMut(js_sys::Array)>,
}
impl ResizeObserverHandle {
pub fn new(
elem: &HtmlElement,
renderer: &Renderer,
session: &Session,
presentation: &Presentation,
root: &Root<PerspectiveViewer>,
) -> Self {
let on_resize = root
.borrow()
.as_ref()
.unwrap()
.callback(|()| PerspectiveViewerMsg::Resize);
let mut state = ResizeObserverState {
elem: elem.clone(),
renderer: renderer.clone(),
session: session.clone(),
presentation: presentation.clone(),
width: elem.offset_width(),
height: elem.offset_height(),
on_resize,
};
let _callback = Closure::new(move |xs: js_sys::Array| state.on_resize(&xs));
let observer = ResizeObserver::new(_callback.as_ref().unchecked_ref::<js_sys::Function>());
observer.observe(elem);
Self {
elem: elem.clone(),
_callback,
observer,
}
}
}
impl Drop for ResizeObserverHandle {
fn drop(&mut self) {
self.observer.unobserve(&self.elem);
}
}
#[derive(Clone)]
struct ResizeObserverState {
elem: HtmlElement,
renderer: Renderer,
session: Session,
presentation: Presentation,
width: i32,
height: i32,
on_resize: Callback<()>,
}
impl HasRenderer for ResizeObserverState {
fn renderer(&self) -> &Renderer {
&self.renderer
}
}
impl HasSession for ResizeObserverState {
fn session(&self) -> &Session {
&self.session
}
}
impl HasPresentation for ResizeObserverState {
fn presentation(&self) -> &'_ crate::presentation::Presentation {
&self.presentation
}
}
impl StateProvider for ResizeObserverState {
type State = ResizeObserverState;
fn clone_state(&self) -> Self::State {
self.clone()
}
}
impl ResizeObserverState {
fn on_resize(&mut self, entries: &js_sys::Array) {
let is_visible = self
.elem
.offset_parent()
.map(|x| !x.is_null())
.unwrap_or(false);
for y in entries.iter() {
let entry: ResizeObserverEntry = y.unchecked_into();
let content = entry.content_rect();
let content_width = content.width().floor() as i32;
let content_height = content.height().floor() as i32;
let resized = self.width != content_width || self.height != content_height;
if resized && is_visible {
let state = self.clone_state();
clone!(self.on_resize);
ApiFuture::spawn_throttled(async move {
let needs_render = state
.renderer()
.clone()
.with_lock(async {
Ok(!state.renderer().is_plugin_activated()?
&& matches!(
state.session().has_table(),
Some(TableLoadState::Loaded)
))
})
.await?;
if needs_render {
state.presentation.reset_attached();
state.update_and_render(Default::default())?.await?;
} else {
state.renderer().resize().await?;
}
on_resize.emit(());
Ok(())
});
}
self.width = content_width;
self.height = content_height;
}
}
}