Remove native and web features from dioxus-html (#3006)

* remove native and web features from dioxus-html

* Fix downcasting web events

* Fix desktop with tokio disabled
This commit is contained in:
Evan Almloff authored and GitHub committed 2024-10-10 13:35:59 -07:00
1 parent 677e0e7f4f
commit cda8bb24f1
54 files changed
+1768 -1512

No files matched your search

+45 -3
View File
@@ -1,7 +1,5 @@
use dioxus_core::ScopeId;
use dioxus_html::document::{
Document, EvalError, Evaluator, JSOwner, WeakDioxusChannel, WebDioxusChannel,
};
use dioxus_html::document::{Document, EvalError, Evaluator};
use generational_box::{AnyStorage, GenerationalBox, UnsyncStorage};
use js_sys::Function;
use serde::Serialize;
@@ -11,6 +9,50 @@ use std::pin::Pin;
use std::{rc::Rc, str::FromStr};
use wasm_bindgen::prelude::*;
#[wasm_bindgen::prelude::wasm_bindgen]
pub struct JSOwner {
_owner: Box<dyn std::any::Any>,
}
impl JSOwner {
pub fn new(owner: impl std::any::Any) -> Self {
Self {
_owner: Box::new(owner),
}
}
}
#[wasm_bindgen::prelude::wasm_bindgen(module = "/src/js/eval.js")]
extern "C" {
pub type WebDioxusChannel;
#[wasm_bindgen(constructor)]
pub fn new(owner: JSOwner) -> WebDioxusChannel;
#[wasm_bindgen(method, js_name = "rustSend")]
pub fn rust_send(this: &WebDioxusChannel, value: wasm_bindgen::JsValue);
#[wasm_bindgen(method, js_name = "rustRecv")]
pub async fn rust_recv(this: &WebDioxusChannel) -> wasm_bindgen::JsValue;
#[wasm_bindgen(method)]
pub fn send(this: &WebDioxusChannel, value: wasm_bindgen::JsValue);
#[wasm_bindgen(method)]
pub async fn recv(this: &WebDioxusChannel) -> wasm_bindgen::JsValue;
#[wasm_bindgen(method)]
pub fn weak(this: &WebDioxusChannel) -> WeakDioxusChannel;
pub type WeakDioxusChannel;
#[wasm_bindgen(method, js_name = "rustSend")]
pub fn rust_send(this: &WeakDioxusChannel, value: wasm_bindgen::JsValue);
#[wasm_bindgen(method, js_name = "rustRecv")]
pub async fn rust_recv(this: &WeakDioxusChannel) -> wasm_bindgen::JsValue;
}
/// Provides the WebEvalProvider through [`ScopeId::provide_context`].
pub fn init_document() {
let provider: Rc<dyn Document> = Rc::new(WebDocument);
-591
View File
@@ -1,591 +0,0 @@
use std::{any::Any, collections::HashMap};
use dioxus_html::{
point_interaction::{
InteractionElementOffset, InteractionLocation, ModifiersInteraction, PointerInteraction,
},
DragData, FormData, FormValue, HasDragData, HasFileData, HasFormData, HasImageData,
HasMouseData, HtmlEventConverter, ImageData, MountedData, PlatformEventData, ScrollData,
};
use js_sys::Array;
use wasm_bindgen::{prelude::wasm_bindgen, JsCast, JsValue};
use web_sys::{Document, Element, Event, MouseEvent};
pub(crate) struct WebEventConverter;
#[inline(always)]
fn downcast_event(event: &dioxus_html::PlatformEventData) -> &GenericWebSysEvent {
event
.downcast::<GenericWebSysEvent>()
.expect("event should be a GenericWebSysEvent")
}
impl HtmlEventConverter for WebEventConverter {
#[inline(always)]
fn convert_animation_data(
&self,
event: &dioxus_html::PlatformEventData,
) -> dioxus_html::AnimationData {
downcast_event(event).raw.clone().into()
}
#[inline(always)]
fn convert_clipboard_data(
&self,
event: &dioxus_html::PlatformEventData,
) -> dioxus_html::ClipboardData {
downcast_event(event).raw.clone().into()
}
#[inline(always)]
fn convert_composition_data(
&self,
event: &dioxus_html::PlatformEventData,
) -> dioxus_html::CompositionData {
downcast_event(event).raw.clone().into()
}
#[inline(always)]
fn convert_drag_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::DragData {
let event = downcast_event(event);
DragData::new(WebDragData::new(event.raw.clone().unchecked_into()))
}
#[inline(always)]
fn convert_focus_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::FocusData {
downcast_event(event).raw.clone().into()
}
#[inline(always)]
fn convert_form_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::FormData {
let event = downcast_event(event);
FormData::new(WebFormData::new(event.element.clone(), event.raw.clone()))
}
#[inline(always)]
fn convert_image_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::ImageData {
let event = downcast_event(event);
let error = event.raw.type_() == "error";
ImageData::new(WebImageEvent::new(event.raw.clone(), error))
}
#[inline(always)]
fn convert_keyboard_data(
&self,
event: &dioxus_html::PlatformEventData,
) -> dioxus_html::KeyboardData {
downcast_event(event).raw.clone().into()
}
#[inline(always)]
fn convert_media_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::MediaData {
downcast_event(event).raw.clone().into()
}
#[allow(unused_variables)]
#[inline(always)]
fn convert_mounted_data(&self, event: &dioxus_html::PlatformEventData) -> MountedData {
#[cfg(feature = "mounted")]
{
MountedData::from(
event
.downcast::<web_sys::Element>()
.expect("event should be a web_sys::Element"),
)
}
#[cfg(not(feature = "mounted"))]
{
panic!("mounted events are not supported without the mounted feature on the dioxus-web crate enabled")
}
}
#[inline(always)]
fn convert_mouse_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::MouseData {
downcast_event(event).raw.clone().into()
}
#[inline(always)]
fn convert_pointer_data(
&self,
event: &dioxus_html::PlatformEventData,
) -> dioxus_html::PointerData {
downcast_event(event).raw.clone().into()
}
#[inline(always)]
fn convert_resize_data(
&self,
event: &dioxus_html::PlatformEventData,
) -> dioxus_html::ResizeData {
downcast_event(event).raw.clone().into()
}
#[inline(always)]
fn convert_scroll_data(
&self,
event: &dioxus_html::PlatformEventData,
) -> dioxus_html::ScrollData {
ScrollData::from(downcast_event(event).raw.clone())
}
#[inline(always)]
fn convert_selection_data(
&self,
event: &dioxus_html::PlatformEventData,
) -> dioxus_html::SelectionData {
downcast_event(event).raw.clone().into()
}
#[inline(always)]
fn convert_toggle_data(
&self,
event: &dioxus_html::PlatformEventData,
) -> dioxus_html::ToggleData {
downcast_event(event).raw.clone().into()
}
#[inline(always)]
fn convert_touch_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::TouchData {
downcast_event(event).raw.clone().into()
}
#[inline(always)]
fn convert_transition_data(
&self,
event: &dioxus_html::PlatformEventData,
) -> dioxus_html::TransitionData {
downcast_event(event).raw.clone().into()
}
#[inline(always)]
fn convert_wheel_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::WheelData {
downcast_event(event).raw.clone().into()
}
}
/// A extension trait for web-sys events that provides a way to get the event as a web-sys event.
pub trait WebEventExt<E> {
/// Try to downcast this event as a `web-sys` event.
fn try_as_web_event(&self) -> Option<E>;
/// Downcast this event as a `web-sys` event.
#[inline(always)]
fn as_web_event(&self) -> E
where
E: 'static,
{
self.try_as_web_event().unwrap_or_else(|| {
panic!(
"Error downcasting to `web-sys`, event should be a {}.",
std::any::type_name::<E>()
)
})
}
}
impl WebEventExt<web_sys::AnimationEvent> for dioxus_html::AnimationData {
#[inline(always)]
fn try_as_web_event(&self) -> Option<web_sys::AnimationEvent> {
self.downcast::<web_sys::AnimationEvent>().cloned()
}
}
impl WebEventExt<web_sys::Event> for dioxus_html::ClipboardData {
#[inline(always)]
fn try_as_web_event(&self) -> Option<web_sys::Event> {
self.downcast::<web_sys::Event>().cloned()
}
}
impl WebEventExt<web_sys::CompositionEvent> for dioxus_html::CompositionData {
#[inline(always)]
fn try_as_web_event(&self) -> Option<web_sys::CompositionEvent> {
self.downcast::<web_sys::CompositionEvent>().cloned()
}
}
impl WebEventExt<web_sys::MouseEvent> for dioxus_html::DragData {
#[inline(always)]
fn try_as_web_event(&self) -> Option<web_sys::MouseEvent> {
self.downcast::<WebDragData>()
.map(|data| &data.raw)
.cloned()
}
}
impl WebEventExt<web_sys::FocusEvent> for dioxus_html::FocusData {
#[inline(always)]
fn try_as_web_event(&self) -> Option<web_sys::FocusEvent> {
self.downcast::<web_sys::FocusEvent>().cloned()
}
}
impl WebEventExt<web_sys::Event> for dioxus_html::FormData {
#[inline(always)]
fn try_as_web_event(&self) -> Option<web_sys::Event> {
self.downcast::<web_sys::Event>().cloned()
}
}
impl WebEventExt<WebImageEvent> for dioxus_html::ImageData {
#[inline(always)]
fn try_as_web_event(&self) -> Option<WebImageEvent> {
self.downcast::<WebImageEvent>().cloned()
}
}
impl WebEventExt<web_sys::KeyboardEvent> for dioxus_html::KeyboardData {
#[inline(always)]
fn try_as_web_event(&self) -> Option<web_sys::KeyboardEvent> {
self.downcast::<web_sys::KeyboardEvent>().cloned()
}
}
impl WebEventExt<web_sys::Event> for dioxus_html::MediaData {
#[inline(always)]
fn try_as_web_event(&self) -> Option<web_sys::Event> {
self.downcast::<web_sys::Event>().cloned()
}
}
impl WebEventExt<web_sys::Element> for MountedData {
#[inline(always)]
fn try_as_web_event(&self) -> Option<web_sys::Element> {
self.downcast::<web_sys::Element>().cloned()
}
}
impl WebEventExt<web_sys::MouseEvent> for dioxus_html::MouseData {
#[inline(always)]
fn try_as_web_event(&self) -> Option<web_sys::MouseEvent> {
self.downcast::<web_sys::MouseEvent>().cloned()
}
}
impl WebEventExt<web_sys::PointerEvent> for dioxus_html::PointerData {
#[inline(always)]
fn try_as_web_event(&self) -> Option<web_sys::PointerEvent> {
self.downcast::<web_sys::PointerEvent>().cloned()
}
}
impl WebEventExt<web_sys::Event> for ScrollData {
#[inline(always)]
fn try_as_web_event(&self) -> Option<web_sys::Event> {
self.downcast::<web_sys::Event>().cloned()
}
}
impl WebEventExt<web_sys::Event> for dioxus_html::SelectionData {
#[inline(always)]
fn try_as_web_event(&self) -> Option<web_sys::Event> {
self.downcast::<web_sys::Event>().cloned()
}
}
impl WebEventExt<web_sys::Event> for dioxus_html::ToggleData {
#[inline(always)]
fn try_as_web_event(&self) -> Option<web_sys::Event> {
self.downcast::<web_sys::Event>().cloned()
}
}
impl WebEventExt<web_sys::TouchEvent> for dioxus_html::TouchData {
#[inline(always)]
fn try_as_web_event(&self) -> Option<web_sys::TouchEvent> {
self.downcast::<web_sys::TouchEvent>().cloned()
}
}
impl WebEventExt<web_sys::TransitionEvent> for dioxus_html::TransitionData {
#[inline(always)]
fn try_as_web_event(&self) -> Option<web_sys::TransitionEvent> {
self.downcast::<web_sys::TransitionEvent>().cloned()
}
}
impl WebEventExt<web_sys::WheelEvent> for dioxus_html::WheelData {
#[inline(always)]
fn try_as_web_event(&self) -> Option<web_sys::WheelEvent> {
self.downcast::<web_sys::WheelEvent>().cloned()
}
}
impl WebEventExt<web_sys::ResizeObserverEntry> for dioxus_html::ResizeData {
#[inline(always)]
fn try_as_web_event(&self) -> Option<web_sys::ResizeObserverEntry> {
self.downcast::<web_sys::CustomEvent>()
.and_then(|e| e.detail().dyn_into::<web_sys::ResizeObserverEntry>().ok())
}
}
struct GenericWebSysEvent {
raw: Event,
element: Element,
}
// todo: some of these events are being casted to the wrong event type.
// We need tests that simulate clicks/etc and make sure every event type works.
pub(crate) fn virtual_event_from_websys_event(
event: web_sys::Event,
target: Element,
) -> PlatformEventData {
PlatformEventData::new(Box::new(GenericWebSysEvent {
raw: event,
element: target,
}))
}
pub(crate) fn load_document() -> Document {
web_sys::window()
.expect("should have access to the Window")
.document()
.expect("should have access to the Document")
}
#[derive(Clone)]
struct WebImageEvent {
raw: Event,
error: bool,
}
impl WebImageEvent {
fn new(raw: Event, error: bool) -> Self {
Self { raw, error }
}
}
impl HasImageData for WebImageEvent {
fn load_error(&self) -> bool {
self.error
}
fn as_any(&self) -> &dyn Any {
&self.raw as &dyn Any
}
}
struct WebFormData {
element: Element,
raw: Event,
}
impl WebFormData {
fn new(element: Element, raw: Event) -> Self {
Self { element, raw }
}
}
impl HasFormData for WebFormData {
fn value(&self) -> String {
let target = &self.element;
target
.dyn_ref()
.map(|input: &web_sys::HtmlInputElement| {
// todo: special case more input types
match input.type_().as_str() {
"checkbox" => {
match input.checked() {
true => "true".to_string(),
false => "false".to_string(),
}
},
_ => {
input.value()
}
}
})
.or_else(|| {
target
.dyn_ref()
.map(|input: &web_sys::HtmlTextAreaElement| input.value())
})
// select elements are NOT input events - because - why woudn't they be??
.or_else(|| {
target
.dyn_ref()
.map(|input: &web_sys::HtmlSelectElement| input.value())
})
.or_else(|| {
target
.dyn_ref::<web_sys::HtmlElement>()
.unwrap()
.text_content()
})
.expect("only an InputElement or TextAreaElement or an element with contenteditable=true can have an oninput event listener")
}
fn values(&self) -> HashMap<String, FormValue> {
let mut values = HashMap::new();
fn insert_value(map: &mut HashMap<String, FormValue>, key: String, new_value: String) {
map.entry(key.clone()).or_default().0.push(new_value);
}
// try to fill in form values
if let Some(form) = self.element.dyn_ref::<web_sys::HtmlFormElement>() {
let form_data = get_form_data(form);
for value in form_data.entries().into_iter().flatten() {
if let Ok(array) = value.dyn_into::<Array>() {
if let Some(name) = array.get(0).as_string() {
if let Ok(item_values) = array.get(1).dyn_into::<Array>() {
item_values
.iter()
.filter_map(|v| v.as_string())
.for_each(|v| insert_value(&mut values, name.clone(), v));
} else if let Ok(item_value) = array.get(1).dyn_into::<JsValue>() {
insert_value(&mut values, name, item_value.as_string().unwrap());
}
}
}
}
} else if let Some(select) = self.element.dyn_ref::<web_sys::HtmlSelectElement>() {
// try to fill in select element values
let options = get_select_data(select);
values.insert("options".to_string(), FormValue(options));
}
values
}
fn as_any(&self) -> &dyn Any {
&self.raw as &dyn Any
}
}
impl HasFileData for WebFormData {
#[cfg(feature = "file_engine")]
fn files(&self) -> Option<std::sync::Arc<dyn dioxus_html::FileEngine>> {
let files = self
.element
.dyn_ref()
.and_then(|input: &web_sys::HtmlInputElement| {
input.files().and_then(|files| {
#[allow(clippy::arc_with_non_send_sync)]
dioxus_html::WebFileEngine::new(files).map(|f| {
std::sync::Arc::new(f) as std::sync::Arc<dyn dioxus_html::FileEngine>
})
})
});
files
}
}
struct WebDragData {
raw: MouseEvent,
}
impl WebDragData {
fn new(raw: MouseEvent) -> Self {
Self { raw }
}
}
impl HasDragData for WebDragData {
fn as_any(&self) -> &dyn std::any::Any {
&self.raw as &dyn std::any::Any
}
}
impl HasMouseData for WebDragData {
fn as_any(&self) -> &dyn std::any::Any {
&self.raw as &dyn std::any::Any
}
}
impl PointerInteraction for WebDragData {
fn trigger_button(&self) -> Option<dioxus_html::input_data::MouseButton> {
self.raw.trigger_button()
}
fn held_buttons(&self) -> dioxus_html::input_data::MouseButtonSet {
self.raw.held_buttons()
}
}
impl ModifiersInteraction for WebDragData {
fn modifiers(&self) -> dioxus_html::prelude::Modifiers {
self.raw.modifiers()
}
}
impl InteractionElementOffset for WebDragData {
fn coordinates(&self) -> dioxus_html::geometry::Coordinates {
self.raw.coordinates()
}
fn element_coordinates(&self) -> dioxus_html::geometry::ElementPoint {
self.raw.element_coordinates()
}
}
impl InteractionLocation for WebDragData {
fn client_coordinates(&self) -> dioxus_html::geometry::ClientPoint {
self.raw.client_coordinates()
}
fn screen_coordinates(&self) -> dioxus_html::geometry::ScreenPoint {
self.raw.screen_coordinates()
}
fn page_coordinates(&self) -> dioxus_html::geometry::PagePoint {
self.raw.page_coordinates()
}
}
impl HasFileData for WebDragData {
#[cfg(feature = "file_engine")]
fn files(&self) -> Option<std::sync::Arc<dyn dioxus_html::FileEngine>> {
let files = self
.raw
.dyn_ref::<web_sys::DragEvent>()
.and_then(|drag_event| {
drag_event.data_transfer().and_then(|dt| {
dt.files().and_then(|files| {
#[allow(clippy::arc_with_non_send_sync)]
dioxus_html::WebFileEngine::new(files).map(|f| {
std::sync::Arc::new(f) as std::sync::Arc<dyn dioxus_html::FileEngine>
})
})
})
});
files
}
}
// web-sys does not expose the keys api for form data, so we need to manually bind to it
#[wasm_bindgen(inline_js = r#"
export function get_form_data(form) {
let values = new Map();
const formData = new FormData(form);
for (let name of formData.keys()) {
values.set(name, formData.getAll(name));
}
return values;
}
"#)]
extern "C" {
fn get_form_data(form: &web_sys::HtmlFormElement) -> js_sys::Map;
}
// web-sys does not expose the keys api for select data, so we need to manually bind to it
#[wasm_bindgen(inline_js = r#"
export function get_select_data(select) {
let values = [];
for (let i = 0; i < select.options.length; i++) {
let option = select.options[i];
if (option.selected) {
values.push(option.value.toString());
}
}
return values;
}
"#)]
extern "C" {
fn get_select_data(select: &web_sys::HtmlSelectElement) -> Vec<String>;
}
+32
View File
@@ -0,0 +1,32 @@
use dioxus_html::HasAnimationData;
use web_sys::AnimationEvent;
use super::{Synthetic, WebEventExt};
impl HasAnimationData for Synthetic<AnimationEvent> {
fn animation_name(&self) -> String {
self.event.animation_name()
}
fn pseudo_element(&self) -> String {
self.event.pseudo_element()
}
fn elapsed_time(&self) -> f32 {
self.event.elapsed_time()
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
}
impl WebEventExt for dioxus_html::AnimationData {
type WebEvent = web_sys::AnimationEvent;
#[inline(always)]
fn try_as_web_event(&self) -> Option<web_sys::AnimationEvent> {
self.downcast::<Synthetic<web_sys::AnimationEvent>>()
.map(|e| e.event.clone())
}
}
+26
View File
@@ -0,0 +1,26 @@
use dioxus_html::HasClipboardData;
use web_sys::Event;
use super::{Synthetic, WebEventExt};
impl From<&Event> for Synthetic<Event> {
fn from(e: &Event) -> Self {
Synthetic::new(e.clone())
}
}
impl HasClipboardData for Synthetic<Event> {
fn as_any(&self) -> &dyn std::any::Any {
self
}
}
impl WebEventExt for dioxus_html::ClipboardData {
type WebEvent = web_sys::Event;
#[inline(always)]
fn try_as_web_event(&self) -> Option<Self::WebEvent> {
self.downcast::<Synthetic<web_sys::Event>>()
.map(|e| e.event.clone())
}
}
+24
View File
@@ -0,0 +1,24 @@
use dioxus_html::HasCompositionData;
use web_sys::CompositionEvent;
use super::{Synthetic, WebEventExt};
impl HasCompositionData for Synthetic<CompositionEvent> {
fn data(&self) -> std::string::String {
self.event.data().unwrap_or_default()
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
}
impl WebEventExt for dioxus_html::CompositionData {
type WebEvent = web_sys::CompositionEvent;
#[inline(always)]
fn try_as_web_event(&self) -> Option<Self::WebEvent> {
self.downcast::<Synthetic<web_sys::CompositionEvent>>()
.map(|e| e.event.clone())
}
}
+108
View File
@@ -0,0 +1,108 @@
use dioxus_html::{
prelude::{
InteractionElementOffset, InteractionLocation, ModifiersInteraction, PointerInteraction,
},
HasDragData, HasFileData, HasMouseData,
};
use web_sys::MouseEvent;
use super::{Synthetic, WebEventExt};
pub(crate) struct WebDragData {
raw: Synthetic<MouseEvent>,
}
impl WebDragData {
pub fn new(raw: MouseEvent) -> Self {
Self {
raw: Synthetic::new(raw),
}
}
}
impl HasDragData for WebDragData {
fn as_any(&self) -> &dyn std::any::Any {
&self.raw as &dyn std::any::Any
}
}
impl HasMouseData for WebDragData {
fn as_any(&self) -> &dyn std::any::Any {
&self.raw as &dyn std::any::Any
}
}
impl PointerInteraction for WebDragData {
fn trigger_button(&self) -> Option<dioxus_html::input_data::MouseButton> {
self.raw.trigger_button()
}
fn held_buttons(&self) -> dioxus_html::input_data::MouseButtonSet {
self.raw.held_buttons()
}
}
impl ModifiersInteraction for WebDragData {
fn modifiers(&self) -> dioxus_html::prelude::Modifiers {
self.raw.modifiers()
}
}
impl InteractionElementOffset for WebDragData {
fn coordinates(&self) -> dioxus_html::geometry::Coordinates {
self.raw.coordinates()
}
fn element_coordinates(&self) -> dioxus_html::geometry::ElementPoint {
self.raw.element_coordinates()
}
}
impl InteractionLocation for WebDragData {
fn client_coordinates(&self) -> dioxus_html::geometry::ClientPoint {
self.raw.client_coordinates()
}
fn screen_coordinates(&self) -> dioxus_html::geometry::ScreenPoint {
self.raw.screen_coordinates()
}
fn page_coordinates(&self) -> dioxus_html::geometry::PagePoint {
self.raw.page_coordinates()
}
}
impl HasFileData for WebDragData {
#[cfg(feature = "file_engine")]
fn files(&self) -> Option<std::sync::Arc<dyn dioxus_html::FileEngine>> {
use wasm_bindgen::JsCast;
let files = self
.raw
.event
.dyn_ref::<web_sys::DragEvent>()
.and_then(|drag_event| {
drag_event.data_transfer().and_then(|dt| {
dt.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
}
}
impl WebEventExt for dioxus_html::DragData {
type WebEvent = web_sys::MouseEvent;
#[inline(always)]
fn try_as_web_event(&self) -> Option<web_sys::MouseEvent> {
self.downcast::<WebDragData>()
.map(|data| &data.raw.event)
.cloned()
}
}
+24
View File
@@ -0,0 +1,24 @@
use dioxus_html::HasFileData;
use super::Synthetic;
impl HasFileData for Synthetic<web_sys::Event> {
#[cfg(feature = "file_engine")]
fn files(&self) -> Option<std::sync::Arc<dyn dioxus_html::FileEngine>> {
use wasm_bindgen::JsCast;
let files = self
.event
.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
}
}
+19
View File
@@ -0,0 +1,19 @@
use dioxus_html::HasFocusData;
use super::{Synthetic, WebEventExt};
impl HasFocusData for Synthetic<web_sys::FocusEvent> {
fn as_any(&self) -> &dyn std::any::Any {
self
}
}
impl WebEventExt for dioxus_html::FocusData {
type WebEvent = web_sys::FocusEvent;
#[inline(always)]
fn try_as_web_event(&self) -> Option<Self::WebEvent> {
self.downcast::<Synthetic<web_sys::FocusEvent>>()
.map(|e| e.event.clone())
}
}
+160
View File
@@ -0,0 +1,160 @@
use std::{any::Any, collections::HashMap};
use dioxus_html::{FormValue, HasFileData, HasFormData};
use js_sys::Array;
use wasm_bindgen::JsValue;
use wasm_bindgen::{prelude::wasm_bindgen, JsCast};
use web_sys::{Element, Event};
use super::WebEventExt;
pub(crate) struct WebFormData {
element: Element,
raw: Event,
}
impl WebEventExt for dioxus_html::FormData {
type WebEvent = Event;
#[inline(always)]
fn try_as_web_event(&self) -> Option<Self::WebEvent> {
self.downcast::<WebFormData>().map(|e| e.raw.clone())
}
}
impl WebFormData {
pub fn new(element: Element, raw: Event) -> Self {
Self { element, raw }
}
}
impl HasFormData for WebFormData {
fn value(&self) -> String {
let target = &self.element;
target
.dyn_ref()
.map(|input: &web_sys::HtmlInputElement| {
// todo: special case more input types
match input.type_().as_str() {
"checkbox" => {
match input.checked() {
true => "true".to_string(),
false => "false".to_string(),
}
},
_ => {
input.value()
}
}
})
.or_else(|| {
target
.dyn_ref()
.map(|input: &web_sys::HtmlTextAreaElement| input.value())
})
// select elements are NOT input events - because - why woudn't they be??
.or_else(|| {
target
.dyn_ref()
.map(|input: &web_sys::HtmlSelectElement| input.value())
})
.or_else(|| {
target
.dyn_ref::<web_sys::HtmlElement>()
.unwrap()
.text_content()
})
.expect("only an InputElement or TextAreaElement or an element with contenteditable=true can have an oninput event listener")
}
fn values(&self) -> HashMap<String, FormValue> {
let mut values = HashMap::new();
fn insert_value(map: &mut HashMap<String, FormValue>, key: String, new_value: String) {
map.entry(key.clone()).or_default().0.push(new_value);
}
// try to fill in form values
if let Some(form) = self.element.dyn_ref::<web_sys::HtmlFormElement>() {
let form_data = get_form_data(form);
for value in form_data.entries().into_iter().flatten() {
if let Ok(array) = value.dyn_into::<Array>() {
if let Some(name) = array.get(0).as_string() {
if let Ok(item_values) = array.get(1).dyn_into::<Array>() {
item_values
.iter()
.filter_map(|v| v.as_string())
.for_each(|v| insert_value(&mut values, name.clone(), v));
} else if let Ok(item_value) = array.get(1).dyn_into::<JsValue>() {
insert_value(&mut values, name, item_value.as_string().unwrap());
}
}
}
}
} else if let Some(select) = self.element.dyn_ref::<web_sys::HtmlSelectElement>() {
// try to fill in select element values
let options = get_select_data(select);
values.insert("options".to_string(), FormValue(options));
}
values
}
fn as_any(&self) -> &dyn Any {
&self.raw as &dyn Any
}
}
impl HasFileData for WebFormData {
#[cfg(feature = "file_engine")]
fn files(&self) -> Option<std::sync::Arc<dyn dioxus_html::FileEngine>> {
let files = self
.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
}
}
// web-sys does not expose the keys api for select data, so we need to manually bind to it
#[wasm_bindgen(inline_js = r#"
export function get_select_data(select) {
let values = [];
for (let i = 0; i < select.options.length; i++) {
let option = select.options[i];
if (option.selected) {
values.push(option.value.toString());
}
}
return values;
}
"#)]
extern "C" {
fn get_select_data(select: &web_sys::HtmlSelectElement) -> Vec<String>;
}
// web-sys does not expose the keys api for form data, so we need to manually bind to it
#[wasm_bindgen(inline_js = r#"
export function get_form_data(form) {
let values = new Map();
const formData = new FormData(form);
for (let name of formData.keys()) {
values.set(name, formData.getAll(name));
}
return values;
}
"#)]
extern "C" {
fn get_form_data(form: &web_sys::HtmlFormElement) -> js_sys::Map;
}
+67
View File
@@ -0,0 +1,67 @@
use std::str::FromStr;
use dioxus_html::{
input_data::decode_key_location,
prelude::{Code, Key, Location, Modifiers, ModifiersInteraction},
HasKeyboardData,
};
use web_sys::KeyboardEvent;
use super::{Synthetic, WebEventExt};
impl HasKeyboardData for Synthetic<KeyboardEvent> {
fn key(&self) -> Key {
Key::from_str(self.event.key().as_str()).unwrap_or(Key::Unidentified)
}
fn code(&self) -> Code {
Code::from_str(self.event.code().as_str()).unwrap_or(Code::Unidentified)
}
fn location(&self) -> Location {
decode_key_location(self.event.location() as usize)
}
fn is_auto_repeating(&self) -> bool {
self.event.repeat()
}
fn is_composing(&self) -> bool {
self.event.is_composing()
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
}
impl ModifiersInteraction for Synthetic<KeyboardEvent> {
fn modifiers(&self) -> Modifiers {
let mut modifiers = Modifiers::empty();
if self.event.alt_key() {
modifiers.insert(Modifiers::ALT);
}
if self.event.ctrl_key() {
modifiers.insert(Modifiers::CONTROL);
}
if self.event.meta_key() {
modifiers.insert(Modifiers::META);
}
if self.event.shift_key() {
modifiers.insert(Modifiers::SHIFT);
}
modifiers
}
}
impl WebEventExt for dioxus_html::KeyboardData {
type WebEvent = web_sys::KeyboardEvent;
#[inline(always)]
fn try_as_web_event(&self) -> Option<web_sys::KeyboardEvent> {
self.downcast::<Synthetic<web_sys::KeyboardEvent>>()
.map(|e| e.event.clone())
}
}
+37
View File
@@ -0,0 +1,37 @@
use std::any::Any;
use dioxus_html::HasImageData;
use web_sys::Event;
use super::WebEventExt;
#[derive(Clone)]
pub(crate) struct WebImageEvent {
raw: Event,
error: bool,
}
impl WebImageEvent {
pub fn new(raw: Event, error: bool) -> Self {
Self { raw, error }
}
}
impl HasImageData for WebImageEvent {
fn load_error(&self) -> bool {
self.error
}
fn as_any(&self) -> &dyn Any {
&self.raw as &dyn Any
}
}
impl WebEventExt for dioxus_html::ImageData {
type WebEvent = Event;
#[inline(always)]
fn try_as_web_event(&self) -> Option<Event> {
self.downcast::<WebImageEvent>().map(|e| e.raw.clone())
}
}
+19
View File
@@ -0,0 +1,19 @@
use dioxus_html::HasMediaData;
use super::{Synthetic, WebEventExt};
impl HasMediaData for Synthetic<web_sys::Event> {
fn as_any(&self) -> &dyn std::any::Any {
self
}
}
impl WebEventExt for dioxus_html::MediaData {
type WebEvent = web_sys::Event;
#[inline(always)]
fn try_as_web_event(&self) -> Option<Self::WebEvent> {
self.downcast::<Synthetic<web_sys::Event>>()
.map(|e| e.event.clone())
}
}
+278
View File
@@ -0,0 +1,278 @@
use dioxus_html::{
DragData, FormData, HtmlEventConverter, ImageData, MountedData, PlatformEventData,
};
use drag::WebDragData;
use form::WebFormData;
use load::WebImageEvent;
use wasm_bindgen::JsCast;
use web_sys::{Document, Element, Event};
mod animation;
mod clipboard;
mod composition;
mod drag;
mod file;
mod focus;
mod form;
mod keyboard;
mod load;
mod media;
#[cfg(feature = "mounted")]
mod mounted;
mod mouse;
mod pointer;
mod resize;
mod selection;
mod toggle;
mod touch;
mod transition;
mod wheel;
/// A wrapper for the websys event that allows us to give it the impls from dioxus-html
pub(crate) struct Synthetic<T: 'static> {
/// The inner web sys event that the synthetic event wraps
pub event: T,
}
impl<T: 'static> Synthetic<T> {
/// Create a new synthetic event from a web sys event
pub fn new(event: T) -> Self {
Self { event }
}
}
pub(crate) struct WebEventConverter;
#[inline(always)]
fn downcast_event(event: &dioxus_html::PlatformEventData) -> &GenericWebSysEvent {
event
.downcast::<GenericWebSysEvent>()
.expect("event should be a GenericWebSysEvent")
}
impl HtmlEventConverter for WebEventConverter {
#[inline(always)]
fn convert_animation_data(
&self,
event: &dioxus_html::PlatformEventData,
) -> dioxus_html::AnimationData {
Synthetic::<web_sys::AnimationEvent>::from(downcast_event(event).raw.clone()).into()
}
#[inline(always)]
fn convert_clipboard_data(
&self,
event: &dioxus_html::PlatformEventData,
) -> dioxus_html::ClipboardData {
Synthetic::new(downcast_event(event).raw.clone()).into()
}
#[inline(always)]
fn convert_composition_data(
&self,
event: &dioxus_html::PlatformEventData,
) -> dioxus_html::CompositionData {
Synthetic::<web_sys::CompositionEvent>::from(downcast_event(event).raw.clone()).into()
}
#[inline(always)]
fn convert_drag_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::DragData {
let event = downcast_event(event);
DragData::new(WebDragData::new(event.raw.clone().unchecked_into()))
}
#[inline(always)]
fn convert_focus_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::FocusData {
Synthetic::<web_sys::FocusEvent>::from(downcast_event(event).raw.clone()).into()
}
#[inline(always)]
fn convert_form_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::FormData {
let event = downcast_event(event);
FormData::new(WebFormData::new(event.element.clone(), event.raw.clone()))
}
#[inline(always)]
fn convert_image_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::ImageData {
let event = downcast_event(event);
let error = event.raw.type_() == "error";
ImageData::new(WebImageEvent::new(event.raw.clone(), error))
}
#[inline(always)]
fn convert_keyboard_data(
&self,
event: &dioxus_html::PlatformEventData,
) -> dioxus_html::KeyboardData {
Synthetic::<web_sys::KeyboardEvent>::from(downcast_event(event).raw.clone()).into()
}
#[inline(always)]
fn convert_media_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::MediaData {
Synthetic::new(downcast_event(event).raw.clone()).into()
}
#[allow(unused_variables)]
#[inline(always)]
fn convert_mounted_data(&self, event: &dioxus_html::PlatformEventData) -> MountedData {
#[cfg(feature = "mounted")]
{
Synthetic::new(
event
.downcast::<web_sys::Element>()
.expect("event should be a web_sys::Element")
.clone(),
)
.into()
}
#[cfg(not(feature = "mounted"))]
{
panic!("mounted events are not supported without the mounted feature on the dioxus-web crate enabled")
}
}
#[inline(always)]
fn convert_mouse_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::MouseData {
Synthetic::<web_sys::MouseEvent>::from(downcast_event(event).raw.clone()).into()
}
#[inline(always)]
fn convert_pointer_data(
&self,
event: &dioxus_html::PlatformEventData,
) -> dioxus_html::PointerData {
Synthetic::<web_sys::PointerEvent>::from(downcast_event(event).raw.clone()).into()
}
#[inline(always)]
fn convert_resize_data(
&self,
event: &dioxus_html::PlatformEventData,
) -> dioxus_html::ResizeData {
Synthetic::<web_sys::ResizeObserverEntry>::from(downcast_event(event).raw.clone()).into()
}
#[inline(always)]
fn convert_scroll_data(
&self,
event: &dioxus_html::PlatformEventData,
) -> dioxus_html::ScrollData {
Synthetic::new(downcast_event(event).raw.clone()).into()
}
#[inline(always)]
fn convert_selection_data(
&self,
event: &dioxus_html::PlatformEventData,
) -> dioxus_html::SelectionData {
Synthetic::new(downcast_event(event).raw.clone()).into()
}
#[inline(always)]
fn convert_toggle_data(
&self,
event: &dioxus_html::PlatformEventData,
) -> dioxus_html::ToggleData {
Synthetic::new(downcast_event(event).raw.clone()).into()
}
#[inline(always)]
fn convert_touch_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::TouchData {
Synthetic::<web_sys::TouchEvent>::from(downcast_event(event).raw.clone()).into()
}
#[inline(always)]
fn convert_transition_data(
&self,
event: &dioxus_html::PlatformEventData,
) -> dioxus_html::TransitionData {
Synthetic::<web_sys::TransitionEvent>::from(downcast_event(event).raw.clone()).into()
}
#[inline(always)]
fn convert_wheel_data(&self, event: &dioxus_html::PlatformEventData) -> dioxus_html::WheelData {
Synthetic::<web_sys::WheelEvent>::from(downcast_event(event).raw.clone()).into()
}
}
/// A extension trait for web-sys events that provides a way to get the event as a web-sys event.
pub trait WebEventExt {
/// The web specific event type
type WebEvent;
/// Try to downcast this event as a `web-sys` event.
fn try_as_web_event(&self) -> Option<Self::WebEvent>;
/// Downcast this event as a `web-sys` event.
#[inline(always)]
fn as_web_event(&self) -> Self::WebEvent
where
Self::WebEvent: 'static,
{
self.try_as_web_event().unwrap_or_else(|| {
panic!(
"Error downcasting to `web-sys`, event should be a {}.",
std::any::type_name::<Self::WebEvent>()
)
})
}
}
struct GenericWebSysEvent {
raw: Event,
element: Element,
}
// todo: some of these events are being casted to the wrong event type.
// We need tests that simulate clicks/etc and make sure every event type works.
pub(crate) fn virtual_event_from_websys_event(
event: web_sys::Event,
target: Element,
) -> PlatformEventData {
PlatformEventData::new(Box::new(GenericWebSysEvent {
raw: event,
element: target,
}))
}
pub(crate) fn load_document() -> Document {
web_sys::window()
.expect("should have access to the Window")
.document()
.expect("should have access to the Document")
}
macro_rules! uncheck_convert {
($t:ty) => {
impl From<Event> for Synthetic<$t> {
#[inline]
fn from(e: Event) -> Self {
let e: $t = e.unchecked_into();
Self::new(e)
}
}
impl From<&Event> for Synthetic<$t> {
#[inline]
fn from(e: &Event) -> Self {
let e: &$t = e.unchecked_ref();
Self::new(e.clone())
}
}
};
($($t:ty),+ $(,)?) => {
$(uncheck_convert!($t);)+
};
}
uncheck_convert![
web_sys::CompositionEvent,
web_sys::KeyboardEvent,
web_sys::TouchEvent,
web_sys::PointerEvent,
web_sys::WheelEvent,
web_sys::AnimationEvent,
web_sys::TransitionEvent,
web_sys::MouseEvent,
web_sys::FocusEvent,
];
+126
View File
@@ -0,0 +1,126 @@
use dioxus_html::{
geometry::euclid::{Point2D, Size2D},
MountedData,
};
use wasm_bindgen::JsCast;
use super::{Synthetic, WebEventExt};
impl dioxus_html::RenderedElementBacking for Synthetic<web_sys::Element> {
fn get_scroll_offset(
&self,
) -> std::pin::Pin<
Box<
dyn std::future::Future<
Output = dioxus_html::MountedResult<dioxus_html::geometry::PixelsVector2D>,
>,
>,
> {
let left = self.event.scroll_left();
let top = self.event.scroll_top();
let result = Ok(dioxus_html::geometry::PixelsVector2D::new(
left as f64,
top as f64,
));
Box::pin(async { result })
}
fn get_scroll_size(
&self,
) -> std::pin::Pin<
Box<
dyn std::future::Future<
Output = dioxus_html::MountedResult<dioxus_html::geometry::PixelsSize>,
>,
>,
> {
let width = self.event.scroll_width();
let height = self.event.scroll_height();
let result = Ok(dioxus_html::geometry::PixelsSize::new(
width as f64,
height as f64,
));
Box::pin(async { result })
}
fn get_client_rect(
&self,
) -> std::pin::Pin<
Box<
dyn std::future::Future<
Output = dioxus_html::MountedResult<dioxus_html::geometry::PixelsRect>,
>,
>,
> {
let rect = self.event.get_bounding_client_rect();
let result = Ok(dioxus_html::geometry::PixelsRect::new(
Point2D::new(rect.left(), rect.top()),
Size2D::new(rect.width(), rect.height()),
));
Box::pin(async { result })
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn scroll_to(
&self,
behavior: dioxus_html::ScrollBehavior,
) -> std::pin::Pin<Box<dyn std::future::Future<Output = dioxus_html::MountedResult<()>>>> {
let options = web_sys::ScrollIntoViewOptions::new();
match behavior {
dioxus_html::ScrollBehavior::Instant => {
options.set_behavior(web_sys::ScrollBehavior::Instant);
}
dioxus_html::ScrollBehavior::Smooth => {
options.set_behavior(web_sys::ScrollBehavior::Smooth);
}
}
self.event
.scroll_into_view_with_scroll_into_view_options(&options);
Box::pin(async { Ok(()) })
}
fn set_focus(
&self,
focus: bool,
) -> std::pin::Pin<Box<dyn std::future::Future<Output = dioxus_html::MountedResult<()>>>> {
#[derive(Debug)]
struct FocusError(wasm_bindgen::JsValue);
impl std::fmt::Display for FocusError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "failed to focus element {:?}", self.0)
}
}
impl std::error::Error for FocusError {}
let result = self
.event
.dyn_ref::<web_sys::HtmlElement>()
.ok_or_else(|| {
dioxus_html::MountedError::OperationFailed(Box::new(FocusError(
self.event.clone().into(),
)))
})
.and_then(|e| {
(if focus { e.focus() } else { e.blur() }).map_err(|err| {
dioxus_html::MountedError::OperationFailed(Box::new(FocusError(err)))
})
});
Box::pin(async { result })
}
}
impl WebEventExt for MountedData {
type WebEvent = web_sys::Element;
#[inline(always)]
fn try_as_web_event(&self) -> Option<web_sys::Element> {
self.downcast::<Synthetic<web_sys::Element>>()
.map(|e| e.event.clone())
}
}
+87
View File
@@ -0,0 +1,87 @@
use dioxus_html::{
geometry::{ClientPoint, ElementPoint, PagePoint, ScreenPoint},
input_data::{decode_mouse_button_set, MouseButton},
prelude::{
InteractionElementOffset, InteractionLocation, Modifiers, ModifiersInteraction,
PointerInteraction,
},
HasDragData, HasFileData, HasMouseData,
};
use web_sys::MouseEvent;
use super::{Synthetic, WebEventExt};
impl InteractionLocation for Synthetic<MouseEvent> {
fn client_coordinates(&self) -> ClientPoint {
ClientPoint::new(self.event.client_x().into(), self.event.client_y().into())
}
fn page_coordinates(&self) -> PagePoint {
PagePoint::new(self.event.page_x().into(), self.event.page_y().into())
}
fn screen_coordinates(&self) -> ScreenPoint {
ScreenPoint::new(self.event.screen_x().into(), self.event.screen_y().into())
}
}
impl InteractionElementOffset for Synthetic<MouseEvent> {
fn element_coordinates(&self) -> ElementPoint {
ElementPoint::new(self.event.offset_x().into(), self.event.offset_y().into())
}
}
impl ModifiersInteraction for Synthetic<MouseEvent> {
fn modifiers(&self) -> Modifiers {
let mut modifiers = Modifiers::empty();
if self.event.alt_key() {
modifiers.insert(Modifiers::ALT);
}
if self.event.ctrl_key() {
modifiers.insert(Modifiers::CONTROL);
}
if self.event.meta_key() {
modifiers.insert(Modifiers::META);
}
if self.event.shift_key() {
modifiers.insert(Modifiers::SHIFT);
}
modifiers
}
}
impl PointerInteraction for Synthetic<MouseEvent> {
fn held_buttons(&self) -> dioxus_html::input_data::MouseButtonSet {
decode_mouse_button_set(self.event.buttons())
}
fn trigger_button(&self) -> Option<MouseButton> {
Some(MouseButton::from_web_code(self.event.button()))
}
}
impl HasMouseData for Synthetic<MouseEvent> {
fn as_any(&self) -> &dyn std::any::Any {
self
}
}
impl HasFileData for Synthetic<MouseEvent> {}
impl HasDragData for Synthetic<MouseEvent> {
fn as_any(&self) -> &dyn std::any::Any {
self
}
}
impl WebEventExt for dioxus_html::MouseData {
type WebEvent = web_sys::MouseEvent;
#[inline(always)]
fn try_as_web_event(&self) -> Option<web_sys::MouseEvent> {
self.downcast::<Synthetic<web_sys::MouseEvent>>()
.map(|e| e.event.clone())
}
}
+119
View File
@@ -0,0 +1,119 @@
use dioxus_html::{
geometry::{ClientPoint, ElementPoint, PagePoint, ScreenPoint},
input_data::{decode_mouse_button_set, MouseButton},
prelude::{
InteractionElementOffset, InteractionLocation, Modifiers, ModifiersInteraction,
PointerInteraction,
},
HasPointerData,
};
use web_sys::PointerEvent;
use super::{Synthetic, WebEventExt};
impl HasPointerData for Synthetic<PointerEvent> {
fn pointer_id(&self) -> i32 {
self.event.pointer_id()
}
fn width(&self) -> i32 {
self.event.width()
}
fn height(&self) -> i32 {
self.event.height()
}
fn pressure(&self) -> f32 {
self.event.pressure()
}
fn tangential_pressure(&self) -> f32 {
self.event.tangential_pressure()
}
fn tilt_x(&self) -> i32 {
self.event.tilt_x()
}
fn tilt_y(&self) -> i32 {
self.event.tilt_y()
}
fn twist(&self) -> i32 {
self.event.twist()
}
fn pointer_type(&self) -> String {
self.event.pointer_type()
}
fn is_primary(&self) -> bool {
self.event.is_primary()
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
}
impl InteractionLocation for Synthetic<PointerEvent> {
fn client_coordinates(&self) -> ClientPoint {
ClientPoint::new(self.event.client_x().into(), self.event.client_y().into())
}
fn screen_coordinates(&self) -> ScreenPoint {
ScreenPoint::new(self.event.screen_x().into(), self.event.screen_y().into())
}
fn page_coordinates(&self) -> PagePoint {
PagePoint::new(self.event.page_x().into(), self.event.page_y().into())
}
}
impl InteractionElementOffset for Synthetic<PointerEvent> {
fn element_coordinates(&self) -> ElementPoint {
ElementPoint::new(self.event.offset_x().into(), self.event.offset_y().into())
}
}
impl ModifiersInteraction for Synthetic<PointerEvent> {
fn modifiers(&self) -> Modifiers {
let mut modifiers = Modifiers::empty();
if self.event.alt_key() {
modifiers.insert(Modifiers::ALT);
}
if self.event.ctrl_key() {
modifiers.insert(Modifiers::CONTROL);
}
if self.event.meta_key() {
modifiers.insert(Modifiers::META);
}
if self.event.shift_key() {
modifiers.insert(Modifiers::SHIFT);
}
modifiers
}
}
impl PointerInteraction for Synthetic<PointerEvent> {
fn held_buttons(&self) -> dioxus_html::input_data::MouseButtonSet {
decode_mouse_button_set(self.event.buttons())
}
fn trigger_button(&self) -> Option<MouseButton> {
Some(MouseButton::from_web_code(self.event.button()))
}
}
impl WebEventExt for dioxus_html::PointerData {
type WebEvent = web_sys::PointerEvent;
#[inline(always)]
fn try_as_web_event(&self) -> Option<web_sys::PointerEvent> {
self.downcast::<Synthetic<web_sys::PointerEvent>>()
.map(|e| e.event.clone())
}
}
+57
View File
@@ -0,0 +1,57 @@
use dioxus_html::{geometry::PixelsSize, HasResizeData, ResizeResult};
use wasm_bindgen::JsCast;
use web_sys::{CustomEvent, Event, ResizeObserverEntry};
use super::{Synthetic, WebEventExt};
impl From<Event> for Synthetic<ResizeObserverEntry> {
#[inline]
fn from(e: Event) -> Self {
<Synthetic<ResizeObserverEntry> as From<&Event>>::from(&e)
}
}
impl From<&Event> for Synthetic<ResizeObserverEntry> {
#[inline]
fn from(e: &Event) -> Self {
let e: &CustomEvent = e.unchecked_ref();
let value = e.detail();
Self::new(value.unchecked_into::<ResizeObserverEntry>())
}
}
impl HasResizeData for Synthetic<ResizeObserverEntry> {
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn get_border_box_size(&self) -> ResizeResult<PixelsSize> {
extract_first_size(self.event.border_box_size())
}
fn get_content_box_size(&self) -> ResizeResult<PixelsSize> {
extract_first_size(self.event.content_box_size())
}
}
impl WebEventExt for dioxus_html::ResizeData {
type WebEvent = web_sys::ResizeObserverEntry;
#[inline(always)]
fn try_as_web_event(&self) -> Option<web_sys::ResizeObserverEntry> {
self.downcast::<Synthetic<web_sys::ResizeObserverEntry>>()
.map(|e| e.event.clone())
}
}
fn extract_first_size(resize_observer_output: js_sys::Array) -> ResizeResult<PixelsSize> {
let first = resize_observer_output.get(0);
let size = first.unchecked_into::<web_sys::ResizeObserverSize>();
// inline_size matches the width of the element if its writing-mode is horizontal, the height otherwise
let inline_size = size.inline_size();
// block_size matches the height of the element if its writing-mode is horizontal, the width otherwise
let block_size = size.block_size();
Ok(PixelsSize::new(inline_size, block_size))
}
+19
View File
@@ -0,0 +1,19 @@
use dioxus_html::HasSelectionData;
use super::{Synthetic, WebEventExt};
impl HasSelectionData for Synthetic<web_sys::Event> {
fn as_any(&self) -> &dyn std::any::Any {
self
}
}
impl WebEventExt for dioxus_html::SelectionData {
type WebEvent = web_sys::Event;
#[inline(always)]
fn try_as_web_event(&self) -> Option<Self::WebEvent> {
self.downcast::<Synthetic<web_sys::Event>>()
.map(|e| e.event.clone())
}
}
+19
View File
@@ -0,0 +1,19 @@
use dioxus_html::HasToggleData;
use super::{Synthetic, WebEventExt};
impl HasToggleData for Synthetic<web_sys::Event> {
fn as_any(&self) -> &dyn std::any::Any {
self
}
}
impl WebEventExt for dioxus_html::ToggleData {
type WebEvent = web_sys::Event;
#[inline(always)]
fn try_as_web_event(&self) -> Option<Self::WebEvent> {
self.downcast::<Synthetic<web_sys::Event>>()
.map(|e| e.event.clone())
}
}
+111
View File
@@ -0,0 +1,111 @@
use dioxus_html::{
geometry::{ClientPoint, PagePoint, ScreenPoint},
prelude::{InteractionLocation, Modifiers, ModifiersInteraction},
HasTouchPointData, TouchPoint,
};
use web_sys::{Touch, TouchEvent};
use super::{Synthetic, WebEventExt};
impl ModifiersInteraction for Synthetic<TouchEvent> {
fn modifiers(&self) -> Modifiers {
let mut modifiers = Modifiers::empty();
if self.event.alt_key() {
modifiers.insert(Modifiers::ALT);
}
if self.event.ctrl_key() {
modifiers.insert(Modifiers::CONTROL);
}
if self.event.meta_key() {
modifiers.insert(Modifiers::META);
}
if self.event.shift_key() {
modifiers.insert(Modifiers::SHIFT);
}
modifiers
}
}
impl dioxus_html::events::HasTouchData for Synthetic<TouchEvent> {
fn touches(&self) -> Vec<TouchPoint> {
let touches = TouchEvent::touches(&self.event);
let mut result = Vec::with_capacity(touches.length() as usize);
for i in 0..touches.length() {
let touch = touches.get(i).unwrap();
result.push(TouchPoint::new(Synthetic::new(touch)));
}
result
}
fn touches_changed(&self) -> Vec<TouchPoint> {
let touches = self.event.changed_touches();
let mut result = Vec::with_capacity(touches.length() as usize);
for i in 0..touches.length() {
let touch = touches.get(i).unwrap();
result.push(TouchPoint::new(Synthetic::new(touch)));
}
result
}
fn target_touches(&self) -> Vec<TouchPoint> {
let touches = self.event.target_touches();
let mut result = Vec::with_capacity(touches.length() as usize);
for i in 0..touches.length() {
let touch = touches.get(i).unwrap();
result.push(TouchPoint::new(Synthetic::new(touch)));
}
result
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
}
impl HasTouchPointData for Synthetic<Touch> {
fn identifier(&self) -> i32 {
self.event.identifier()
}
fn radius(&self) -> ScreenPoint {
ScreenPoint::new(self.event.radius_x().into(), self.event.radius_y().into())
}
fn rotation(&self) -> f64 {
self.event.rotation_angle() as f64
}
fn force(&self) -> f64 {
self.event.force() as f64
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
}
impl InteractionLocation for Synthetic<Touch> {
fn client_coordinates(&self) -> ClientPoint {
ClientPoint::new(self.event.client_x().into(), self.event.client_y().into())
}
fn screen_coordinates(&self) -> ScreenPoint {
ScreenPoint::new(self.event.screen_x().into(), self.event.screen_y().into())
}
fn page_coordinates(&self) -> PagePoint {
PagePoint::new(self.event.page_x().into(), self.event.page_y().into())
}
}
impl WebEventExt for dioxus_html::TouchData {
type WebEvent = web_sys::TouchEvent;
#[inline(always)]
fn try_as_web_event(&self) -> Option<web_sys::TouchEvent> {
self.downcast::<Synthetic<web_sys::TouchEvent>>()
.map(|e| e.event.clone())
}
}
+22
View File
@@ -0,0 +1,22 @@
use dioxus_html::HasTransitionData;
use web_sys::TransitionEvent;
use super::Synthetic;
impl HasTransitionData for Synthetic<TransitionEvent> {
fn elapsed_time(&self) -> f32 {
self.event.elapsed_time()
}
fn property_name(&self) -> String {
self.event.property_name()
}
fn pseudo_element(&self) -> String {
self.event.pseudo_element()
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
}
+110
View File
@@ -0,0 +1,110 @@
use dioxus_html::{
geometry::{ClientPoint, ElementPoint, PagePoint, ScreenPoint},
input_data::{decode_mouse_button_set, MouseButton},
prelude::{
InteractionElementOffset, InteractionLocation, Modifiers, ModifiersInteraction,
PointerInteraction,
},
HasMouseData, HasScrollData, HasWheelData,
};
use web_sys::{Event, WheelEvent};
use super::{Synthetic, WebEventExt};
impl HasWheelData for Synthetic<WheelEvent> {
fn delta(&self) -> dioxus_html::geometry::WheelDelta {
dioxus_html::geometry::WheelDelta::from_web_attributes(
self.event.delta_mode(),
self.event.delta_x(),
self.event.delta_y(),
self.event.delta_z(),
)
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
}
impl HasMouseData for Synthetic<WheelEvent> {
fn as_any(&self) -> &dyn std::any::Any {
self
}
}
impl InteractionLocation for Synthetic<WheelEvent> {
fn client_coordinates(&self) -> ClientPoint {
ClientPoint::new(self.event.client_x().into(), self.event.client_y().into())
}
fn screen_coordinates(&self) -> ScreenPoint {
ScreenPoint::new(self.event.screen_x().into(), self.event.screen_y().into())
}
fn page_coordinates(&self) -> PagePoint {
PagePoint::new(self.event.page_x().into(), self.event.page_y().into())
}
}
impl InteractionElementOffset for Synthetic<WheelEvent> {
fn element_coordinates(&self) -> ElementPoint {
ElementPoint::new(self.event.offset_x().into(), self.event.offset_y().into())
}
}
impl ModifiersInteraction for Synthetic<WheelEvent> {
fn modifiers(&self) -> Modifiers {
let mut modifiers = Modifiers::empty();
if self.event.alt_key() {
modifiers.insert(Modifiers::ALT);
}
if self.event.ctrl_key() {
modifiers.insert(Modifiers::CONTROL);
}
if self.event.meta_key() {
modifiers.insert(Modifiers::META);
}
if self.event.shift_key() {
modifiers.insert(Modifiers::SHIFT);
}
modifiers
}
}
impl PointerInteraction for Synthetic<WheelEvent> {
fn held_buttons(&self) -> dioxus_html::input_data::MouseButtonSet {
decode_mouse_button_set(self.event.buttons())
}
fn trigger_button(&self) -> Option<MouseButton> {
Some(MouseButton::from_web_code(self.event.button()))
}
}
impl HasScrollData for Synthetic<Event> {
fn as_any(&self) -> &dyn std::any::Any {
self
}
}
impl WebEventExt for dioxus_html::ScrollData {
type WebEvent = web_sys::Event;
#[inline(always)]
fn try_as_web_event(&self) -> Option<Self::WebEvent> {
self.downcast::<Synthetic<web_sys::Event>>()
.map(|e| e.event.clone())
}
}
impl WebEventExt for dioxus_html::WheelData {
type WebEvent = web_sys::WheelEvent;
#[inline(always)]
fn try_as_web_event(&self) -> Option<Self::WebEvent> {
self.downcast::<Synthetic<web_sys::WheelEvent>>()
.map(|e| e.event.clone())
}
}
+133
View File
@@ -0,0 +1,133 @@
use std::any::Any;
use dioxus_html::FileEngine;
use futures_channel::oneshot;
use js_sys::Uint8Array;
use wasm_bindgen::{prelude::Closure, JsCast};
use web_sys::{File, FileList, FileReader};
/// A file engine for the web platform
pub struct WebFileEngine {
file_reader: FileReader,
file_list: FileList,
}
impl WebFileEngine {
/// Create a new file engine from a file list
pub fn new(file_list: FileList) -> Option<Self> {
Some(Self {
file_list,
file_reader: FileReader::new().ok()?,
})
}
fn len(&self) -> usize {
self.file_list.length() as usize
}
fn get(&self, index: usize) -> Option<File> {
self.file_list.item(index as u32)
}
fn find(&self, name: &str) -> Option<File> {
(0..self.len())
.filter_map(|i| self.get(i))
.find(|f| f.name() == name)
}
}
#[async_trait::async_trait(?Send)]
impl FileEngine for WebFileEngine {
fn files(&self) -> Vec<String> {
(0..self.len())
.filter_map(|i| self.get(i).map(|f| f.name()))
.collect()
}
async fn file_size(&self, file: &str) -> Option<u64> {
let file = self.find(file)?;
Some(file.size() as u64)
}
// read a file to bytes
async fn read_file(&self, file: &str) -> Option<Vec<u8>> {
let file = self.find(file)?;
let file_reader = self.file_reader.clone();
let (rx, tx) = oneshot::channel();
let on_load: Closure<dyn FnMut()> = Closure::new({
let mut rx = Some(rx);
move || {
let result = file_reader.result();
let _ = rx
.take()
.expect("multiple files read without refreshing the channel")
.send(result);
}
});
self.file_reader
.set_onload(Some(on_load.as_ref().unchecked_ref()));
on_load.forget();
self.file_reader.read_as_array_buffer(&file).ok()?;
if let Ok(Ok(js_val)) = tx.await {
let as_u8_arr = Uint8Array::new(&js_val);
let as_u8_vec = as_u8_arr.to_vec();
Some(as_u8_vec)
} else {
None
}
}
// read a file to string
async fn read_file_to_string(&self, file: &str) -> Option<String> {
let file = self.find(file)?;
let file_reader = self.file_reader.clone();
let (rx, tx) = oneshot::channel();
let on_load: Closure<dyn FnMut()> = Closure::new({
let mut rx = Some(rx);
move || {
let result = file_reader.result();
let _ = rx
.take()
.expect("multiple files read without refreshing the channel")
.send(result);
}
});
self.file_reader
.set_onload(Some(on_load.as_ref().unchecked_ref()));
on_load.forget();
self.file_reader.read_as_text(&file).ok()?;
if let Ok(Ok(js_val)) = tx.await {
js_val.as_string()
} else {
None
}
}
async fn get_native_file(&self, file: &str) -> Option<Box<dyn Any>> {
let file = self.find(file)?;
Some(Box::new(file))
}
}
/// Helper trait for WebFileEngine
#[async_trait::async_trait(?Send)]
pub trait WebFileEngineExt {
/// returns web_sys::File
async fn get_web_file(&self, file: &str) -> Option<web_sys::File>;
}
#[async_trait::async_trait(?Send)]
impl WebFileEngineExt for std::sync::Arc<dyn FileEngine> {
async fn get_web_file(&self, file: &str) -> Option<web_sys::File> {
let native_file = self.get_native_file(file).await?;
let ret = native_file.downcast::<web_sys::File>().ok()?;
Some(*ret)
}
}
+1
View File
@@ -0,0 +1 @@
class Channel{pending;waiting;constructor(){this.pending=[],this.waiting=[]}send(data){if(this.waiting.length>0){this.waiting.shift()(data);return}this.pending.push(data)}async recv(){return new Promise((resolve,_reject)=>{if(this.pending.length>0){resolve(this.pending.shift());return}this.waiting.push(resolve)})}}class WeakDioxusChannel{inner;constructor(channel){this.inner=new WeakRef(channel)}rustSend(data){let channel=this.inner.deref();if(channel)channel.rustSend(data)}async rustRecv(){let channel=this.inner.deref();if(channel)return await channel.rustRecv()}}class DioxusChannel{weak(){return new WeakDioxusChannel(this)}}class WebDioxusChannel extends DioxusChannel{js_to_rust;rust_to_js;owner;constructor(owner){super();this.owner=owner,this.js_to_rust=new Channel,this.rust_to_js=new Channel}weak(){return new WeakDioxusChannel(this)}async recv(){return await this.rust_to_js.recv()}send(data){this.js_to_rust.send(data)}rustSend(data){this.rust_to_js.send(data)}async rustRecv(){return await this.js_to_rust.recv()}}export{WebDioxusChannel};
+1
View File
@@ -0,0 +1 @@
[3479327739946104450]
+6 -2
View File
@@ -29,15 +29,19 @@ use futures_util::{pin_mut, select, FutureExt, StreamExt};
mod cfg;
mod dom;
mod event;
mod events;
pub mod launch;
mod mutations;
pub use event::*;
pub use events::*;
#[cfg(feature = "document")]
mod document;
#[cfg(feature = "file_engine")]
mod file_engine;
#[cfg(feature = "document")]
pub use document::WebDocument;
#[cfg(feature = "file_engine")]
pub use file_engine::*;
#[cfg(all(feature = "devtools", debug_assertions))]
mod devtools;
+43
View File
@@ -0,0 +1,43 @@
import {
DioxusChannel,
Channel,
WeakDioxusChannel,
} from "../../../html/src/ts/eval";
export class WebDioxusChannel extends DioxusChannel {
js_to_rust: Channel;
rust_to_js: Channel;
owner: any;
constructor(owner: any) {
super();
this.owner = owner;
this.js_to_rust = new Channel();
this.rust_to_js = new Channel();
}
// Return a weak reference to this channel
weak(): WeakDioxusChannel {
return new WeakDioxusChannel(this);
}
// Receive message from Rust
async recv() {
return await this.rust_to_js.recv();
}
// Send message to rust.
send(data: any) {
this.js_to_rust.send(data);
}
// Send data from rust to javascript
rustSend(data: any) {
this.rust_to_js.send(data);
}
// Receive data sent from javascript in rust
async rustRecv(): Promise<any> {
return await this.js_to_rust.recv();
}
}