Fix autofill event downcast - ignore events of the wrong type (#5379)

* fix autofill events

* ignore the wrong type of event like we do in desktop

* check the event type before dispatch instead of panicing on downcast

* clean up conversion

* remove example

* pull out shared event list

* pull in deserialize_raw

* unified event list

* prettier comment

* restore test

* restore alias

* normal doc comments

* move over comments

* minimize diff

* remove alias, was moved into the macro
This commit is contained in:
Evan Almloff authored and GitHub committed 2026-04-07 13:18:24 -07:00
1 parent 610ee8107f
commit d8026f8824
27 files changed
+596 -960

No files matched your search

-13
View File
@@ -130,16 +130,3 @@ pub trait HasAnimationData: std::any::Any {
/// return self as Any
fn as_any(&self) -> &dyn std::any::Any;
}
impl_event! [
AnimationData;
/// onanimationstart
onanimationstart
/// onanimationend
onanimationend
/// onanimationiteration
onanimationiteration
];
-7
View File
@@ -79,10 +79,3 @@ pub trait HasCancelData: std::any::Any {
/// return self as Any
fn as_any(&self) -> &dyn std::any::Any;
}
impl_event! {
CancelData;
/// oncancel
oncancel
}
-13
View File
@@ -79,16 +79,3 @@ pub trait HasClipboardData: std::any::Any {
/// return self as Any
fn as_any(&self) -> &dyn std::any::Any;
}
impl_event![
ClipboardData;
/// oncopy
oncopy
/// oncut
oncut
/// onpaste
onpaste
];
-13
View File
@@ -94,16 +94,3 @@ pub trait HasCompositionData: std::any::Any {
/// return self as Any
fn as_any(&self) -> &dyn std::any::Any;
}
impl_event! [
CompositionData;
/// oncompositionstart
oncompositionstart
/// oncompositionend
oncompositionend
/// oncompositionupdate
oncompositionupdate
];
-28
View File
@@ -226,31 +226,3 @@ pub trait HasDragData: HasMouseData + crate::HasFileData + crate::HasDataTransfe
/// return self as Any
fn as_any(&self) -> &dyn std::any::Any;
}
impl_event! {
DragData;
/// ondrag
ondrag
/// ondragend
ondragend
/// ondragenter
ondragenter
/// ondragexit
ondragexit
/// ondragleave
ondragleave
/// ondragover
ondragover
/// ondragstart
ondragstart
/// ondrop
ondrop
}
-16
View File
@@ -79,19 +79,3 @@ pub trait HasFocusData: std::any::Any {
/// return self as Any
fn as_any(&self) -> &dyn std::any::Any;
}
impl_event! [
FocusData;
/// onfocus
onfocus
// onfocusout
onfocusout
// onfocusin
onfocusin
/// onblur
onblur
];
-62
View File
@@ -346,65 +346,3 @@ mod serialize {
}
}
}
impl_event! {
FormData;
/// onchange
onchange
/// The `oninput` event is fired when the value of a `<input>`, `<select>`, or `<textarea>` element is changed.
///
/// There are two main approaches to updating your input element:
/// 1) Controlled inputs directly update the value of the input element as the user interacts with the element
///
/// ```rust
/// use dioxus::prelude::*;
///
/// fn App() -> Element {
/// let mut value = use_signal(|| "hello world".to_string());
///
/// rsx! {
/// input {
/// // We directly set the value of the input element to our value signal
/// value: "{value}",
/// // The `oninput` event handler will run every time the user changes the value of the input element
/// // We can set the `value` signal to the new value of the input element
/// oninput: move |event| value.set(event.value())
/// }
/// // Since this is a controlled input, we can also update the value of the input element directly
/// button {
/// onclick: move |_| value.write().clear(),
/// "Clear"
/// }
/// }
/// }
/// ```
///
/// 2) Uncontrolled inputs just read the value of the input element as it changes
///
/// ```rust
/// use dioxus::prelude::*;
///
/// fn App() -> Element {
/// rsx! {
/// input {
/// // In uncontrolled inputs, we don't set the value of the input element directly
/// // But you can still read the value of the input element
/// oninput: move |event| println!("{}", event.value()),
/// }
/// // Since we don't directly control the value of the input element, we can't easily modify it
/// }
/// }
/// ```
oninput
/// oninvalid
oninvalid
/// onreset
onreset
/// onsubmit
onsubmit
}
+448
View File
@@ -0,0 +1,448 @@
use super::*;
#[doc(hidden)]
#[macro_export]
macro_rules! with_html_event_groups {
($macro:ident) => {
$macro! {
enum Event {
#[convert = convert_animation_data]
#[events = [
onanimationstart => animationstart,
onanimationend => animationend,
onanimationiteration => animationiteration,
]]
Animation(AnimationData),
#[convert = convert_cancel_data]
#[events = [
oncancel => cancel,
]]
Cancel(CancelData),
#[convert = convert_clipboard_data]
#[events = [
oncopy => copy,
oncut => cut,
onpaste => paste,
]]
Clipboard(ClipboardData),
#[convert = convert_composition_data]
#[events = [
oncompositionstart => compositionstart,
oncompositionend => compositionend,
oncompositionupdate => compositionupdate,
]]
Composition(CompositionData),
#[convert = convert_drag_data]
#[events = [
ondrag => drag,
ondragend => dragend,
ondragenter => dragenter,
ondragexit => dragexit,
ondragleave => dragleave,
ondragover => dragover,
ondragstart => dragstart,
ondrop => drop,
]]
Drag(DragData),
#[convert = convert_focus_data]
#[events = [
onfocus => focus,
onfocusout => focusout,
onfocusin => focusin,
onblur => blur,
]]
Focus(FocusData),
#[convert = convert_form_data]
#[events = [
onchange => change,
/// The `oninput` event is fired when the value of a `<input>`, `<select>`, or `<textarea>` element is changed.
///
/// There are two main approaches to updating your input element:
/// 1) Controlled inputs directly update the value of the input element as the user interacts with the element
///
/// ```rust
/// use dioxus::prelude::*;
///
/// fn App() -> Element {
/// let mut value = use_signal(|| "hello world".to_string());
///
/// rsx! {
/// input {
/// value: "{value}",
/// oninput: move |event| value.set(event.value())
/// }
/// button {
/// onclick: move |_| value.write().clear(),
/// "Clear"
/// }
/// }
/// }
/// ```
///
/// 2) Uncontrolled inputs just read the value of the input element as it changes
///
/// ```rust
/// use dioxus::prelude::*;
///
/// fn App() -> Element {
/// rsx! {
/// input {
/// oninput: move |event| println!("{}", event.value()),
/// }
/// }
/// }
/// ```
oninput => input,
oninvalid => invalid,
onreset => reset,
onsubmit => submit,
]]
Form(FormData),
#[convert = convert_image_data]
#[events = [
onerror => error,
onload => load,
]]
Image(ImageData),
#[convert = convert_keyboard_data]
#[events = [
onkeydown => keydown,
onkeypress => keypress,
onkeyup => keyup,
]]
Keyboard(KeyboardData),
#[convert = convert_media_data]
#[events = [
onabort => abort,
oncanplay => canplay,
oncanplaythrough => canplaythrough,
ondurationchange => durationchange,
onemptied => emptied,
onencrypted => encrypted,
onended => ended,
onloadeddata => loadeddata,
onloadedmetadata => loadedmetadata,
onloadstart => loadstart,
onpause => pause,
onplay => play,
onplaying => playing,
onprogress => progress,
onratechange => ratechange,
onseeked => seeked,
onseeking => seeking,
onstalled => stalled,
onsuspend => suspend,
ontimeupdate => timeupdate,
onvolumechange => volumechange,
onwaiting => waiting,
]]
#[raw = [interruptbegin, interruptend, loadend, timeout]]
Media(MediaData),
#[convert = convert_mounted_data]
#[events = [
#[doc(alias = "ref")]
#[doc(alias = "createRef")]
#[doc(alias = "useRef")]
/// The onmounted event is fired when the element is first added to the DOM. This event gives you a [`MountedData`] object and lets you interact with the raw DOM element.
///
/// This event is fired once per element. If you need to access the element multiple times, you can store the [`MountedData`] object in a [`use_signal`](https://docs.rs/dioxus-hooks/latest/dioxus_hooks/fn.use_signal.html) hook and use it as needed.
///
/// # Examples
///
/// ```rust, no_run
/// # use dioxus::prelude::*;
/// fn App() -> Element {
/// let mut header_element = use_signal(|| None);
///
/// rsx! {
/// div {
/// h1 {
/// // The onmounted event will run the first time the h1 element is mounted
/// onmounted: move |element| header_element.set(Some(element.data())),
/// "Scroll to top example"
/// }
///
/// for i in 0..100 {
/// div { "Item {i}" }
/// }
///
/// button {
/// // When you click the button, if the header element has been mounted, we scroll to that element
/// onclick: move |_| async move {
/// if let Some(header) = header_element.cloned() {
/// let _ = header.scroll_to(ScrollBehavior::Smooth).await;
/// }
/// },
/// "Scroll to top"
/// }
/// }
/// }
/// }
/// ```
///
/// The `MountedData` struct contains cross platform APIs that work on the desktop, mobile, liveview and web platforms. For the web platform, you can also downcast the `MountedData` event to the `web-sys::Element` type for more web specific APIs:
///
/// ```rust, ignore
/// use dioxus::prelude::*;
/// use dioxus_web::WebEventExt; // provides [`as_web_event()`] method
///
/// fn App() -> Element {
/// rsx! {
/// div {
/// id: "some-id",
/// onmounted: move |element| {
/// // You can use the web_event trait to downcast the element to a web specific event. For the mounted event, this will be a web_sys::Element
/// let web_sys_element = element.as_web_event();
/// assert_eq!(web_sys_element.id(), "some-id");
/// }
/// }
/// }
/// }
/// ```
onmounted => mounted,
]]
Mounted(MountedData),
#[convert = convert_mouse_data]
#[events = [
/// Execute a callback when a button is clicked.
///
/// ## Description
///
/// An element receives a click event when a pointing device button (such as a mouse's primary mouse button)
/// is both pressed and released while the pointer is located inside the element.
///
/// - Bubbles: Yes
/// - Cancelable: Yes
/// - Interface(InteData): [`MouseEvent`]
///
/// If the button is pressed on one element and the pointer is moved outside the element before the button
/// is released, the event is fired on the most specific ancestor element that contained both elements.
/// `click` fires after both the `mousedown` and `mouseup` events have fired, in that order.
///
/// ## Example
/// ```rust, ignore
/// rsx!( button { onclick: move |_| tracing::info!("Clicked!"), "click me" } )
/// ```
///
/// ## Reference
/// - <https://www.w3schools.com/tags/ev_onclick.asp>
/// - <https://developer.mozilla.org/en-US/docs/Web/API/Element/click_event>
onclick => click,
oncontextmenu => contextmenu,
#[deprecated(since = "0.5.0", note = "use ondoubleclick instead")]
ondblclick => dblclick,
#[doc(alias = "ondblclick")]
ondoubleclick => dblclick,
onmousedown => mousedown,
onmouseenter => mouseenter,
onmouseleave => mouseleave,
onmousemove => mousemove,
onmouseout => mouseout,
/// Triggered when the users's mouse hovers over an element.
onmouseover => mouseover,
onmouseup => mouseup,
]]
#[raw = [doubleclick]]
Mouse(MouseData),
#[convert = convert_pointer_data]
#[events = [
onpointerdown => pointerdown,
onpointermove => pointermove,
onpointerup => pointerup,
onpointercancel => pointercancel,
ongotpointercapture => gotpointercapture,
onlostpointercapture => lostpointercapture,
onpointerenter => pointerenter,
onpointerleave => pointerleave,
onpointerover => pointerover,
onpointerout => pointerout,
onauxclick => auxclick,
]]
#[raw = [pointerlockchange, pointerlockerror]]
Pointer(PointerData),
#[convert = convert_resize_data]
#[events = [
onresize => resize,
]]
Resize(ResizeData),
#[convert = convert_scroll_data]
#[events = [
onscroll => scroll,
onscrollend => scrollend,
]]
Scroll(ScrollData),
#[convert = convert_selection_data]
#[events = [
onselect => select,
onselectstart => selectstart,
onselectionchange => selectionchange,
]]
Selection(SelectionData),
#[convert = convert_toggle_data]
#[events = [
ontoggle => toggle,
onbeforetoggle => beforetoggle,
]]
Toggle(ToggleData),
#[convert = convert_touch_data]
#[events = [
ontouchstart => touchstart,
ontouchmove => touchmove,
ontouchend => touchend,
ontouchcancel => touchcancel,
]]
Touch(TouchData),
#[convert = convert_transition_data]
#[events = [
ontransitionend => transitionend,
]]
Transition(TransitionData),
#[convert = convert_visible_data]
#[events = [
onvisible => visible,
]]
Visible(VisibleData),
#[convert = convert_wheel_data]
#[events = [
/// Called when the mouse wheel is rotated over an element.
onwheel => wheel,
]]
Wheel(WheelData),
}
}
};
}
macro_rules! expand_html_event_converter {
(
enum Event {
$(
#[convert = $converter:ident]
#[events = [
$(
$( #[$attr:meta] )*
$name:ident => $raw:ident,
)*
]]
$(#[raw = [$($raw_only:ident),* $(,)?]])?
$group:ident($data:ident),
)*
}
) => {
/// A converter between a platform specific event and a general event. All code in a renderer that has a large binary size should be placed in this trait. Each of these functions should be snipped in high levels of optimization.
pub trait HtmlEventConverter: Send + Sync {
$(
fn $converter(&self, event: &PlatformEventData) -> $data;
)*
}
$(
impl From<&PlatformEventData> for $data {
fn from(val: &PlatformEventData) -> Self {
with_event_converter(|c| c.$converter(val))
}
}
)*
};
}
#[cfg(feature = "serialize")]
macro_rules! expand_html_event_deserialize {
(
enum Event {
$(
#[convert = $converter:ident]
#[events = [
$(
$( #[$attr:meta] )*
$name:ident => $raw:ident,
)*
]]
$(#[raw = [$($raw_only:ident),* $(,)?]])?
$group:ident($data:ident),
)*
}
) => {
pub(crate) fn deserialize_raw_event(
name: &str,
data: &serde_json::Value,
) -> Result<Option<crate::transit::EventData>, serde_json::Error> {
#[inline]
fn de<'de, F>(f: &'de serde_json::Value) -> Result<F, serde_json::Error>
where
F: serde::Deserialize<'de>,
{
F::deserialize(f)
}
Ok(match name {
$(
$( stringify!($raw) )|* $($(| stringify!($raw_only))*)? => {
Some(expand_html_event_deserialize!(@deserialize $group, data))
}
)*
_ => None,
})
}
};
(@deserialize Mounted, $data:ident) => {
crate::transit::EventData::Mounted
};
(@deserialize $group:ident, $data:ident) => {
crate::transit::EventData::$group(de($data)?)
};
}
macro_rules! expand_html_event_listeners {
(
enum Event {
$(
#[convert = $converter:ident]
#[events = [
$(
$( #[$attr:meta] )*
$name:ident => $raw:ident,
)*
]]
$(#[raw = [$($raw_only:ident),* $(,)?]])?
$group:ident($data:ident),
)*
}
) => {
$(
impl_event! {
$data;
$(
#[doc = concat!(stringify!($name))]
$( #[$attr] )*
$name: concat!("on", stringify!($raw));
)*
}
)*
};
}
with_html_event_groups!(expand_html_event_converter);
#[cfg(feature = "serialize")]
with_html_event_groups!(expand_html_event_deserialize);
with_html_event_groups!(expand_html_event_listeners);
-10
View File
@@ -94,13 +94,3 @@ pub trait HasImageData: std::any::Any {
/// return self as Any
fn as_any(&self) -> &dyn std::any::Any;
}
impl_event! [
ImageData;
/// onerror
onerror
/// onload
onload
];
-13
View File
@@ -226,19 +226,6 @@ impl<'de> serde::Deserialize<'de> for KeyboardData {
}
}
impl_event! {
KeyboardData;
/// onkeydown
onkeydown
/// onkeypress
onkeypress
/// onkeyup
onkeyup
}
pub trait HasKeyboardData: ModifiersInteraction + std::any::Any {
/// The value of the key pressed by the user, taking into consideration the state of modifier keys such as Shift as well as the keyboard locale and layout.
fn key(&self) -> Key;
-75
View File
@@ -78,78 +78,3 @@ pub trait HasMediaData: std::any::Any {
/// return self as Any
fn as_any(&self) -> &dyn std::any::Any;
}
impl_event! [
MediaData;
///abort
onabort
///canplay
oncanplay
///canplaythrough
oncanplaythrough
///durationchange
ondurationchange
///emptied
onemptied
///encrypted
onencrypted
///ended
onended
// todo: this conflicts with Media events
// neither have data, so it's okay
// ///error
// onerror
///loadeddata
onloadeddata
///loadedmetadata
onloadedmetadata
///loadstart
onloadstart
///pause
onpause
///play
onplay
///playing
onplaying
///progress
onprogress
///ratechange
onratechange
///seeked
onseeked
///seeking
onseeking
///stalled
onstalled
///suspend
onsuspend
///timeupdate
ontimeupdate
///volumechange
onvolumechange
///waiting
onwaiting
];
+4 -176
View File
@@ -8,7 +8,7 @@ macro_rules! impl_event {
$data:ty;
$(
$( #[$attr:meta] )*
$name:ident $(: $js_name:literal)?
$name:ident $( : $js_name:expr )?;
)*
) => {
$(
@@ -21,9 +21,6 @@ macro_rules! impl_event {
/// </details>
///
#[doc = include_str!("../../docs/common_event_handler_errors.md")]
$(
#[doc(alias = $js_name)]
)?
#[inline]
pub fn $name<__Marker>(mut _f: impl ::dioxus_core::SuperInto<::dioxus_core::ListenerCallback<$data>, __Marker>) -> ::dioxus_core::Attribute {
let event_handler = _f.super_into();
@@ -60,7 +57,7 @@ macro_rules! impl_event {
)*
};
(@name $name:ident $js_name:literal) => {
(@name $name:ident $js_name:expr) => {
$js_name
};
(@name $name:ident) => {
@@ -111,177 +108,7 @@ impl PlatformEventData {
}
}
/// A converter between a platform specific event and a general event. All code in a renderer that has a large binary size should be placed in this trait. Each of these functions should be snipped in high levels of optimization.
pub trait HtmlEventConverter: Send + Sync {
/// Convert a general event to an animation data event
fn convert_animation_data(&self, event: &PlatformEventData) -> AnimationData;
/// Convert a general event to a cancel data event
fn convert_cancel_data(&self, event: &PlatformEventData) -> CancelData;
/// Convert a general event to a clipboard data event
fn convert_clipboard_data(&self, event: &PlatformEventData) -> ClipboardData;
/// Convert a general event to a composition data event
fn convert_composition_data(&self, event: &PlatformEventData) -> CompositionData;
/// Convert a general event to a drag data event
fn convert_drag_data(&self, event: &PlatformEventData) -> DragData;
/// Convert a general event to a focus data event
fn convert_focus_data(&self, event: &PlatformEventData) -> FocusData;
/// Convert a general event to a form data event
fn convert_form_data(&self, event: &PlatformEventData) -> FormData;
/// Convert a general event to an image data event
fn convert_image_data(&self, event: &PlatformEventData) -> ImageData;
/// Convert a general event to a keyboard data event
fn convert_keyboard_data(&self, event: &PlatformEventData) -> KeyboardData;
/// Convert a general event to a media data event
fn convert_media_data(&self, event: &PlatformEventData) -> MediaData;
/// Convert a general event to a mounted data event
fn convert_mounted_data(&self, event: &PlatformEventData) -> MountedData;
/// Convert a general event to a mouse data event
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
fn convert_selection_data(&self, event: &PlatformEventData) -> SelectionData;
/// Convert a general event to a toggle data event
fn convert_toggle_data(&self, event: &PlatformEventData) -> ToggleData;
/// Convert a general event to a touch data event
fn convert_touch_data(&self, event: &PlatformEventData) -> TouchData;
/// Convert a general event to a transition data event
fn convert_transition_data(&self, event: &PlatformEventData) -> TransitionData;
/// Convert a general event to a visible data event
fn convert_visible_data(&self, event: &PlatformEventData) -> VisibleData;
/// Convert a general event to a wheel data event
fn convert_wheel_data(&self, event: &PlatformEventData) -> WheelData;
}
impl From<&PlatformEventData> for AnimationData {
fn from(val: &PlatformEventData) -> Self {
with_event_converter(|c| c.convert_animation_data(val))
}
}
impl From<&PlatformEventData> for CancelData {
fn from(val: &PlatformEventData) -> Self {
with_event_converter(|c| c.convert_cancel_data(val))
}
}
impl From<&PlatformEventData> for ClipboardData {
fn from(val: &PlatformEventData) -> Self {
with_event_converter(|c| c.convert_clipboard_data(val))
}
}
impl From<&PlatformEventData> for CompositionData {
fn from(val: &PlatformEventData) -> Self {
with_event_converter(|c| c.convert_composition_data(val))
}
}
impl From<&PlatformEventData> for DragData {
fn from(val: &PlatformEventData) -> Self {
with_event_converter(|c| c.convert_drag_data(val))
}
}
impl From<&PlatformEventData> for FocusData {
fn from(val: &PlatformEventData) -> Self {
with_event_converter(|c| c.convert_focus_data(val))
}
}
impl From<&PlatformEventData> for FormData {
fn from(val: &PlatformEventData) -> Self {
with_event_converter(|c| c.convert_form_data(val))
}
}
impl From<&PlatformEventData> for ImageData {
fn from(val: &PlatformEventData) -> Self {
with_event_converter(|c| c.convert_image_data(val))
}
}
impl From<&PlatformEventData> for KeyboardData {
fn from(val: &PlatformEventData) -> Self {
with_event_converter(|c| c.convert_keyboard_data(val))
}
}
impl From<&PlatformEventData> for MediaData {
fn from(val: &PlatformEventData) -> Self {
with_event_converter(|c| c.convert_media_data(val))
}
}
impl From<&PlatformEventData> for MountedData {
fn from(val: &PlatformEventData) -> Self {
with_event_converter(|c| c.convert_mounted_data(val))
}
}
impl From<&PlatformEventData> for MouseData {
fn from(val: &PlatformEventData) -> Self {
with_event_converter(|c| c.convert_mouse_data(val))
}
}
impl From<&PlatformEventData> for PointerData {
fn from(val: &PlatformEventData) -> Self {
with_event_converter(|c| c.convert_pointer_data(val))
}
}
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))
}
}
impl From<&PlatformEventData> for SelectionData {
fn from(val: &PlatformEventData) -> Self {
with_event_converter(|c| c.convert_selection_data(val))
}
}
impl From<&PlatformEventData> for ToggleData {
fn from(val: &PlatformEventData) -> Self {
with_event_converter(|c| c.convert_toggle_data(val))
}
}
impl From<&PlatformEventData> for TouchData {
fn from(val: &PlatformEventData) -> Self {
with_event_converter(|c| c.convert_touch_data(val))
}
}
impl From<&PlatformEventData> for TransitionData {
fn from(val: &PlatformEventData) -> Self {
with_event_converter(|c| c.convert_transition_data(val))
}
}
impl From<&PlatformEventData> for VisibleData {
fn from(val: &PlatformEventData) -> Self {
with_event_converter(|c| c.convert_visible_data(val))
}
}
impl From<&PlatformEventData> for WheelData {
fn from(val: &PlatformEventData) -> Self {
with_event_converter(|c| c.convert_wheel_data(val))
}
}
mod generated;
mod animation;
mod cancel;
@@ -312,6 +139,7 @@ pub use composition::*;
pub use drag::*;
pub use focus::*;
pub use form::*;
pub use generated::*;
pub use image::*;
pub use keyboard::*;
pub use media::*;
+1 -66
View File
@@ -213,72 +213,7 @@ use crate::geometry::{PixelsRect, PixelsSize, PixelsVector2D};
pub type MountedEvent = Event<MountedData>;
impl_event! [
MountedData;
#[doc(alias = "ref")]
#[doc(alias = "createRef")]
#[doc(alias = "useRef")]
/// The onmounted event is fired when the element is first added to the DOM. This event gives you a [`MountedData`] object and lets you interact with the raw DOM element.
///
/// This event is fired once per element. If you need to access the element multiple times, you can store the [`MountedData`] object in a [`use_signal`](https://docs.rs/dioxus-hooks/latest/dioxus_hooks/fn.use_signal.html) hook and use it as needed.
///
/// # Examples
///
/// ```rust, no_run
/// # use dioxus::prelude::*;
/// fn App() -> Element {
/// let mut header_element = use_signal(|| None);
///
/// rsx! {
/// div {
/// h1 {
/// // The onmounted event will run the first time the h1 element is mounted
/// onmounted: move |element| header_element.set(Some(element.data())),
/// "Scroll to top example"
/// }
///
/// for i in 0..100 {
/// div { "Item {i}" }
/// }
///
/// button {
/// // When you click the button, if the header element has been mounted, we scroll to that element
/// onclick: move |_| async move {
/// if let Some(header) = header_element.cloned() {
/// let _ = header.scroll_to(ScrollBehavior::Smooth).await;
/// }
/// },
/// "Scroll to top"
/// }
/// }
/// }
/// }
/// ```
///
/// The `MountedData` struct contains cross platform APIs that work on the desktop, mobile, liveview and web platforms. For the web platform, you can also downcast the `MountedData` event to the `web-sys::Element` type for more web specific APIs:
///
/// ```rust, ignore
/// use dioxus::prelude::*;
/// use dioxus_web::WebEventExt; // provides [`as_web_event()`] method
///
/// fn App() -> Element {
/// rsx! {
/// div {
/// id: "some-id",
/// onmounted: move |element| {
/// // You can use the web_event trait to downcast the element to a web specific event. For the mounted event, this will be a web_sys::Element
/// let web_sys_element = element.as_web_event();
/// assert_eq!(web_sys_element.id(), "some-id");
/// }
/// }
/// }
/// }
/// ```
onmounted
];
pub use onmounted as onmount;
pub use super::onmounted as onmount;
/// The MountedResult type for the MountedData
pub type MountedResult<T> = Result<T, MountedError>;
-60
View File
@@ -44,66 +44,6 @@ pub trait HasMouseData: PointerInteraction {
fn as_any(&self) -> &dyn std::any::Any;
}
impl_event! {
MouseData;
/// Execute a callback when a button is clicked.
///
/// ## Description
///
/// An element receives a click event when a pointing device button (such as a mouse's primary mouse button)
/// is both pressed and released while the pointer is located inside the element.
///
/// - Bubbles: Yes
/// - Cancelable: Yes
/// - Interface(InteData): [`MouseEvent`]
///
/// If the button is pressed on one element and the pointer is moved outside the element before the button
/// is released, the event is fired on the most specific ancestor element that contained both elements.
/// `click` fires after both the `mousedown` and `mouseup` events have fired, in that order.
///
/// ## Example
/// ```rust, ignore
/// rsx!( button { onclick: move |_| tracing::info!("Clicked!"), "click me" } )
/// ```
///
/// ## Reference
/// - <https://www.w3schools.com/tags/ev_onclick.asp>
/// - <https://developer.mozilla.org/en-US/docs/Web/API/Element/click_event>
onclick
/// oncontextmenu
oncontextmenu
#[deprecated(since = "0.5.0", note = "use ondoubleclick instead")]
ondblclick
ondoubleclick: "ondblclick"
/// onmousedown
onmousedown
/// onmouseenter
onmouseenter
/// onmouseleave
onmouseleave
/// onmousemove
onmousemove
/// onmouseout
onmouseout
/// onmouseover
///
/// Triggered when the users's mouse hovers over an element.
onmouseover
/// onmouseup
onmouseup
}
impl MouseData {
/// Create a new instance of MouseData
pub fn new(inner: impl HasMouseData + 'static) -> Self {
-36
View File
@@ -105,42 +105,6 @@ pub trait HasPointerData: PointerInteraction {
fn as_any(&self) -> &dyn std::any::Any;
}
impl_event![
PointerData;
/// pointerdown
onpointerdown
/// pointermove
onpointermove
/// pointerup
onpointerup
/// pointercancel
onpointercancel
/// gotpointercapture
ongotpointercapture
/// lostpointercapture
onlostpointercapture
/// pointerenter
onpointerenter
/// pointerleave
onpointerleave
/// pointerover
onpointerover
/// pointerout
onpointerout
/// auxclick
onauxclick
];
impl PointerData {
/// Gets the unique identifier of the pointer causing the event.
pub fn pointer_id(&self) -> i32 {
-7
View File
@@ -132,13 +132,6 @@ 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>;
-10
View File
@@ -171,13 +171,3 @@ pub trait HasScrollData: std::any::Any {
/// Get the viewport height
fn client_height(&self) -> i32;
}
impl_event! {
ScrollData;
/// onscroll
onscroll
/// onscrollend
onscrollend
}
-13
View File
@@ -79,16 +79,3 @@ pub trait HasSelectionData: std::any::Any {
/// return self as Any
fn as_any(&self) -> &dyn std::any::Any;
}
impl_event! [
SelectionData;
/// select
onselect
/// selectstart
onselectstart
/// selectionchange
onselectionchange
];
-10
View File
@@ -79,13 +79,3 @@ pub trait HasToggleData: std::any::Any {
/// return self as Any
fn as_any(&self) -> &dyn std::any::Any;
}
impl_event! {
ToggleData;
/// ontoggle
ontoggle
/// onbeforetoggle
onbeforetoggle
}
-15
View File
@@ -351,18 +351,3 @@ impl InteractionLocation for SerializedTouchPoint {
PagePoint::new(self.page_x, self.page_y)
}
}
impl_event! {
TouchData;
/// touchstart
ontouchstart
/// touchmove
ontouchmove
/// touchend
ontouchend
/// touchcancel
ontouchcancel
}
-7
View File
@@ -108,10 +108,3 @@ pub trait HasTransitionData: std::any::Any {
/// return self as Any
fn as_any(&self) -> &dyn std::any::Any;
}
impl_event! {
TransitionData;
/// transitionend
ontransitionend
}
-7
View File
@@ -266,13 +266,6 @@ use crate::geometry::PixelsRect;
pub type VisibleEvent = Event<VisibleData>;
impl_event! {
VisibleData;
/// onvisible
onvisible
}
/// The VisibleResult type for the VisibleData
pub type VisibleResult<T> = Result<T, VisibleError>;
-7
View File
@@ -223,13 +223,6 @@ impl<'de> serde::Deserialize<'de> for WheelData {
}
}
impl_event![
WheelData;
/// Called when the mouse wheel is rotated over an element.
onwheel
];
pub trait HasWheelData: HasMouseData + std::any::Any {
/// The amount of wheel movement
fn delta(&self) -> WheelDelta;
+5 -90
View File
@@ -53,97 +53,12 @@ impl<'de> Deserialize<'de> for HtmlEvent {
#[cfg(feature = "serialize")]
fn deserialize_raw(name: &str, data: &serde_json::Value) -> Result<EventData, serde_json::Error> {
use EventData::*;
// a little macro-esque thing to make the code below more readable
#[inline]
fn de<'de, F>(f: &'de serde_json::Value) -> Result<F, serde_json::Error>
where
F: Deserialize<'de>,
{
F::deserialize(f)
match deserialize_raw_event(name, data)? {
Some(result) => Ok(result),
None => Err(serde::de::Error::custom(format!(
"Unknown event type: {name}"
))),
}
let data = match name {
// Cancel
"cancel" => Cancel(de(data)?),
// Mouse
"click" | "contextmenu" | "dblclick" | "doubleclick" | "mousedown" | "mouseenter"
| "mouseleave" | "mousemove" | "mouseout" | "mouseover" | "mouseup" => Mouse(de(data)?),
// Clipboard
"copy" | "cut" | "paste" => Clipboard(de(data)?),
// Composition
"compositionend" | "compositionstart" | "compositionupdate" => Composition(de(data)?),
// Keyboard
"keydown" | "keypress" | "keyup" => Keyboard(de(data)?),
// Focus
"blur" | "focus" | "focusin" | "focusout" => Focus(de(data)?),
// Form
"change" | "input" | "invalid" | "reset" | "submit" => Form(de(data)?),
// Drag
"drag" | "dragend" | "dragenter" | "dragexit" | "dragleave" | "dragover" | "dragstart"
| "drop" => Drag(de(data)?),
// Pointer
"pointerlockchange" | "pointerlockerror" | "pointerdown" | "pointermove" | "pointerup"
| "pointerover" | "pointerout" | "pointerenter" | "pointerleave" | "gotpointercapture"
| "lostpointercapture" | "auxclick" => Pointer(de(data)?),
// Selection
"selectstart" | "selectionchange" | "select" => Selection(de(data)?),
// Touch
"touchcancel" | "touchend" | "touchmove" | "touchstart" => Touch(de(data)?),
// Resize
"resize" => Resize(de(data)?),
// Scroll
"scroll" | "scrollend" => Scroll(de(data)?),
// Visible
"visible" => Visible(de(data)?),
// Wheel
"wheel" => Wheel(de(data)?),
// Media
"abort" | "canplay" | "canplaythrough" | "durationchange" | "emptied" | "encrypted"
| "ended" | "interruptbegin" | "interruptend" | "loadeddata" | "loadedmetadata"
| "loadstart" | "pause" | "play" | "playing" | "progress" | "ratechange" | "seeked"
| "seeking" | "stalled" | "suspend" | "timeupdate" | "volumechange" | "waiting"
| "loadend" | "timeout" => Media(de(data)?),
// Animation
"animationstart" | "animationend" | "animationiteration" => Animation(de(data)?),
// Transition
"transitionend" => Transition(de(data)?),
// Toggle
"toggle" => Toggle(de(data)?),
"load" | "error" => Image(de(data)?),
// Mounted
"mounted" => Mounted,
// OtherData => "abort" | "afterprint" | "beforeprint" | "beforeunload" | "hashchange" | "languagechange" | "message" | "offline" | "online" | "pagehide" | "pageshow" | "popstate" | "rejectionhandled" | "storage" | "unhandledrejection" | "unload" | "userproximity" | "vrdisplayactivate" | "vrdisplayblur" | "vrdisplayconnect" | "vrdisplaydeactivate" | "vrdisplaydisconnect" | "vrdisplayfocus" | "vrdisplaypointerrestricted" | "vrdisplaypointerunrestricted" | "vrdisplaypresentchange";
other => {
return Err(serde::de::Error::custom(format!(
"Unknown event type: {other}"
)))
}
};
Ok(data)
}
#[cfg(feature = "serialize")]
+11 -1
View File
@@ -15,7 +15,9 @@ use rustc_hash::FxHashMap;
use wasm_bindgen::{closure::Closure, JsCast};
use web_sys::{Document, Event, Node};
use crate::{load_document, virtual_event_from_websys_event, Config, WebEventConverter};
use crate::{
event_type_matches, load_document, virtual_event_from_websys_event, Config, WebEventConverter,
};
pub struct WebsysDom {
#[allow(dead_code)]
@@ -94,6 +96,14 @@ impl WebsysDom {
return;
};
// Some browser features (e.g. datalist autocomplete) dispatch
// a plain Event with a typed name like "keydown" that isn't
// actually a KeyboardEvent. Drop events whose JS type doesn't
// match what the converters will unchecked-cast to.
if !event_type_matches(name.as_str(), web_sys_event) {
return;
}
let data = virtual_event_from_websys_event(web_sys_event.clone(), target);
let event = dioxus_core::Event::new(Rc::new(data) as Rc<dyn Any>, bubbles);
-6
View File
@@ -3,12 +3,6 @@ use web_sys::Event;
use super::{Synthetic, WebEventExt};
impl From<&Event> for Synthetic<Event> {
fn from(e: &Event) -> Self {
Synthetic::new(e.clone())
}
}
impl HasClipboardData for Synthetic<Event> {
fn as_any(&self) -> &dyn std::any::Any {
&self.event
+127 -199
View File
@@ -1,6 +1,4 @@
use dioxus_html::{
DragData, FormData, HtmlEventConverter, ImageData, MountedData, PlatformEventData,
};
use dioxus_html::{DragData, FormData, HtmlEventConverter, ImageData, PlatformEventData};
use form::WebFormData;
use load::WebImageEvent;
use wasm_bindgen::JsCast;
@@ -52,170 +50,135 @@ fn downcast_event(event: &dioxus_html::PlatformEventData) -> &GenericWebSysEvent
.expect("event should be a GenericWebSysEvent")
}
impl HtmlEventConverter for WebEventConverter {
#[inline(always)]
fn convert_animation_data(
&self,
event: &dioxus_html::PlatformEventData,
) -> dioxus_html::AnimationData {
Synthetic::<web_sys::AnimationEvent>::from(downcast_event(event).raw.clone()).into()
}
#[inline(always)]
fn convert_cancel_data(
&self,
event: &dioxus_html::PlatformEventData,
) -> dioxus_html::CancelData {
Synthetic::new(downcast_event(event).raw.clone()).into()
}
#[inline(always)]
fn convert_clipboard_data(
&self,
event: &dioxus_html::PlatformEventData,
) -> dioxus_html::ClipboardData {
Synthetic::new(downcast_event(event).raw.clone()).into()
}
#[inline(always)]
fn convert_composition_data(
&self,
event: &dioxus_html::PlatformEventData,
) -> dioxus_html::CompositionData {
Synthetic::<web_sys::CompositionEvent>::from(downcast_event(event).raw.clone()).into()
}
#[inline(always)]
fn convert_drag_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::DragData {
let event = downcast_event(event);
DragData::new(Synthetic::new(
event.raw.clone().unchecked_into::<web_sys::DragEvent>(),
))
}
#[inline(always)]
fn convert_focus_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::FocusData {
Synthetic::<web_sys::FocusEvent>::from(downcast_event(event).raw.clone()).into()
}
#[inline(always)]
fn convert_form_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::FormData {
let event = downcast_event(event);
FormData::new(WebFormData::new(event.element.clone(), event.raw.clone()))
}
#[inline(always)]
fn convert_image_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::ImageData {
let event = downcast_event(event);
let error = event.raw.type_() == "error";
ImageData::new(WebImageEvent::new(event.raw.clone(), error))
}
#[inline(always)]
fn convert_keyboard_data(
&self,
event: &dioxus_html::PlatformEventData,
) -> dioxus_html::KeyboardData {
Synthetic::<web_sys::KeyboardEvent>::from(downcast_event(event).raw.clone()).into()
}
#[inline(always)]
fn convert_media_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::MediaData {
Synthetic::new(downcast_event(event).raw.clone()).into()
}
#[allow(unused_variables)]
#[inline(always)]
fn convert_mounted_data(&self, event: &dioxus_html::PlatformEventData) -> MountedData {
#[cfg(feature = "mounted")]
{
Synthetic::new(
event
.downcast::<web_sys::Element>()
.expect("event should be a web_sys::Element")
.clone(),
)
.into()
macro_rules! with_web_event_converters {
($macro:ident) => {
$macro! {
convert_animation_data(AnimationData) => web_sys::AnimationEvent;
convert_cancel_data(CancelData) => web_sys::Event;
convert_clipboard_data(ClipboardData) => web_sys::Event;
convert_composition_data(CompositionData) => web_sys::CompositionEvent;
convert_drag_data(DragData) => web_sys::DragEvent => |event| {
let event = downcast_event(event);
DragData::new(Synthetic::new(
event.raw.clone().unchecked_into::<web_sys::DragEvent>(),
))
};
convert_focus_data(FocusData) => web_sys::FocusEvent;
convert_form_data(FormData) => web_sys::Event => |event| {
let event = downcast_event(event);
FormData::new(WebFormData::new(event.element.clone(), event.raw.clone()))
};
convert_image_data(ImageData) => web_sys::Event => |event| {
let event = downcast_event(event);
ImageData::new(WebImageEvent::new(
event.raw.clone(),
event.raw.type_() == "error",
))
};
convert_keyboard_data(KeyboardData) => web_sys::KeyboardEvent;
convert_media_data(MediaData) => web_sys::Event;
convert_mounted_data(MountedData) => web_sys::Element => |event| {
#[cfg(feature = "mounted")]
{
Synthetic::new(
event
.downcast::<web_sys::Element>()
.expect("event should be a web_sys::Element")
.clone(),
)
.into()
}
#[cfg(not(feature = "mounted"))]
{
let _ = event;
panic!("mounted events require the `mounted` feature on dioxus-web")
}
};
convert_mouse_data(MouseData) => web_sys::MouseEvent;
convert_pointer_data(PointerData) => web_sys::PointerEvent;
convert_resize_data(ResizeData) => web_sys::ResizeObserverEntry;
convert_scroll_data(ScrollData) => web_sys::Event;
convert_selection_data(SelectionData) => web_sys::Event;
convert_toggle_data(ToggleData) => web_sys::Event;
convert_touch_data(TouchData) => web_sys::TouchEvent;
convert_transition_data(TransitionData) => web_sys::TransitionEvent;
convert_visible_data(VisibleData) => web_sys::IntersectionObserverEntry;
convert_wheel_data(WheelData) => web_sys::WheelEvent;
}
#[cfg(not(feature = "mounted"))]
{
panic!("mounted events are not supported without the mounted feature on the dioxus-web crate enabled")
}
}
#[inline(always)]
fn convert_mouse_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::MouseData {
Synthetic::<web_sys::MouseEvent>::from(downcast_event(event).raw.clone()).into()
}
#[inline(always)]
fn convert_pointer_data(
&self,
event: &dioxus_html::PlatformEventData,
) -> dioxus_html::PointerData {
Synthetic::<web_sys::PointerEvent>::from(downcast_event(event).raw.clone()).into()
}
#[inline(always)]
fn convert_resize_data(
&self,
event: &dioxus_html::PlatformEventData,
) -> dioxus_html::ResizeData {
Synthetic::<web_sys::ResizeObserverEntry>::from(downcast_event(event).raw.clone()).into()
}
#[inline(always)]
fn convert_scroll_data(
&self,
event: &dioxus_html::PlatformEventData,
) -> dioxus_html::ScrollData {
Synthetic::new(downcast_event(event).raw.clone()).into()
}
#[inline(always)]
fn convert_selection_data(
&self,
event: &dioxus_html::PlatformEventData,
) -> dioxus_html::SelectionData {
Synthetic::new(downcast_event(event).raw.clone()).into()
}
#[inline(always)]
fn convert_toggle_data(
&self,
event: &dioxus_html::PlatformEventData,
) -> dioxus_html::ToggleData {
Synthetic::new(downcast_event(event).raw.clone()).into()
}
#[inline(always)]
fn convert_touch_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::TouchData {
Synthetic::<web_sys::TouchEvent>::from(downcast_event(event).raw.clone()).into()
}
#[inline(always)]
fn convert_transition_data(
&self,
event: &dioxus_html::PlatformEventData,
) -> dioxus_html::TransitionData {
Synthetic::<web_sys::TransitionEvent>::from(downcast_event(event).raw.clone()).into()
}
#[inline(always)]
fn convert_visible_data(
&self,
event: &dioxus_html::PlatformEventData,
) -> dioxus_html::VisibleData {
Synthetic::<web_sys::IntersectionObserverEntry>::from(downcast_event(event).raw.clone())
.into()
}
#[inline(always)]
fn convert_wheel_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::WheelData {
Synthetic::<web_sys::WheelEvent>::from(downcast_event(event).raw.clone()).into()
}
};
}
macro_rules! expand_web_event_converter {
(
$(
$converter:ident($data:ident) => $web_ty:ty $(=> |$event:ident| $body:block)?;
)*
) => {
macro_rules! web_event_type_matches {
$(
($event_name:ident, $converter) => {
$event_name.is_instance_of::<$web_ty>()
};
)*
}
impl HtmlEventConverter for WebEventConverter {
$(
expand_web_event_converter!(@method $converter, $data, $web_ty $(, $event, $body)?);
)*
}
};
(@method $converter:ident, $data:ident, $web_ty:ty) => {
#[inline(always)]
fn $converter(&self, event: &PlatformEventData) -> dioxus_html::$data {
Synthetic::new(downcast_event(event).raw.clone().unchecked_into::<$web_ty>()).into()
}
};
(@method $converter:ident, $data:ident, $web_ty:ty, $event:ident, $body:block) => {
#[inline(always)]
fn $converter(&self, $event: &PlatformEventData) -> dioxus_html::$data $body
};
}
with_web_event_converters!(expand_web_event_converter);
macro_rules! expand_web_event_changes {
(
enum Event {
$(
#[convert = $converter:ident]
#[events = [
$(
$( #[$attr:meta] )*
$name:ident => $raw:ident,
)*
]]
$(#[raw = [$($raw_only:ident),* $(,)?]])?
$group:ident($data:ident),
)*
}
) => {
pub(crate) fn event_type_matches(name: &str, event: &web_sys::Event) -> bool {
let m = match name {
$(
$( stringify!($raw) )|* $($(| stringify!($raw_only))*)? => {
web_event_type_matches!(event, $converter)
}
)*
_ => true,
};
if !m {
tracing::warn!("Ignoring \"{name}\": not the expected type: {event:?}");
}
m
}
};
}
dioxus_html::with_html_event_groups!(expand_web_event_changes);
/// A extension trait for web-sys events that provides a way to get the event as a web-sys event.
pub trait WebEventExt {
/// The web specific event type
@@ -262,38 +225,3 @@ pub(crate) fn load_document() -> Document {
.document()
.expect("should have access to the Document")
}
macro_rules! uncheck_convert {
($t:ty) => {
impl From<Event> for Synthetic<$t> {
#[inline]
fn from(e: Event) -> Self {
let e: $t = e.unchecked_into();
Self::new(e)
}
}
impl From<&Event> for Synthetic<$t> {
#[inline]
fn from(e: &Event) -> Self {
let e: &$t = e.unchecked_ref();
Self::new(e.clone())
}
}
};
($($t:ty),+ $(,)?) => {
$(uncheck_convert!($t);)+
};
}
uncheck_convert![
web_sys::CompositionEvent,
web_sys::KeyboardEvent,
web_sys::TouchEvent,
web_sys::PointerEvent,
web_sys::WheelEvent,
web_sys::AnimationEvent,
web_sys::TransitionEvent,
web_sys::MouseEvent,
web_sys::FocusEvent,
];