1
0
Jonathan Kelley 1 жил өмнө
parent
commit
44c9697021

+ 1 - 1
packages/html/Cargo.toml

@@ -24,7 +24,7 @@ serde-value = "0.7.0"
 tokio = { workspace = true, features = ["fs", "io-util"], optional = true }
 rfd = { version = "0.12", optional = true }
 async-channel = "1.8.0"
-serde_json = { version = "1" }
+serde_json = { version = "1", optional = true }
 
 [dependencies.web-sys]
 optional = true

+ 13 - 51
packages/html/src/events/form.rs

@@ -1,4 +1,4 @@
-use std::{any::Any, collections::HashMap, fmt::Debug};
+use std::{any::Any, collections::HashMap, fmt::Debug, sync::Arc};
 
 use dioxus_core::Event;
 use serde::{Deserialize, Serialize};
@@ -14,20 +14,13 @@ pub enum FormValue {
 }
 
 /* DOMEvent:  Send + SyncTarget relatedTarget */
-#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
-#[derive(Clone)]
 pub struct FormData {
     inner: Box<dyn HasFormData>,
-    pub values: HashMap<String, FormValue>,
 }
 
 impl<E: HasFormData> From<E> for FormData {
     fn from(e: E) -> Self {
-        // todo: fix this?
-        Self {
-            inner: Box::new(e),
-            values: Default::default(),
-        }
+        Self { inner: Box::new(e) }
     }
 }
 
