Merge branch 'master' into jk/breaking-merged
This commit is contained in:
211 files changed
+21135
-3770
No files matched your search
@@ -1,9 +1,14 @@
|
||||
#![allow(non_upper_case_globals)]
|
||||
|
||||
use dioxus_core::prelude::IntoAttributeValue;
|
||||
use dioxus_core::HasAttributes;
|
||||
use dioxus_html_internal_macro::impl_extension_attributes;
|
||||
#[cfg(feature = "hot-reload-context")]
|
||||
use dioxus_rsx::HotReloadingContext;
|
||||
|
||||
#[cfg(feature = "hot-reload-context")]
|
||||
use crate::{map_global_attributes, map_svg_attributes};
|
||||
use crate::{GlobalAttributes, SvgAttributes};
|
||||
#[cfg(feature = "hot-reload-context")]
|
||||
use dioxus_rsx::HotReloadingContext;
|
||||
|
||||
pub type AttributeDiscription = (&'static str, Option<&'static str>, bool);
|
||||
|
||||
@@ -378,6 +383,13 @@ macro_rules! builder_constructors {
|
||||
}
|
||||
);
|
||||
)*
|
||||
|
||||
pub(crate) mod extensions {
|
||||
use super::*;
|
||||
$(
|
||||
impl_extension_attributes![ELEMENT $name { $($fil,)* }];
|
||||
)*
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ macro_rules! impl_event {
|
||||
$data:ty;
|
||||
$(
|
||||
$( #[$attr:meta] )*
|
||||
$name:ident
|
||||
$name:ident $(: $js_name:literal)?
|
||||
)*
|
||||
) => {
|
||||
$(
|
||||
@@ -20,10 +20,17 @@ macro_rules! impl_event {
|
||||
}),
|
||||
None,
|
||||
false,
|
||||
)
|
||||
).into()
|
||||
}
|
||||
)*
|
||||
};
|
||||
|
||||
(@name $name:ident $js_name:literal) => {
|
||||
$js_name
|
||||
};
|
||||
(@name $name:ident) => {
|
||||
stringify!($name)
|
||||
};
|
||||
}
|
||||
|
||||
static EVENT_CONVERTER: RwLock<Option<Box<dyn HtmlEventConverter>>> = RwLock::new(None);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use crate::file_data::{FileEngine, HasFileData};
|
||||
use crate::geometry::{ClientPoint, Coordinates, ElementPoint, PagePoint, ScreenPoint};
|
||||
use crate::input_data::{MouseButton, MouseButtonSet};
|
||||
use crate::prelude::*;
|
||||
@@ -53,7 +54,13 @@ impl DragData {
|
||||
|
||||
/// Downcast this event data to a specific type
|
||||
pub fn downcast<T: 'static>(&self) -> Option<&T> {
|
||||
self.inner.as_any().downcast_ref::<T>()
|
||||
HasDragData::as_any(&*self.inner).downcast_ref::<T>()
|
||||
}
|
||||
}
|
||||
|
||||
impl HasFileData for DragData {
|
||||
fn files(&self) -> Option<std::sync::Arc<dyn FileEngine>> {
|
||||
self.inner.files()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,19 +110,34 @@ impl PointerInteraction for DragData {
|
||||
#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Clone)]
|
||||
pub struct SerializedDragData {
|
||||
mouse: crate::point_interaction::SerializedPointInteraction,
|
||||
files: Option<crate::file_data::SerializedFileEngine>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
impl From<&DragData> for SerializedDragData {
|
||||
fn from(data: &DragData) -> Self {
|
||||
impl SerializedDragData {
|
||||
fn new(drag: &DragData, files: Option<crate::file_data::SerializedFileEngine>) -> Self {
|
||||
Self {
|
||||
mouse: crate::point_interaction::SerializedPointInteraction::from(data),
|
||||
mouse: crate::point_interaction::SerializedPointInteraction::from(drag),
|
||||
files,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
impl HasDragData for SerializedDragData {}
|
||||
impl HasDragData for SerializedDragData {
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
impl HasFileData for SerializedDragData {
|
||||
fn files(&self) -> Option<std::sync::Arc<dyn FileEngine>> {
|
||||
self.files
|
||||
.as_ref()
|
||||
.map(|files| std::sync::Arc::new(files.clone()) as _)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
impl HasMouseData for SerializedDragData {
|
||||
@@ -171,7 +193,7 @@ impl PointerInteraction for SerializedDragData {
|
||||
#[cfg(feature = "serialize")]
|
||||
impl serde::Serialize for DragData {
|
||||
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
SerializedDragData::from(self).serialize(serializer)
|
||||
SerializedDragData::new(self, None).serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,7 +208,10 @@ impl<'de> serde::Deserialize<'de> for DragData {
|
||||
}
|
||||
|
||||
/// A trait for any object that has the data for a drag event
|
||||
pub trait HasDragData: HasMouseData {}
|
||||
pub trait HasDragData: HasMouseData + HasFileData {
|
||||
/// return self as Any
|
||||
fn as_any(&self) -> &dyn std::any::Any;
|
||||
}
|
||||
|
||||
impl_event! {
|
||||
DragData;
|
||||
|
||||
@@ -1,9 +1,25 @@
|
||||
use std::{any::Any, collections::HashMap, fmt::Debug};
|
||||
use crate::file_data::FileEngine;
|
||||
use crate::file_data::HasFileData;
|
||||
use std::{collections::HashMap, fmt::Debug};
|
||||
|
||||
use dioxus_core::Event;
|
||||
|
||||
pub type FormEvent = Event<FormData>;
|
||||
|
||||
/// A form value that may either be a list of values or a single value
|
||||
#[cfg_attr(
|
||||
feature = "serialize",
|
||||
derive(serde::Serialize, serde::Deserialize),
|
||||
// this will serialize Text(String) -> String and VecText(Vec<String>) to Vec<String>
|
||||
serde(untagged)
|
||||
)]
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum FormValue {
|
||||
Text(String),
|
||||
VecText(Vec<String>),
|
||||
}
|
||||
|
||||
/* DOMEvent: Send + SyncTarget relatedTarget */
|
||||
pub struct FormData {
|
||||
inner: Box<dyn HasFormData>,
|
||||
}
|
||||
@@ -43,7 +59,7 @@ impl FormData {
|
||||
}
|
||||
|
||||
/// Get the values of the form event
|
||||
pub fn values(&self) -> HashMap<String, Vec<String>> {
|
||||
pub fn values(&self) -> HashMap<String, FormValue> {
|
||||
self.inner.values()
|
||||
}
|
||||
|
||||
@@ -59,30 +75,50 @@ impl FormData {
|
||||
}
|
||||
|
||||
/// An object that has all the data for a form event
|
||||
pub trait HasFormData: std::any::Any {
|
||||
pub trait HasFormData: HasFileData + std::any::Any {
|
||||
fn value(&self) -> String {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn values(&self) -> HashMap<String, Vec<String>> {
|
||||
fn values(&self) -> HashMap<String, FormValue> {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn files(&self) -> Option<std::sync::Arc<dyn FileEngine>> {
|
||||
None
|
||||
}
|
||||
|
||||
/// return self as Any
|
||||
fn as_any(&self) -> &dyn std::any::Any;
|
||||
}
|
||||
|
||||
impl FormData {
|
||||
#[cfg(feature = "serialize")]
|
||||
/// Parse the values into a struct with one field per value
|
||||
pub fn parsed_values<T>(&self) -> Result<T, serde_json::Error>
|
||||
where
|
||||
T: serde::de::DeserializeOwned,
|
||||
{
|
||||
use serde::Serialize;
|
||||
|
||||
fn convert_hashmap_to_json<K, V>(hashmap: &HashMap<K, V>) -> serde_json::Result<String>
|
||||
where
|
||||
K: Serialize + std::hash::Hash + Eq,
|
||||
V: Serialize,
|
||||
{
|
||||
serde_json::to_string(hashmap)
|
||||
}
|
||||
|
||||
let parsed_json =
|
||||
convert_hashmap_to_json(&self.values()).expect("Failed to parse values to JSON");
|
||||
|
||||
serde_json::from_str(&parsed_json)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
/// A serialized form data object
|
||||
#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Clone)]
|
||||
pub struct SerializedFormData {
|
||||
value: String,
|
||||
values: HashMap<String, Vec<String>>,
|
||||
files: Option<std::sync::Arc<SerializedFileEngine>>,
|
||||
values: HashMap<String, FormValue>,
|
||||
files: Option<crate::file_data::SerializedFileEngine>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
@@ -90,8 +126,8 @@ impl SerializedFormData {
|
||||
/// Create a new serialized form data object
|
||||
pub fn new(
|
||||
value: String,
|
||||
values: HashMap<String, Vec<String>>,
|
||||
files: Option<std::sync::Arc<SerializedFileEngine>>,
|
||||
values: HashMap<String, FormValue>,
|
||||
files: Option<crate::file_data::SerializedFileEngine>,
|
||||
) -> Self {
|
||||
Self {
|
||||
value,
|
||||
@@ -114,9 +150,9 @@ impl SerializedFormData {
|
||||
resolved_files.insert(file, bytes.unwrap_or_default());
|
||||
}
|
||||
|
||||
Some(std::sync::Arc::new(SerializedFileEngine {
|
||||
Some(crate::file_data::SerializedFileEngine {
|
||||
files: resolved_files,
|
||||
}))
|
||||
})
|
||||
}
|
||||
None => None,
|
||||
},
|
||||
@@ -138,21 +174,24 @@ impl HasFormData for SerializedFormData {
|
||||
self.value.clone()
|
||||
}
|
||||
|
||||
fn values(&self) -> HashMap<String, Vec<String>> {
|
||||
fn values(&self) -> HashMap<String, FormValue> {
|
||||
self.values.clone()
|
||||
}
|
||||
|
||||
fn files(&self) -> Option<std::sync::Arc<dyn FileEngine>> {
|
||||
self.files
|
||||
.as_ref()
|
||||
.map(|files| std::sync::Arc::clone(files) as std::sync::Arc<dyn FileEngine>)
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
impl HasFileData for SerializedFormData {
|
||||
fn files(&self) -> Option<std::sync::Arc<dyn FileEngine>> {
|
||||
self.files
|
||||
.as_ref()
|
||||
.map(|files| std::sync::Arc::new(files.clone()) as _)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
impl serde::Serialize for FormData {
|
||||
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
@@ -170,52 +209,6 @@ impl<'de> serde::Deserialize<'de> for FormData {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
/// A file engine that serializes files to bytes
|
||||
#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Clone)]
|
||||
pub struct SerializedFileEngine {
|
||||
files: HashMap<String, Vec<u8>>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl FileEngine for SerializedFileEngine {
|
||||
fn files(&self) -> Vec<String> {
|
||||
self.files.keys().cloned().collect()
|
||||
}
|
||||
|
||||
async fn read_file(&self, file: &str) -> Option<Vec<u8>> {
|
||||
self.files.get(file).cloned()
|
||||
}
|
||||
|
||||
async fn read_file_to_string(&self, file: &str) -> Option<String> {
|
||||
self.read_file(file)
|
||||
.await
|
||||
.map(|bytes| String::from_utf8_lossy(&bytes).to_string())
|
||||
}
|
||||
|
||||
async fn get_native_file(&self, file: &str) -> Option<Box<dyn Any>> {
|
||||
self.read_file(file)
|
||||
.await
|
||||
.map(|val| Box::new(val) as Box<dyn Any>)
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
pub trait FileEngine {
|
||||
// get a list of file names
|
||||
fn files(&self) -> Vec<String>;
|
||||
|
||||
// read a file to bytes
|
||||
async fn read_file(&self, file: &str) -> Option<Vec<u8>>;
|
||||
|
||||
// read a file to string
|
||||
async fn read_file_to_string(&self, file: &str) -> Option<String>;
|
||||
|
||||
// returns a file in platform's native representation
|
||||
async fn get_native_file(&self, file: &str) -> Option<Box<dyn Any>>;
|
||||
}
|
||||
|
||||
impl_event! {
|
||||
FormData;
|
||||
|
||||
|
||||
@@ -78,6 +78,8 @@ impl_event! {
|
||||
#[deprecated(since = "0.5.0", note = "use ondoubleclick instead")]
|
||||
ondblclick
|
||||
|
||||
ondoubleclick: "ondblclick"
|
||||
|
||||
/// onmousedown
|
||||
onmousedown
|
||||
|
||||
@@ -102,21 +104,6 @@ impl_event! {
|
||||
onmouseup
|
||||
}
|
||||
|
||||
/// ondoubleclick
|
||||
#[inline]
|
||||
pub fn ondoubleclick<E: crate::EventReturn<T>, T>(
|
||||
mut _f: impl FnMut(::dioxus_core::Event<MouseData>) -> E + 'static,
|
||||
) -> ::dioxus_core::Attribute {
|
||||
::dioxus_core::Attribute::new(
|
||||
"ondblclick",
|
||||
dioxus_core::AttributeValue::listener(move |e: ::dioxus_core::Event<MouseData>| {
|
||||
_f(e).spawn();
|
||||
}),
|
||||
None,
|
||||
false,
|
||||
)
|
||||
}
|
||||
|
||||
impl MouseData {
|
||||
/// Create a new instance of MouseData
|
||||
pub fn new(inner: impl HasMouseData + 'static) -> Self {
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
use std::any::Any;
|
||||
|
||||
pub trait HasFileData: std::any::Any {
|
||||
fn files(&self) -> Option<std::sync::Arc<dyn FileEngine>> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
/// A file engine that serializes files to bytes
|
||||
#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Clone)]
|
||||
pub struct SerializedFileEngine {
|
||||
pub files: std::collections::HashMap<String, Vec<u8>>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl FileEngine for SerializedFileEngine {
|
||||
fn files(&self) -> Vec<String> {
|
||||
self.files.keys().cloned().collect()
|
||||
}
|
||||
|
||||
async fn read_file(&self, file: &str) -> Option<Vec<u8>> {
|
||||
self.files.get(file).cloned()
|
||||
}
|
||||
|
||||
async fn read_file_to_string(&self, file: &str) -> Option<String> {
|
||||
self.read_file(file)
|
||||
.await
|
||||
.map(|bytes| String::from_utf8_lossy(&bytes).to_string())
|
||||
}
|
||||
|
||||
async fn get_native_file(&self, file: &str) -> Option<Box<dyn Any>> {
|
||||
self.read_file(file)
|
||||
.await
|
||||
.map(|val| Box::new(val) as Box<dyn Any>)
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
pub trait FileEngine {
|
||||
// get a list of file names
|
||||
fn files(&self) -> Vec<String>;
|
||||
|
||||
// read a file to bytes
|
||||
async fn read_file(&self, file: &str) -> Option<Vec<u8>>;
|
||||
|
||||
// read a file to string
|
||||
async fn read_file_to_string(&self, file: &str) -> Option<String>;
|
||||
|
||||
// returns a file in platform's native representation
|
||||
async fn get_native_file(&self, file: &str) -> Option<Box<dyn Any>>;
|
||||
}
|
||||
@@ -1,5 +1,9 @@
|
||||
#![allow(non_upper_case_globals)]
|
||||
|
||||
use dioxus_core::prelude::IntoAttributeValue;
|
||||
use dioxus_core::HasAttributes;
|
||||
use dioxus_html_internal_macro::impl_extension_attributes;
|
||||
|
||||
use crate::AttributeDiscription;
|
||||
|
||||
#[cfg(feature = "hot-reload-context")]
|
||||
@@ -106,6 +110,8 @@ macro_rules! trait_methods {
|
||||
)*
|
||||
None
|
||||
}
|
||||
|
||||
impl_extension_attributes![GLOBAL $trait { $($name,)* }];
|
||||
};
|
||||
|
||||
// Rename the incoming ident and apply a custom namespace
|
||||
|
||||
@@ -22,6 +22,8 @@ pub use elements::HtmlCtx;
|
||||
#[cfg(feature = "html-to-rsx")]
|
||||
pub use elements::{map_html_attribute_to_rsx, map_html_element_to_rsx};
|
||||
pub mod events;
|
||||
pub(crate) mod file_data;
|
||||
pub use file_data::*;
|
||||
pub mod geometry;
|
||||
mod global_attributes;
|
||||
pub mod input_data;
|
||||
@@ -46,10 +48,17 @@ pub use render_template::*;
|
||||
#[cfg(feature = "eval")]
|
||||
pub mod eval;
|
||||
|
||||
pub mod extensions {
|
||||
pub use crate::elements::extensions::*;
|
||||
pub use crate::global_attributes::{GlobalAttributesExtension, SvgAttributesExtension};
|
||||
}
|
||||
|
||||
pub mod prelude {
|
||||
pub use crate::elements::extensions::*;
|
||||
#[cfg(feature = "eval")]
|
||||
pub use crate::eval::*;
|
||||
pub use crate::events::*;
|
||||
pub use crate::global_attributes::{GlobalAttributesExtension, SvgAttributesExtension};
|
||||
pub use crate::point_interaction::*;
|
||||
pub use keyboard_types::{self, Code, Key, Location, Modifiers};
|
||||
}
|
||||
@@ -1,10 +1,11 @@
|
||||
use std::any::Any;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::FileEngine;
|
||||
use tokio::fs::File;
|
||||
use tokio::io::AsyncReadExt;
|
||||
|
||||
use crate::file_data::FileEngine;
|
||||
|
||||
pub struct NativeFileEngine {
|
||||
files: Vec<PathBuf>,
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ use crate::events::{
|
||||
AnimationData, CompositionData, KeyboardData, MouseData, PointerData, TouchData,
|
||||
TransitionData, WheelData,
|
||||
};
|
||||
use crate::file_data::{FileEngine, HasFileData};
|
||||
use crate::geometry::{ClientPoint, ElementPoint, PagePoint, ScreenPoint};
|
||||
use crate::input_data::{decode_key_location, decode_mouse_button_set, MouseButton};
|
||||
use crate::prelude::*;
|
||||
@@ -103,8 +104,6 @@ impl ModifiersInteraction for KeyboardEvent {
|
||||
}
|
||||
}
|
||||
|
||||
impl HasDragData for MouseEvent {}
|
||||
|
||||
impl InteractionLocation for MouseEvent {
|
||||
fn client_coordinates(&self) -> ClientPoint {
|
||||
ClientPoint::new(self.client_x().into(), self.client_y().into())
|
||||
@@ -162,6 +161,14 @@ impl HasMouseData for MouseEvent {
|
||||
}
|
||||
}
|
||||
|
||||
impl HasFileData for MouseEvent {}
|
||||
|
||||
impl HasDragData for MouseEvent {
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl ModifiersInteraction for TouchEvent {
|
||||
fn modifiers(&self) -> Modifiers {
|
||||
let mut modifiers = Modifiers::empty();
|
||||
@@ -512,3 +519,23 @@ impl HasMediaData for web_sys::Event {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl HasFileData for web_sys::Event {
|
||||
fn files(&self) -> Option<std::sync::Arc<dyn FileEngine>> {
|
||||
#[cfg(not(feature = "file_engine"))]
|
||||
let files = None;
|
||||
#[cfg(feature = "file_engine")]
|
||||
let files = element
|
||||
.dyn_ref()
|
||||
.and_then(|input: &web_sys::HtmlInputElement| {
|
||||
input.files().and_then(|files| {
|
||||
#[allow(clippy::arc_with_non_send_sync)]
|
||||
crate::file_engine::WebFileEngine::new(files).map(|f| {
|
||||
std::sync::Arc::new(f) as std::sync::Arc<dyn dioxus_html::FileEngine>
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
files
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user