Last active
September 6, 2022 22:59
-
-
Save rparrett/2137f02dcbc2bdc552d7ce3cdf3b68c7 to your computer and use it in GitHub Desktop.
Use FileReader to display uploaded text of text files dragged onto the seed-rs 0.6 drop zone example
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[package] | |
name = "drop_zone" | |
version = "0.1.0" | |
authors = ["David O'Connor <[email protected]>"] | |
edition = "2018" | |
[lib] | |
crate-type = ["cdylib"] | |
[dependencies] | |
seed = {path = "../../"} | |
wasm-bindgen = "0.2.55" | |
[dependencies.web-sys] | |
version = "0.3.32" | |
features = [ | |
"DataTransfer", | |
"DragEvent", | |
"File", | |
"FileList", | |
"FileReader" | |
] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use seed::{prelude::*, *}; | |
use wasm_bindgen::JsCast; | |
use web_sys::{self, DragEvent, Event, FileList, FileReader}; | |
// ------ ------ | |
// Model | |
// ------ ------ | |
struct Model { | |
drop_zone_active: bool, | |
drop_zone_content: Vec<Node<Msg>>, | |
uploaded_text: Vec<Node<Msg>>, | |
} | |
impl Default for Model { | |
fn default() -> Self { | |
Self { | |
drop_zone_active: false, | |
drop_zone_content: vec![div!["Drop files here"]], | |
uploaded_text: vec![div!["Uploaded text appears here"]] | |
} | |
} | |
} | |
// ------ ------ | |
// Update | |
// ------ ------ | |
enum Msg { | |
DragEnter, | |
DragOver, | |
DragLeave, | |
Drop(FileList), | |
Uploaded(String) | |
} | |
fn update(msg: Msg, model: &mut Model, orders: &mut impl Orders<Msg>) { | |
match msg { | |
Msg::DragEnter => model.drop_zone_active = true, | |
Msg::DragOver => (), | |
Msg::DragLeave => model.drop_zone_active = false, | |
Msg::Drop(file_list) => { | |
model.drop_zone_active = false; | |
// FileList is not an iterator, so instead we iterate over (0..len(FileList)) range. | |
// As `.item(index)` returns an `Option` we need to unwrap it. | |
model.drop_zone_content = (0..file_list.length()) | |
.map(|index| file_list.item(index).unwrap()) | |
.map(|file| div![file.name()]) | |
.collect(); | |
let (app, msg_mapper) = (orders.clone_app(), orders.msg_mapper()); | |
let file_reader = FileReader::new().unwrap(); | |
file_reader.read_as_text(&file_list.item(0).unwrap()).unwrap(); | |
let onload = Closure::wrap(Box::new(move |event: Event| { | |
let file_reader: FileReader = event.target().unwrap().dyn_into().unwrap(); | |
let text = file_reader.result().unwrap().as_string().unwrap(); | |
app.update(msg_mapper(Msg::Uploaded(text))); | |
}) as Box<dyn FnMut(_)>); | |
file_reader.set_onload(Some(onload.as_ref().unchecked_ref())); | |
onload.forget(); | |
}, | |
Msg::Uploaded(text) => { | |
model.uploaded_text = text | |
.split("\n") | |
.flat_map(|line| vec![Node::new_text(line.to_string()), br![]]) | |
.collect::<Vec<_>>(); | |
} | |
} | |
} | |
// ------ ------ | |
// View | |
// ------ ------ | |
trait IntoDragEvent { | |
fn into_drag_event(self) -> DragEvent; | |
} | |
impl IntoDragEvent for Event { | |
fn into_drag_event(self) -> DragEvent { | |
self.dyn_into::<web_sys::DragEvent>() | |
.expect("cannot cast given event into DragEvent") | |
} | |
} | |
// Note: It's macro so you can use it with all events. | |
macro_rules! stop_and_prevent { | |
{ $event:expr } => { | |
{ | |
$event.stop_propagation(); | |
$event.prevent_default(); | |
} | |
}; | |
} | |
fn view(model: &Model) -> impl View<Msg> { | |
div![ | |
div![ | |
style![ | |
St::Height => px(200), | |
St::Width => px(200), | |
St::Margin => "auto", | |
St::Background => if model.drop_zone_active { "lightgreen" } else { "lightgray" }, | |
St::FontFamily => "sans-serif", | |
St::Display => "flex", | |
St::FlexDirection => "column", | |
St::JustifyContent => "center", | |
St::AlignItems => "center", | |
St::Border => [&px(2), "dashed", "black"].join(" "); | |
St::BorderRadius => px(20), | |
], | |
ev(Ev::DragEnter, |event| { | |
stop_and_prevent!(event); | |
Msg::DragEnter | |
}), | |
ev(Ev::DragOver, |event| { | |
let drag_event = event.into_drag_event(); | |
stop_and_prevent!(drag_event); | |
drag_event.data_transfer().unwrap().set_drop_effect("copy"); | |
Msg::DragOver | |
}), | |
ev(Ev::DragLeave, |event| { | |
stop_and_prevent!(event); | |
Msg::DragLeave | |
}), | |
ev(Ev::Drop, |event| { | |
let drag_event = event.into_drag_event(); | |
stop_and_prevent!(drag_event); | |
let file_list = drag_event.data_transfer().unwrap().files().unwrap(); | |
Msg::Drop(file_list) | |
}), | |
div![ | |
style! { | |
// we don't want to fire `DragLeave` when we are dragging over drop-zone children | |
St::PointerEvents => "none", | |
}, | |
model.drop_zone_content.clone(), | |
], | |
], | |
div![ | |
model.uploaded_text.clone() | |
] | |
] | |
} | |
// ------ ------ | |
// Start | |
// ------ ------ | |
#[wasm_bindgen(start)] | |
pub fn start() { | |
App::builder(update, view).build_and_start(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment