Commit 5b9ef0e36746ad99245c953001fe9a2d4c7c0233
1 parent
b3ee7df8
Get preview and file info from file selector
Showing
7 changed files
with
215 additions
and
51 deletions
1 | 1 | .markdown { |
2 | - float: left; | |
3 | - padding: 1em; | |
4 | - border-radius: .5em; | |
5 | - border: 1px solid #ddd; | |
6 | - background: #f7f7f7; | |
2 | + float: left; | |
3 | + padding: 1em; | |
4 | + border-radius: .5em; | |
5 | + border: 1px solid #ddd; | |
6 | + background: #f7f7f7; | |
7 | 7 | } |
8 | 8 | |
9 | 9 | .markdown p { |
10 | - text-align: justify; | |
11 | - text-indent: .5em; | |
12 | - margin-block: .5em; | |
10 | + text-align: justify; | |
11 | + text-indent: .5em; | |
12 | + margin-block: .5em; | |
13 | 13 | } |
14 | 14 | |
15 | 15 | .markdown > div:first-child { |
16 | - position: fixed; | |
16 | + position: fixed; | |
17 | 17 | width: inherit; |
18 | 18 | z-index: 10; |
19 | 19 | } |
20 | 20 | |
21 | 21 | .markdown > div:first-child > div { |
22 | 22 | position: relative; |
23 | - left: .5em; | |
23 | + left: .5em; | |
24 | 24 | width: inherit; |
25 | 25 | } |
26 | 26 | |
27 | 27 | .markdown > div:first-child > pre { |
28 | - white-space: pre-wrap; | |
29 | - height: 15em; | |
28 | + white-space: pre-wrap; | |
29 | + height: 15em; | |
30 | 30 | width: calc(100% - 10px); |
31 | - overflow-y: auto; | |
32 | - overflow-x: hidden; | |
33 | - background: #ffffff; | |
34 | - border-radius: .35em; | |
35 | - border: 2px solid #bbb; | |
36 | - margin: 2px 0px; | |
31 | + overflow-y: auto; | |
32 | + overflow-x: hidden; | |
33 | + background: #ffffff; | |
34 | + border-radius: .35em; | |
35 | + border: 2px solid #bbb; | |
36 | + margin: 2px 0px; | |
37 | 37 | opacity: .95; |
38 | 38 | } |
39 | 39 | |
40 | 40 | .markdown > div:first-child > div > div { |
41 | - display: inline; | |
41 | + display: inline; | |
42 | 42 | } |
43 | 43 | |
44 | 44 | .markdown > div:first-child > div ul { |
45 | - height: 15em; | |
46 | - list-style-type: none; | |
47 | - margin: 2px 0px; | |
48 | - overflow-y: auto; | |
49 | - padding-left: 0px; | |
50 | - position: absolute; | |
51 | - scroll-behavior: auto; | |
52 | - top: 1.8em; | |
45 | + height: 15em; | |
46 | + list-style-type: none; | |
47 | + margin: 2px 0px; | |
48 | + overflow-y: auto; | |
49 | + padding-left: 0px; | |
50 | + position: absolute; | |
51 | + scroll-behavior: auto; | |
52 | + top: 1.8em; | |
53 | 53 | } |
54 | 54 | |
55 | 55 | .markdown blockquote { |
56 | - border-left: 5px solid #ccc; | |
57 | - margin: .5em 10px; | |
58 | - padding: .5em 10px; | |
56 | + border-left: 5px solid #ccc; | |
57 | + margin: .5em 10px; | |
58 | + padding: .5em 10px; | |
59 | 59 | } |
60 | 60 | |
61 | 61 | .markdown blockquote p { |
62 | - text-align: left; | |
63 | - text-indent: 0; | |
64 | - margin-block: 0; | |
62 | + text-align: left; | |
63 | + text-indent: 0; | |
64 | + margin-block: 0; | |
65 | 65 | } |
66 | 66 | |
67 | 67 | .markdown .footnote-definition > * { |
68 | - display: inline; | |
68 | + display: inline; | |
69 | +} | |
70 | + | |
71 | +@keyframes spinner { | |
72 | + 0% { | |
73 | + transform: translate3d(-50%, -50%, 0) rotate(0deg); | |
74 | + } | |
75 | + 100% { | |
76 | + transform: translate3d(-50%, -50%, 0) rotate(360deg); | |
77 | + } | |
78 | +} | |
79 | + | |
80 | +.spin::before { | |
81 | + animation: 1.5s linear infinite spinner; | |
82 | + animation-play-state: inherit; | |
83 | + border: solid 5px #cfd0d1; | |
84 | + border-bottom-color: #1c87c9; | |
85 | + border-radius: 50%; | |
86 | + content: ""; | |
87 | + height: 1.5em; | |
88 | + width: 1.5em; | |
89 | + position: absolute; | |
90 | + top: 10%; | |
91 | + left: 1.5em; | |
92 | + transform: translate3d(-50%, -50%, 0); | |
93 | + will-change: transform; | |
69 | 94 | } | ... | ... |
... | ... | @@ -36,20 +36,28 @@ version = "^0.5" |
36 | 36 | [dependencies.web-sys] |
37 | 37 | version = "^0.3" |
38 | 38 | features = [ |
39 | - "Document", | |
40 | - "DomParser", | |
41 | - "Headers", | |
42 | - "HtmlElement", | |
43 | - "HtmlInputElement", | |
44 | - "MouseEvent", | |
45 | - "Node", | |
46 | - "Request", | |
47 | - "RequestInit", | |
48 | - "RequestMode", | |
49 | - "Response", | |
50 | - "SupportedType", | |
51 | - "Window", | |
52 | -] | |
39 | + "Blob", | |
40 | + "CanvasRenderingContext2d", | |
41 | + "Document", | |
42 | + "DomParser", | |
43 | + "File", | |
44 | + "FileList", | |
45 | + "FileReader", | |
46 | + "ImageBitmap", | |
47 | + "Headers", | |
48 | + "HtmlCanvasElement", | |
49 | + "HtmlElement", | |
50 | + "HtmlInputElement", | |
51 | + "MouseEvent", | |
52 | + "Node", | |
53 | + "ReadableStream", | |
54 | + "Request", | |
55 | + "RequestInit", | |
56 | + "RequestMode", | |
57 | + "Response", | |
58 | + "SupportedType", | |
59 | + "Window", | |
60 | + ] | |
53 | 61 | |
54 | 62 | [dev-dependencies] |
55 | 63 | wasm-bindgen-test = "0.2" | ... | ... |
ui/src/component/upload/logic.rs
0 → 100644
1 | +use mogwai::prelude::*; | |
2 | +use web_sys::{HtmlInputElement, window, ImageBitmap, HtmlCanvasElement, CanvasRenderingContext2d}; | |
3 | + | |
4 | +use crate::component::upload::view::upload_preview_view; | |
5 | + | |
6 | +async fn upload_preview_logic( mut rx_canvas :broadcast::Receiver<Dom> | |
7 | + , bitmap :ImageBitmap ) { | |
8 | + while let Some(dom) = rx_canvas.next().await { | |
9 | + match dom.inner_read() { | |
10 | + Either::Left(c) => { | |
11 | + let canvas = c.to_owned().dyn_into::<HtmlCanvasElement>().unwrap(); | |
12 | + let context = canvas | |
13 | + . get_context("2d").unwrap().unwrap() | |
14 | + . dyn_into::<CanvasRenderingContext2d>().unwrap(); | |
15 | + context . draw_image_with_image_bitmap_and_dw_and_dh( &bitmap | |
16 | + , 0.0, 0.0 | |
17 | + , 100.0, 100.0 ) | |
18 | + . unwrap(); | |
19 | + }, | |
20 | + _ => (), | |
21 | + } | |
22 | + } | |
23 | +} | |
24 | + | |
25 | +pub(super) async fn upload_logic( mut rx_logic: broadcast::Receiver<DomEvent> | |
26 | + , tx_previews: mpmc::Sender<ListPatch<ViewBuilder<Dom>>> | |
27 | + ) { | |
28 | + while let Some(msg) = rx_logic.next().await { | |
29 | + match msg.clone_inner() { | |
30 | + Either::Left(val) => { | |
31 | + let event = val.dyn_into::<Event>().unwrap(); | |
32 | + let target = event.target().unwrap(); | |
33 | + let element = target.dyn_into::<HtmlInputElement>().unwrap(); | |
34 | + let filelist = element.files().unwrap(); | |
35 | + | |
36 | + let mut previews = vec![]; | |
37 | + for index in 0..filelist.length() { | |
38 | + let (tx_canvas, rx_canvas) = broadcast::bounded(1); | |
39 | + let file = filelist.item(index).unwrap(); | |
40 | + let bitmap = | |
41 | + JsFuture::from( window().unwrap() | |
42 | + . create_image_bitmap_with_blob(&file.clone().into()).unwrap()); | |
43 | + let bitmap = bitmap | |
44 | + . await.unwrap() | |
45 | + . dyn_into::<ImageBitmap>().unwrap(); | |
46 | + | |
47 | + let view = upload_preview_view( tx_canvas | |
48 | + , file.name() | |
49 | + , file.size() | |
50 | + , file.type_() | |
51 | + , file.last_modified() ); | |
52 | + let logic = upload_preview_logic(rx_canvas, bitmap); | |
53 | + | |
54 | + previews.push(Component::from(view).with_logic(logic).into()); | |
55 | + } | |
56 | + let previews = ListPatch::splice(.., previews.into_iter()); | |
57 | + tx_previews.send(previews).await.unwrap(); | |
58 | + }, | |
59 | + _ => (), | |
60 | + } | |
61 | + } | |
62 | +} | ... | ... |
ui/src/component/upload/mod.rs
0 → 100644
1 | +mod view; | |
2 | +mod logic; | |
3 | + | |
4 | +use mogwai::prelude::*; | |
5 | + | |
6 | +use self::{view::upload_view, logic::upload_logic}; | |
7 | + | |
8 | +pub(crate) async fn new() -> Component<Dom> { | |
9 | + let (tx_logic, rx_logic) = broadcast::bounded(1); | |
10 | + let (tx_previews, rx_previews) = mpmc::bounded(1); | |
11 | + | |
12 | + let view = upload_view(tx_logic, rx_previews); | |
13 | + let logic = upload_logic(rx_logic, tx_previews); | |
14 | + | |
15 | + Component::from(view).with_logic(logic) | |
16 | +} | ... | ... |
ui/src/component/upload/view.rs
0 → 100644
1 | +use mogwai::prelude::*; | |
2 | + | |
3 | +pub(super) fn upload_preview_view( tx_canvas :broadcast::Sender<Dom> | |
4 | + , filename :String | |
5 | + , size :f64 | |
6 | + , mime_type :String | |
7 | + , mtime :f64 ) -> ViewBuilder<Dom> { | |
8 | + let post_build = move |dom: &mut Dom| { | |
9 | + tx_canvas.try_broadcast(dom.clone()).unwrap(); | |
10 | + }; | |
11 | + | |
12 | + builder! { | |
13 | + <li style:display="flex"> | |
14 | + <div style:width="100px" | |
15 | + style:height="100px"> | |
16 | + <canvas post:build=post_build /> | |
17 | + </div> | |
18 | + <div style:width="fit-content"> | |
19 | + <ul> | |
20 | + <li>{format!("filename: {}", filename)}</li> | |
21 | + <li>{format!("size: {}", size)}</li> | |
22 | + <li>{format!("mime type: {}", mime_type)}</li> | |
23 | + <li>{format!("modification time: {}", mtime)}</li> | |
24 | + </ul> | |
25 | + </div> | |
26 | + </li> | |
27 | + } | |
28 | +} | |
29 | + | |
30 | +pub(super) fn upload_view( tx_logic: broadcast::Sender<DomEvent> | |
31 | + , rx_previews: mpmc::Receiver<ListPatch<ViewBuilder<Dom>>> | |
32 | + ) -> ViewBuilder<Dom> { | |
33 | + builder! { | |
34 | + <div class="upload"> | |
35 | + <div class="spin"></div> | |
36 | + <div> | |
37 | + <input type="file" | |
38 | + multiple="multiple" | |
39 | + accept="image/*" | |
40 | + on:change=tx_logic.sink()/> | |
41 | + </div> | |
42 | + <ul patch:children=rx_previews> | |
43 | + </ul> | |
44 | + </div> | |
45 | + } | |
46 | +} | ... | ... |
... | ... | @@ -6,7 +6,7 @@ mod component; |
6 | 6 | |
7 | 7 | use std::panic; |
8 | 8 | |
9 | -use crate::component::markdown; | |
9 | +use crate::component::*; | |
10 | 10 | use log::Level; |
11 | 11 | use mogwai::prelude::*; |
12 | 12 | use wasm_bindgen::prelude::*; |
... | ... | @@ -16,8 +16,14 @@ pub async fn main() -> Result<(), JsValue> { |
16 | 16 | panic::set_hook(Box::new(console_error_panic_hook::hook)); |
17 | 17 | console_log::init_with_level(Level::Trace).unwrap(); |
18 | 18 | |
19 | - let comp = markdown::new().await; | |
19 | + let md = markdown::new().await; | |
20 | + let comp = upload::new().await; | |
20 | 21 | |
21 | - let page = Component::from(builder! {{comp}}); | |
22 | + let page = Component::from(builder! { | |
23 | + <div> | |
24 | + {comp} | |
25 | + {md} | |
26 | + </div> | |
27 | + }); | |
22 | 28 | page.build()?.run() |
23 | 29 | } | ... | ... |
Please
register
or
login
to post a comment