diff --git a/packages/html/Cargo.toml b/packages/html/Cargo.toml index e60fcd8e5..513cc253a 100644 --- a/packages/html/Cargo.toml +++ b/packages/html/Cargo.toml @@ -39,6 +39,11 @@ features = [ "FocusEvent", "CompositionEvent", "ClipboardEvent", + "Element", + "DomRect", + "ScrollIntoViewOptions", + "ScrollLogicalPosition", + "ScrollBehavior", ] [dev-dependencies] diff --git a/packages/html/src/events.rs b/packages/html/src/events.rs index 853400c28..85a408d0b 100644 --- a/packages/html/src/events.rs +++ b/packages/html/src/events.rs @@ -33,6 +33,7 @@ mod form; mod image; mod keyboard; mod media; +mod mounted; mod mouse; mod pointer; mod scroll; @@ -51,6 +52,7 @@ pub use form::*; pub use image::*; pub use keyboard::*; pub use media::*; +pub use mounted::*; pub use mouse::*; pub use pointer::*; pub use scroll::*; @@ -144,6 +146,7 @@ pub fn event_bubbles(evt: &str) -> bool { "animationiteration" => true, "transitionend" => true, "toggle" => true, + "mounted" => false, _ => true, } } diff --git a/packages/html/src/events/mounted.rs b/packages/html/src/events/mounted.rs new file mode 100644 index 000000000..a5413bb5b --- /dev/null +++ b/packages/html/src/events/mounted.rs @@ -0,0 +1,85 @@ +//! Handles quering data from the renderer + +use euclid::Rect; + +use std::{any::Any, rc::Rc}; + +/// An Element that has been rendered and allows reading and modifying information about it. +/// +/// Different platforms will have different implementations and different levels of support for this trait. Renderers that do not support specific features will return `None` for those queries. +pub trait RenderedElementBacking { + /// Get the renderer specific element for the given id + fn get_raw_element(&self) -> Option<&dyn Any> { + None + } + + /// Get the bounding rectangle of the element relative to the viewport (this does not include the scroll position) + fn get_client_rect(&self) -> Option> { + None + } + + /// Scroll to make the element visible + fn scroll_to(&self, _behavior: ScrollBehavior) -> Option<()> { + None + } + + /// Set the focus on the element + fn set_focus(&self, _focus: bool) -> Option<()> { + None + } +} + +/// The way that scrolling should be performed +pub enum ScrollBehavior { + /// Scroll to the element immediately + Instant, + /// Scroll to the element smoothly + Smooth, +} + +/// An Element that has been rendered and allows reading and modifying information about it. +/// +/// Different platforms will have different implementations and different levels of support for this trait. Renderers that do not support specific features will return `None` for those queries. +pub struct MountedData { + inner: Rc, +} + +impl MountedData { + /// Create a new MountedData + pub fn new(registry: impl RenderedElementBacking + 'static) -> Self { + Self { + inner: Rc::new(registry), + } + } + + /// Get the renderer specific element for the given id + pub fn get_raw_element(&self) -> Option<&dyn Any> { + self.inner.get_raw_element() + } + + /// Get the bounding rectangle of the element relative to the viewport (this does not include the scroll position) + pub fn get_client_rect(&self) -> Option> { + self.inner.get_client_rect() + } + + /// Scroll to make the element visible + pub fn scroll_to(&self, behavior: ScrollBehavior) -> Option<()> { + self.inner.scroll_to(behavior) + } + + /// Set the focus on the element + pub fn set_focus(&self, focus: bool) -> Option<()> { + self.inner.set_focus(focus) + } +} + +use dioxus_core::Event; + +pub type MountedEvent = Event; + +impl_event! [ + MountedData; + + /// mounted + onmounted +]; diff --git a/packages/html/src/web_sys_bind/events.rs b/packages/html/src/web_sys_bind/events.rs index 196099444..01b25dfc2 100644 --- a/packages/html/src/web_sys_bind/events.rs +++ b/packages/html/src/web_sys_bind/events.rs @@ -4,14 +4,14 @@ use crate::events::{ }; use crate::geometry::{ClientPoint, Coordinates, ElementPoint, PagePoint, ScreenPoint}; use crate::input_data::{decode_key_location, decode_mouse_button_set, MouseButton}; -use crate::DragData; +use crate::{DragData, MountedData, RenderedElementBacking, ScrollBehavior}; use keyboard_types::{Code, Key, Modifiers}; use std::convert::TryInto; use std::str::FromStr; use wasm_bindgen::JsCast; use web_sys::{ - AnimationEvent, CompositionEvent, Event, KeyboardEvent, MouseEvent, PointerEvent, TouchEvent, - TransitionEvent, WheelEvent, + AnimationEvent, CompositionEvent, Event, KeyboardEvent, MouseEvent, PointerEvent, + ScrollIntoViewOptions, TouchEvent, TransitionEvent, WheelEvent, }; macro_rules! uncheck_convert { @@ -193,3 +193,46 @@ impl From<&TransitionEvent> for TransitionData { } } } + +impl From<&web_sys::Element> for MountedData { + fn from(e: &web_sys::Element) -> Self { + MountedData::new(e.clone()) + } +} + +impl RenderedElementBacking for web_sys::Element { + fn get_client_rect(&self) -> Option> { + let rect = self.get_bounding_client_rect(); + Some(euclid::Rect::new( + euclid::Point2D::new(rect.left(), rect.top()), + euclid::Size2D::new(rect.width(), rect.height()), + )) + } + + fn get_raw_element(&self) -> Option<&dyn std::any::Any> { + Some(self) + } + + fn scroll_to(&self, behavior: ScrollBehavior) -> Option<()> { + match behavior { + ScrollBehavior::Instant => self.scroll_into_view_with_scroll_into_view_options( + ScrollIntoViewOptions::new().behavior(web_sys::ScrollBehavior::Instant), + ), + ScrollBehavior::Smooth => self.scroll_into_view_with_scroll_into_view_options( + ScrollIntoViewOptions::new().behavior(web_sys::ScrollBehavior::Smooth), + ), + } + + Some(()) + } + + fn set_focus(&self, focus: bool) -> Option<()> { + self.dyn_ref::().and_then(|e| { + if focus { + e.focus().ok() + } else { + e.blur().ok() + } + }) + } +}