Merge branch 'master' into add-file-data-drag-event
This commit is contained in:
commit
847801908f
575 files changed
+12880
-17751
No files matched your search
@@ -332,7 +332,7 @@ builder_constructors! {
|
||||
meta None {
|
||||
charset: String DEFAULT, // FIXME IANA standard names
|
||||
content: String DEFAULT,
|
||||
http_equiv: HTTPEquiv DEFAULT,
|
||||
http_equiv: String "http-equiv",
|
||||
name: Metadata DEFAULT,
|
||||
};
|
||||
|
||||
@@ -1258,6 +1258,8 @@ builder_constructors! {
|
||||
spellcheck: BoolOrDefault DEFAULT,
|
||||
wrap: Wrap DEFAULT,
|
||||
value: String volatile,
|
||||
|
||||
initial_value: String DEFAULT,
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
#![allow(clippy::await_holding_refcell_ref)]
|
||||
|
||||
use async_trait::async_trait;
|
||||
use dioxus_core::ScopeState;
|
||||
use std::future::{Future, IntoFuture};
|
||||
use std::pin::Pin;
|
||||
use std::rc::Rc;
|
||||
|
||||
/// A struct that implements EvalProvider is sent through [`ScopeState`]'s provide_context function
|
||||
/// so that [`use_eval`] can provide a platform agnostic interface for evaluating JavaScript code.
|
||||
pub trait EvalProvider {
|
||||
fn new_evaluator(&self, js: String) -> Result<Rc<dyn Evaluator>, EvalError>;
|
||||
}
|
||||
|
||||
/// The platform's evaluator.
|
||||
#[async_trait(?Send)]
|
||||
pub trait Evaluator {
|
||||
/// Sends a message to the evaluated JavaScript.
|
||||
fn send(&self, data: serde_json::Value) -> Result<(), EvalError>;
|
||||
/// Receive any queued messages from the evaluated JavaScript.
|
||||
async fn recv(&self) -> Result<serde_json::Value, EvalError>;
|
||||
/// Gets the return value of the JavaScript
|
||||
async fn join(&self) -> Result<serde_json::Value, EvalError>;
|
||||
}
|
||||
|
||||
type EvalCreator = Rc<dyn Fn(&str) -> Result<UseEval, EvalError>>;
|
||||
|
||||
/// Get a struct that can execute any JavaScript.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Please be very careful with this function. A script with too many dynamic
|
||||
/// parts is practically asking for a hacker to find an XSS vulnerability in
|
||||
/// it. **This applies especially to web targets, where the JavaScript context
|
||||
/// has access to most, if not all of your application data.**
|
||||
pub fn use_eval(cx: &ScopeState) -> &EvalCreator {
|
||||
&*cx.use_hook(|| {
|
||||
let eval_provider = cx
|
||||
.consume_context::<Rc<dyn EvalProvider>>()
|
||||
.expect("evaluator not provided");
|
||||
|
||||
Rc::new(move |script: &str| {
|
||||
eval_provider
|
||||
.new_evaluator(script.to_string())
|
||||
.map(UseEval::new)
|
||||
}) as Rc<dyn Fn(&str) -> Result<UseEval, EvalError>>
|
||||
})
|
||||
}
|
||||
|
||||
/// A wrapper around the target platform's evaluator.
|
||||
#[derive(Clone)]
|
||||
pub struct UseEval {
|
||||
evaluator: Rc<dyn Evaluator + 'static>,
|
||||
}
|
||||
|
||||
impl UseEval {
|
||||
/// Creates a new UseEval
|
||||
pub fn new(evaluator: Rc<dyn Evaluator + 'static>) -> Self {
|
||||
Self { evaluator }
|
||||
}
|
||||
|
||||
/// Sends a [`serde_json::Value`] to the evaluated JavaScript.
|
||||
pub fn send(&self, data: serde_json::Value) -> Result<(), EvalError> {
|
||||
self.evaluator.send(data)
|
||||
}
|
||||
|
||||
/// Gets an UnboundedReceiver to receive messages from the evaluated JavaScript.
|
||||
pub async fn recv(&self) -> Result<serde_json::Value, EvalError> {
|
||||
self.evaluator.recv().await
|
||||
}
|
||||
|
||||
/// Gets the return value of the evaluated JavaScript.
|
||||
pub async fn join(self) -> Result<serde_json::Value, EvalError> {
|
||||
self.evaluator.join().await
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoFuture for UseEval {
|
||||
type Output = Result<serde_json::Value, EvalError>;
|
||||
type IntoFuture = Pin<Box<dyn Future<Output = Self::Output>>>;
|
||||
|
||||
fn into_future(self) -> Self::IntoFuture {
|
||||
Box::pin(self.join())
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents an error when evaluating JavaScript
|
||||
#[derive(Debug)]
|
||||
pub enum EvalError {
|
||||
/// The provided JavaScript has already been ran.
|
||||
Finished,
|
||||
/// The provided JavaScript is not valid and can't be ran.
|
||||
InvalidJs(String),
|
||||
/// Represents an error communicating between JavaScript and Rust.
|
||||
Communication(String),
|
||||
}
|
||||
@@ -10,15 +10,14 @@ macro_rules! impl_event {
|
||||
$( #[$attr] )*
|
||||
#[inline]
|
||||
pub fn $name<'a, E: crate::EventReturn<T>, T>(_cx: &'a ::dioxus_core::ScopeState, mut _f: impl FnMut(::dioxus_core::Event<$data>) -> E + 'a) -> ::dioxus_core::Attribute<'a> {
|
||||
::dioxus_core::Attribute {
|
||||
name: stringify!($name),
|
||||
value: _cx.listener(move |e: ::dioxus_core::Event<$data>| {
|
||||
::dioxus_core::Attribute::new(
|
||||
stringify!($name),
|
||||
_cx.listener(move |e: ::dioxus_core::Event<$data>| {
|
||||
_f(e).spawn(_cx);
|
||||
}),
|
||||
namespace: None,
|
||||
mounted_element: Default::default(),
|
||||
volatile: false,
|
||||
}
|
||||
None,
|
||||
false,
|
||||
)
|
||||
}
|
||||
)*
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use std::{collections::HashMap, fmt::Debug};
|
||||
use std::{any::Any, collections::HashMap, fmt::Debug};
|
||||
|
||||
use dioxus_core::Event;
|
||||
|
||||
@@ -45,6 +45,12 @@ impl FileEngine for SerializedFileEngine {
|
||||
.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>)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
@@ -89,6 +95,9 @@ pub trait FileEngine {
|
||||
|
||||
// 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! {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! Handles quering data from the renderer
|
||||
//! Handles querying data from the renderer
|
||||
|
||||
use euclid::Rect;
|
||||
|
||||
|
||||
@@ -108,7 +108,7 @@ impl_event! {
|
||||
///
|
||||
/// ## Example
|
||||
/// ```rust, ignore
|
||||
/// rsx!( button { "click me", onclick: move |_| log::info!("Clicked!`") } )
|
||||
/// rsx!( button { "click me", onclick: move |_| tracing::info!("Clicked!`") } )
|
||||
/// ```
|
||||
///
|
||||
/// ## Reference
|
||||
|
||||
@@ -37,6 +37,9 @@ pub use events::*;
|
||||
pub use global_attributes::*;
|
||||
pub use render_template::*;
|
||||
|
||||
mod eval;
|
||||
|
||||
pub mod prelude {
|
||||
pub use crate::eval::*;
|
||||
pub use crate::events::*;
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
use std::any::Any;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::FileEngine;
|
||||
@@ -40,4 +41,9 @@ impl FileEngine for NativeFileEngine {
|
||||
|
||||
Some(contents)
|
||||
}
|
||||
|
||||
async fn get_native_file(&self, file: &str) -> Option<Box<dyn Any>> {
|
||||
let file = File::open(file).await.ok()?;
|
||||
Some(Box::new(file))
|
||||
}
|
||||
}
|
||||
@@ -4,18 +4,14 @@ use crate::events::{
|
||||
};
|
||||
use crate::geometry::{ClientPoint, Coordinates, ElementPoint, PagePoint, ScreenPoint};
|
||||
use crate::input_data::{decode_key_location, decode_mouse_button_set, MouseButton};
|
||||
use crate::{
|
||||
DragData, MountedData, MountedError, MountedResult, RenderedElementBacking, ScrollBehavior,
|
||||
};
|
||||
use crate::{DragData, MountedData};
|
||||
use keyboard_types::{Code, Key, Modifiers};
|
||||
use std::convert::TryInto;
|
||||
use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
use std::str::FromStr;
|
||||
use wasm_bindgen::{JsCast, JsValue};
|
||||
use web_sys::{
|
||||
AnimationEvent, CompositionEvent, Event, KeyboardEvent, MouseEvent, PointerEvent,
|
||||
ScrollIntoViewOptions, TouchEvent, TransitionEvent, WheelEvent,
|
||||
AnimationEvent, CompositionEvent, Event, KeyboardEvent, MouseEvent, PointerEvent, TouchEvent,
|
||||
TransitionEvent, WheelEvent,
|
||||
};
|
||||
|
||||
macro_rules! uncheck_convert {
|
||||
@@ -199,16 +195,20 @@ impl From<&TransitionEvent> for TransitionData {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "mounted")]
|
||||
impl From<&web_sys::Element> for MountedData {
|
||||
fn from(e: &web_sys::Element) -> Self {
|
||||
MountedData::new(e.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderedElementBacking for web_sys::Element {
|
||||
#[cfg(feature = "mounted")]
|
||||
impl crate::RenderedElementBacking for web_sys::Element {
|
||||
fn get_client_rect(
|
||||
&self,
|
||||
) -> Pin<Box<dyn Future<Output = MountedResult<euclid::Rect<f64, f64>>>>> {
|
||||
) -> std::pin::Pin<
|
||||
Box<dyn std::future::Future<Output = crate::MountedResult<euclid::Rect<f64, f64>>>>,
|
||||
> {
|
||||
let rect = self.get_bounding_client_rect();
|
||||
let result = Ok(euclid::Rect::new(
|
||||
euclid::Point2D::new(rect.left(), rect.top()),
|
||||
@@ -217,33 +217,36 @@ impl RenderedElementBacking for web_sys::Element {
|
||||
Box::pin(async { result })
|
||||
}
|
||||
|
||||
fn get_raw_element(&self) -> MountedResult<&dyn std::any::Any> {
|
||||
fn get_raw_element(&self) -> crate::MountedResult<&dyn std::any::Any> {
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
fn scroll_to(
|
||||
&self,
|
||||
behavior: ScrollBehavior,
|
||||
) -> Pin<Box<dyn Future<Output = MountedResult<()>>>> {
|
||||
behavior: crate::ScrollBehavior,
|
||||
) -> std::pin::Pin<Box<dyn std::future::Future<Output = crate::MountedResult<()>>>> {
|
||||
match behavior {
|
||||
ScrollBehavior::Instant => self.scroll_into_view_with_scroll_into_view_options(
|
||||
ScrollIntoViewOptions::new().behavior(web_sys::ScrollBehavior::Instant),
|
||||
crate::ScrollBehavior::Instant => self.scroll_into_view_with_scroll_into_view_options(
|
||||
web_sys::ScrollIntoViewOptions::new().behavior(web_sys::ScrollBehavior::Instant),
|
||||
),
|
||||
ScrollBehavior::Smooth => self.scroll_into_view_with_scroll_into_view_options(
|
||||
ScrollIntoViewOptions::new().behavior(web_sys::ScrollBehavior::Smooth),
|
||||
crate::ScrollBehavior::Smooth => self.scroll_into_view_with_scroll_into_view_options(
|
||||
web_sys::ScrollIntoViewOptions::new().behavior(web_sys::ScrollBehavior::Smooth),
|
||||
),
|
||||
}
|
||||
|
||||
Box::pin(async { Ok(()) })
|
||||
}
|
||||
|
||||
fn set_focus(&self, focus: bool) -> Pin<Box<dyn Future<Output = MountedResult<()>>>> {
|
||||
fn set_focus(
|
||||
&self,
|
||||
focus: bool,
|
||||
) -> std::pin::Pin<Box<dyn std::future::Future<Output = crate::MountedResult<()>>>> {
|
||||
let result = self
|
||||
.dyn_ref::<web_sys::HtmlElement>()
|
||||
.ok_or_else(|| MountedError::OperationFailed(Box::new(FocusError(self.into()))))
|
||||
.ok_or_else(|| crate::MountedError::OperationFailed(Box::new(FocusError(self.into()))))
|
||||
.and_then(|e| {
|
||||
(if focus { e.focus() } else { e.blur() })
|
||||
.map_err(|err| MountedError::OperationFailed(Box::new(FocusError(err))))
|
||||
.map_err(|err| crate::MountedError::OperationFailed(Box::new(FocusError(err))))
|
||||
});
|
||||
Box::pin(async { result })
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user