Commit 5b9ef0e36746ad99245c953001fe9a2d4c7c0233
1 parent
b3ee7df8
Get preview and file info from file selector
Showing
7 changed files
with
215 additions
and
51 deletions
1 | .markdown { | 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 | .markdown p { | 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 | .markdown > div:first-child { | 15 | .markdown > div:first-child { |
16 | - position: fixed; | 16 | + position: fixed; |
17 | width: inherit; | 17 | width: inherit; |
18 | z-index: 10; | 18 | z-index: 10; |
19 | } | 19 | } |
20 | 20 | ||
21 | .markdown > div:first-child > div { | 21 | .markdown > div:first-child > div { |
22 | position: relative; | 22 | position: relative; |
23 | - left: .5em; | 23 | + left: .5em; |
24 | width: inherit; | 24 | width: inherit; |
25 | } | 25 | } |
26 | 26 | ||
27 | .markdown > div:first-child > pre { | 27 | .markdown > div:first-child > pre { |
28 | - white-space: pre-wrap; | ||
29 | - height: 15em; | 28 | + white-space: pre-wrap; |
29 | + height: 15em; | ||
30 | width: calc(100% - 10px); | 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 | opacity: .95; | 37 | opacity: .95; |
38 | } | 38 | } |
39 | 39 | ||
40 | .markdown > div:first-child > div > div { | 40 | .markdown > div:first-child > div > div { |
41 | - display: inline; | 41 | + display: inline; |
42 | } | 42 | } |
43 | 43 | ||
44 | .markdown > div:first-child > div ul { | 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 | .markdown blockquote { | 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 | .markdown blockquote p { | 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 | .markdown .footnote-definition > * { | 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,20 +36,28 @@ version = "^0.5" | ||
36 | [dependencies.web-sys] | 36 | [dependencies.web-sys] |
37 | version = "^0.3" | 37 | version = "^0.3" |
38 | features = [ | 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 | [dev-dependencies] | 62 | [dev-dependencies] |
55 | wasm-bindgen-test = "0.2" | 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,7 +6,7 @@ mod component; | ||
6 | 6 | ||
7 | use std::panic; | 7 | use std::panic; |
8 | 8 | ||
9 | -use crate::component::markdown; | 9 | +use crate::component::*; |
10 | use log::Level; | 10 | use log::Level; |
11 | use mogwai::prelude::*; | 11 | use mogwai::prelude::*; |
12 | use wasm_bindgen::prelude::*; | 12 | use wasm_bindgen::prelude::*; |
@@ -16,8 +16,14 @@ pub async fn main() -> Result<(), JsValue> { | @@ -16,8 +16,14 @@ pub async fn main() -> Result<(), JsValue> { | ||
16 | panic::set_hook(Box::new(console_error_panic_hook::hook)); | 16 | panic::set_hook(Box::new(console_error_panic_hook::hook)); |
17 | console_log::init_with_level(Level::Trace).unwrap(); | 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 | page.build()?.run() | 28 | page.build()?.run() |
23 | } | 29 | } |
Please
register
or
login
to post a comment