diff --git a/.docker/Dockerfile_pre_test b/.docker/Dockerfile_pre_test
new file mode 100644
index 000000000..380bb5773
--- /dev/null
+++ b/.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"]
diff --git a/.docker/Dockerfile_test b/.docker/Dockerfile_test
new file mode 100644
index 000000000..f6265c2bb
--- /dev/null
+++ b/.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"]
diff --git a/.docker/README.md b/.docker/README.md
new file mode 100644
index 000000000..57578f405
--- /dev/null
+++ b/.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
\ No newline at end of file
diff --git a/.docker/run_local_tests.sh b/.docker/run_local_tests.sh
new file mode 100644
index 000000000..4684625d4
--- /dev/null
+++ b/.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"
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index b941960b7..244313ef9 100644
--- a/.github/workflows/main.yml
+++ b/.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
diff --git a/.vscode/settings.json b/.vscode/settings.json
index ac2195ccd..44bcb08e3 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -4,4 +4,8 @@
"desktop",
"router"
],
+ "editor.formatOnSave": true,
+ "[toml]": {
+ "editor.formatOnSave": false
+ }
}
diff --git a/Makefile.toml b/Makefile.toml
new file mode 100644
index 000000000..17c32fd15
--- /dev/null
+++ b/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
diff --git a/README.md b/README.md
index 52493b119..cc39ddf82 100644
--- a/README.md
+++ b/README.md
@@ -26,9 +26,7 @@
-
-
diff --git a/examples/svg.rs b/examples/svg.rs
new file mode 100644
index 000000000..6e5676fe3
--- /dev/null
+++ b/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
+ }
+ )
+}
diff --git a/examples/todomvc.rs b/examples/todomvc.rs
index e5f7d1dfa..937d6992c 100644
--- a/examples/todomvc.rs
+++ b/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 {
diff --git a/packages/core/src/diff.rs b/packages/core/src/diff.rs
index 3f3629c10..d7189a710 100644
--- a/packages/core/src/diff.rs
+++ b/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 = vcomponent.props.borrow_mut().take().unwrap();
- let props: Box = 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 = vcomponent.props.borrow_mut().take().unwrap();
+ let props: Box = 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 {
- 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
}
}
diff --git a/packages/desktop/examples/async.rs b/packages/desktop/examples/async.rs
deleted file mode 100644
index 064ef1960..000000000
--- a/packages/desktop/examples/async.rs
+++ /dev/null
@@ -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!"
- }
- }
- })
-}
diff --git a/packages/html/src/elements.rs b/packages/html/src/elements.rs
index 1a2f36e57..c6b1cb7e0 100644
--- a/packages/html/src/elements.rs
+++ b/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
diff --git a/packages/html/src/global_attributes.rs b/packages/html/src/global_attributes.rs
index 1368f87e3..151a1539b 100644
--- a/packages/html/src/global_attributes.rs
+++ b/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:
+ ///
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:
+ ///
+ 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",
diff --git a/packages/interpreter/Cargo.toml b/packages/interpreter/Cargo.toml
index fe150e1f9..1e0f4bb51 100644
--- a/packages/interpreter/Cargo.toml
+++ b/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"]
diff --git a/packages/interpreter/build.rs b/packages/interpreter/build.rs
deleted file mode 100644
index 610e3c3c3..000000000
--- a/packages/interpreter/build.rs
+++ /dev/null
@@ -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 :(");
- }
- }
- }
-}
diff --git a/packages/interpreter/gen/interpreter.js b/packages/interpreter/gen/interpreter.js
deleted file mode 100644
index 429b5c95a..000000000
--- a/packages/interpreter/gen/interpreter.js
+++ /dev/null
@@ -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,
-};
diff --git a/packages/web/build.rs b/packages/interpreter/src/bindings.rs
similarity index 77%
rename from packages/web/build.rs
rename to packages/interpreter/src/bindings.rs
index 3c9baa7c8..fa96e3230 100644
--- a/packages/web/build.rs
+++ b/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}};
+use web_sys::{Element, Node};
-/*
-This is an autogenerated file from build.rs.
-Do not edit this file directly.
-*/
-
-#[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();
}
diff --git a/packages/interpreter/src/interpreter.ts b/packages/interpreter/src/interpreter.js
similarity index 71%
rename from packages/interpreter/src/interpreter.ts
rename to packages/interpreter/src/interpreter.js
index 41036905b..4b2f3b701 100644
--- a/packages/interpreter/src/interpreter.ts
+++ b/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;
diff --git a/packages/interpreter/src/lib.rs b/packages/interpreter/src/lib.rs
index 7d680ae25..ebca3cbf4 100644
--- a/packages/interpreter/src/lib.rs
+++ b/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;
diff --git a/packages/interpreter/tsconfig.json b/packages/interpreter/tsconfig.json
deleted file mode 100644
index 1c1ed1ef9..000000000
--- a/packages/interpreter/tsconfig.json
+++ /dev/null
@@ -1,15 +0,0 @@
-{
- "compilerOptions": {
- "target": "ESNext",
- "module": "ES2015",
- "lib": [
- "es2015",
- "es5",
- "es6",
- "dom"
- ],
- "rootDir": "src",
- "strict": true,
- "outDir": "gen",
- }
-}
diff --git a/packages/mobile/Cargo.toml b/packages/mobile/Cargo.toml
index 9da440343..cdf0a5c05 100644
--- a/packages/mobile/Cargo.toml
+++ b/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
diff --git a/packages/mobile/Makefile.toml b/packages/mobile/Makefile.toml
new file mode 100644
index 000000000..90f08a170
--- /dev/null
+++ b/packages/mobile/Makefile.toml
@@ -0,0 +1,7 @@
+[tasks.test]
+command = "cargo"
+args = [
+ "test",
+ "--no-run",
+]
+
diff --git a/packages/router/Cargo.toml b/packages/router/Cargo.toml
index 1c6ed24d2..239a3c5d8 100644
--- a/packages/router/Cargo.toml
+++ b/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",
+]
diff --git a/packages/router/Makefile.toml b/packages/router/Makefile.toml
new file mode 100644
index 000000000..42b7f5c9a
--- /dev/null
+++ b/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}",
+]
\ No newline at end of file
diff --git a/packages/router/tests/route.rs b/packages/router/tests/route.rs
new file mode 100644
index 000000000..89b7a64fa
--- /dev/null
+++ b/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::("id")?;
+
+ cx.render(rsx! {
+ div {
+
+ }
+ })
+ }
+
+ main();
+
+ let element = gloo_utils::document();
+}
diff --git a/packages/router/webdriver.json b/packages/router/webdriver.json
new file mode 100644
index 000000000..81413f24b
--- /dev/null
+++ b/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": []
+ }
+ }
+
\ No newline at end of file
diff --git a/packages/web/Cargo.toml b/packages/web/Cargo.toml
index 2f6f239df..57fc797df 100644
--- a/packages/web/Cargo.toml
+++ b/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" }
diff --git a/packages/web/src/bindings.rs b/packages/web/src/bindings.rs
deleted file mode 100644
index 356f5b8eb..000000000
--- a/packages/web/src/bindings.rs
+++ /dev/null
@@ -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);
-}
diff --git a/packages/web/src/dom.rs b/packages/web/src/dom.rs
index efa21ebbf..555e04e43 100644
--- a/packages/web/src/dom.rs
+++ b/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};
diff --git a/packages/web/src/lib.rs b/packages/web/src/lib.rs
index f4266a7c8..cef1bc14c 100644
--- a/packages/web/src/lib.rs
+++ b/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;