yew_hooks/hooks/
use_drag.rs

1use web_sys::Element;
2use yew::prelude::*;
3
4use super::{use_event, use_mut_latest};
5
6/// Options for drag.
7#[derive(Default)]
8pub struct UseDragOptions {
9    /// Callback for `dragstart`.
10    pub ondragstart: Option<Box<dyn FnMut(DragEvent)>>,
11    /// Callback for `dragend`.
12    pub ondragend: Option<Box<dyn FnMut(DragEvent)>>,
13}
14
15/// State handle for the [`use_drag`] hook.
16pub struct UseDragHandle {
17    /// State for whether is dragging.
18    pub dragging: UseStateHandle<bool>,
19}
20
21/// This hook tracks file, link and copy-paste drags.
22///
23/// # Example
24///
25/// ```rust
26/// # use yew::prelude::*;
27/// #
28/// use yew_hooks::prelude::*;
29///
30/// #[function_component(UseDrag)]
31/// fn drag() -> Html {
32///     let node = use_node_ref();
33///     let state = use_drag(node.clone());
34///
35///     html! {
36///         <div ref={node}>
37///             <p>
38///                 <b>{ " Dragging: " }</b>
39///                 { *state.dragging }
40///             </p>
41///             <p>
42///                 { "Try to drag this area" }
43///             </p>
44///         </div>
45///     }
46/// }
47/// ```
48#[hook]
49pub fn use_drag(node: NodeRef) -> UseDragHandle {
50    use_drag_with_options(node, UseDragOptions::default())
51}
52
53/// This hook tracks file, link and copy-paste drags.
54/// [`use_drag`] hook with options.
55///
56/// # Example
57///
58/// ```rust
59/// # use yew::prelude::*;
60/// #
61/// use yew_hooks::prelude::*;
62///
63/// #[function_component(UseDrag)]
64/// fn drag() -> Html {
65///     let node = use_node_ref();
66///     let state = use_drag_with_options(node.clone(), UseDragOptions {
67///         ondragstart: Some(Box::new(move |e| {
68///             if let Some(data_transfer) = e.data_transfer() {
69///                 let _ = data_transfer.set_data("text", "hello");
70///             }
71///         })),
72///         ondragend: Some(Box::new(move |e| {
73///         })),
74///     });
75///
76///     html! {
77///         <div ref={node}>
78///             <p>
79///                 <b>{ " Dragging: " }</b>
80///                 { *state.dragging }
81///             </p>
82///             <p>
83///                 { "Try to drag this area" }
84///             </p>
85///         </div>
86///     }
87/// }
88/// ```
89#[hook]
90pub fn use_drag_with_options(node: NodeRef, options: UseDragOptions) -> UseDragHandle {
91    let dragging = use_state(|| false);
92
93    let ondragstart_ref = use_mut_latest(options.ondragstart);
94    let ondragend_ref = use_mut_latest(options.ondragend);
95
96    {
97        let dragging = dragging.clone();
98        use_event(node.clone(), "dragstart", move |e: DragEvent| {
99            dragging.set(true);
100            let ondragstart_ref = ondragstart_ref.current();
101            let ondragstart = &mut *ondragstart_ref.borrow_mut();
102            if let Some(ondragstart) = ondragstart {
103                ondragstart(e);
104            }
105        });
106    }
107
108    {
109        let dragging = dragging.clone();
110        use_event(node.clone(), "dragend", move |e: DragEvent| {
111            dragging.set(false);
112            let ondragend_ref = ondragend_ref.current();
113            let ondragend = &mut *ondragend_ref.borrow_mut();
114            if let Some(ondragend) = ondragend {
115                ondragend(e);
116            }
117        });
118    }
119
120    use_effect_with(node, move |node| {
121        if let Some(element) = &node.cast::<Element>() {
122            let _ = element.set_attribute("draggable", "true");
123        }
124
125        || ()
126    });
127
128    UseDragHandle { dragging }
129}