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