Skip to content

Instantly share code, notes, and snippets.

@rparrett
Last active September 6, 2022 22:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rparrett/2137f02dcbc2bdc552d7ce3cdf3b68c7 to your computer and use it in GitHub Desktop.
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
[package]
name = "drop_zone"
version = "0.1.0"
authors = ["David O'Connor <david.alan.oconnor@gmail.com>"]
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"
]
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