wip: add tests and slightly refactor interpreter code
This commit is contained in:
5 files changed
+570
-414
No files matched your search
+13
-4
@@ -4,19 +4,24 @@
|
|||||||
//! in the "values" field.
|
//! in the "values" field.
|
||||||
|
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
launch_desktop(app);
|
launch(app);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn app() -> Element {
|
fn app() -> Element {
|
||||||
|
let mut values = use_signal(|| HashMap::new());
|
||||||
rsx! {
|
rsx! {
|
||||||
div {
|
div {
|
||||||
h1 { "Form" }
|
h1 { "Form" }
|
||||||
form {
|
form {
|
||||||
onsubmit: move |ev| println!("Submitted {:?}", ev.values()),
|
oninput: move |ev| values.set(ev.values()),
|
||||||
oninput: move |ev| println!("Input {:?}", ev.values()),
|
input {
|
||||||
input { r#type: "text", name: "username" }
|
r#type: "text",
|
||||||
|
name: "username",
|
||||||
|
oninput: move |ev| values.set(ev.values())
|
||||||
|
}
|
||||||
input { r#type: "text", name: "full-name" }
|
input { r#type: "text", name: "full-name" }
|
||||||
input { r#type: "password", name: "password" }
|
input { r#type: "password", name: "password" }
|
||||||
input { r#type: "radio", name: "color", value: "red" }
|
input { r#type: "radio", name: "color", value: "red" }
|
||||||
@@ -24,5 +29,9 @@ fn app() -> Element {
|
|||||||
button { r#type: "submit", value: "Submit", "Submit the form" }
|
button { r#type: "submit", value: "Submit", "Submit the form" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
div {
|
||||||
|
h1 { "Oninput Values" }
|
||||||
|
"{values:#?}"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,73 +1,96 @@
|
|||||||
|
use std::{collections::HashMap, ops::Deref};
|
||||||
|
|
||||||
use dioxus::html::geometry::euclid::Vector3D;
|
use dioxus::html::geometry::euclid::Vector3D;
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
use dioxus_core::prelude::consume_context;
|
use dioxus_core::prelude::consume_context;
|
||||||
use dioxus_desktop::DesktopContext;
|
use dioxus_desktop::DesktopContext;
|
||||||
|
|
||||||
|
#[path = "./utils.rs"]
|
||||||
|
mod utils;
|
||||||
|
|
||||||
pub fn main() {
|
pub fn main() {
|
||||||
check_app_exits(app);
|
utils::check_app_exits(app);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn check_app_exits(app: fn() -> Element) {
|
static RECEIVED_EVENTS: GlobalSignal<usize> = Signal::global(|| 0);
|
||||||
use dioxus_desktop::tao::window::WindowBuilder;
|
|
||||||
use dioxus_desktop::Config;
|
|
||||||
// This is a deadman's switch to ensure that the app exits
|
|
||||||
let should_panic = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(true));
|
|
||||||
let should_panic_clone = should_panic.clone();
|
|
||||||
std::thread::spawn(move || {
|
|
||||||
std::thread::sleep(std::time::Duration::from_secs(60));
|
|
||||||
if should_panic_clone.load(std::sync::atomic::Ordering::SeqCst) {
|
|
||||||
eprintln!("App did not exit in time");
|
|
||||||
std::process::exit(exitcode::SOFTWARE);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
LaunchBuilder::desktop()
|
|
||||||
.with_cfg(Config::new().with_window(WindowBuilder::new().with_visible(true)))
|
|
||||||
.launch(app);
|
|
||||||
|
|
||||||
// Stop deadman's switch
|
|
||||||
should_panic.store(false, std::sync::atomic::Ordering::SeqCst);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mock_event(id: &'static str, value: &'static str) {
|
|
||||||
use_hook(move || {
|
|
||||||
spawn(async move {
|
|
||||||
tokio::time::sleep(std::time::Duration::from_millis(5000)).await;
|
|
||||||
|
|
||||||
let js = format!(
|
|
||||||
r#"
|
|
||||||
//console.log("ran");
|
|
||||||
// Dispatch a synthetic event
|
|
||||||
let event = {};
|
|
||||||
let element = document.getElementById('{}');
|
|
||||||
console.log(element, event);
|
|
||||||
element.dispatchEvent(event);
|
|
||||||
"#,
|
|
||||||
value, id
|
|
||||||
);
|
|
||||||
|
|
||||||
eval(&js).await.unwrap();
|
|
||||||
});
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(deprecated)]
|
|
||||||
fn app() -> Element {
|
fn app() -> Element {
|
||||||
let desktop_context: DesktopContext = consume_context();
|
let desktop_context: DesktopContext = consume_context();
|
||||||
let mut received_events = use_signal(|| 0);
|
|
||||||
|
|
||||||
// button
|
let received = RECEIVED_EVENTS();
|
||||||
mock_event(
|
let expected = utils::EXPECTED_EVENTS();
|
||||||
|
|
||||||
|
if expected != 0 && received == expected {
|
||||||
|
println!("all events recieved");
|
||||||
|
desktop_context.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
rsx! {
|
||||||
|
div {
|
||||||
|
test_mounted {}
|
||||||
|
test_button {}
|
||||||
|
test_mouse_move_div {}
|
||||||
|
test_mouse_click_div {}
|
||||||
|
test_mouse_dblclick_div {}
|
||||||
|
test_mouse_down_div {}
|
||||||
|
test_mouse_up_div {}
|
||||||
|
test_mouse_scroll_div {}
|
||||||
|
test_key_down_div {}
|
||||||
|
test_key_up_div {}
|
||||||
|
test_key_press_div {}
|
||||||
|
test_focus_in_div {}
|
||||||
|
test_focus_out_div {}
|
||||||
|
test_form_input {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_mounted() -> Element {
|
||||||
|
rsx! {
|
||||||
|
div {
|
||||||
|
width: "100px",
|
||||||
|
height: "100px",
|
||||||
|
onmounted: move |evt| async move {
|
||||||
|
let rect = evt.get_client_rect().await.unwrap();
|
||||||
|
println!("rect: {:?}", rect);
|
||||||
|
assert_eq!(rect.width(), 100.0);
|
||||||
|
assert_eq!(rect.height(), 100.0);
|
||||||
|
RECEIVED_EVENTS.with_mut(|x| *x += 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_button() -> Element {
|
||||||
|
utils::mock_event(
|
||||||
"button",
|
"button",
|
||||||
r#"new MouseEvent("click", {
|
r#"new MouseEvent("click", {
|
||||||
view: window,
|
view: window,
|
||||||
bubbles: true,
|
bubbles: true,
|
||||||
cancelable: true,
|
cancelable: true,
|
||||||
button: 0,
|
button: 0,
|
||||||
})"#,
|
})"#,
|
||||||
);
|
);
|
||||||
// mouse_move_div
|
|
||||||
mock_event(
|
rsx! {
|
||||||
|
button {
|
||||||
|
id: "button",
|
||||||
|
onclick: move |event| {
|
||||||
|
println!("{:?}", event.data);
|
||||||
|
assert!(event.data.modifiers().is_empty());
|
||||||
|
assert!(event.data.held_buttons().is_empty());
|
||||||
|
assert_eq!(
|
||||||
|
event.data.trigger_button(),
|
||||||
|
Some(dioxus_html::input_data::MouseButton::Primary),
|
||||||
|
);
|
||||||
|
RECEIVED_EVENTS.with_mut(|x| *x += 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_mouse_move_div() -> Element {
|
||||||
|
utils::mock_event(
|
||||||
"mouse_move_div",
|
"mouse_move_div",
|
||||||
r#"new MouseEvent("mousemove", {
|
r#"new MouseEvent("mousemove", {
|
||||||
view: window,
|
view: window,
|
||||||
@@ -76,8 +99,27 @@ fn app() -> Element {
|
|||||||
buttons: 2,
|
buttons: 2,
|
||||||
})"#,
|
})"#,
|
||||||
);
|
);
|
||||||
// mouse_click_div
|
|
||||||
mock_event(
|
rsx! {
|
||||||
|
div {
|
||||||
|
id: "mouse_move_div",
|
||||||
|
onmousemove: move |event| {
|
||||||
|
println!("{:?}", event.data);
|
||||||
|
assert!(event.data.modifiers().is_empty());
|
||||||
|
assert!(
|
||||||
|
event
|
||||||
|
.data
|
||||||
|
.held_buttons()
|
||||||
|
.contains(dioxus_html::input_data::MouseButton::Secondary),
|
||||||
|
);
|
||||||
|
RECEIVED_EVENTS.with_mut(|x| *x += 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_mouse_click_div() -> Element {
|
||||||
|
utils::mock_event(
|
||||||
"mouse_click_div",
|
"mouse_click_div",
|
||||||
r#"new MouseEvent("click", {
|
r#"new MouseEvent("click", {
|
||||||
view: window,
|
view: window,
|
||||||
@@ -87,8 +129,31 @@ fn app() -> Element {
|
|||||||
button: 2,
|
button: 2,
|
||||||
})"#,
|
})"#,
|
||||||
);
|
);
|
||||||
// mouse_dblclick_div
|
|
||||||
mock_event(
|
rsx! {
|
||||||
|
div {
|
||||||
|
id: "mouse_click_div",
|
||||||
|
onclick: move |event| {
|
||||||
|
println!("{:?}", event.data);
|
||||||
|
assert!(event.data.modifiers().is_empty());
|
||||||
|
assert!(
|
||||||
|
event
|
||||||
|
.data
|
||||||
|
.held_buttons()
|
||||||
|
.contains(dioxus_html::input_data::MouseButton::Secondary),
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
event.data.trigger_button(),
|
||||||
|
Some(dioxus_html::input_data::MouseButton::Secondary),
|
||||||
|
);
|
||||||
|
RECEIVED_EVENTS.with_mut(|x| *x += 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_mouse_dblclick_div() -> Element {
|
||||||
|
utils::mock_event(
|
||||||
"mouse_dblclick_div",
|
"mouse_dblclick_div",
|
||||||
r#"new MouseEvent("dblclick", {
|
r#"new MouseEvent("dblclick", {
|
||||||
view: window,
|
view: window,
|
||||||
@@ -98,8 +163,34 @@ fn app() -> Element {
|
|||||||
button: 2,
|
button: 2,
|
||||||
})"#,
|
})"#,
|
||||||
);
|
);
|
||||||
// mouse_down_div
|
|
||||||
mock_event(
|
rsx! {
|
||||||
|
div {
|
||||||
|
id: "mouse_dblclick_div",
|
||||||
|
ondoubleclick: move |event| {
|
||||||
|
println!("{:?}", event.data);
|
||||||
|
assert!(event.data.modifiers().is_empty());
|
||||||
|
assert!(
|
||||||
|
event.data.held_buttons().contains(dioxus_html::input_data::MouseButton::Primary),
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
event
|
||||||
|
.data
|
||||||
|
.held_buttons()
|
||||||
|
.contains(dioxus_html::input_data::MouseButton::Secondary),
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
event.data.trigger_button(),
|
||||||
|
Some(dioxus_html::input_data::MouseButton::Secondary),
|
||||||
|
);
|
||||||
|
RECEIVED_EVENTS.with_mut(|x| *x += 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_mouse_down_div() -> Element {
|
||||||
|
utils::mock_event(
|
||||||
"mouse_down_div",
|
"mouse_down_div",
|
||||||
r#"new MouseEvent("mousedown", {
|
r#"new MouseEvent("mousedown", {
|
||||||
view: window,
|
view: window,
|
||||||
@@ -109,8 +200,31 @@ fn app() -> Element {
|
|||||||
button: 2,
|
button: 2,
|
||||||
})"#,
|
})"#,
|
||||||
);
|
);
|
||||||
// mouse_up_div
|
|
||||||
mock_event(
|
rsx! {
|
||||||
|
div {
|
||||||
|
id: "mouse_down_div",
|
||||||
|
onmousedown: move |event| {
|
||||||
|
println!("{:?}", event.data);
|
||||||
|
assert!(event.data.modifiers().is_empty());
|
||||||
|
assert!(
|
||||||
|
event
|
||||||
|
.data
|
||||||
|
.held_buttons()
|
||||||
|
.contains(dioxus_html::input_data::MouseButton::Secondary),
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
event.data.trigger_button(),
|
||||||
|
Some(dioxus_html::input_data::MouseButton::Secondary),
|
||||||
|
);
|
||||||
|
RECEIVED_EVENTS.with_mut(|x| *x += 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_mouse_up_div() -> Element {
|
||||||
|
utils::mock_event(
|
||||||
"mouse_up_div",
|
"mouse_up_div",
|
||||||
r#"new MouseEvent("mouseup", {
|
r#"new MouseEvent("mouseup", {
|
||||||
view: window,
|
view: window,
|
||||||
@@ -120,8 +234,26 @@ fn app() -> Element {
|
|||||||
button: 0,
|
button: 0,
|
||||||
})"#,
|
})"#,
|
||||||
);
|
);
|
||||||
// wheel_div
|
|
||||||
mock_event(
|
rsx! {
|
||||||
|
div {
|
||||||
|
id: "mouse_up_div",
|
||||||
|
onmouseup: move |event| {
|
||||||
|
println!("{:?}", event.data);
|
||||||
|
assert!(event.data.modifiers().is_empty());
|
||||||
|
assert!(event.data.held_buttons().is_empty());
|
||||||
|
assert_eq!(
|
||||||
|
event.data.trigger_button(),
|
||||||
|
Some(dioxus_html::input_data::MouseButton::Primary),
|
||||||
|
);
|
||||||
|
RECEIVED_EVENTS.with_mut(|x| *x += 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_mouse_scroll_div() -> Element {
|
||||||
|
utils::mock_event(
|
||||||
"wheel_div",
|
"wheel_div",
|
||||||
r#"new WheelEvent("wheel", {
|
r#"new WheelEvent("wheel", {
|
||||||
view: window,
|
view: window,
|
||||||
@@ -132,8 +264,26 @@ fn app() -> Element {
|
|||||||
bubbles: true,
|
bubbles: true,
|
||||||
})"#,
|
})"#,
|
||||||
);
|
);
|
||||||
// key_down_div
|
|
||||||
mock_event(
|
rsx! {
|
||||||
|
div {
|
||||||
|
id: "wheel_div",
|
||||||
|
width: "100px",
|
||||||
|
height: "100px",
|
||||||
|
background_color: "red",
|
||||||
|
onwheel: move |event| {
|
||||||
|
println!("{:?}", event.data);
|
||||||
|
let dioxus_html::geometry::WheelDelta::Pixels(delta) = event.data.delta() else {
|
||||||
|
panic!("Expected delta to be in pixels") };
|
||||||
|
assert_eq!(delta, Vector3D::new(1.0, 2.0, 3.0));
|
||||||
|
RECEIVED_EVENTS.with_mut(|x| *x += 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_key_down_div() -> Element {
|
||||||
|
utils::mock_event(
|
||||||
"key_down_div",
|
"key_down_div",
|
||||||
r#"new KeyboardEvent("keydown", {
|
r#"new KeyboardEvent("keydown", {
|
||||||
key: "a",
|
key: "a",
|
||||||
@@ -153,8 +303,24 @@ fn app() -> Element {
|
|||||||
bubbles: true,
|
bubbles: true,
|
||||||
})"#,
|
})"#,
|
||||||
);
|
);
|
||||||
// key_up_div
|
rsx! {
|
||||||
mock_event(
|
input {
|
||||||
|
id: "key_down_div",
|
||||||
|
onkeydown: move |event| {
|
||||||
|
println!("{:?}", event.data);
|
||||||
|
assert!(event.data.modifiers().is_empty());
|
||||||
|
assert_eq!(event.data.key().to_string(), "a");
|
||||||
|
assert_eq!(event.data.code().to_string(), "KeyA");
|
||||||
|
assert_eq!(event.data.location(), Location::Standard);
|
||||||
|
assert!(event.data.is_auto_repeating());
|
||||||
|
assert!(event.data.is_composing());
|
||||||
|
RECEIVED_EVENTS.with_mut(|x| *x += 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn test_key_up_div() -> Element {
|
||||||
|
utils::mock_event(
|
||||||
"key_up_div",
|
"key_up_div",
|
||||||
r#"new KeyboardEvent("keyup", {
|
r#"new KeyboardEvent("keyup", {
|
||||||
key: "a",
|
key: "a",
|
||||||
@@ -174,8 +340,25 @@ fn app() -> Element {
|
|||||||
bubbles: true,
|
bubbles: true,
|
||||||
})"#,
|
})"#,
|
||||||
);
|
);
|
||||||
// key_press_div
|
|
||||||
mock_event(
|
rsx! {
|
||||||
|
input {
|
||||||
|
id: "key_up_div",
|
||||||
|
onkeyup: move |event| {
|
||||||
|
println!("{:?}", event.data);
|
||||||
|
assert!(event.data.modifiers().is_empty());
|
||||||
|
assert_eq!(event.data.key().to_string(), "a");
|
||||||
|
assert_eq!(event.data.code().to_string(), "KeyA");
|
||||||
|
assert_eq!(event.data.location(), Location::Standard);
|
||||||
|
assert!(!event.data.is_auto_repeating());
|
||||||
|
assert!(!event.data.is_composing());
|
||||||
|
RECEIVED_EVENTS.with_mut(|x| *x += 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn test_key_press_div() -> Element {
|
||||||
|
utils::mock_event(
|
||||||
"key_press_div",
|
"key_press_div",
|
||||||
r#"new KeyboardEvent("keypress", {
|
r#"new KeyboardEvent("keypress", {
|
||||||
key: "a",
|
key: "a",
|
||||||
@@ -195,197 +378,94 @@ fn app() -> Element {
|
|||||||
bubbles: true,
|
bubbles: true,
|
||||||
})"#,
|
})"#,
|
||||||
);
|
);
|
||||||
// focus_in_div
|
|
||||||
mock_event(
|
|
||||||
"focus_in_div",
|
|
||||||
r#"new FocusEvent("focusin", {bubbles: true})"#,
|
|
||||||
);
|
|
||||||
// focus_out_div
|
|
||||||
mock_event(
|
|
||||||
"focus_out_div",
|
|
||||||
r#"new FocusEvent("focusout",{bubbles: true})"#,
|
|
||||||
);
|
|
||||||
|
|
||||||
if received_events() == 13 {
|
|
||||||
println!("all events recieved");
|
|
||||||
desktop_context.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
rsx! {
|
rsx! {
|
||||||
div {
|
input {
|
||||||
div {
|
id: "key_press_div",
|
||||||
width: "100px",
|
onkeypress: move |event| {
|
||||||
height: "100px",
|
println!("{:?}", event.data);
|
||||||
onmounted: move |evt| async move {
|
assert!(event.data.modifiers().is_empty());
|
||||||
let rect = evt.get_client_rect().await.unwrap();
|
assert_eq!(event.data.key().to_string(), "a");
|
||||||
println!("rect: {:?}", rect);
|
assert_eq!(event.data.code().to_string(), "KeyA");
|
||||||
assert_eq!(rect.width(), 100.0);
|
assert_eq!(event.data.location(), Location::Standard);
|
||||||
assert_eq!(rect.height(), 100.0);
|
assert!(!event.data.is_auto_repeating());
|
||||||
received_events.with_mut(|x| *x += 1);
|
assert!(!event.data.is_composing());
|
||||||
}
|
RECEIVED_EVENTS.with_mut(|x| *x += 1);
|
||||||
}
|
}
|
||||||
button {
|
}
|
||||||
id: "button",
|
}
|
||||||
onclick: move |event| {
|
}
|
||||||
println!("{:?}", event.data);
|
|
||||||
assert!(event.data.modifiers().is_empty());
|
fn test_focus_in_div() -> Element {
|
||||||
assert!(event.data.held_buttons().is_empty());
|
utils::mock_event(
|
||||||
assert_eq!(
|
"focus_in_div",
|
||||||
event.data.trigger_button(),
|
r#"new FocusEvent("focusin", {bubbles: true})"#,
|
||||||
Some(dioxus_html::input_data::MouseButton::Primary),
|
);
|
||||||
);
|
|
||||||
received_events.with_mut(|x| *x += 1);
|
rsx! {
|
||||||
}
|
input {
|
||||||
|
id: "focus_in_div",
|
||||||
|
onfocusin: move |event| {
|
||||||
|
println!("{:?}", event.data);
|
||||||
|
RECEIVED_EVENTS.with_mut(|x| *x += 1);
|
||||||
}
|
}
|
||||||
div {
|
}
|
||||||
id: "mouse_move_div",
|
}
|
||||||
onmousemove: move |event| {
|
}
|
||||||
println!("{:?}", event.data);
|
|
||||||
assert!(event.data.modifiers().is_empty());
|
fn test_focus_out_div() -> Element {
|
||||||
assert!(
|
utils::mock_event(
|
||||||
event
|
"focus_out_div",
|
||||||
.data
|
r#"new FocusEvent("focusout",{bubbles: true})"#,
|
||||||
.held_buttons()
|
);
|
||||||
.contains(dioxus_html::input_data::MouseButton::Secondary),
|
rsx! {
|
||||||
);
|
input {
|
||||||
received_events.with_mut(|x| *x += 1);
|
id: "focus_out_div",
|
||||||
}
|
onfocusout: move |event| {
|
||||||
|
println!("{:?}", event.data);
|
||||||
|
RECEIVED_EVENTS.with_mut(|x| *x += 1);
|
||||||
}
|
}
|
||||||
div {
|
}
|
||||||
id: "mouse_click_div",
|
}
|
||||||
onclick: move |event| {
|
}
|
||||||
println!("{:?}", event.data);
|
|
||||||
assert!(event.data.modifiers().is_empty());
|
fn test_form_input() -> Element {
|
||||||
assert!(
|
let mut values = use_signal(|| HashMap::new());
|
||||||
event
|
|
||||||
.data
|
utils::mock_event_with_extra(
|
||||||
.held_buttons()
|
"form-username",
|
||||||
.contains(dioxus_html::input_data::MouseButton::Secondary),
|
r#"new Event("input", {
|
||||||
);
|
bubbles: true,
|
||||||
assert_eq!(
|
cancelable: true,
|
||||||
event.data.trigger_button(),
|
composed: true,
|
||||||
Some(dioxus_html::input_data::MouseButton::Secondary),
|
})"#,
|
||||||
);
|
r#"element.value = "hello";"#,
|
||||||
received_events.with_mut(|x| *x += 1);
|
);
|
||||||
}
|
|
||||||
}
|
let set_values = move |ev: FormEvent| {
|
||||||
div {
|
values.set(ev.values());
|
||||||
id: "mouse_dblclick_div",
|
eprintln!("values: {:?}", values);
|
||||||
ondoubleclick: move |event| {
|
values.with_mut(|x| {
|
||||||
println!("{:?}", event.data);
|
assert_eq!(x.get("username").unwrap().deref(), &["hello"]);
|
||||||
assert!(event.data.modifiers().is_empty());
|
});
|
||||||
assert!(
|
};
|
||||||
event.data.held_buttons().contains(dioxus_html::input_data::MouseButton::Primary),
|
|
||||||
);
|
rsx! {
|
||||||
assert!(
|
div {
|
||||||
event
|
h1 { "Form" }
|
||||||
.data
|
form {
|
||||||
.held_buttons()
|
id: "form",
|
||||||
.contains(dioxus_html::input_data::MouseButton::Secondary),
|
oninput: move |ev| values.set(ev.values()),
|
||||||
);
|
input {
|
||||||
assert_eq!(
|
r#type: "text",
|
||||||
event.data.trigger_button(),
|
name: "username",
|
||||||
Some(dioxus_html::input_data::MouseButton::Secondary),
|
id: "form-username",
|
||||||
);
|
oninput: set_values,
|
||||||
received_events.with_mut(|x| *x += 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
div {
|
|
||||||
id: "mouse_down_div",
|
|
||||||
onmousedown: move |event| {
|
|
||||||
println!("{:?}", event.data);
|
|
||||||
assert!(event.data.modifiers().is_empty());
|
|
||||||
assert!(
|
|
||||||
event
|
|
||||||
.data
|
|
||||||
.held_buttons()
|
|
||||||
.contains(dioxus_html::input_data::MouseButton::Secondary),
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
event.data.trigger_button(),
|
|
||||||
Some(dioxus_html::input_data::MouseButton::Secondary),
|
|
||||||
);
|
|
||||||
received_events.with_mut(|x| *x += 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
div {
|
|
||||||
id: "mouse_up_div",
|
|
||||||
onmouseup: move |event| {
|
|
||||||
println!("{:?}", event.data);
|
|
||||||
assert!(event.data.modifiers().is_empty());
|
|
||||||
assert!(event.data.held_buttons().is_empty());
|
|
||||||
assert_eq!(
|
|
||||||
event.data.trigger_button(),
|
|
||||||
Some(dioxus_html::input_data::MouseButton::Primary),
|
|
||||||
);
|
|
||||||
received_events.with_mut(|x| *x += 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
div {
|
|
||||||
id: "wheel_div",
|
|
||||||
width: "100px",
|
|
||||||
height: "100px",
|
|
||||||
background_color: "red",
|
|
||||||
onwheel: move |event| {
|
|
||||||
println!("{:?}", event.data);
|
|
||||||
let dioxus_html::geometry::WheelDelta::Pixels(delta) = event.data.delta() else {
|
|
||||||
panic!("Expected delta to be in pixels") };
|
|
||||||
assert_eq!(delta, Vector3D::new(1.0, 2.0, 3.0));
|
|
||||||
received_events.with_mut(|x| *x += 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
input {
|
|
||||||
id: "key_down_div",
|
|
||||||
onkeydown: move |event| {
|
|
||||||
println!("{:?}", event.data);
|
|
||||||
assert!(event.data.modifiers().is_empty());
|
|
||||||
assert_eq!(event.data.key().to_string(), "a");
|
|
||||||
assert_eq!(event.data.code().to_string(), "KeyA");
|
|
||||||
assert_eq!(event.data.location(), Location::Standard);
|
|
||||||
assert!(event.data.is_auto_repeating());
|
|
||||||
assert!(event.data.is_composing());
|
|
||||||
received_events.with_mut(|x| *x += 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
input {
|
|
||||||
id: "key_up_div",
|
|
||||||
onkeyup: move |event| {
|
|
||||||
println!("{:?}", event.data);
|
|
||||||
assert!(event.data.modifiers().is_empty());
|
|
||||||
assert_eq!(event.data.key().to_string(), "a");
|
|
||||||
assert_eq!(event.data.code().to_string(), "KeyA");
|
|
||||||
assert_eq!(event.data.location(), Location::Standard);
|
|
||||||
assert!(!event.data.is_auto_repeating());
|
|
||||||
assert!(!event.data.is_composing());
|
|
||||||
received_events.with_mut(|x| *x += 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
input {
|
|
||||||
id: "key_press_div",
|
|
||||||
onkeypress: move |event| {
|
|
||||||
println!("{:?}", event.data);
|
|
||||||
assert!(event.data.modifiers().is_empty());
|
|
||||||
assert_eq!(event.data.key().to_string(), "a");
|
|
||||||
assert_eq!(event.data.code().to_string(), "KeyA");
|
|
||||||
assert_eq!(event.data.location(), Location::Standard);
|
|
||||||
assert!(!event.data.is_auto_repeating());
|
|
||||||
assert!(!event.data.is_composing());
|
|
||||||
received_events.with_mut(|x| *x += 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
input {
|
|
||||||
id: "focus_in_div",
|
|
||||||
onfocusin: move |event| {
|
|
||||||
println!("{:?}", event.data);
|
|
||||||
received_events.with_mut(|x| *x += 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
input {
|
|
||||||
id: "focus_out_div",
|
|
||||||
onfocusout: move |event| {
|
|
||||||
println!("{:?}", event.data);
|
|
||||||
received_events.with_mut(|x| *x += 1);
|
|
||||||
}
|
}
|
||||||
|
input { r#type: "text", name: "full-name" }
|
||||||
|
input { r#type: "password", name: "password" }
|
||||||
|
input { r#type: "radio", name: "color", value: "red" }
|
||||||
|
input { r#type: "radio", name: "color", value: "blue" }
|
||||||
|
button { r#type: "submit", value: "Submit", "Submit the form" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,28 +2,11 @@ use dioxus::prelude::*;
|
|||||||
use dioxus_core::Element;
|
use dioxus_core::Element;
|
||||||
use dioxus_desktop::DesktopContext;
|
use dioxus_desktop::DesktopContext;
|
||||||
|
|
||||||
|
#[path = "./utils.rs"]
|
||||||
|
mod utils;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
check_app_exits(check_html_renders);
|
utils::check_app_exits(check_html_renders);
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn check_app_exits(app: fn() -> Element) {
|
|
||||||
use dioxus_desktop::Config;
|
|
||||||
use tao::window::WindowBuilder;
|
|
||||||
// This is a deadman's switch to ensure that the app exits
|
|
||||||
let should_panic = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(true));
|
|
||||||
let should_panic_clone = should_panic.clone();
|
|
||||||
std::thread::spawn(move || {
|
|
||||||
std::thread::sleep(std::time::Duration::from_secs(5));
|
|
||||||
if should_panic_clone.load(std::sync::atomic::Ordering::SeqCst) {
|
|
||||||
std::process::exit(exitcode::SOFTWARE);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
LaunchBuilder::desktop()
|
|
||||||
.with_cfg(Config::new().with_window(WindowBuilder::new().with_visible(true)))
|
|
||||||
.launch(app);
|
|
||||||
|
|
||||||
should_panic.store(false, std::sync::atomic::Ordering::SeqCst);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn use_inner_html(id: &'static str) -> Option<String> {
|
fn use_inner_html(id: &'static str) -> Option<String> {
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
use dioxus::prelude::*;
|
||||||
|
use dioxus_core::Element;
|
||||||
|
|
||||||
|
pub fn check_app_exits(app: fn() -> Element) {
|
||||||
|
use dioxus_desktop::tao::window::WindowBuilder;
|
||||||
|
use dioxus_desktop::Config;
|
||||||
|
// This is a deadman's switch to ensure that the app exits
|
||||||
|
let should_panic = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(true));
|
||||||
|
let should_panic_clone = should_panic.clone();
|
||||||
|
std::thread::spawn(move || {
|
||||||
|
std::thread::sleep(std::time::Duration::from_secs(60));
|
||||||
|
if should_panic_clone.load(std::sync::atomic::Ordering::SeqCst) {
|
||||||
|
eprintln!("App did not exit in time");
|
||||||
|
std::process::exit(exitcode::SOFTWARE);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
LaunchBuilder::desktop()
|
||||||
|
.with_cfg(Config::new().with_window(WindowBuilder::new().with_visible(true)))
|
||||||
|
.launch(app);
|
||||||
|
|
||||||
|
// Stop deadman's switch
|
||||||
|
should_panic.store(false, std::sync::atomic::Ordering::SeqCst);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub static EXPECTED_EVENTS: GlobalSignal<usize> = Signal::global(|| 0);
|
||||||
|
|
||||||
|
pub fn mock_event(id: &'static str, value: &'static str) {
|
||||||
|
mock_event_with_extra(id, value, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mock_event_with_extra(id: &'static str, value: &'static str, extra: &'static str) {
|
||||||
|
EXPECTED_EVENTS.with_mut(|x| *x += 1);
|
||||||
|
|
||||||
|
use_hook(move || {
|
||||||
|
spawn(async move {
|
||||||
|
tokio::time::sleep(std::time::Duration::from_millis(5000)).await;
|
||||||
|
|
||||||
|
let js = format!(
|
||||||
|
r#"
|
||||||
|
//console.log("ran");
|
||||||
|
// Dispatch a synthetic event
|
||||||
|
let event = {};
|
||||||
|
let element = document.getElementById('{}');
|
||||||
|
console.log(element, event);
|
||||||
|
{}
|
||||||
|
element.dispatchEvent(event);
|
||||||
|
"#,
|
||||||
|
value, id, extra
|
||||||
|
);
|
||||||
|
|
||||||
|
eval(&js).await.unwrap();
|
||||||
|
});
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -8,155 +8,182 @@ class InterpreterConfig {
|
|||||||
// method is not used by the web implementation
|
// method is not used by the web implementation
|
||||||
async function handler(event, name, bubbles, config) {
|
async function handler(event, name, bubbles, config) {
|
||||||
let target = event.target;
|
let target = event.target;
|
||||||
if (target != null) {
|
if (target == null) {
|
||||||
let preventDefaultRequests = null;
|
return;
|
||||||
// Some events can be triggered on text nodes, which don't have attributes
|
}
|
||||||
if (target instanceof Element) {
|
|
||||||
preventDefaultRequests = target.getAttribute(`dioxus-prevent-default`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event.type === "click") {
|
const realId = find_real_id(target);
|
||||||
// todo call prevent default if it's the right type of event
|
if (realId === null) {
|
||||||
if (config.intercept_link_redirects) {
|
return;
|
||||||
let a_element = target.closest("a");
|
}
|
||||||
if (a_element != null) {
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
let elementShouldPreventDefault =
|
prevent_defaults(event, target, config);
|
||||||
preventDefaultRequests && preventDefaultRequests.includes(`onclick`);
|
|
||||||
let aElementShouldPreventDefault = a_element.getAttribute(
|
let contents = await serialize_event(event);
|
||||||
`dioxus-prevent-default`
|
|
||||||
|
// TODO: this should be liveview only
|
||||||
|
if (
|
||||||
|
target.tagName === "INPUT" &&
|
||||||
|
(event.type === "change" || event.type === "input")
|
||||||
|
) {
|
||||||
|
const type = target.getAttribute("type");
|
||||||
|
|
||||||
|
if (type === "file") {
|
||||||
|
async function read_files() {
|
||||||
|
const files = target.files;
|
||||||
|
const file_contents = {};
|
||||||
|
|
||||||
|
for (let i = 0; i < files.length; i++) {
|
||||||
|
const file = files[i];
|
||||||
|
|
||||||
|
file_contents[file.name] = Array.from(
|
||||||
|
new Uint8Array(await file.arrayBuffer())
|
||||||
);
|
);
|
||||||
let linkShouldPreventDefault =
|
|
||||||
aElementShouldPreventDefault &&
|
|
||||||
aElementShouldPreventDefault.includes(`onclick`);
|
|
||||||
|
|
||||||
if (!elementShouldPreventDefault && !linkShouldPreventDefault) {
|
|
||||||
const href = a_element.getAttribute("href");
|
|
||||||
if (href !== "" && href !== null && href !== undefined) {
|
|
||||||
window.ipc.postMessage(
|
|
||||||
window.interpreter.serializeIpcMessage("browser_open", { href })
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
let file_engine = {
|
||||||
|
files: file_contents,
|
||||||
|
};
|
||||||
|
contents.files = file_engine;
|
||||||
|
|
||||||
// also prevent buttons from submitting
|
if (realId === null) {
|
||||||
if (target.tagName === "BUTTON" && event.type == "submit") {
|
return;
|
||||||
event.preventDefault();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const realId = find_real_id(target);
|
|
||||||
|
|
||||||
if (
|
|
||||||
preventDefaultRequests &&
|
|
||||||
preventDefaultRequests.includes(`on${event.type}`)
|
|
||||||
) {
|
|
||||||
event.preventDefault();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event.type === "submit") {
|
|
||||||
event.preventDefault();
|
|
||||||
}
|
|
||||||
|
|
||||||
let contents = await serialize_event(event);
|
|
||||||
|
|
||||||
// TODO: this should be liveview only
|
|
||||||
if (
|
|
||||||
target.tagName === "INPUT" &&
|
|
||||||
(event.type === "change" || event.type === "input")
|
|
||||||
) {
|
|
||||||
const type = target.getAttribute("type");
|
|
||||||
if (type === "file") {
|
|
||||||
async function read_files() {
|
|
||||||
const files = target.files;
|
|
||||||
const file_contents = {};
|
|
||||||
|
|
||||||
for (let i = 0; i < files.length; i++) {
|
|
||||||
const file = files[i];
|
|
||||||
|
|
||||||
file_contents[file.name] = Array.from(
|
|
||||||
new Uint8Array(await file.arrayBuffer())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
let file_engine = {
|
|
||||||
files: file_contents,
|
|
||||||
};
|
|
||||||
contents.files = file_engine;
|
|
||||||
|
|
||||||
if (realId === null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const message = window.interpreter.serializeIpcMessage("user_event", {
|
|
||||||
name: name,
|
|
||||||
element: parseInt(realId),
|
|
||||||
data: contents,
|
|
||||||
bubbles,
|
|
||||||
});
|
|
||||||
window.ipc.postMessage(message);
|
|
||||||
}
|
}
|
||||||
read_files();
|
const message = window.interpreter.serializeIpcMessage("user_event", {
|
||||||
return;
|
name: name,
|
||||||
|
element: parseInt(realId),
|
||||||
|
data: contents,
|
||||||
|
bubbles,
|
||||||
|
});
|
||||||
|
window.ipc.postMessage(message);
|
||||||
}
|
}
|
||||||
}
|
read_files();
|
||||||
|
|
||||||
if (
|
|
||||||
target.tagName === "FORM" &&
|
|
||||||
(event.type === "submit" || event.type === "input")
|
|
||||||
) {
|
|
||||||
const formData = new FormData(target);
|
|
||||||
|
|
||||||
for (let name of formData.keys()) {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
target.tagName === "SELECT" &&
|
|
||||||
event.type === "input"
|
|
||||||
) {
|
|
||||||
const selectData = target.options;
|
|
||||||
contents.values["options"] = [];
|
|
||||||
for (let i = 0; i < selectData.length; i++) {
|
|
||||||
let option = selectData[i];
|
|
||||||
if (option.selected) {
|
|
||||||
contents.values["options"].push(option.value.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (realId === null) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
window.ipc.postMessage(
|
}
|
||||||
window.interpreter.serializeIpcMessage("user_event", {
|
|
||||||
name: name,
|
if (
|
||||||
element: parseInt(realId),
|
target.tagName === "FORM" &&
|
||||||
data: contents,
|
(event.type === "submit" || event.type === "input")
|
||||||
bubbles,
|
) {
|
||||||
})
|
const formData = new FormData(target);
|
||||||
);
|
|
||||||
|
for (let name of formData.keys()) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
target.tagName === "SELECT" &&
|
||||||
|
event.type === "input"
|
||||||
|
) {
|
||||||
|
const selectData = target.options;
|
||||||
|
contents.values["options"] = [];
|
||||||
|
for (let i = 0; i < selectData.length; i++) {
|
||||||
|
let option = selectData[i];
|
||||||
|
if (option.selected) {
|
||||||
|
contents.values["options"].push(option.value.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.ipc.postMessage(
|
||||||
|
window.interpreter.serializeIpcMessage("user_event", {
|
||||||
|
name: name,
|
||||||
|
element: parseInt(realId),
|
||||||
|
data: contents,
|
||||||
|
bubbles,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do our best to prevent the default action of the event
|
||||||
|
// This should:
|
||||||
|
// - prevent form submissions from navigating
|
||||||
|
// - prevent anchor tags from navigating
|
||||||
|
// - prevent buttons from submitting forms
|
||||||
|
function prevent_defaults(event, target, config) {
|
||||||
|
let preventDefaultRequests = null;
|
||||||
|
|
||||||
|
// Some events can be triggered on text nodes, which don't have attributes
|
||||||
|
if (target instanceof Element) {
|
||||||
|
preventDefaultRequests = target.getAttribute(`dioxus-prevent-default`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (preventDefaultRequests && preventDefaultRequests.includes(`on${event.type}`)) {
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.type === "submit") {
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt to intercept if the event is a click
|
||||||
|
intercept_form_submit(event, target, config, preventDefaultRequests);
|
||||||
|
}
|
||||||
|
|
||||||
|
function intercept_form_submit(event, target, config, preventDefaultRequests) {
|
||||||
|
if (event.type !== "click") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo call prevent default if it's the right type of event
|
||||||
|
if (!config.intercept_link_redirects) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// prevent buttons in forms from submitting the form
|
||||||
|
if (target.tagName === "BUTTON" && event.type == "submit") {
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the target is an anchor tag, we want to intercept the click too, to prevent the browser from navigating
|
||||||
|
let a_element = target.closest("a");
|
||||||
|
|
||||||
|
if (a_element == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
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(
|
||||||
|
window.interpreter.serializeIpcMessage("browser_open", { href })
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function find_real_id(target) {
|
function find_real_id(target) {
|
||||||
let realId = null;
|
let realId = null;
|
||||||
|
|
||||||
if (target instanceof Element) {
|
if (target instanceof Element) {
|
||||||
realId = target.getAttribute(`data-dioxus-id`);
|
realId = target.getAttribute(`data-dioxus-id`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// walk the tree to find the real element
|
// walk the tree to find the real element
|
||||||
while (realId == null) {
|
while (realId == null) {
|
||||||
// we've reached the root we don't want to send an event
|
// we've reached the root we don't want to send an event
|
||||||
@@ -169,6 +196,7 @@ function find_real_id(target) {
|
|||||||
realId = target.getAttribute(`data-dioxus-id`);
|
realId = target.getAttribute(`data-dioxus-id`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return realId;
|
return realId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -421,7 +449,8 @@ async function serialize_event(event) {
|
|||||||
case "drop": {
|
case "drop": {
|
||||||
let files = null;
|
let files = null;
|
||||||
if (event.dataTransfer && event.dataTransfer.files) {
|
if (event.dataTransfer && event.dataTransfer.files) {
|
||||||
files = await serializeFileList(event.dataTransfer.files);
|
files = ["a", "b", "c"];
|
||||||
|
// files = await serializeFileList(event.dataTransfer.files);
|
||||||
}
|
}
|
||||||
|
|
||||||
return { mouse: get_mouse_data(event), files };
|
return { mouse: get_mouse_data(event), files };
|
||||||
|
|||||||
Reference in new issue
Block a user