123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566 |
- use std::{any::Any, collections::HashMap};
- use dioxus_html::{
- point_interaction::{
- InteractionElementOffset, InteractionLocation, ModifiersInteraction, PointerInteraction,
- },
- DragData, FileEngine, FormData, HasDragData, HasFileData, HasFormData, HasImageData,
- HasMouseData, HtmlEventConverter, ImageData, MountedData, PlatformEventData, ScrollData,
- };
- use js_sys::Array;
- use wasm_bindgen::{prelude::wasm_bindgen, JsCast, JsValue};
- use web_sys::{Document, DragEvent, Element, Event, MouseEvent};
- pub(crate) struct WebEventConverter;
- #[inline(always)]
- fn downcast_event(event: &dioxus_html::PlatformEventData) -> &GenericWebSysEvent {
- event
- .downcast::<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 {
- downcast_event(event).raw.clone().into()
- }
- #[inline(always)]
- fn convert_clipboard_data(
- &self,
- event: &dioxus_html::PlatformEventData,
- ) -> dioxus_html::ClipboardData {
- downcast_event(event).raw.clone().into()
- }
- #[inline(always)]
- fn convert_composition_data(
- &self,
- event: &dioxus_html::PlatformEventData,
- ) -> dioxus_html::CompositionData {
- 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(WebDragData::new(event.raw.clone().unchecked_into()))
- }
- #[inline(always)]
- fn convert_focus_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::FocusData {
- 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 {
- downcast_event(event).raw.clone().into()
- }
- #[inline(always)]
- fn convert_media_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::MediaData {
- downcast_event(event).raw.clone().into()
- }
- #[allow(unused_variables)]
- #[inline(always)]
- fn convert_mounted_data(&self, event: &dioxus_html::PlatformEventData) -> MountedData {
- #[cfg(feature = "mounted")]
- {
- MountedData::from(
- event
- .downcast::<web_sys::Element>()
- .expect("event should be a web_sys::Element"),
- )
- }
- #[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 {
- downcast_event(event).raw.clone().into()
- }
- #[inline(always)]
- fn convert_pointer_data(
- &self,
- event: &dioxus_html::PlatformEventData,
- ) -> dioxus_html::PointerData {
- downcast_event(event).raw.clone().into()
- }
- #[inline(always)]
- fn convert_scroll_data(
- &self,
- event: &dioxus_html::PlatformEventData,
- ) -> dioxus_html::ScrollData {
- ScrollData::from(downcast_event(event).raw.clone())
- }
- #[inline(always)]
- fn convert_selection_data(
- &self,
- event: &dioxus_html::PlatformEventData,
- ) -> dioxus_html::SelectionData {
- downcast_event(event).raw.clone().into()
- }
- #[inline(always)]
- fn convert_toggle_data(
- &self,
- event: &dioxus_html::PlatformEventData,
- ) -> dioxus_html::ToggleData {
- downcast_event(event).raw.clone().into()
- }
- #[inline(always)]
- fn convert_touch_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::TouchData {
- downcast_event(event).raw.clone().into()
- }
- #[inline(always)]
- fn convert_transition_data(
- &self,
- event: &dioxus_html::PlatformEventData,
- ) -> dioxus_html::TransitionData {
- downcast_event(event).raw.clone().into()
- }
- #[inline(always)]
- fn convert_wheel_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::WheelData {
- downcast_event(event).raw.clone().into()
- }
- }
- /// A extension trait for web-sys events that provides a way to get the event as a web-sys event.
- pub trait WebEventExt<E> {
- /// Get the event as a web-sys event.
- fn web_event(&self) -> &E;
- }
- impl WebEventExt<web_sys::AnimationEvent> for dioxus_html::AnimationData {
- fn web_event(&self) -> &web_sys::AnimationEvent {
- self.downcast::<web_sys::AnimationEvent>()
- .expect("event should be a WebAnimationEvent")
- }
- }
- impl WebEventExt<web_sys::Event> for dioxus_html::ClipboardData {
- fn web_event(&self) -> &web_sys::Event {
- self.downcast::<web_sys::Event>()
- .expect("event should be a web_sys::Event")
- }
- }
- impl WebEventExt<web_sys::CompositionEvent> for dioxus_html::CompositionData {
- fn web_event(&self) -> &web_sys::CompositionEvent {
- self.downcast::<web_sys::CompositionEvent>()
- .expect("event should be a WebCompositionEvent")
- }
- }
- impl WebEventExt<web_sys::MouseEvent> for dioxus_html::DragData {
- fn web_event(&self) -> &web_sys::MouseEvent {
- &self
- .downcast::<WebDragData>()
- .expect("event should be a WebMouseEvent")
- .raw
- }
- }
- impl WebEventExt<web_sys::FocusEvent> for dioxus_html::FocusData {
- fn web_event(&self) -> &web_sys::FocusEvent {
- self.downcast::<web_sys::FocusEvent>()
- .expect("event should be a WebFocusEvent")
- }
- }
- impl WebEventExt<web_sys::Event> for dioxus_html::FormData {
- fn web_event(&self) -> &web_sys::Event {
- self.downcast::<web_sys::Event>()
- .expect("event should be a WebFormData")
- }
- }
- impl WebEventExt<WebImageEvent> for dioxus_html::ImageData {
- fn web_event(&self) -> &WebImageEvent {
- self.downcast::<WebImageEvent>()
- .expect("event should be a WebImageEvent")
- }
- }
- impl WebEventExt<web_sys::KeyboardEvent> for dioxus_html::KeyboardData {
- fn web_event(&self) -> &web_sys::KeyboardEvent {
- self.downcast::<web_sys::KeyboardEvent>()
- .expect("event should be a WebKeyboardEvent")
- }
- }
- impl WebEventExt<web_sys::Event> for dioxus_html::MediaData {
- fn web_event(&self) -> &web_sys::Event {
- self.downcast::<web_sys::Event>()
- .expect("event should be a WebMediaEvent")
- }
- }
- impl WebEventExt<web_sys::Element> for MountedData {
- fn web_event(&self) -> &web_sys::Element {
- self.downcast::<web_sys::Element>()
- .expect("event should be a web_sys::Element")
- }
- }
- impl WebEventExt<web_sys::MouseEvent> for dioxus_html::MouseData {
- fn web_event(&self) -> &web_sys::MouseEvent {
- self.downcast::<web_sys::MouseEvent>()
- .expect("event should be a WebMouseEvent")
- }
- }
- impl WebEventExt<web_sys::PointerEvent> for dioxus_html::PointerData {
- fn web_event(&self) -> &web_sys::PointerEvent {
- self.downcast::<web_sys::PointerEvent>()
- .expect("event should be a WebPointerEvent")
- }
- }
- impl WebEventExt<web_sys::Event> for ScrollData {
- fn web_event(&self) -> &web_sys::Event {
- self.downcast::<web_sys::Event>()
- .expect("event should be a WebScrollEvent")
- }
- }
- impl WebEventExt<web_sys::Event> for dioxus_html::SelectionData {
- fn web_event(&self) -> &web_sys::Event {
- self.downcast::<web_sys::Event>()
- .expect("event should be a WebSelectionEvent")
- }
- }
- impl WebEventExt<web_sys::Event> for dioxus_html::ToggleData {
- fn web_event(&self) -> &web_sys::Event {
- self.downcast::<web_sys::Event>()
- .expect("event should be a WebToggleEvent")
- }
- }
- impl WebEventExt<web_sys::TouchEvent> for dioxus_html::TouchData {
- fn web_event(&self) -> &web_sys::TouchEvent {
- self.downcast::<web_sys::TouchEvent>()
- .expect("event should be a WebTouchEvent")
- }
- }
- impl WebEventExt<web_sys::TransitionEvent> for dioxus_html::TransitionData {
- fn web_event(&self) -> &web_sys::TransitionEvent {
- self.downcast::<web_sys::TransitionEvent>()
- .expect("event should be a WebTransitionEvent")
- }
- }
- impl WebEventExt<web_sys::WheelEvent> for dioxus_html::WheelData {
- fn web_event(&self) -> &web_sys::WheelEvent {
- self.downcast::<web_sys::WheelEvent>()
- .expect("event should be a WebWheelEvent")
- }
- }
- struct GenericWebSysEvent {
- raw: Event,
- element: Element,
- }
- // todo: some of these events are being casted to the wrong event type.
- // We need tests that simulate clicks/etc and make sure every event type works.
- pub(crate) fn virtual_event_from_websys_event(
- event: web_sys::Event,
- target: Element,
- ) -> PlatformEventData {
- PlatformEventData::new(Box::new(GenericWebSysEvent {
- raw: event,
- element: target,
- }))
- }
- pub(crate) fn load_document() -> Document {
- web_sys::window()
- .expect("should have access to the Window")
- .document()
- .expect("should have access to the Document")
- }
- struct WebImageEvent {
- raw: Event,
- error: bool,
- }
- impl WebImageEvent {
- fn new(raw: Event, error: bool) -> Self {
- Self { raw, error }
- }
- }
- impl HasImageData for WebImageEvent {
- fn load_error(&self) -> bool {
- self.error
- }
- fn as_any(&self) -> &dyn Any {
- &self.raw as &dyn Any
- }
- }
- struct WebFormData {
- element: Element,
- raw: Event,
- }
- impl WebFormData {
- fn new(element: Element, raw: Event) -> Self {
- Self { element, raw }
- }
- }
- impl HasFormData for WebFormData {
- fn value(&self) -> String {
- let target = &self.element;
- target
- .dyn_ref()
- .map(|input: &web_sys::HtmlInputElement| {
- // todo: special case more input types
- match input.type_().as_str() {
- "checkbox" => {
- match input.checked() {
- true => "true".to_string(),
- false => "false".to_string(),
- }
- },
- _ => {
- input.value()
- }
- }
- })
- .or_else(|| {
- target
- .dyn_ref()
- .map(|input: &web_sys::HtmlTextAreaElement| input.value())
- })
- // select elements are NOT input events - because - why woudn't they be??
- .or_else(|| {
- target
- .dyn_ref()
- .map(|input: &web_sys::HtmlSelectElement| input.value())
- })
- .or_else(|| {
- target
- .dyn_ref::<web_sys::HtmlElement>()
- .unwrap()
- .text_content()
- })
- .expect("only an InputElement or TextAreaElement or an element with contenteditable=true can have an oninput event listener")
- }
- fn values(&self) -> HashMap<String, String> {
- let mut values = HashMap::new();
- fn insert_value(map: &mut HashMap<String, String>, key: String, new_value: String) {
- if let Some(value) = map.get(&key) {
- map.insert(key, format!("{},{}", value, new_value));
- } else {
- map.insert(key, new_value);
- }
- }
- // try to fill in form values
- if let Some(form) = self.element.dyn_ref::<web_sys::HtmlFormElement>() {
- let form_data = get_form_data(form);
- for value in form_data.entries().into_iter().flatten() {
- if let Ok(array) = value.dyn_into::<Array>() {
- if let Some(name) = array.get(0).as_string() {
- if let Ok(item_values) = array.get(1).dyn_into::<Array>() {
- item_values
- .iter()
- .filter_map(|v| v.as_string())
- .for_each(|v| insert_value(&mut values, name.clone(), v));
- } else if let Ok(item_value) = array.get(1).dyn_into::<JsValue>() {
- insert_value(&mut values, name, item_value.as_string().unwrap());
- }
- }
- }
- }
- } else if let Some(select) = self.element.dyn_ref::<web_sys::HtmlSelectElement>() {
- // try to fill in select element values
- let options = get_select_data(select).join(",");
- values.insert("options".to_string(), options);
- }
- values
- }
- fn as_any(&self) -> &dyn Any {
- &self.raw as &dyn Any
- }
- }
- impl HasFileData for WebFormData {
- fn files(&self) -> Option<std::sync::Arc<dyn FileEngine>> {
- #[cfg(not(feature = "file_engine"))]
- let files = None;
- #[cfg(feature = "file_engine")]
- let files = self
- .element
- .dyn_ref()
- .and_then(|input: &web_sys::HtmlInputElement| {
- input.files().and_then(|files| {
- #[allow(clippy::arc_with_non_send_sync)]
- crate::file_engine::WebFileEngine::new(files).map(|f| {
- std::sync::Arc::new(f) as std::sync::Arc<dyn dioxus_html::FileEngine>
- })
- })
- });
- files
- }
- }
- struct WebDragData {
- raw: MouseEvent,
- }
- impl WebDragData {
- fn new(raw: MouseEvent) -> Self {
- Self { raw }
- }
- }
- impl HasDragData for WebDragData {
- fn as_any(&self) -> &dyn std::any::Any {
- &self.raw as &dyn std::any::Any
- }
- }
- impl HasMouseData for WebDragData {
- fn as_any(&self) -> &dyn std::any::Any {
- &self.raw as &dyn std::any::Any
- }
- }
- impl PointerInteraction for WebDragData {
- fn trigger_button(&self) -> Option<dioxus_html::input_data::MouseButton> {
- self.raw.trigger_button()
- }
- fn held_buttons(&self) -> dioxus_html::input_data::MouseButtonSet {
- self.raw.held_buttons()
- }
- }
- impl ModifiersInteraction for WebDragData {
- fn modifiers(&self) -> dioxus_html::prelude::Modifiers {
- self.raw.modifiers()
- }
- }
- impl InteractionElementOffset for WebDragData {
- fn coordinates(&self) -> dioxus_html::geometry::Coordinates {
- self.raw.coordinates()
- }
- fn element_coordinates(&self) -> dioxus_html::geometry::ElementPoint {
- self.raw.element_coordinates()
- }
- }
- impl InteractionLocation for WebDragData {
- fn client_coordinates(&self) -> dioxus_html::geometry::ClientPoint {
- self.raw.client_coordinates()
- }
- fn screen_coordinates(&self) -> dioxus_html::geometry::ScreenPoint {
- self.raw.screen_coordinates()
- }
- fn page_coordinates(&self) -> dioxus_html::geometry::PagePoint {
- self.raw.page_coordinates()
- }
- }
- impl HasFileData for WebDragData {
- fn files(&self) -> Option<std::sync::Arc<dyn FileEngine>> {
- #[cfg(not(feature = "file_engine"))]
- let files = None;
- #[cfg(feature = "file_engine")]
- let files = self.raw.dyn_ref::<DragEvent>().and_then(|drag_event| {
- drag_event.data_transfer().and_then(|dt| {
- dt.files().and_then(|files| {
- #[allow(clippy::arc_with_non_send_sync)]
- crate::file_engine::WebFileEngine::new(files).map(|f| {
- std::sync::Arc::new(f) as std::sync::Arc<dyn dioxus_html::FileEngine>
- })
- })
- })
- });
- files
- }
- }
- // web-sys does not expose the keys api for form data, so we need to manually bind to it
- #[wasm_bindgen(inline_js = r#"
- export function get_form_data(form) {
- let values = new Map();
- const formData = new FormData(form);
- for (let name of formData.keys()) {
- values.set(name, formData.getAll(name));
- }
- return values;
- }
- "#)]
- extern "C" {
- fn get_form_data(form: &web_sys::HtmlFormElement) -> js_sys::Map;
- }
- // web-sys does not expose the keys api for select data, so we need to manually bind to it
- #[wasm_bindgen(inline_js = r#"
- export function get_select_data(select) {
- let values = [];
- for (let i = 0; i < select.options.length; i++) {
- let option = select.options[i];
- if (option.selected) {
- values.push(option.value.toString());
- }
- }
- return values;
- }
- "#)]
- extern "C" {
- fn get_select_data(select: &web_sys::HtmlSelectElement) -> Vec<String>;
- }
|