diff --git a/examples/form.rs b/examples/form.rs index 9f6215226..28afa10c0 100644 --- a/examples/form.rs +++ b/examples/form.rs @@ -60,8 +60,9 @@ fn app() -> Element { label { r#for: "full-name", "Full Name" } input { r#type: "text", name: "full-name" } + input { r#type: "text", name: "full-name" } - label { r#for: "email", "Email" } + label { r#for: "email", "Email (matching @example.com)" } input { r#type: "email", pattern: ".+@example\\.com", size: "30", required: "true", id: "email", name: "email" } label { r#for: "password", "Password" } @@ -102,6 +103,26 @@ fn app() -> Element { id: "start" } + // CHekcboxes + label { r#for: "cbox", "Color" } + div { + label { r#for: "cbox-red", "red" } + input { r#type: "checkbox", checked: true, name: "cbox", value: "red", id: "cbox-red" } + } + div { + label { r#for: "cbox-blue", "blue" } + input { r#type: "checkbox", name: "cbox", value: "blue", id: "cbox-blue" } + } + div { + label { r#for: "cbox-green", "green" } + input { r#type: "checkbox", name: "cbox", value: "green", id: "cbox-green" } + } + div { + label { r#for: "cbox-yellow", "yellow" } + input { r#type: "checkbox", name: "cbox", value: "yellow", id: "cbox-yellow" } + } + + // Buttons will submit your form by default. button { r#type: "submit", value: "Submit", "Submit the form" } } diff --git a/packages/core/src/scopes.rs b/packages/core/src/scopes.rs index 03c438978..f3fe758ea 100644 --- a/packages/core/src/scopes.rs +++ b/packages/core/src/scopes.rs @@ -13,6 +13,7 @@ use std::{cell::Ref, rc::Rc}; pub struct ScopeId(pub usize); impl std::fmt::Debug for ScopeId { + #[allow(unused_mut)] fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let mut builder = f.debug_tuple("ScopeId"); let mut builder = builder.field(&self.0); diff --git a/packages/desktop/headless_tests/events.rs b/packages/desktop/headless_tests/events.rs index c981144d5..c2bdef52e 100644 --- a/packages/desktop/headless_tests/events.rs +++ b/packages/desktop/headless_tests/events.rs @@ -1,4 +1,4 @@ -use std::{collections::HashMap, ops::Deref}; +use std::collections::HashMap; use dioxus::html::geometry::euclid::Vector3D; use dioxus::prelude::*; @@ -453,10 +453,10 @@ fn test_form_input() -> Element { // And then the value the form gives us should also match values.with_mut(|x| { - assert_eq!(x.get("username").unwrap().deref(), "hello"); - assert_eq!(x.get("full-name").unwrap().deref(), "lorem"); - assert_eq!(x.get("password").unwrap().deref(), "ipsum"); - assert_eq!(x.get("color").unwrap().deref(), "red"); + assert_eq!(x.get("username").unwrap(), "hello"); + assert_eq!(x.get("full-name").unwrap(), "lorem"); + assert_eq!(x.get("password").unwrap(), "ipsum"); + assert_eq!(x.get("color").unwrap(), "red"); }); RECEIVED_EVENTS.with_mut(|x| *x += 1); }; @@ -500,10 +500,10 @@ fn test_form_submit() -> Element { let set_values = move |ev: FormEvent| { values.set(ev.values()); values.with_mut(|x| { - assert_eq!(x.get("username").unwrap().deref(), "goodbye"); - assert_eq!(x.get("full-name").unwrap().deref(), "lorem"); - assert_eq!(x.get("password").unwrap().deref(), "ipsum"); - assert_eq!(x.get("color").unwrap().deref(), "red"); + assert_eq!(x.get("username").unwrap(), "goodbye"); + assert_eq!(x.get("full-name").unwrap(), "lorem"); + assert_eq!(x.get("password").unwrap(), "ipsum"); + assert_eq!(x.get("color").unwrap(), "red"); }); RECEIVED_EVENTS.with_mut(|x| *x += 1); }; diff --git a/packages/html/src/events/form.rs b/packages/html/src/events/form.rs index bd8c45fec..65476c664 100644 --- a/packages/html/src/events/form.rs +++ b/packages/html/src/events/form.rs @@ -1,11 +1,47 @@ use crate::file_data::FileEngine; use crate::file_data::HasFileData; -use std::{collections::HashMap, fmt::Debug}; +use std::{collections::HashMap, fmt::Debug, ops::Deref}; use dioxus_core::Event; pub type FormEvent = Event; +/// A form value that may either be a list of values or a single value +#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] +#[derive(Debug, Default, Clone, PartialEq)] +pub struct FormValue(pub Vec); + +impl Deref for FormValue { + type Target = [String]; + + fn deref(&self) -> &Self::Target { + self.as_slice() + } +} + +impl FormValue { + /// Convenient way to represent Value as slice + pub fn as_slice(&self) -> &[String] { + &self.0 + } + + /// Return the first value, panicking if there are none + pub fn as_value(&self) -> String { + self.0.first().unwrap().clone() + } + + /// Convert into Vec + pub fn to_vec(self) -> Vec { + self.0.clone() + } +} + +impl PartialEq for FormValue { + fn eq(&self, other: &str) -> bool { + self.0.len() == 1 && self.0.first().map(|s| s.as_str()) == Some(other) + } +} + /* DOMEvent: Send + SyncTarget relatedTarget */ pub struct FormData { inner: Box, @@ -65,7 +101,7 @@ impl FormData { /// Collect all the named form values from the containing form. /// /// Every input must be named! - pub fn values(&self) -> HashMap { + pub fn values(&self) -> HashMap { self.inner.values() } @@ -95,7 +131,7 @@ pub trait HasFormData: HasFileData + std::any::Any { true } - fn values(&self) -> HashMap { + fn values(&self) -> HashMap { Default::default() } @@ -135,7 +171,7 @@ pub struct SerializedFormData { value: String, #[serde(default)] - values: HashMap, + values: HashMap, #[serde(default)] valid: bool, @@ -149,7 +185,7 @@ impl SerializedFormData { /// Create a new serialized form data object pub fn new( value: String, - values: HashMap, + values: HashMap, files: Option, ) -> Self { Self { @@ -200,7 +236,7 @@ impl HasFormData for SerializedFormData { self.value.clone() } - fn values(&self) -> HashMap { + fn values(&self) -> HashMap { self.values.clone() } diff --git a/packages/interpreter/build.rs b/packages/interpreter/build.rs index f5d69db40..aee7cb526 100644 --- a/packages/interpreter/build.rs +++ b/packages/interpreter/build.rs @@ -2,7 +2,11 @@ use std::process::Command; fn main() { // If any TS changes, re-run the build script - println!("cargo:rerun-if-changed=src/ts/*.ts"); + println!("cargo:rerun-if-changed=src/ts/form.ts"); + println!("cargo:rerun-if-changed=src/ts/core.ts"); + println!("cargo:rerun-if-changed=src/ts/serialize.ts"); + println!("cargo:rerun-if-changed=src/ts/set_attribute.ts"); + println!("cargo:rerun-if-changed=src/ts/common.ts"); // Compute the hash of the ts files let hash = hash_ts_files(); diff --git a/packages/interpreter/src/js/common.js b/packages/interpreter/src/js/common.js index 9e4964594..9b3af6cc8 100644 --- a/packages/interpreter/src/js/common.js +++ b/packages/interpreter/src/js/common.js @@ -1 +1 @@ -function setAttributeInner(node,field,value,ns){if(ns==="style"){node.style.setProperty(field,value);return}if(ns){node.setAttributeNS(ns,field,value);return}switch(field){case"value":if(node.value!==value)node.value=value;break;case"initial_value":node.defaultValue=value;break;case"checked":node.checked=truthy(value);break;case"initial_checked":node.defaultChecked=truthy(value);break;case"selected":node.selected=truthy(value);break;case"initial_selected":node.defaultSelected=truthy(value);break;case"dangerous_inner_html":node.innerHTML=value;break;default:if(!truthy(value)&&isBoolAttr(field))node.removeAttribute(field);else node.setAttribute(field,value)}}var truthy=function(val){return val==="true"||val===!0},isBoolAttr=function(field){switch(field){case"allowfullscreen":case"allowpaymentrequest":case"async":case"autofocus":case"autoplay":case"checked":case"controls":case"default":case"defer":case"disabled":case"formnovalidate":case"hidden":case"ismap":case"itemscope":case"loop":case"multiple":case"muted":case"nomodule":case"novalidate":case"open":case"playsinline":case"readonly":case"required":case"reversed":case"selected":case"truespeed":case"webkitdirectory":return!0;default:return!1}};function retrieveFormValues(form){const formData=new FormData(form),contents={};return formData.forEach((value,key)=>{if(contents[key])contents[key]+=","+value;else contents[key]=value}),{valid:form.checkValidity(),values:contents}}export{setAttributeInner,retrieveFormValues}; +function setAttributeInner(node,field,value,ns){if(ns==="style"){node.style.setProperty(field,value);return}if(ns){node.setAttributeNS(ns,field,value);return}switch(field){case"value":if(node.value!==value)node.value=value;break;case"initial_value":node.defaultValue=value;break;case"checked":node.checked=truthy(value);break;case"initial_checked":node.defaultChecked=truthy(value);break;case"selected":node.selected=truthy(value);break;case"initial_selected":node.defaultSelected=truthy(value);break;case"dangerous_inner_html":node.innerHTML=value;break;default:if(!truthy(value)&&isBoolAttr(field))node.removeAttribute(field);else node.setAttribute(field,value)}}var truthy=function(val){return val==="true"||val===!0},isBoolAttr=function(field){switch(field){case"allowfullscreen":case"allowpaymentrequest":case"async":case"autofocus":case"autoplay":case"checked":case"controls":case"default":case"defer":case"disabled":case"formnovalidate":case"hidden":case"ismap":case"itemscope":case"loop":case"multiple":case"muted":case"nomodule":case"novalidate":case"open":case"playsinline":case"readonly":case"required":case"reversed":case"selected":case"truespeed":case"webkitdirectory":return!0;default:return!1}};function retrieveFormValues(form){const formData=new FormData(form),contents={};return formData.forEach((value,key)=>{if(contents[key])contents[key].push(value);else contents[key]=[value]}),{valid:form.checkValidity(),values:contents}}export{setAttributeInner,retrieveFormValues}; diff --git a/packages/interpreter/src/js/native.js b/packages/interpreter/src/js/native.js index 290823ee7..4df91d3b9 100644 --- a/packages/interpreter/src/js/native.js +++ b/packages/interpreter/src/js/native.js @@ -1 +1 @@ -function retriveValues(event,target){let contents={values:{}},form=target.closest("form");if(form){if(event.type==="input"||event.type==="change"||event.type==="submit"||event.type==="reset"||event.type==="click")contents=retrieveFormValues(form)}return contents}function retrieveFormValues(form){const formData=new FormData(form),contents={};return formData.forEach((value,key)=>{if(contents[key])contents[key]+=","+value;else contents[key]=value}),{valid:form.checkValidity(),values:contents}}function retriveSelectValue(target){let options=target.selectedOptions,values=[];for(let i=0;icontents={...contents,...obj};if(event instanceof WheelEvent)extend(serializeWheelEvent(event));if(event instanceof MouseEvent)extend(serializeMouseEvent(event));if(event instanceof KeyboardEvent)extend(serializeKeyboardEvent(event));if(event instanceof InputEvent)extend(serializeInputEvent(event,target));if(event instanceof PointerEvent)extend(serializePointerEvent(event));if(event instanceof AnimationEvent)extend(serializeAnimationEvent(event));if(event instanceof TransitionEvent)extend({property_name:event.propertyName,elapsed_time:event.elapsedTime,pseudo_element:event.pseudoElement});if(event instanceof CompositionEvent)extend({data:event.data});if(event instanceof DragEvent)extend(serializeDragEvent(event));if(event instanceof FocusEvent)extend({});if(event instanceof ClipboardEvent)extend({});if(typeof TouchEvent!=="undefined"&&event instanceof TouchEvent)extend(serializeTouchEvent(event));if(event.type==="submit"||event.type==="reset"||event.type==="click"||event.type==="change"||event.type==="input")extend(serializeInputEvent(event,target));if(event instanceof DragEvent);return contents}var serializeInputEvent=function(event,target){let contents={};if(target instanceof HTMLElement){let values=retriveValues(event,target);contents.values=values.values,contents.valid=values.valid}if(event.target instanceof HTMLInputElement){let target2=event.target,value=target2.value??target2.textContent??"";if(target2.type==="checkbox")value=target2.checked?"true":"false";else if(target2.type==="radio")value=target2.value;contents.value=value}if(event.target instanceof HTMLTextAreaElement)contents.value=event.target.value;if(event.target instanceof HTMLSelectElement)contents.value=retriveSelectValue(event.target).join(",");if(contents.value===void 0)contents.value="";return contents},serializeWheelEvent=function(event){return{delta_x:event.deltaX,delta_y:event.deltaY,delta_z:event.deltaZ,delta_mode:event.deltaMode}},serializeTouchEvent=function(event){return{alt_key:event.altKey,ctrl_key:event.ctrlKey,meta_key:event.metaKey,shift_key:event.shiftKey,changed_touches:event.changedTouches,target_touches:event.targetTouches,touches:event.touches}},serializePointerEvent=function(event){return{alt_key:event.altKey,button:event.button,buttons:event.buttons,client_x:event.clientX,client_y:event.clientY,ctrl_key:event.ctrlKey,meta_key:event.metaKey,page_x:event.pageX,page_y:event.pageY,screen_x:event.screenX,screen_y:event.screenY,shift_key:event.shiftKey,pointer_id:event.pointerId,width:event.width,height:event.height,pressure:event.pressure,tangential_pressure:event.tangentialPressure,tilt_x:event.tiltX,tilt_y:event.tiltY,twist:event.twist,pointer_type:event.pointerType,is_primary:event.isPrimary}},serializeMouseEvent=function(event){return{alt_key:event.altKey,button:event.button,buttons:event.buttons,client_x:event.clientX,client_y:event.clientY,ctrl_key:event.ctrlKey,meta_key:event.metaKey,offset_x:event.offsetX,offset_y:event.offsetY,page_x:event.pageX,page_y:event.pageY,screen_x:event.screenX,screen_y:event.screenY,shift_key:event.shiftKey}},serializeKeyboardEvent=function(event){return{char_code:event.charCode,is_composing:event.isComposing,key:event.key,alt_key:event.altKey,ctrl_key:event.ctrlKey,meta_key:event.metaKey,key_code:event.keyCode,shift_key:event.shiftKey,location:event.location,repeat:event.repeat,which:event.which,code:event.code}},serializeAnimationEvent=function(event){return{animation_name:event.animationName,elapsed_time:event.elapsedTime,pseudo_element:event.pseudoElement}},serializeDragEvent=function(event){return{mouse:{alt_key:event.altKey,ctrl_key:event.ctrlKey,meta_key:event.metaKey,shift_key:event.shiftKey,...serializeMouseEvent(event)},files:{files:{a:[1,2,3]}}}};var getTargetId=function(target){if(!(target instanceof Node))return null;let ourTarget=target,realId=null;while(realId==null){if(ourTarget===null)return null;if(ourTarget instanceof Element)realId=ourTarget.getAttribute("data-dioxus-id");ourTarget=ourTarget.parentNode}return parseInt(realId)},JSChannel_;if(RawInterpreter!==void 0&&RawInterpreter!==null)JSChannel_=RawInterpreter;class NativeInterpreter extends JSChannel_{intercept_link_redirects;ipc;editsPath;liveview;constructor(editsPath){super();this.editsPath=editsPath}initialize(root){this.intercept_link_redirects=!0,this.liveview=!1,window.addEventListener("dragover",function(e){if(e.target instanceof Element&&e.target.tagName!="INPUT")e.preventDefault()},!1),window.addEventListener("drop",function(e){if(!(e.target instanceof Element))return;e.preventDefault()},!1),window.addEventListener("click",(event)=>{const target=event.target;if(target instanceof HTMLInputElement&&target.getAttribute("type")==="file"){let target_id=getTargetId(target);if(target_id!==null){const message=this.serializeIpcMessage("file_dialog",{event:"change&input",accept:target.getAttribute("accept"),directory:target.getAttribute("webkitdirectory")==="true",multiple:target.hasAttribute("multiple"),target:target_id,bubbles:event.bubbles});this.ipc.postMessage(message)}event.preventDefault()}}),this.ipc=window.ipc;const handler=(event)=>this.handleEvent(event,event.type,!0);super.initialize(root,handler)}serializeIpcMessage(method,params={}){return JSON.stringify({method,params})}scrollTo(id,behavior){const node=this.nodes[id];if(node instanceof HTMLElement)node.scrollIntoView({behavior})}getClientRect(id){const node=this.nodes[id];if(node instanceof HTMLElement){const rect=node.getBoundingClientRect();return{type:"GetClientRect",origin:[rect.x,rect.y],size:[rect.width,rect.height]}}}setFocus(id,focus){const node=this.nodes[id];if(node instanceof HTMLElement)if(focus)node.focus();else node.blur()}loadChild(array){let node=this.stack[this.stack.length-1];for(let i=0;i0;end--)node=node.nextSibling}return node}appendChildren(id,many){const root=this.nodes[id],els=this.stack.splice(this.stack.length-many);for(let k=0;kresponse.arrayBuffer()).then((bytes)=>{if(headless)this.run_from_bytes(bytes);else requestAnimationFrame(()=>this.run_from_bytes(bytes));this.waitForRequest(headless)})}async readFiles(target,contents,bubbles,realId,name){let files=target.files,file_contents={};for(let i=0;i{if(contents[key])contents[key].push(value);else contents[key]=[value]}),{valid:form.checkValidity(),values:contents}}function retriveSelectValue(target){let options=target.selectedOptions,values=[];for(let i=0;icontents={...contents,...obj};if(event instanceof WheelEvent)extend(serializeWheelEvent(event));if(event instanceof MouseEvent)extend(serializeMouseEvent(event));if(event instanceof KeyboardEvent)extend(serializeKeyboardEvent(event));if(event instanceof InputEvent)extend(serializeInputEvent(event,target));if(event instanceof PointerEvent)extend(serializePointerEvent(event));if(event instanceof AnimationEvent)extend(serializeAnimationEvent(event));if(event instanceof TransitionEvent)extend({property_name:event.propertyName,elapsed_time:event.elapsedTime,pseudo_element:event.pseudoElement});if(event instanceof CompositionEvent)extend({data:event.data});if(event instanceof DragEvent)extend(serializeDragEvent(event));if(event instanceof FocusEvent)extend({});if(event instanceof ClipboardEvent)extend({});if(typeof TouchEvent!=="undefined"&&event instanceof TouchEvent)extend(serializeTouchEvent(event));if(event.type==="submit"||event.type==="reset"||event.type==="click"||event.type==="change"||event.type==="input")extend(serializeInputEvent(event,target));if(event instanceof DragEvent);return contents}var serializeInputEvent=function(event,target){let contents={};if(target instanceof HTMLElement){let values=retriveValues(event,target);contents.values=values.values,contents.valid=values.valid}if(event.target instanceof HTMLInputElement){let target2=event.target,value=target2.value??target2.textContent??"";if(target2.type==="checkbox")value=target2.checked?"true":"false";else if(target2.type==="radio")value=target2.value;contents.value=value}if(event.target instanceof HTMLTextAreaElement)contents.value=event.target.value;if(event.target instanceof HTMLSelectElement)contents.value=retriveSelectValue(event.target).join(",");if(contents.value===void 0)contents.value="";return contents},serializeWheelEvent=function(event){return{delta_x:event.deltaX,delta_y:event.deltaY,delta_z:event.deltaZ,delta_mode:event.deltaMode}},serializeTouchEvent=function(event){return{alt_key:event.altKey,ctrl_key:event.ctrlKey,meta_key:event.metaKey,shift_key:event.shiftKey,changed_touches:event.changedTouches,target_touches:event.targetTouches,touches:event.touches}},serializePointerEvent=function(event){return{alt_key:event.altKey,button:event.button,buttons:event.buttons,client_x:event.clientX,client_y:event.clientY,ctrl_key:event.ctrlKey,meta_key:event.metaKey,page_x:event.pageX,page_y:event.pageY,screen_x:event.screenX,screen_y:event.screenY,shift_key:event.shiftKey,pointer_id:event.pointerId,width:event.width,height:event.height,pressure:event.pressure,tangential_pressure:event.tangentialPressure,tilt_x:event.tiltX,tilt_y:event.tiltY,twist:event.twist,pointer_type:event.pointerType,is_primary:event.isPrimary}},serializeMouseEvent=function(event){return{alt_key:event.altKey,button:event.button,buttons:event.buttons,client_x:event.clientX,client_y:event.clientY,ctrl_key:event.ctrlKey,meta_key:event.metaKey,offset_x:event.offsetX,offset_y:event.offsetY,page_x:event.pageX,page_y:event.pageY,screen_x:event.screenX,screen_y:event.screenY,shift_key:event.shiftKey}},serializeKeyboardEvent=function(event){return{char_code:event.charCode,is_composing:event.isComposing,key:event.key,alt_key:event.altKey,ctrl_key:event.ctrlKey,meta_key:event.metaKey,key_code:event.keyCode,shift_key:event.shiftKey,location:event.location,repeat:event.repeat,which:event.which,code:event.code}},serializeAnimationEvent=function(event){return{animation_name:event.animationName,elapsed_time:event.elapsedTime,pseudo_element:event.pseudoElement}},serializeDragEvent=function(event){return{mouse:{alt_key:event.altKey,ctrl_key:event.ctrlKey,meta_key:event.metaKey,shift_key:event.shiftKey,...serializeMouseEvent(event)},files:{files:{a:[1,2,3]}}}};var getTargetId=function(target){if(!(target instanceof Node))return null;let ourTarget=target,realId=null;while(realId==null){if(ourTarget===null)return null;if(ourTarget instanceof Element)realId=ourTarget.getAttribute("data-dioxus-id");ourTarget=ourTarget.parentNode}return parseInt(realId)},JSChannel_;if(RawInterpreter!==void 0&&RawInterpreter!==null)JSChannel_=RawInterpreter;class NativeInterpreter extends JSChannel_{intercept_link_redirects;ipc;editsPath;liveview;constructor(editsPath){super();this.editsPath=editsPath}initialize(root){this.intercept_link_redirects=!0,this.liveview=!1,window.addEventListener("dragover",function(e){if(e.target instanceof Element&&e.target.tagName!="INPUT")e.preventDefault()},!1),window.addEventListener("drop",function(e){if(!(e.target instanceof Element))return;e.preventDefault()},!1),window.addEventListener("click",(event)=>{const target=event.target;if(target instanceof HTMLInputElement&&target.getAttribute("type")==="file"){let target_id=getTargetId(target);if(target_id!==null){const message=this.serializeIpcMessage("file_dialog",{event:"change&input",accept:target.getAttribute("accept"),directory:target.getAttribute("webkitdirectory")==="true",multiple:target.hasAttribute("multiple"),target:target_id,bubbles:event.bubbles});this.ipc.postMessage(message)}event.preventDefault()}}),this.ipc=window.ipc;const handler=(event)=>this.handleEvent(event,event.type,!0);super.initialize(root,handler)}serializeIpcMessage(method,params={}){return JSON.stringify({method,params})}scrollTo(id,behavior){const node=this.nodes[id];if(node instanceof HTMLElement)node.scrollIntoView({behavior})}getClientRect(id){const node=this.nodes[id];if(node instanceof HTMLElement){const rect=node.getBoundingClientRect();return{type:"GetClientRect",origin:[rect.x,rect.y],size:[rect.width,rect.height]}}}setFocus(id,focus){const node=this.nodes[id];if(node instanceof HTMLElement)if(focus)node.focus();else node.blur()}loadChild(array){let node=this.stack[this.stack.length-1];for(let i=0;i0;end--)node=node.nextSibling}return node}appendChildren(id,many){const root=this.nodes[id],els=this.stack.splice(this.stack.length-many);for(let k=0;kresponse.arrayBuffer()).then((bytes)=>{if(headless)this.run_from_bytes(bytes);else requestAnimationFrame(()=>this.run_from_bytes(bytes));this.waitForRequest(headless)})}async readFiles(target,contents,bubbles,realId,name){let files=target.files,file_contents={};for(let i=0;i { if (contents[key]) { - contents[key] += "," + value; + contents[key].push(value); } else { - contents[key] = value; + contents[key] = [value]; } }); return { diff --git a/packages/interpreter/src/ts/serialize.ts b/packages/interpreter/src/ts/serialize.ts index c9288ac3f..a5c1f5411 100644 --- a/packages/interpreter/src/ts/serialize.ts +++ b/packages/interpreter/src/ts/serialize.ts @@ -5,7 +5,7 @@ import { retriveSelectValue, retriveValues } from "./form"; export type AppTouchEvent = TouchEvent; export type SerializedEvent = { - values?: { [key: string]: FormDataEntryValue }; + values?: { [key: string]: FormDataEntryValue[] }; value?: string; [key: string]: any; }; diff --git a/packages/rsx/src/hot_reload/hot_reloading_file_map.rs b/packages/rsx/src/hot_reload/hot_reloading_file_map.rs index 7d7edc745..c3b1a9c7f 100644 --- a/packages/rsx/src/hot_reload/hot_reloading_file_map.rs +++ b/packages/rsx/src/hot_reload/hot_reloading_file_map.rs @@ -9,7 +9,7 @@ pub use proc_macro2::TokenStream; pub use std::collections::HashMap; pub use std::sync::Mutex; pub use std::time::SystemTime; -use std::{collections::HashSet, path::PathBuf}; +use std::{collections::HashSet, ffi::OsStr, path::PathBuf}; pub use std::{fs, io, path::Path}; pub use std::{fs::File, io::Read}; pub use syn::__private::ToTokens; @@ -55,8 +55,14 @@ pub struct CachedSynFile { impl FileMap { /// Create a new FileMap from a crate directory + /// + /// TODO: this should be created with a gitignore filter pub fn create(path: PathBuf) -> io::Result> { - Self::create_with_filter(path, |_| false) + Self::create_with_filter(path, |p| { + // skip some stuff we know is large by default + p.file_name() == Some(OsStr::new("target")) + || p.file_name() == Some(OsStr::new("node_modules")) + }) } /// Create a new FileMap from a crate directory @@ -352,7 +358,6 @@ fn find_rs_files(root: PathBuf, filter: &mut impl FnMut(&Path) -> bool) -> FileM }; // track assets while we're here - files.insert(root, cached_file); } Err(err) => { diff --git a/packages/web/src/event.rs b/packages/web/src/event.rs index 1765fd38a..a96269c71 100644 --- a/packages/web/src/event.rs +++ b/packages/web/src/event.rs @@ -4,7 +4,7 @@ use dioxus_html::{ point_interaction::{ InteractionElementOffset, InteractionLocation, ModifiersInteraction, PointerInteraction, }, - DragData, FileEngine, FormData, HasDragData, HasFileData, HasFormData, HasImageData, + DragData, FileEngine, FormData, FormValue, HasDragData, HasFileData, HasFormData, HasImageData, HasMouseData, HtmlEventConverter, ImageData, MountedData, PlatformEventData, ScrollData, }; use js_sys::Array; @@ -384,15 +384,11 @@ 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 { + fn values(&self) -> HashMap { let mut values = HashMap::new(); - fn insert_value(map: &mut HashMap, 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); - } + fn insert_value(map: &mut HashMap, key: String, new_value: String) { + map.entry(key.clone()).or_default().0.push(new_value); } // try to fill in form values @@ -414,8 +410,8 @@ impl HasFormData for WebFormData { } } else if let Some(select) = self.element.dyn_ref::() { // try to fill in select element values - let options = get_select_data(select).join(","); - values.insert("options".to_string(), options); + let options = get_select_data(select); + values.insert("options".to_string(), FormValue(options)); } values