From cfe94950c62e27bdc8efc41573811386dda65ea7 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Wed, 26 Apr 2023 18:05:13 -0500 Subject: [PATCH 1/4] make files optional when deserializing form data --- packages/html/src/events/form.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/html/src/events/form.rs b/packages/html/src/events/form.rs index ec65ffe03..113c765c5 100644 --- a/packages/html/src/events/form.rs +++ b/packages/html/src/events/form.rs @@ -14,7 +14,11 @@ pub struct FormData { #[cfg_attr( feature = "serialize", - serde(skip_serializing, deserialize_with = "deserialize_file_engine") + serde( + default, + skip_serializing, + deserialize_with = "deserialize_file_engine" + ) )] pub files: Option>, } From c9044111908338c347b2b00bb48f579e5d9e1877 Mon Sep 17 00:00:00 2001 From: Tim Newsome Date: Thu, 27 Apr 2023 05:27:07 -0700 Subject: [PATCH 2/4] Fix typo in event_handlers.md (#991) --- docs/guide/src/en/interactivity/event_handlers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guide/src/en/interactivity/event_handlers.md b/docs/guide/src/en/interactivity/event_handlers.md index 5e12ce259..bee0175e2 100644 --- a/docs/guide/src/en/interactivity/event_handlers.md +++ b/docs/guide/src/en/interactivity/event_handlers.md @@ -31,7 +31,7 @@ Some events will trigger first on the element the event originated at upward. Fo > For more information about event propigation see [the mdn docs on event bubling](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events#event_bubbling) -If you want to prevent this behavior, you can call `stop_propogation()` on the event: +If you want to prevent this behavior, you can call `stop_propagation()` on the event: ```rust {{#include ../../../examples/event_nested.rs:rsx}} From c645e1c6aaa44829b38f273d97f5ae274a5671f5 Mon Sep 17 00:00:00 2001 From: Attilio <75045331+attilio-oliva@users.noreply.github.com> Date: Thu, 27 Apr 2023 18:26:04 +0200 Subject: [PATCH 3/4] Allow multiple prevent default for Web, Desktop and LiveView (#987) * Allow multiple prevent default in dioxus_web * Use SplitWhitespace for prevent default requests * Add multiple prevent default option to liveview and desktop * Update docs about prevent_default attribute * cargo clippy * update prevent default guide example --------- Co-authored-by: Evan Almloff --- docs/guide/examples/event_prevent_default.rs | 3 +-- .../src/en/interactivity/event_handlers.md | 2 +- packages/interpreter/src/interpreter.js | 18 +++++++++++------- packages/web/src/dom.rs | 12 ++++++++---- 4 files changed, 21 insertions(+), 14 deletions(-) diff --git a/docs/guide/examples/event_prevent_default.rs b/docs/guide/examples/event_prevent_default.rs index b109700bf..32443d017 100644 --- a/docs/guide/examples/event_prevent_default.rs +++ b/docs/guide/examples/event_prevent_default.rs @@ -10,8 +10,7 @@ fn App(cx: Scope) -> Element { // ANCHOR: prevent_default cx.render(rsx! { input { - prevent_default: "oninput", - prevent_default: "onclick", + prevent_default: "oninput onclick", } }) // ANCHOR_END: prevent_default diff --git a/docs/guide/src/en/interactivity/event_handlers.md b/docs/guide/src/en/interactivity/event_handlers.md index bee0175e2..317038923 100644 --- a/docs/guide/src/en/interactivity/event_handlers.md +++ b/docs/guide/src/en/interactivity/event_handlers.md @@ -41,7 +41,7 @@ If you want to prevent this behavior, you can call `stop_propagation()` on the e Some events have a default behavior. For keyboard events, this might be entering the typed character. For mouse events, this might be selecting some text. -In some instances, might want to avoid this default behavior. For this, you can add the `prevent_default` attribute with the name of the handler whose default behavior you want to stop. This attribute is special: you can attach it multiple times for multiple attributes: +In some instances, might want to avoid this default behavior. For this, you can add the `prevent_default` attribute with the name of the handler whose default behavior you want to stop. This attribute can be used for multiple handlers using their name separated by spaces: ```rust {{#include ../../../examples/event_prevent_default.rs:prevent_default}} diff --git a/packages/interpreter/src/interpreter.js b/packages/interpreter/src/interpreter.js index 9b93ec9e3..d7b74a63e 100644 --- a/packages/interpreter/src/interpreter.js +++ b/packages/interpreter/src/interpreter.js @@ -352,7 +352,7 @@ class Interpreter { let target = event.target; if (target != null) { let realId = target.getAttribute(`data-dioxus-id`); - let shouldPreventDefault = target.getAttribute( + let preventDefaultRequests = target.getAttribute( `dioxus-prevent-default` ); @@ -361,10 +361,14 @@ class Interpreter { let a_element = target.closest("a"); if (a_element != null) { event.preventDefault(); - if ( - shouldPreventDefault !== `onclick` && - a_element.getAttribute(`dioxus-prevent-default`) !== `onclick` - ) { + + let elementShouldPreventDefault = + preventDefaultRequests.includes(`onclick`); + let linkShouldPreventDefault = a_element + .getAttribute(`dioxus-prevent-default`) + .includes(`onclick`); + + if (!elementShouldPreventDefault && !linkShouldPreventDefault) { const href = a_element.getAttribute("href"); if (href !== "" && href !== null && href !== undefined) { window.ipc.postMessage( @@ -390,13 +394,13 @@ class Interpreter { realId = target.getAttribute(`data-dioxus-id`); } - shouldPreventDefault = target.getAttribute( + preventDefaultRequests = target.getAttribute( `dioxus-prevent-default` ); let contents = serialize_event(event); - if (shouldPreventDefault === `on${event.type}`) { + if (preventDefaultRequests.includes(`on${event.type}`)) { event.preventDefault(); } diff --git a/packages/web/src/dom.rs b/packages/web/src/dom.rs index e1e3b6365..20119dd25 100644 --- a/packages/web/src/dom.rs +++ b/packages/web/src/dom.rs @@ -55,13 +55,17 @@ impl WebsysDom { let element = walk_event_for_id(event); let bubbles = dioxus_html::event_bubbles(name.as_str()); if let Some((element, target)) = element { - if target + if let Some(prevent_requests) = target .get_attribute("dioxus-prevent-default") .as_deref() - .map(|f| f.trim_start_matches("on")) - == Some(&name) + .map(|f| f.split_whitespace()) { - event.prevent_default(); + if prevent_requests + .map(|f| f.trim_start_matches("on")) + .any(|f| f == name) + { + event.prevent_default(); + } } let data = virtual_event_from_websys_event(event.clone(), target); From f044bb212889eb2539286ed28c9f37f4c4f3d03c Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Thu, 27 Apr 2023 15:19:10 -0500 Subject: [PATCH 4/4] fix onclick event --- packages/desktop/src/file_upload.rs | 5 +- packages/desktop/src/protocol.rs | 16 +-- packages/interpreter/src/interpreter.js | 129 ++++-------------------- 3 files changed, 32 insertions(+), 118 deletions(-) diff --git a/packages/desktop/src/file_upload.rs b/packages/desktop/src/file_upload.rs index ae4a81cc2..1b24ffc55 100644 --- a/packages/desktop/src/file_upload.rs +++ b/packages/desktop/src/file_upload.rs @@ -4,7 +4,8 @@ use serde::Deserialize; #[derive(Debug, Deserialize)] pub(crate) struct FileDiologRequest { - accept: String, + #[serde(default)] + accept: Option, multiple: bool, pub event: String, pub target: usize, @@ -16,6 +17,8 @@ pub(crate) fn get_file_event(request: &FileDiologRequest) -> Vec { let filters: Vec<_> = request .accept + .as_deref() + .unwrap_or_default() .split(',') .filter_map(|s| Filters::from_str(s).ok()) .collect(); diff --git a/packages/desktop/src/protocol.rs b/packages/desktop/src/protocol.rs index e35d67bb1..2e407b258 100644 --- a/packages/desktop/src/protocol.rs +++ b/packages/desktop/src/protocol.rs @@ -15,12 +15,12 @@ fn module_loader(root_name: &str) -> String { let inputs = document.querySelectorAll("input"); for (let input of inputs) { if (!input.getAttribute("data-dioxus-file-listener")) { - input.setAttribute("data-dioxus-file-listener", true); - input.addEventListener("click", (event) => { - let target = event.target; - // prevent file inputs from opening the file dialog on click - const type = target.getAttribute("type"); - if (type === "file") { + // prevent file inputs from opening the file dialog on click + const type = input.getAttribute("type"); + if (type === "file") { + input.setAttribute("data-dioxus-file-listener", true); + input.addEventListener("click", (event) => { + let target = event.target; let target_id = find_real_id(target); if (target_id !== null) { const send = (event_name) => { @@ -30,8 +30,8 @@ fn module_loader(root_name: &str) -> String { send("change&input"); } event.preventDefault(); - } - }); + }); + } } }"#, ); diff --git a/packages/interpreter/src/interpreter.js b/packages/interpreter/src/interpreter.js index d7b74a63e..e3c8579a4 100644 --- a/packages/interpreter/src/interpreter.js +++ b/packages/interpreter/src/interpreter.js @@ -346,106 +346,9 @@ class Interpreter { case "NewEventListener": let bubbles = event_bubbles(edit.name); - // this handler is only provided on desktop implementations since this - // method is not used by the web implementation - let handler = (event) => { - let target = event.target; - if (target != null) { - let realId = target.getAttribute(`data-dioxus-id`); - let preventDefaultRequests = target.getAttribute( - `dioxus-prevent-default` - ); - - if (event.type === "click") { - // todo call prevent default if it's the right type of event - let a_element = target.closest("a"); - if (a_element != null) { - event.preventDefault(); - - let elementShouldPreventDefault = - preventDefaultRequests.includes(`onclick`); - let linkShouldPreventDefault = a_element - .getAttribute(`dioxus-prevent-default`) - .includes(`onclick`); - - if (!elementShouldPreventDefault && !linkShouldPreventDefault) { - const href = a_element.getAttribute("href"); - if (href !== "" && href !== null && href !== undefined) { - window.ipc.postMessage( - serializeIpcMessage("browser_open", { href }) - ); - } - } - } - - // also prevent buttons from submitting - if (target.tagName === "BUTTON" && event.type == "submit") { - event.preventDefault(); - } - } - // walk the tree to find the real element - while (realId == null) { - // we've reached the root we don't want to send an event - if (target.parentElement === null) { - return; - } - - target = target.parentElement; - realId = target.getAttribute(`data-dioxus-id`); - } - - preventDefaultRequests = target.getAttribute( - `dioxus-prevent-default` - ); - - let contents = serialize_event(event); - - if (preventDefaultRequests.includes(`on${event.type}`)) { - event.preventDefault(); - } - - if (event.type === "submit") { - event.preventDefault(); - } - - if ( - target.tagName === "FORM" && - (event.type === "submit" || event.type === "input") - ) { - for (let x = 0; x < target.elements.length; x++) { - let element = target.elements[x]; - let name = element.getAttribute("name"); - if (name != null) { - if (element.getAttribute("type") === "checkbox") { - // @ts-ignore - contents.values[name] = element.checked ? "true" : "false"; - } else if (element.getAttribute("type") === "radio") { - if (element.checked) { - contents.values[name] = element.value; - } - } else { - // @ts-ignore - contents.values[name] = - element.value ?? element.textContent; - } - } - } - } - - if (realId === null) { - return; - } - window.ipc.postMessage( - serializeIpcMessage("user_event", { - name: edit.name, - element: parseInt(realId), - data: contents, - bubbles, - }) - ); - } - }; - this.NewEventListener(edit.name, edit.id, bubbles, handler); + this.NewEventListener(edit.name, edit.id, bubbles, (event) => { + handler(event, edit.name, bubbles); + }); break; } } @@ -456,17 +359,24 @@ class Interpreter { function handler(event, name, bubbles) { let target = event.target; if (target != null) { - let shouldPreventDefault = target.getAttribute(`dioxus-prevent-default`); + let preventDefaultRequests = target.getAttribute(`dioxus-prevent-default`); if (event.type === "click") { - // Prevent redirects from links + // todo call prevent default if it's the right type of event let a_element = target.closest("a"); if (a_element != null) { event.preventDefault(); - if ( - shouldPreventDefault !== `onclick` && - a_element.getAttribute(`dioxus-prevent-default`) !== `onclick` - ) { + + let elementShouldPreventDefault = + preventDefaultRequests && preventDefaultRequests.includes(`onclick`); + let aElementShouldPreventDefault = a_element.getAttribute( + `dioxus-prevent-default` + ); + let linkShouldPreventDefault = + aElementShouldPreventDefault && + aElementShouldPreventDefault.includes(`onclick`); + + if (!elementShouldPreventDefault && !linkShouldPreventDefault) { const href = a_element.getAttribute("href"); if (href !== "" && href !== null && href !== undefined) { window.ipc.postMessage( @@ -484,9 +394,10 @@ function handler(event, name, bubbles) { const realId = find_real_id(target); - shouldPreventDefault = target.getAttribute(`dioxus-prevent-default`); - - if (shouldPreventDefault === `on${event.type}`) { + if ( + preventDefaultRequests && + preventDefaultRequests.includes(`on${event.type}`) + ) { event.preventDefault(); }