leptos_use/
use_drop_zone.rs1use crate::core::IntoElementMaybeSignal;
2use cfg_if::cfg_if;
3use default_struct_builder::DefaultBuilder;
4use leptos::prelude::*;
5use leptos::reactive::wrappers::read::Signal;
6use send_wrapper::SendWrapper;
7use std::fmt::{Debug, Formatter};
8use std::sync::Arc;
9
10cfg_if! { if #[cfg(not(feature = "ssr"))] {
11 use crate::use_event_listener;
12 use leptos::ev::{dragenter, dragleave, dragover, drop};
13}}
14
15pub fn use_drop_zone<El, M>(target: El) -> UseDropZoneReturn
59where
60 El: IntoElementMaybeSignal<web_sys::EventTarget, M>,
61{
62 use_drop_zone_with_options(target, UseDropZoneOptions::default())
63}
64
65#[cfg_attr(feature = "ssr", allow(unused_variables))]
67pub fn use_drop_zone_with_options<El, M>(
68 target: El,
69 options: UseDropZoneOptions,
70) -> UseDropZoneReturn
71where
72 El: IntoElementMaybeSignal<web_sys::EventTarget, M>,
73{
74 let (is_over_drop_zone, set_over_drop_zone) = signal(false);
75 let (files, set_files) = signal(Vec::<SendWrapper<web_sys::File>>::new());
76
77 #[cfg(not(feature = "ssr"))]
78 {
79 use std::ops::Deref;
80
81 let UseDropZoneOptions {
82 on_drop,
83 on_enter,
84 on_leave,
85 on_over,
86 } = options;
87
88 let counter = StoredValue::new(0_usize);
89
90 let update_files = move |event: &web_sys::DragEvent| {
91 if let Some(data_transfer) = event.data_transfer() {
92 let files: Vec<_> = data_transfer
93 .files()
94 .map(|f| js_sys::Array::from(&f).to_vec())
95 .unwrap_or_default()
96 .into_iter()
97 .map(web_sys::File::from)
98 .map(SendWrapper::new)
99 .collect();
100
101 set_files.update(move |f| *f = files);
102 }
103 };
104
105 let target = target.into_element_maybe_signal();
106
107 let use_drop_zone_event = move |event| UseDropZoneEvent {
108 files: files
109 .read_untracked()
110 .iter()
111 .map(|f| f.deref().clone())
112 .collect(),
113 event,
114 };
115
116 let _ = use_event_listener(target, dragenter, move |event| {
117 event.prevent_default();
118 counter.update_value(|counter| *counter += 1);
119 set_over_drop_zone.set(true);
120
121 update_files(&event);
122
123 #[cfg(debug_assertions)]
124 let _z = leptos::reactive::diagnostics::SpecialNonReactiveZone::enter();
125
126 on_enter(use_drop_zone_event(event));
127 });
128
129 let _ = use_event_listener(target, dragover, move |event| {
130 event.prevent_default();
131 update_files(&event);
132
133 #[cfg(debug_assertions)]
134 let _z = leptos::reactive::diagnostics::SpecialNonReactiveZone::enter();
135
136 on_over(use_drop_zone_event(event));
137 });
138
139 let _ = use_event_listener(target, dragleave, move |event| {
140 event.prevent_default();
141 counter.update_value(|counter| *counter -= 1);
142 if counter.get_value() == 0 {
143 set_over_drop_zone.set(false);
144 }
145
146 update_files(&event);
147
148 #[cfg(debug_assertions)]
149 let _z = leptos::reactive::diagnostics::SpecialNonReactiveZone::enter();
150
151 on_leave(use_drop_zone_event(event));
152 });
153
154 let _ = use_event_listener(target, drop, move |event| {
155 event.prevent_default();
156 counter.update_value(|counter| *counter = 0);
157 set_over_drop_zone.set(false);
158
159 update_files(&event);
160
161 #[cfg(debug_assertions)]
162 let _z = leptos::reactive::diagnostics::SpecialNonReactiveZone::enter();
163
164 on_drop(use_drop_zone_event(event));
165 });
166 }
167
168 UseDropZoneReturn {
169 files: files.into(),
170 is_over_drop_zone: is_over_drop_zone.into(),
171 }
172}
173
174#[derive(DefaultBuilder, Clone)]
176#[cfg_attr(feature = "ssr", allow(dead_code))]
177pub struct UseDropZoneOptions {
178 on_drop: Arc<dyn Fn(UseDropZoneEvent) + Send + Sync>,
180 on_enter: Arc<dyn Fn(UseDropZoneEvent) + Send + Sync>,
182 on_leave: Arc<dyn Fn(UseDropZoneEvent) + Send + Sync>,
184 on_over: Arc<dyn Fn(UseDropZoneEvent) + Send + Sync>,
186}
187
188impl Default for UseDropZoneOptions {
189 fn default() -> Self {
190 Self {
191 on_drop: Arc::new(|_| {}),
192 on_enter: Arc::new(|_| {}),
193 on_leave: Arc::new(|_| {}),
194 on_over: Arc::new(|_| {}),
195 }
196 }
197}
198
199impl Debug for UseDropZoneOptions {
200 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
201 write!(f, "UseDropZoneOptions")
202 }
203}
204
205#[derive(Clone, Debug)]
207pub struct UseDropZoneEvent {
208 pub files: Vec<web_sys::File>,
210 pub event: web_sys::DragEvent,
212}
213
214#[derive(Clone, Copy)]
216pub struct UseDropZoneReturn {
217 pub files: Signal<Vec<SendWrapper<web_sys::File>>>,
219 pub is_over_drop_zone: Signal<bool>,
221}