And move it into the dom module.
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
--- /dev/null
+use wasm_bindgen::prelude::*;
+use wasm_bindgen::JsCast;
+use wasm_bindgen::UnwrapThrowExt;
+use web_sys::Element;
+
+use crate::web_sys_ext::{ResizeObserver, ResizeObserverEntry, ResizeObserverOptions};
+
+use yew::prelude::*;
+
+type ObserverClosure = Closure<dyn Fn(Vec<ResizeObserverEntry>)>;
+
+/// Wrapper around a ResizeObserver browser object, intended to create callbacks for size changes
+/// of an element
+pub struct DomSizeObserver {
+ observer: ResizeObserver,
+ // keep it alive
+ _observer_closure: ObserverClosure,
+}
+
+/// Custom trait to automatically handle different types of callbacks
+pub trait IntoSizeCallback<T> {
+ fn into_size_cb(self) -> SizeCallback;
+}
+
+/// Callback with width and height parameters
+impl<T> IntoSizeCallback<(f64, f64)> for T
+where
+ T: Into<Callback<(f64, f64)>>,
+{
+ fn into_size_cb(self) -> SizeCallback {
+ SizeCallback::Normal(self.into())
+ }
+}
+
+/// Callback with width, height, client_width and client_height parameters
+impl<T> IntoSizeCallback<(f64, f64, f64, f64)> for T
+where
+ T: Into<Callback<(f64, f64, f64, f64)>>,
+{
+ fn into_size_cb(self) -> SizeCallback {
+ SizeCallback::ClientRect(self.into())
+ }
+}
+
+pub enum SizeCallback {
+ Normal(Callback<(f64, f64)>),
+ ClientRect(Callback<(f64, f64, f64, f64)>),
+}
+
+impl DomSizeObserver {
+ fn create_observer(callback: SizeCallback) -> (ResizeObserver, ObserverClosure) {
+ let observer_closure = Closure::wrap(Box::new(move |entries: Vec<ResizeObserverEntry>| {
+ if entries.len() == 1 {
+ let el = entries[0].target();
+ let rect = el.get_bounding_client_rect();
+ match &callback {
+ SizeCallback::Normal(cb) => cb.emit((rect.width(), rect.height())),
+ SizeCallback::ClientRect(cb) => {
+ let width: f64 = el.client_width().into();
+ let height: f64 = el.client_height().into();
+ cb.emit((rect.width(), rect.height(), width, height))
+ }
+ }
+ } else {
+ unreachable!();
+ }
+ }) as Box<dyn Fn(Vec<ResizeObserverEntry>)>);
+
+ (
+ ResizeObserver::new(observer_closure.as_ref().unchecked_ref()).unwrap_throw(),
+ observer_closure,
+ )
+ }
+
+ /// Create a new DomSizeObserver for the given element which calls the given callback
+ pub fn new<X>(el: &Element, callback: impl IntoSizeCallback<X>) -> Self {
+ let (observer, _observer_closure) = Self::create_observer(callback.into_size_cb());
+ observer.observe(el);
+
+ Self {
+ _observer_closure,
+ observer,
+ }
+ }
+
+ /// Create a new DomSizeObserver for the given element which calls the given callback
+ /// allows to specify ResizeObserverOptions
+ pub fn new_with_options<X>(
+ el: &Element,
+ callback: impl IntoSizeCallback<X>,
+ options: ResizeObserverOptions,
+ ) -> Self {
+ let (observer, _observer_closure) = Self::create_observer(callback.into_size_cb());
+ observer.observe_with_options(el, &options);
+
+ Self {
+ _observer_closure,
+ observer,
+ }
+ }
+}
+
+impl Drop for DomSizeObserver {
+ fn drop(&mut self) {
+ self.observer.disconnect();
+ }
+}
mod number_format;
pub use number_format::{format_float, parse_float, LocaleInfo};
+mod dom_size_observer;
+pub use dom_size_observer::{DomSizeObserver, IntoSizeCallback, SizeCallback};
+
use web_sys::Node;
use yew::prelude::*;
use yew::prelude::*;
use yew::virtual_dom::VNode;
+use crate::dom::DomSizeObserver;
use crate::prelude::*;
use crate::props::CssLength;
use crate::touch::{GestureDetector, GestureDragEvent, GestureSwipeEvent};
-use crate::widget::{Container, Row, SizeObserver};
+use crate::widget::{Container, Row};
use pwt_macros::widget;
left_size: f64,
left_ref: NodeRef,
left_action_ref: NodeRef,
- left_observer: Option<SizeObserver>,
+ left_observer: Option<DomSizeObserver>,
right_size: f64,
right_ref: NodeRef,
right_action_ref: NodeRef,
- right_observer: Option<SizeObserver>,
+ right_observer: Option<DomSizeObserver>,
content_width: f64,
content_height: f64,
content_ref: NodeRef,
- content_observer: Option<SizeObserver>,
+ content_observer: Option<DomSizeObserver>,
last_action_left: bool,
switch_back: bool,
view_state: ViewState,
if first_render {
if let Some(el) = self.content_ref.cast::<web_sys::HtmlElement>() {
let link = ctx.link().clone();
- self.content_observer = Some(SizeObserver::new(&el, move |(x, y)| {
+ self.content_observer = Some(DomSizeObserver::new(&el, move |(x, y)| {
link.send_message(Msg::ContentResize(x, y));
}));
}
if let Some(el) = self.left_ref.cast::<web_sys::HtmlElement>() {
let link = ctx.link().clone();
- self.left_observer = Some(SizeObserver::new(&el, move |(x, _y)| {
+ self.left_observer = Some(DomSizeObserver::new(&el, move |(x, _y)| {
link.send_message(Msg::LeftResize(x));
}));
}
if let Some(el) = self.right_ref.cast::<web_sys::HtmlElement>() {
let link = ctx.link().clone();
- self.right_observer = Some(SizeObserver::new(&el, move |(x, _y)| {
+ self.right_observer = Some(DomSizeObserver::new(&el, move |(x, _y)| {
link.send_message(Msg::RightResize(x));
}));
}
use js_sys::Error;
use web_sys::{window, HtmlElement};
-use crate::dom::{element_direction_rtl, IntoHtmlElement};
-use crate::widget::SizeObserver;
+use crate::dom::{element_direction_rtl, DomSizeObserver, IntoHtmlElement};
/// Defines a point on a rectangle
///
Ok(())
}
-/// Uses [`align_to`] and a [`SizeObserver`] to automatically adjust the position of floating
+/// Uses [`align_to`] and a [`DomSizeObserver`] to automatically adjust the position of floating
/// elements when they change size. This is useful for elements where the initial size is not
/// known (e.g. a [`crate::widget::data_table::DataTable`] with virtual scrolling).
pub struct AutoFloatingPlacement {
base: HtmlElement,
element: HtmlElement,
options: AlignOptions,
- _size_observer: SizeObserver,
+ _size_observer: DomSizeObserver,
}
impl AutoFloatingPlacement {
- /// Sets up the [`SizeObserver`] on `element` and updates the intial alignment.
+ /// Sets up the [`DomSizeObserver`] on `element` and updates the intial alignment.
pub fn new<B, N>(base: B, element: N, options: AlignOptions) -> Result<Self, Error>
where
B: IntoHtmlElement + Clone + 'static,
.into_html_element()
.ok_or_else(|| js_sys::Error::new("element is not an HtmlElement"))?;
- let size_observer = SizeObserver::new(element.as_ref(), move |(_, _)| {
+ let size_observer = DomSizeObserver::new(element.as_ref(), move |(_, _)| {
if let Err(err) = align_to(
observer_base.clone(),
observer_element.clone(),
use yew::prelude::*;
use yew::virtual_dom::{Key, VComp, VNode};
-use crate::dom::IntoHtmlElement;
+use crate::dom::{DomSizeObserver, IntoHtmlElement};
use crate::prelude::*;
use crate::props::{
AsClassesMut, AsCssStylesMut, CallbackMut, CssLength, CssStyles, IntoEventCallbackMut,
};
use crate::state::{DataStore, Selection, SelectionObserver};
use crate::widget::focus::focus_inside_input;
-use crate::widget::{get_unique_element_id, Column, Container, SizeObserver};
+use crate::widget::{get_unique_element_id, Column, Container};
use super::{
create_indexed_header_list, CellConfiguration, DataTableColumn, DataTableHeader,
viewport_width: f64,
table_height: f64,
- viewport_size_observer: Option<SizeObserver>,
+ viewport_size_observer: Option<DomSizeObserver>,
table_ref: NodeRef,
- table_size_observer: Option<SizeObserver>,
+ table_size_observer: Option<DomSizeObserver>,
row_height: f64,
scrollbar_size: Option<f64>,
if let Some(el) = self.scroll_ref.cast::<web_sys::Element>() {
let link = ctx.link().clone();
let size_observer =
- SizeObserver::new(&el, move |(width, height, client_width, _)| {
+ DomSizeObserver::new(&el, move |(width, height, client_width, _)| {
link.send_message(Msg::ViewportResize(width, height, width - client_width));
});
self.viewport_size_observer = Some(size_observer);
if let Some(el) = self.table_ref.cast::<web_sys::HtmlElement>() {
let link = ctx.link().clone();
- let size_observer = SizeObserver::new(&el, move |(width, height)| {
+ let size_observer = DomSizeObserver::new(&el, move |(width, height)| {
link.send_message(Msg::TableResize(width, height));
});
self.table_size_observer = Some(size_observer);
use crate::css::ColorScheme;
use crate::dom::element_direction_rtl;
+use crate::dom::DomSizeObserver;
use crate::prelude::*;
use crate::props::{BuilderFn, IntoOptionalBuilderFn};
use crate::widget::focus::FocusTracker;
use crate::widget::menu::{Menu, MenuButton};
-use crate::widget::{Container, Row, SizeObserver};
+use crate::widget::{Container, Row};
// Note about node_ref property: make it optional, and generate an
// unique one in Component::create(). That way we can clone Properies without
width: f64,
pointermove_listener: Option<EventListener>,
pointerup_listener: Option<EventListener>,
- size_observer: Option<SizeObserver>,
+ size_observer: Option<DomSizeObserver>,
has_focus: bool,
picker_ref: NodeRef,
show_picker: bool,
let props = ctx.props();
if let Some(el) = self.node_ref.cast::<web_sys::HtmlElement>() {
let on_size_change = props.on_size_change.clone();
- self.size_observer = Some(SizeObserver::new(&el, move |(x, _y)| {
+ self.size_observer = Some(DomSizeObserver::new(&el, move |(x, _y)| {
if let Some(on_size_change) = &on_size_change {
on_size_change.emit(x);
}
use pwt_macros::{builder, widget};
-use super::{Container, CssBorderBuilder, ListTile, SizeObserver};
+use super::{Container, CssBorderBuilder, ListTile};
+use crate::dom::DomSizeObserver;
/// List tile. A container with grid/subgrid layout.
///
pub struct PwtListTileObserver {
node_ref: NodeRef,
- size_observer: Option<SizeObserver>,
+ size_observer: Option<DomSizeObserver>,
}
pub enum Msg {
}
let tile_pos = props.tile_pos;
- self.size_observer = Some(SizeObserver::new(&el, {
+ self.size_observer = Some(DomSizeObserver::new(&el, {
let el = el.clone();
move |(w, h)| {
resize_callback.emit((tile_pos, w, h));
use crate::widget::Container;
-use super::SizeObserver;
+use crate::dom::DomSizeObserver;
use pwt_macros::{builder, widget};
viewport_height: f64,
viewport_width: f64,
viewport_ref: NodeRef,
- viewport_size_observer: Option<SizeObserver>,
+ viewport_size_observer: Option<DomSizeObserver>,
viewport_scrollbar_size: Option<f64>,
viewport_scroll_top: usize,
table_ref: NodeRef,
- table_size_observer: Option<SizeObserver>,
+ table_size_observer: Option<DomSizeObserver>,
table_height: f64,
scroll_info: VirtualScrollInfo,
if let Some(el) = &viewport_el {
let link = ctx.link().clone();
let size_observer =
- SizeObserver::new(&el, move |(width, height, client_width, _)| {
+ DomSizeObserver::new(&el, move |(width, height, client_width, _)| {
link.send_message(Msg::ViewportResize(width, height, width - client_width));
});
self.viewport_size_observer = Some(size_observer);
}
if let Some(el) = self.table_ref.cast::<web_sys::HtmlElement>() {
let link = ctx.link().clone();
- let size_observer = SizeObserver::new(&el, move |(width, height)| {
+ let size_observer = DomSizeObserver::new(&el, move |(width, height)| {
link.send_message(Msg::TableResize(width, height));
});
self.table_size_observer = Some(size_observer);
use gloo_timers::callback::Timeout;
+use crate::dom::DomSizeObserver;
use crate::props::{ContainerBuilder, EventSubscriber, WidgetBuilder};
-use crate::widget::{Container, SizeObserver};
+use crate::widget::Container;
use pwt_macros::widget;
#[doc(hidden)]
pub struct PwtMiniScroll {
handle_ref: NodeRef,
- handle_size_observer: Option<SizeObserver>,
+ handle_size_observer: Option<DomSizeObserver>,
scroll_ref: NodeRef,
content_ref: NodeRef,
- content_size_observer: Option<SizeObserver>,
- scroll_size_observer: Option<SizeObserver>,
+ content_size_observer: Option<DomSizeObserver>,
+ scroll_size_observer: Option<DomSizeObserver>,
width: f64,
handle_width: f64,
content_width: f64,
if first_render {
if let Some(el) = self.scroll_ref.cast::<web_sys::Element>() {
let link = ctx.link().clone();
- let size_observer = SizeObserver::new(&el, move |(width, height)| {
+ let size_observer = DomSizeObserver::new(&el, move |(width, height)| {
link.send_message(Msg::ScrollResize(width, height));
});
self.scroll_size_observer = Some(size_observer);
}
if let Some(el) = self.content_ref.cast::<web_sys::Element>() {
let link = ctx.link().clone();
- let size_observer = SizeObserver::new(&el, move |(width, height)| {
+ let size_observer = DomSizeObserver::new(&el, move |(width, height)| {
link.send_message(Msg::ContentResize(width, height));
});
self.content_size_observer = Some(size_observer);
}
if let Some(el) = self.handle_ref.cast::<web_sys::Element>() {
let link = ctx.link().clone();
- let size_observer = SizeObserver::new(&el, move |(width, height)| {
+ let size_observer = DomSizeObserver::new(&el, move |(width, height)| {
link.send_message(Msg::HandleResize(width, height));
});
self.handle_size_observer = Some(size_observer);
pub use segmented_button::PwtSegmentedButton;
pub use segmented_button::SegmentedButton;
-mod size_observer;
-pub use size_observer::{IntoSizeCallback, SizeObserver};
-
mod tab;
#[doc(hidden)]
pub use tab::{PwtTabBar, PwtTabPanel};
+++ /dev/null
-use wasm_bindgen::prelude::*;
-use wasm_bindgen::JsCast;
-use wasm_bindgen::UnwrapThrowExt;
-use web_sys::Element;
-
-use crate::web_sys_ext::{ResizeObserver, ResizeObserverEntry, ResizeObserverOptions};
-
-use yew::prelude::*;
-
-type ObserverClosure = Closure<dyn Fn(Vec<ResizeObserverEntry>)>;
-
-/// Wrapper around a ResizeObserver browser object, intended to create callbacks for size changes
-/// of an element
-pub struct SizeObserver {
- observer: ResizeObserver,
- // keep it alive
- _observer_closure: ObserverClosure,
-}
-
-/// Custom trait to automatically handle different types of callbacks
-pub trait IntoSizeCallback<T> {
- fn into_size_cb(self) -> SizeCallback;
-}
-
-/// Callback with width and height parameters
-impl<T> IntoSizeCallback<(f64, f64)> for T
-where
- T: Into<Callback<(f64, f64)>>,
-{
- fn into_size_cb(self) -> SizeCallback {
- SizeCallback::Normal(self.into())
- }
-}
-
-/// Callback with width, height, client_width and client_height parameters
-impl<T> IntoSizeCallback<(f64, f64, f64, f64)> for T
-where
- T: Into<Callback<(f64, f64, f64, f64)>>,
-{
- fn into_size_cb(self) -> SizeCallback {
- SizeCallback::ClientRect(self.into())
- }
-}
-
-pub enum SizeCallback {
- Normal(Callback<(f64, f64)>),
- ClientRect(Callback<(f64, f64, f64, f64)>),
-}
-
-impl SizeObserver {
- fn create_observer(callback: SizeCallback) -> (ResizeObserver, ObserverClosure) {
- let observer_closure = Closure::wrap(Box::new(move |entries: Vec<ResizeObserverEntry>| {
- if entries.len() == 1 {
- let el = entries[0].target();
- let rect = el.get_bounding_client_rect();
- match &callback {
- SizeCallback::Normal(cb) => cb.emit((rect.width(), rect.height())),
- SizeCallback::ClientRect(cb) => {
- let width: f64 = el.client_width().into();
- let height: f64 = el.client_height().into();
- cb.emit((rect.width(), rect.height(), width, height))
- }
- }
- } else {
- unreachable!();
- }
- }) as Box<dyn Fn(Vec<ResizeObserverEntry>)>);
-
- (
- ResizeObserver::new(observer_closure.as_ref().unchecked_ref()).unwrap_throw(),
- observer_closure,
- )
- }
-
- /// Create a new SizeObserver for the given element which calls the given callback
- pub fn new<X>(el: &Element, callback: impl IntoSizeCallback<X>) -> Self {
- let (observer, _observer_closure) = Self::create_observer(callback.into_size_cb());
- observer.observe(el);
-
- Self {
- _observer_closure,
- observer,
- }
- }
-
- /// Create a new SizeObserver for the given element which calls the given callback
- /// allows to specify ResizeObserverOptions
- pub fn new_with_options<X>(
- el: &Element,
- callback: impl IntoSizeCallback<X>,
- options: ResizeObserverOptions,
- ) -> Self {
- let (observer, _observer_closure) = Self::create_observer(callback.into_size_cb());
- observer.observe_with_options(el, &options);
-
- Self {
- _observer_closure,
- observer,
- }
- }
-}
-
-impl Drop for SizeObserver {
- fn drop(&mut self) {
- self.observer.disconnect();
- }
-}
use yew::html::{IntoEventCallback, IntoPropValue};
use yew::virtual_dom::{Key, VComp, VNode};
-use crate::dom::{element_direction_rtl, IntoHtmlElement};
+use crate::dom::{element_direction_rtl, DomSizeObserver, IntoHtmlElement};
use crate::prelude::*;
use crate::props::{IntoStorageLocation, StorageLocation};
use crate::state::{NavigationContext, NavigationContextExt, PersistentState, Selection};
use crate::web_sys_ext::{ResizeObserverBoxOptions, ResizeObserverOptions};
use crate::widget::focus::roving_tabindex_next;
-use crate::widget::{Container, SizeObserver};
+use crate::widget::Container;
use super::TabBarItem;
indicator_ref: NodeRef,
active_ref: NodeRef,
size_ref: NodeRef,
- active_size_observer: Option<SizeObserver>,
+ active_size_observer: Option<DomSizeObserver>,
}
fn get_active_or_default(props: &TabBar, active: &Option<Key>) -> Option<Key> {
if let Some(element) = self.active_ref.clone().into_html_element() {
let mut options = ResizeObserverOptions::new();
options.box_(ResizeObserverBoxOptions::BorderBox);
- self.active_size_observer = Some(SizeObserver::new_with_options(
+ self.active_size_observer = Some(DomSizeObserver::new_with_options(
&element,
move |(_, _)| {
link.send_message(Msg::UpdateIndicator);