feat: get web working properly
This commit is contained in:
1 parent
3c19def550
commit
18d6b1ad6f
26 files changed
+674
-1422
No files matched your search
+167
-308
@@ -7,11 +7,12 @@
|
||||
//! - tests to ensure dyn_into works for various event types.
|
||||
//! - Partial delegation?>
|
||||
|
||||
use dioxus_core::{DomEdit, ElementId, SchedulerMsg, UserEvent};
|
||||
use dioxus_html::event_bubbles;
|
||||
use dioxus_core::{ElementId, Mutation, Mutations};
|
||||
use dioxus_html::{event_bubbles, CompositionData, FormData};
|
||||
use dioxus_interpreter_js::Interpreter;
|
||||
use futures_channel::mpsc;
|
||||
use js_sys::Function;
|
||||
use std::{any::Any, rc::Rc, sync::Arc};
|
||||
use std::{any::Any, rc::Rc};
|
||||
use wasm_bindgen::{closure::Closure, JsCast, JsValue};
|
||||
use web_sys::{Document, Element, Event, HtmlElement};
|
||||
|
||||
@@ -26,68 +27,26 @@ pub struct WebsysDom {
|
||||
}
|
||||
|
||||
impl WebsysDom {
|
||||
pub fn new(cfg: Config, sender_callback: Rc<dyn Fn(SchedulerMsg)>) -> Self {
|
||||
pub fn new(cfg: Config, event_channel: mpsc::UnboundedSender<Event>) -> Self {
|
||||
// eventually, we just want to let the interpreter do all the work of decoding events into our event type
|
||||
let callback: Box<dyn FnMut(&Event)> = Box::new(move |event: &web_sys::Event| {
|
||||
let mut target = event
|
||||
.target()
|
||||
.expect("missing target")
|
||||
.dyn_into::<Element>()
|
||||
.expect("not a valid element");
|
||||
_ = event_channel.unbounded_send(event.clone());
|
||||
|
||||
let typ = event.type_();
|
||||
// if let Ok(synthetic_event) = decoded {
|
||||
// // Try to prevent default if the attribute is set
|
||||
// if let Some(node) = target.dyn_ref::<HtmlElement>() {
|
||||
// if let Some(name) = node.get_attribute("dioxus-prevent-default") {
|
||||
// if name == synthetic_event.name
|
||||
// || name.trim_start_matches("on") == synthetic_event.name
|
||||
// {
|
||||
// log::trace!("Preventing default");
|
||||
// event.prevent_default();
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
let decoded: anyhow::Result<UserEvent> = loop {
|
||||
match target.get_attribute("data-dioxus-id").map(|f| f.parse()) {
|
||||
Some(Ok(id)) => {
|
||||
break Ok(UserEvent {
|
||||
name: event_name_from_typ(&typ),
|
||||
data: virtual_event_from_websys_event(event.clone(), target.clone()),
|
||||
element: Some(ElementId(id)),
|
||||
scope_id: None,
|
||||
priority: dioxus_core::EventPriority::Medium,
|
||||
bubbles: event.bubbles(),
|
||||
});
|
||||
}
|
||||
Some(Err(e)) => {
|
||||
break Err(e.into());
|
||||
}
|
||||
None => {
|
||||
// walk the tree upwards until we actually find an event target
|
||||
if let Some(parent) = target.parent_element() {
|
||||
target = parent;
|
||||
} else {
|
||||
break Ok(UserEvent {
|
||||
name: event_name_from_typ(&typ),
|
||||
data: virtual_event_from_websys_event(
|
||||
event.clone(),
|
||||
target.clone(),
|
||||
),
|
||||
element: None,
|
||||
scope_id: None,
|
||||
priority: dioxus_core::EventPriority::Low,
|
||||
bubbles: event.bubbles(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if let Ok(synthetic_event) = decoded {
|
||||
// Try to prevent default if the attribute is set
|
||||
if let Some(node) = target.dyn_ref::<HtmlElement>() {
|
||||
if let Some(name) = node.get_attribute("dioxus-prevent-default") {
|
||||
if name == synthetic_event.name
|
||||
|| name.trim_start_matches("on") == synthetic_event.name
|
||||
{
|
||||
log::trace!("Preventing default");
|
||||
event.prevent_default();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sender_callback.as_ref()(SchedulerMsg::Event(synthetic_event))
|
||||
}
|
||||
// sender_callback.as_ref()(SchedulerMsg::Event(synthetic_event))
|
||||
// }
|
||||
});
|
||||
|
||||
// a match here in order to avoid some error during runtime browser test
|
||||
@@ -104,75 +63,56 @@ impl WebsysDom {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn apply_edits(&mut self, mut edits: Vec<DomEdit>) {
|
||||
pub fn apply_edits(&mut self, mut edits: Vec<Mutation>) {
|
||||
use Mutation::*;
|
||||
let i = &self.interpreter;
|
||||
|
||||
for edit in edits.drain(..) {
|
||||
match edit {
|
||||
DomEdit::AppendChildren { root, children } => {
|
||||
self.interpreter.AppendChildren(root, children);
|
||||
}
|
||||
DomEdit::ReplaceWith { root, nodes } => self.interpreter.ReplaceWith(root, nodes),
|
||||
DomEdit::InsertAfter { root, nodes } => self.interpreter.InsertAfter(root, nodes),
|
||||
DomEdit::InsertBefore { root, nodes } => self.interpreter.InsertBefore(root, nodes),
|
||||
DomEdit::Remove { root } => self.interpreter.Remove(root),
|
||||
|
||||
DomEdit::CreateElement {
|
||||
root,
|
||||
tag,
|
||||
children,
|
||||
} => self.interpreter.CreateElement(tag, root, children),
|
||||
DomEdit::CreateElementNs {
|
||||
root,
|
||||
tag,
|
||||
AppendChildren { m } => i.AppendChildren(m as u32),
|
||||
AssignId { path, id } => i.AssignId(path, id.0 as u32),
|
||||
CreateElement { name } => i.CreateElement(name),
|
||||
CreateElementNamespace { name, namespace } => i.CreateElementNs(name, namespace),
|
||||
CreatePlaceholder { id } => i.CreatePlaceholder(id.0 as u32),
|
||||
CreateStaticPlaceholder => i.CreateStaticPlaceholder(),
|
||||
CreateTextPlaceholder => i.CreateTextPlaceholder(),
|
||||
CreateStaticText { value } => i.CreateStaticText(value),
|
||||
CreateTextNode { value, id } => i.CreateTextNode(value.into(), id.0 as u32),
|
||||
HydrateText { path, value, id } => i.HydrateText(path, value, id.0 as u32),
|
||||
LoadTemplate { name, index, id } => i.LoadTemplate(name, index as u32, id.0 as u32),
|
||||
ReplaceWith { id, m } => i.ReplaceWith(id.0 as u32, m as u32),
|
||||
ReplacePlaceholder { path, m } => i.ReplacePlaceholder(path, m as u32),
|
||||
InsertAfter { id, m } => i.InsertAfter(id.0 as u32, m as u32),
|
||||
InsertBefore { id, m } => i.InsertBefore(id.0 as u32, m as u32),
|
||||
SaveTemplate { name, m } => i.SaveTemplate(name, m as u32),
|
||||
SetAttribute {
|
||||
name,
|
||||
value,
|
||||
id,
|
||||
ns,
|
||||
children,
|
||||
} => self.interpreter.CreateElementNs(tag, root, ns, children),
|
||||
DomEdit::CreatePlaceholder { root } => self.interpreter.CreatePlaceholder(root),
|
||||
DomEdit::NewEventListener {
|
||||
event_name, root, ..
|
||||
} => {
|
||||
} => i.SetAttribute(id.0 as u32, name, value.into(), ns),
|
||||
SetStaticAttribute { name, value, ns } => {
|
||||
i.SetStaticAttribute(name, value.into(), ns)
|
||||
}
|
||||
SetBoolAttribute { name, value, id } => {
|
||||
i.SetBoolAttribute(id.0 as u32, name, value)
|
||||
}
|
||||
SetText { value, id } => i.SetText(id.0 as u32, value.into()),
|
||||
NewEventListener { name, scope, id } => {
|
||||
let handler: &Function = self.handler.as_ref().unchecked_ref();
|
||||
self.interpreter.NewEventListener(
|
||||
event_name,
|
||||
root,
|
||||
name,
|
||||
id.0 as u32,
|
||||
handler,
|
||||
event_bubbles(event_name),
|
||||
event_bubbles(&name[2..]),
|
||||
);
|
||||
}
|
||||
|
||||
DomEdit::RemoveEventListener { root, event } => self
|
||||
.interpreter
|
||||
.RemoveEventListener(root, event, event_bubbles(event)),
|
||||
|
||||
DomEdit::RemoveAttribute { root, name, ns } => {
|
||||
self.interpreter.RemoveAttribute(root, name, ns)
|
||||
}
|
||||
|
||||
DomEdit::CreateTextNode { text, root } => {
|
||||
let text = JsValue::from_str(text);
|
||||
self.interpreter.CreateTextNode(text, root)
|
||||
}
|
||||
DomEdit::SetText { root, text } => {
|
||||
let text = JsValue::from_str(text);
|
||||
self.interpreter.SetText(root, text)
|
||||
}
|
||||
DomEdit::SetAttribute {
|
||||
root,
|
||||
field,
|
||||
value,
|
||||
ns,
|
||||
} => {
|
||||
let value = JsValue::from_str(&value.to_string());
|
||||
self.interpreter.SetAttribute(root, field, value, ns)
|
||||
}
|
||||
DomEdit::CloneNode { id, new_id } => self.interpreter.CloneNode(id, new_id),
|
||||
DomEdit::CloneNodeChildren { id, new_ids } => {
|
||||
self.interpreter.CloneNodeChildren(id, new_ids)
|
||||
}
|
||||
DomEdit::FirstChild {} => self.interpreter.FirstChild(),
|
||||
DomEdit::NextSibling {} => self.interpreter.NextSibling(),
|
||||
DomEdit::ParentNode {} => self.interpreter.ParentNode(),
|
||||
DomEdit::StoreWithId { id } => self.interpreter.StoreWithId(id),
|
||||
DomEdit::SetLastNode { id } => self.interpreter.SetLastNode(id),
|
||||
RemoveEventListener { name, id } => i.RemoveEventListener(name, id.0 as u32),
|
||||
Remove { id } => i.Remove(id.0 as u32),
|
||||
PushRoot { id } => i.PushRoot(id.0 as u32),
|
||||
// Mutation::RemoveEventListener { root, name: event } => self
|
||||
// .interpreter
|
||||
// .RemoveEventListener(root, event, event_bubbles(event)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -187,129 +127,54 @@ unsafe impl Sync for DioxusWebsysEvent {}
|
||||
|
||||
// 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.
|
||||
fn virtual_event_from_websys_event(
|
||||
event: web_sys::Event,
|
||||
target: Element,
|
||||
) -> Arc<dyn Any + Send + Sync> {
|
||||
use dioxus_html::on::*;
|
||||
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" => Arc::new(ClipboardData {}),
|
||||
"copy" | "cut" | "paste" => Rc::new(ClipboardData {}),
|
||||
"compositionend" | "compositionstart" | "compositionupdate" => {
|
||||
let evt: &web_sys::CompositionEvent = event.dyn_ref().unwrap();
|
||||
Arc::new(CompositionData {
|
||||
data: evt.data().unwrap_or_default(),
|
||||
})
|
||||
make_composition_event(&event)
|
||||
}
|
||||
"keydown" | "keypress" | "keyup" => Arc::new(KeyboardData::from(event)),
|
||||
"focus" | "blur" | "focusout" | "focusin" => Arc::new(FocusData {}),
|
||||
"keydown" | "keypress" | "keyup" => Rc::new(KeyboardData::from(event)),
|
||||
"focus" | "blur" | "focusout" | "focusin" => Rc::new(FocusData {}),
|
||||
|
||||
// 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
|
||||
"change" | "input" | "invalid" | "reset" | "submit" => {
|
||||
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");
|
||||
"change" | "input" | "invalid" | "reset" | "submit" => read_input_to_data(target),
|
||||
|
||||
let mut values = std::collections::HashMap::new();
|
||||
|
||||
// try to fill in form values
|
||||
if let Some(form) = target.dyn_ref::<web_sys::HtmlFormElement>() {
|
||||
let elements = form.elements();
|
||||
for x in 0..elements.length() {
|
||||
let element = elements.item(x).unwrap();
|
||||
if let Some(name) = element.get_attribute("name") {
|
||||
let value: Option<String> = element
|
||||
.dyn_ref()
|
||||
.map(|input: &web_sys::HtmlInputElement| {
|
||||
match input.type_().as_str() {
|
||||
"checkbox" => {
|
||||
match input.checked() {
|
||||
true => Some("true".to_string()),
|
||||
false => Some("false".to_string()),
|
||||
}
|
||||
},
|
||||
"radio" => {
|
||||
match input.checked() {
|
||||
true => Some(input.value()),
|
||||
false => None,
|
||||
}
|
||||
}
|
||||
_ => Some(input.value())
|
||||
}
|
||||
})
|
||||
.or_else(|| element.dyn_ref().map(|input: &web_sys::HtmlTextAreaElement| Some(input.value())))
|
||||
.or_else(|| element.dyn_ref().map(|input: &web_sys::HtmlSelectElement| Some(input.value())))
|
||||
.or_else(|| Some(element.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");
|
||||
if let Some(value) = value {
|
||||
values.insert(name, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Arc::new(FormData { value, values })
|
||||
}
|
||||
"click" | "contextmenu" | "dblclick" | "doubleclick" | "drag" | "dragend" | "dragenter"
|
||||
| "dragexit" | "dragleave" | "dragover" | "dragstart" | "drop" | "mousedown"
|
||||
| "mouseenter" | "mouseleave" | "mousemove" | "mouseout" | "mouseover" | "mouseup" => {
|
||||
Arc::new(MouseData::from(event))
|
||||
Rc::new(MouseData::from(event))
|
||||
}
|
||||
"pointerdown" | "pointermove" | "pointerup" | "pointercancel" | "gotpointercapture"
|
||||
| "lostpointercapture" | "pointerenter" | "pointerleave" | "pointerover" | "pointerout" => {
|
||||
Arc::new(PointerData::from(event))
|
||||
Rc::new(PointerData::from(event))
|
||||
}
|
||||
"select" => Arc::new(SelectionData {}),
|
||||
"touchcancel" | "touchend" | "touchmove" | "touchstart" => Arc::new(TouchData::from(event)),
|
||||
"select" => Rc::new(SelectionData {}),
|
||||
"touchcancel" | "touchend" | "touchmove" | "touchstart" => Rc::new(TouchData::from(event)),
|
||||
|
||||
"scroll" => Arc::new(()),
|
||||
"wheel" => Arc::new(WheelData::from(event)),
|
||||
"scroll" => Rc::new(()),
|
||||
"wheel" => Rc::new(WheelData::from(event)),
|
||||
"animationstart" | "animationend" | "animationiteration" => {
|
||||
Arc::new(AnimationData::from(event))
|
||||
Rc::new(AnimationData::from(event))
|
||||
}
|
||||
"transitionend" => Arc::new(TransitionData::from(event)),
|
||||
"transitionend" => Rc::new(TransitionData::from(event)),
|
||||
"abort" | "canplay" | "canplaythrough" | "durationchange" | "emptied" | "encrypted"
|
||||
| "ended" | "error" | "loadeddata" | "loadedmetadata" | "loadstart" | "pause" | "play"
|
||||
| "playing" | "progress" | "ratechange" | "seeked" | "seeking" | "stalled" | "suspend"
|
||||
| "timeupdate" | "volumechange" | "waiting" => Arc::new(MediaData {}),
|
||||
"toggle" => Arc::new(ToggleData {}),
|
||||
| "timeupdate" | "volumechange" | "waiting" => Rc::new(MediaData {}),
|
||||
"toggle" => Rc::new(ToggleData {}),
|
||||
|
||||
_ => Arc::new(()),
|
||||
_ => 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")
|
||||
@@ -317,92 +182,86 @@ pub(crate) fn load_document() -> Document {
|
||||
.expect("should have access to the Document")
|
||||
}
|
||||
|
||||
fn event_name_from_typ(typ: &str) -> &'static str {
|
||||
match typ {
|
||||
"copy" => "copy",
|
||||
"cut" => "cut",
|
||||
"paste" => "paste",
|
||||
"compositionend" => "compositionend",
|
||||
"compositionstart" => "compositionstart",
|
||||
"compositionupdate" => "compositionupdate",
|
||||
"keydown" => "keydown",
|
||||
"keypress" => "keypress",
|
||||
"keyup" => "keyup",
|
||||
"focus" => "focus",
|
||||
"focusout" => "focusout",
|
||||
"focusin" => "focusin",
|
||||
"blur" => "blur",
|
||||
"change" => "change",
|
||||
"input" => "input",
|
||||
"invalid" => "invalid",
|
||||
"reset" => "reset",
|
||||
"submit" => "submit",
|
||||
"click" => "click",
|
||||
"contextmenu" => "contextmenu",
|
||||
"doubleclick" => "doubleclick",
|
||||
"dblclick" => "dblclick",
|
||||
"drag" => "drag",
|
||||
"dragend" => "dragend",
|
||||
"dragenter" => "dragenter",
|
||||
"dragexit" => "dragexit",
|
||||
"dragleave" => "dragleave",
|
||||
"dragover" => "dragover",
|
||||
"dragstart" => "dragstart",
|
||||
"drop" => "drop",
|
||||
"mousedown" => "mousedown",
|
||||
"mouseenter" => "mouseenter",
|
||||
"mouseleave" => "mouseleave",
|
||||
"mousemove" => "mousemove",
|
||||
"mouseout" => "mouseout",
|
||||
"mouseover" => "mouseover",
|
||||
"mouseup" => "mouseup",
|
||||
"pointerdown" => "pointerdown",
|
||||
"pointermove" => "pointermove",
|
||||
"pointerup" => "pointerup",
|
||||
"pointercancel" => "pointercancel",
|
||||
"gotpointercapture" => "gotpointercapture",
|
||||
"lostpointercapture" => "lostpointercapture",
|
||||
"pointerenter" => "pointerenter",
|
||||
"pointerleave" => "pointerleave",
|
||||
"pointerover" => "pointerover",
|
||||
"pointerout" => "pointerout",
|
||||
"select" => "select",
|
||||
"touchcancel" => "touchcancel",
|
||||
"touchend" => "touchend",
|
||||
"touchmove" => "touchmove",
|
||||
"touchstart" => "touchstart",
|
||||
"scroll" => "scroll",
|
||||
"wheel" => "wheel",
|
||||
"animationstart" => "animationstart",
|
||||
"animationend" => "animationend",
|
||||
"animationiteration" => "animationiteration",
|
||||
"transitionend" => "transitionend",
|
||||
"abort" => "abort",
|
||||
"canplay" => "canplay",
|
||||
"canplaythrough" => "canplaythrough",
|
||||
"durationchange" => "durationchange",
|
||||
"emptied" => "emptied",
|
||||
"encrypted" => "encrypted",
|
||||
"ended" => "ended",
|
||||
"error" => "error",
|
||||
"loadeddata" => "loadeddata",
|
||||
"loadedmetadata" => "loadedmetadata",
|
||||
"loadstart" => "loadstart",
|
||||
"pause" => "pause",
|
||||
"play" => "play",
|
||||
"playing" => "playing",
|
||||
"progress" => "progress",
|
||||
"ratechange" => "ratechange",
|
||||
"seeked" => "seeked",
|
||||
"seeking" => "seeking",
|
||||
"stalled" => "stalled",
|
||||
"suspend" => "suspend",
|
||||
"timeupdate" => "timeupdate",
|
||||
"volumechange" => "volumechange",
|
||||
"waiting" => "waiting",
|
||||
"toggle" => "toggle",
|
||||
a => {
|
||||
panic!("unsupported event type {:?}", a);
|
||||
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 = std::collections::HashMap::new();
|
||||
|
||||
// try to fill in form values
|
||||
if let Some(form) = target.dyn_ref::<web_sys::HtmlFormElement>() {
|
||||
let elements = form.elements();
|
||||
for x in 0..elements.length() {
|
||||
let element = elements.item(x).unwrap();
|
||||
if let Some(name) = element.get_attribute("name") {
|
||||
let value: Option<String> = element
|
||||
.dyn_ref()
|
||||
.map(|input: &web_sys::HtmlInputElement| {
|
||||
match input.type_().as_str() {
|
||||
"checkbox" => {
|
||||
match input.checked() {
|
||||
true => Some("true".to_string()),
|
||||
false => Some("false".to_string()),
|
||||
}
|
||||
},
|
||||
"radio" => {
|
||||
match input.checked() {
|
||||
true => Some(input.value()),
|
||||
false => None,
|
||||
}
|
||||
}
|
||||
_ => Some(input.value())
|
||||
}
|
||||
})
|
||||
.or_else(|| element.dyn_ref().map(|input: &web_sys::HtmlTextAreaElement| Some(input.value())))
|
||||
.or_else(|| element.dyn_ref().map(|input: &web_sys::HtmlSelectElement| Some(input.value())))
|
||||
.or_else(|| Some(element.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");
|
||||
if let Some(value) = value {
|
||||
values.insert(name, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rc::new(FormData {
|
||||
value,
|
||||
values,
|
||||
files: None,
|
||||
})
|
||||
}
|
||||
Reference in new issue
Block a user