leptos_use/
use_drop_zone.rsuse crate::core::IntoElementMaybeSignal;
use cfg_if::cfg_if;
use default_struct_builder::DefaultBuilder;
use leptos::prelude::*;
use leptos::reactive::wrappers::read::Signal;
use send_wrapper::SendWrapper;
use std::fmt::{Debug, Formatter};
use std::sync::Arc;
cfg_if! { if #[cfg(not(feature = "ssr"))] {
use crate::use_event_listener;
use leptos::ev::{dragenter, dragleave, dragover, drop};
}}
pub fn use_drop_zone<El, M>(target: El) -> UseDropZoneReturn
where
El: IntoElementMaybeSignal<web_sys::EventTarget, M>,
{
use_drop_zone_with_options(target, UseDropZoneOptions::default())
}
#[cfg_attr(feature = "ssr", allow(unused_variables))]
pub fn use_drop_zone_with_options<El, M>(
target: El,
options: UseDropZoneOptions,
) -> UseDropZoneReturn
where
El: IntoElementMaybeSignal<web_sys::EventTarget, M>,
{
let (is_over_drop_zone, set_over_drop_zone) = signal(false);
let (files, set_files) = signal(Vec::<SendWrapper<web_sys::File>>::new());
#[cfg(not(feature = "ssr"))]
{
use std::ops::Deref;
let UseDropZoneOptions {
on_drop,
on_enter,
on_leave,
on_over,
} = options;
let counter = StoredValue::new(0_usize);
let update_files = move |event: &web_sys::DragEvent| {
if let Some(data_transfer) = event.data_transfer() {
let files: Vec<_> = data_transfer
.files()
.map(|f| js_sys::Array::from(&f).to_vec())
.unwrap_or_default()
.into_iter()
.map(web_sys::File::from)
.map(SendWrapper::new)
.collect();
set_files.update(move |f| *f = files);
}
};
let target = target.into_element_maybe_signal();
let use_drop_zone_event = move |event| UseDropZoneEvent {
files: files
.read_untracked()
.iter()
.map(|f| f.deref().clone())
.collect(),
event,
};
let _ = use_event_listener(target, dragenter, move |event| {
event.prevent_default();
counter.update_value(|counter| *counter += 1);
set_over_drop_zone.set(true);
update_files(&event);
#[cfg(debug_assertions)]
let _z = leptos::reactive::diagnostics::SpecialNonReactiveZone::enter();
on_enter(use_drop_zone_event(event));
});
let _ = use_event_listener(target, dragover, move |event| {
event.prevent_default();
update_files(&event);
#[cfg(debug_assertions)]
let _z = leptos::reactive::diagnostics::SpecialNonReactiveZone::enter();
on_over(use_drop_zone_event(event));
});
let _ = use_event_listener(target, dragleave, move |event| {
event.prevent_default();
counter.update_value(|counter| *counter -= 1);
if counter.get_value() == 0 {
set_over_drop_zone.set(false);
}
update_files(&event);
#[cfg(debug_assertions)]
let _z = leptos::reactive::diagnostics::SpecialNonReactiveZone::enter();
on_leave(use_drop_zone_event(event));
});
let _ = use_event_listener(target, drop, move |event| {
event.prevent_default();
counter.update_value(|counter| *counter = 0);
set_over_drop_zone.set(false);
update_files(&event);
#[cfg(debug_assertions)]
let _z = leptos::reactive::diagnostics::SpecialNonReactiveZone::enter();
on_drop(use_drop_zone_event(event));
});
}
UseDropZoneReturn {
files: files.into(),
is_over_drop_zone: is_over_drop_zone.into(),
}
}
#[derive(DefaultBuilder, Clone)]
#[cfg_attr(feature = "ssr", allow(dead_code))]
pub struct UseDropZoneOptions {
on_drop: Arc<dyn Fn(UseDropZoneEvent) + Send + Sync>,
on_enter: Arc<dyn Fn(UseDropZoneEvent) + Send + Sync>,
on_leave: Arc<dyn Fn(UseDropZoneEvent) + Send + Sync>,
on_over: Arc<dyn Fn(UseDropZoneEvent) + Send + Sync>,
}
impl Default for UseDropZoneOptions {
fn default() -> Self {
Self {
on_drop: Arc::new(|_| {}),
on_enter: Arc::new(|_| {}),
on_leave: Arc::new(|_| {}),
on_over: Arc::new(|_| {}),
}
}
}
impl Debug for UseDropZoneOptions {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "UseDropZoneOptions")
}
}
#[derive(Clone, Debug)]
pub struct UseDropZoneEvent {
pub files: Vec<web_sys::File>,
pub event: web_sys::DragEvent,
}
#[derive(Clone, Copy)]
pub struct UseDropZoneReturn {
pub files: Signal<Vec<SendWrapper<web_sys::File>>>,
pub is_over_drop_zone: Signal<bool>,
}