yew_hooks/hooks/
use_drop.rs

1use std::rc::Rc;
2
3use web_sys::{DataTransfer, File};
4use yew::prelude::*;
5
6use super::{use_event, use_mut_latest};
7use crate::web_sys_ext::ClipboardEvent;
8
9/// Options for drop.
10#[derive(Default)]
11pub struct UseDropOptions {
12    /// Callback for file drops.
13    #[allow(clippy::type_complexity)]
14    pub onfiles: Option<Box<dyn FnMut(Vec<File>, DataTransfer)>>,
15    /// Callback for text drops.
16    pub ontext: Option<Box<dyn FnMut(String, DataTransfer)>>,
17    /// Callback for uri drops.
18    pub onuri: Option<Box<dyn FnMut(String, DataTransfer)>>,
19
20    /// Callback for `dragover`.
21    pub ondragover: Option<Box<dyn FnMut(DragEvent)>>,
22    /// Callback for `dragenter`.
23    pub ondragenter: Option<Box<dyn FnMut(DragEvent)>>,
24    /// Callback for `dragleave`.
25    pub ondragleave: Option<Box<dyn FnMut(DragEvent)>>,
26    /// Callback for `dragexit`.
27    pub ondragexit: Option<Box<dyn FnMut(DragEvent)>>,
28    /// Callback for `drop`.
29    pub ondrop: Option<Box<dyn FnMut(DragEvent)>>,
30    /// Callback for `paste`.
31    pub onpaste: Option<Box<dyn FnMut(ClipboardEvent)>>,
32}
33
34/// State handle for the [`use_drop`] hook.
35pub struct UseDropHandle {
36    /// State for whether is over the drop area.
37    pub over: UseStateHandle<bool>,
38    /// Latest files dropped.
39    pub files: UseStateHandle<Option<Vec<File>>>,
40    /// Latest text dropped.
41    pub text: UseStateHandle<Option<String>>,
42    /// Latest uri dropped.
43    pub uri: UseStateHandle<Option<String>>,
44}
45
46/// This hook tracks file, link and copy-paste drops.
47///
48/// # Example
49///
50/// ```rust
51/// # use yew::prelude::*;
52/// #
53/// use yew_hooks::prelude::*;
54///
55/// #[function_component(UseDrop)]
56/// fn drop() -> Html {
57///     let node = use_node_ref();
58///     let state = use_drop(node.clone());
59///
60///     html! {
61///         <div ref={node}>
62///             <p><b>{ " Files: " }</b></p>
63///             {if let Some(files) = &*state.files {
64///                 html! {for files.iter().map(|file| {
65///                     html! { <p> { file.name() }</p> }
66///                 })}
67///             } else {
68///                 html! {}
69///             }}
70///             <p><b>{ " Text: " }</b></p>
71///             {if let Some(text) = &*state.text {
72///                 html! {<p>{ text }</p>}
73///             } else {
74///                 html! {}
75///             }}
76///             <p><b>{ " Uri: " }</b></p>
77///             {if let Some(uri) = &*state.uri {
78///                 html! {<p>{ uri }</p>}
79///             } else {
80///                 html! {}
81///             }}
82///             <p>
83///                 { "Try to drag & drop or copy & paste something here, e.g. files, links or text" }
84///             </p>
85///         </div>
86///     }
87/// }
88/// ```
89#[hook]
90pub fn use_drop(node: NodeRef) -> UseDropHandle {
91    use_drop_with_options(node, UseDropOptions::default())
92}
93
94/// This hook tracks file, link and copy-paste drops.
95/// [`use_drop`] hook with options.
96///
97/// # Example
98///
99/// ```rust
100/// # use yew::prelude::*;
101/// #
102/// use yew_hooks::prelude::*;
103///
104/// #[function_component(UseDrop)]
105/// fn drop() -> Html {
106///     let node = use_node_ref();
107///     let state = use_drop_with_options(node.clone(), UseDropOptions {
108///         onfiles: Some(Box::new(move |files, data_transfer| {
109///             // Process files or data_transfer
110///         })),
111///         ..Default::default()
112///     });
113///
114///     html! {
115///         <div ref={node}>
116///             <p>
117///                 { "Try to drag & drop or copy & paste something here, e.g. files, links or text" }
118///             </p>
119///         </div>
120///     }
121/// }
122/// ```
123#[hook]
124pub fn use_drop_with_options(node: NodeRef, options: UseDropOptions) -> UseDropHandle {
125    let over = use_state(|| false);
126    let files = use_state(|| None);
127    let text = use_state(|| None);
128    let uri = use_state(|| None);
129
130    let onfiles_ref = use_mut_latest(options.onfiles);
131    let ontext_ref = use_mut_latest(options.ontext);
132    let onuri_ref = use_mut_latest(options.onuri);
133
134    let ondragover_ref = use_mut_latest(options.ondragover);
135    let ondragenter_ref = use_mut_latest(options.ondragenter);
136    let ondragleave_ref = use_mut_latest(options.ondragleave);
137    let ondragexit_ref = use_mut_latest(options.ondragexit);
138    let ondrop_ref = use_mut_latest(options.ondrop);
139    let onpaste_ref = use_mut_latest(options.onpaste);
140
141    {
142        let over = over.clone();
143        use_event(node.clone(), "dragover", move |e: DragEvent| {
144            e.prevent_default();
145            over.set(true);
146
147            let ondragover_ref = ondragover_ref.current();
148            let ondragover = &mut *ondragover_ref.borrow_mut();
149            if let Some(ondragover) = ondragover {
150                ondragover(e);
151            }
152        });
153    }
154
155    {
156        let over = over.clone();
157        use_event(node.clone(), "dragenter", move |e: DragEvent| {
158            e.prevent_default();
159            over.set(true);
160
161            let ondragenter_ref = ondragenter_ref.current();
162            let ondragenter = &mut *ondragenter_ref.borrow_mut();
163            if let Some(ondragenter) = ondragenter {
164                ondragenter(e);
165            }
166        });
167    }
168
169    {
170        let over = over.clone();
171        use_event(node.clone(), "dragleave", move |e: DragEvent| {
172            over.set(false);
173
174            let ondragleave_ref = ondragleave_ref.current();
175            let ondragleave = &mut *ondragleave_ref.borrow_mut();
176            if let Some(ondragleave) = ondragleave {
177                ondragleave(e);
178            }
179        });
180    }
181
182    {
183        let over = over.clone();
184        use_event(node.clone(), "dragexit", move |e: DragEvent| {
185            over.set(false);
186
187            let ondragexit_ref = ondragexit_ref.current();
188            let ondragexit = &mut *ondragexit_ref.borrow_mut();
189            if let Some(ondragexit) = ondragexit {
190                ondragexit(e);
191            }
192        });
193    }
194
195    let on_data_transfer = {
196        let uri = uri.clone();
197        let files = files.clone();
198        let text = text.clone();
199        let ontext_ref = ontext_ref.clone();
200        let onfiles_ref = onfiles_ref.clone();
201
202        Rc::new(move |data_transfer: DataTransfer| {
203            if let Ok(uri_data) = data_transfer.get_data("text/uri-list") {
204                if !uri_data.is_empty() {
205                    let onuri_ref = onuri_ref.current();
206                    let onuri = &mut *onuri_ref.borrow_mut();
207                    if let Some(onuri) = onuri {
208                        let uri_data = uri_data.clone();
209                        onuri(uri_data, data_transfer);
210                    }
211                    uri.set(Some(uri_data));
212                    return;
213                }
214            }
215
216            if let Some(files_list) = data_transfer.files() {
217                if files_list.length() > 0 {
218                    log::debug!("files_list");
219                    let mut files_vec = vec![];
220                    for index in 0..files_list.length() {
221                        if let Some(file) = files_list.item(index) {
222                            files_vec.push(file);
223                        }
224                    }
225
226                    let onfiles_ref = onfiles_ref.current();
227                    let onfiles = &mut *onfiles_ref.borrow_mut();
228                    if let Some(onfiles) = onfiles {
229                        let files_vec = files_vec.clone();
230                        onfiles(files_vec, data_transfer);
231                    }
232                    files.set(Some(files_vec));
233                    return;
234                }
235            }
236
237            if let Ok(text_data) = data_transfer.get_data("text") {
238                let ontext_ref = ontext_ref.current();
239                let ontext = &mut *ontext_ref.borrow_mut();
240                if let Some(ontext) = ontext {
241                    let text_data = text_data.clone();
242                    ontext(text_data, data_transfer);
243                }
244                text.set(Some(text_data));
245            }
246        })
247    };
248
249    {
250        let over = over.clone();
251        let on_data_transfer = on_data_transfer.clone();
252        use_event(node.clone(), "drop", move |e: DragEvent| {
253            e.prevent_default();
254            over.set(false);
255
256            if let Some(data_transfer) = e.data_transfer() {
257                on_data_transfer(data_transfer);
258            }
259
260            let ondrop_ref = ondrop_ref.current();
261            let ondrop = &mut *ondrop_ref.borrow_mut();
262            if let Some(ondrop) = ondrop {
263                ondrop(e);
264            }
265        });
266    }
267
268    {
269        let on_data_transfer = on_data_transfer.clone();
270        use_event(node, "paste", move |e: ClipboardEvent| {
271            if let Some(data_transfer) = e.clipboard_data() {
272                on_data_transfer(data_transfer);
273            }
274
275            let onpaste_ref = onpaste_ref.current();
276            let onpaste = &mut *onpaste_ref.borrow_mut();
277            if let Some(onpaste) = onpaste {
278                onpaste(e);
279            }
280        });
281    }
282
283    UseDropHandle {
284        over,
285        files,
286        text,
287        uri,
288    }
289}