diff --git a/packages/web/Cargo.toml b/packages/web/Cargo.toml index f4b87d695..1f6607a6e 100644 --- a/packages/web/Cargo.toml +++ b/packages/web/Cargo.toml @@ -74,6 +74,7 @@ separator = "0.4.1" uuid = { version = "0.8.2", features = ["v4", "wasm-bindgen"] } dioxus-hooks = { path = "../hooks" } serde = { version = "1.0.126", features = ["derive"] } +reqwest = { version = "0.11", features = ["json"] } # rand = { version="0.8.4", features=["small_rng"] } # surf = { version = "2.3.1", default-features = false, features = [ diff --git a/packages/web/examples/async_web.rs b/packages/web/examples/async_web.rs index d0dac5440..a19becdf0 100644 --- a/packages/web/examples/async_web.rs +++ b/packages/web/examples/async_web.rs @@ -1,24 +1,13 @@ //! Basic example that renders a simple VNode to the browser. - -use dioxus::events::on::MouseEvent; use dioxus_core as dioxus; use dioxus_core::prelude::*; use dioxus_hooks::*; use dioxus_html as dioxus_elements; -// use wasm_timer; - -use std::future::Future; - -use std::{pin::Pin, time::Duration}; - -use dioxus::prelude::*; fn main() { - // Setup logging console_error_panic_hook::set_once(); wasm_logger::init(wasm_logger::Config::new(log::Level::Debug)); - - dioxus_web::launch(App, |c| c) + dioxus_web::launch(APP, |c| c) } #[derive(serde::Deserialize)] @@ -26,14 +15,13 @@ struct DogApi { message: String, } -const ENDPOINT: &str = "https://dog.ceo/api/breeds/image/random/"; - -static App: FC<()> = |cx, props|{ +static APP: FC<()> = |cx, _props| { let state = use_state(cx, || 0); - let dog_node = use_suspense( + const ENDPOINT: &str = "https://dog.ceo/api/breeds/image/random/"; + let doggo = use_suspense( cx, - || surf::get(ENDPOINT).recv_json::(), + || async { reqwest::get(ENDPOINT).await.unwrap().json::().await }, |cx, res| match res { Ok(res) => rsx!( cx, @@ -45,29 +33,18 @@ static App: FC<()> = |cx, props|{ }, ); - cx.render(rsx! { - div { style: { align_items: "center" } - section { class: "py-12 px-4 text-center" - div { class: "w-full max-w-2xl mx-auto" - span { class: "text-sm font-semibold" - "count: {state}" - } - br {} - div { - button { - onclick: move |_| state.set(state + 1) - "incr" - } - br {} - button { - onclick: move |_| state.set(state - 1) - "decr" - } - } - div { - h1{"doggo!"} - {dog_node} - } + rsx!(cx, div { align_items: "center" + section { class: "py-12 px-4 text-center" + div { class: "w-full max-w-2xl mx-auto" + span { class: "text-sm font-semibold" + "count: {state}" + } + div { + button { onclick: move |_| state.set(state + 1), "incr" } + button { onclick: move |_| state.set(state - 1), "decr" } + } + div { + {doggo} } } } diff --git a/packages/web/examples/basic.rs b/packages/web/examples/basic.rs index f72e75b81..4913657e5 100644 --- a/packages/web/examples/basic.rs +++ b/packages/web/examples/basic.rs @@ -1,18 +1,9 @@ //! Basic example that renders a simple VNode to the browser. - -// all these imports are done automatically with the `dioxus` crate and `prelude` -// need to do them manually for this example -use dioxus::events::on::MouseEvent; use dioxus_core as dioxus; use dioxus_core::prelude::*; use dioxus_hooks::*; use dioxus_html as dioxus_elements; -use dioxus::prelude::*; -use dioxus_web::*; -use std::future::Future; -use std::{pin::Pin, time::Duration}; - fn main() { // Setup logging wasm_logger::init(wasm_logger::Config::new(log::Level::Debug)); @@ -22,12 +13,10 @@ fn main() { dioxus_web::launch(APP, |c| c) } -static APP: FC<()> = |cx, props| { +static APP: FC<()> = |cx, _| { let mut count = use_state(cx, || 3); - let mut content = use_state(cx, || String::from("h1")); - let mut text_content = use_state(cx, || String::from("Hello, world!")); - - log::debug!("running scope..."); + let content = use_state(cx, || String::from("h1")); + let text_content = use_state(cx, || String::from("Hello, world!")); cx.render(rsx! { div { @@ -39,8 +28,7 @@ static APP: FC<()> = |cx, props| { oninput: move |e| text_content.set(e.value()) } - br {} - {(0..10).map(|f| { + {(0..10).map(|_| { rsx!( button { onclick: move |_| count += 1, @@ -74,7 +62,7 @@ static APP: FC<()> = |cx, props| { {render_bullets(cx)} - Child {} + CHILD {} } }) }; @@ -97,7 +85,4 @@ fn render_list(cx: Context, count: usize) -> DomTree { rsx!(cx, ul { {items} }) } -static Child: FC<()> = |cx, props| { - // render - rsx!(cx, div {"hello child"}) -}; +static CHILD: FC<()> = |cx, _| rsx!(cx, div {"hello child"}); diff --git a/packages/web/src/dom.rs b/packages/web/src/dom.rs index 4dfaecd5d..438062d06 100644 --- a/packages/web/src/dom.rs +++ b/packages/web/src/dom.rs @@ -1,3 +1,12 @@ +//! Implementation of a renderer for Dioxus on the web. +//! +//! Oustanding todos: +//! - Removing event listeners (delegation) +//! - Passive event listeners +//! - no-op event listener patch for safari +//! - tests to ensure dyn_into works for various event types. +//! - Partial delegation?> + use dioxus_core::{ events::{SyntheticEvent, UserEvent}, mutations::NodeRefMutation, @@ -29,7 +38,7 @@ pub struct WebsysDom { // map of listener types to number of those listeners // This is roughly a delegater // TODO: check how infero delegates its events - some are more performant - listeners: FxHashMap<&'static str, (usize, Closure)>, + listeners: FxHashMap<&'static str, ListenerEntry>, // We need to make sure to add comments between text nodes // We ensure that the text siblings are patched by preventing the browser from merging @@ -38,6 +47,9 @@ pub struct WebsysDom { // -> https://github.com/facebook/react/pull/5753 last_node_was_text: bool, } + +type ListenerEntry = (usize, Closure); + impl WebsysDom { pub fn new(root: Element, cfg: WebConfig, sender_callback: Rc) -> Self { let document = load_document(); @@ -92,7 +104,6 @@ impl WebsysDom { pub fn process_edits(&mut self, edits: &mut Vec) { for edit in edits.drain(..) { - // log::info!("Handling edit: {:#?}", edit); match edit { DomEdit::PushRoot { id: root } => self.push(root), DomEdit::PopRoot => self.pop(), @@ -423,34 +434,29 @@ impl WebsysDom { } #[derive(Debug, Default)] -pub struct Stack { - pub list: Vec, +struct Stack { + list: Vec, } impl Stack { #[inline] - pub fn with_capacity(cap: usize) -> Self { + fn with_capacity(cap: usize) -> Self { Stack { list: Vec::with_capacity(cap), } } #[inline] - pub fn push(&mut self, node: Node) { + fn push(&mut self, node: Node) { self.list.push(node); } #[inline] - pub fn pop(&mut self) -> Node { + fn pop(&mut self) -> Node { self.list.pop().unwrap() } - #[inline] - pub fn clear(&mut self) { - self.list.clear(); - } - - pub fn top(&self) -> &Node { + fn top(&self) -> &Node { match self.list.last() { Some(a) => a, None => panic!("Called 'top' of an empty stack, make sure to push the root first"), @@ -458,6 +464,8 @@ impl Stack { } } +// 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. fn virtual_event_from_websys_event(event: web_sys::Event) -> SyntheticEvent { use crate::events::*; use dioxus_core::events::on::*; @@ -549,12 +557,13 @@ fn decode_trigger(event: &web_sys::Event) -> anyhow::Result { let typ = event.type_(); - log::debug!("Event type is {:?}", typ); - - let attrs = target.attributes(); - for x in 0..attrs.length() { - let attr: Attr = attrs.item(x).unwrap(); - // log::debug!("attrs include: {:#?}, {:#?}", attr.name(), attr.value()); + // TODO: clean this up + if cfg!(debug_assertions) { + let attrs = target.attributes(); + for x in 0..attrs.length() { + let attr: Attr = attrs.item(x).unwrap(); + // log::debug!("attrs include: {:#?}, {:#?}", attr.name(), attr.value()); + } } use anyhow::Context; @@ -576,12 +585,8 @@ fn decode_trigger(event: &web_sys::Event) -> anyhow::Result { .and_then(|raw_id| raw_id.parse::().ok()) .context("failed to parse real id")?; - // Call the trigger - // log::debug!("decoded scope_id: {}, node_id: {:#?}", gi_id, real_id); - let triggered_scope = gi_id; - // let triggered_scope: ScopeId = KeyData::from_ffi(gi_id).into(); - // log::debug!("Triggered scope is {:#?}", triggered_scope); + Ok(UserEvent { name: event_name_from_typ(&typ), event: virtual_event_from_websys_event(event.clone()), @@ -590,14 +595,14 @@ fn decode_trigger(event: &web_sys::Event) -> anyhow::Result { }) } -pub fn load_document() -> Document { +pub(crate) fn load_document() -> Document { web_sys::window() .expect("should have access to the Window") .document() .expect("should have access to the Document") } -pub fn event_name_from_typ(typ: &str) -> &'static str { +fn event_name_from_typ(typ: &str) -> &'static str { match typ { "copy" => "copy", "cut" => "cut",