|
@@ -9,15 +9,11 @@
|
|
use dioxus_core::{
|
|
use dioxus_core::{
|
|
BorrowedAttributeValue, ElementId, Mutation, Template, TemplateAttribute, TemplateNode,
|
|
BorrowedAttributeValue, ElementId, Mutation, Template, TemplateAttribute, TemplateNode,
|
|
};
|
|
};
|
|
-use dioxus_html::{event_bubbles, CompositionData, FormData, FormValue, MountedData};
|
|
|
|
|
|
+use dioxus_html::PlatformEventData;
|
|
|
|
+use dioxus_html::{event_bubbles, MountedData};
|
|
use dioxus_interpreter_js::{get_node, minimal_bindings, save_template, Channel};
|
|
use dioxus_interpreter_js::{get_node, minimal_bindings, save_template, Channel};
|
|
-use dioxus_html::{event_bubbles, MountedData, PlatformEventData};
|
|
|
|
-use dioxus_interpreter_js::get_node;
|
|
|
|
-use dioxus_interpreter_js::{minimal_bindings, save_template, Channel};
|
|
|
|
use futures_channel::mpsc;
|
|
use futures_channel::mpsc;
|
|
use rustc_hash::FxHashMap;
|
|
use rustc_hash::FxHashMap;
|
|
-use std::{any::Any, collections::HashMap, rc::Rc};
|
|
|
|
-use wasm_bindgen::{closure::Closure, prelude::wasm_bindgen, JsCast, JsValue};
|
|
|
|
use wasm_bindgen::{closure::Closure, JsCast, JsValue};
|
|
use wasm_bindgen::{closure::Closure, JsCast, JsValue};
|
|
use web_sys::{Document, Element, Event};
|
|
use web_sys::{Document, Element, Event};
|
|
|
|
|
|
@@ -264,185 +260,6 @@ impl WebsysDom {
|
|
|
|
|
|
#[cfg(feature = "mounted")]
|
|
#[cfg(feature = "mounted")]
|
|
for id in to_mount {
|
|
for id in to_mount {
|
|
- let node = get_node(id.0 as u32);
|
|
|
|
- if let Some(element) = node.dyn_ref::<Element>() {
|
|
|
|
- let data: MountedData = element.into();
|
|
|
|
- let data = Rc::new(data);
|
|
|
|
- let _ = self.event_channel.unbounded_send(UiEvent {
|
|
|
|
- name: "mounted".to_string(),
|
|
|
|
- bubbles: false,
|
|
|
|
- element: id,
|
|
|
|
- data,
|
|
|
|
- });
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-// 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 fn virtual_event_from_websys_event(event: web_sys::Event, target: Element) -> Rc<dyn Any> {
|
|
|
|
- use dioxus_html::events::*;
|
|
|
|
-
|
|
|
|
- match event.type_().as_str() {
|
|
|
|
- "copy" | "cut" | "paste" => Rc::new(ClipboardData {}),
|
|
|
|
- "compositionend" | "compositionstart" | "compositionupdate" => {
|
|
|
|
- make_composition_event(&event)
|
|
|
|
- }
|
|
|
|
- "keydown" | "keypress" | "keyup" => Rc::new(KeyboardData::from(event)),
|
|
|
|
- "focus" | "blur" | "focusout" | "focusin" => Rc::new(FocusData {}),
|
|
|
|
-
|
|
|
|
- "change" | "input" | "invalid" | "reset" | "submit" => read_input_to_data(target),
|
|
|
|
-
|
|
|
|
- "click" | "contextmenu" | "dblclick" | "doubleclick" | "mousedown" | "mouseenter"
|
|
|
|
- | "mouseleave" | "mousemove" | "mouseout" | "mouseover" | "mouseup" => {
|
|
|
|
- Rc::new(MouseData::from(event))
|
|
|
|
- }
|
|
|
|
- "drag" | "dragend" | "dragenter" | "dragexit" | "dragleave" | "dragover" | "dragstart"
|
|
|
|
- | "drop" => {
|
|
|
|
- let mouse = MouseData::from(event);
|
|
|
|
- Rc::new(DragData { mouse })
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- "pointerdown" | "pointermove" | "pointerup" | "pointercancel" | "gotpointercapture"
|
|
|
|
- | "lostpointercapture" | "pointerenter" | "pointerleave" | "pointerover" | "pointerout" => {
|
|
|
|
- Rc::new(PointerData::from(event))
|
|
|
|
- }
|
|
|
|
- "select" => Rc::new(SelectionData {}),
|
|
|
|
- "touchcancel" | "touchend" | "touchmove" | "touchstart" => Rc::new(TouchData::from(event)),
|
|
|
|
-
|
|
|
|
- "scroll" => Rc::new(ScrollData {}),
|
|
|
|
- "wheel" => Rc::new(WheelData::from(event)),
|
|
|
|
- "animationstart" | "animationend" | "animationiteration" => {
|
|
|
|
- Rc::new(AnimationData::from(event))
|
|
|
|
- }
|
|
|
|
- "transitionend" => Rc::new(TransitionData::from(event)),
|
|
|
|
- "abort" | "canplay" | "canplaythrough" | "durationchange" | "emptied" | "encrypted"
|
|
|
|
- | "ended" | "loadeddata" | "loadedmetadata" | "loadstart" | "pause" | "play"
|
|
|
|
- | "playing" | "progress" | "ratechange" | "seeked" | "seeking" | "stalled" | "suspend"
|
|
|
|
- | "timeupdate" | "volumechange" | "waiting" => Rc::new(MediaData {}),
|
|
|
|
- "error" => Rc::new(ImageData { load_error: true }),
|
|
|
|
- "load" => Rc::new(ImageData { load_error: false }),
|
|
|
|
- "toggle" => Rc::new(ToggleData {}),
|
|
|
|
-
|
|
|
|
- _ => Rc::new(()),
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-fn make_composition_event(event: &Event) -> Rc<CompositionData> {
|
|
|
|
- let evt: &web_sys::CompositionEvent = event.dyn_ref().unwrap();
|
|
|
|
- Rc::new(CompositionData {
|
|
|
|
- data: evt.data().unwrap_or_default(),
|
|
|
|
- })
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-pub(crate) fn load_document() -> Document {
|
|
|
|
- web_sys::window()
|
|
|
|
- .expect("should have access to the Window")
|
|
|
|
- .document()
|
|
|
|
- .expect("should have access to the Document")
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-fn read_input_to_data(target: Element) -> Rc<FormData> {
|
|
|
|
- // todo: these handlers might get really slow if the input box gets large and allocation pressure is heavy
|
|
|
|
- // don't have a good solution with the serialized event problem
|
|
|
|
-
|
|
|
|
- let value: String = 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");
|
|
|
|
-
|
|
|
|
- let mut values = HashMap::new();
|
|
|
|
-
|
|
|
|
- // try to fill in form values
|
|
|
|
- if let Some(form) = target.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>() {
|
|
|
|
- let item_values: Vec<String> =
|
|
|
|
- item_values.iter().filter_map(|v| v.as_string()).collect();
|
|
|
|
-
|
|
|
|
- values.insert(name, FormValue::VecText(item_values));
|
|
|
|
- } else if let Ok(item_value) = array.get(1).dyn_into::<JsValue>() {
|
|
|
|
- values.insert(name, FormValue::Text(item_value.as_string().unwrap()));
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- #[cfg(not(feature = "file_engine"))]
|
|
|
|
- let files = None;
|
|
|
|
- #[cfg(feature = "file_engine")]
|
|
|
|
- let files = target
|
|
|
|
- .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>)
|
|
|
|
- })
|
|
|
|
- });
|
|
|
|
-
|
|
|
|
- Rc::new(FormData {
|
|
|
|
- value,
|
|
|
|
- values,
|
|
|
|
- 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()) {
|
|
|
|
- const fieldType = form.elements[name].type;
|
|
|
|
-
|
|
|
|
- switch (fieldType) {
|
|
|
|
- case "select-multiple":
|
|
|
|
- values.set(name, formData.getAll(name));
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- // add cases for fieldTypes that can hold multiple values here
|
|
|
|
- default:
|
|
|
|
- values.set(name, formData.get(name));
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
self.send_mount_event(id);
|
|
self.send_mount_event(id);
|
|
}
|
|
}
|
|
}
|
|
}
|