Selaa lähdekoodia

feat: rebuild doesn't return errors

Jonathan Kelley 3 vuotta sitten
vanhempi
commit
f457b71

+ 1 - 1
packages/core/benches/jsframework.rs

@@ -45,7 +45,7 @@ fn create_rows(c: &mut Criterion) {
     c.bench_function("create rows", |b| {
         b.iter(|| {
             let mut dom = VirtualDom::new(App);
-            let g = dom.rebuild().unwrap();
+            let g = dom.rebuild();
             assert!(g.edits.len() > 1);
         })
     });

+ 1 - 1
packages/core/examples/jsframework.rs

@@ -6,7 +6,7 @@ use std::fmt::Display;
 
 fn main() {
     let mut dom = VirtualDom::new(App);
-    let g = dom.rebuild().unwrap();
+    let g = dom.rebuild();
     assert!(g.edits.len() > 1);
 }
 

+ 1 - 3
packages/core/src/diff.rs

@@ -153,7 +153,7 @@ impl<'bump> DiffMachine<'bump> {
     /// This method implements a depth-first iterative tree traversal.
     ///
     /// We do depth-first to maintain high cache locality (nodes were originally generated recursively).
-    pub async fn work(&mut self) -> Result<()> {
+    pub async fn work(&mut self) {
         // defer to individual functions so the compiler produces better code
         // large functions tend to be difficult for the compiler to work with
         while let Some(instruction) = self.stack.pop() {
@@ -190,8 +190,6 @@ impl<'bump> DiffMachine<'bump> {
                 }
             };
         }
-
-        Ok(())
     }
 
     fn mount(&mut self, and: MountType<'bump>) {

+ 24 - 22
packages/core/src/events.rs

@@ -602,8 +602,31 @@ pub mod on {
     }
 
     pub trait KeyboardEventInner {
+        fn alt_key(&self) -> bool;
+
         fn char_code(&self) -> u32;
 
+        /// Identify which "key" was entered.
+        ///
+        /// This is the best method to use for all languages. They key gets mapped to a String sequence which you can match on.
+        /// The key isn't an enum because there are just so many context-dependent keys.
+        ///
+        /// A full list on which keys to use is available at:
+        /// <https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values>
+        ///
+        /// # Example
+        ///
+        /// ```rust
+        /// match event.key().as_str() {
+        ///     "Esc" | "Escape" => {}
+        ///     "ArrowDown" => {}
+        ///     "ArrowLeft" => {}
+        ///      _ => {}
+        /// }
+        /// ```
+        ///
+        fn key(&self) -> String;
+
         /// Get the key code as an enum Variant.
         ///
         /// This is intended for things like arrow keys, escape keys, function keys, and other non-international keys.
@@ -627,35 +650,14 @@ pub mod on {
         /// Check if the ctrl key was pressed down
         fn ctrl_key(&self) -> bool;
 
-        /// Identify which "key" was entered.
-        ///
-        /// This is the best method to use for all languages. They key gets mapped to a String sequence which you can match on.
-        /// The key isn't an enum because there are just so many context-dependent keys.
-        ///
-        /// A full list on which keys to use is available at:
-        /// <https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values>
-        ///
-        /// # Example
-        ///
-        /// ```rust
-        /// match event.key().as_str() {
-        ///     "Esc" | "Escape" => {}
-        ///     "ArrowDown" => {}
-        ///     "ArrowLeft" => {}
-        ///      _ => {}
-        /// }
-        /// ```
-        ///
-        fn key(&self) -> String;
+        fn get_modifier_state(&self, key_code: &str) -> bool;
 
-        // fn key(&self) -> String;
         fn locale(&self) -> String;
         fn location(&self) -> usize;
         fn meta_key(&self) -> bool;
         fn repeat(&self) -> bool;
         fn shift_key(&self) -> bool;
         fn which(&self) -> usize;
-        fn get_modifier_state(&self, key_code: usize) -> bool;
     }
 
     pub trait FocusEventInner {

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

@@ -139,8 +139,8 @@ impl<'a> Mutations<'a> {
 
 // refs are only assigned once
 pub struct NodeRefMutation<'a> {
-    element: &'a mut Option<once_cell::sync::OnceCell<Box<dyn Any>>>,
-    element_id: ElementId,
+    pub element: &'a mut Option<once_cell::sync::OnceCell<Box<dyn Any>>>,
+    pub element_id: ElementId,
 }
 
 impl<'a> std::fmt::Debug for NodeRefMutation<'a> {

+ 24 - 25
packages/core/src/virtual_dom.rs

@@ -124,26 +124,12 @@ impl VirtualDom {
         }
     }
 
-    pub fn launch_in_place(root: FC<()>) -> Self {
-        let mut s = Self::new(root);
-        s.rebuild().unwrap();
-        s
-    }
-
-    /// Creates a new virtualdom and immediately rebuilds it in place, not caring about the RealDom to write into.
-    ///
-    pub fn launch_with_props_in_place<P: Properties + 'static>(root: FC<P>, root_props: P) -> Self {
-        let mut s = Self::new_with_props(root, root_props);
-        s.rebuild().unwrap();
-        s
-    }
-
     pub fn base_scope(&self) -> &Scope {
-        unsafe { self.shared.get_scope(self.base_scope).unwrap() }
+        self.shared.get_scope(self.base_scope).unwrap()
     }
 
     pub fn get_scope(&self, id: ScopeId) -> Option<&Scope> {
-        unsafe { self.shared.get_scope(id) }
+        self.shared.get_scope(id)
     }
 
     /// Performs a *full* rebuild of the virtual dom, returning every edit required to generate the actual dom rom scratch
@@ -152,7 +138,7 @@ impl VirtualDom {
     ///
     /// Events like garabge collection, application of refs, etc are not handled by this method and can only be progressed
     /// through "run". We completely avoid the task scheduler infrastructure.
-    pub fn rebuild<'s>(&'s mut self) -> Result<Mutations<'s>> {
+    pub fn rebuild<'s>(&'s mut self) -> Mutations<'s> {
         let mut fut = self.rebuild_async().boxed_local();
 
         loop {
@@ -163,7 +149,11 @@ impl VirtualDom {
     }
 
     /// Rebuild the dom from the ground up
-    pub async fn rebuild_async<'s>(&'s mut self) -> Result<Mutations<'s>> {
+    ///
+    /// This method is asynchronous to prevent the application from blocking while the dom is being rebuilt. Computing
+    /// the diff and creating nodes can be expensive, so we provide this method to avoid blocking the main thread. This
+    /// method can be useful when needing to perform some crucial periodic tasks.
+    pub async fn rebuild_async<'s>(&'s mut self) -> Mutations<'s> {
         let mut diff_machine = DiffMachine::new(Mutations::new(), self.base_scope, &self.shared);
 
         let cur_component = self
@@ -176,7 +166,7 @@ impl VirtualDom {
             diff_machine
                 .stack
                 .create_node(cur_component.frames.fin_head(), MountType::Append);
-            diff_machine.work().await.unwrap();
+            diff_machine.work().await;
         } else {
             // todo: should this be a hard error?
             log::warn!(
@@ -185,11 +175,20 @@ impl VirtualDom {
             );
         }
 
-        Ok(diff_machine.mutations)
+        diff_machine.mutations
+    }
+
+    pub fn diff_sync<'s>(&'s mut self) -> Mutations<'s> {
+        let mut fut = self.diff_async().boxed_local();
+
+        loop {
+            if let Some(edits) = (&mut fut).now_or_never() {
+                break edits;
+            }
+        }
     }
 
