mirror of
https://github.com/DioxusLabs/dioxus
synced 2025-02-16 13:48:26 +00:00
Add the onresize
event handler to Element (#2479)
* Add the capability to handle resize event for web target * Add the capability to handle resize event for desktop target * Return all the sizes, not just the first one * Fix conversion from platform to generic ResizedData for liveview * Update the generated interpreter js code base * Fix clippy warnings * Fix inconsistent use of block_size and inline_size * Rename `onresized` event to `onresize` * Remove the the special-casing logic from the binding logic * Propagating the resize events using CustomEvent * Fix case convention in core ts * revert changes to unified bindings * Cleanup as suggested * add a resize example * Fix desktop resize events * remove tracing from resize example * use the raw resize entry so we can downcast on web * remove unused ResizeEventDetail --------- Co-authored-by: Evan Almloff <evanalmloff@gmail.com>
This commit is contained in:
parent
4d6fb74e87
commit
2f49a89638
19 changed files with 442 additions and 50 deletions
27
examples/resize.rs
Normal file
27
examples/resize.rs
Normal file
|
@ -0,0 +1,27 @@
|
|||
//! Run a callback
|
||||
//!
|
||||
//! Whenever an Element is finally mounted to the Dom, its data is available to be read.
|
||||
//! These fields can typically only be read asynchronously, since various renderers need to release the main thread to
|
||||
//! perform layout and painting.
|
||||
|
||||
use dioxus::prelude::*;
|
||||
use dioxus_elements::geometry::euclid::Size2D;
|
||||
|
||||
fn main() {
|
||||
launch(app);
|
||||
}
|
||||
|
||||
fn app() -> Element {
|
||||
let mut dimensions = use_signal(Size2D::zero);
|
||||
|
||||
rsx!(
|
||||
head::Link { rel: "stylesheet", href: asset!("./examples/assets/read_size.css") }
|
||||
div {
|
||||
width: "50%",
|
||||
height: "50%",
|
||||
background_color: "red",
|
||||
onresize: move |evt| dimensions.set(evt.data().get_content_box_size().unwrap()),
|
||||
"This element is {dimensions():?}"
|
||||
}
|
||||
)
|
||||
}
|
|
@ -113,6 +113,14 @@ impl HtmlEventConverter for SerializedHtmlEventConverter {
|
|||
.into()
|
||||
}
|
||||
|
||||
fn convert_resize_data(&self, event: &PlatformEventData) -> ResizeData {
|
||||
event
|
||||
.downcast::<SerializedResizeData>()
|
||||
.cloned()
|
||||
.unwrap()
|
||||
.into()
|
||||
}
|
||||
|
||||
fn convert_scroll_data(&self, event: &PlatformEventData) -> ScrollData {
|
||||
event
|
||||
.downcast::<SerializedScrollData>()
|
||||
|
|
|
@ -50,6 +50,9 @@ features = [
|
|||
"PointerEvent",
|
||||
"FocusEvent",
|
||||
"CompositionEvent",
|
||||
"CustomEvent",
|
||||
"ResizeObserverEntry",
|
||||
"ResizeObserverSize"
|
||||
]
|
||||
|
||||
[build-dependencies]
|
||||
|
|
|
@ -134,6 +134,8 @@ pub trait HtmlEventConverter: Send + Sync {
|
|||
fn convert_mouse_data(&self, event: &PlatformEventData) -> MouseData;
|
||||
/// Convert a general event to a pointer data event
|
||||
fn convert_pointer_data(&self, event: &PlatformEventData) -> PointerData;
|
||||
/// Convert a general event to a resize data event
|
||||
fn convert_resize_data(&self, event: &PlatformEventData) -> ResizeData;
|
||||
/// Convert a general event to a scroll data event
|
||||
fn convert_scroll_data(&self, event: &PlatformEventData) -> ScrollData;
|
||||
/// Convert a general event to a selection data event
|
||||
|
@ -220,6 +222,12 @@ impl From<&PlatformEventData> for PointerData {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<&PlatformEventData> for ResizeData {
|
||||
fn from(val: &PlatformEventData) -> Self {
|
||||
with_event_converter(|c| c.convert_resize_data(val))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&PlatformEventData> for ScrollData {
|
||||
fn from(val: &PlatformEventData) -> Self {
|
||||
with_event_converter(|c| c.convert_scroll_data(val))
|
||||
|
@ -268,6 +276,7 @@ mod media;
|
|||
mod mounted;
|
||||
mod mouse;
|
||||
mod pointer;
|
||||
mod resize;
|
||||
mod scroll;
|
||||
mod selection;
|
||||
mod toggle;
|
||||
|
@ -287,6 +296,7 @@ pub use media::*;
|
|||
pub use mounted::*;
|
||||
pub use mouse::*;
|
||||
pub use pointer::*;
|
||||
pub use resize::*;
|
||||
pub use scroll::*;
|
||||
pub use selection::*;
|
||||
pub use toggle::*;
|
||||
|
@ -367,6 +377,7 @@ pub fn event_bubbles(evt: &str) -> bool {
|
|||
"playing" => false,
|
||||
"progress" => false,
|
||||
"ratechange" => false,
|
||||
"resize" => false,
|
||||
"seeked" => false,
|
||||
"seeking" => false,
|
||||
"stalled" => false,
|
||||
|
|
168
packages/html/src/events/resize.rs
Normal file
168
packages/html/src/events/resize.rs
Normal file
|
@ -0,0 +1,168 @@
|
|||
use std::fmt::{Display, Formatter};
|
||||
|
||||
pub struct ResizeData {
|
||||
inner: Box<dyn HasResizeData>,
|
||||
}
|
||||
|
||||
impl<E: HasResizeData> From<E> for ResizeData {
|
||||
fn from(e: E) -> Self {
|
||||
Self { inner: Box::new(e) }
|
||||
}
|
||||
}
|
||||
|
||||
impl ResizeData {
|
||||
/// Create a new ResizeData
|
||||
pub fn new(inner: impl HasResizeData + 'static) -> Self {
|
||||
Self {
|
||||
inner: Box::new(inner),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the border box size of the observed element
|
||||
pub fn get_border_box_size(&self) -> ResizeResult<PixelsSize> {
|
||||
self.inner.get_border_box_size()
|
||||
}
|
||||
|
||||
/// Get the content box size of the observed element
|
||||
pub fn get_content_box_size(&self) -> ResizeResult<PixelsSize> {
|
||||
self.inner.get_content_box_size()
|
||||
}
|
||||
|
||||
/// Downcast this event to a concrete event type
|
||||
pub fn downcast<T: 'static>(&self) -> Option<&T> {
|
||||
self.inner.as_any().downcast_ref::<T>()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for ResizeData {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("ResizeData")
|
||||
.field("border_box_size", &self.inner.get_border_box_size())
|
||||
.field("content_box_size", &self.inner.get_content_box_size())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for ResizeData {
|
||||
fn eq(&self, _: &Self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
/// A serialized version of ResizeData
|
||||
#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Clone)]
|
||||
pub struct SerializedResizeData {
|
||||
pub border_box_size: PixelsSize,
|
||||
pub content_box_size: PixelsSize,
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
impl SerializedResizeData {
|
||||
/// Create a new SerializedResizeData
|
||||
pub fn new(border_box_size: PixelsSize, content_box_size: PixelsSize) -> Self {
|
||||
Self {
|
||||
border_box_size,
|
||||
content_box_size,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
impl From<&ResizeData> for SerializedResizeData {
|
||||
fn from(data: &ResizeData) -> Self {
|
||||
Self::new(
|
||||
data.get_border_box_size().unwrap(),
|
||||
data.get_content_box_size().unwrap(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
impl HasResizeData for SerializedResizeData {
|
||||
/// Get the border box size of the observed element
|
||||
fn get_border_box_size(&self) -> ResizeResult<PixelsSize> {
|
||||
Ok(self.border_box_size)
|
||||
}
|
||||
|
||||
/// Get the content box size of the observed element
|
||||
fn get_content_box_size(&self) -> ResizeResult<PixelsSize> {
|
||||
Ok(self.content_box_size)
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
impl serde::Serialize for ResizeData {
|
||||
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
SerializedResizeData::from(self).serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
impl<'de> serde::Deserialize<'de> for ResizeData {
|
||||
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
||||
let data = SerializedResizeData::deserialize(deserializer)?;
|
||||
Ok(Self {
|
||||
inner: Box::new(data),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub trait HasResizeData: std::any::Any {
|
||||
/// Get the border box size of the observed element
|
||||
fn get_border_box_size(&self) -> ResizeResult<PixelsSize> {
|
||||
Err(ResizeError::NotSupported)
|
||||
}
|
||||
/// Get the content box size of the observed element
|
||||
fn get_content_box_size(&self) -> ResizeResult<PixelsSize> {
|
||||
Err(ResizeError::NotSupported)
|
||||
}
|
||||
|
||||
/// return self as Any
|
||||
fn as_any(&self) -> &dyn std::any::Any;
|
||||
}
|
||||
|
||||
use dioxus_core::Event;
|
||||
|
||||
use crate::geometry::PixelsSize;
|
||||
|
||||
pub type ResizeEvent = Event<ResizeData>;
|
||||
|
||||
impl_event! {
|
||||
ResizeData;
|
||||
|
||||
/// onresize
|
||||
onresize
|
||||
}
|
||||
|
||||
/// The ResizeResult type for the ResizeData
|
||||
pub type ResizeResult<T> = Result<T, ResizeError>;
|
||||
|
||||
#[derive(Debug)]
|
||||
/// The error type for the MountedData
|
||||
#[non_exhaustive]
|
||||
pub enum ResizeError {
|
||||
/// The renderer does not support the requested operation
|
||||
NotSupported,
|
||||
/// The element was not found
|
||||
OperationFailed(Box<dyn std::error::Error>),
|
||||
}
|
||||
|
||||
impl Display for ResizeError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
ResizeError::NotSupported => {
|
||||
write!(f, "The renderer does not support the requested operation")
|
||||
}
|
||||
ResizeError::OperationFailed(e) => {
|
||||
write!(f, "The operation failed: {}", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for ResizeError {}
|
|
@ -99,6 +99,9 @@ fn deserialize_raw(name: &str, data: &serde_json::Value) -> Result<EventData, se
|
|||
// Touch
|
||||
"touchcancel" | "touchend" | "touchmove" | "touchstart" => Touch(de(data)?),
|
||||
|
||||
// Resize
|
||||
"resize" => Resize(de(data)?),
|
||||
|
||||
// Scroll
|
||||
"scroll" => Scroll(de(data)?),
|
||||
|
||||
|
@ -158,6 +161,7 @@ pub enum EventData {
|
|||
Pointer(SerializedPointerData),
|
||||
Selection(SerializedSelectionData),
|
||||
Touch(SerializedTouchData),
|
||||
Resize(SerializedResizeData),
|
||||
Scroll(SerializedScrollData),
|
||||
Wheel(SerializedWheelData),
|
||||
Media(SerializedMediaData),
|
||||
|
@ -197,6 +201,9 @@ impl EventData {
|
|||
EventData::Touch(data) => {
|
||||
Rc::new(PlatformEventData::new(Box::new(data))) as Rc<dyn Any>
|
||||
}
|
||||
EventData::Resize(data) => {
|
||||
Rc::new(PlatformEventData::new(Box::new(data))) as Rc<dyn Any>
|
||||
}
|
||||
EventData::Scroll(data) => {
|
||||
Rc::new(PlatformEventData::new(Box::new(data))) as Rc<dyn Any>
|
||||
}
|
||||
|
@ -360,6 +367,13 @@ impl HtmlEventConverter for SerializedHtmlEventConverter {
|
|||
.unwrap()
|
||||
.into()
|
||||
}
|
||||
fn convert_resize_data(&self, event: &PlatformEventData) -> ResizeData {
|
||||
event
|
||||
.downcast::<SerializedResizeData>()
|
||||
.cloned()
|
||||
.unwrap()
|
||||
.into()
|
||||
}
|
||||
|
||||
fn convert_scroll_data(&self, event: &PlatformEventData) -> ScrollData {
|
||||
event
|
||||
|
|
|
@ -4,15 +4,17 @@ use crate::events::{
|
|||
TransitionData, WheelData,
|
||||
};
|
||||
use crate::file_data::HasFileData;
|
||||
use crate::geometry::PixelsSize;
|
||||
use crate::geometry::{ClientPoint, ElementPoint, PagePoint, ScreenPoint};
|
||||
use crate::input_data::{decode_key_location, decode_mouse_button_set, MouseButton};
|
||||
use crate::prelude::*;
|
||||
use keyboard_types::{Code, Key, Modifiers};
|
||||
use std::str::FromStr;
|
||||
use wasm_bindgen::JsCast;
|
||||
use web_sys::{js_sys, ResizeObserverEntry};
|
||||
use web_sys::{
|
||||
AnimationEvent, CompositionEvent, Event, KeyboardEvent, MouseEvent, PointerEvent, Touch,
|
||||
TouchEvent, TransitionEvent, WheelEvent,
|
||||
AnimationEvent, CompositionEvent, CustomEvent, Event, KeyboardEvent, MouseEvent, PointerEvent,
|
||||
Touch, TouchEvent, TransitionEvent, WheelEvent,
|
||||
};
|
||||
|
||||
macro_rules! uncheck_convert {
|
||||
|
@ -51,6 +53,22 @@ uncheck_convert![
|
|||
web_sys::FocusEvent => FocusData,
|
||||
];
|
||||
|
||||
impl From<Event> for ResizeData {
|
||||
#[inline]
|
||||
fn from(e: Event) -> Self {
|
||||
<ResizeData as From<&Event>>::from(&e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Event> for ResizeData {
|
||||
#[inline]
|
||||
fn from(e: &Event) -> Self {
|
||||
let e: &CustomEvent = e.unchecked_ref();
|
||||
let value = e.detail();
|
||||
Self::from(value.unchecked_into::<ResizeObserverEntry>())
|
||||
}
|
||||
}
|
||||
|
||||
impl HasCompositionData for CompositionEvent {
|
||||
fn data(&self) -> std::string::String {
|
||||
self.data().unwrap_or_default()
|
||||
|
@ -571,6 +589,32 @@ impl crate::RenderedElementBacking for web_sys::Element {
|
|||
}
|
||||
}
|
||||
|
||||
fn extract_first_size(resize_observer_output: js_sys::Array) -> ResizeResult<PixelsSize> {
|
||||
let first = resize_observer_output.get(0);
|
||||
let size = first.unchecked_into::<web_sys::ResizeObserverSize>();
|
||||
|
||||
// inline_size matches the width of the element if its writing-mode is horizontal, the height otherwise
|
||||
let inline_size = size.inline_size();
|
||||
// block_size matches the height of the element if its writing-mode is horizontal, the width otherwise
|
||||
let block_size = size.block_size();
|
||||
|
||||
Ok(PixelsSize::new(inline_size, block_size))
|
||||
}
|
||||
|
||||
impl HasResizeData for ResizeObserverEntry {
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn get_border_box_size(&self) -> ResizeResult<PixelsSize> {
|
||||
extract_first_size(self.border_box_size())
|
||||
}
|
||||
|
||||
fn get_content_box_size(&self) -> ResizeResult<PixelsSize> {
|
||||
extract_first_size(self.content_box_size())
|
||||
}
|
||||
}
|
||||
|
||||
impl HasScrollData for Event {
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1 +1 @@
|
|||
[6449103750905854967, 12029349297046688094, 13069001215487072322, 8716623267269178440, 5336385715226370016, 14456089431355876478, 5618841090288840293, 5052021921702764563, 16478152596505612522, 5638004933879392817]
|
||||
[6449103750905854967, 4461869229701639737, 13069001215487072322, 8716623267269178440, 5336385715226370016, 14456089431355876478, 11703635120274352436, 5052021921702764563, 17534315583914394253, 5638004933879392817]
|
File diff suppressed because one or more lines are too long
|
@ -19,6 +19,8 @@ export class BaseInterpreter {
|
|||
|
||||
root: HTMLElement;
|
||||
handler: EventListener;
|
||||
resizeObserver: ResizeObserver;
|
||||
|
||||
nodes: Node[];
|
||||
stack: Node[];
|
||||
templates: {
|
||||
|
@ -39,8 +41,35 @@ export class BaseInterpreter {
|
|||
this.stack = [root];
|
||||
this.templates = {};
|
||||
|
||||
if (handler) {
|
||||
this.handler = handler;
|
||||
this.handler = handler;
|
||||
}
|
||||
|
||||
handleResizeEvent(entry: ResizeObserverEntry) {
|
||||
const target = entry.target;
|
||||
|
||||
let event = new CustomEvent<ResizeObserverEntry>("resize", {
|
||||
bubbles: false,
|
||||
detail: entry,
|
||||
});
|
||||
|
||||
target.dispatchEvent(event);
|
||||
}
|
||||
|
||||
createObserver(element: HTMLElement) {
|
||||
// Lazily create the resize observer
|
||||
if (!this.resizeObserver) {
|
||||
this.resizeObserver = new ResizeObserver((entries) => {
|
||||
for (const entry of entries) {
|
||||
this.handleResizeEvent(entry);
|
||||
}
|
||||
});
|
||||
}
|
||||
this.resizeObserver.observe(element);
|
||||
}
|
||||
|
||||
removeObserver(element: HTMLElement) {
|
||||
if (this.resizeObserver) {
|
||||
this.resizeObserver.unobserve(element);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -59,6 +88,10 @@ export class BaseInterpreter {
|
|||
}
|
||||
element.addEventListener(event_name, this.handler);
|
||||
}
|
||||
|
||||
if (event_name == "resize") {
|
||||
this.createObserver(element);
|
||||
}
|
||||
}
|
||||
|
||||
removeListener(element: HTMLElement, event_name: string, bubbles: boolean) {
|
||||
|
|
|
@ -98,6 +98,7 @@ export class NativeInterpreter extends JSChannel_ {
|
|||
// make sure we pass the handler to the base interpreter
|
||||
const handler: EventListener = (event) =>
|
||||
this.handleEvent(event, event.type, true);
|
||||
|
||||
super.initialize(root, handler);
|
||||
}
|
||||
|
||||
|
@ -209,7 +210,7 @@ export class NativeInterpreter extends JSChannel_ {
|
|||
// This is to support the prevent_default: "onclick" attribute that dioxus has had for a while, but is not necessary
|
||||
// now that we expose preventDefault to the virtualdom on desktop
|
||||
// Liveview will still need to use this
|
||||
this.preventDefaults(event, target);
|
||||
this.preventDefaults(event);
|
||||
|
||||
// liveview does not have synchronous event handling, so we need to send the event to the host
|
||||
if (this.liveview) {
|
||||
|
@ -262,7 +263,7 @@ export class NativeInterpreter extends JSChannel_ {
|
|||
// - prevent anchor tags from navigating
|
||||
// - prevent buttons from submitting forms
|
||||
// - let the virtualdom attempt to prevent the event
|
||||
preventDefaults(event: Event, target: EventTarget) {
|
||||
preventDefaults(event: Event) {
|
||||
if (event.type === "submit") {
|
||||
event.preventDefault();
|
||||
}
|
||||
|
|
|
@ -58,6 +58,13 @@ export function serializeEvent(
|
|||
extend({});
|
||||
}
|
||||
|
||||
if (event instanceof CustomEvent) {
|
||||
const detail = event.detail;
|
||||
if (detail instanceof ResizeObserverEntry) {
|
||||
extend(serializeResizeEventDetail(detail));
|
||||
}
|
||||
}
|
||||
|
||||
// safari is quirky and doesn't have TouchEvent
|
||||
if (typeof TouchEvent !== "undefined" && event instanceof TouchEvent) {
|
||||
extend(serializeTouchEvent(event));
|
||||
|
@ -96,6 +103,47 @@ export function serializeEvent(
|
|||
return contents;
|
||||
}
|
||||
|
||||
function toSerializableResizeObserverSize(
|
||||
size: ResizeObserverSize,
|
||||
is_inline_width: boolean
|
||||
): Object {
|
||||
return [
|
||||
is_inline_width ? size.inlineSize : size.blockSize,
|
||||
is_inline_width ? size.blockSize : size.inlineSize,
|
||||
];
|
||||
}
|
||||
|
||||
export function serializeResizeEventDetail(
|
||||
detail: ResizeObserverEntry
|
||||
): SerializedEvent {
|
||||
let is_inline_width = true;
|
||||
if (detail.target instanceof HTMLElement) {
|
||||
let target_style = window.getComputedStyle(detail.target);
|
||||
let target_writing_mode = target_style.getPropertyValue("writing-mode");
|
||||
if (target_writing_mode !== "horizontal-tb") {
|
||||
is_inline_width = false;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
border_box_size:
|
||||
detail.borderBoxSize !== undefined
|
||||
? toSerializableResizeObserverSize(
|
||||
detail.borderBoxSize[0],
|
||||
is_inline_width
|
||||
)
|
||||
: detail.contentRect,
|
||||
content_box_size:
|
||||
detail.contentBoxSize !== undefined
|
||||
? toSerializableResizeObserverSize(
|
||||
detail.contentBoxSize[0],
|
||||
is_inline_width
|
||||
)
|
||||
: detail.contentRect,
|
||||
content_rect: detail.contentRect,
|
||||
};
|
||||
}
|
||||
|
||||
function serializeInputEvent(
|
||||
event: InputEvent,
|
||||
target: EventTarget
|
||||
|
|
|
@ -77,9 +77,10 @@ mod js {
|
|||
fn create_placeholder(id: u32) {
|
||||
"{let node = document.createComment('placeholder'); this.stack.push(node); this.nodes[$id$] = node;}"
|
||||
}
|
||||
|
||||
fn new_event_listener(event_name: &str<u8, evt>, id: u32, bubbles: u8) {
|
||||
r#"
|
||||
let node = this.nodes[id];
|
||||
const node = this.nodes[id];
|
||||
if(node.listening){node.listening += 1;}else{node.listening = 1;}
|
||||
node.setAttribute('data-dioxus-id', `\${id}`);
|
||||
this.createListener($event_name$, node, $bubbles$);
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
"noImplicitAny": true,
|
||||
"removeComments": true,
|
||||
"preserveConstEnums": true,
|
||||
"typeRoots": [".src/ts/types"]
|
||||
},
|
||||
"exclude": [
|
||||
"**/*.spec.ts"
|
||||
|
|
|
@ -99,6 +99,14 @@ impl HtmlEventConverter for SerializedHtmlEventConverter {
|
|||
.into()
|
||||
}
|
||||
|
||||
fn convert_resize_data(&self, event: &PlatformEventData) -> ResizeData {
|
||||
event
|
||||
.downcast::<SerializedResizeData>()
|
||||
.cloned()
|
||||
.unwrap()
|
||||
.into()
|
||||
}
|
||||
|
||||
fn convert_scroll_data(&self, event: &PlatformEventData) -> ScrollData {
|
||||
event
|
||||
.downcast::<SerializedScrollData>()
|
||||
|
|
|
@ -53,7 +53,8 @@ features = [
|
|||
"DataTransfer",
|
||||
"console",
|
||||
"NodeList",
|
||||
"CloseEvent"
|
||||
"CloseEvent",
|
||||
"CustomEvent",
|
||||
]
|
||||
|
||||
[features]
|
||||
|
|
|
@ -13,7 +13,7 @@ use dioxus_core::{ElementId, Template};
|
|||
use dioxus_interpreter_js::unified_bindings::Interpreter;
|
||||
use rustc_hash::FxHashMap;
|
||||
use wasm_bindgen::{closure::Closure, JsCast};
|
||||
use web_sys::{Document, Element, Event};
|
||||
use web_sys::{Document, Element, Event, Node};
|
||||
|
||||
use crate::{load_document, virtual_event_from_websys_event, Config, WebEventConverter};
|
||||
|
||||
|
@ -146,6 +146,11 @@ fn walk_event_for_id(event: &web_sys::Event) -> Option<(ElementId, web_sys::Elem
|
|||
.expect("missing target")
|
||||
.dyn_into::<web_sys::Node>()
|
||||
.expect("not a valid node");
|
||||
|
||||
walk_element_for_id(&target)
|
||||
}
|
||||
|
||||
fn walk_element_for_id(target: &Node) -> Option<(ElementId, web_sys::Element)> {
|
||||
let mut current_target_element = target.dyn_ref::<web_sys::Element>().cloned();
|
||||
|
||||
loop {
|
||||
|
|
|
@ -112,6 +112,14 @@ impl HtmlEventConverter for WebEventConverter {
|
|||
downcast_event(event).raw.clone().into()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn convert_resize_data(
|
||||
&self,
|
||||
event: &dioxus_html::PlatformEventData,
|
||||
) -> dioxus_html::ResizeData {
|
||||
downcast_event(event).raw.clone().into()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn convert_scroll_data(
|
||||
&self,
|
||||
|
@ -158,11 +166,11 @@ impl HtmlEventConverter for WebEventConverter {
|
|||
/// A extension trait for web-sys events that provides a way to get the event as a web-sys event.
|
||||
pub trait WebEventExt<E> {
|
||||
/// Try to downcast this event as a `web-sys` event.
|
||||
fn try_as_web_event(&self) -> Option<&E>;
|
||||
fn try_as_web_event(&self) -> Option<E>;
|
||||
|
||||
/// Downcast this event as a `web-sys` event.
|
||||
#[inline(always)]
|
||||
fn as_web_event(&self) -> &E
|
||||
fn as_web_event(&self) -> E
|
||||
where
|
||||
E: 'static,
|
||||
{
|
||||
|
@ -177,127 +185,137 @@ pub trait WebEventExt<E> {
|
|||
|
||||
impl WebEventExt<web_sys::AnimationEvent> for dioxus_html::AnimationData {
|
||||
#[inline(always)]
|
||||
fn try_as_web_event(&self) -> Option<&web_sys::AnimationEvent> {
|
||||
self.downcast::<web_sys::AnimationEvent>()
|
||||
fn try_as_web_event(&self) -> Option<web_sys::AnimationEvent> {
|
||||
self.downcast::<web_sys::AnimationEvent>().cloned()
|
||||
}
|
||||
}
|
||||
|
||||
impl WebEventExt<web_sys::Event> for dioxus_html::ClipboardData {
|
||||
#[inline(always)]
|
||||
fn try_as_web_event(&self) -> Option<&web_sys::Event> {
|
||||
self.downcast::<web_sys::Event>()
|
||||
fn try_as_web_event(&self) -> Option<web_sys::Event> {
|
||||
self.downcast::<web_sys::Event>().cloned()
|
||||
}
|
||||
}
|
||||
|
||||
impl WebEventExt<web_sys::CompositionEvent> for dioxus_html::CompositionData {
|
||||
#[inline(always)]
|
||||
fn try_as_web_event(&self) -> Option<&web_sys::CompositionEvent> {
|
||||
self.downcast::<web_sys::CompositionEvent>()
|
||||
fn try_as_web_event(&self) -> Option<web_sys::CompositionEvent> {
|
||||
self.downcast::<web_sys::CompositionEvent>().cloned()
|
||||
}
|
||||
}
|
||||
|
||||
impl WebEventExt<web_sys::MouseEvent> for dioxus_html::DragData {
|
||||
#[inline(always)]
|
||||
fn try_as_web_event(&self) -> Option<&web_sys::MouseEvent> {
|
||||
self.downcast::<WebDragData>().map(|data| &data.raw)
|
||||
fn try_as_web_event(&self) -> Option<web_sys::MouseEvent> {
|
||||
self.downcast::<WebDragData>()
|
||||
.map(|data| &data.raw)
|
||||
.cloned()
|
||||
}
|
||||
}
|
||||
|
||||
impl WebEventExt<web_sys::FocusEvent> for dioxus_html::FocusData {
|
||||
#[inline(always)]
|
||||
fn try_as_web_event(&self) -> Option<&web_sys::FocusEvent> {
|
||||
self.downcast::<web_sys::FocusEvent>()
|
||||
fn try_as_web_event(&self) -> Option<web_sys::FocusEvent> {
|
||||
self.downcast::<web_sys::FocusEvent>().cloned()
|
||||
}
|
||||
}
|
||||
|
||||
impl WebEventExt<web_sys::Event> for dioxus_html::FormData {
|
||||
#[inline(always)]
|
||||
fn try_as_web_event(&self) -> Option<&web_sys::Event> {
|
||||
self.downcast::<web_sys::Event>()
|
||||
fn try_as_web_event(&self) -> Option<web_sys::Event> {
|
||||
self.downcast::<web_sys::Event>().cloned()
|
||||
}
|
||||
}
|
||||
|
||||
impl WebEventExt<WebImageEvent> for dioxus_html::ImageData {
|
||||
#[inline(always)]
|
||||
fn try_as_web_event(&self) -> Option<&WebImageEvent> {
|
||||
self.downcast::<WebImageEvent>()
|
||||
fn try_as_web_event(&self) -> Option<WebImageEvent> {
|
||||
self.downcast::<WebImageEvent>().cloned()
|
||||
}
|
||||
}
|
||||
|
||||
impl WebEventExt<web_sys::KeyboardEvent> for dioxus_html::KeyboardData {
|
||||
#[inline(always)]
|
||||
fn try_as_web_event(&self) -> Option<&web_sys::KeyboardEvent> {
|
||||
self.downcast::<web_sys::KeyboardEvent>()
|
||||
fn try_as_web_event(&self) -> Option<web_sys::KeyboardEvent> {
|
||||
self.downcast::<web_sys::KeyboardEvent>().cloned()
|
||||
}
|
||||
}
|
||||
|
||||
impl WebEventExt<web_sys::Event> for dioxus_html::MediaData {
|
||||
#[inline(always)]
|
||||
fn try_as_web_event(&self) -> Option<&web_sys::Event> {
|
||||
self.downcast::<web_sys::Event>()
|
||||
fn try_as_web_event(&self) -> Option<web_sys::Event> {
|
||||
self.downcast::<web_sys::Event>().cloned()
|
||||
}
|
||||
}
|
||||
|
||||
impl WebEventExt<web_sys::Element> for MountedData {
|
||||
#[inline(always)]
|
||||
fn try_as_web_event(&self) -> Option<&web_sys::Element> {
|
||||
self.downcast::<web_sys::Element>()
|
||||
fn try_as_web_event(&self) -> Option<web_sys::Element> {
|
||||
self.downcast::<web_sys::Element>().cloned()
|
||||
}
|
||||
}
|
||||
|
||||
impl WebEventExt<web_sys::MouseEvent> for dioxus_html::MouseData {
|
||||
#[inline(always)]
|
||||
fn try_as_web_event(&self) -> Option<&web_sys::MouseEvent> {
|
||||
self.downcast::<web_sys::MouseEvent>()
|
||||
fn try_as_web_event(&self) -> Option<web_sys::MouseEvent> {
|
||||
self.downcast::<web_sys::MouseEvent>().cloned()
|
||||
}
|
||||
}
|
||||
|
||||
impl WebEventExt<web_sys::PointerEvent> for dioxus_html::PointerData {
|
||||
#[inline(always)]
|
||||
fn try_as_web_event(&self) -> Option<&web_sys::PointerEvent> {
|
||||
self.downcast::<web_sys::PointerEvent>()
|
||||
fn try_as_web_event(&self) -> Option<web_sys::PointerEvent> {
|
||||
self.downcast::<web_sys::PointerEvent>().cloned()
|
||||
}
|
||||
}
|
||||
|
||||
impl WebEventExt<web_sys::Event> for ScrollData {
|
||||
#[inline(always)]
|
||||
fn try_as_web_event(&self) -> Option<&web_sys::Event> {
|
||||
self.downcast::<web_sys::Event>()
|
||||
fn try_as_web_event(&self) -> Option<web_sys::Event> {
|
||||
self.downcast::<web_sys::Event>().cloned()
|
||||
}
|
||||
}
|
||||
|
||||
impl WebEventExt<web_sys::Event> for dioxus_html::SelectionData {
|
||||
#[inline(always)]
|
||||
fn try_as_web_event(&self) -> Option<&web_sys::Event> {
|
||||
self.downcast::<web_sys::Event>()
|
||||
fn try_as_web_event(&self) -> Option<web_sys::Event> {
|
||||
self.downcast::<web_sys::Event>().cloned()
|
||||
}
|
||||
}
|
||||
|
||||
impl WebEventExt<web_sys::Event> for dioxus_html::ToggleData {
|
||||
#[inline(always)]
|
||||
fn try_as_web_event(&self) -> Option<&web_sys::Event> {
|
||||
self.downcast::<web_sys::Event>()
|
||||
fn try_as_web_event(&self) -> Option<web_sys::Event> {
|
||||
self.downcast::<web_sys::Event>().cloned()
|
||||
}
|
||||
}
|
||||
|
||||
impl WebEventExt<web_sys::TouchEvent> for dioxus_html::TouchData {
|
||||
#[inline(always)]
|
||||
fn try_as_web_event(&self) -> Option<&web_sys::TouchEvent> {
|
||||
self.downcast::<web_sys::TouchEvent>()
|
||||
fn try_as_web_event(&self) -> Option<web_sys::TouchEvent> {
|
||||
self.downcast::<web_sys::TouchEvent>().cloned()
|
||||
}
|
||||
}
|
||||
|
||||
impl WebEventExt<web_sys::TransitionEvent> for dioxus_html::TransitionData {
|
||||
#[inline(always)]
|
||||
fn try_as_web_event(&self) -> Option<&web_sys::TransitionEvent> {
|
||||
self.downcast::<web_sys::TransitionEvent>()
|
||||
fn try_as_web_event(&self) -> Option<web_sys::TransitionEvent> {
|
||||
self.downcast::<web_sys::TransitionEvent>().cloned()
|
||||
}
|
||||
}
|
||||
|
||||
impl WebEventExt<web_sys::WheelEvent> for dioxus_html::WheelData {
|
||||
#[inline(always)]
|
||||
fn try_as_web_event(&self) -> Option<&web_sys::WheelEvent> {
|
||||
self.downcast::<web_sys::WheelEvent>()
|
||||
fn try_as_web_event(&self) -> Option<web_sys::WheelEvent> {
|
||||
self.downcast::<web_sys::WheelEvent>().cloned()
|
||||
}
|
||||
}
|
||||
|
||||
impl WebEventExt<web_sys::ResizeObserverEntry> for dioxus_html::ResizeData {
|
||||
#[inline(always)]
|
||||
fn try_as_web_event(&self) -> Option<web_sys::ResizeObserverEntry> {
|
||||
self.downcast::<web_sys::CustomEvent>()
|
||||
.and_then(|e| e.detail().dyn_into::<web_sys::ResizeObserverEntry>().ok())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -325,6 +343,7 @@ pub(crate) fn load_document() -> Document {
|
|||
.expect("should have access to the Document")
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct WebImageEvent {
|
||||
raw: Event,
|
||||
error: bool,
|
||||
|
|
Loading…
Add table
Reference in a new issue