diff --git a/examples/file_upload.rs b/examples/file_upload.rs index b23648b50..c7358c685 100644 --- a/examples/file_upload.rs +++ b/examples/file_upload.rs @@ -1,32 +1,47 @@ #![allow(non_snake_case)] use dioxus::prelude::*; +use tokio::time::sleep; fn main() { dioxus_desktop::launch(App); } fn App(cx: Scope) -> Element { + let enable_directory_upload = use_state(cx, || false); let files_uploaded: &UseRef> = use_ref(cx, Vec::new); cx.render(rsx! { + label { + input { + r#type: "checkbox", + checked: "{enable_directory_upload}", + oninput: move |evt| { + enable_directory_upload.set(evt.value.parse().unwrap()); + }, + }, + "Enable directory upload" + } + input { r#type: "file", - accept: ".txt, .rs", + accept: ".txt,.rs", multiple: true, + directory: **enable_directory_upload, onchange: |evt| { to_owned![files_uploaded]; async move { if let Some(file_engine) = &evt.files { let files = file_engine.files(); - for file_name in &files { - if let Some(file) = file_engine.read_file_to_string(file_name).await{ - files_uploaded.write().push(file); - } + for file_name in files { + sleep(std::time::Duration::from_secs(1)).await; + files_uploaded.write().push(file_name); } } } }, - } + }, + + div { "progress: {files_uploaded.read().len()}" }, ul { for file in files_uploaded.read().iter() { diff --git a/packages/desktop/src/file_upload.rs b/packages/desktop/src/file_upload.rs index d09783bcf..eab06c2cb 100644 --- a/packages/desktop/src/file_upload.rs +++ b/packages/desktop/src/file_upload.rs @@ -8,36 +8,24 @@ pub(crate) struct FileDialogRequest { #[serde(default)] accept: Option, multiple: bool, + directory: bool, pub event: String, pub target: usize, pub bubbles: bool, } -#[cfg(not(any( - target_os = "windows", - target_os = "macos", - target_os = "linux", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "netbsd", - target_os = "openbsd" -)))] -pub(crate) fn get_file_event(_request: &FileDialogRequest) -> Vec { - vec![] +fn get_file_event_for_folder(request: &FileDialogRequest, dialog: rfd::FileDialog) -> Vec { + if request.multiple { + dialog.pick_folders().into_iter().flatten().collect() + } else { + dialog.pick_folder().into_iter().collect() + } } -#[cfg(any( - target_os = "windows", - target_os = "macos", - target_os = "linux", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "netbsd", - target_os = "openbsd" -))] -pub(crate) fn get_file_event(request: &FileDialogRequest) -> Vec { - let mut dialog = rfd::FileDialog::new(); - +fn get_file_event_for_file( + request: &FileDialogRequest, + mut dialog: rfd::FileDialog, +) -> Vec { let filters: Vec<_> = request .accept .as_deref() @@ -62,6 +50,38 @@ pub(crate) fn get_file_event(request: &FileDialogRequest) -> Vec { files } +#[cfg(not(any( +target_os = "windows", +target_os = "macos", +target_os = "linux", +target_os = "dragonfly", +target_os = "freebsd", +target_os = "netbsd", +target_os = "openbsd" +)))] +pub(crate) fn get_file_event(_request: &FileDialogRequest) -> Vec { + vec![] +} + +#[cfg(any( +target_os = "windows", +target_os = "macos", +target_os = "linux", +target_os = "dragonfly", +target_os = "freebsd", +target_os = "netbsd", +target_os = "openbsd" +))] +pub(crate) fn get_file_event(request: &FileDialogRequest) -> Vec { + let dialog = rfd::FileDialog::new(); + + if request.directory { + get_file_event_for_folder(request, dialog) + } else { + get_file_event_for_file(request, dialog) + } +} + enum Filters { Extension(String), Mime(String), diff --git a/packages/desktop/src/protocol.rs b/packages/desktop/src/protocol.rs index 6be5d08b4..89ce50a48 100644 --- a/packages/desktop/src/protocol.rs +++ b/packages/desktop/src/protocol.rs @@ -24,7 +24,7 @@ fn module_loader(root_name: &str) -> String { let target_id = find_real_id(target); if (target_id !== null) { const send = (event_name) => { - const message = serializeIpcMessage("file_diolog", { accept: target.getAttribute("accept"), multiple: target.hasAttribute("multiple"), target: parseInt(target_id), bubbles: event_bubbles(event_name), event: event_name }); + const message = serializeIpcMessage("file_diolog", { accept: target.getAttribute("accept"), directory: target.getAttribute("webkitdirectory") === "true", multiple: target.hasAttribute("multiple"), target: parseInt(target_id), bubbles: event_bubbles(event_name), event: event_name }); window.ipc.postMessage(message); }; send("change&input"); diff --git a/packages/html/src/elements.rs b/packages/html/src/elements.rs index e1e80057e..514aea2d1 100644 --- a/packages/html/src/elements.rs +++ b/packages/html/src/elements.rs @@ -1098,6 +1098,7 @@ builder_constructors! { autofocus: Bool DEFAULT, capture: String DEFAULT, checked: Bool DEFAULT, + directory: Bool "webkitdirectory", disabled: Bool DEFAULT, form: Id DEFAULT, formaction: Uri DEFAULT, diff --git a/packages/interpreter/src/common.js b/packages/interpreter/src/common.js index c93f9eb60..1583da10e 100644 --- a/packages/interpreter/src/common.js +++ b/packages/interpreter/src/common.js @@ -25,6 +25,7 @@ const bool_attrs = { reversed: true, selected: true, truespeed: true, + webkitdirectory: true, }; export function setAttributeInner(node, field, value, ns) { diff --git a/packages/interpreter/src/sledgehammer_bindings.rs b/packages/interpreter/src/sledgehammer_bindings.rs index e661a7c16..2eb883500 100644 --- a/packages/interpreter/src/sledgehammer_bindings.rs +++ b/packages/interpreter/src/sledgehammer_bindings.rs @@ -163,6 +163,7 @@ mod js { reversed: true, selected: true, truespeed: true, + webkitdirectory: true, }; function truthy(val) { return val === "true" || val === true;