Merge branch 'master' into add-file-data-drag-event

This commit is contained in:
Evan Almloff committed 2023-09-17 18:56:59 -05:00
commit 847801908f
575 files changed
+12880 -17751

No files matched your search

+31 -24
View File
@@ -1,64 +1,71 @@
[package]
name = "dioxus-html"
version = "0.3.1"
version = { workspace = true }
authors = ["Jonathan Kelley"]
edition = "2018"
description = "HTML Element pack for Dioxus - a concurrent renderer-agnostic Virtual DOM for interactive user experiences"
license = "MIT/Apache-2.0"
license = "MIT OR Apache-2.0"
repository = "https://github.com/DioxusLabs/dioxus/"
homepage = "https://dioxuslabs.com"
keywords = ["dom", "ui", "gui", "react"]
[dependencies]
dioxus-core = { workspace = true }
dioxus-rsx = { workspace = true, optional = true }
dioxus-rsx = { workspace = true, features = ["hot_reload"], optional = true }
serde = { version = "1", features = ["derive"], optional = true }
serde_repr = { version = "0.1", optional = true }
wasm-bindgen = { workspace = true, optional = true }
euclid = "0.22.7"
enumset = "1.0.11"
keyboard-types = "0.6.2"
keyboard-types = "0.7"
async-trait = "0.1.58"
serde-value = "0.7.0"
tokio = { workspace = true, features = ["fs", "io-util"], optional = true }
rfd = { version = "0.11.3", optional = true }
async-channel = "1.8.0"
serde_json = { version = "1", optional = true }
[dependencies.web-sys]
optional = true
version = "0.3.56"
features = [
"TouchEvent",
"MouseEvent",
"InputEvent",
"ClipboardEvent",
"KeyboardEvent",
"TouchEvent",
"WheelEvent",
"AnimationEvent",
"TransitionEvent",
"PointerEvent",
"FocusEvent",
"CompositionEvent",
"ClipboardEvent",
"DragEvent",
"Element",
"DomRect",
"ScrollIntoViewOptions",
"ScrollLogicalPosition",
"ScrollBehavior",
"TouchEvent",
"MouseEvent",
"InputEvent",
"ClipboardEvent",
"DragEvent",
"KeyboardEvent",
"TouchEvent",
"WheelEvent",
"AnimationEvent",
"TransitionEvent",
"PointerEvent",
"FocusEvent",
"CompositionEvent",
"ClipboardEvent",
]
[dev-dependencies]
serde_json = "1"
[features]
default = ["serialize"]
default = ["serialize", "mounted"]
serialize = [
"serde",
"serde_repr",
"serde_json",
"euclid/serde",
"keyboard-types/serde",
"dioxus-core/serialize",
]
mounted = [
"web-sys/Element",
"web-sys/DomRect",
"web-sys/ScrollIntoViewOptions",
"web-sys/ScrollLogicalPosition",
"web-sys/ScrollBehavior",
"web-sys/HtmlElement",
]
wasm-bind = ["web-sys", "wasm-bindgen"]
native-bind = ["tokio"]
hot-reload-context = ["dioxus-rsx"]
+4 -11
View File
@@ -1,6 +1,5 @@
# `dioxus-html`: Html (and SVG) Namespace for Dioxus
[![Crates.io][crates-badge]][crates-url]
[![MIT licensed][mit-badge]][mit-url]
[![Build Status][actions-badge]][actions-url]
@@ -8,22 +7,18 @@
[crates-badge]: https://img.shields.io/crates/v/dioxus-html.svg
[crates-url]: https://crates.io/crates/dioxus-html
[mit-badge]: https://img.shields.io/badge/license-MIT-blue.svg
[mit-url]: https://github.com/dioxuslabs/dioxus/blob/master/LICENSE
[actions-badge]: https://github.com/dioxuslabs/dioxus/actions/workflows/main.yml/badge.svg
[actions-url]: https://github.com/dioxuslabs/dioxus/actions?query=workflow%3ACI+branch%3Amaster
[discord-badge]: https://img.shields.io/discord/899851952891002890.svg?logo=discord&style=flat-square
[discord-url]: https://discord.gg/XgGxMSkvUM
[Website](https://dioxuslabs.com) |
[Guides](https://dioxuslabs.com/docs/0.3/guide/en/) |
[Guides](https://dioxuslabs.com/learn/0.4/) |
[API Docs](https://docs.rs/dioxus-html/latest/dioxus_html) |
[Chat](https://discord.gg/XgGxMSkvUM)
## Overview
The Dioxus `rsx!` and `html!` macros can accept any compile-time correct namespace on top of NodeFactory. This crate provides the HTML (and SVG) namespaces which get imported in the Dioxus prelude.
@@ -64,6 +59,7 @@ All elements should be defined as a zero-sized-struct (also known as unit struct
Attributes would then be implemented as constants on these unit structs.
The HTML namespace is defined mostly with macros. However, the expanded form would look something like this:
```rust
struct base;
impl DioxusElement for base {
@@ -75,8 +71,8 @@ impl base {
const target: (&'static str, Option<'static str>, bool) = ("target", None, false);
}
```
Because attributes are defined as methods on the unit struct, they guard the attribute creation behind a compile-time correct interface.
Because attributes are defined as methods on the unit struct, they guard the attribute creation behind a compile-time correct interface.
## How to extend it:
@@ -93,16 +89,13 @@ mod dioxus_elements {
}
```
## Contributing
- Report issues on our [issue tracker](https://github.com/dioxuslabs/dioxus/issues).
- Join the discord and ask questions!
## License
This project is licensed under the [MIT license].
[mit license]: https://github.com/DioxusLabs/dioxus/blob/master/LICENSE-MIT
+3 -1
View File
@@ -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,
};
+96
View File
@@ -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),
}
+6 -7
View File
@@ -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,
)
}
)*
};
+10 -1
View File
@@ -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 -1
View File
@@ -1,4 +1,4 @@
//! Handles quering data from the renderer
//! Handles querying data from the renderer
use euclid::Rect;
+1 -1
View File
@@ -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
+3
View File
@@ -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))
}
}
+22 -19
View 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 })
}