mirror of
https://github.com/DioxusLabs/dioxus
synced 2024-11-26 22:20:19 +00:00
Fix merge conflicts
This commit is contained in:
parent
7f6e2dd64b
commit
44c9697021
5 changed files with 37 additions and 246 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue