orbital_base_components/upload/
base.rs1use leptos::{ev, html, prelude::*};
2
3use crate::{overlay::dom_events::add_event_listener, Handler, UploadFileList};
4
5#[component]
7pub fn BaseUpload(
8 #[prop(optional, into)] class: MaybeProp<String>,
9 #[prop(optional, into)] id: MaybeProp<String>,
10 #[prop(optional, into)] name: MaybeProp<String>,
11 #[prop(optional, into)] accept: Signal<String>,
12 #[prop(optional, into)] multiple: Signal<bool>,
13 #[prop(into)] on_change: Handler<UploadFileList>,
14 children: Children,
15) -> impl IntoView {
16 let input_ref = NodeRef::<html::Input>::new();
17 let trigger_ref = NodeRef::<html::Div>::new();
18 let is_trigger_dragover = RwSignal::new(false);
19
20 Effect::new(move |_| {
21 let Some(trigger_el) = trigger_ref.get() else {
22 return;
23 };
24 let handle = add_event_listener(trigger_el, ev::click, move |_| {
25 if let Some(input_el) = input_ref.get_untracked() {
26 input_el.click();
27 }
28 });
29 on_cleanup(move || handle.remove());
30 });
31
32 let dispatch_files = {
33 let on_change = on_change.clone();
34 move |files: UploadFileList| {
35 on_change.run(files);
36 }
37 };
38
39 let on_input_change = {
40 let dispatch_files = dispatch_files.clone();
41 move |_| {
42 if let Some(input_el) = input_ref.get_untracked() {
43 if let Some(files) = input_el.files() {
44 dispatch_files(files);
45 }
46 input_el.set_value("");
47 }
48 }
49 };
50
51 let on_trigger_drop = {
52 let dispatch_files = dispatch_files.clone();
53 move |event: ev::DragEvent| {
54 event.prevent_default();
55 if let Some(data) = event.data_transfer() {
56 if let Some(files) = data.files() {
57 dispatch_files(files);
58 }
59 }
60 is_trigger_dragover.set(false);
61 }
62 };
63
64 let root_class = move || {
65 let mut parts = vec!["orbital-upload".to_string()];
66 if is_trigger_dragover.get() {
67 parts.push("orbital-upload--drag-over".to_string());
68 }
69 if let Some(extra) = class.get() {
70 let extra = extra.trim();
71 if !extra.is_empty() {
72 parts.push(extra.to_string());
73 }
74 }
75 parts.join(" ")
76 };
77
78 view! {
79 <div class=root_class>
80 <input
81 class="orbital-upload__input"
82 data-testid="upload-input"
83 aria-hidden="true"
84 tabindex="-1"
85 id=move || id.get()
86 name=move || name.get()
87 node_ref=input_ref
88 type="file"
89 accept=move || accept.get()
90 multiple=move || multiple.get()
91 on:change=on_input_change
92 />
93 <div
94 class="orbital-upload__trigger"
95 node_ref=trigger_ref
96 on:drop=on_trigger_drop
97 on:dragover=move |event: ev::DragEvent| {
98 event.prevent_default();
99 is_trigger_dragover.set(true);
100 }
101 on:dragenter=move |event: ev::DragEvent| event.prevent_default()
102 on:dragleave=move |event: ev::DragEvent| {
103 event.prevent_default();
104 is_trigger_dragover.set(false);
105 }
106 >
107 {children()}
108 </div>
109 </div>
110 }
111}