@@ -51,7 +44,6 @@ impl FormData {
     pub fn new(event: impl HasFormData + 'static) -> Self {
         Self {
             inner: Box::new(event),
-            values: Default::default(),
         }
     }
 
@@ -61,7 +53,7 @@ impl FormData {
     }
 
     /// Get the values of the form event
-    pub fn values(&self) -> HashMap<String, Vec<String>> {
+    pub fn values(&self) -> HashMap<String, FormValue> {
         self.inner.values()
     }
 
@@ -82,7 +74,7 @@ pub trait HasFormData: std::any::Any {
         Default::default()
     }
 
-    fn values(&self) -> HashMap<String, Vec<String>> {
+    fn values(&self) -> HashMap<String, FormValue> {
         Default::default()
     }
 
@@ -109,7 +101,7 @@ impl FormData {
         T: serde::de::DeserializeOwned,
     {
         let parsed_json =
-            convert_hashmap_to_json(&self.values).expect("Failed to parse values to JSON");
+            convert_hashmap_to_json(&self.values()).expect("Failed to parse values to JSON");
 
         serde_json::from_str(&parsed_json)
     }
@@ -120,8 +112,8 @@ impl FormData {
 #[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Clone)]
 pub struct SerializedFormData {
     value: String,
-    values: HashMap<String, Vec<String>>,
-    files: Option<std::sync::Arc<SerializedFileEngine>>,
+    values: HashMap<String, FormValue>,
+    files: Option<SerializedFileEngine>,
 }
 
 #[cfg(feature = "serialize")]
@@ -129,8 +121,8 @@ impl SerializedFormData {
     /// Create a new serialized form data object
     pub fn new(
         value: String,
-        values: HashMap<String, Vec<String>>,
-        files: Option<std::sync::Arc<SerializedFileEngine>>,
+        values: HashMap<String, FormValue>,
+        files: Option<SerializedFileEngine>,
     ) -> Self {
         Self {
             value,
@@ -153,9 +145,9 @@ impl SerializedFormData {
                         resolved_files.insert(file, bytes.unwrap_or_default());
                     }
 
-                    Some(std::sync::Arc::new(SerializedFileEngine {
+                    Some(SerializedFileEngine {
                         files: resolved_files,
-                    }))
+                    })
                 }
                 None => None,
             },
@@ -177,14 +169,14 @@ impl HasFormData for SerializedFormData {
         self.value.clone()
     }
 
-    fn values(&self) -> HashMap<String, Vec<String>> {
+    fn values(&self) -> HashMap<String, FormValue> {
         self.values.clone()
     }
 
     fn files(&self) -> Option<std::sync::Arc<dyn FileEngine>> {
         self.files
             .as_ref()
-            .map(|files| std::sync::Arc::clone(files) as std::sync::Arc<dyn FileEngine>)
+            .map(|files| Arc::new(files.clone()) as _)
     }
 
     fn as_any(&self) -> &dyn std::any::Any {
@@ -240,36 +232,6 @@ impl FileEngine for SerializedFileEngine {
     }
 }
 
-#[cfg(feature = "serialize")]
-fn deserialize_file_engine<'de, D>(
-    deserializer: D,
-) -> Result<Option<std::sync::Arc<dyn FileEngine>>, D::Error>
-where
-    D: serde::Deserializer<'de>,
-{
-    let Ok(file_engine) = SerializedFileEngine::deserialize(deserializer) else {
-        return Ok(None);
-    };
-
-    let file_engine = std::sync::Arc::new(file_engine);
-    Ok(Some(file_engine))
-}
-
-impl PartialEq for FormData {
-    fn eq(&self, other: &Self) -> bool {
-        self.value == other.value && self.values == other.values
-    }
-}
-
-impl Debug for FormData {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        f.debug_struct("FormEvent")
-            .field("value", &self.value)
-            .field("values", &self.values)
-            .finish()
-    }
-}
-
 #[async_trait::async_trait(?Send)]
 pub trait FileEngine {
     // get a list of file names

+ 2 - 2
packages/rink/src/hooks.rs

@@ -18,8 +18,8 @@ use dioxus_html::input_data::keyboard_types::{Code, Key, Location, Modifiers};
 use dioxus_html::input_data::{
     MouseButton as DioxusMouseButton, MouseButtonSet as DioxusMouseButtons,
 };
+use dioxus_html::FormValue;
 use dioxus_html::{event_bubbles, prelude::*};
-use dioxus_html::{FocusData, FormValue, KeyboardData, MouseData, WheelData};
 use std::any::Any;
 use std::collections::HashMap;
 use std::{
@@ -77,7 +77,7 @@ impl HasFormData for FormData {
         self.value.clone()
     }
 
-    fn values(&self) -> HashMap<String, Vec<String>> {
+    fn values(&self) -> HashMap<String, FormValue> {
         self.values.clone()
     }
 

+ 2 - 185
packages/web/src/dom.rs

@@ -9,15 +9,11 @@
 use dioxus_core::{
     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_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 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 web_sys::{Document, Element, Event};
 
@@ -264,185 +260,6 @@ impl WebsysDom {
 
         #[cfg(feature = "mounted")]
         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);
         }
     }

+ 19 - 7
packages/web/src/event.rs

@@ -1,11 +1,11 @@
 use std::{any::Any, collections::HashMap};
 
 use dioxus_html::{
-    FileEngine, FormData, HasFormData, HasImageData, HtmlEventConverter, ImageData, MountedData,
-    PlatformEventData, ScrollData,
+    prelude::FormValue, FileEngine, FormData, HasFormData, HasImageData, HtmlEventConverter,
+    ImageData, MountedData, PlatformEventData, ScrollData,
 };
 use js_sys::Array;
-use wasm_bindgen::{prelude::wasm_bindgen, JsCast};
+use wasm_bindgen::{prelude::wasm_bindgen, JsCast, JsValue};
 use web_sys::{Document, Element, Event};
 
 pub(crate) struct WebEventConverter;
@@ -378,7 +378,7 @@ impl HasFormData for WebFormData {
         .expect("only an InputElement or TextAreaElement or an element with contenteditable=true can have an oninput event listener")
     }
 
-    fn values(&self) -> HashMap<String, Vec<String>> {
+    fn values(&self) -> HashMap<String, FormValue> {
         let mut values = std::collections::HashMap::new();
 
         // try to fill in form values
@@ -388,10 +388,12 @@ impl HasFormData for WebFormData {
                 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 =
+                            let item_values: Vec<String> =
                                 item_values.iter().filter_map(|v| v.as_string()).collect();
 
-                            values.insert(name, item_values);
+                            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()));
                         }
                     }
                 }
@@ -432,7 +434,17 @@ impl HasFormData for WebFormData {
         const formData = new FormData(form);
 
         for (let name of formData.keys()) {
-            values.set(name, formData.getAll(name));
+            const fieldType = target.elements[name].type;
+
+            switch (fieldType) {
+                case "select-multiple":
+                    contents.values[name] = formData.getAll(name);
+                    break;
+
+                // add cases for fieldTypes that can hold multiple values here
+                default:
+                    contents.values[name] = formData.get(name);
+                    break;
         }
 
         return values;