소스 검색

wip: some ideas

Jonathan Kelley 4 년 전
부모
커밋
05c909f320

+ 23 - 25
examples/async.rs

@@ -2,43 +2,41 @@
 //!
 //! The example from the README.md
 
-use std::{pin::Pin, time::Duration};
-
 use dioxus::prelude::*;
-use futures::Future;
 fn main() {
-    env_logger::init();
-    log::info!("hello world");
     dioxus::desktop::launch(App, |c| c).expect("faield to launch");
 }
 
-#[derive(serde::Deserialize)]
-struct DogApi {
-    message: String,
-}
-
-const ENDPOINT: &str = "https://dog.ceo/api/breeds/image/random";
+pub static App: FC<()> = |cx| {
+    let count = use_state(cx, || 0);
+    let mut direction = use_state(cx, || 1);
 
-static App: FC<()> = |cx| {
-    let mut count = use_state(cx, || 0);
-    let mut fut = Box::pin(async {
-        let mut tick: i32 = 0;
+    let (async_count, dir) = (count.for_async(), *direction);
+    let (task, _result) = cx.use_task(move || async move {
         loop {
-            async_std::task::sleep(Duration::from_millis(250)).await;
-            log::debug!("ticking forward... {}", tick);
-            tick += 1;
-            // match surf::get(ENDPOINT).recv_json::<DogApi>().await {
-            //     Ok(_) => (),
-            //     Err(_) => (),
-            // }
+            gloo_timers::future::TimeoutFuture::new(250).await;
+            *async_count.get_mut() += dir;
         }
     });
 
-    cx.submit_task(fut);
-
     cx.render(rsx! {
         div {
-            h1 {"it's working somewhat properly"}
+            h1 {"count is {count}"}
+            button {
+                "Stop counting"
+                onclick: move |_| task.stop()
+            }
+            button {
+                "Start counting"
+                onclick: move |_| task.start()
+            }
+            button {
+                "Switch counting direcion"
+                onclick: move |_| {
+                    direction *= -1;
+                    task.restart();
+                }
+            }
         }
     })
 };

+ 0 - 16
examples/doggo.rs

@@ -1,16 +0,0 @@
-use dioxus::prelude::*;
-fn main() {}
-
-pub static Example: FC<()> = |cx| {
-    let (g, set_g) = use_state(cx, || 0).classic();
-    let v = (0..10).map(move |f| {
-        rsx!(li {
-            onclick: move |_| set_g(10)
-        })
-    });
-    cx.render(rsx! {
-        div {
-            {v}
-        }
-    })
-};

+ 0 - 170
examples/reference/main.rs

@@ -1,170 +0,0 @@
-#![allow(
-    unused,
-    dead_code,
-    non_upper_case_globals,
-    non_camel_case_types,
-    non_snake_case
-)]
-
-mod antipatterns;
-mod basics;
-mod children;
-mod conditional_rendering;
-mod controlled_inputs;
-mod custom_elements;
-mod empty;
-mod fragments;
-mod global_css;
-mod inline_styles;
-mod iterators;
-mod listener;
-mod memo;
-mod noderefs;
-mod signals;
-mod spreadpattern;
-mod statemanagement;
-mod suspense;
-mod task;
-mod testing;
-mod tostring;
-
-fn main() {}
-
-use std::rc::Rc;
-
-use dioxus::prelude::*;
-
-static App: FC<()> = |cx| {
-    let (selection, set_selection) = use_state(cx, || None as Option<usize>).classic();
-
-    let body = match selection {
-        Some(id) => rsx!(in cx, ReferenceItem { selected: *id }),
-        None => rsx!(in cx, div { "Select an concept to explore" }),
-    };
-
-    cx.render(rsx! {
-        div {
-            ScrollSelector { onselect: move |id| set_selection(id)  }
-            {body}
-        }
-    })
-};
-
-// this is its own component to stay memoized
-#[derive(Props)]
-struct ScrollSelectorProps<'a> {
-    onselect: &'a dyn Fn(Option<usize>),
-}
-
-fn ScrollSelector<'a>(cx: Context<'a, ScrollSelectorProps>) -> VNode<'a> {
-    let selection_list = (&REFERENCES).iter().enumerate().map(|(id, _)| {
-        rsx! {
-            li {
-                h3 {}
-            }
-        }
-    });
-    cx.render(rsx! {
-        div {
-            h1 {""}
-            ul {
-                {selection_list}
-                button {
-                    onclick: move |_| (cx.onselect)(Some(10))
-                }
-            }
-        }
-    })
-}
-
-#[derive(PartialEq, Props)]
-struct ReferenceItemProps {
-    selected: usize,
-}
-
-static ReferenceItem: FC<ReferenceItemProps> = |cx| {
-    let (caller, name, code) = REFERENCES[cx.selected];
-
-    // Create the component using the factory API directly
-    let caller_node = LazyNodes::new(move |f| f.component(caller, (), None, &[]));
-
-    cx.render(rsx! {
-        div {
-            // Source of the left, rendered on the right
-            div {
-                code { "{code}" }
-            }
-            div {
-                {caller_node}
-            }
-        }
-    })
-};
-
-static REFERENCES: &[(FC<()>, &str, &str)] = &[
-    (basics::Example, "Basics", include_str!("./basics.rs")),
-    (children::Example, "Children", include_str!("./children.rs")),
-    (
-        conditional_rendering::Example,
-        "Conditional Rendering",
-        include_str!("./conditional_rendering.rs"),
-    ),
-    // TODO
-    (
-        controlled_inputs::Example,
-        "Controlled Inputs",
-        include_str!("./controlled_inputs.rs"),
-    ),
-    (empty::Example, "empty", include_str!("./empty.rs")),
-    (
-        custom_elements::Example,
-        "Custom Elements & Web Components",
-        include_str!("./custom_elements.rs"),
-    ),
-    (
-        fragments::Example,
-        "Fragments",
-        include_str!("./fragments.rs"),
-    ),
-    (
-        iterators::Example,
-        "Iterators",
-        include_str!("./iterators.rs"),
-    ),
-    (
-        global_css::Example,
-        "Global CSS",
-        include_str!("./global_css.rs"),
-    ),
-    (
-        inline_styles::Example,
-        "Inline Styles",
-        include_str!("./inline_styles.rs"),
-    ),
-    (listener::Example, "Listener", include_str!("./listener.rs")),
-    (memo::Example, "Memo", include_str!("./memo.rs")),
-    (
-        spreadpattern::Example,
-        "Spread Pattern",
-        include_str!("./spreadpattern.rs"),
-    ),
-    (suspense::Example, "Suspense", include_str!("./suspense.rs")),
-    (task::Example, "Task", include_str!("./task.rs")),
-    (tostring::Example, "Tostring", include_str!("./tostring.rs")),
-    (
-        antipatterns::Example,
-        "Anti-patterns",
-        include_str!("./antipatterns.rs"),
-    ),
-    /*
-        TODO!
-    */
-    (signals::Example, "Signals", include_str!("./signals.rs")),
-    (noderefs::Example, "NodeRefs", include_str!("./noderefs.rs")),
-    (
-        statemanagement::Example,
-        "State Management",
-        include_str!("./statemanagement.rs"),
-    ),
-    (testing::Example, "Testing", include_str!("./testing.rs")),
-];

+ 1 - 1
examples/reference/tostring.rs

@@ -7,7 +7,7 @@ pub static Example: FC<()> = |cx| {
         // This is an easy/low hanging fruit to improve upon
         let mut dom = VirtualDom::new(SomeApp);
         dom.rebuild_in_place().unwrap();
-        ssr::render_root(&dom)
+        ssr::render_vdom(&dom)
     });
 
     cx.render(rsx! {

+ 185 - 7
examples/showcase.rs

@@ -1,8 +1,84 @@
-//! Example: Reference Showcase
-//! ---------------------------
-//!
-//! This example provides a cute interface for browsing the reference examples.
+#![allow(
+    unused,
+    dead_code,
+    non_upper_case_globals,
+    non_camel_case_types,
+    non_snake_case
+)]
 
+fn main() {}
+
+use dioxus::prelude::*;
+use std::rc::Rc;
+
+static App: FC<()> = |cx| {
+    let (selection, set_selection) = use_state(cx, || None as Option<usize>).classic();
+
+    let body = match selection {
+        Some(id) => rsx!(in cx, ReferenceItem { selected: *id }),
+        None => rsx!(in cx, div { "Select an concept to explore" }),
+    };
+
+    cx.render(rsx! {
+        div {
+            ScrollSelector { onselect: move |id| set_selection(id)  }
+            {body}
+        }
+    })
+};
+
+// this is its own component to stay memoized
+#[derive(Props)]
+struct ScrollSelectorProps<'a> {
+    onselect: &'a dyn Fn(Option<usize>),
+}
+
+fn ScrollSelector<'a>(cx: Context<'a, ScrollSelectorProps>) -> VNode<'a> {
+    let selection_list = (&REFERENCES).iter().enumerate().map(|(id, _)| {
+        rsx! {
+            li {
+                h3 {}
+            }
+        }
+    });
+    cx.render(rsx! {
+        div {
+            h1 {""}
+            ul {
+                {selection_list}
+                button {
+                    onclick: move |_| (cx.onselect)(Some(10))
+                }
+            }
+        }
+    })
+}
+
+#[derive(PartialEq, Props)]
+struct ReferenceItemProps {
+    selected: usize,
+}
+
+static ReferenceItem: FC<ReferenceItemProps> = |cx| {
+    let (caller, name, code) = REFERENCES[cx.selected];
+
+    // Create the component using the factory API directly
+    let caller_node = LazyNodes::new(move |f| f.component(caller, (), None, &[]));
+
+    cx.render(rsx! {
+        div {
+            // Source of the left, rendered on the right
+            div {
+                code { "{code}" }
+            }
+            div {
+                {caller_node}
+            }
+        }
+    })
+};
+
+use reference::REFERENCES;
 mod reference {
     mod antipatterns;
     mod basics;
@@ -22,9 +98,111 @@ mod reference {
     mod spreadpattern;
     mod statemanagement;
     mod suspense;
+    mod task;
     mod testing;
     mod tostring;
-    mod webcomponents;
+    use super::*;
+    pub static REFERENCES: &[(FC<()>, &str, &str)] = &[
+        (
+            basics::Example,
+            "Basics",
+            include_str!("./reference/basics.rs"),
+        ),
+        (
+            children::Example,
+            "Children",
+            include_str!("./reference/children.rs"),
+        ),
+        (
+            conditional_rendering::Example,
+            "Conditional Rendering",
+            include_str!("./reference/conditional_rendering.rs"),
+        ),
+        // TODO
+        (
+            controlled_inputs::Example,
+            "Controlled Inputs",
+            include_str!("./reference/controlled_inputs.rs"),
+        ),
+        (
+            empty::Example,
+            "empty",
+            include_str!("./reference/empty.rs"),
+        ),
+        (
+            custom_elements::Example,
+            "Custom Elements & Web Components",
+            include_str!("./reference/custom_elements.rs"),
+        ),
+        (
+            fragments::Example,
+            "Fragments",
+            include_str!("./reference/fragments.rs"),
+        ),
+        (
+            iterators::Example,
+            "Iterators",
+            include_str!("./reference/iterators.rs"),
+        ),
+        (
+            global_css::Example,
+            "Global CSS",
+            include_str!("./reference/global_css.rs"),
+        ),
+        (
+            inline_styles::Example,
+            "Inline Styles",
+            include_str!("./reference/inline_styles.rs"),
+        ),
+        (
+            listener::Example,
+            "Listener",
+            include_str!("./reference/listener.rs"),
+        ),
+        (memo::Example, "Memo", include_str!("./reference/memo.rs")),
+        (
+            spreadpattern::Example,
+            "Spread Pattern",
+            include_str!("./reference/spreadpattern.rs"),
+        ),
+        (
+            suspense::Example,
+            "Suspense",
+            include_str!("./reference/suspense.rs"),
+        ),
+        (task::Example, "Task", include_str!("./reference/task.rs")),
+        (
+            tostring::Example,
+            "Tostring",
+            include_str!("./reference/tostring.rs"),
+        ),
+        (
+            antipatterns::Example,
+            "Anti-patterns",
+            include_str!("./reference/antipatterns.rs"),
+        ),
+        /*
+            TODO! these showcase features that aren't quite ready
+        */
+        (
+            signals::Example,
+            "Signals",
+            include_str!("./reference/signals.rs"),
+        ),
+        (
+            noderefs::Example,
+            "NodeRefs",
+            include_str!("./reference/noderefs.rs"),
+        ),
+        (
+            statemanagement::Example,
+            "State Management",
+            include_str!("./reference/statemanagement.rs"),
+        ),
+        (
+            testing::Example,
+            "Testing",
+            include_str!("./reference/testing.rs"),
+        ),
+    ];
 }
-
-fn main() {}

+ 1 - 1
examples/ssr.rs

@@ -4,7 +4,7 @@ use dioxus::ssr;
 fn main() {
     let mut vdom = VirtualDom::new(App);
     vdom.rebuild_in_place();
-    println!("{}", ssr::render_root(&vdom));
+    println!("{}", ssr::render_vdom(&vdom));
 }
 
 const App: FC<()> = |cx| {

+ 1 - 1
examples/tailwind.rs

@@ -1,7 +1,7 @@
-use dioxus::desktop::wry::application::platform::macos::*;
 use dioxus::prelude::*;
 
 fn main() {
+    use dioxus::desktop::wry::application::platform::macos::*;
     dioxus::desktop::launch(App, |c| {
         c.with_fullsize_content_view(true)
             .with_titlebar_buttons_hidden(false)

+ 4 - 6
packages/core/src/arena.rs

@@ -22,18 +22,16 @@ impl SharedArena {
 
     /// THIS METHOD IS CURRENTLY UNSAFE
     /// THERE ARE NO CHECKS TO VERIFY THAT WE ARE ALLOWED TO DO THIS
-    pub fn try_get(&self, idx: ScopeId) -> Result<&Scope> {
+    pub fn get(&self, idx: ScopeId) -> Option<&Scope> {
         let inner = unsafe { &*self.components.get() };
-        let scope = inner.get(idx);
-        scope.ok_or_else(|| Error::FatalInternal("Scope not found"))
+        inner.get(idx)
     }
 
     /// THIS METHOD IS CURRENTLY UNSAFE
     /// THERE ARE NO CHECKS TO VERIFY THAT WE ARE ALLOWED TO DO THIS
-    pub fn try_get_mut(&self, idx: ScopeId) -> Result<&mut Scope> {
+    pub fn get_mut(&self, idx: ScopeId) -> Option<&mut Scope> {
         let inner = unsafe { &mut *self.components.get() };
-        let scope = inner.get_mut(idx);
-        scope.ok_or_else(|| Error::FatalInternal("Scope not found"))
+        inner.get_mut(idx)
     }
 
     fn inner(&self) -> &ScopeMap {

+ 2 - 2
packages/core/src/context.rs

@@ -278,8 +278,8 @@ Any function prefixed with "use" should not be called conditionally.
                             Some(parent_id) => {
                                 let parent = inner
                                     .arena_link
-                                    .try_get(parent_id)
-                                    .map_err(|_| Error::FatalInternal("Failed to find parent"))?;
+                                    .get(parent_id)
+                                    .ok_or_else(|| Error::FatalInternal("Failed to find parent"))?;
 
                                 scope = Some(parent);
                             }

+ 4 - 4
packages/core/src/diff.rs

@@ -162,7 +162,7 @@ where
                     new.ass_scope.set(scope_id);
 
                     // make sure the component's caller function is up to date
-                    let scope = self.components.try_get_mut(scope_id.unwrap()).unwrap();
+                    let scope = self.components.get_mut(scope_id.unwrap()).unwrap();
 
                     scope.caller = new.caller.clone();
 
@@ -406,7 +406,7 @@ where
                     .components
                     .with(|components| {
                         components.insert_with_key(|new_idx| {
-                            let parent_scope = self.components.try_get(parent_idx).unwrap();
+                            let parent_scope = self.components.get(parent_idx).unwrap();
                             let height = parent_scope.height + 1;
                             Scope::new(
                                 caller,
@@ -495,7 +495,7 @@ impl<'a, 'bump, Dom: RealDom<'bump>> DiffMachine<'a, 'bump, Dom> {
         while let Some(scope_id) = scopes_to_explore.pop() {
             // If we're planning on deleting this node, then we don't need to both rendering it
             self.seen_nodes.insert(scope_id);
-            let scope = self.components.try_get(scope_id).unwrap();
+            let scope = self.components.get(scope_id).unwrap();
             for child in scope.descendents.borrow().iter() {
                 // Add this node to be explored
                 scopes_to_explore.push(child.clone());
@@ -1324,7 +1324,7 @@ impl<'a> Iterator for RealChildIterator<'a> {
 
                     // For components, we load their root and push them onto the stack
                     VNodeKind::Component(sc) => {
-                        let scope = self.scopes.try_get(sc.ass_scope.get().unwrap()).unwrap();
+                        let scope = self.scopes.get(sc.ass_scope.get().unwrap()).unwrap();
 
                         // Simply swap the current node on the stack with the root of the component
                         *node = scope.root();

+ 3 - 2
packages/core/src/lib.rs

@@ -10,8 +10,9 @@
 //!
 
 pub use crate::innerlude::{
-    format_args_f, html, rsx, DioxusElement, DomEdit, EventTrigger, LazyNodes, NodeFactory,
-    Properties, RealDom, RealDomNode, ScopeId, VNode, VNodeKind, VirtualDom, VirtualEvent, FC,
+    format_args_f, html, rsx, Context, DioxusElement, DomEdit, EventTrigger, LazyNodes,
+    NodeFactory, Properties, RealDom, RealDomNode, ScopeId, VNode, VNodeKind, VirtualDom,
+    VirtualEvent, FC,
 };
 
 pub mod prelude {

+ 6 - 6
packages/core/src/virtual_dom.rs

@@ -224,7 +224,7 @@ impl VirtualDom {
             &self.tasks,
         );
 
-        let cur_component = self.components.try_get_mut(self.base_scope).unwrap();
+        let cur_component = self.components.get_mut(self.base_scope).unwrap();
         cur_component.run_scope()?;
 
         let meta = diff_machine.create(cur_component.next_frame());
@@ -312,7 +312,7 @@ impl VirtualDom {
 
             // Suspense Events! A component's suspended node is updated
             VirtualEvent::SuspenseEvent { hook_idx, domnode } => {
-                let scope = self.components.try_get_mut(trigger.originator).unwrap();
+                let scope = self.components.get_mut(trigger.originator).unwrap();
 
                 // safety: we are sure that there are no other references to the inner content of this hook
                 let hook = unsafe { scope.hooks.get_mut::<SuspenseHook>(*hook_idx) }.unwrap();
@@ -339,8 +339,8 @@ impl VirtualDom {
             // We use the reconciler to request new IDs and then commit/uncommit the IDs when the scheduler is finished
             _ => {
                 self.components
-                    .try_get_mut(trigger.originator)?
-                    .call_listener(trigger)?;
+                    .get_mut(trigger.originator)
+                    .map(|f| f.call_listener(trigger));
 
                 // Now, there are events in the queue
                 let mut updates = self.event_queue.queue.as_ref().borrow_mut();
@@ -366,7 +366,7 @@ impl VirtualDom {
 
                     // Start a new mutable borrow to components
                     // We are guaranteeed that this scope is unique because we are tracking which nodes have modified
-                    let cur_component = self.components.try_get_mut(update.idx).unwrap();
+                    let cur_component = self.components.get_mut(update.idx).unwrap();
 
                     cur_component.run_scope()?;
 
@@ -380,7 +380,7 @@ impl VirtualDom {
     }
 
     pub fn base_scope(&self) -> &Scope {
-        self.components.try_get(self.base_scope).unwrap()
+        self.components.get(self.base_scope).unwrap()
     }
 }
 

+ 12 - 2
packages/html/src/lib.rs

@@ -423,7 +423,7 @@ pub trait GlobalAttributes {
         list_style_position: "list-style-position",
 
         /// Specifies the marker style for a list_item.
-        list_style_type: "list-style-type",
+        list_styler_type: "list-style-type",
 
         /// Sets the margin on all four sides of the element.
         margin: "margin",
@@ -1016,6 +1016,8 @@ builder_constructors! {
         r#type: Mime,
         // ping: SpacedList<Uri>,
         // rel: SpacedList<LinkType>,
+        ping: SpacedList,
+        rel: SpacedList,
     };
 
     /// Build a
@@ -1274,6 +1276,14 @@ builder_constructors! {
         src: Uri,
         srcdoc: Uri,
         width: usize,
+
+        marginWidth: String,
+        align: String,
+        longdesc: String,
+
+        scrolling: String,
+        marginHeight: String,
+        frameBorder: String,
         // sandbox: SpacedSet<Sandbox>,
     };
 
@@ -1850,7 +1860,7 @@ pub trait SvgAttributes {
         to: "to",
         transform: "transform",
         transform_origin: "transform-origin",
-        _type: "_type",
+        r#type: "_type",
         u1: "u1",
         u2: "u2",
         underline_position: "underline-position",

+ 2 - 2
packages/ssr/examples/tide.rs

@@ -22,7 +22,7 @@ async fn main() -> Result<(), std::io::Error> {
         let dom = VirtualDom::launch_with_props_in_place(Example, ExampleProps { initial_name });
 
         Ok(Response::builder(200)
-            .body(format!("{}", dioxus_ssr::render_root(&dom)))
+            .body(format!("{}", dioxus_ssr::render_vdom(&dom)))
             .content_type(tide::http::mime::HTML)
             .build())
     });
@@ -38,7 +38,7 @@ struct ExampleProps {
     initial_name: String,
 }
 
-pub static Example: FC<ExampleProps> = |cx| {
+static Example: FC<ExampleProps> = |cx| {
     let dispaly_name = use_state(cx, move || cx.initial_name.clone());
 
     cx.render(rsx! {

+ 1 - 1
packages/ssr/examples/tofile.rs

@@ -15,7 +15,7 @@ fn main() {
     let mut dom = VirtualDom::new(App);
     dom.rebuild_in_place().expect("failed to run virtualdom");
 
-    file.write_fmt(format_args!("{}", TextRenderer::new(&dom)))
+    file.write_fmt(format_args!("{}", TextRenderer::from_vdom(&dom)))
         .unwrap();
 }
 

+ 35 - 22
packages/ssr/src/lib.rs

@@ -10,8 +10,21 @@ use std::fmt::{Display, Formatter};
 
 use dioxus_core::*;
 
-pub fn render_root(vdom: &VirtualDom) -> String {
-    format!("{:}", TextRenderer::new(vdom))
+pub fn render_vnode(vnode: &VNode, string: &mut String) {}
+
+pub fn render_vdom(vdom: &VirtualDom) -> String {
+    format!("{:}", TextRenderer::from_vdom(vdom))
+}
+
+pub fn render_vdom_scope(vdom: &VirtualDom, scope: ScopeId) -> Option<String> {
+    Some(format!(
+        "{:}",
+        TextRenderer {
+            cfg: SsrConfig::default(),
+            root: vdom.components.get(scope).unwrap().root(),
+            vdom: Some(vdom)
+        }
+    ))
 }
 
 pub struct SsrConfig {
@@ -56,14 +69,22 @@ impl Default for SsrConfig {
 /// assert_eq!(output, "<div>hello world</div>");
 /// ```
 pub struct TextRenderer<'a> {
-    vdom: &'a VirtualDom,
+    vdom: Option<&'a VirtualDom>,
+    root: &'a VNode<'a>,
     cfg: SsrConfig,
 }
 
+impl Display for TextRenderer<'_> {
+    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+        self.html_render(self.root, f, 0)
+    }
+}
+
 impl<'a> TextRenderer<'a> {
-    pub fn new(vdom: &'a VirtualDom) -> Self {
+    pub fn from_vdom(vdom: &'a VirtualDom) -> Self {
         Self {
-            vdom,
+            root: vdom.base_scope().root(),
+            vdom: Some(vdom),
             cfg: SsrConfig::default(),
         }
     }
@@ -141,10 +162,10 @@ impl<'a> TextRenderer<'a> {
             }
             VNodeKind::Component(vcomp) => {
                 let idx = vcomp.ass_scope.get().unwrap();
-
-                let new_node = self.vdom.components.try_get(idx).unwrap().root();
-
-                self.html_render(new_node, f, il + 1)?;
+                if let Some(vdom) = self.vdom {
+                    let new_node = vdom.components.get(idx).unwrap().root();
+                    self.html_render(new_node, f, il + 1)?;
+                }
             }
             VNodeKind::Suspended { .. } => {
                 // we can't do anything with suspended nodes
@@ -154,14 +175,6 @@ impl<'a> TextRenderer<'a> {
     }
 }
 
-impl Display for TextRenderer<'_> {
-    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
-        let root = self.vdom.base_scope();
-        let root_node = root.root();
-        self.html_render(root_node, f, 0)
-    }
-}
-
 #[cfg(test)]
 mod tests {
     use super::*;
@@ -216,21 +229,21 @@ mod tests {
     fn to_string_works() {
         let mut dom = VirtualDom::new(SIMPLE_APP);
         dom.rebuild_in_place().expect("failed to run virtualdom");
-        dbg!(render_root(&dom));
+        dbg!(render_vdom(&dom));
     }
 
     #[test]
     fn nested() {
         let mut dom = VirtualDom::new(NESTED_APP);
         dom.rebuild_in_place().expect("failed to run virtualdom");
-        dbg!(render_root(&dom));
+        dbg!(render_vdom(&dom));
     }
 
     #[test]
     fn fragment_app() {
         let mut dom = VirtualDom::new(FRAGMENT_APP);
         dom.rebuild_in_place().expect("failed to run virtualdom");
-        dbg!(render_root(&dom));
+        dbg!(render_vdom(&dom));
     }
 
     #[test]
@@ -243,7 +256,7 @@ mod tests {
         let mut dom = VirtualDom::new(SLIGHTLY_MORE_COMPLEX);
         dom.rebuild_in_place().expect("failed to run virtualdom");
 
-        file.write_fmt(format_args!("{}", TextRenderer::new(&dom)))
+        file.write_fmt(format_args!("{}", TextRenderer::from_vdom(&dom)))
             .unwrap();
     }
 
@@ -263,6 +276,6 @@ mod tests {
 
         let mut dom = VirtualDom::new(STLYE_APP);
         dom.rebuild_in_place().expect("failed to run virtualdom");
-        dbg!(render_root(&dom));
+        dbg!(render_vdom(&dom));
     }
 }

+ 4 - 12
packages/web/examples/async_web.rs

@@ -13,15 +13,12 @@ use std::{pin::Pin, time::Duration};
 
 use dioxus::prelude::*;
 
-use dioxus_web::*;
-
 fn main() {
     // Setup logging
-    wasm_logger::init(wasm_logger::Config::new(log::Level::Debug));
     console_error_panic_hook::set_once();
+    wasm_logger::init(wasm_logger::Config::new(log::Level::Debug));
 
-    // Run the app
-    wasm_bindgen_futures::spawn_local(WebsysRenderer::start(App));
+    dioxus_web::launch(App, |c| c)
 }
 
 #[derive(serde::Deserialize)]
@@ -38,17 +35,12 @@ static App: FC<()> = |cx| {
         || surf::get(ENDPOINT).recv_json::<DogApi>(),
         |cx, res| match res {
             Ok(res) => rsx!(in cx, img { src: "{res.message}" }),
-            Err(err) => rsx!(in cx, div { "No doggos for you :(" }),
+            Err(_err) => rsx!(in cx, div { "No doggos for you :(" }),
         },
     );
 
-    log::error!("RIP WE RAN THE COMPONENT");
-
     cx.render(rsx! {
-        div {
-            style: {
-                align_items: "center"
-            }
+        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"

+ 292 - 0
packages/web/src/cache.rs

@@ -0,0 +1,292 @@
+/// Wasm-bindgen has a performance option to intern commonly used phrases
+/// This saves the decoding cost, making the interaction of Rust<->JS more performant.
+/// We intern all the HTML tags and attributes, making most operations much faster.
+///
+/// Interning takes about 1ms at the start of the app, but saves a *ton* of time later on.
+pub fn intern_cache() {
+    let cached_words = [
+        // All the HTML Tags
+        "a",
+        "abbr",
+        "address",
+        "area",
+        "article",
+        "aside",
+        "audio",
+        "b",
+        "base",
+        "bdi",
+        "bdo",
+        "big",
+        "blockquote",
+        "body",
+        "br",
+        "button",
+        "canvas",
+        "caption",
+        "cite",
+        "code",
+        "col",
+        "colgroup",
+        "command",
+        "data",
+        "datalist",
+        "dd",
+        "del",
+        "details",
+        "dfn",
+        "dialog",
+        "div",
+        "dl",
+        "dt",
+        "em",
+        "embed",
+        "fieldset",
+        "figcaption",
+        "figure",
+        "footer",
+        "form",
+        "h1",
+        "h2",
+        "h3",
+        "h4",
+        "h5",
+        "h6",
+        "head",
+        "header",
+        "hr",
+        "html",
+        "i",
+        "iframe",
+        "img",
+        "input",
+        "ins",
+        "kbd",
+        "keygen",
+        "label",
+        "legend",
+        "li",
+        "link",
+        "main",
+        "map",
+        "mark",
+        "menu",
+        "menuitem",
+        "meta",
+        "meter",
+        "nav",
+        "noscript",
+        "object",
+        "ol",
+        "optgroup",
+        "option",
+        "output",
+        "p",
+        "param",
+        "picture",
+        "pre",
+        "progress",
+        "q",
+        "rp",
+        "rt",
+        "ruby",
+        "s",
+        "samp",
+        "script",
+        "section",
+        "select",
+        "small",
+        "source",
+        "span",
+        "strong",
+        "style",
+        "sub",
+        "summary",
+        "sup",
+        "table",
+        "tbody",
+        "td",
+        "textarea",
+        "tfoot",
+        "th",
+        "thead",
+        "time",
+        "title",
+        "tr",
+        "track",
+        "u",
+        "ul",
+        "var",
+        "video",
+        "wbr",
+        // All the event handlers
+        "Attribute",
+        "accept",
+        "accept-charset",
+        "accesskey",
+        "action",
+        "alt",
+        "async",
+        "autocomplete",
+        "autofocus",
+        "autoplay",
+        "charset",
+        "checked",
+        "cite",
+        "class",
+        "cols",
+        "colspan",
+        "content",
+        "contenteditable",
+        "controls",
+        "coords",
+        "data",
+        "data-*",
+        "datetime",
+        "default",
+        "defer",
+        "dir",
+        "dirname",
+        "disabled",
+        "download",
+        "draggable",
+        "enctype",
+        "for",
+        "form",
+        "formaction",
+        "headers",
+        "height",
+        "hidden",
+        "high",
+        "href",
+        "hreflang",
+        "http-equiv",
+        "id",
+        "ismap",
+        "kind",
+        "label",
+        "lang",
+        "list",
+        "loop",
+        "low",
+        "max",
+        "maxlength",
+        "media",
+        "method",
+        "min",
+        "multiple",
+        "muted",
+        "name",
+        "novalidate",
+        "onabort",
+        "onafterprint",
+        "onbeforeprint",
+        "onbeforeunload",
+        "onblur",
+        "oncanplay",
+        "oncanplaythrough",
+        "onchange",
+        "onclick",
+        "oncontextmenu",
+        "oncopy",
+        "oncuechange",
+        "oncut",
+        "ondblclick",
+        "ondrag",
+        "ondragend",
+        "ondragenter",
+        "ondragleave",
+        "ondragover",
+        "ondragstart",
+        "ondrop",
+        "ondurationchange",
+        "onemptied",
+        "onended",
+        "onerror",
+        "onfocus",
+        "onhashchange",
+        "oninput",
+        "oninvalid",
+        "onkeydown",
+        "onkeypress",
+        "onkeyup",
+        "onload",
+        "onloadeddata",
+        "onloadedmetadata",
+        "onloadstart",
+        "onmousedown",
+        "onmousemove",
+        "onmouseout",
+        "onmouseover",
+        "onmouseup",
+        "onmousewheel",
+        "onoffline",
+        "ononline",
+        "<body>",
+        "onpageshow",
+        "onpaste",
+        "onpause",
+        "onplay",
+        "onplaying",
+        "<body>",
+        "onprogress",
+        "onratechange",
+        "onreset",
+        "onresize",
+        "onscroll",
+        "onsearch",
+        "onseeked",
+        "onseeking",
+        "onselect",
+        "onstalled",
+        "<body>",
+        "onsubmit",
+        "onsuspend",
+        "ontimeupdate",
+        "ontoggle",
+        "onunload",
+        "onvolumechange",
+        "onwaiting",
+        "onwheel",
+        "open",
+        "optimum",
+        "pattern",
+        "placeholder",
+        "poster",
+        "preload",
+        "readonly",
+        "rel",
+        "required",
+        "reversed",
+        "rows",
+        "rowspan",
+        "sandbox",
+        "scope",
+        "selected",
+        "shape",
+        "size",
+        "sizes",
+        "span",
+        "spellcheck",
+        "src",
+        "srcdoc",
+        "srclang",
+        "srcset",
+        "start",
+        "step",
+        "style",
+        "tabindex",
+        "target",
+        "title",
+        "translate",
+        "type",
+        "usemap",
+        "value",
+        "width",
+        "wrap",
+    ];
+
+    for s in cached_words {
+        wasm_bindgen::intern(s);
+    }
+}

+ 80 - 417
packages/web/src/lib.rs

@@ -3,449 +3,112 @@
 //! This crate implements a renderer of the Dioxus Virtual DOM for the web browser using Websys.
 
 use dioxus::prelude::{Context, Properties, VNode};
-use futures_util::{pin_mut, Stream, StreamExt};
-
-use fxhash::FxHashMap;
-use web_sys::{window, Document, Element, Event, Node};
-// use futures::{channel::mpsc, SinkExt, StreamExt};
-
 use dioxus::virtual_dom::VirtualDom;
 pub use dioxus_core as dioxus;
 use dioxus_core::{events::EventTrigger, prelude::FC};
+use futures_util::{pin_mut, Stream, StreamExt};
+use fxhash::FxHashMap;
+use web_sys::{window, Document, Element, Event, Node};
 
-pub use dioxus_core::prelude;
-// pub mod interpreter;
-pub mod new;
+mod cache;
+mod new;
 
-/// The `WebsysRenderer` provides a way of rendering a Dioxus Virtual DOM to the browser's DOM.
-/// Under the hood, we leverage WebSys and interact directly with the DOM
+/// Launches the VirtualDOM from the specified component function.
 ///
-pub struct WebsysRenderer {
-    internal_dom: VirtualDom,
+/// This method will block the thread with `spawn_local`
+pub fn launch<F>(root: FC<()>, config: F)
+where
+    F: FnOnce(()),
+{
+    wasm_bindgen_futures::spawn_local(run(root))
 }
 
-impl WebsysRenderer {
-    /// This method is the primary entrypoint for Websys Dioxus apps. Will panic if an error occurs while rendering.
-    /// See DioxusErrors for more information on how these errors could occour.
-    ///
-    /// ```ignore
-    /// fn main() {
-    ///     wasm_bindgen_futures::spawn_local(WebsysRenderer::start(Example));
-    /// }
-    /// ```
-    ///
-    /// Run the app to completion, panicing if any error occurs while rendering.
-    /// Pairs well with the wasm_bindgen async handler
-    pub async fn start(root: FC<()>) {
-        Self::new(root).run().await.expect("Virtual DOM failed :(");
-    }
-
-    /// Create a new instance of the Dioxus Virtual Dom with no properties for the root component.
-    ///
-    /// This means that the root component must either consumes its own context, or statics are used to generate the page.
-    /// The root component can access things like routing in its context.
-    pub fn new(root: FC<()>) -> Self {
-        Self::new_with_props(root, ())
-    }
+pub fn launch_with_props<T, F>(root: FC<T>, root_props: T, config: F)
+where
+    T: Properties + 'static,
+    F: FnOnce(()),
+{
+    wasm_bindgen_futures::spawn_local(run_with_props(root, root_props))
+}
 
-    /// Create a new text-renderer instance from a functional component root.
-    /// Automatically progresses the creation of the VNode tree to completion.
-    ///
-    /// A VDom is automatically created. If you want more granular control of the VDom, use `from_vdom`
-    pub fn new_with_props<T: Properties + 'static>(root: FC<T>, root_props: T) -> Self {
-        Self::from_vdom(VirtualDom::new_with_props(root, root_props))
-    }
+/// This method is the primary entrypoint for Websys Dioxus apps. Will panic if an error occurs while rendering.
+/// See DioxusErrors for more information on how these errors could occour.
+///
+/// ```ignore
+/// fn main() {
+///     wasm_bindgen_futures::spawn_local(WebsysRenderer::start(Example));
+/// }
+/// ```
+///
+/// Run the app to completion, panicing if any error occurs while rendering.
+/// Pairs well with the wasm_bindgen async handler
+pub async fn run(root: FC<()>) {
+    run_with_props(root, ()).await;
+}
 
-    /// Create a new text renderer from an existing Virtual DOM.
-    pub fn from_vdom(dom: VirtualDom) -> Self {
-        Self { internal_dom: dom }
-    }
+pub async fn run_with_props<T: Properties + 'static>(root: FC<T>, root_props: T) {
+    let dom = VirtualDom::new_with_props(root, root_props);
+    event_loop(dom).await.expect("Event loop failed");
+}
 
-    pub async fn run(&mut self) -> dioxus_core::error::Result<()> {
-        use wasm_bindgen::JsCast;
+pub async fn event_loop(mut internal_dom: VirtualDom) -> dioxus_core::error::Result<()> {
+    use wasm_bindgen::JsCast;
 
-        let root = prepare_websys_dom();
-        let root_node = root.clone().dyn_into::<Node>().unwrap();
+    let root = prepare_websys_dom();
+    let root_node = root.clone().dyn_into::<Node>().unwrap();
 
-        let mut websys_dom = crate::new::WebsysDom::new(root.clone());
+    let mut websys_dom = crate::new::WebsysDom::new(root.clone());
 
-        websys_dom.stack.push(root_node.clone());
-        websys_dom.stack.push(root_node);
+    websys_dom.stack.push(root_node.clone());
+    websys_dom.stack.push(root_node);
 
-        let mut edits = Vec::new();
-        self.internal_dom.rebuild(&mut websys_dom, &mut edits)?;
-        websys_dom.process_edits(&mut edits);
+    let mut edits = Vec::new();
+    internal_dom.rebuild(&mut websys_dom, &mut edits)?;
+    websys_dom.process_edits(&mut edits);
 
-        log::info!("Going into event loop");
-        loop {
-            let trigger = {
-                let real_queue = websys_dom.wait_for_event();
-                if self.internal_dom.tasks.is_empty() {
-                    log::info!("tasks is empty, waiting for dom event to trigger soemthing");
-                    real_queue.await
-                } else {
-                    log::info!("tasks is not empty, waiting for either tasks or event system");
-                    let task_queue = (&mut self.internal_dom.tasks).next();
+    log::info!("Going into event loop");
+    loop {
+        let trigger = {
+            let real_queue = websys_dom.wait_for_event();
+            if internal_dom.tasks.is_empty() {
+                log::info!("tasks is empty, waiting for dom event to trigger soemthing");
+                real_queue.await
+            } else {
+                log::info!("tasks is not empty, waiting for either tasks or event system");
+                let task_queue = (&mut internal_dom.tasks).next();
 
-                    pin_mut!(real_queue);
-                    pin_mut!(task_queue);
+                pin_mut!(real_queue);
+                pin_mut!(task_queue);
 
-                    match futures_util::future::select(real_queue, task_queue).await {
-                        futures_util::future::Either::Left((trigger, _)) => trigger,
-                        futures_util::future::Either::Right((trigger, _)) => trigger,
-                    }
+                match futures_util::future::select(real_queue, task_queue).await {
+                    futures_util::future::Either::Left((trigger, _)) => trigger,
+                    futures_util::future::Either::Right((trigger, _)) => trigger,
                 }
-            };
+            }
+        };
 
-            if let Some(real_trigger) = trigger {
-                log::info!("event received");
+        if let Some(real_trigger) = trigger {
+            log::info!("event received");
 
-                self.internal_dom.queue_event(real_trigger)?;
+            internal_dom.queue_event(real_trigger)?;
 
-                let mut edits = Vec::new();
-                self.internal_dom
-                    .progress_with_event(&mut websys_dom, &mut edits)
-                    .await?;
-                websys_dom.process_edits(&mut edits);
-            }
+            let mut edits = Vec::new();
+            internal_dom
+                .progress_with_event(&mut websys_dom, &mut edits)
+                .await?;
+            websys_dom.process_edits(&mut edits);
         }
-
-        // should actually never return from this, should be an error, rustc just cant see it
-        Ok(())
     }
+
+    // should actually never return from this, should be an error, rustc just cant see it
+    Ok(())
 }
 
 fn prepare_websys_dom() -> Element {
-    // Initialize the container on the dom
-    // Hook up the body as the root component to render tinto
-    let window = web_sys::window().expect("should have access to the Window");
-    let document = window
+    web_sys::window()
+        .expect("should have access to the Window")
         .document()
-        .expect("should have access to the Document");
-
-    // let body = document.body().unwrap();
-    let el = document.get_element_by_id("dioxusroot").unwrap();
-
-    // Build a dummy div
-    // let container: &Element = body.as_ref();
-    // container.set_inner_html("");
-    // container
-    //     .append_child(
-    //         document
-    //             .create_element("div")
-    //             .expect("should create element OK")
-    //             .as_ref(),
-    //     )
-    //     .expect("should append child OK");
-    el
-    // container.clone()
-}
-
-// Progress the mount of the root component
-
-// Iterate through the nodes, attaching the closure and sender to the listener
-// {
-//     let mut remote_sender = sender.clone();
-//     let listener = move || {
-//         let event = EventTrigger::new();
-//         wasm_bindgen_futures::spawn_local(async move {
-//             remote_sender
-//                 .send(event)
-//                 .await
-//                 .expect("Updating receiver failed");
-//         })
-//     };
-// }
-
-/// Wasm-bindgen has a performance option to intern commonly used phrases
-/// This saves the decoding cost, making the interaction of Rust<->JS more performant.
-/// We intern all the HTML tags and attributes, making most operations much faster.
-///
-/// Interning takes about 1ms at the start of the app, but saves a *ton* of time later on.
-pub fn intern_cache() {
-    let cached_words = [
-        // All the HTML Tags
-        "a",
-        "abbr",
-        "address",
-        "area",
-        "article",
-        "aside",
-        "audio",
-        "b",
-        "base",
-        "bdi",
-        "bdo",
-        "big",
-        "blockquote",
-        "body",
-        "br",
-        "button",
-        "canvas",
-        "caption",
-        "cite",
-        "code",
-        "col",
-        "colgroup",
-        "command",
-        "data",
-        "datalist",
-        "dd",
-        "del",
-        "details",
-        "dfn",
-        "dialog",
-        "div",
-        "dl",
-        "dt",
-        "em",
-        "embed",
-        "fieldset",
-        "figcaption",
-        "figure",
-        "footer",
-        "form",
-        "h1",
-        "h2",
-        "h3",
-        "h4",
-        "h5",
-        "h6",
-        "head",
-        "header",
-        "hr",
-        "html",
-        "i",
-        "iframe",
-        "img",
-        "input",
-        "ins",
-        "kbd",
-        "keygen",
-        "label",
-        "legend",
-        "li",
-        "link",
-        "main",
-        "map",
-        "mark",
-        "menu",
-        "menuitem",
-        "meta",
-        "meter",
-        "nav",
-        "noscript",
-        "object",
-        "ol",
-        "optgroup",
-        "option",
-        "output",
-        "p",
-        "param",
-        "picture",
-        "pre",
-        "progress",
-        "q",
-        "rp",
-        "rt",
-        "ruby",
-        "s",
-        "samp",
-        "script",
-        "section",
-        "select",
-        "small",
-        "source",
-        "span",
-        "strong",
-        "style",
-        "sub",
-        "summary",
-        "sup",
-        "table",
-        "tbody",
-        "td",
-        "textarea",
-        "tfoot",
-        "th",
-        "thead",
-        "time",
-        "title",
-        "tr",
-        "track",
-        "u",
-        "ul",
-        "var",
-        "video",
-        "wbr",
-        // All the event handlers
-        "Attribute",
-        "accept",
-        "accept-charset",
-        "accesskey",
-        "action",
-        "alt",
-        "async",
-        "autocomplete",
-        "autofocus",
-        "autoplay",
-        "charset",
-        "checked",
-        "cite",
-        "class",
-        "cols",
-        "colspan",
-        "content",
-        "contenteditable",
-        "controls",
-        "coords",
-        "data",
-        "data-*",
-        "datetime",
-        "default",
-        "defer",
-        "dir",
-        "dirname",
-        "disabled",
-        "download",
-        "draggable",
-        "enctype",
-        "for",
-        "form",
-        "formaction",
-        "headers",
-        "height",
-        "hidden",
-        "high",
-        "href",
-        "hreflang",
-        "http-equiv",
-        "id",
-        "ismap",
-        "kind",
-        "label",
-        "lang",
-        "list",
-        "loop",
-        "low",
-        "max",
-        "maxlength",
-        "media",
-        "method",
-        "min",
-        "multiple",
-        "muted",
-        "name",
-        "novalidate",
-        "onabort",
-        "onafterprint",
-        "onbeforeprint",
-        "onbeforeunload",
-        "onblur",
-        "oncanplay",
-        "oncanplaythrough",
-        "onchange",
-        "onclick",
-        "oncontextmenu",
-        "oncopy",
-        "oncuechange",
-        "oncut",
-        "ondblclick",
-        "ondrag",
-        "ondragend",
-        "ondragenter",
-        "ondragleave",
-        "ondragover",
-        "ondragstart",
-        "ondrop",
-        "ondurationchange",
-        "onemptied",
-        "onended",
-        "onerror",
-        "onfocus",
-        "onhashchange",
-        "oninput",
-        "oninvalid",
-        "onkeydown",
-        "onkeypress",
-        "onkeyup",
-        "onload",
-        "onloadeddata",
-        "onloadedmetadata",
-        "onloadstart",
-        "onmousedown",
-        "onmousemove",
-        "onmouseout",
-        "onmouseover",
-        "onmouseup",
-        "onmousewheel",
-        "onoffline",
-        "ononline",
-        "<body>",
-        "onpageshow",
-        "onpaste",
-        "onpause",
-        "onplay",
-        "onplaying",
-        "<body>",
-        "onprogress",
-        "onratechange",
-        "onreset",
-        "onresize",
-        "onscroll",
-        "onsearch",
-        "onseeked",
-        "onseeking",
-        "onselect",
-        "onstalled",
-        "<body>",
-        "onsubmit",
-        "onsuspend",
-        "ontimeupdate",
-        "ontoggle",
-        "onunload",
-        "onvolumechange",
-        "onwaiting",
-        "onwheel",
-        "open",
-        "optimum",
-        "pattern",
-        "placeholder",
-        "poster",
-        "preload",
-        "readonly",
-        "rel",
-        "required",
-        "reversed",
-        "rows",
-        "rowspan",
-        "sandbox",
-        "scope",
-        "selected",
-        "shape",
-        "size",
-        "sizes",
-        "span",
-        "spellcheck",
-        "src",
-        "srcdoc",
-        "srclang",
-        "srcset",
-        "start",
-        "step",
-        "style",
-        "tabindex",
-        "target",
-        "title",
-        "translate",
-        "type",
-        "usemap",
-        "value",
-        "width",
-        "wrap",
-    ];
-
-    for s in cached_words {
-        wasm_bindgen::intern(s);
-    }
+        .expect("should have access to the Document")
+        .get_element_by_id("dioxusroot")
+        .unwrap()
 }

+ 0 - 409
packages/web/src/percypatch.rs

@@ -1,409 +0,0 @@
-use std::collections::HashMap;
-use std::collections::HashSet;
-use std::{cmp::min, rc::Rc};
-use wasm_bindgen::JsCast;
-use wasm_bindgen::JsValue;
-use web_sys::{Element, Node, Text};
-
-/// Apply all of the patches to our old root node in order to create the new root node
-/// that we desire.
-/// This is usually used after diffing two virtual nodes.
-pub fn patch<N: Into<Node>>(root_node: N, patches: &Vec<Patch>) -> Result<(), JsValue> {
-    // pub fn patch<N: Into<Node>>(root_node: N, patches: &Vec<Patch>) -> Result<ActiveClosures, JsValue> {
-    let root_node: Node = root_node.into();
-
-    let mut cur_node_idx = 0;
-
-    let mut nodes_to_find = HashSet::new();
-
-    for patch in patches {
-        nodes_to_find.insert(patch.node_idx());
-    }
-
-    let mut element_nodes_to_patch = HashMap::new();
-    let mut text_nodes_to_patch = HashMap::new();
-
-    // Closures that were added to the DOM during this patch operation.
-    // let mut active_closures = HashMap::new();
-
-    find_nodes(
-        root_node,
-        &mut cur_node_idx,
-        &mut nodes_to_find,
-        &mut element_nodes_to_patch,
-        &mut text_nodes_to_patch,
-    );
-
-    for patch in patches {
-        let patch_node_idx = patch.node_idx();
-
-        if let Some(element) = element_nodes_to_patch.get(&patch_node_idx) {
-            let new_closures = apply_element_patch(&element, &patch)?;
-            // active_closures.extend(new_closures);
-            continue;
-        }
-
-        if let Some(text_node) = text_nodes_to_patch.get(&patch_node_idx) {
-            apply_text_patch(&text_node, &patch)?;
-            continue;
-        }
-
-        unreachable!("Getting here means we didn't find the element or next node that we were supposed to patch.")
-    }
-
-    // Ok(active_closures)
-    Ok(())
-}
-
-fn find_nodes(
-    root_node: Node,
-    cur_node_idx: &mut usize,
-    nodes_to_find: &mut HashSet<usize>,
-    element_nodes_to_patch: &mut HashMap<usize, Element>,
-    text_nodes_to_patch: &mut HashMap<usize, Text>,
-) {
-    if nodes_to_find.len() == 0 {
-        return;
-    }
-
-    // We use child_nodes() instead of children() because children() ignores text nodes
-    let children = root_node.child_nodes();
-    let child_node_count = children.length();
-
-    // If the root node matches, mark it for patching
-    if nodes_to_find.get(&cur_node_idx).is_some() {
-        match root_node.node_type() {
-            Node::ELEMENT_NODE => {
-                element_nodes_to_patch.insert(*cur_node_idx, root_node.unchecked_into());
-            }
-            Node::TEXT_NODE => {
-                text_nodes_to_patch.insert(*cur_node_idx, root_node.unchecked_into());
-            }
-            other => unimplemented!("Unsupported root node type: {}", other),
-        }
-        nodes_to_find.remove(&cur_node_idx);
-    }
-
-    *cur_node_idx += 1;
-
-    for i in 0..child_node_count {
-        let node = children.item(i).unwrap();
-
-        match node.node_type() {
-            Node::ELEMENT_NODE => {
-                find_nodes(
-                    node,
-                    cur_node_idx,
-                    nodes_to_find,
-                    element_nodes_to_patch,
-                    text_nodes_to_patch,
-                );
-            }
-            Node::TEXT_NODE => {
-                if nodes_to_find.get(&cur_node_idx).is_some() {
-                    text_nodes_to_patch.insert(*cur_node_idx, node.unchecked_into());
-                }
-
-                *cur_node_idx += 1;
-            }
-            Node::COMMENT_NODE => {
-                // At this time we do not support user entered comment nodes, so if we see a comment
-                // then it was a delimiter created by virtual-dom-rs in order to ensure that two
-                // neighboring text nodes did not get merged into one by the browser. So we skip
-                // over this virtual-dom-rs generated comment node.
-            }
-            _other => {
-                // Ignoring unsupported child node type
-                // TODO: What do we do with this situation? Log a warning?
-            }
-        }
-    }
-}
-
-// pub type ActiveClosures = HashMap<u32, Vec<DynClosure>>;
-
-// fn apply_element_patch(node: &Element, patch: &Patch) -> Result<ActiveClosures, JsValue> {
-fn apply_element_patch(node: &Element, patch: &Patch) -> Result<(), JsValue> {
-    // let active_closures = HashMap::new();
-
-    match patch {
-        Patch::AddAttributes(_node_idx, attributes) => {
-            for (attrib_name, attrib_val) in attributes.iter() {
-                node.set_attribute(attrib_name, attrib_val)?;
-            }
-
-            // Ok(active_closures)
-            Ok(())
-        }
-        Patch::RemoveAttributes(_node_idx, attributes) => {
-            for attrib_name in attributes.iter() {
-                node.remove_attribute(attrib_name)?;
-            }
-
-            // Ok(active_closures)
-            Ok(())
-        }
-        Patch::Replace(_node_idx, new_node) => {
-            let created_node = create_dom_node(&new_node);
-
-            node.replace_with_with_node_1(&created_node.node)?;
-
-            Ok(())
-            // Ok(created_node.closures)
-        }
-        Patch::TruncateChildren(_node_idx, num_children_remaining) => {
-            let children = node.child_nodes();
-            let mut child_count = children.length();
-
-            // We skip over any separators that we placed between two text nodes
-            //   -> `<!--ptns-->`
-            //  and trim all children that come after our new desired `num_children_remaining`
-            let mut non_separator_children_found = 0;
-
-            for index in 0 as u32..child_count {
-                let child = children
-                    .get(min(index, child_count - 1))
-                    .expect("Potential child to truncate");
-
-                // If this is a comment node then we know that it is a `<!--ptns-->`
-                // text node separator that was created in virtual_node/mod.rs.
-                if child.node_type() == Node::COMMENT_NODE {
-                    continue;
-                }
-
-                non_separator_children_found += 1;
-
-                if non_separator_children_found <= *num_children_remaining as u32 {
-                    continue;
-                }
-
-                node.remove_child(&child).expect("Truncated children");
-                child_count -= 1;
-            }
-
-            Ok(())
-            // Ok(active_closures)
-        }
-        Patch::AppendChildren(_node_idx, new_nodes) => {
-            let parent = &node;
-
-            let mut active_closures = HashMap::new();
-
-            for new_node in new_nodes {
-                let created_node = create_dom_node(&new_node);
-                // let created_node = new_node.create_dom_node();
-
-                parent.append_child(&created_node.node)?;
-
-                active_closures.extend(created_node.closures);
-            }
-
-            Ok(())
-            // Ok(active_closures)
-        }
-        Patch::ChangeText(_node_idx, _new_node) => {
-            unreachable!("Elements should not receive ChangeText patches.")
-        }
-    }
-}
-
-fn apply_text_patch(node: &Text, patch: &Patch) -> Result<(), JsValue> {
-    match patch {
-        Patch::ChangeText(_node_idx, new_node) => {
-            node.set_node_value(Some(&new_node.text));
-        }
-        Patch::Replace(_node_idx, new_node) => {
-            node.replace_with_with_node_1(&create_dom_node(&new_node).node)?;
-            // node.replace_with_with_node_1(&new_node.create_dom_node().node)?;
-        }
-        other => unreachable!(
-            "Text nodes should only receive ChangeText or Replace patches, not ",
-            // other,
-            // "Text nodes should only receive ChangeText or Replace patches, not {:?}.",
-            // other,
-        ),
-    };
-
-    Ok(())
-}
-
-/// A node along with all of the closures that were created for that
-/// node's events and all of it's child node's events.
-pub struct CreatedNode<T> {
-    /// A `Node` or `Element` that was created from a `VirtualNode`
-    pub node: T,
-    /// A map of a node's unique identifier along with all of the Closures for that node.
-    ///
-    /// The DomUpdater uses this to look up nodes and see if they're still in the page. If not
-    /// the reference that we maintain to their closure will be dropped, thus freeing the Closure's
-    /// memory.
-    pub closures: HashMap<u32, Vec<DynClosure>>,
-}
-
-/// Box<dyn AsRef<JsValue>>> is our js_sys::Closure. Stored this way to allow us to store
-/// any Closure regardless of the arguments.
-pub type DynClosure = Rc<dyn AsRef<JsValue>>;
-
-impl<T> CreatedNode<T> {
-    pub fn without_closures<N: Into<T>>(node: N) -> Self {
-        CreatedNode {
-            node: node.into(),
-            closures: HashMap::with_capacity(0),
-        }
-    }
-}
-
-impl<T> std::ops::Deref for CreatedNode<T> {
-    type Target = T;
-    fn deref(&self) -> &Self::Target {
-        &self.node
-    }
-}
-
-impl From<CreatedNode<Element>> for CreatedNode<Node> {
-    fn from(other: CreatedNode<Element>) -> CreatedNode<Node> {
-        CreatedNode {
-            node: other.node.into(),
-            closures: other.closures,
-        }
-    }
-}
-fn create_dom_node(node: &VNode<'_>) -> CreatedNode<Node> {
-    match node {
-        VNode::Text(text_node) => CreatedNode::without_closures(create_text_node(text_node)),
-        VNode::Element(element_node) => create_element_node(element_node).into(),
-        // VNode::Element(element_node) => element_node.create_element_node().into(),
-        VNode::Suspended => todo!(" not iimplemented yet"),
-        VNode::Component(_) => todo!(" not iimplemented yet"),
-    }
-}
-
-/// Build a DOM element by recursively creating DOM nodes for this element and it's
-/// children, it's children's children, etc.
-pub fn create_element_node(node: &dioxus_core::nodes::VElement) -> CreatedNode<Element> {
-    let document = web_sys::window().unwrap().document().unwrap();
-
-    // TODO: enable svg again
-    // let element = if html_validation::is_svg_namespace(&node.tag_name) {
-    //     document
-    //         .create_element_ns(Some("http://www.w3.org/2000/svg"), &node.tag_name)
-    //         .unwrap()
-    // } else {
-    let element = document.create_element(&node.tag_name).unwrap();
-    // };
-
-    let mut closures = HashMap::new();
-
-    node.attributes
-        .iter()
-        .map(|f| (f.name, f.value))
-        .for_each(|(name, value)| {
-            if name == "unsafe_inner_html" {
-                element.set_inner_html(value);
-
-                return;
-            }
-
-            element
-                .set_attribute(name, value)
-                .expect("Set element attribute in create element");
-        });
-
-    // if node.events.0.len() > 0 {
-    //     let unique_id = create_unique_identifier();
-
-    //     element
-    //         .set_attribute("data-vdom-id".into(), &unique_id.to_string())
-    //         .expect("Could not set attribute on element");
-
-    //     closures.insert(unique_id, vec![]);
-
-    //     node.events.0.iter().for_each(|(onevent, callback)| {
-    //         // onclick -> click
-    //         let event = &onevent[2..];
-
-    //         let current_elem: &EventTarget = element.dyn_ref().unwrap();
-
-    //         current_elem
-    //             .add_event_listener_with_callback(event, callback.as_ref().as_ref().unchecked_ref())
-    //             .unwrap();
-
-    //         closures
-    //             .get_mut(&unique_id)
-    //             .unwrap()
-    //             .push(Rc::clone(callback));
-    //     });
-    // }
-
-    let mut previous_node_was_text = false;
-
-    node.children.iter().for_each(|child| {
-        // log::info!("Patching child");
-        match child {
-            VNode::Text(text_node) => {
-                let current_node = element.as_ref() as &web_sys::Node;
-
-                // We ensure that the text siblings are patched by preventing the browser from merging
-                // neighboring text nodes. Originally inspired by some of React's work from 2016.
-                //  -> https://reactjs.org/blog/2016/04/07/react-v15.html#major-changes
-                //  -> https://github.com/facebook/react/pull/5753
-                //
-                // `ptns` = Percy text node separator
-                if previous_node_was_text {
-                    let separator = document.create_comment("ptns");
-                    current_node
-                        .append_child(separator.as_ref() as &web_sys::Node)
-                        .unwrap();
-                }
-
-                current_node
-                    .append_child(&create_text_node(&text_node))
-                    .unwrap();
-
-                previous_node_was_text = true;
-            }
-            VNode::Element(element_node) => {
-                previous_node_was_text = false;
-
-                let child = create_element_node(element_node);
-                // let child = element_node.create_element_node();
-                let child_elem: Element = child.node;
-
-                closures.extend(child.closures);
-
-                element.append_child(&child_elem).unwrap();
-            }
-            VNode::Suspended => {
-                todo!("Not yet supported")
-            }
-            VNode::Component(_) => {
-                todo!("Not yet supported")
-            }
-        }
-    });
-
-    // TODO: connect on mount to the event system somehow
-    // if let Some(on_create_elem) = node.events.0.get("on_create_elem") {
-    //     let on_create_elem: &js_sys::Function = on_create_elem.as_ref().as_ref().unchecked_ref();
-    //     on_create_elem
-    //         .call1(&wasm_bindgen::JsValue::NULL, &element)
-    //         .unwrap();
-    // }
-
-    CreatedNode {
-        node: element,
-        closures,
-    }
-}
-
-/// Return a `Text` element from a `VirtualNode`, typically right before adding it
-/// into the DOM.
-pub fn create_text_node(node: &VText) -> Text {
-    let document = web_sys::window().unwrap().document().unwrap();
-    document.create_text_node(&node.text)
-}
-
-// /// For any listeners in the tree, attach the sender closure.
-// /// When a event is triggered, we convert it into the synthetic event type and dump it back in the Virtual Dom's queu
-// fn attach_listeners(sender: &UnboundedSender<EventTrigger>, dom: &VirtualDom) {}
-// fn render_diffs() {}

+ 4 - 4
src/lib.rs

@@ -49,7 +49,7 @@
 //! }
 //! ```
 //!
-//! Props that are valid for the `'static` lifetime automatically get memoized by Diouxs. This means the component won't
+//! Props that are valid for the `'pub static` lifetime automatically get memoized by Diouxs. This means the component won't
 //! re-render if its Props didn't change. However, Props that borrow data from their parent cannot be safely memoized, and
 //! will always re-render if their parent changes. To borrow data from a parent, your component needs to add explicit lifetimes,
 //! otherwise Rust will get confused about whether data is borrowed from either Props or Context. Since Dioxus manages
@@ -67,7 +67,7 @@
 //!
 //!
 //! The lifetimes might look a little messy, but are crucially important for Dioxus's efficiency and overall ergonimics.
-//! Components can also be crafted as static closures, enabling type inference without all the type signature noise. However,
+//! Components can also be crafted as pub static closures, enabling type inference without all the type signature noise. However,
 //! closure-style components cannot work with borrowed data due to limitations in Rust's lifetime system.
 //!
 //! To use custom properties for components, you'll need to derive the `Props` trait for your properties. This trait
@@ -93,7 +93,7 @@
 //! Dioxus uses hooks for state management. Hooks are a form of state persisted between calls of the function component.
 //!
 //! ```
-//! pub static Example: FC<()> = |cx| {
+//! pub pub static Example: FC<()> = |cx| {
 //!     let (val, set_val) = use_state(cx, || 0);
 //!     cx.render(rsx!(
 //!         button { onclick: move |_| set_val(val + 1) }
@@ -156,7 +156,7 @@
 //!     diouxs::web::launch(Example);
 //! }
 //!
-//! pub static Example: FC<()> = |cx| {
+//! pub pub static Example: FC<()> = |cx| {
 //!     cx.render(rsx! {
 //!         div { "Hello World!" }
 //!     })