Browse Source

Merge branch 'master' into jk/form-ma

Jonathan Kelley 3 years ago
parent
commit
169f1dac11

+ 20 - 0
.docker/Dockerfile_pre_test

@@ -0,0 +1,20 @@
+FROM rust:1.58-buster
+
+RUN apt update
+RUN apt install -y \
+    libglib2.0-dev \
+    libgtk-3-dev \
+    libsoup2.4-dev \
+    libappindicator3-dev \
+    libwebkit2gtk-4.0-dev \
+    firefox-esr
+# for kcov and Tarpaulin
+#liblzma-dev binutils-dev libcurl4-openssl-dev libdw-dev libelf-dev
+
+RUN cargo install cargo-make --debug
+# for test coverage
+#RUN cargo install cargo-tarpaulin
+# clean up a bit
+RUN cargo install cargo-cache && cargo cache -a
+
+CMD ["exit"]

+ 8 - 0
.docker/Dockerfile_test

@@ -0,0 +1,8 @@
+FROM dioxus-base-test-image
+
+RUN mkdir run_test
+COPY tmp /run_test
+WORKDIR /run_test
+RUN cargo make tests
+
+CMD ["exit"]

+ 27 - 0
.docker/README.md

@@ -0,0 +1,27 @@
+# Why this?
+
+This part is used to test whole package before pushing it
+
+# How to use it?
+
+Just run in the folder:
+`bash run_local_tests.sh`. If nothing fails, then you can push your code to the repo.
+or run:
+`bash run_local_tests.sh --with-full-docker-cleanup`
+for cleaning up images as well
+
+# How is it composed of?
+
+  1. `Dockerfile_pre_test` will build the base image for the tests to be run into
+  2. `Dockerfile_test` will run the actual tests based on 1.
+  3. `run_local_tests.sh` to wrap this up
+
+# Warning
+
+The task requires some amount of CPU work and disk space (5GB per tests). Some clean up is included in the script.
+
+# Requirements
+
+ * [docker](https://docs.docker.com/engine/install/)
+ * bash
+ * rsync

+ 37 - 0
.docker/run_local_tests.sh

@@ -0,0 +1,37 @@
+set -eux
+
+echo "Test script started"
+
+function run_script {
+    if [[ -d tmp ]]
+    then
+        rm -rf tmp
+    fi
+    mkdir tmp
+    # copy files first
+    rsync -a --progress ../ tmp --exclude target --exclude docker
+
+    # build base image
+    docker build -f Dockerfile_pre_test -t dioxus-base-test-image .
+    # run test
+    docker build -f Dockerfile_test -t dioxus-test-image .
+
+    # clean up
+    rm -rf tmp
+    if [ $# -ge 1 ]
+    then
+        echo "Got some parameter"
+        if [ $1 = "--with-full-docker-cleanup" ]
+        then
+        docker image rm dioxus-base-test-image
+        docker image rm dioxus-test-image
+        fi
+    fi
+}
+
+run_script || echo "Error occured.. cleaning a bit." && \
+    docker system prune -f;
+
+docker system prune -f
+
+echo "Script finished to execute"

+ 5 - 2
.github/workflows/main.yml

@@ -33,10 +33,13 @@ jobs:
       - uses: Swatinem/rust-cache@v1
       - run: sudo apt-get update
       - run: sudo apt install libwebkit2gtk-4.0-dev libappindicator3-dev libgtk-3-dev
+      - uses: davidB/rust-cargo-make@v1
+      - uses: browser-actions/setup-firefox@latest
+      - uses: jetli/wasm-pack-action@v0.3.0
       - uses: actions-rs/cargo@v1
         with:
-          command: test
-          args: --features "desktop, ssr, router"
+          command: make
+          args: tests
 
   fmt:
     name: Rustfmt

+ 4 - 0
.vscode/settings.json

@@ -4,4 +4,8 @@
     "desktop",
     "router"
   ],
+  "editor.formatOnSave": true,
+  "[toml]": {
+    "editor.formatOnSave": false
+  }
 }

+ 51 - 0
Makefile.toml

@@ -0,0 +1,51 @@
+[config]
+default_to_workspace = false
+min_version = "0.32.4"
+
+[env]
+CARGO_MAKE_CLIPPY_ARGS = "-- --deny=warnings"
+CARGO_MAKE_EXTEND_WORKSPACE_MAKEFILE = true
+
+[config.modify_core_tasks]
+namespace = "core"
+private = true
+
+[tasks.tests-setup]
+private = true
+script = [
+  """
+    test_flags = array --headless --firefox
+    dioxus_test_features = set wasm_test
+    dioxus_test_flags = array_join ${test_flags} " "
+    echo "running tests with flags: ${dioxus_test_flags} and features: ${dioxus_test_features}"
+    set_env DIOXUS_TEST_FLAGS ${dioxus_test_flags}
+    set_env DIOXUS_TEST_FEATURES ${dioxus_test_features}
+    """,
+]
+script_runner = "@duckscript"
+
+[tasks.tests]
+category = "Testing"
+dependencies = ["tests-setup"]
+description = "Run all tests"
+env = {CARGO_MAKE_WORKSPACE_SKIP_MEMBERS = ["**/examples/*"]}
+run_task = {name = ["test-flow", "test-with-browser"], fork = true}
+
+[tasks.build]
+command = "cargo"
+args = ["build"]
+
+[tasks.test-flow]
+dependencies = ["test"]
+private = true
+
+[tasks.test]
+dependencies = ["build"]
+command = "cargo"
+args = ["test", "--all-targets", "--workspace", "--exclude", "dioxus-router"]
+private = true
+
+[tasks.test-with-browser]
+env = { CARGO_MAKE_WORKSPACE_INCLUDE_MEMBERS = ["**/packages/router"] }
+private = true
+workspace = true

+ 0 - 2
README.md

@@ -26,9 +26,7 @@
     <img src="https://github.com/dioxuslabs/dioxus/actions/workflows/main.yml/badge.svg"
       alt="CI status" />
   </a>
-</div>
 
-<div align="center">
   <!--Awesome -->
   <a href="https://github.com/dioxuslabs/awesome-dioxus">
     <img src="https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg" alt="Awesome Page" />

+ 101 - 0
examples/svg.rs

@@ -0,0 +1,101 @@
+// Thanks to @japsu and their project https://github.com/japsu/jatsi for the example!
+
+use dioxus::{events::MouseEvent, prelude::*};
+
+fn main() {
+    dioxus::desktop::launch(app);
+}
+
+fn app(cx: Scope) -> Element {
+    let (val, set_val) = use_state(&cx, || 5);
+
+    cx.render(rsx! {
+        div {
+            user_select: "none",
+            webkit_user_select: "none",
+            margin_left: "10%",
+            margin_right: "10%",
+            h1 { "Click die to generate a new value" }
+            div {
+                cursor: "pointer",
+                height: "80%",
+                width: "80%",
+                Die {
+                    value: *val,
+                    keep: true,
+                    onclick: move |_| {
+                        use rand::Rng;
+                        let mut rng = rand::thread_rng();
+                        set_val(rng.gen_range(1..6));
+                    }
+                }
+            }
+        }
+    })
+}
+
+#[derive(Props)]
+pub struct DieProps<'a> {
+    pub value: u64,
+    pub keep: bool,
+    pub onclick: EventHandler<'a, MouseEvent>,
+}
+
+const DOTS: [(i64, i64); 7] = [(-1, -1), (-1, -0), (-1, 1), (1, -1), (1, 0), (1, 1), (0, 0)];
+const DOTS_FOR_VALUE: [[bool; 7]; 6] = [
+    [false, false, false, false, false, false, true],
+    [false, false, true, true, false, false, false],
+    [false, false, true, true, false, false, true],
+    [true, false, true, true, false, true, false],
+    [true, false, true, true, false, true, true],
+    [true, true, true, true, true, true, false],
+];
+
+const OFFSET: i64 = 600;
+const DOT_RADIUS: &str = "200";
+const HELD_COLOR: &str = "#aaa";
+const UNHELD_COLOR: &str = "#ddd";
+
+// A six-sided die (D6) with dots.
+#[allow(non_snake_case)]
+pub fn Die<'a>(cx: Scope<'a, DieProps<'a>>) -> Element {
+    let &DieProps { value, keep, .. } = cx.props;
+
+    let active_dots = &DOTS_FOR_VALUE[(value - 1) as usize];
+    let fill = if keep { HELD_COLOR } else { UNHELD_COLOR };
+    let dots = DOTS
+        .iter()
+        .zip(active_dots.iter())
+        .filter(|(_, &active)| active)
+        .map(|((x, y), _)| {
+            let dcx = x * OFFSET;
+            let dcy = y * OFFSET;
+            rsx!(circle {
+                cx: "{dcx}",
+                cy: "{dcy}",
+                r: "{DOT_RADIUS}",
+                fill: "#333"
+            })
+        });
+
+    rsx!(cx,
+      svg {
+        onclick: move |e| cx.props.onclick.call(e),
+        prevent_default: "onclick",
+        "dioxus-prevent-default": "onclick",
+        class: "die",
+        view_box: "-1000 -1000 2000 2000",
+
+        rect {
+          x: "-1000",
+          y: "-1000",
+          width: "2000",
+          height: "2000",
+          rx: "{DOT_RADIUS}",
+          fill: "{fill}",
+        }
+
+        dots
+      }
+    )
+}

+ 6 - 3
examples/todomvc.rs