-    // diff the dom with itself
-    pub async fn diff_async<'s>(&'s mut self) -> Result<Mutations<'s>> {
+    pub async fn diff_async<'s>(&'s mut self) -> Mutations<'s> {
         let mut diff_machine = DiffMachine::new(Mutations::new(), self.base_scope, &self.shared);
 
         let cur_component = self
@@ -204,15 +203,15 @@ impl VirtualDom {
             new: cur_component.frames.fin_head(),
         });
 
-        diff_machine.work().await.unwrap();
+        diff_machine.work().await;
 
-        Ok(diff_machine.mutations)
+        diff_machine.mutations
     }
 
     /// Runs the virtualdom immediately, not waiting for any suspended nodes to complete.
     ///
     /// This method will not wait for any suspended nodes to complete.
-    pub fn run_immediate<'s>(&'s mut self) -> Result<Mutations<'s>> {
+    pub fn run_immediate<'s>(&'s mut self) -> Mutations<'s> {
         todo!()
         // use futures_util::FutureExt;
         // let mut is_ready = || false;

+ 7 - 7
packages/core/tests/create_iterative.rs

@@ -22,7 +22,7 @@ fn test_original_diff() {
     };
 
     let mut dom = VirtualDom::new(App);
-    let mutations = dom.rebuild().unwrap();
+    let mutations = dom.rebuild();
     assert_eq!(
         mutations.edits,
         [
@@ -61,7 +61,7 @@ async fn create() {
 
     test_logging::set_up_logging(LOGGING_ENABLED);
     let mut dom = VirtualDom::new(App);
-    let mutations = dom.rebuild_async().await.unwrap();
+    let mutations = dom.rebuild_async().await;
     assert_eq!(
         mutations.edits,
         [
@@ -103,7 +103,7 @@ async fn create_list() {
     test_logging::set_up_logging(LOGGING_ENABLED);
 
     let mut dom = VirtualDom::new(App);
-    let mutations = dom.rebuild_async().await.unwrap();
+    let mutations = dom.rebuild_async().await;
 
     // copilot wrote this test :P
     assert_eq!(
@@ -146,7 +146,7 @@ async fn create_simple() {
     test_logging::set_up_logging(LOGGING_ENABLED);
 
     let mut dom = VirtualDom::new(App);
-    let mutations = dom.rebuild_async().await.unwrap();
+    let mutations = dom.rebuild_async().await;
 
     // copilot wrote this test :P
     assert_eq!(
@@ -182,7 +182,7 @@ async fn create_components() {
     test_logging::set_up_logging(LOGGING_ENABLED);
 
     let mut dom = VirtualDom::new(App);
-    let mutations = dom.rebuild_async().await.unwrap();
+    let mutations = dom.rebuild_async().await;
 
     assert_eq!(
         mutations.edits,
@@ -228,7 +228,7 @@ async fn anchors() {
     test_logging::set_up_logging(LOGGING_ENABLED);
 
     let mut dom = VirtualDom::new(App);
-    let mutations = dom.rebuild_async().await.unwrap();
+    let mutations = dom.rebuild_async().await;
     assert_eq!(
         mutations.edits,
         [
@@ -254,7 +254,7 @@ async fn suspended() {
     test_logging::set_up_logging(LOGGING_ENABLED);
 
     let mut dom = VirtualDom::new(App);
-    let mutations = dom.rebuild_async().await.unwrap();
+    let mutations = dom.rebuild_async().await;
 
     assert_eq!(
         mutations.edits,

+ 2 - 2
packages/core/tests/diff_iterative.rs

@@ -40,9 +40,9 @@ async fn test_iterative_create_components() {
 
     let mut dom = VirtualDom::new(App);
 
-    let mutations = dom.rebuild_async().await.unwrap();
+    let mutations = dom.rebuild_async().await;
     dbg!(mutations);
 
-    let mutations = dom.diff_async().await.unwrap();
+    let mutations = dom.diff_async().await;
     dbg!(mutations);
 }

+ 1 - 1
packages/core/tests/eventsystem.rs

@@ -14,7 +14,7 @@ async fn event_queue_works() {
     };
 
     let mut dom = VirtualDom::new(App);
-    let edits = dom.rebuild().unwrap();
+    let edits = dom.rebuild();
 
     async_std::task::spawn_local(async move {
         match dom.run_unbounded().await {

+ 6 - 6
packages/core/tests/vdom_rebuild.rs

@@ -19,7 +19,7 @@ fn app_runs() {
         cx.render(rsx!( div{"hello"} ))
     };
     let mut vdom = VirtualDom::new(App);
-    let edits = vdom.rebuild().unwrap();
+    let edits = vdom.rebuild();
     dbg!(edits);
 }
 
@@ -32,7 +32,7 @@ fn fragments_work() {
         ))
     };
     let mut vdom = VirtualDom::new(App);
-    let edits = vdom.rebuild().unwrap();
+    let edits = vdom.rebuild();
     // should result in a final "appendchildren n=2"
     dbg!(edits);
 }
@@ -46,7 +46,7 @@ fn lists_work() {
         ))
     };
     let mut vdom = VirtualDom::new(App);
-    let edits = vdom.rebuild().unwrap();
+    let edits = vdom.rebuild();
     dbg!(edits);
 }
 
@@ -61,7 +61,7 @@ fn conditional_rendering() {
     };
     let mut vdom = VirtualDom::new(App);
 
-    let mutations = vdom.rebuild().unwrap();
+    let mutations = vdom.rebuild();
     dbg!(&mutations);
     // the "false" fragment should generate an empty placeholder to re-visit
     assert!(mutations.edits[mutations.edits.len() - 2].is("CreatePlaceholder"));
@@ -82,7 +82,7 @@ fn child_components() {
         ))
     };
     let mut vdom = VirtualDom::new(App);
-    let edits = vdom.rebuild().unwrap();
+    let edits = vdom.rebuild();
     dbg!(edits);
 }
 
@@ -94,6 +94,6 @@ fn suspended_works() {
     };
 
     let mut vdom = VirtualDom::new(App);
-    let edits = vdom.rebuild().unwrap();
+    let edits = vdom.rebuild();
     dbg!(edits);
 }

+ 1 - 1
packages/core/tests/work_sync.rs

@@ -22,7 +22,7 @@ fn worksync() {
     let mutations = loop {
         let g = (&mut fut).now_or_never();
         if g.is_some() {
-            break g.unwrap().unwrap();
+            break g.unwrap();
         }
     };
 

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

@@ -5,7 +5,7 @@ use dioxus_html as dioxus_elements;
 
 fn main() {
     let mut dom = VirtualDom::new(App);
-    dom.rebuild().unwrap();
+    dom.rebuild();
     println!(
         "{}",
         dioxus_ssr::render_vdom(&dom, |c| c.newline(true).indent(true))

+ 3 - 1
packages/ssr/examples/tide.rs

@@ -19,7 +19,9 @@ async fn main() -> Result<(), std::io::Error> {
             .map(|f| f.parse().unwrap_or("...?".to_string()))
             .unwrap_or("...?".to_string());
 
-        let dom = VirtualDom::launch_with_props_in_place(Example, ExampleProps { initial_name });
+        let mut dom = VirtualDom::new_with_props(Example, ExampleProps { initial_name });
+
+        dom.rebuild();
 
         Ok(Response::builder(200)
             .body(format!("{}", dioxus_ssr::render_vdom(&dom, |c| c)))

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

@@ -13,7 +13,8 @@ fn main() {
     let mut file = File::create("example.html").unwrap();
 
     let mut dom = VirtualDom::new(App);
-    dom.rebuild().expect("failed to run virtualdom");
+
+    dom.rebuild();
 
     file.write_fmt(format_args!(
         "{}",

+ 6 - 6
packages/ssr/src/lib.rs

@@ -286,28 +286,28 @@ mod tests {
     #[test]
     fn to_string_works() {
         let mut dom = VirtualDom::new(SIMPLE_APP);
-        dom.rebuild().expect("failed to run virtualdom");
+        dom.rebuild();
         dbg!(render_vdom(&dom, |c| c));
     }
 
     #[test]
     fn hydration() {
         let mut dom = VirtualDom::new(NESTED_APP);
-        dom.rebuild().expect("failed to run virtualdom");
+        dom.rebuild();
         dbg!(render_vdom(&dom, |c| c.pre_render(true)));
     }
 
     #[test]
     fn nested() {
         let mut dom = VirtualDom::new(NESTED_APP);
-        dom.rebuild().expect("failed to run virtualdom");
+        dom.rebuild();
         dbg!(render_vdom(&dom, |c| c));
     }
 
     #[test]
     fn fragment_app() {
         let mut dom = VirtualDom::new(FRAGMENT_APP);
-        dom.rebuild().expect("failed to run virtualdom");
+        dom.rebuild();
         dbg!(render_vdom(&dom, |c| c));
     }
 
@@ -319,7 +319,7 @@ mod tests {
         let mut file = File::create("index.html").unwrap();
 
         let mut dom = VirtualDom::new(SLIGHTLY_MORE_COMPLEX);
-        dom.rebuild().expect("failed to run virtualdom");
+        dom.rebuild();
 
         file.write_fmt(format_args!(
             "{}",
@@ -337,7 +337,7 @@ mod tests {
         };
 
         let mut dom = VirtualDom::new(STLYE_APP);
-        dom.rebuild().expect("failed to run virtualdom");
+        dom.rebuild();
         dbg!(render_vdom(&dom, |c| c));
     }
 }

+ 37 - 11
packages/web/examples/basic.rs

@@ -1,19 +1,17 @@
 //! 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 wasm_timer;
-
-use std::future::Future;
-
-use std::{pin::Pin, time::Duration};
 
 use dioxus::prelude::*;
-
 use dioxus_web::*;
+use std::future::Future;
+use std::{pin::Pin, time::Duration};
 
 fn main() {
     // Setup logging
@@ -21,17 +19,33 @@ fn main() {
     console_error_panic_hook::set_once();
 
     // Run the app
-    dioxus_web::launch(App, |c| c)
+    dioxus_web::launch(APP, |c| c)
 }
 
-static App: FC<()> = |cx| {
+static APP: FC<()> = |cx| {
     let mut count = use_state(cx, || 3);
 
     cx.render(rsx! {
         div {
-            button {
-                "add"
-                onclick: move |_| count += 1
+            button { onclick: move |_| count += 1, "add"  }
+            div {
+                div {
+                    div {
+                        div {
+                            div {
+                                Fragment {
+                                    a {"wo"}
+                                    p {"wo"}
+                                    li {"wo"}
+                                    em {"wo"}
+                                }
+                            }
+                        }
+                        Child {}
+                    }
+                    Child {}
+                }
+                Child {}
             }
             ul {
                 {(0..*count).map(|f| rsx!{
@@ -43,3 +57,15 @@ static App: FC<()> = |cx| {
         }
     })
 };
+
+static Child: FC<()> = |cx| {
+    cx.render(rsx! {
+        div {
+            div {
+                div {
+                    "hello child"
+                }
+            }
+        }
+    })
+};

+ 111 - 95
packages/web/src/dom.rs

@@ -2,6 +2,7 @@ use std::{collections::HashMap, rc::Rc, sync::Arc};
 
 use dioxus_core::{
     events::{EventTrigger, VirtualEvent},
+    mutations::NodeRefMutation,
     DomEdit, ElementId, ScopeId,
 };
 use fxhash::FxHashMap;
@@ -77,6 +78,18 @@ impl WebsysDom {
         }
     }
 
+    pub fn apply_refs(&mut self, refs: &[NodeRefMutation]) {
+        for item in refs {
+            if let Some(bla) = &item.element {
+                let node = self.nodes[item.element_id.as_u64() as usize]
+                    .as_ref()
+                    .unwrap()
+                    .clone();
+                bla.set(Box::new(node)).unwrap();
+            }
+        }
+    }
+
     pub fn process_edits(&mut self, edits: &mut Vec<DomEdit>) {
         for edit in edits.drain(..) {
             log::info!("Handling edit: {:#?}", edit);
@@ -92,12 +105,13 @@ impl WebsysDom {
                 DomEdit::CreateElementNs { tag, id, ns } => self.create_element(tag, Some(ns), id),
                 DomEdit::CreatePlaceholder { id } => self.create_placeholder(id),
                 DomEdit::NewEventListener {
-                    event_name: event,
+                    event_name,
                     scope,
-                    mounted_node_id: node,
-                } => self.new_event_listener(event, scope, node),
+                    mounted_node_id,
+                } => self.new_event_listener(event_name, scope, mounted_node_id),
 
                 DomEdit::RemoveEventListener { event } => todo!(),
+
                 DomEdit::SetText { text } => self.set_text(text),
                 DomEdit::SetAttribute { field, value, ns } => self.set_attribute(field, value, ns),
                 DomEdit::RemoveAttribute { name } => self.remove_attribute(name),
@@ -118,6 +132,7 @@ impl WebsysDom {
 
         self.stack.push(real_node);
     }
+
     // drop the node off the stack
     fn pop(&mut self) {
         self.stack.pop();
@@ -154,44 +169,21 @@ impl WebsysDom {
     }
 
     fn replace_with(&mut self, m: u32, root: u64) {
-        let mut new_nodes = vec![];
-        for _ in 0..m {
-            new_nodes.push(self.stack.pop());
-        }
-
         let old = self.nodes[root as usize].as_ref().unwrap();
-        let arr: js_sys::Array = new_nodes.iter().collect();
-        let el = old.dyn_ref::<Element>().unwrap();
-        el.replace_with_with_node(&arr).unwrap();
-
-        // let arr = js_sys::Array::from();
-
-        // TODO: use different-sized replace withs
-        // if m == 1 {
-        //     if old_node.has_type::<Element>() {
-        //         old_node
-        //             .dyn_ref::<Element>()
-        //             .unwrap()
-        //             .replace_with_with_node_1(&new_node)
-        //             .unwrap();
-        //     } else if old_node.has_type::<web_sys::CharacterData>() {
-        //         old_node
-        //             .dyn_ref::<web_sys::CharacterData>()
-        //             .unwrap()
-        //             .replace_with_with_node_1(&new_node)
-        //             .unwrap();
-        //     } else if old_node.has_type::<web_sys::DocumentType>() {
-        //         old_node
-        //             .dyn_ref::<web_sys::DocumentType>()
-        //             .unwrap()
-        //             .replace_with_with_node_1(&new_node)
-        //             .unwrap();
-        //     } else {
-        //         panic!("Cannot replace node: {:?}", old_node);
-        //     }
-        // }
-
-        // self.stack.push(new_node);
+
+        let arr: js_sys::Array = self
+            .stack
+            .list
+            .drain((self.stack.list.len() - m as usize)..)
+            .collect();
+
+        if let Some(el) = old.dyn_ref::<Element>() {
+            el.replace_with_with_node(&arr).unwrap();
+        } else if let Some(el) = old.dyn_ref::<web_sys::CharacterData>() {
+            el.replace_with_with_node(&arr).unwrap();
+        } else if let Some(el) = old.dyn_ref::<web_sys::DocumentType>() {
+            el.replace_with_with_node(&arr).unwrap();
+        }
     }
 
     fn remove(&mut self, root: u64) {
@@ -213,22 +205,22 @@ impl WebsysDom {
         self.create_element("pre", None, id);
         self.set_attribute("hidden", "", None);
     }
-    fn create_text_node(&mut self, text: &str, id: u64) {
-        // let nid = self.node_counter.next();
 
+    fn create_text_node(&mut self, text: &str, id: u64) {
         let textnode = self
             .document
             .create_text_node(text)
             .dyn_into::<Node>()
             .unwrap();
 
-        let id = id as usize;
         self.stack.push(textnode.clone());
-        self.nodes[id] = Some(textnode);
+
+        self.nodes[(id as usize)] = Some(textnode);
     }
 
     fn create_element(&mut self, tag: &str, ns: Option<&'static str>, id: u64) {
         let tag = wasm_bindgen::intern(tag);
+
         let el = match ns {
             Some(ns) => self
                 .document
@@ -243,17 +235,12 @@ impl WebsysDom {
                 .dyn_into::<Node>()
                 .unwrap(),
         };
-        let id = id as usize;
 
         self.stack.push(el.clone());
-        self.nodes[id] = Some(el);
-        // let nid = self.node_counter.?next();
-        // let nid = self.nodes.insert(el).data().as_ffi();
-        // ElementId::new(nid)
+        self.nodes[(id as usize)] = Some(el);
     }
 
     fn new_event_listener(&mut self, event: &'static str, scope: ScopeId, real_id: u64) {
-        // let (_on, event) = event.split_at(2);
         let event = wasm_bindgen::intern(event);
 
         // attach the correct attributes to the element
@@ -313,7 +300,8 @@ impl WebsysDom {
     }
 
     fn set_attribute(&mut self, name: &str, value: &str, ns: Option<&str>) {
-        if let Some(el) = self.stack.top().dyn_ref::<Element>() {
+        let node = self.stack.top();
+        if let Some(el) = node.dyn_ref::<Element>() {
             match ns {
                 // inline style support
                 Some("style") => {
@@ -323,11 +311,20 @@ impl WebsysDom {
                 }
                 _ => el.set_attribute(name, value).unwrap(),
             }
-            match name {
-                "value" => {}
-                "checked" => {}
-                "selected" => {}
-                _ => {}
+        }
+
+        if let Some(node) = node.dyn_ref::<HtmlInputElement>() {
+            if name == "value" {
+                node.set_value(value);
+            }
+            if name == "checked" {
+                node.set_checked(true);
+            }
+        }
+
+        if let Some(node) = node.dyn_ref::<HtmlOptionElement>() {
+            if name == "selected" {
+                node.set_selected(true);
             }
         }
     }
@@ -355,35 +352,50 @@ impl WebsysDom {
     }
 
     fn insert_after(&mut self, n: u32, root: u64) {
-        let mut new_nodes = vec![];
-        for _ in 0..n {
-            new_nodes.push(self.stack.pop());
-        }
-
-        let after = self.stack.top().clone();
-        let arr: js_sys::Array = new_nodes.iter().collect();
+        let old = self.nodes[root as usize].as_ref().unwrap();
 
-        let el = after.dyn_into::<Element>().unwrap();
-        el.after_with_node(&arr).unwrap();
-        // let mut old_nodes = vec![];
-        // for _ in 0..n {
-        //     old_nodes.push(self.stack.pop());
-        // }
+        let arr: js_sys::Array = self
+            .stack
+            .list
+            .drain((self.stack.list.len() - n as usize)..)
+            .collect();
 
-        // let el = self.stack.top();
+        if let Some(el) = old.dyn_ref::<Element>() {
+            el.after_with_node(&arr).unwrap();
+        } else if let Some(el) = old.dyn_ref::<web_sys::CharacterData>() {
+            el.after_with_node(&arr).unwrap();
+        } else if let Some(el) = old.dyn_ref::<web_sys::DocumentType>() {
+            el.after_with_node(&arr).unwrap();
+        }
     }
 
     fn insert_before(&mut self, n: u32, root: u64) {
-        let n = n as usize;
-        let root = self
-            .stack
-            .list
-            .get(self.stack.list.len() - n)
-            .unwrap()
-            .clone();
-        for _ in 0..n {
-            let el = self.stack.pop();
-            root.insert_before(&el, None).unwrap();
+        let after = self.nodes[root as usize].as_ref().unwrap();
+
+        if n == 1 {
+            let before = self.stack.pop();
+
+            after
+                .parent_node()
+                .unwrap()
+                .insert_before(&before, Some(&after))
+                .unwrap();
+
+            after.insert_before(&before, None).unwrap();
+        } else {
+            let arr: js_sys::Array = self
+                .stack
+                .list
+                .drain((self.stack.list.len() - n as usize)..)
+                .collect();
+
+            if let Some(el) = after.dyn_ref::<Element>() {
+                el.before_with_node(&arr).unwrap();
+            } else if let Some(el) = after.dyn_ref::<web_sys::CharacterData>() {
+                el.before_with_node(&arr).unwrap();
+            } else if let Some(el) = after.dyn_ref::<web_sys::DocumentType>() {
+                el.before_with_node(&arr).unwrap();
+            }
         }
     }
 }
@@ -447,46 +459,50 @@ fn virtual_event_from_websys_event(event: &web_sys::Event) -> VirtualEvent {
         "keydown" | "keypress" | "keyup" => {
             struct Event(web_sys::KeyboardEvent);
             impl KeyboardEventInner for Event {
+                fn alt_key(&self) -> bool {
+                    self.0.alt_key()
+                }
                 fn char_code(&self) -> u32 {
-                    todo!()
+                    self.0.char_code()
                 }
+                fn key(&self) -> String {
+                    self.0.key()
+                }
+
                 fn key_code(&self) -> KeyCode {
-                    todo!()
+                    KeyCode::from_raw_code(self.0.key_code() as u8)
                 }
+
                 fn ctrl_key(&self) -> bool {
-                    todo!()
+                    self.0.ctrl_key()
                 }
 
-                fn key(&self) -> String {
-                    todo!()
+                fn get_modifier_state(&self, key_code: &str) -> bool {
+                    self.0.get_modifier_state(key_code)
                 }
 
                 fn locale(&self) -> String {
-                    todo!()
+                    todo!("Locale is currently not supported. :(")
                 }
 
                 fn location(&self) -> usize {
-                    todo!()
+                    self.0.location() as usize
                 }
 
                 fn meta_key(&self) -> bool {
-                    todo!()
+                    self.0.meta_key()
                 }
 
                 fn repeat(&self) -> bool {
-                    todo!()
+                    self.0.repeat()
                 }
 
                 fn shift_key(&self) -> bool {
-                    todo!()
+                    self.0.shift_key()
                 }
 
                 fn which(&self) -> usize {
-                    todo!()
-                }
-
-                fn get_modifier_state(&self, key_code: usize) -> bool {
-                    todo!()
+                    self.0.which() as usize
                 }
             }
             let evt: web_sys::KeyboardEvent = event.clone().dyn_into().unwrap();

+ 1 - 1
packages/web/src/lib.rs

@@ -137,7 +137,7 @@ pub async fn run_with_props<T: Properties + 'static>(
 
     let mut websys_dom = dom::WebsysDom::new(root_el, cfg, sender_callback);
 
-    let mut mutations = dom.rebuild().unwrap();
+    let mut mutations = dom.rebuild();
     log::info!("Mutations: {:#?}", mutations);
 
     // hydrating is simply running the dom for a single render. If the page is already written, then the corresponding