datatransfer object, drag_and_drop example, fix drag-and-drop (#4789)
* wip: implement datatransfer * Editable kanban contents * desktop sorta works without crashing * lint
This commit is contained in:
1 parent
7856679641
commit
0aa7351473
13 files changed
+562
-122
No files matched your search
@@ -790,6 +790,11 @@ name = "websocket"
|
|||||||
path = "examples/07-fullstack/websocket.rs"
|
path = "examples/07-fullstack/websocket.rs"
|
||||||
doc-scrape-examples = true
|
doc-scrape-examples = true
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "drag_and_drop"
|
||||||
|
path = "examples/08-apis/drag_and_drop.rs"
|
||||||
|
doc-scrape-examples = true
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "control_focus"
|
name = "control_focus"
|
||||||
path = "examples/08-apis/control_focus.rs"
|
path = "examples/08-apis/control_focus.rs"
|
||||||
|
|||||||
@@ -0,0 +1,122 @@
|
|||||||
|
//! This example shows how to implement a simple drag-and-drop kanban board using Dioxus.
|
||||||
|
//! You can drag items between different categories and edit their contents.
|
||||||
|
//!
|
||||||
|
//! This example uses the `.data_transfer()` API to handle drag-and-drop events. When an item is dragged,
|
||||||
|
//! its ID is stored in the data transfer object. When the item is dropped into a new category, its ID is retrieved
|
||||||
|
//! from the data transfer object and used to update the item's category.
|
||||||
|
//!
|
||||||
|
//! Note that in a real-world application, you'll want more sophisticated drop handling, such as visual
|
||||||
|
//! feedback during dragging, and better drop-zone detection to allow dropping *between* items.
|
||||||
|
|
||||||
|
use dioxus::prelude::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
dioxus::launch(app);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Item {
|
||||||
|
id: usize,
|
||||||
|
name: String,
|
||||||
|
category: String,
|
||||||
|
contents: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn app() -> Element {
|
||||||
|
let mut items = use_signal(initial_kanban_data);
|
||||||
|
|
||||||
|
rsx! {
|
||||||
|
div {
|
||||||
|
display: "flex",
|
||||||
|
gap: "20px",
|
||||||
|
flex_direction: "row",
|
||||||
|
for category in ["A", "B", "C"] {
|
||||||
|
div {
|
||||||
|
class: "category",
|
||||||
|
display: "flex",
|
||||||
|
flex_direction: "column",
|
||||||
|
gap: "10px",
|
||||||
|
padding: "10px",
|
||||||
|
flex_grow: "1",
|
||||||
|
border: "2px solid black",
|
||||||
|
min_height: "300px",
|
||||||
|
background_color: "#f0f0f0",
|
||||||
|
ondragover: |e| e.prevent_default(),
|
||||||
|
ondrop: move |e| {
|
||||||
|
if let Some(item_id) = e.data_transfer().get_data("text/plain").and_then(|data| data.parse::<usize>().ok()) {
|
||||||
|
if let Some(pos) = items.iter().position(|item| item.id == item_id) {
|
||||||
|
items.write()[pos].category = category.to_string();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
h2 { "Category: {category}" }
|
||||||
|
for (index, item) in items.iter().enumerate().filter(|item| item.1.category == category) {
|
||||||
|
div {
|
||||||
|
key: "{item.id}",
|
||||||
|
width: "200px",
|
||||||
|
height: "50px",
|
||||||
|
border: "1px solid black",
|
||||||
|
padding: "10px",
|
||||||
|
class: "item",
|
||||||
|
draggable: "true",
|
||||||
|
background: "white",
|
||||||
|
cursor: "grab",
|
||||||
|
ondragstart: move |e| {
|
||||||
|
let id = items.read()[index].id.to_string();
|
||||||
|
e.data_transfer().set_data("text/plain", &id).unwrap();
|
||||||
|
},
|
||||||
|
pre { webkit_user_select: "none", "{item.name}" }
|
||||||
|
input {
|
||||||
|
r#type: "text",
|
||||||
|
value: "{item.contents}",
|
||||||
|
oninput: move |e| {
|
||||||
|
items.write()[index].contents = e.value();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn initial_kanban_data() -> Vec<Item> {
|
||||||
|
vec![
|
||||||
|
Item {
|
||||||
|
id: 1,
|
||||||
|
name: "Item 1".into(),
|
||||||
|
category: "A".into(),
|
||||||
|
contents: "This is item 1".into(),
|
||||||
|
},
|
||||||
|
Item {
|
||||||
|
id: 2,
|
||||||
|
name: "Item 2".into(),
|
||||||
|
category: "A".into(),
|
||||||
|
contents: "This is item 2".into(),
|
||||||
|
},
|
||||||
|
Item {
|
||||||
|
id: 3,
|
||||||
|
name: "Item 3".into(),
|
||||||
|
category: "A".into(),
|
||||||
|
contents: "This is item 3".into(),
|
||||||
|
},
|
||||||
|
Item {
|
||||||
|
id: 4,
|
||||||
|
name: "Item 4".into(),
|
||||||
|
category: "B".into(),
|
||||||
|
contents: "This is item 4".into(),
|
||||||
|
},
|
||||||
|
Item {
|
||||||
|
id: 5,
|
||||||
|
name: "Item 5".into(),
|
||||||
|
category: "B".into(),
|
||||||
|
contents: "This is item 5".into(),
|
||||||
|
},
|
||||||
|
Item {
|
||||||
|
id: 6,
|
||||||
|
name: "Item 6".into(),
|
||||||
|
category: "C".into(),
|
||||||
|
contents: "This is item 6".into(),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -11,8 +11,9 @@ use dioxus_html::{
|
|||||||
point_interaction::{
|
point_interaction::{
|
||||||
InteractionElementOffset, InteractionLocation, ModifiersInteraction, PointerInteraction,
|
InteractionElementOffset, InteractionLocation, ModifiersInteraction, PointerInteraction,
|
||||||
},
|
},
|
||||||
FileData, FormValue, HasDragData, HasFileData, HasFormData, HasMouseData, NativeFileData,
|
FileData, FormValue, HasDataTransferData, HasDragData, HasFileData, HasFormData, HasMouseData,
|
||||||
SerializedFormData, SerializedFormObject, SerializedMouseData, SerializedPointInteraction,
|
NativeFileData, SerializedDataTransfer, SerializedFormData, SerializedFormObject,
|
||||||
|
SerializedMouseData, SerializedPointInteraction,
|
||||||
};
|
};
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@@ -234,6 +235,7 @@ impl NativeFileHover {
|
|||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub(crate) struct DesktopFileDragEvent {
|
pub(crate) struct DesktopFileDragEvent {
|
||||||
pub mouse: SerializedPointInteraction,
|
pub mouse: SerializedPointInteraction,
|
||||||
|
pub data_transfer: SerializedDataTransfer,
|
||||||
pub files: Vec<PathBuf>,
|
pub files: Vec<PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -247,6 +249,12 @@ impl HasFileData for DesktopFileDragEvent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl HasDataTransferData for DesktopFileDragEvent {
|
||||||
|
fn data_transfer(&self) -> dioxus_html::DataTransfer {
|
||||||
|
dioxus_html::DataTransfer::new(self.data_transfer.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl HasDragData for DesktopFileDragEvent {
|
impl HasDragData for DesktopFileDragEvent {
|
||||||
fn as_any(&self) -> &dyn std::any::Any {
|
fn as_any(&self) -> &dyn std::any::Any {
|
||||||
self
|
self
|
||||||
@@ -389,3 +397,5 @@ impl NativeFileData for DesktopFileData {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct DesktopDataTransfer {}
|
||||||
@@ -155,16 +155,17 @@ impl WebviewEdits {
|
|||||||
}
|
}
|
||||||
dioxus_html::EventData::Drag(ref drag) => {
|
dioxus_html::EventData::Drag(ref drag) => {
|
||||||
// we want to override this with a native file engine, provided by the most recent drag event
|
// we want to override this with a native file engine, provided by the most recent drag event
|
||||||
let file_event = hovered_file.current().unwrap();
|
let file_event = hovered_file.current();
|
||||||
let paths = match file_event {
|
let file_paths = match file_event {
|
||||||
wry::DragDropEvent::Enter { paths, .. } => paths,
|
Some(wry::DragDropEvent::Enter { paths, .. }) => paths,
|
||||||
wry::DragDropEvent::Drop { paths, .. } => paths,
|
Some(wry::DragDropEvent::Drop { paths, .. }) => paths,
|
||||||
_ => vec![],
|
_ => vec![],
|
||||||
};
|
};
|
||||||
|
|
||||||
Rc::new(PlatformEventData::new(Box::new(DesktopFileDragEvent {
|
Rc::new(PlatformEventData::new(Box::new(DesktopFileDragEvent {
|
||||||
mouse: drag.mouse.clone(),
|
mouse: drag.mouse.clone(),
|
||||||
files: paths,
|
data_transfer: drag.data_transfer.clone(),
|
||||||
|
files: file_paths,
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
_ => data.into_any(),
|
_ => data.into_any(),
|
||||||
|
|||||||
@@ -0,0 +1,180 @@
|
|||||||
|
pub struct DataTransfer {
|
||||||
|
inner: Box<dyn NativeDataTransfer>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DataTransfer {
|
||||||
|
pub fn new(inner: impl NativeDataTransfer + 'static) -> Self {
|
||||||
|
Self {
|
||||||
|
inner: Box::new(inner),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "serialize")]
|
||||||
|
pub fn store(&self, item: impl Serialize) -> Result<(), String> {
|
||||||
|
let serialized = serde_json::to_string(&item).map_err(|e| e.to_string())?;
|
||||||
|
self.set_data("application/json", &serialized)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "serialize")]
|
||||||
|
pub fn retrieve<T: for<'de> serde::Deserialize<'de>>(&self) -> Result<Option<T>, String> {
|
||||||
|
if let Some(data) = self.get_data("application/json") {
|
||||||
|
let deserialized = serde_json::from_str(&data).map_err(|e| e.to_string())?;
|
||||||
|
Ok(Some(deserialized))
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_data(&self, format: &str) -> Option<String> {
|
||||||
|
self.inner.get_data(format)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_as_text(&self) -> Option<String> {
|
||||||
|
self.get_data("text/plain")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_data(&self, format: &str, data: &str) -> Result<(), String> {
|
||||||
|
self.inner.set_data(format, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clear_data(&self, format: Option<&str>) -> Result<(), String> {
|
||||||
|
self.inner.clear_data(format)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn effect_allowed(&self) -> String {
|
||||||
|
self.inner.effect_allowed()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_effect_allowed(&self, effect: &str) {
|
||||||
|
self.inner.set_effect_allowed(effect)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn drop_effect(&self) -> String {
|
||||||
|
self.inner.drop_effect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_drop_effect(&self, effect: &str) {
|
||||||
|
self.inner.set_drop_effect(effect)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn files(&self) -> Vec<crate::file_data::FileData> {
|
||||||
|
self.inner.files()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait NativeDataTransfer: Send + Sync {
|
||||||
|
fn get_data(&self, format: &str) -> Option<String>;
|
||||||
|
fn set_data(&self, format: &str, data: &str) -> Result<(), String>;
|
||||||
|
fn clear_data(&self, format: Option<&str>) -> Result<(), String>;
|
||||||
|
fn effect_allowed(&self) -> String;
|
||||||
|
fn set_effect_allowed(&self, effect: &str);
|
||||||
|
fn drop_effect(&self) -> String;
|
||||||
|
fn set_drop_effect(&self, effect: &str);
|
||||||
|
fn files(&self) -> Vec<crate::file_data::FileData>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait HasDataTransferData {
|
||||||
|
fn data_transfer(&self) -> DataTransfer;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "serialize")]
|
||||||
|
pub use ser::*;
|
||||||
|
#[cfg(feature = "serialize")]
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
#[cfg(feature = "serialize")]
|
||||||
|
mod ser {
|
||||||
|
use crate::DragData;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
/// A serialized version of DataTransfer
|
||||||
|
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
|
||||||
|
pub struct SerializedDataTransfer {
|
||||||
|
pub items: Vec<SerializedDataTransferItem>,
|
||||||
|
pub files: Vec<crate::file_data::SerializedFileData>,
|
||||||
|
pub effect_allowed: String,
|
||||||
|
pub drop_effect: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
|
||||||
|
pub struct SerializedDataTransferItem {
|
||||||
|
pub kind: String,
|
||||||
|
pub type_: String,
|
||||||
|
pub data: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NativeDataTransfer for SerializedDataTransfer {
|
||||||
|
fn get_data(&self, format: &str) -> Option<String> {
|
||||||
|
self.items
|
||||||
|
.iter()
|
||||||
|
.find(|item| item.type_ == format)
|
||||||
|
.map(|item| item.data.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_data(&self, _format: &str, _data: &str) -> Result<(), String> {
|
||||||
|
// todo!()
|
||||||
|
// Err("Cannot set data on serialized DataTransfer".into())
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clear_data(&self, _format: Option<&str>) -> Result<(), String> {
|
||||||
|
// todo!()
|
||||||
|
// Err("Cannot clear data on serialized DataTransfer".into())
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn effect_allowed(&self) -> String {
|
||||||
|
self.effect_allowed.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_effect_allowed(&self, _effect: &str) {
|
||||||
|
// No-op
|
||||||
|
}
|
||||||
|
|
||||||
|
fn drop_effect(&self) -> String {
|
||||||
|
self.drop_effect.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_drop_effect(&self, _effect: &str) {
|
||||||
|
// No-op
|
||||||
|
}
|
||||||
|
|
||||||
|
fn files(&self) -> Vec<crate::file_data::FileData> {
|
||||||
|
self.files
|
||||||
|
.iter()
|
||||||
|
.map(|f| crate::file_data::FileData::new(f.clone()))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&DragData> for SerializedDataTransfer {
|
||||||
|
fn from(_drag: &DragData) -> Self {
|
||||||
|
todo!()
|
||||||
|
// let items = vec![]; // drag.data_transfer().items().iter().map(|item| SerializedDataTransferItem {
|
||||||
|
// // kind: item.kind().to_string(),
|
||||||
|
// // type_: item.type_().to_string(),
|
||||||
|
// // data: item.data().unwrap_or_default(),
|
||||||
|
// // }).collect();
|
||||||
|
|
||||||
|
// let files = drag
|
||||||
|
// .files()
|
||||||
|
// .iter()
|
||||||
|
// .map(|f| crate::file_data::SerializedFileData {
|
||||||
|
// name: f.name().to_string(),
|
||||||
|
// size: f.size(),
|
||||||
|
// type_: f.type_().to_string(),
|
||||||
|
// })
|
||||||
|
// .collect();
|
||||||
|
|
||||||
|
// Self {
|
||||||
|
// items,
|
||||||
|
// files,
|
||||||
|
// effect_allowed: drag.effect_allowed().to_string(),
|
||||||
|
// drop_effect: drag.drop_effect().to_string(),
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,9 @@
|
|||||||
use crate::geometry::{ClientPoint, Coordinates, ElementPoint, PagePoint, ScreenPoint};
|
|
||||||
use crate::input_data::{MouseButton, MouseButtonSet};
|
use crate::input_data::{MouseButton, MouseButtonSet};
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
use crate::{
|
||||||
|
data_transfer::DataTransfer,
|
||||||
|
geometry::{ClientPoint, Coordinates, ElementPoint, PagePoint, ScreenPoint},
|
||||||
|
};
|
||||||
|
|
||||||
use dioxus_core::Event;
|
use dioxus_core::Event;
|
||||||
use keyboard_types::Modifiers;
|
use keyboard_types::Modifiers;
|
||||||
@@ -51,6 +54,11 @@ impl DragData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The DataTransfer object is used to hold the data that is being dragged during a drag and drop operation.
|
||||||
|
pub fn data_transfer(&self) -> DataTransfer {
|
||||||
|
self.inner.data_transfer()
|
||||||
|
}
|
||||||
|
|
||||||
/// Downcast this event data to a specific type
|
/// Downcast this event data to a specific type
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn downcast<T: 'static>(&self) -> Option<&T> {
|
pub fn downcast<T: 'static>(&self) -> Option<&T> {
|
||||||
@@ -106,111 +114,115 @@ impl PointerInteraction for DragData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "serialize")]
|
#[cfg(feature = "serialize")]
|
||||||
/// A serialized version of DragData
|
pub use ser::*;
|
||||||
#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Clone)]
|
|
||||||
pub struct SerializedDragData {
|
|
||||||
pub mouse: crate::point_interaction::SerializedPointInteraction,
|
|
||||||
|
|
||||||
pub files: Vec<SerializedFileData>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "serialize")]
|
#[cfg(feature = "serialize")]
|
||||||
impl SerializedDragData {
|
mod ser {
|
||||||
fn new(drag: &DragData) -> Self {
|
use super::*;
|
||||||
Self {
|
|
||||||
mouse: crate::point_interaction::SerializedPointInteraction::from(drag),
|
/// A serialized version of DragData
|
||||||
files: vec![],
|
#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Clone)]
|
||||||
|
pub struct SerializedDragData {
|
||||||
|
pub mouse: crate::point_interaction::SerializedPointInteraction,
|
||||||
|
|
||||||
|
pub data_transfer: crate::data_transfer::SerializedDataTransfer,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SerializedDragData {
|
||||||
|
fn new(drag: &DragData) -> Self {
|
||||||
|
Self {
|
||||||
|
mouse: crate::point_interaction::SerializedPointInteraction::from(drag),
|
||||||
|
data_transfer: crate::data_transfer::SerializedDataTransfer::from(drag),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HasDataTransferData for SerializedDragData {
|
||||||
|
fn data_transfer(&self) -> crate::data_transfer::DataTransfer {
|
||||||
|
crate::data_transfer::DataTransfer::new(self.data_transfer.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HasDragData for SerializedDragData {
|
||||||
|
fn as_any(&self) -> &dyn std::any::Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl crate::file_data::HasFileData for SerializedDragData {
|
||||||
|
fn files(&self) -> Vec<FileData> {
|
||||||
|
self.data_transfer
|
||||||
|
.files
|
||||||
|
.iter()
|
||||||
|
.map(|f| FileData::new(f.clone()))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HasMouseData for SerializedDragData {
|
||||||
|
fn as_any(&self) -> &dyn std::any::Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InteractionLocation for SerializedDragData {
|
||||||
|
fn client_coordinates(&self) -> ClientPoint {
|
||||||
|
self.mouse.client_coordinates()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn page_coordinates(&self) -> PagePoint {
|
||||||
|
self.mouse.page_coordinates()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn screen_coordinates(&self) -> ScreenPoint {
|
||||||
|
self.mouse.screen_coordinates()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InteractionElementOffset for SerializedDragData {
|
||||||
|
fn element_coordinates(&self) -> ElementPoint {
|
||||||
|
self.mouse.element_coordinates()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn coordinates(&self) -> Coordinates {
|
||||||
|
self.mouse.coordinates()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ModifiersInteraction for SerializedDragData {
|
||||||
|
fn modifiers(&self) -> Modifiers {
|
||||||
|
self.mouse.modifiers()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PointerInteraction for SerializedDragData {
|
||||||
|
fn held_buttons(&self) -> MouseButtonSet {
|
||||||
|
self.mouse.held_buttons()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn trigger_button(&self) -> Option<MouseButton> {
|
||||||
|
self.mouse.trigger_button()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl serde::Serialize for DragData {
|
||||||
|
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||||
|
SerializedDragData::new(self).serialize(serializer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> serde::Deserialize<'de> for DragData {
|
||||||
|
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
||||||
|
let data = SerializedDragData::deserialize(deserializer)?;
|
||||||
|
Ok(Self {
|
||||||
|
inner: Box::new(data),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "serialize")]
|
|
||||||
impl HasDragData for SerializedDragData {
|
|
||||||
fn as_any(&self) -> &dyn std::any::Any {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "serialize")]
|
|
||||||
impl crate::file_data::HasFileData for SerializedDragData {
|
|
||||||
fn files(&self) -> Vec<FileData> {
|
|
||||||
self.files
|
|
||||||
.iter()
|
|
||||||
.map(|f| FileData::new(f.clone()))
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "serialize")]
|
|
||||||
impl HasMouseData for SerializedDragData {
|
|
||||||
fn as_any(&self) -> &dyn std::any::Any {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "serialize")]
|
|
||||||
impl InteractionLocation for SerializedDragData {
|
|
||||||
fn client_coordinates(&self) -> ClientPoint {
|
|
||||||
self.mouse.client_coordinates()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn page_coordinates(&self) -> PagePoint {
|
|
||||||
self.mouse.page_coordinates()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn screen_coordinates(&self) -> ScreenPoint {
|
|
||||||
self.mouse.screen_coordinates()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "serialize")]
|
|
||||||
impl InteractionElementOffset for SerializedDragData {
|
|
||||||
fn element_coordinates(&self) -> ElementPoint {
|
|
||||||
self.mouse.element_coordinates()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn coordinates(&self) -> Coordinates {
|
|
||||||
self.mouse.coordinates()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "serialize")]
|
|
||||||
impl ModifiersInteraction for SerializedDragData {
|
|
||||||
fn modifiers(&self) -> Modifiers {
|
|
||||||
self.mouse.modifiers()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "serialize")]
|
|
||||||
impl PointerInteraction for SerializedDragData {
|
|
||||||
fn held_buttons(&self) -> MouseButtonSet {
|
|
||||||
self.mouse.held_buttons()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn trigger_button(&self) -> Option<MouseButton> {
|
|
||||||
self.mouse.trigger_button()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "serialize")]
|
|
||||||
impl serde::Serialize for DragData {
|
|
||||||
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
|
||||||
SerializedDragData::new(self).serialize(serializer)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "serialize")]
|
|
||||||
impl<'de> serde::Deserialize<'de> for DragData {
|
|
||||||
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
|
||||||
let data = SerializedDragData::deserialize(deserializer)?;
|
|
||||||
Ok(Self {
|
|
||||||
inner: Box::new(data),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A trait for any object that has the data for a drag event
|
/// A trait for any object that has the data for a drag event
|
||||||
pub trait HasDragData: HasMouseData + crate::HasFileData {
|
pub trait HasDragData: HasMouseData + crate::HasFileData + crate::HasDataTransferData {
|
||||||
/// return self as Any
|
/// return self as Any
|
||||||
fn as_any(&self) -> &dyn std::any::Any;
|
fn as_any(&self) -> &dyn std::any::Any;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,10 +25,12 @@ pub mod events;
|
|||||||
pub(crate) mod file_data;
|
pub(crate) mod file_data;
|
||||||
pub use file_data::*;
|
pub use file_data::*;
|
||||||
mod attribute_groups;
|
mod attribute_groups;
|
||||||
|
mod data_transfer;
|
||||||
pub mod geometry;
|
pub mod geometry;
|
||||||
pub mod input_data;
|
pub mod input_data;
|
||||||
pub mod point_interaction;
|
pub mod point_interaction;
|
||||||
mod render_template;
|
mod render_template;
|
||||||
|
pub use data_transfer::*;
|
||||||
|
|
||||||
pub use bytes;
|
pub use bytes;
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
[17669692872757955279, 11420464406527728232, 3770103091118609057, 5444526391971481782, 8889858244860485542, 5052021921702764563, 5113938988622536969, 11339769846046015954]
|
[17669692872757955279, 11420464406527728232, 3770103091118609057, 5444526391971481782, 8889858244860485542, 5052021921702764563, 15193138787416885137, 11339769846046015954]
|
||||||
@@ -1 +1 @@
|
|||||||
function serializeEvent(event,target){let contents={},extend=(obj)=>contents={...contents,...obj};if(event instanceof WheelEvent)extend(serializeWheelEvent(event));if(event instanceof MouseEvent)extend(serializeMouseEvent(event));if(event instanceof KeyboardEvent)extend(serializeKeyboardEvent(event));if(event instanceof InputEvent)extend(serializeInputEvent(event,target));if(event instanceof PointerEvent)extend(serializePointerEvent(event));if(event instanceof AnimationEvent)extend(serializeAnimationEvent(event));if(event instanceof TransitionEvent)extend({property_name:event.propertyName,elapsed_time:event.elapsedTime,pseudo_element:event.pseudoElement});if(event instanceof CompositionEvent)extend({data:event.data});if(event instanceof DragEvent)extend(serializeDragEvent(event));if(event instanceof FocusEvent)extend({});if(event instanceof ClipboardEvent)extend({});if(event instanceof CustomEvent){let detail=event.detail;if(detail instanceof ResizeObserverEntry)extend(serializeResizeEventDetail(detail));else if(detail instanceof IntersectionObserverEntry)extend(serializeIntersectionEventDetail(detail))}if(typeof TouchEvent<"u"&&event instanceof TouchEvent)extend(serializeTouchEvent(event));if(event.type==="submit"||event.type==="reset"||event.type==="click"||event.type==="change"||event.type==="input")extend(serializeInputEvent(event,target));if(event instanceof DragEvent){let files=[];if(event.dataTransfer&&event.dataTransfer.files)for(let i=0;i<event.dataTransfer.files.length;i++){let file=event.dataTransfer.files[i],data={path:file.name,size:file.size,last_modified:file.lastModified,content_type:file.type};files.push({key:file.name,file:data})}extend({files})}if(event.type==="scroll")extend(serializeScrollEvent(event));return contents}function toSerializableResizeObserverSize(size,is_inline_width){return[is_inline_width?size.inlineSize:size.blockSize,is_inline_width?size.blockSize:size.inlineSize]}function serializeResizeEventDetail(detail){let is_inline_width=!0;if(detail.target instanceof HTMLElement){if(window.getComputedStyle(detail.target).getPropertyValue("writing-mode")!=="horizontal-tb")is_inline_width=!1}return{border_box_size:detail.borderBoxSize!==void 0?toSerializableResizeObserverSize(detail.borderBoxSize[0],is_inline_width):detail.contentRect,content_box_size:detail.contentBoxSize!==void 0?toSerializableResizeObserverSize(detail.contentBoxSize[0],is_inline_width):detail.contentRect,content_rect:detail.contentRect}}function serializeIntersectionEventDetail(detail){return{bounding_client_rect:detail.boundingClientRect,intersection_ratio:detail.intersectionRatio,intersection_rect:detail.intersectionRect,is_intersecting:detail.isIntersecting,root_bounds:detail.rootBounds,time_ms:Math.floor(Date.now()+detail.time)}}function serializeInputEvent(event,target){let contents={};if(target instanceof HTMLElement){let values=extractSerializedFormValues(event,target);contents.values=values.values,contents.valid=values.valid}if(event.target instanceof HTMLInputElement){let target2=event.target,value=target2.value??target2.textContent??"";if(target2.type==="checkbox")value=target2.checked?"true":"false";else if(target2.type==="radio")value=target2.value;contents.value=value}if(event.target instanceof HTMLTextAreaElement)contents.value=event.target.value;if(event.target instanceof HTMLSelectElement)contents.value=retrieveSelectValue(event.target).join(",");if(contents.value===void 0)contents.value="";return contents}function serializeWheelEvent(event){return{delta_x:event.deltaX,delta_y:event.deltaY,delta_z:event.deltaZ,delta_mode:event.deltaMode}}function serializeTouchEvent(event){return{alt_key:event.altKey,ctrl_key:event.ctrlKey,meta_key:event.metaKey,shift_key:event.shiftKey,changed_touches:serializeTouchList(event.changedTouches),target_touches:serializeTouchList(event.targetTouches),touches:serializeTouchList(event.touches)}}function serializePointerEvent(event){return{alt_key:event.altKey,button:event.button,buttons:event.buttons,client_x:event.clientX,client_y:event.clientY,ctrl_key:event.ctrlKey,meta_key:event.metaKey,page_x:event.pageX,page_y:event.pageY,screen_x:event.screenX,screen_y:event.screenY,shift_key:event.shiftKey,pointer_id:event.pointerId,width:event.width,height:event.height,pressure:event.pressure,tangential_pressure:event.tangentialPressure,tilt_x:event.tiltX,tilt_y:event.tiltY,twist:event.twist,pointer_type:event.pointerType,is_primary:event.isPrimary}}function serializeTouchList(touchList){let serializedTouches=[];for(let i=0;i<touchList.length;i++){let touch=touchList[i];serializedTouches.push({identifier:touch.identifier,client_x:touch.clientX,client_y:touch.clientY,page_x:touch.pageX,page_y:touch.pageY,screen_x:touch.screenX,screen_y:touch.screenY,radius_x:touch.radiusX,radius_y:touch.radiusY,rotation_angle:touch.rotationAngle,force:touch.force})}return serializedTouches}function serializeMouseEvent(event){return{alt_key:event.altKey,button:event.button,buttons:event.buttonsLine truncated
|
function serializeEvent(event,target){let contents={},extend=(obj)=>contents={...contents,...obj};if(event instanceof WheelEvent)extend(serializeWheelEvent(event));if(event instanceof MouseEvent)extend(serializeMouseEvent(event));if(event instanceof KeyboardEvent)extend(serializeKeyboardEvent(event));if(event instanceof InputEvent)extend(serializeInputEvent(event,target));if(event instanceof PointerEvent)extend(serializePointerEvent(event));if(event instanceof AnimationEvent)extend(serializeAnimationEvent(event));if(event instanceof TransitionEvent)extend({property_name:event.propertyName,elapsed_time:event.elapsedTime,pseudo_element:event.pseudoElement});if(event instanceof CompositionEvent)extend({data:event.data});if(event instanceof DragEvent)extend(serializeDragEvent(event));if(event instanceof FocusEvent)extend({});if(event instanceof ClipboardEvent)extend({});if(event instanceof CustomEvent){let detail=event.detail;if(detail instanceof ResizeObserverEntry)extend(serializeResizeEventDetail(detail));else if(detail instanceof IntersectionObserverEntry)extend(serializeIntersectionEventDetail(detail))}if(typeof TouchEvent<"u"&&event instanceof TouchEvent)extend(serializeTouchEvent(event));if(event.type==="submit"||event.type==="reset"||event.type==="click"||event.type==="change"||event.type==="input")extend(serializeInputEvent(event,target));if(event instanceof DragEvent){let files=[];if(event.dataTransfer&&event.dataTransfer.files)for(let i=0;i<event.dataTransfer.files.length;i++){let file=event.dataTransfer.files[i],data={path:file.name,size:file.size,last_modified:file.lastModified,content_type:file.type};files.push({key:file.name,file:data})}extend({files})}if(event.type==="scroll")extend(serializeScrollEvent(event));return contents}function toSerializableResizeObserverSize(size,is_inline_width){return[is_inline_width?size.inlineSize:size.blockSize,is_inline_width?size.blockSize:size.inlineSize]}function serializeResizeEventDetail(detail){let is_inline_width=!0;if(detail.target instanceof HTMLElement){if(window.getComputedStyle(detail.target).getPropertyValue("writing-mode")!=="horizontal-tb")is_inline_width=!1}return{border_box_size:detail.borderBoxSize!==void 0?toSerializableResizeObserverSize(detail.borderBoxSize[0],is_inline_width):detail.contentRect,content_box_size:detail.contentBoxSize!==void 0?toSerializableResizeObserverSize(detail.contentBoxSize[0],is_inline_width):detail.contentRect,content_rect:detail.contentRect}}function serializeIntersectionEventDetail(detail){return{bounding_client_rect:detail.boundingClientRect,intersection_ratio:detail.intersectionRatio,intersection_rect:detail.intersectionRect,is_intersecting:detail.isIntersecting,root_bounds:detail.rootBounds,time_ms:Math.floor(Date.now()+detail.time)}}function serializeInputEvent(event,target){let contents={};if(target instanceof HTMLElement){let values=extractSerializedFormValues(event,target);contents.values=values.values,contents.valid=values.valid}if(event.target instanceof HTMLInputElement){let target2=event.target,value=target2.value??target2.textContent??"";if(target2.type==="checkbox")value=target2.checked?"true":"false";else if(target2.type==="radio")value=target2.value;contents.value=value}if(event.target instanceof HTMLTextAreaElement)contents.value=event.target.value;if(event.target instanceof HTMLSelectElement)contents.value=retrieveSelectValue(event.target).join(",");if(contents.value===void 0)contents.value="";return contents}function serializeWheelEvent(event){return{delta_x:event.deltaX,delta_y:event.deltaY,delta_z:event.deltaZ,delta_mode:event.deltaMode}}function serializeTouchEvent(event){return{alt_key:event.altKey,ctrl_key:event.ctrlKey,meta_key:event.metaKey,shift_key:event.shiftKey,changed_touches:serializeTouchList(event.changedTouches),target_touches:serializeTouchList(event.targetTouches),touches:serializeTouchList(event.touches)}}function serializePointerEvent(event){return{alt_key:event.altKey,button:event.button,buttons:event.buttons,client_x:event.clientX,client_y:event.clientY,ctrl_key:event.ctrlKey,meta_key:event.metaKey,page_x:event.pageX,page_y:event.pageY,screen_x:event.screenX,screen_y:event.screenY,shift_key:event.shiftKey,pointer_id:event.pointerId,width:event.width,height:event.height,pressure:event.pressure,tangential_pressure:event.tangentialPressure,tilt_x:event.tiltX,tilt_y:event.tiltY,twist:event.twist,pointer_type:event.pointerType,is_primary:event.isPrimary}}function serializeTouchList(touchList){let serializedTouches=[];for(let i=0;i<touchList.length;i++){let touch=touchList[i];serializedTouches.push({identifier:touch.identifier,client_x:touch.clientX,client_y:touch.clientY,page_x:touch.pageX,page_y:touch.pageY,screen_x:touch.screenX,screen_y:touch.screenY,radius_x:touch.radiusX,radius_y:touch.radiusY,rotation_angle:touch.rotationAngle,force:touch.force})}return serializedTouches}function serializeMouseEvent(event){return{alt_key:event.altKey,button:event.button,buttons:event.buttonsLine truncated
|
||||||
@@ -321,17 +321,42 @@ function serializeAnimationEvent(event: AnimationEvent): SerializedEvent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function serializeDragEvent(event: DragEvent): SerializedEvent {
|
function serializeDragEvent(event: DragEvent): SerializedEvent {
|
||||||
let files = undefined;
|
let data_transfer = event.dataTransfer || new DataTransfer();
|
||||||
// On desktop if there is file data, we insert it from wry. We just add a placeholder to let the rust side of dioxus know there's files
|
let items = [];
|
||||||
if (
|
let files = [];
|
||||||
event.dataTransfer &&
|
let effect_allowed = data_transfer.effectAllowed;
|
||||||
event.dataTransfer.files &&
|
let drop_effect = data_transfer.dropEffect;
|
||||||
event.dataTransfer.files.length > 0
|
|
||||||
) {
|
for (let i = 0; i < data_transfer.items.length; i++) {
|
||||||
files = {
|
let item = data_transfer.items[i];
|
||||||
files: { placeholder: [] },
|
let data;
|
||||||
};
|
if (item.kind === "string") {
|
||||||
|
// we can only get the data if we do this synchronously
|
||||||
|
// which is a bit sad, but oh well.
|
||||||
|
data = data_transfer.getData(item.type);
|
||||||
|
} else {
|
||||||
|
data = item.getAsFile()?.name || "";
|
||||||
|
}
|
||||||
|
|
||||||
|
items.push({
|
||||||
|
kind: item.kind,
|
||||||
|
type: item.type,
|
||||||
|
data
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < data_transfer.files.length; i++) {
|
||||||
|
let file = data_transfer.files[i];
|
||||||
|
files.push({
|
||||||
|
name: file.name,
|
||||||
|
path: file.name,
|
||||||
|
size: file.size,
|
||||||
|
last_modified: file.lastModified,
|
||||||
|
content_type: file.type,
|
||||||
|
contents: undefined, // we don't serialize contents here
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
mouse: {
|
mouse: {
|
||||||
alt_key: event.altKey,
|
alt_key: event.altKey,
|
||||||
@@ -340,7 +365,12 @@ function serializeDragEvent(event: DragEvent): SerializedEvent {
|
|||||||
shift_key: event.shiftKey,
|
shift_key: event.shiftKey,
|
||||||
...serializeMouseEvent(event),
|
...serializeMouseEvent(event),
|
||||||
},
|
},
|
||||||
files,
|
data_transfer: {
|
||||||
|
items,
|
||||||
|
files,
|
||||||
|
effect_allowed,
|
||||||
|
drop_effect,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -458,3 +488,6 @@ export function retrieveSelectValue(target: HTMLSelectElement): string[] {
|
|||||||
}
|
}
|
||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type SerializedDataTransfer = {}
|
||||||
|
export type SerializedDataTransferItem = {}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
use dioxus_html::{FileData, NativeDataTransfer};
|
||||||
|
|
||||||
|
/// A wrapper around the web_sys::DataTransfer to implement NativeDataTransfer
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct WebDataTransfer {
|
||||||
|
pub(crate) data: web_sys::DataTransfer,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WebDataTransfer {
|
||||||
|
/// Create a new WebDataTransfer from a web_sys::DataTransfer
|
||||||
|
pub fn new(data: web_sys::DataTransfer) -> Self {
|
||||||
|
Self { data }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Send for WebDataTransfer {}
|
||||||
|
unsafe impl Sync for WebDataTransfer {}
|
||||||
|
|
||||||
|
impl NativeDataTransfer for WebDataTransfer {
|
||||||
|
fn get_data(&self, format: &str) -> Option<String> {
|
||||||
|
self.data.get_data(format).ok()
|
||||||
|
}
|
||||||
|
fn set_data(&self, format: &str, data: &str) -> Result<(), String> {
|
||||||
|
self.data.set_data(format, data).map_err(|e| {
|
||||||
|
format!(
|
||||||
|
"Failed to set data for format {format}: {:?}",
|
||||||
|
e.as_string()
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
fn clear_data(&self, format: Option<&str>) -> Result<(), String> {
|
||||||
|
match format {
|
||||||
|
Some(f) => self.data.clear_data_with_format(f),
|
||||||
|
None => self.data.clear_data(),
|
||||||
|
}
|
||||||
|
.map_err(|e| format!("{:?}", e))
|
||||||
|
}
|
||||||
|
fn effect_allowed(&self) -> String {
|
||||||
|
self.data.effect_allowed()
|
||||||
|
}
|
||||||
|
fn set_effect_allowed(&self, effect: &str) {
|
||||||
|
self.data.set_effect_allowed(effect);
|
||||||
|
}
|
||||||
|
fn drop_effect(&self) -> String {
|
||||||
|
self.data.drop_effect()
|
||||||
|
}
|
||||||
|
fn set_drop_effect(&self, effect: &str) {
|
||||||
|
self.data.set_drop_effect(effect);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn files(&self) -> Vec<FileData> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,11 +1,12 @@
|
|||||||
use crate::{WebFileData, WebFileEngine};
|
use crate::{WebDataTransfer, WebFileData, WebFileEngine};
|
||||||
|
|
||||||
use super::{Synthetic, WebEventExt};
|
use super::{Synthetic, WebEventExt};
|
||||||
use dioxus_html::{
|
use dioxus_html::{
|
||||||
geometry::{ClientPoint, ElementPoint, PagePoint, ScreenPoint},
|
geometry::{ClientPoint, ElementPoint, PagePoint, ScreenPoint},
|
||||||
input_data::{decode_mouse_button_set, MouseButton},
|
input_data::{decode_mouse_button_set, MouseButton},
|
||||||
FileData, HasDragData, HasFileData, HasMouseData, InteractionElementOffset,
|
FileData, HasDataTransferData, HasDragData, HasFileData, HasMouseData,
|
||||||
InteractionLocation, Modifiers, ModifiersInteraction, PointerInteraction,
|
InteractionElementOffset, InteractionLocation, Modifiers, ModifiersInteraction,
|
||||||
|
PointerInteraction,
|
||||||
};
|
};
|
||||||
use web_sys::{DragEvent, FileReader};
|
use web_sys::{DragEvent, FileReader};
|
||||||
|
|
||||||
@@ -72,6 +73,23 @@ impl HasDragData for Synthetic<DragEvent> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl HasDataTransferData for Synthetic<DragEvent> {
|
||||||
|
fn data_transfer(&self) -> dioxus_html::DataTransfer {
|
||||||
|
use wasm_bindgen::JsCast;
|
||||||
|
|
||||||
|
if let Some(target) = self.event.dyn_ref::<web_sys::DragEvent>() {
|
||||||
|
if let Some(data) = target.data_transfer() {
|
||||||
|
let web_data_transfer = WebDataTransfer::new(data);
|
||||||
|
return dioxus_html::DataTransfer::new(web_data_transfer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return an empty DataTransfer if we couldn't get one from the event
|
||||||
|
let web_data_transfer = WebDataTransfer::new(web_sys::DataTransfer::new().unwrap());
|
||||||
|
dioxus_html::DataTransfer::new(web_data_transfer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl HasFileData for Synthetic<DragEvent> {
|
impl HasFileData for Synthetic<DragEvent> {
|
||||||
fn files(&self) -> Vec<FileData> {
|
fn files(&self) -> Vec<FileData> {
|
||||||
use wasm_bindgen::JsCast;
|
use wasm_bindgen::JsCast;
|
||||||
|
|||||||
@@ -30,6 +30,9 @@ pub use history::{HashHistory, WebHistory};
|
|||||||
mod files;
|
mod files;
|
||||||
pub use files::*;
|
pub use files::*;
|
||||||
|
|
||||||
|
mod data_transfer;
|
||||||
|
pub use data_transfer::*;
|
||||||
|
|
||||||
#[cfg(all(feature = "devtools", debug_assertions))]
|
#[cfg(all(feature = "devtools", debug_assertions))]
|
||||||
mod devtools;
|
mod devtools;
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user