@@ -120,15 +120,18 @@ pub fn todo_entry<'a>(cx: Scope<'a, TodoEntryProps<'a>>) -> Element {
 
     rsx!(cx, li {
         class: "{completed} {editing}",
-        onclick: move |_| set_is_editing(true),
-        onfocusout: move |_| set_is_editing(false),
         div { class: "view",
             input { class: "toggle", r#type: "checkbox", id: "cbg-{todo.id}", checked: "{todo.checked}",
                 onchange: move |evt| {
                     cx.props.set_todos.make_mut()[&cx.props.id].checked = evt.value.parse().unwrap();
                 }
             }
-            label { r#for: "cbg-{todo.id}", pointer_events: "none", "{todo.contents}" }
+            label {
+                r#for: "cbg-{todo.id}",
+                onclick: move |_| set_is_editing(true),
+                onfocusout: move |_| set_is_editing(false),
+                "{todo.contents}"
+            }
         }
         is_editing.then(|| rsx!{
             input {

+ 127 - 100
packages/core/src/diff.rs

@@ -119,11 +119,15 @@ impl<'b> DiffState<'b> {
 
     pub fn diff_scope(&mut self, scopeid: ScopeId) {
         let (old, new) = (self.scopes.wip_head(scopeid), self.scopes.fin_head(scopeid));
-        self.scope_stack.push(scopeid);
         let scope = self.scopes.get_scope(scopeid).unwrap();
-        self.element_stack.push(scope.container);
 
-        self.diff_node(old, new);
+        self.scope_stack.push(scopeid);
+        self.element_stack.push(scope.container);
+        {
+            self.diff_node(old, new);
+        }
+        self.element_stack.pop();
+        self.scope_stack.pop();
 
         self.mutations.mark_dirty_scope(scopeid);
     }
@@ -131,63 +135,26 @@ impl<'b> DiffState<'b> {
     pub fn diff_node(&mut self, old_node: &'b VNode<'b>, new_node: &'b VNode<'b>) {
         use VNode::{Component, Element, Fragment, Placeholder, Text};
         match (old_node, new_node) {
-            // Check the most common cases first
-            // these are *actual* elements, not wrappers around lists
             (Text(old), Text(new)) => {
-                if std::ptr::eq(old, new) {
-                    return;
-                }
-
-                let root = old
-                    .id
-                    .get()
-                    .expect("existing text nodes should have an ElementId");
-
-                if old.text != new.text {
-                    self.mutations.set_text(new.text, root.as_u64());
-                }
-                self.scopes.update_node(new_node, root);
-
-                new.id.set(Some(root));
+                self.diff_text_nodes(old, new, old_node, new_node);
             }
 
             (Placeholder(old), Placeholder(new)) => {
-                if std::ptr::eq(old, new) {
-                    return;
-                }
-
-                let root = old
-                    .id
-                    .get()
-                    .expect("existing placeholder nodes should have an ElementId");
-
-                self.scopes.update_node(new_node, root);
-                new.id.set(Some(root));
+                self.diff_placeholder_nodes(old, new, old_node, new_node);
             }
 
             (Element(old), Element(new)) => {
-                if std::ptr::eq(old, new) {
-                    return;
-                }
                 self.diff_element_nodes(old, new, old_node, new_node);
             }
 
-            // These two sets are pointers to nodes but are not actually nodes themselves
             (Component(old), Component(new)) => {
-                if std::ptr::eq(old, new) {
-                    return;
-                }
                 self.diff_component_nodes(old_node, new_node, *old, *new);
             }
 
             (Fragment(old), Fragment(new)) => {
-                if std::ptr::eq(old, new) {
-                    return;
-                }
                 self.diff_fragment_nodes(old, new);
             }
 
-            // Anything else is just a basic replace and create
             (
                 Component(_) | Fragment(_) | Text(_) | Element(_) | Placeholder(_),
                 Component(_) | Fragment(_) | Text(_) | Element(_) | Placeholder(_),
@@ -241,9 +208,7 @@ impl<'b> DiffState<'b> {
         {
             self.mutations.create_element(tag_name, *namespace, real_id);
 
-            let cur_scope_id = self
-                .current_scope()
-                .expect("diffing should always have a scope");
+            let cur_scope_id = self.current_scope();
 
             for listener in listeners.iter() {
                 listener.mounted_node.set(Some(real_id));
@@ -268,28 +233,25 @@ impl<'b> DiffState<'b> {
     }
 
     fn create_component_node(&mut self, vcomponent: &'b VComponent<'b>) -> usize {
-        let parent_idx = self.current_scope().unwrap();
+        let parent_idx = self.current_scope();
 
-        // ensure this scope doesn't already exist if we're trying to create it
-        debug_assert!(
-            vcomponent
-                .scope
-                .get()
-                .and_then(|f| self.scopes.get_scope(f))
-                .is_none(),
-            "component scope already exists"
-        );
-
-        // Insert a new scope into our component list
-        let props: Box<dyn AnyProps + 'b> = vcomponent.props.borrow_mut().take().unwrap();
-        let props: Box<dyn AnyProps + 'static> = unsafe { std::mem::transmute(props) };
-        let new_idx = self.scopes.new_with_key(
-            vcomponent.user_fc,
-            props,
-            Some(parent_idx),
-            self.element_stack.last().copied().unwrap(),
-            0,
-        );
+        // the component might already exist - if it does, we need to reuse it
+        // this makes figure out when to drop the component more complicated
+        let new_idx = if let Some(idx) = vcomponent.scope.get() {
+            assert!(self.scopes.get_scope(idx).is_some());
+            idx
+        } else {
+            // Insert a new scope into our component list
+            let props: Box<dyn AnyProps + 'b> = vcomponent.props.borrow_mut().take().unwrap();
+            let props: Box<dyn AnyProps + 'static> = unsafe { std::mem::transmute(props) };
+            self.scopes.new_with_key(
+                vcomponent.user_fc,
+                props,
+                Some(parent_idx),
+                self.element_stack.last().copied().unwrap(),
+                0,
+            )
+        };
 
         // Actually initialize the caller's slot with the right address
         vcomponent.scope.set(Some(new_idx));
@@ -325,6 +287,53 @@ impl<'b> DiffState<'b> {
         created
     }
 
+    pub(crate) fn diff_text_nodes(
+        &mut self,
+        old: &'b VText<'b>,
+        new: &'b VText<'b>,
+        _old_node: &'b VNode<'b>,
+        new_node: &'b VNode<'b>,
+    ) {
+        if std::ptr::eq(old, new) {
+            return;
+        }
+
+        // if the node is comming back not assigned, that means it was borrowed but removed
+        let root = match old.id.get() {
+            Some(id) => id,
+            None => self.scopes.reserve_node(new_node),
+        };
+
+        if old.text != new.text {
+            self.mutations.set_text(new.text, root.as_u64());
+        }
+
+        self.scopes.update_node(new_node, root);
+
+        new.id.set(Some(root));
+    }
+
+    pub(crate) fn diff_placeholder_nodes(
+        &mut self,
+        old: &'b VPlaceholder,
+        new: &'b VPlaceholder,
+        _old_node: &'b VNode<'b>,
+        new_node: &'b VNode<'b>,
+    ) {
+        if std::ptr::eq(old, new) {
+            return;
+        }
+
+        // if the node is comming back not assigned, that means it was borrowed but removed
+        let root = match old.id.get() {
+            Some(id) => id,
+            None => self.scopes.reserve_node(new_node),
+        };
+
+        self.scopes.update_node(new_node, root);
+        new.id.set(Some(root));
+    }
+
     fn diff_element_nodes(
         &mut self,
         old: &'b VElement<'b>,
@@ -332,10 +341,15 @@ impl<'b> DiffState<'b> {
         old_node: &'b VNode<'b>,
         new_node: &'b VNode<'b>,
     ) {
-        let root = old
-            .id
-            .get()
-            .expect("existing element nodes should have an ElementId");
+        if std::ptr::eq(old, new) {
+            return;
+        }
+
+        // if the node is comming back not assigned, that means it was borrowed but removed
+        let root = match old.id.get() {
+            Some(id) => id,
+            None => self.scopes.reserve_node(new_node),
+        };
 
         // If the element type is completely different, the element needs to be re-rendered completely
         // This is an optimization React makes due to how users structure their code
@@ -385,25 +399,25 @@ impl<'b> DiffState<'b> {
         // We also need to make sure that all listeners are properly attached to the parent scope (fix_listener)
         //
         // TODO: take a more efficient path than this
-        if let Some(cur_scope_id) = self.current_scope() {
-            if old.listeners.len() == new.listeners.len() {
-                for (old_l, new_l) in old.listeners.iter().zip(new.listeners.iter()) {
-                    if old_l.event != new_l.event {
-                        self.mutations
-                            .remove_event_listener(old_l.event, root.as_u64());
-                        self.mutations.new_event_listener(new_l, cur_scope_id);
-                    }
-                    new_l.mounted_node.set(old_l.mounted_node.get());
-                }
-            } else {
-                for listener in old.listeners {
+        let cur_scope_id = self.current_scope();
+
+        if old.listeners.len() == new.listeners.len() {
+            for (old_l, new_l) in old.listeners.iter().zip(new.listeners.iter()) {
+                if old_l.event != new_l.event {
                     self.mutations
-                        .remove_event_listener(listener.event, root.as_u64());
-                }
-                for listener in new.listeners {
-                    listener.mounted_node.set(Some(root));
-                    self.mutations.new_event_listener(listener, cur_scope_id);
+                        .remove_event_listener(old_l.event, root.as_u64());
+                    self.mutations.new_event_listener(new_l, cur_scope_id);
                 }
+                new_l.mounted_node.set(old_l.mounted_node.get());
+            }
+        } else {
+            for listener in old.listeners {
+                self.mutations
+                    .remove_event_listener(listener.event, root.as_u64());
+            }
+            for listener in new.listeners {
+                listener.mounted_node.set(Some(root));
+                self.mutations.new_event_listener(listener, cur_scope_id);
             }
         }
 
@@ -492,6 +506,10 @@ impl<'b> DiffState<'b> {
     }
 
     fn diff_fragment_nodes(&mut self, old: &'b VFragment<'b>, new: &'b VFragment<'b>) {
+        if std::ptr::eq(old, new) {
+            return;
+        }
+
         // This is the case where options or direct vnodes might be used.
         // In this case, it's faster to just skip ahead to their diff
         if old.children.len() == 1 && new.children.len() == 1 {
@@ -934,12 +952,18 @@ impl<'b> DiffState<'b> {
 
                     log::trace!("Replacing component x2 {:?}", old);
 
-                    // we can only remove components if they are actively being diffed
-                    if self.scope_stack.contains(&c.originator) {
-                        log::trace!("Removing component {:?}", old);
+                    let scope = self.scopes.get_scope(scope_id).unwrap();
+                    c.scope.set(None);
+                    let props = scope.props.take().unwrap();
+                    c.props.borrow_mut().replace(props);
+                    self.scopes.try_remove(scope_id).unwrap();
 
-                        self.scopes.try_remove(scope_id).unwrap();
-                    }
+                    // // we can only remove components if they are actively being diffed
+                    // if self.scope_stack.contains(&c.originator) {
+                    //     log::trace!("Removing component {:?}", old);
+
+                    //     self.scopes.try_remove(scope_id).unwrap();
+                    // }
                 }
                 self.leave_scope();
             }
@@ -953,6 +977,7 @@ impl<'b> DiffState<'b> {
                     // this check exists because our null node will be removed but does not have an ID
                     if let Some(id) = t.id.get() {
                         self.scopes.collect_garbage(id);
+                        t.id.set(None);
 
                         if gen_muts {
                             self.mutations.remove(id.as_u64());
@@ -962,6 +987,7 @@ impl<'b> DiffState<'b> {
                 VNode::Placeholder(a) => {
                     let id = a.id.get().unwrap();
                     self.scopes.collect_garbage(id);
+                    a.id.set(None);
 
                     if gen_muts {
                         self.mutations.remove(id.as_u64());
@@ -975,6 +1001,7 @@ impl<'b> DiffState<'b> {
                     }
 
                     self.scopes.collect_garbage(id);
+                    e.id.set(None);
 
                     self.remove_nodes(e.children, false);
                 }
@@ -990,10 +1017,12 @@ impl<'b> DiffState<'b> {
                         let root = self.scopes.root_node(scope_id);
                         self.remove_nodes([root], gen_muts);
 
-                        // we can only remove this node if the originator is actively in our stackß
-                        if self.scope_stack.contains(&c.originator) {
-                            self.scopes.try_remove(scope_id).unwrap();
-                        }
+                        let scope = self.scopes.get_scope(scope_id).unwrap();
+                        c.scope.set(None);
+
+                        let props = scope.props.take().unwrap();
+                        c.props.borrow_mut().replace(props);
+                        self.scopes.try_remove(scope_id).unwrap();
                     }
                     self.leave_scope();
                 }
@@ -1026,8 +1055,8 @@ impl<'b> DiffState<'b> {
         self.mutations.insert_before(first, created as u32);
     }
 
-    fn current_scope(&self) -> Option<ScopeId> {
-        self.scope_stack.last().copied()
+    fn current_scope(&self) -> ScopeId {
+        self.scope_stack.last().copied().expect("no current scope")
     }
 
     fn enter_scope(&mut self, scope: ScopeId) {
@@ -1097,9 +1126,7 @@ impl<'b> DiffState<'b> {
                 for child in el.children.iter() {
                     num_on_stack += self.push_all_nodes(child);
                 }
-
                 self.mutations.push_root(el.id.get().unwrap());
-
                 num_on_stack + 1
             }
         }

+ 0 - 34
packages/desktop/examples/async.rs

@@ -1,34 +0,0 @@
-use dioxus::prelude::*;
-use dioxus_core as dioxus;
-use dioxus_core_macro::*;
-use dioxus_hooks::*;
-use dioxus_html as dioxus_elements;
-use std::time::Duration;
-
-fn main() {
-    dioxus_desktop::launch(app);
-}
-
-fn app(cx: Scope) -> Element {
-    let (count, set_count) = use_state(&cx, || 0);
-
-    use_future(&cx, || {
-        let set_count = set_count.clone();
-        async move {
-            loop {
-                tokio::time::sleep(Duration::from_millis(1000)).await;
-                set_count.modify(|f| f + 1)
-            }
-        }
-    });
-
-    cx.render(rsx! {
-        div {
-            h1 { "High-Five counter: {count}" }
-            button {
-                onclick: move |_| set_count(0),
-                "Click me!"
-            }
-        }
-    })
-}

+ 1 - 6
packages/html/src/elements.rs

@@ -1047,7 +1047,7 @@ impl input {
     /// - `text`
     /// - `time`
     /// - `url`
-    /// - `week`    
+    /// - `week`
     pub fn r#type<'a>(&self, cx: NodeFactory<'a>, val: Arguments) -> Attribute<'a> {
         cx.attr("type", val, None, false)
     }
@@ -1100,11 +1100,6 @@ impl label {
         cx.attr("for", val, None, false)
     }
 }
-impl a {
-    pub fn prevent_default<'a>(&self, cx: NodeFactory<'a>, val: Arguments) -> Attribute<'a> {
-        cx.attr("dioxus-prevent-default", val, None, false)
-    }
-}
 
 builder_constructors! {
     // SVG components

+ 16 - 0
packages/html/src/global_attributes.rs

@@ -49,6 +49,10 @@ macro_rules! aria_trait_methods {
 }
 
 pub trait GlobalAttributes {
+    /// Prevent the default action for this element.
+    ///
+    /// For more information, see the MDN docs:
+    /// <https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault>
     fn prevent_default<'a>(&self, cx: NodeFactory<'a>, val: Arguments) -> Attribute<'a> {
         cx.attr("dioxus-prevent-default", val, None, false)
     }
@@ -603,6 +607,11 @@ pub trait GlobalAttributes {
         /// Specifies the speed curve of the transition effect.
         transition_timing_function: "transition-timing-function",
 
+        /// The user-select CSS property controls whether the user can select text.
+        /// This doesn't have any effect on content loaded as part of a browser's user interface (its chrome), except in textboxes.
+        user_select: "user-select",
+        webkit_user_select: "-webkit-user-select",
+
         /// Sets the vertical positioning of an element relative to the current text baseline.
         vertical_align: "vertical-align",
 
@@ -685,6 +694,13 @@ pub trait GlobalAttributes {
 }
 
 pub trait SvgAttributes {
+    /// Prevent the default action for this element.
+    ///
+    /// For more information, see the MDN docs:
+    /// <https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault>
+    fn prevent_default<'a>(&self, cx: NodeFactory<'a>, val: Arguments) -> Attribute<'a> {
+        cx.attr("dioxus-prevent-default", val, None, false)
+    }
     aria_trait_methods! {
         accent_height: "accent-height",
         accumulate: "accumulate",

+ 8 - 0
packages/interpreter/Cargo.toml

@@ -14,3 +14,11 @@ keywords = ["dom", "ui", "gui", "react", "wasm"]
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
 [dependencies]
+wasm-bindgen = { version = "0.2.79", optional = true }
+js-sys = { version = "0.3.56", optional = true }
+web-sys = { version = "0.3.56", optional = true, features = ["Element", "Node"] }
+
+
+[features]
+default = []
+web = ["wasm-bindgen", "js-sys", "web-sys"]

+ 0 - 16
packages/interpreter/build.rs

@@ -1,16 +0,0 @@
-use std::process::Command;
-
-fn main() {
-    println!("cargo:rerun-if-changed=interpreter.ts");
-
-    match Command::new("tsc").spawn() {
-        Ok(_) => println!("Was spawned :)"),
-        Err(e) => {
-            if let std::io::ErrorKind::NotFound = e.kind() {
-                println!("`tsc` was not found! Not going to generate new interpreter")
-            } else {
-                println!("Some strange error occurred :(");
-            }
-        }
-    }
-}

+ 0 - 519
packages/interpreter/gen/interpreter.js

@@ -1,519 +0,0 @@
-export function main() {
-    let root = window.document.getElementById("main");
-    if (root != null) {
-        window.interpreter = new Interpreter(root);
-        window.rpc.call("initialize");
-    }
-}
-export class Interpreter {
-    root;
-    stack;
-    listeners;
-    handlers;
-    lastNodeWasText;
-    nodes;
-    constructor(root) {
-        this.root = root;
-        this.stack = [root];
-        this.listeners = {};
-        this.handlers = {};
-        this.lastNodeWasText = false;
-        this.nodes = [root];
-    }
-    top() {
-        return this.stack[this.stack.length - 1];
-    }
-    pop() {
-        return this.stack.pop();
-    }
-    PushRoot(root) {
-        const node = this.nodes[root];
-        this.stack.push(node);
-    }
-    AppendChildren(many) {
-        let root = this.stack[this.stack.length - (1 + many)];
-        let to_add = this.stack.splice(this.stack.length - many);
-        for (let i = 0; i < many; i++) {
-            root.appendChild(to_add[i]);
-        }
-    }
-    ReplaceWith(root_id, m) {
-        let root = this.nodes[root_id];
-        let els = this.stack.splice(this.stack.length - m);
-        root.replaceWith(...els);
-    }
-    InsertAfter(root, n) {
-        let old = this.nodes[root];
-        let new_nodes = this.stack.splice(this.stack.length - n);
-        old.after(...new_nodes);
-    }
-    InsertBefore(root, n) {
-        let old = this.nodes[root];
-        let new_nodes = this.stack.splice(this.stack.length - n);
-        old.before(...new_nodes);
-    }
-    Remove(root) {
-        let node = this.nodes[root];
-        if (node !== undefined) {
-            node.remove();
-        }
-    }
-    CreateTextNode(text, root) {
-        // todo: make it so the types are okay
-        const node = document.createTextNode(text);
-        this.nodes[root] = node;
-        this.stack.push(node);
-    }
-    CreateElement(tag, root) {
-        const el = document.createElement(tag);
-        // el.setAttribute("data-dioxus-id", `${root}`);
-        this.nodes[root] = el;
-        this.stack.push(el);
-    }
-    CreateElementNs(tag, root, ns) {
-        let el = document.createElementNS(ns, tag);
-        this.stack.push(el);
-        this.nodes[root] = el;
-    }
-    CreatePlaceholder(root) {
-        let el = document.createElement("pre");
-        el.hidden = true;
-        this.stack.push(el);
-        this.nodes[root] = el;
-    }
-    NewEventListener(event_name, root, handler) {
-        const element = this.nodes[root];
-        element.setAttribute("data-dioxus-id", `${root}`);
-        if (this.listeners[event_name] === undefined) {
-            this.listeners[event_name] = 0;
-            this.handlers[event_name] = handler;
-            this.root.addEventListener(event_name, handler);
-        }
-        else {
-            this.listeners[event_name]++;
-        }
-    }
-    RemoveEventListener(root, event_name) {
-        const element = this.nodes[root];
-        element.removeAttribute(`data-dioxus-id`);
-        this.listeners[event_name]--;
-        if (this.listeners[event_name] === 0) {
-            this.root.removeEventListener(event_name, this.handlers[event_name]);
-            delete this.listeners[event_name];
-            delete this.handlers[event_name];
-        }
-    }
-    SetText(root, text) {
-        this.nodes[root].textContent = text;
-    }
-    SetAttribute(root, field, value, ns) {
-        const name = field;
-        const node = this.nodes[root];
-        if (ns == "style") {
-            // @ts-ignore
-            node.style[name] = value;
-        }
-        else if (ns != null || ns != undefined) {
-            node.setAttributeNS(ns, name, value);
-        }
-        else {
-            switch (name) {
-                case "value":
-                    if (value != node.value) {
-                        node.value = value;
-                    }
-                    break;
-                case "checked":
-                    node.checked = value === "true";
-                    break;
-                case "selected":
-                    node.selected = value === "true";
-                    break;
-                case "dangerous_inner_html":
-                    node.innerHTML = value;
-                    break;
-                default:
-                    // https://github.com/facebook/react/blob/8b88ac2592c5f555f315f9440cbb665dd1e7457a/packages/react-dom/src/shared/DOMProperty.js#L352-L364
-                    if (value == "false" && bool_attrs.hasOwnProperty(name)) {
-                        node.removeAttribute(name);
-                    }
-                    else {
-                        node.setAttribute(name, value);
-                    }
-            }
-        }
-    }
-    RemoveAttribute(root, name) {
-        const node = this.nodes[root];
-        node.removeAttribute(name);
-        if (name === "value") {
-            node.value = "";
-        }
-        if (name === "checked") {
-            node.checked = false;
-        }
-        if (name === "selected") {
-            node.selected = false;
-        }
-    }
-    handleEdits(edits) {
-        this.stack.push(this.root);
-        for (let edit of edits) {
-            this.handleEdit(edit);
-        }
-    }
-    handleEdit(edit) {
-        switch (edit.type) {
-            case "PushRoot":
-                this.PushRoot(edit.root);
-                break;
-            case "AppendChildren":
-                this.AppendChildren(edit.many);
-                break;
-            case "ReplaceWith":
-                this.ReplaceWith(edit.root, edit.m);
-                break;
-            case "InsertAfter":
-                this.InsertAfter(edit.root, edit.n);
-                break;
-            case "InsertBefore":
-                this.InsertBefore(edit.root, edit.n);
-                break;
-            case "Remove":
-                this.Remove(edit.root);
-                break;
-            case "CreateTextNode":
-                this.CreateTextNode(edit.text, edit.root);
-                break;
-            case "CreateElement":
-                this.CreateElement(edit.tag, edit.root);
-                break;
-            case "CreateElementNs":
-                this.CreateElementNs(edit.tag, edit.root, edit.ns);
-                break;
-            case "CreatePlaceholder":
-                this.CreatePlaceholder(edit.root);
-                break;
-            case "RemoveEventListener":
-                this.RemoveEventListener(edit.root, edit.event_name);
-                break;
-            case "NewEventListener":
-                // this handler is only provided on desktop implementations since this
-                // method is not used by the web implementation
-                let handler = (event) => {
-                    let target = event.target;
-                    if (target != null) {
-                        let realId = target.getAttribute(`data-dioxus-id`);
-                        let shouldPreventDefault = target.getAttribute(`dioxus-prevent-default`);
-                        if (event.type == "click") {
-                            event.preventDefault();
-                            if (shouldPreventDefault !== `onclick`) {
-                                if (target.tagName == "A") {
-                                    const href = target.getAttribute("href");
-                                    if (href !== "" && href !== null && href !== undefined) {
-                                        window.rpc.call("browser_open", { href });
-                                    }
-                                }
-                            }
-                        }
-                        // walk the tree to find the real element
-                        while (realId == null) {
-                            // we've reached the root we don't want to send an event
-                            if (target.parentElement === null) {
-                                return;
-                            }
-                            target = target.parentElement;
-                            realId = target.getAttribute(`data-dioxus-id`);
-                        }
-                        shouldPreventDefault = target.getAttribute(`dioxus-prevent-default`);
-                        let contents = serialize_event(event);
-                        if (shouldPreventDefault === `on${event.type}`) {
-                            event.preventDefault();
-                        }
-                        if (event.type == "submit") {
-                            event.preventDefault();
-                        }
-                        if (target.tagName == "FORM") {
-                            let formTarget = target;
-                            for (let x = 0; x < formTarget.elements.length; x++) {
-                                let element = formTarget.elements[x];
-                                let name = element.getAttribute("name");
-                                if (name != null) {
-                                    if (element.getAttribute("type") == "checkbox") {
-                                        // @ts-ignore
-                                        contents.values[name] = element.checked ? "true" : "false";
-                                    }
-                                    else {
-                                        // @ts-ignore
-                                        contents.values[name] = element.value ?? element.textContent;
-                                    }
-                                }
-                            }
-                        }
-                        if (realId == null) {
-                            return;
-                        }
-                        window.rpc.call("user_event", {
-                            event: edit.event_name,
-                            mounted_dom_id: parseInt(realId),
-                            contents: contents,
-                        });
-                    }
-                };
-                this.NewEventListener(edit.event_name, edit.root, handler);
-                break;
-            case "SetText":
-                this.SetText(edit.root, edit.text);
-                break;
-            case "SetAttribute":
-                this.SetAttribute(edit.root, edit.field, edit.value, edit.ns);
-                break;
-            case "RemoveAttribute":
-                this.RemoveAttribute(edit.root, edit.name);
-                break;
-        }
-    }
-}
-export function serialize_event(event) {
-    switch (event.type) {
-        case "copy":
-        case "cut":
-        case "past": {
-            return {};
-        }
-        case "compositionend":
-        case "compositionstart":
-        case "compositionupdate": {
-            let { data } = event;
-            return {
-                data,
-            };
-        }
-        case "keydown":
-        case "keypress":
-        case "keyup": {
-            let { charCode, key, altKey, ctrlKey, metaKey, keyCode, shiftKey, location, repeat, which, } = event;
-            return {
-                char_code: charCode,
-                key: key,
-                alt_key: altKey,
-                ctrl_key: ctrlKey,
-                meta_key: metaKey,
-                key_code: keyCode,
-                shift_key: shiftKey,
-                location: location,
-                repeat: repeat,
-                which: which,
-                locale: "locale",
-            };
-        }
-        case "focus":
-        case "blur": {
-            return {};
-        }
-        case "change": {
-            let target = event.target;
-            let value;
-            if (target.type === "checkbox" || target.type === "radio") {
-                value = target.checked ? "true" : "false";
-            }
-            else {
-                value = target.value ?? target.textContent;
-            }
-            return {
-                value: value,
-            };
-        }
-        case "input":
-        case "invalid":
-        case "reset":
-        case "submit": {
-            let target = event.target;
-            let value = target.value ?? target.textContent;
-            if (target.type == "checkbox") {
-                value = target.checked ? "true" : "false";
-            }
-            return {
-                value: value,
-                values: {}
-            };
-        }
-        case "click":
-        case "contextmenu":
-        case "doubleclick":
-        case "drag":
-        case "dragend":
-        case "dragenter":
-        case "dragexit":
-        case "dragleave":
-        case "dragover":
-        case "dragstart":
-        case "drop":
-        case "mousedown":
-        case "mouseenter":
-        case "mouseleave":
-        case "mousemove":
-        case "mouseout":
-        case "mouseover":
-        case "mouseup": {
-            const { altKey, button, buttons, clientX, clientY, ctrlKey, metaKey, pageX, pageY, screenX, screenY, shiftKey, } = event;
-            return {
-                alt_key: altKey,
-                button: button,
-                buttons: buttons,
-                client_x: clientX,
-                client_y: clientY,
-                ctrl_key: ctrlKey,
-                meta_key: metaKey,
-                page_x: pageX,
-                page_y: pageY,
-                screen_x: screenX,
-                screen_y: screenY,
-                shift_key: shiftKey,
-            };
-        }
-        case "pointerdown":
-        case "pointermove":
-        case "pointerup":
-        case "pointercancel":
-        case "gotpointercapture":
-        case "lostpointercapture":
-        case "pointerenter":
-        case "pointerleave":
-        case "pointerover":
-        case "pointerout": {
-            const { altKey, button, buttons, clientX, clientY, ctrlKey, metaKey, pageX, pageY, screenX, screenY, shiftKey, pointerId, width, height, pressure, tangentialPressure, tiltX, tiltY, twist, pointerType, isPrimary, } = event;
-            return {
-                alt_key: altKey,
-                button: button,
-                buttons: buttons,
-                client_x: clientX,
-                client_y: clientY,
-                ctrl_key: ctrlKey,
-                meta_key: metaKey,
-                page_x: pageX,
-                page_y: pageY,
-                screen_x: screenX,
-                screen_y: screenY,
-                shift_key: shiftKey,
-                pointer_id: pointerId,
-                width: width,
-                height: height,
-                pressure: pressure,
-                tangential_pressure: tangentialPressure,
-                tilt_x: tiltX,
-                tilt_y: tiltY,
-                twist: twist,
-                pointer_type: pointerType,
-                is_primary: isPrimary,
-            };
-        }
-        case "select": {
-            return {};
-        }
-        case "touchcancel":
-        case "touchend":
-        case "touchmove":
-        case "touchstart": {
-            const { altKey, ctrlKey, metaKey, shiftKey, } = event;
-            return {
-                // changed_touches: event.changedTouches,
-                // target_touches: event.targetTouches,
-                // touches: event.touches,
-                alt_key: altKey,
-                ctrl_key: ctrlKey,
-                meta_key: metaKey,
-                shift_key: shiftKey,
-            };
-        }
-        case "scroll": {
-            return {};
-        }
-        case "wheel": {
-            const { deltaX, deltaY, deltaZ, deltaMode, } = event;
-            return {
-                delta_x: deltaX,
-                delta_y: deltaY,
-                delta_z: deltaZ,
-                delta_mode: deltaMode,
-            };
-        }
-        case "animationstart":
-        case "animationend":
-        case "animationiteration": {
-            const { animationName, elapsedTime, pseudoElement, } = event;
-            return {
-                animation_name: animationName,
-                elapsed_time: elapsedTime,
-                pseudo_element: pseudoElement,
-            };
-        }
-        case "transitionend": {
-            const { propertyName, elapsedTime, pseudoElement, } = event;
-            return {
-                property_name: propertyName,
-                elapsed_time: elapsedTime,
-                pseudo_element: pseudoElement,
-            };
-        }
-        case "abort":
-        case "canplay":
-        case "canplaythrough":
-        case "durationchange":
-        case "emptied":
-        case "encrypted":
-        case "ended":
-        case "error":
-        case "loadeddata":
-        case "loadedmetadata":
-        case "loadstart":
-        case "pause":
-        case "play":
-        case "playing":
-        case "progress":
-        case "ratechange":
-        case "seeked":
-        case "seeking":
-        case "stalled":
-        case "suspend":
-        case "timeupdate":
-        case "volumechange":
-        case "waiting": {
-            return {};
-        }
-        case "toggle": {
-            return {};
-        }
-        default: {
-            return {};
-        }
-    }
-}
-const bool_attrs = {
-    allowfullscreen: true,
-    allowpaymentrequest: true,
-    async: true,
-    autofocus: true,
-    autoplay: true,
-    checked: true,
-    controls: true,
-    default: true,
-    defer: true,
-    disabled: true,
-    formnovalidate: true,
-    hidden: true,
-    ismap: true,
-    itemscope: true,
-    loop: true,
-    multiple: true,
-    muted: true,
-    nomodule: true,
-    novalidate: true,
-    open: true,
-    playsinline: true,
-    readonly: true,
-    required: true,
-    reversed: true,
-    selected: true,
-    truespeed: true,
-};

+ 4 - 21
packages/web/build.rs → packages/interpreter/src/bindings.rs

@@ -1,18 +1,9 @@
-use std::{fs::File, io::Write};
-
-fn main() {
-    let src = format!(
-        r###"use js_sys::Function;
+use js_sys::Function;
 use wasm_bindgen::prelude::*;
-use web_sys::{{Element, Node}};
-
-/*
-This is an autogenerated file from build.rs.
-Do not edit this file directly.
-*/
+use web_sys::{Element, Node};
 
-#[wasm_bindgen(inline_js = r##"{}"##)]
-extern "C" {{
+#[wasm_bindgen(module = "/src/interpreter.js")]
+extern "C" {
     pub type Interpreter;
 
     #[wasm_bindgen(constructor)]
@@ -65,12 +56,4 @@ extern "C" {{
 
     #[wasm_bindgen(method)]
     pub fn RemoveAttribute(this: &Interpreter, root: u64, field: &str);
-}}
-"###,
-        dioxus_interpreter_js::INTERPRTER_JS
-    );
-
-    // write the bindings to a local file
-    let mut file = File::create("src/bindings.rs").unwrap();
-    file.write_all(src.as_bytes()).unwrap();
 }

+ 66 - 193
packages/interpreter/src/interpreter.ts → packages/interpreter/src/interpreter.js

@@ -5,25 +5,14 @@ export function main() {
     window.rpc.call("initialize");
   }
 }
-
-declare global {
-  interface Window {
-    interpreter: Interpreter;
-    rpc: { call: (method: string, args?: any) => void };
-  }
-}
-
-
 export class Interpreter {
-  root: Element;
-  stack: Element[];
-  listeners: { [key: string]: number };
-  handlers: { [key: string]: (evt: Event) => void };
-  lastNodeWasText: boolean;
-  nodes: Element[];
-
-
-  constructor(root: Element) {
+  root;
+  stack;
+  listeners;
+  handlers;
+  lastNodeWasText;
+  nodes;
+  constructor(root) {
     this.root = root;
     this.stack = [root];
     this.listeners = {};
@@ -31,88 +20,70 @@ export class Interpreter {
     this.lastNodeWasText = false;
     this.nodes = [root];
   }
-
   top() {
     return this.stack[this.stack.length - 1];
   }
-
   pop() {
     return this.stack.pop();
   }
-
-  PushRoot(root: number) {
+  PushRoot(root) {
     const node = this.nodes[root];
     this.stack.push(node);
   }
-
-  AppendChildren(many: number) {
+  AppendChildren(many) {
     let root = this.stack[this.stack.length - (1 + many)];
-
     let to_add = this.stack.splice(this.stack.length - many);
-
     for (let i = 0; i < many; i++) {
       root.appendChild(to_add[i]);
     }
   }
-
-  ReplaceWith(root_id: number, m: number) {
-    let root = this.nodes[root_id] as Element;
+  ReplaceWith(root_id, m) {
+    let root = this.nodes[root_id];
     let els = this.stack.splice(this.stack.length - m);
-
     root.replaceWith(...els);
   }
-
-  InsertAfter(root: number, n: number) {
-    let old = this.nodes[root] as Element;
+  InsertAfter(root, n) {
+    let old = this.nodes[root];
     let new_nodes = this.stack.splice(this.stack.length - n);
     old.after(...new_nodes);
   }
-
-  InsertBefore(root: number, n: number) {
-    let old = this.nodes[root] as Element;
+  InsertBefore(root, n) {
+    let old = this.nodes[root];
     let new_nodes = this.stack.splice(this.stack.length - n);
     old.before(...new_nodes);
   }
-
-  Remove(root: number) {
-    let node = this.nodes[root] as Element;
+  Remove(root) {
+    let node = this.nodes[root];
     if (node !== undefined) {
       node.remove();
     }
   }
-
-  CreateTextNode(text: string, root: number) {
+  CreateTextNode(text, root) {
     // todo: make it so the types are okay
-    const node = document.createTextNode(text) as any as Element;
+    const node = document.createTextNode(text);
     this.nodes[root] = node;
     this.stack.push(node);
   }
-
-  CreateElement(tag: string, root: number) {
+  CreateElement(tag, root) {
     const el = document.createElement(tag);
     // el.setAttribute("data-dioxus-id", `${root}`);
-
     this.nodes[root] = el;
     this.stack.push(el);
   }
-
-  CreateElementNs(tag: string, root: number, ns: string) {
+  CreateElementNs(tag, root, ns) {
     let el = document.createElementNS(ns, tag);
     this.stack.push(el);
     this.nodes[root] = el;
   }
-
-  CreatePlaceholder(root: number) {
+  CreatePlaceholder(root) {
     let el = document.createElement("pre");
     el.hidden = true;
     this.stack.push(el);
     this.nodes[root] = el;
   }
-
-  NewEventListener(event_name: string, root: number, handler: (evt: Event) => void) {
+  NewEventListener(event_name, root, handler) {
     const element = this.nodes[root];
     element.setAttribute("data-dioxus-id", `${root}`);
-
     if (this.listeners[event_name] === undefined) {
       this.listeners[event_name] = 0;
       this.handlers[event_name] = handler;
@@ -121,48 +92,39 @@ export class Interpreter {
       this.listeners[event_name]++;
     }
   }
-
-  RemoveEventListener(root: number, event_name: string) {
+  RemoveEventListener(root, event_name) {
     const element = this.nodes[root];
     element.removeAttribute(`data-dioxus-id`);
-
     this.listeners[event_name]--;
-
     if (this.listeners[event_name] === 0) {
       this.root.removeEventListener(event_name, this.handlers[event_name]);
       delete this.listeners[event_name];
       delete this.handlers[event_name];
     }
   }
-
-
-  SetText(root: number, text: string) {
+  SetText(root, text) {
     this.nodes[root].textContent = text;
   }
-
-  SetAttribute(root: number, field: string, value: string, ns: string | undefined) {
+  SetAttribute(root, field, value, ns) {
     const name = field;
     const node = this.nodes[root];
-
     if (ns == "style") {
-
       // @ts-ignore
-      (node as HTMLElement).style[name] = value;
-
+      node.style[name] = value;
     } else if (ns != null || ns != undefined) {
       node.setAttributeNS(ns, name, value);
     } else {
       switch (name) {
         case "value":
-          if (value != (node as HTMLInputElement).value) {
-            (node as HTMLInputElement).value = value;
+          if (value != node.value) {
+            node.value = value;
           }
           break;
         case "checked":
-          (node as HTMLInputElement).checked = value === "true";
+          node.checked = value === "true";
           break;
         case "selected":
-          (node as HTMLOptionElement).selected = value === "true";
+          node.selected = value === "true";
           break;
         case "dangerous_inner_html":
           node.innerHTML = value;
@@ -177,33 +139,26 @@ export class Interpreter {
       }
     }
   }
-  RemoveAttribute(root: number, name: string) {
-
+  RemoveAttribute(root, name) {
     const node = this.nodes[root];
     node.removeAttribute(name);
-
     if (name === "value") {
-      (node as HTMLInputElement).value = "";
+      node.value = "";
     }
-
     if (name === "checked") {
-      (node as HTMLInputElement).checked = false;
+      node.checked = false;
     }
-
     if (name === "selected") {
-      (node as HTMLOptionElement).selected = false;
+      node.selected = false;
     }
   }
-
-  handleEdits(edits: DomEdit[]) {
+  handleEdits(edits) {
     this.stack.push(this.root);
-
     for (let edit of edits) {
       this.handleEdit(edit);
     }
   }
-
-  handleEdit(edit: DomEdit) {
+  handleEdit(edit) {
     switch (edit.type) {
       case "PushRoot":
         this.PushRoot(edit.root);
@@ -239,29 +194,27 @@ export class Interpreter {
         this.RemoveEventListener(edit.root, edit.event_name);
         break;
       case "NewEventListener":
-
-
         // this handler is only provided on desktop implementations since this
         // method is not used by the web implementation
-        let handler = (event: Event) => {
-          let target = event.target as Element | null;
-
+        let handler = (event) => {
+          let target = event.target;
           if (target != null) {
             let realId = target.getAttribute(`data-dioxus-id`);
-            let shouldPreventDefault = target.getAttribute(`dioxus-prevent-default`);
+            let shouldPreventDefault = target.getAttribute(
+              `dioxus-prevent-default`
+            );
 
             if (event.type == "click") {
-              event.preventDefault();
+              // todo call prevent default if it's the right type of event
               if (shouldPreventDefault !== `onclick`) {
                 if (target.tagName == "A") {
-                  const href = target.getAttribute("href")
+                  const href = target.getAttribute("href");
                   if (href !== "" && href !== null && href !== undefined) {
                     window.rpc.call("browser_open", { href });
                   }
                 }
               }
             }
-
             // walk the tree to find the real element
             while (realId == null) {
               // we've reached the root we don't want to send an event
@@ -273,23 +226,20 @@ export class Interpreter {
               realId = target.getAttribute(`data-dioxus-id`);
             }
 
-            shouldPreventDefault = target.getAttribute(`dioxus-prevent-default`);
-
+            shouldPreventDefault = target.getAttribute(
+              `dioxus-prevent-default`
+            );
             let contents = serialize_event(event);
-
             if (shouldPreventDefault === `on${event.type}`) {
-              event.preventDefault();
+              //   event.preventDefault();
             }
-
             if (event.type == "submit") {
-              event.preventDefault();
+              //   event.preventDefault();
             }
 
-
             if (target.tagName == "FORM") {
-              let formTarget = target as HTMLFormElement;
-              for (let x = 0; x < formTarget.elements.length; x++) {
-                let element = formTarget.elements[x];
+              for (let x = 0; x < target.elements.length; x++) {
+                let element = target.elements[x];
                 let name = element.getAttribute("name");
                 if (name != null) {
                   if (element.getAttribute("type") == "checkbox") {
@@ -297,7 +247,8 @@ export class Interpreter {
                     contents.values[name] = element.checked ? "true" : "false";
                   } else {
                     // @ts-ignore
-                    contents.values[name] = element.value ?? element.textContent;
+                    contents.values[name] =
+                      element.value ?? element.textContent;
                   }
                 }
               }
@@ -306,9 +257,8 @@ export class Interpreter {
             if (realId == null) {
               return;
             }
-
             window.rpc.call("user_event", {
-              event: (edit as NewEventListener).event_name,
+              event: edit.event_name,
               mounted_dom_id: parseInt(realId),
               contents: contents,
             });
@@ -329,25 +279,21 @@ export class Interpreter {
   }
 }
 
-
-
-export function serialize_event(event: Event) {
+export function serialize_event(event) {
   switch (event.type) {
     case "copy":
     case "cut":
     case "past": {
       return {};
     }
-
     case "compositionend":
     case "compositionstart":
     case "compositionupdate": {
-      let { data } = (event as CompositionEvent);
+      let { data } = event;
       return {
         data,
       };
     }
-
     case "keydown":
     case "keypress":
     case "keyup": {
@@ -362,8 +308,7 @@ export function serialize_event(event: Event) {
         location,
         repeat,
         which,
-      } = (event as KeyboardEvent);
-
+      } = event;
       return {
         char_code: charCode,
         key: key,
@@ -378,45 +323,36 @@ export function serialize_event(event: Event) {
         locale: "locale",
       };
     }
-
     case "focus":
     case "blur": {
       return {};
     }
-
     case "change": {
-      let target = event.target as HTMLInputElement;
+      let target = event.target;
       let value;
       if (target.type === "checkbox" || target.type === "radio") {
         value = target.checked ? "true" : "false";
       } else {
         value = target.value ?? target.textContent;
       }
-
       return {
         value: value,
       };
     }
-
     case "input":
     case "invalid":
     case "reset":
     case "submit": {
-
-
-      let target = event.target as HTMLFormElement;
+      let target = event.target;
       let value = target.value ?? target.textContent;
-
       if (target.type == "checkbox") {
         value = target.checked ? "true" : "false";
       }
-
       return {
         value: value,
-        values: {}
+        values: {},
       };
     }
-
     case "click":
     case "contextmenu":
     case "doubleclick":
@@ -448,8 +384,7 @@ export function serialize_event(event: Event) {
         screenX,
         screenY,
         shiftKey,
-      } = event as MouseEvent;
-
+      } = event;
       return {
         alt_key: altKey,
         button: button,
@@ -465,7 +400,6 @@ export function serialize_event(event: Event) {
         shift_key: shiftKey,
       };
     }
-
     case "pointerdown":
     case "pointermove":
     case "pointerup":
@@ -499,7 +433,7 @@ export function serialize_event(event: Event) {
         twist,
         pointerType,
         isPrimary,
-      } = event as PointerEvent;
+      } = event;
       return {
         alt_key: altKey,
         button: button,
@@ -525,21 +459,14 @@ export function serialize_event(event: Event) {
         is_primary: isPrimary,
       };
     }
-
     case "select": {
       return {};
     }
-
     case "touchcancel":
     case "touchend":
     case "touchmove":
     case "touchstart": {
-      const {
-        altKey,
-        ctrlKey,
-        metaKey,
-        shiftKey,
-      } = event as TouchEvent;
+      const { altKey, ctrlKey, metaKey, shiftKey } = event;
       return {
         // changed_touches: event.changedTouches,
         // target_touches: event.targetTouches,
@@ -550,18 +477,11 @@ export function serialize_event(event: Event) {
         shift_key: shiftKey,
       };
     }
-
     case "scroll": {
       return {};
     }
-
     case "wheel": {
-      const {
-        deltaX,
-        deltaY,
-        deltaZ,
-        deltaMode,
-      } = event as WheelEvent;
+      const { deltaX, deltaY, deltaZ, deltaMode } = event;
       return {
         delta_x: deltaX,
         delta_y: deltaY,
@@ -569,35 +489,24 @@ export function serialize_event(event: Event) {
         delta_mode: deltaMode,
       };
     }
-
     case "animationstart":
     case "animationend":
     case "animationiteration": {
-      const {
-        animationName,
-        elapsedTime,
-        pseudoElement,
-      } = event as AnimationEvent;
+      const { animationName, elapsedTime, pseudoElement } = event;
       return {
         animation_name: animationName,
         elapsed_time: elapsedTime,
         pseudo_element: pseudoElement,
       };
     }
-
     case "transitionend": {
-      const {
-        propertyName,
-        elapsedTime,
-        pseudoElement,
-      } = event as TransitionEvent;
+      const { propertyName, elapsedTime, pseudoElement } = event;
       return {
         property_name: propertyName,
         elapsed_time: elapsedTime,
         pseudo_element: pseudoElement,
       };
     }
-
     case "abort":
     case "canplay":
     case "canplaythrough":
@@ -623,17 +532,14 @@ export function serialize_event(event: Event) {
     case "waiting": {
       return {};
     }
-
     case "toggle": {
       return {};
     }
-
     default: {
       return {};
     }
   }
 }
-
 const bool_attrs = {
   allowfullscreen: true,
   allowpaymentrequest: true,
@@ -662,36 +568,3 @@ const bool_attrs = {
   selected: true,
   truespeed: true,
 };
-
-type PushRoot = { type: "PushRoot", root: number };
-type AppendChildren = { type: "AppendChildren", many: number };
-type ReplaceWith = { type: "ReplaceWith", root: number, m: number };
-type InsertAfter = { type: "InsertAfter", root: number, n: number };
-type InsertBefore = { type: "InsertBefore", root: number, n: number };
-type Remove = { type: "Remove", root: number };
-type CreateTextNode = { type: "CreateTextNode", text: string, root: number };
-type CreateElement = { type: "CreateElement", tag: string, root: number };
-type CreateElementNs = { type: "CreateElementNs", tag: string, root: number, ns: string };
-type CreatePlaceholder = { type: "CreatePlaceholder", root: number };
-type NewEventListener = { type: "NewEventListener", root: number, event_name: string, scope: number };
-type RemoveEventListener = { type: "RemoveEventListener", event_name: string, scope: number, root: number };
-type SetText = { type: "SetText", root: number, text: string };
-type SetAttribute = { type: "SetAttribute", root: number, field: string, value: string, ns: string | undefined };
-type RemoveAttribute = { type: "RemoveAttribute", root: number, name: string };
-
-type DomEdit =
-  PushRoot |
-  AppendChildren |
-  ReplaceWith |
-  InsertAfter |
-  InsertBefore |
-  Remove |
-  CreateTextNode |
-  CreateElement |
-  CreateElementNs |
-  CreatePlaceholder |
-  NewEventListener |
-  RemoveEventListener |
-  SetText |
-  SetAttribute |
-  RemoveAttribute;

+ 7 - 1
packages/interpreter/src/lib.rs

@@ -1 +1,7 @@
-pub static INTERPRTER_JS: &str = include_str!("../gen/interpreter.js");
+pub static INTERPRTER_JS: &str = include_str!("./interpreter.js");
+
+#[cfg(feature = "web")]
+mod bindings;
+
+#[cfg(feature = "web")]
+pub use bindings::Interpreter;

+ 0 - 15
packages/interpreter/tsconfig.json

@@ -1,15 +0,0 @@
-{
-    "compilerOptions": {
-        "target": "ESNext",
-        "module": "ES2015",
-        "lib": [
-            "es2015",
-            "es5",
-            "es6",
-            "dom"
-        ],
-        "rootDir": "src",
-        "strict": true,
-        "outDir": "gen",
-    }
-}

+ 5 - 0
packages/mobile/Cargo.toml

@@ -13,3 +13,8 @@ license = "MIT/Apache-2.0"
 
 [dependencies]
 dioxus-desktop = { path = "../desktop", version = "^0.1.6" }
+
+[lib]
+doctest = false
+# tests suspended until package ready
+test = false

+ 7 - 0
packages/mobile/Makefile.toml

@@ -0,0 +1,7 @@
+[tasks.test]
+command = "cargo"
+args = [
+  "test",
+  "--no-run",
+]
+

+ 9 - 0
packages/router/Cargo.toml

@@ -43,9 +43,18 @@ web = ["web-sys", "gloo", "js-sys", "wasm-bindgen"]
 desktop = []
 mobile = []
 derive = []
+wasm_test = []
 
 [dev-dependencies]
 console_error_panic_hook = "0.1.7"
 dioxus-web = { path = "../web" }
 log = "0.4.14"
 wasm-logger = "0.2.0"
+wasm-bindgen-test = "0.3"
+gloo-utils = "0.1.2"
+
+[dev-dependencies.web-sys]
+version = "0.3"
+features = [
+    "Document",
+]

+ 10 - 0
packages/router/Makefile.toml

@@ -0,0 +1,10 @@
+[tasks.test-with-browser]
+extend = "core::wasm-pack-base"
+command = "wasm-pack"
+args = [
+  "test",
+  "@@split(DIOXUS_TEST_FLAGS, )",
+  "--",
+  "--features",
+  "${DIOXUS_TEST_FEATURES}",
+]

+ 63 - 0
packages/router/tests/route.rs

@@ -0,0 +1,63 @@
+#![cfg(target_arch = "wasm32")]
+
+use dioxus_core::prelude::*;
+use dioxus_core_macro::*;
+use dioxus_html as dioxus_elements;
+use dioxus_router::*;
+use gloo_utils::document;
+use serde::{Deserialize, Serialize};
+use wasm_bindgen_test::*;
+
+wasm_bindgen_test_configure!(run_in_browser);
+
+#[wasm_bindgen_test]
+fn simple_test() {
+    fn main() {
+        console_error_panic_hook::set_once();
+        wasm_logger::init(wasm_logger::Config::new(log::Level::Debug));
+        dioxus_web::launch(APP);
+    }
+
+    static APP: Component = |cx| {
+        cx.render(rsx! {
+            Router {
+                onchange: move |route| log::info!("route changed to {}", route),
+                Route { to: "/", Home {} }
+                Route { to: "blog"
+                    Route { to: "/", BlogList {} }
+                    Route { to: ":id", BlogPost {} }
+                }
+            }
+        })
+    };
+
+    fn Home(cx: Scope) -> Element {
+        cx.render(rsx! {
+            div {
+                h1 { "Home" }
+            }
+        })
+    }
+
+    fn BlogList(cx: Scope) -> Element {
+        cx.render(rsx! {
+            div {
+
+            }
+        })
+    }
+
+    fn BlogPost(cx: Scope) -> Element {
+        let id = use_route(&cx).segment::<usize>("id")?;
+
+        cx.render(rsx! {
+            div {
+
+            }
+        })
+    }
+
+    main();
+
+    let element = gloo_utils::document();
+}

+ 11 - 0
packages/router/webdriver.json

@@ -0,0 +1,11 @@
+{
+    "moz:firefoxOptions": {
+      "binary": "/usr/bin/firefox",
+      "prefs": {
+        "media.navigator.streams.fake": true,
+        "media.navigator.permission.disabled": true
+      },
+      "args": []
+    }
+  }
+  

+ 11 - 13
packages/web/Cargo.toml

@@ -13,27 +13,25 @@ keywords = ["dom", "ui", "gui", "react", "wasm"]
 [dependencies]
 dioxus-core = { path = "../core", version = "^0.1.9" }
 dioxus-html = { path = "../html", version = "^0.1.6" }
-js-sys = "0.3"
-wasm-bindgen = { version = "0.2.78", features = ["enable-interning"] }
+js-sys = "0.3.56"
+wasm-bindgen = { version = "0.2.79", features = ["enable-interning"] }
 lazy_static = "1.4.0"
-wasm-bindgen-futures = "0.4.20"
+wasm-bindgen-futures = "0.4.29"
 log = { version = "0.4.14", features = ["release_max_level_off"] }
 fxhash = "0.2.1"
 wasm-logger = "0.2.0"
 console_error_panic_hook = { version = "0.1.7", optional = true }
-wasm-bindgen-test = "0.3.21"
-once_cell = "1.8"
+wasm-bindgen-test = "0.3.29"
+once_cell = "1.9.0"
 async-channel = "1.6.1"
-anyhow = "1.0"
-gloo-timers = { version = "0.2.1", features = ["futures"] }
-futures-util = "0.3.15"
+anyhow = "1.0.53"
+gloo-timers = { version = "0.2.3", features = ["futures"] }
+futures-util = "0.3.19"
 smallstr = "0.2.0"
-
-[build-dependencies]
-dioxus-interpreter-js = { path = "../interpreter", version = "^0.0.0" }
+dioxus-interpreter-js = { path = "../interpreter", version = "^0.0.0", features = ["web"] }
 
 [dependencies.web-sys]
-version = "0.3.51"
+version = "0.3.56"
 features = [
     "Comment",
     "Attr",
@@ -79,5 +77,5 @@ panic_hook = ["console_error_panic_hook"]
 
 [dev-dependencies]
 dioxus-core-macro = { path = "../core-macro" }
-wasm-bindgen-test = "0.3.28"
+wasm-bindgen-test = "0.3.29"
 dioxus-ssr = { path = "../ssr" }

+ 0 - 583
packages/web/src/bindings.rs

@@ -1,583 +0,0 @@
-use js_sys::Function;
-use wasm_bindgen::prelude::*;
-use web_sys::{Element, Node};
-
-/*
-This is an autogenerated file from build.rs.
-Do not edit this file directly.
-*/
-
-#[wasm_bindgen(inline_js = r##"export function main() {
-    let root = window.document.getElementById("main");
-    if (root != null) {
-        window.interpreter = new Interpreter(root);
-        window.rpc.call("initialize");
-    }
-}
-export class Interpreter {
-    root;
-    stack;
-    listeners;
-    handlers;
-    lastNodeWasText;
-    nodes;
-    constructor(root) {
-        this.root = root;
-        this.stack = [root];
-        this.listeners = {};
-        this.handlers = {};
-        this.lastNodeWasText = false;
-        this.nodes = [root];
-    }
-    top() {
-        return this.stack[this.stack.length - 1];
-    }
-    pop() {
-        return this.stack.pop();
-    }
-    PushRoot(root) {
-        const node = this.nodes[root];
-        this.stack.push(node);
-    }
-    AppendChildren(many) {
-        let root = this.stack[this.stack.length - (1 + many)];
-        let to_add = this.stack.splice(this.stack.length - many);
-        for (let i = 0; i < many; i++) {
-            root.appendChild(to_add[i]);
-        }
-    }
-    ReplaceWith(root_id, m) {
-        let root = this.nodes[root_id];
-        let els = this.stack.splice(this.stack.length - m);
-        root.replaceWith(...els);
-    }
-    InsertAfter(root, n) {
-        let old = this.nodes[root];
-        let new_nodes = this.stack.splice(this.stack.length - n);
-        old.after(...new_nodes);
-    }
-    InsertBefore(root, n) {
-        let old = this.nodes[root];
-        let new_nodes = this.stack.splice(this.stack.length - n);
-        old.before(...new_nodes);
-    }
-    Remove(root) {
-        let node = this.nodes[root];
-        if (node !== undefined) {
-            node.remove();
-        }
-    }
-    CreateTextNode(text, root) {
-        // todo: make it so the types are okay
-        const node = document.createTextNode(text);
-        this.nodes[root] = node;
-        this.stack.push(node);
-    }
-    CreateElement(tag, root) {
-        const el = document.createElement(tag);
-        // el.setAttribute("data-dioxus-id", `${root}`);
-        this.nodes[root] = el;
-        this.stack.push(el);
-    }
-    CreateElementNs(tag, root, ns) {
-        let el = document.createElementNS(ns, tag);
-        this.stack.push(el);
-        this.nodes[root] = el;
-    }
-    CreatePlaceholder(root) {
-        let el = document.createElement("pre");
-        el.hidden = true;
-        this.stack.push(el);
-        this.nodes[root] = el;
-    }
-    NewEventListener(event_name, root, handler) {
-        const element = this.nodes[root];
-        element.setAttribute("data-dioxus-id", `${root}`);
-        if (this.listeners[event_name] === undefined) {
-            this.listeners[event_name] = 0;
-            this.handlers[event_name] = handler;
-            this.root.addEventListener(event_name, handler);
-        }
-        else {
-            this.listeners[event_name]++;
-        }
-    }
-    RemoveEventListener(root, event_name) {
-        const element = this.nodes[root];
-        element.removeAttribute(`data-dioxus-id`);
-        this.listeners[event_name]--;
-        if (this.listeners[event_name] === 0) {
-            this.root.removeEventListener(event_name, this.handlers[event_name]);
-            delete this.listeners[event_name];
-            delete this.handlers[event_name];
-        }
-    }
-    SetText(root, text) {
-        this.nodes[root].textContent = text;
-    }
-    SetAttribute(root, field, value, ns) {
-        const name = field;
-        const node = this.nodes[root];
-        if (ns == "style") {
-            // @ts-ignore
-            node.style[name] = value;
-        }
-        else if (ns != null || ns != undefined) {
-            node.setAttributeNS(ns, name, value);
-        }
-        else {
-            switch (name) {
-                case "value":
-                    if (value != node.value) {
-                        node.value = value;
-                    }
-                    break;
-                case "checked":
-                    node.checked = value === "true";
-                    break;
-                case "selected":
-                    node.selected = value === "true";
-                    break;
-                case "dangerous_inner_html":
-                    node.innerHTML = value;
-                    break;
-                default:
-                    // https://github.com/facebook/react/blob/8b88ac2592c5f555f315f9440cbb665dd1e7457a/packages/react-dom/src/shared/DOMProperty.js#L352-L364
-                    if (value == "false" && bool_attrs.hasOwnProperty(name)) {
-                        node.removeAttribute(name);
-                    }
-                    else {
-                        node.setAttribute(name, value);
-                    }
-            }
-        }
-    }
-    RemoveAttribute(root, name) {
-        const node = this.nodes[root];
-        node.removeAttribute(name);
-        if (name === "value") {
-            node.value = "";
-        }
-        if (name === "checked") {
-            node.checked = false;
-        }
-        if (name === "selected") {
-            node.selected = false;
-        }
-    }
-    handleEdits(edits) {
-        this.stack.push(this.root);
-        for (let edit of edits) {
-            this.handleEdit(edit);
-        }
-    }
-    handleEdit(edit) {
-        switch (edit.type) {
-            case "PushRoot":
-                this.PushRoot(edit.root);
-                break;
-            case "AppendChildren":
-                this.AppendChildren(edit.many);
-                break;
-            case "ReplaceWith":
-                this.ReplaceWith(edit.root, edit.m);
-                break;
-            case "InsertAfter":
-                this.InsertAfter(edit.root, edit.n);
-                break;
-            case "InsertBefore":
-                this.InsertBefore(edit.root, edit.n);
-                break;
-            case "Remove":
-                this.Remove(edit.root);
-                break;
-            case "CreateTextNode":
-                this.CreateTextNode(edit.text, edit.root);
-                break;
-            case "CreateElement":
-                this.CreateElement(edit.tag, edit.root);
-                break;
-            case "CreateElementNs":
-                this.CreateElementNs(edit.tag, edit.root, edit.ns);
-                break;
-            case "CreatePlaceholder":
-                this.CreatePlaceholder(edit.root);
-                break;
-            case "RemoveEventListener":
-                this.RemoveEventListener(edit.root, edit.event_name);
-                break;
-            case "NewEventListener":
-                // this handler is only provided on desktop implementations since this
-                // method is not used by the web implementation
-                let handler = (event) => {
-                    let target = event.target;
-                    if (target != null) {
-                        let realId = target.getAttribute(`data-dioxus-id`);
-                        let shouldPreventDefault = target.getAttribute(`dioxus-prevent-default`);
-                        if (event.type == "click") {
-                            event.preventDefault();
-                            if (shouldPreventDefault !== `onclick`) {
-                                if (target.tagName == "A") {
-                                    const href = target.getAttribute("href");
-                                    if (href !== "" && href !== null && href !== undefined) {
-                                        window.rpc.call("browser_open", { href });
-                                    }
-                                }
-                            }
-                        }
-                        // walk the tree to find the real element
-                        while (realId == null) {
-                            // we've reached the root we don't want to send an event
-                            if (target.parentElement === null) {
-                                return;
-                            }
-                            target = target.parentElement;
-                            realId = target.getAttribute(`data-dioxus-id`);
-                        }
-                        shouldPreventDefault = target.getAttribute(`dioxus-prevent-default`);
-                        let contents = serialize_event(event);
-                        if (shouldPreventDefault === `on${event.type}`) {
-                            event.preventDefault();
-                        }
-                        if (event.type == "submit") {
-                            event.preventDefault();
-                        }
-                        if (target.tagName == "FORM") {
-                            let formTarget = target;
-                            for (let x = 0; x < formTarget.elements.length; x++) {
-                                let element = formTarget.elements[x];
-                                let name = element.getAttribute("name");
-                                if (name != null) {
-                                    if (element.getAttribute("type") == "checkbox") {
-                                        // @ts-ignore
-                                        contents.values[name] = element.checked ? "true" : "false";
-                                    }
-                                    else {
-                                        // @ts-ignore
-                                        contents.values[name] = element.value ?? element.textContent;
-                                    }
-                                }
-                            }
-                        }
-                        if (realId == null) {
-                            return;
-                        }
-                        window.rpc.call("user_event", {
-                            event: edit.event_name,
-                            mounted_dom_id: parseInt(realId),
-                            contents: contents,
-                        });
-                    }
-                };
-                this.NewEventListener(edit.event_name, edit.root, handler);
-                break;
-            case "SetText":
-                this.SetText(edit.root, edit.text);
-                break;
-            case "SetAttribute":
-                this.SetAttribute(edit.root, edit.field, edit.value, edit.ns);
-                break;
-            case "RemoveAttribute":
-                this.RemoveAttribute(edit.root, edit.name);
-                break;
-        }
-    }
-}
-export function serialize_event(event) {
-    switch (event.type) {
-        case "copy":
-        case "cut":
-        case "past": {
-            return {};
-        }
-        case "compositionend":
-        case "compositionstart":
-        case "compositionupdate": {
-            let { data } = event;
-            return {
-                data,
-            };
-        }
-        case "keydown":
-        case "keypress":
-        case "keyup": {
-            let { charCode, key, altKey, ctrlKey, metaKey, keyCode, shiftKey, location, repeat, which, } = event;
-            return {
-                char_code: charCode,
-                key: key,
-                alt_key: altKey,
-                ctrl_key: ctrlKey,
-                meta_key: metaKey,
-                key_code: keyCode,
-                shift_key: shiftKey,
-                location: location,
-                repeat: repeat,
-                which: which,
-                locale: "locale",
-            };
-        }
-        case "focus":
-        case "blur": {
-            return {};
-        }
-        case "change": {
-            let target = event.target;
-            let value;
-            if (target.type === "checkbox" || target.type === "radio") {
-                value = target.checked ? "true" : "false";
-            }
-            else {
-                value = target.value ?? target.textContent;
-            }
-            return {
-                value: value,
-            };
-        }
-        case "input":
-        case "invalid":
-        case "reset":
-        case "submit": {
-            let target = event.target;
-            let value = target.value ?? target.textContent;
-            if (target.type == "checkbox") {
-                value = target.checked ? "true" : "false";
-            }
-            return {
-                value: value,
-                values: {}
-            };
-        }
-        case "click":
-        case "contextmenu":
-        case "doubleclick":
-        case "drag":
-        case "dragend":
-        case "dragenter":
-        case "dragexit":
-        case "dragleave":
-        case "dragover":
-        case "dragstart":
-        case "drop":
-        case "mousedown":
-        case "mouseenter":
-        case "mouseleave":
-        case "mousemove":
-        case "mouseout":
-        case "mouseover":
-        case "mouseup": {
-            const { altKey, button, buttons, clientX, clientY, ctrlKey, metaKey, pageX, pageY, screenX, screenY, shiftKey, } = event;
-            return {
-                alt_key: altKey,
-                button: button,
-                buttons: buttons,
-                client_x: clientX,
-                client_y: clientY,
-                ctrl_key: ctrlKey,
-                meta_key: metaKey,
-                page_x: pageX,
-                page_y: pageY,
-                screen_x: screenX,
-                screen_y: screenY,
-                shift_key: shiftKey,
-            };
-        }
-        case "pointerdown":
-        case "pointermove":
-        case "pointerup":
-        case "pointercancel":
-        case "gotpointercapture":
-        case "lostpointercapture":
-        case "pointerenter":
-        case "pointerleave":
-        case "pointerover":
-        case "pointerout": {
-            const { altKey, button, buttons, clientX, clientY, ctrlKey, metaKey, pageX, pageY, screenX, screenY, shiftKey, pointerId, width, height, pressure, tangentialPressure, tiltX, tiltY, twist, pointerType, isPrimary, } = event;
-            return {
-                alt_key: altKey,
-                button: button,
-                buttons: buttons,
-                client_x: clientX,
-                client_y: clientY,
-                ctrl_key: ctrlKey,
-                meta_key: metaKey,
-                page_x: pageX,
-                page_y: pageY,
-                screen_x: screenX,
-                screen_y: screenY,
-                shift_key: shiftKey,
-                pointer_id: pointerId,
-                width: width,
-                height: height,
-                pressure: pressure,
-                tangential_pressure: tangentialPressure,
-                tilt_x: tiltX,
-                tilt_y: tiltY,
-                twist: twist,
-                pointer_type: pointerType,
-                is_primary: isPrimary,
-            };
-        }
-        case "select": {
-            return {};
-        }
-        case "touchcancel":
-        case "touchend":
-        case "touchmove":
-        case "touchstart": {
-            const { altKey, ctrlKey, metaKey, shiftKey, } = event;
-            return {
-                // changed_touches: event.changedTouches,
-                // target_touches: event.targetTouches,
-                // touches: event.touches,
-                alt_key: altKey,
-                ctrl_key: ctrlKey,
-                meta_key: metaKey,
-                shift_key: shiftKey,
-            };
-        }
-        case "scroll": {
-            return {};
-        }
-        case "wheel": {
-            const { deltaX, deltaY, deltaZ, deltaMode, } = event;
-            return {
-                delta_x: deltaX,
-                delta_y: deltaY,
-                delta_z: deltaZ,
-                delta_mode: deltaMode,
-            };
-        }
-        case "animationstart":
-        case "animationend":
-        case "animationiteration": {
-            const { animationName, elapsedTime, pseudoElement, } = event;
-            return {
-                animation_name: animationName,
-                elapsed_time: elapsedTime,
-                pseudo_element: pseudoElement,
-            };
-        }
-        case "transitionend": {
-            const { propertyName, elapsedTime, pseudoElement, } = event;
-            return {
-                property_name: propertyName,
-                elapsed_time: elapsedTime,
-                pseudo_element: pseudoElement,
-            };
-        }
-        case "abort":
-        case "canplay":
-        case "canplaythrough":
-        case "durationchange":
-        case "emptied":
-        case "encrypted":
-        case "ended":
-        case "error":
-        case "loadeddata":
-        case "loadedmetadata":
-        case "loadstart":
-        case "pause":
-        case "play":
-        case "playing":
-        case "progress":
-        case "ratechange":
-        case "seeked":
-        case "seeking":
-        case "stalled":
-        case "suspend":
-        case "timeupdate":
-        case "volumechange":
-        case "waiting": {
-            return {};
-        }
-        case "toggle": {
-            return {};
-        }
-        default: {
-            return {};
-        }
-    }
-}
-const bool_attrs = {
-    allowfullscreen: true,
-    allowpaymentrequest: true,
-    async: true,
-    autofocus: true,
-    autoplay: true,
-    checked: true,
-    controls: true,
-    default: true,
-    defer: true,
-    disabled: true,
-    formnovalidate: true,
-    hidden: true,
-    ismap: true,
-    itemscope: true,
-    loop: true,
-    multiple: true,
-    muted: true,
-    nomodule: true,
-    novalidate: true,
-    open: true,
-    playsinline: true,
-    readonly: true,
-    required: true,
-    reversed: true,
-    selected: true,
-    truespeed: true,
-};
-"##)]
-extern "C" {
-    pub type Interpreter;
-
-    #[wasm_bindgen(constructor)]
-    pub fn new(arg: Element) -> Interpreter;
-
-    #[wasm_bindgen(method)]
-    pub fn set_node(this: &Interpreter, id: usize, node: Node);
-
-    #[wasm_bindgen(method)]
-    pub fn PushRoot(this: &Interpreter, root: u64);
-
-    #[wasm_bindgen(method)]
-    pub fn AppendChildren(this: &Interpreter, many: u32);
-
-    #[wasm_bindgen(method)]
-    pub fn ReplaceWith(this: &Interpreter, root: u64, m: u32);
-
-    #[wasm_bindgen(method)]
-    pub fn InsertAfter(this: &Interpreter, root: u64, n: u32);
-
-    #[wasm_bindgen(method)]
-    pub fn InsertBefore(this: &Interpreter, root: u64, n: u32);
-
-    #[wasm_bindgen(method)]
-    pub fn Remove(this: &Interpreter, root: u64);
-
-    #[wasm_bindgen(method)]
-    pub fn CreateTextNode(this: &Interpreter, text: &str, root: u64);
-
-    #[wasm_bindgen(method)]
-    pub fn CreateElement(this: &Interpreter, tag: &str, root: u64);
-
-    #[wasm_bindgen(method)]
-    pub fn CreateElementNs(this: &Interpreter, tag: &str, root: u64, ns: &str);
-
-    #[wasm_bindgen(method)]
-    pub fn CreatePlaceholder(this: &Interpreter, root: u64);
-
-    #[wasm_bindgen(method)]
-    pub fn NewEventListener(this: &Interpreter, name: &str, root: u64, handler: &Function);
-
-    #[wasm_bindgen(method)]
-    pub fn RemoveEventListener(this: &Interpreter, root: u64, name: &str);
-
-    #[wasm_bindgen(method)]
-    pub fn SetText(this: &Interpreter, root: u64, text: &str);
-
-    #[wasm_bindgen(method)]
-    pub fn SetAttribute(this: &Interpreter, root: u64, field: &str, value: &str, ns: Option<&str>);
-
-    #[wasm_bindgen(method)]
-    pub fn RemoveAttribute(this: &Interpreter, root: u64, field: &str);
-}

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

@@ -7,8 +7,8 @@
 //! - tests to ensure dyn_into works for various event types.
 //! - Partial delegation?>
 
-use crate::bindings::Interpreter;
 use dioxus_core::{DomEdit, ElementId, SchedulerMsg, UserEvent};
+use dioxus_interpreter_js::Interpreter;
 use js_sys::Function;
 use std::{any::Any, rc::Rc, sync::Arc};
 use wasm_bindgen::{closure::Closure, JsCast};

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

@@ -61,7 +61,6 @@ pub use dioxus_core as dioxus;
 use dioxus_core::prelude::Component;
 use futures_util::FutureExt;
 
-pub(crate) mod bindings;
 mod cache;
 mod cfg;
 mod dom;