wip: docs and router

This commit is contained in:
Jonathan Kelley 2021-11-19 00:49:04 -05:00
parent 8bf57dc21d
commit a5f05d73ac
50 changed files with 1263 additions and 234 deletions

View File

@ -91,3 +91,7 @@ path = "./examples/webview.rs"
required-features = ["desktop"]
name = "tailwind"
path = "./examples/tailwind.rs"
[patch.crates-io]
wasm-bindgen = { path = "../Tinkering/wasm-bindgen" }

View File

@ -50,8 +50,8 @@ async fn ExampleLoader(cx: Context<()>) -> Vnode {
This API stores the result on the Context object, so the loaded data is taken as reference.
*/
let name: &Result<SomeStructure> = use_fetch_data("http://example.com/json", ())
.place_holder(|(cx, props)|rsx!{<div> "loading..." </div>})
.delayed_place_holder(1000, |(cx, props)|rsx!{ <div> "still loading..." </div>})
.place_holder(|cx, props|rsx!{<div> "loading..." </div>})
.delayed_place_holder(1000, |cx, props|rsx!{ <div> "still loading..." </div>})
.await;
match name {

View File

@ -21,9 +21,9 @@ fn test() -> DomTree {
}
}
static TestComponent: FC<()> = |(cx, props)|html!{<div>"Hello world"</div>};
static TestComponent: FC<()> = |cx, props|html!{<div>"Hello world"</div>};
static TestComponent: FC<()> = |(cx, props)|{
static TestComponent: FC<()> = |cx, props|{
let g = "BLAH";
html! {
<div> "Hello world" </div>
@ -31,7 +31,7 @@ static TestComponent: FC<()> = |(cx, props)|{
};
#[functional_component]
static TestComponent: FC<{ name: String }> = |(cx, props)|html! { <div> "Hello {name}" </div> };
static TestComponent: FC<{ name: String }> = |cx, props|html! { <div> "Hello {name}" </div> };
```
## Why this behavior?

View File

@ -96,7 +96,7 @@ Sometimes you want a signal to propagate across your app, either through far-awa
```rust
const TITLE: Atom<String> = || "".to_string();
const Provider: FC<()> = |(cx, props)|{
const Provider: FC<()> = |cx, props|{
let title = use_signal(&cx, &TITLE);
rsx!(cx, input { value: title })
};
@ -105,7 +105,7 @@ const Provider: FC<()> = |(cx, props)|{
If we use the `TITLE` atom in another component, we can cause updates to flow between components without calling render or diffing either component trees:
```rust
const Receiver: FC<()> = |(cx, props)|{
const Receiver: FC<()> = |cx, props|{
let title = use_signal(&cx, &TITLE);
log::info!("This will only be called once!");
rsx!(cx,
@ -132,7 +132,7 @@ Dioxus automatically understands how to use your signals when mixed with iterato
```rust
const DICT: AtomFamily<String, String> = |_| {};
const List: FC<()> = |(cx, props)|{
const List: FC<()> = |cx, props|{
let dict = use_signal(&cx, &DICT);
cx.render(rsx!(
ul {

View File

@ -83,7 +83,7 @@ fn App((cx, props): Scope<()>) -> Element {
This syntax even enables us to write a one-line component:
```rust
static App: Fc<()> = |(cx, props)| rsx!(cx, "hello world!");
static App: Fc<()> = |cx, props| rsx!(cx, "hello world!");
```
Alternatively, for match statements, we can just return the builder itself and pass it into a final, single call to `cx.render`:

View File

@ -143,7 +143,7 @@ fn App<'a>(cx: Component<'a, ()>) -> Element<'a> {
Writing `fn App((cx, props): Component<()>) -> Element {` might become tedious. Rust will also let you write functions as static closures, but these types of Components cannot have props that borrow data.
```rust
static App: Fc<()> = |(cx, props)| {
static App: Fc<()> = |cx, props| {
cx.render(rsx! {
div { "Hello, world!" }
})

View File

@ -11,7 +11,7 @@ async fn main() {
dioxus::desktop::launch(App, |c| c);
}
pub static App: FC<()> = |(cx, _)| {
pub static App: FC<()> = |cx, _| {
let count = use_state(cx, || 0);
let mut direction = use_state(cx, || 1);

View File

@ -10,7 +10,7 @@ fn main() {
dioxus::desktop::launch(APP, |cfg| cfg);
}
const APP: FC<()> = |(cx, _)| {
const APP: FC<()> = |cx, _| {
let cur_val = use_state(cx, || 0.0_f64);
let operator = use_state(cx, || None as Option<&'static str>);
let display_value = use_state(cx, || String::from(""));
@ -114,10 +114,10 @@ const APP: FC<()> = |(cx, _)| {
struct CalculatorKeyProps<'a> {
name: &'static str,
onclick: &'a dyn Fn(MouseEvent),
children: ScopeChildren<'a>,
children: Element,
}
fn CalculatorKey<'a>((cx, props): Scope<'a, CalculatorKeyProps<'a>>) -> Element {
fn CalculatorKey<'a>(cx: Context, props: &CalculatorKeyProps) -> Element {
rsx!(cx, button {
class: "calculator-key {props.name}"
onclick: {props.onclick}

View File

@ -9,7 +9,7 @@ fn main() {
println!("{}", dom);
}
pub static EXAMPLE: FC<()> = |(cx, _)| {
pub static EXAMPLE: FC<()> = |cx, _| {
let list = (0..10).map(|_f| {
rsx! {
"{_f}"

View File

@ -31,7 +31,7 @@ fn html_usage() {
// let p = rsx!(div { {f} });
}
static App2: FC<()> = |(cx, _)| cx.render(rsx!("hello world!"));
static App2: FC<()> = |cx, _| cx.render(rsx!("hello world!"));
static App: FC<()> = |cx, props| {
let name = cx.use_state(|| 0);

View File

@ -19,7 +19,7 @@ pub struct Client {
pub description: String,
}
static App: FC<()> = |(cx, _)| {
static App: FC<()> = |cx, _| {
let mut clients = use_ref(cx, || vec![] as Vec<Client>);
let mut scene = use_state(cx, || Scene::ClientsList);

View File

@ -21,7 +21,7 @@ pub struct Client {
pub description: String,
}
static App: FC<()> = |(cx, _)| {
static App: FC<()> = |cx, _| {
let mut scene = use_state(cx, || Scene::ClientsList);
let clients = use_ref(cx, || vec![] as Vec<Client>);

View File

@ -32,7 +32,7 @@ pub struct TodoItem {
}
pub type Todos = HashMap<u32, TodoItem>;
pub static App: FC<()> = |(cx, _)| {
pub static App: FC<()> = |cx, _| {
// Share our TodoList to the todos themselves
use_provide_state(cx, Todos::new);

View File

@ -1,50 +1,38 @@
use dioxus::{events::MouseEvent, prelude::*};
use fxhash::FxBuildHasher;
use std::rc::Rc;
use dioxus::prelude::*;
use rand::prelude::*;
fn main() {
dioxus::desktop::launch(App, |c| c);
dioxus::web::launch(App, |c| c);
// dioxus::desktop::launch(App, |c| c);
}
// We use a special immutable hashmap to make hashmap operations efficient
type RowList = im_rc::HashMap<usize, Rc<str>, FxBuildHasher>;
#[derive(Clone, PartialEq)]
struct Label {
key: usize,
labels: [&'static str; 3],
}
static App: FC<()> = |(cx, _props)| {
let mut items = use_state(cx, || RowList::default());
let create_rendered_rows = move |from, num| move |_| items.set(create_row_list(from, num));
let mut append_1_000_rows =
move |_| items.set(create_row_list(items.len(), 1000).union((*items).clone()));
let update_every_10th_row = move |_| {
let mut new_items = (*items).clone();
let mut small_rng = SmallRng::from_entropy();
new_items.iter_mut().step_by(10).for_each(|(_, val)| {
*val = create_new_row_label(&mut String::with_capacity(30), &mut small_rng)
impl Label {
fn new_list(num: usize) -> Vec<Self> {
let mut rng = SmallRng::from_entropy();
let mut labels = Vec::with_capacity(num);
for _ in 0..num {
labels.push(Label {
key: 0,
labels: [
ADJECTIVES.choose(&mut rng).unwrap(),
COLOURS.choose(&mut rng).unwrap(),
NOUNS.choose(&mut rng).unwrap(),
],
});
items.set(new_items);
};
let clear_rows = move |_| items.set(RowList::default());
let swap_rows = move |_| {
// this looks a bit ugly because we're using a hashmap instead of a vec
if items.len() > 998 {
let mut new_items = (*items).clone();
let a = new_items.get(&0).unwrap().clone();
*new_items.get_mut(&0).unwrap() = new_items.get(&998).unwrap().clone();
*new_items.get_mut(&998).unwrap() = a;
items.set(new_items);
}
};
labels
}
}
let rows = items.iter().map(|(key, value)| {
rsx!(Row {
key: "{key}",
row_id: *key as usize,
label: value.clone(),
})
});
static App: FC<()> = |cx, _props| {
let mut items = use_ref(cx, || vec![]);
let mut selected = use_state(cx, || None);
cx.render(rsx! {
div { class: "container"
@ -53,22 +41,49 @@ static App: FC<()> = |(cx, _props)| {
div { class: "col-md-6", h1 { "Dioxus" } }
div { class: "col-md-6"
div { class: "row"
ActionButton { name: "Create 1,000 rows", id: "run", onclick: {create_rendered_rows(0, 1_000)} }
ActionButton { name: "Create 10,000 rows", id: "runlots", onclick: {create_rendered_rows(0, 10_000)} }
ActionButton { name: "Append 1,000 rows", id: "add", onclick: {append_1_000_rows} }
ActionButton { name: "Update every 10th row", id: "update", onclick: {update_every_10th_row} }
ActionButton { name: "Clear", id: "clear", onclick: {clear_rows} }
ActionButton { name: "Swap rows", id: "swaprows", onclick: {swap_rows} }
ActionButton { name: "Create 1,000 rows", id: "run",
onclick: move || items.set(Label::new_list(1_000)),
}
ActionButton { name: "Create 10,000 rows", id: "runlots",
onclick: move || items.set(Label::new_list(10_000)),
}
ActionButton { name: "Append 1,000 rows", id: "add",
onclick: move || items.write().extend(Label::new_list(1_000)),
}
ActionButton { name: "Update every 10th row", id: "update",
onclick: move || items.write().iter_mut().step_by(10).for_each(|item| item.labels[2] = "!!!"),
}
ActionButton { name: "Clear", id: "clear",
onclick: move || items.write().clear(),
}
ActionButton { name: "Swap rows", id: "swaprows",
onclick: move || items.write().swap(0, 998),
}
}
}
}
}
table {
tbody {
{rows}
{items.read().iter().enumerate().map(|(id, item)| {
let is_in_danger = if (*selected).map(|s| s == id).unwrap_or(false) {"danger"} else {""};
rsx!(tr { class: "{is_in_danger}"
td { class:"col-md-1" }
td { class:"col-md-1", "{item.key}" }
td { class:"col-md-1", onclick: move |_| selected.set(Some(id)),
a { class: "lbl", {item.labels} }
}
td { class: "col-md-1"
a { class: "remove", onclick: move |_| { items.write().remove(id); },
span { class: "glyphicon glyphicon-remove remove" aria_hidden: "true" }
}
}
span {}
td { class: "col-md-6" }
})
})}
}
}
// span { class: "preloadicon glyphicon glyphicon-remove" aria_hidden: "true" }
}
})
};
@ -77,59 +92,17 @@ static App: FC<()> = |(cx, _props)| {
struct ActionButtonProps<'a> {
name: &'static str,
id: &'static str,
onclick: &'a dyn Fn(MouseEvent),
onclick: &'a dyn Fn(),
}
fn ActionButton<'a>((cx, props): Scope<'a, ActionButtonProps>) -> Element<'a> {
fn ActionButton(cx: Context, props: &ActionButtonProps) -> Element {
rsx!(cx, div { class: "col-sm-6 smallpad"
button { class:"btn btn-primary btn-block", r#type: "button", id: "{props.id}", onclick: {props.onclick},
button { class:"btn btn-primary btn-block", r#type: "button", id: "{props.id}", onclick: move |_| (props.onclick)(),
"{props.name}"
}
})
}
#[derive(PartialEq, Props)]
struct RowProps {
row_id: usize,
label: Rc<str>,
}
fn Row((cx, props): Scope<RowProps>) -> Element {
rsx!(cx, tr {
td { class:"col-md-1", "{props.row_id}" }
td { class:"col-md-1", onclick: move |_| { /* run onselect */ }
a { class: "lbl", "{props.label}" }
}
td { class: "col-md-1"
a { class: "remove", onclick: move |_| {/* remove */}
span { class: "glyphicon glyphicon-remove remove" aria_hidden: "true" }
}
}
td { class: "col-md-6" }
})
}
use rand::prelude::*;
fn create_new_row_label(label: &mut String, rng: &mut SmallRng) -> Rc<str> {
label.push_str(ADJECTIVES.choose(rng).unwrap());
label.push(' ');
label.push_str(COLOURS.choose(rng).unwrap());
label.push(' ');
label.push_str(NOUNS.choose(rng).unwrap());
Rc::from(label.as_ref())
}
fn create_row_list(from: usize, num: usize) -> RowList {
let mut small_rng = SmallRng::from_entropy();
let mut buf = String::with_capacity(35);
(from..num + from)
.map(|f| {
let o = (f, create_new_row_label(&mut buf, &mut small_rng));
buf.clear();
o
})
.collect::<RowList>()
}
static ADJECTIVES: &[&str] = &[
"pretty",
"large",
@ -167,3 +140,24 @@ static NOUNS: &[&str] = &[
"table", "chair", "house", "bbq", "desk", "car", "pony", "cookie", "sandwich", "burger",
"pizza", "mouse", "keyboard",
];
// #[derive(PartialEq, Props)]
// struct RowProps<'a> {
// row_id: usize,
// label: &'a Label,
// }
// fn Row(cx: Context, props: &RowProps) -> Element {
// rsx!(cx, tr {
// td { class:"col-md-1", "{props.row_id}" }
// td { class:"col-md-1", onclick: move |_| { /* run onselect */ }
// a { class: "lbl", {props.label.labels} }
// }
// td { class: "col-md-1"
// a { class: "remove", onclick: move |_| {/* remove */}
// span { class: "glyphicon glyphicon-remove remove" aria_hidden: "true" }
// }
// }
// td { class: "col-md-6" }
// })
// }

View File

@ -11,7 +11,7 @@ fn main() {
dioxus::desktop::launch(App, |c| c);
}
pub static App: FC<()> = |(cx, _)| {
pub static App: FC<()> = |cx, _| {
let state = use_state(cx, PlayerState::new);
let is_playing = state.is_playing();

View File

@ -14,7 +14,7 @@ fn main() {
dioxus_web::launch(APP, |c| c)
}
static APP: FC<()> = |(cx, _)| {
static APP: FC<()> = |cx, _| {
let mut count = use_state(cx, || 3);
let content = use_state(cx, || String::from("h1"));
let text_content = use_state(cx, || String::from("Hello, world!"));
@ -86,4 +86,4 @@ fn render_list(cx: Context, count: usize) -> Element {
rsx!(cx, ul { {items} })
}
static CHILD: FC<()> = |(cx, _)| rsx!(cx, div {"hello child"});
static CHILD: FC<()> = |cx, _| rsx!(cx, div {"hello child"});

View File

@ -28,7 +28,7 @@ pub struct Client {
pub description: String,
}
static App: FC<()> = |(cx, _)| {
static App: FC<()> = |cx, _| {
let scene = use_state(cx, || Scene::ClientsList);
let clients = use_ref(cx, || vec![] as Vec<Client>);

View File

@ -12,7 +12,7 @@ https://github.com/rustwasm/gloo
For example, resize observer would function like this:
```rust
pub static Example: FC<()> = |(cx, props)|{
pub static Example: FC<()> = |cx, props|{
let observer = use_resize_observer();
cx.render(rsx!(

View File

@ -153,13 +153,13 @@ Notice that LiveComponent receivers (the client-side interpretation of a LiveCom
The `VNodeTree` type is a very special type that allows VNodes to be created using a pluggable allocator. The html! macro creates something that looks like:
```rust
pub static Example: FC<()> = |(cx, props)|{
pub static Example: FC<()> = |cx, props|{
html! { <div> "blah" </div> }
};
// expands to...
pub static Example: FC<()> = |(cx, props)|{
pub static Example: FC<()> = |cx, props|{
// This function converts a Fn(allocator) -> DomTree closure to a VNode struct that will later be evaluated.
html_macro_to_vnodetree(move |allocator| {
let mut node0 = allocator.alloc(VElement::div);
@ -313,7 +313,7 @@ Here's how react does it:
Any "dirty" node causes an entire subtree render. Calling "setState" at the very top will cascade all the way down. This is particularly bad for this component design:
```rust
static APP: FC<()> = |(cx, props)|{
static APP: FC<()> = |cx, props|{
let title = use_context(Title);
cx.render(html!{
<div>
@ -334,7 +334,7 @@ static APP: FC<()> = |(cx, props)|{
</div>
})
};
static HEAVY_LIST: FC<()> = |(cx, props)|{
static HEAVY_LIST: FC<()> = |cx, props|{
cx.render({
{0.100.map(i => <BigElement >)}
})
@ -378,7 +378,7 @@ struct Props {
}
static Component: FC<Props> = |(cx, props)|{
static Component: FC<Props> = |cx, props|{
}
```

View File

@ -30,7 +30,7 @@ pub fn derive_typed_builder(input: proc_macro::TokenStream) -> proc_macro::Token
///
/// ## Complete Reference Guide:
/// ```
/// const Example: FC<()> = |(cx, props)|{
/// const Example: FC<()> = |cx, props|{
/// let formatting = "formatting!";
/// let formatting_tuple = ("a", "b");
/// let lazy_fmt = format_args!("lazily formatted text");

View File

@ -72,7 +72,7 @@ impl ToTokens for RsxTemplate {
// // create a lazy tree that accepts a bump allocator
// let final_tokens = quote! {
// dioxus::prelude::LazyNodes::new(move |(cx, props)|{
// dioxus::prelude::LazyNodes::new(move |cx, props|{
// let bump = &cx.bump();
// #new_toks

View File

@ -49,8 +49,7 @@ rand = { version = "0.8.4", features = ["small_rng"] }
simple_logger = "1.13.0"
dioxus-core-macro = { path = "../core-macro", version = "0.1.2" }
dioxus-hooks = { path = "../hooks" }
# async-std = { version = "1.9.0", features = ["attributes"] }
# criterion = "0.3.5"
criterion = "0.3.5"
[features]
default = []
@ -64,3 +63,11 @@ harness = false
[[bench]]
name = "jsframework"
harness = false
[[example]]
name = "rows"
path = "./examples/rows.rs"
[profile.release]
debug = true

View File

@ -19,7 +19,6 @@ use dioxus_core_macro::*;
use dioxus_html as dioxus_elements;
use rand::prelude::*;
fn main() {}
criterion_group!(mbenches, create_rows);
criterion_main!(mbenches);
@ -28,19 +27,15 @@ fn create_rows(c: &mut Criterion) {
let mut rng = SmallRng::from_entropy();
let rows = (0..10_000_usize).map(|f| {
let label = Label::new(&mut rng);
rsx! {
Row {
rsx!(Row {
row_id: f,
label: label
}
}
})
});
cx.render(rsx! {
table {
rsx!(cx, table {
tbody {
{rows}
}
}
})
};

View File

@ -0,0 +1,120 @@
#![allow(non_snake_case, non_upper_case_globals)]
//! This benchmark tests just the overhead of Dioxus itself.
//!
//! For the JS Framework Benchmark, both the framework and the browser is benchmarked together. Dioxus prepares changes
//! to be made, but the change application phase will be just as performant as the vanilla wasm_bindgen code. In essence,
//! we are measuring the overhead of Dioxus, not the performance of the "apply" phase.
//!
//! On my MBP 2019:
//! - Dioxus takes 3ms to create 1_000 rows
//! - Dioxus takes 30ms to create 10_000 rows
//!
//! As pure "overhead", these are amazing good numbers, mostly slowed down by hitting the global allocator.
//! These numbers don't represent Dioxus with the heuristic engine installed, so I assume it'll be even faster.
use criterion::{criterion_group, criterion_main, Criterion};
use dioxus_core as dioxus;
use dioxus_core::prelude::*;
use dioxus_core_macro::*;
use dioxus_html as dioxus_elements;
use rand::prelude::*;
fn main() {
static App: FC<()> = |cx, _| {
let mut rng = SmallRng::from_entropy();
let rows = (0..10_000_usize).map(|f| {
let label = Label::new(&mut rng);
rsx! {
Row {
row_id: f,
label: label
}
}
});
cx.render(rsx! {
table {
tbody {
{rows}
}
}
})
};
let mut dom = VirtualDom::new(App);
let g = dom.rebuild();
assert!(g.edits.len() > 1);
}
#[derive(PartialEq, Props)]
struct RowProps {
row_id: usize,
label: Label,
}
fn Row(cx: Context, props: &RowProps) -> Element {
let [adj, col, noun] = props.label.0;
cx.render(rsx! {
tr {
td { class:"col-md-1", "{props.row_id}" }
td { class:"col-md-1", onclick: move |_| { /* run onselect */ }
a { class: "lbl", "{adj}" "{col}" "{noun}" }
}
td { class: "col-md-1"
a { class: "remove", onclick: move |_| {/* remove */}
span { class: "glyphicon glyphicon-remove remove" aria_hidden: "true" }
}
}
td { class: "col-md-6" }
}
})
}
#[derive(PartialEq)]
struct Label([&'static str; 3]);
impl Label {
fn new(rng: &mut SmallRng) -> Self {
Label([
ADJECTIVES.choose(rng).unwrap(),
COLOURS.choose(rng).unwrap(),
NOUNS.choose(rng).unwrap(),
])
}
}
static ADJECTIVES: &[&str] = &[
"pretty",
"large",
"big",
"small",
"tall",
"short",
"long",
"handsome",
"plain",
"quaint",
"clean",
"elegant",
"easy",
"angry",
"crazy",
"helpful",
"mushy",
"odd",
"unsightly",
"adorable",
"important",
"inexpensive",
"cheap",
"expensive",
"fancy",
];
static COLOURS: &[&str] = &[
"red", "yellow", "blue", "green", "pink", "brown", "purple", "brown", "white", "black",
"orange",
];
static NOUNS: &[&str] = &[
"table", "chair", "house", "bbq", "desk", "car", "pony", "cookie", "sandwich", "burger",
"pizza", "mouse", "keyboard",
];

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 38 KiB

View File

@ -434,9 +434,16 @@ impl<'bump> DiffState<'bump> {
}
if !children.is_empty() {
self.stack.create_children(children, MountType::Append);
if children.len() == 1 {
if let VNode::Text(vtext) = children[0] {
self.mutations.set_text(vtext.text, real_id.as_u64());
return;
}
}
}
self.stack.create_children(children, MountType::Append);
}
fn create_fragment_node(&mut self, frag: &'bump VFragment<'bump>) {
self.stack.create_children(frag.children, MountType::Absorb);
@ -645,19 +652,61 @@ impl<'bump> DiffState<'bump> {
}
}
if old.children.is_empty() && !new.children.is_empty() {
match (old.children.len(), new.children.len()) {
(0, 0) => {}
(1, 1) => {
let old1 = &old.children[0];
let new1 = &new.children[0];
match (old1, new1) {
(VNode::Text(old_text), VNode::Text(new_text)) => {
if old_text.text != new_text.text {
self.mutations.set_text(new_text.text, root.as_u64());
}
}
(VNode::Text(old_text), _) => {
self.stack.element_stack.push(root);
self.stack.instructions.push(DiffInstruction::PopElement);
self.stack.create_node(new1, MountType::Append);
}
(_, VNode::Text(new_text)) => {
self.remove_nodes([old1], false);
self.mutations.set_text(new_text.text, root.as_u64());
}
_ => {
self.stack.element_stack.push(root);
self.stack.instructions.push(DiffInstruction::PopElement);
self.diff_children(old.children, new.children);
}
}
}
(0, 1) => {
if let VNode::Text(text) = &new.children[0] {
self.mutations.set_text(text.text, root.as_u64());
} else {
self.stack.element_stack.push(root);
self.stack.instructions.push(DiffInstruction::PopElement);
}
}
(0, _) => {
self.mutations.edits.push(PushRoot {
root: root.as_u64(),
});
self.stack.element_stack.push(root);
self.stack.instructions.push(DiffInstruction::PopElement);
self.stack.create_children(new.children, MountType::Append);
} else {
}
(_, 0) => {
self.remove_nodes(old.children, false);
self.mutations.set_text("", root.as_u64());
}
(_, _) => {
self.stack.element_stack.push(root);
self.stack.instructions.push(DiffInstruction::PopElement);
self.diff_children(old.children, new.children);
}
}
}
fn diff_component_nodes(
&mut self,

View File

@ -196,7 +196,7 @@ impl Scope {
let chan = self.sender.clone();
let id = self.scope_id();
Rc::new(move || {
log::debug!("set on channel an update for scope {:?}", id);
// log::debug!("set on channel an update for scope {:?}", id);
let _ = chan.unbounded_send(SchedulerMsg::Immediate(id));
})
}

View File

@ -92,15 +92,15 @@ impl ScopeArena {
let new_scope_id = ScopeId(self.scope_counter.get());
self.scope_counter.set(self.scope_counter.get() + 1);
log::debug!("new scope {:?} with parent {:?}", new_scope_id, container);
// log::debug!("new scope {:?} with parent {:?}", new_scope_id, container);
if let Some(old_scope) = self.free_scopes.borrow_mut().pop() {
let scope = unsafe { &mut *old_scope };
log::debug!(
"reusing scope {:?} as {:?}",
scope.our_arena_idx,
new_scope_id
);
// log::debug!(
// "reusing scope {:?} as {:?}",
// scope.our_arena_idx,
// new_scope_id
// );
scope.caller = caller;
scope.parent_scope = parent_scope;
@ -202,7 +202,7 @@ impl ScopeArena {
pub fn try_remove(&self, id: &ScopeId) -> Option<()> {
self.ensure_drop_safety(id);
log::debug!("removing scope {:?}", id);
// log::debug!("removing scope {:?}", id);
// Safety:
// - ensure_drop_safety ensures that no references to this scope are in use
@ -311,7 +311,7 @@ impl ScopeArena {
let scope = unsafe { &mut *self.get_scope_mut(id).expect("could not find scope") };
log::debug!("found scope, about to run: {:?}", id);
// log::debug!("found scope, about to run: {:?}", id);
// Safety:
// - We dropped the listeners, so no more &mut T can be used while these are held

View File

@ -352,7 +352,7 @@ impl VirtualDom {
let mut committed_mutations = vec![];
while !self.dirty_scopes.is_empty() {
log::debug!("working with deadline");
// log::debug!("working with deadline");
let scopes = &self.scopes;
let mut diff_state = DiffState::new(scopes);

View File

@ -7,7 +7,7 @@ fn main() {
dioxus::desktop::launch(App, |c| c)
}
static App: FC<()> = |(cx, props)| {
static App: FC<()> = |cx, props| {
let (count, set_count) = use_state(cx, || 0);
cx.render(rsx!(
@ -34,7 +34,7 @@ Window management, system trays, notifications, and other desktop-related functi
Managing windows is done by simply rendering content into a `WebviewWindow` component.
```rust
static App: FC<()> = |(cx, props)| {
static App: FC<()> = |cx, props| {
rsx!(cx, WebviewWindow { "hello world" } )
}
```
@ -46,7 +46,7 @@ Notifications also use a declarative approach. Sending a notification has never
The api has been somewhat modeled after https://github.com/mikaelbr/node-notifier
```rust
static Notifications: FC<()> = |(cx, props)| {
static Notifications: FC<()> = |cx, props| {
cx.render(rsx!(
Notification {
title: "title"
@ -78,7 +78,7 @@ static Notifications: FC<()> = |(cx, props)| {
Dioxus Desktop supports app trays, which can be built with native menu groups or with a custom window.
```rust
static Tray: FC<()> = |(cx, props)| {
static Tray: FC<()> = |cx, props| {
cx.render(rsx!(
GlobalTray {
MenuGroup {
@ -90,7 +90,7 @@ static Tray: FC<()> = |(cx, props)| {
};
// using a builder
static Tray: FC<()> = |(cx, props)| {
static Tray: FC<()> = |cx, props| {
let menu = MenuGroup::builder(cx)
.with_items([
MenuGroupItem::builder()
@ -107,7 +107,7 @@ static Tray: FC<()> = |(cx, props)| {
}
// or with a custom window
static Tray: FC<()> = |(cx, props)| {
static Tray: FC<()> = |cx, props| {
rsx!(cx, GlobalTray { div { "custom buttons here" } })
};
```
@ -116,7 +116,7 @@ static Tray: FC<()> = |(cx, props)| {
Declaring menus is convenient and cross-platform.
```rust
static Menu: FC<()> = |(cx, props)| {
static Menu: FC<()> = |cx, props| {
cx.render(rsx!(
MenuBarMajorItem { title: "File"
MenuGroup {

View File

@ -19,10 +19,9 @@
<body>
<div id="_dioxusroot">
<div id="main">
</div>
</body>
<script type="text/javascript" src="index.js">
</script>
<script type="text/javascript" src="index.js"> </script>
</html>

View File

@ -90,6 +90,8 @@ pub fn run<T: 'static + Send + Sync>(
let edit_queue = Arc::new(RwLock::new(VecDeque::new()));
let is_ready: Arc<AtomicBool> = Default::default();
let mut frame = 0;
event_loop.run(move |window_event, event_loop, control_flow| {
*control_flow = ControlFlow::Wait;
@ -145,6 +147,9 @@ pub fn run<T: 'static + Send + Sync>(
view.evaluate_script(&format!("window.interpreter.handleEdits({})", edit))
.unwrap();
}
} else {
println!("waiting for onload {:?}", frame);
frame += 1;
}
}
Event::Resumed => {}
@ -258,6 +263,7 @@ fn create_webview(
// always driven through eval
None
})
// .with_initialization_script(include_str!("./index.js"))
// Any content that that uses the `wry://` scheme will be shuttled through this handler as a "special case"
// For now, we only serve two pieces of content which get included as bytes into the final binary.
.with_custom_protocol("wry".into(), move |request| {

View File

@ -36,7 +36,7 @@ uses the same memoization on top of the use_context API.
Here's a fully-functional todo app using the use_map API:
```rust
static TodoList: FC<()> = |(cx, props)|{
static TodoList: FC<()> = |cx, props|{
let todos = use_map(cx, || HashMap::new());
let input = use_ref(|| None);

View File

@ -33,6 +33,11 @@ impl<'a, T> UseRef<'a, T> {
self.inner.value.borrow()
}
pub fn set(&self, new: T) {
*self.inner.value.borrow_mut() = new;
self.needs_update();
}
pub fn read_write(&self) -> (Ref<'_, T>, &Self) {
(self.read(), self)
}

View File

@ -35,7 +35,7 @@ use std::{
///
/// Usage:
/// ```ignore
/// const Example: FC<()> = |(cx, props)|{
/// const Example: FC<()> = |cx, props|{
/// let counter = use_state(cx, || 0);
/// let increment = |_| counter += 1;
/// let decrement = |_| counter += 1;

View File

@ -717,8 +717,7 @@ builder_constructors! {
nonce: Nonce,
src: Uri,
text: String,
r#async: Bool,
r#type: String, // TODO could be an enum
};
@ -823,7 +822,6 @@ builder_constructors! {
formnovalidate: Bool,
formtarget: Target,
name: Id,
r#type: ButtonType,
value: String,
};
@ -1064,6 +1062,23 @@ impl input {
volatile attributes
*/
impl script {
// r#async: Bool,
// r#type: String, // TODO could be an enum
pub fn r#type<'a>(&self, cx: NodeFactory<'a>, val: Arguments) -> Attribute<'a> {
cx.attr("type", val, None, false)
}
pub fn r#script<'a>(&self, cx: NodeFactory<'a>, val: Arguments) -> Attribute<'a> {
cx.attr("script", val, None, false)
}
}
impl button {
pub fn r#type<'a>(&self, cx: NodeFactory<'a>, val: Arguments) -> Attribute<'a> {
cx.attr("type", val, None, false)
}
}
impl select {
pub fn value<'a>(&self, cx: NodeFactory<'a>, val: Arguments) -> Attribute<'a> {
cx.attr("value", val, None, true)

View File

@ -37,3 +37,9 @@ default = ["web"]
web = ["web-sys"]
desktop = []
mobile = []
[dev-dependencies]
console_error_panic_hook = "0.1.7"
dioxus-web = { path = "../web" }
log = "0.4.14"
wasm-logger = "0.2.0"

40
packages/router/README.md Normal file
View File

@ -0,0 +1,40 @@
# Router hook for Dioxus apps
Dioxus-router provides a use_router hook that returns a different value depending on the route.
The router is generic over any value, however it makes sense to return a different set of VNodes
and feed them into the App's return VNodes.
Using the router should feel similar to tide's routing framework where an "address" book is assembled at the head of the app.
Here's an example of how to use the router hook:
```rust
#[derive(Clone, Routable)]
enum AppRoute {
Home,
Posts,
NotFound
}
static App: FC<()> = |cx, props| {
let route = use_router(cx, AppRoute::parse);
match route {
AppRoute::Home => rsx!(cx, Home {})
AppRoute::Posts => rsx!(cx, Posts {})
AppRoute::Notfound => rsx!(cx, Notfound {})
}
};
```
Adding links into your app:
```rust
static Leaf: FC<()> = |cx, props| {
rsx!(cx, div {
Link { to: AppRoute::Home }
})
}
```
Currently, the router is only supported in a web environment, but we plan to add 1st-party support via the context API when new renderers are available.

View File

@ -0,0 +1,45 @@
use dioxus_core::prelude::*;
use dioxus_core_macro::*;
use dioxus_html as dioxus_elements;
use dioxus_router::*;
fn main() {
console_error_panic_hook::set_once();
dioxus_web::launch(App, |c| c);
}
#[derive(Clone, Debug, PartialEq)]
enum Route {
Home,
About,
NotFound,
}
static App: FC<()> = |cx, props| {
let route = use_router(cx, Route::parse);
match route {
Route::Home => rsx!(cx, div { "Home" }),
Route::About => rsx!(cx, div { "About" }),
Route::NotFound => rsx!(cx, div { "NotFound" }),
}
};
impl ToString for Route {
fn to_string(&self) -> String {
match self {
Route::Home => "/".to_string(),
Route::About => "/about".to_string(),
Route::NotFound => "/404".to_string(),
}
}
}
impl Route {
fn parse(s: &str) -> Self {
match s {
"/" => Route::Home,
"/about" => Route::About,
_ => Route::NotFound,
}
}
}

View File

@ -11,14 +11,18 @@ use web_sys::Event;
use crate::utils::fetch_base_url;
pub trait Routable: 'static + Send + Clone + ToString + PartialEq {}
impl<T> Routable for T where T: 'static + Send + Clone + ToString + PartialEq {}
pub struct RouterService<R: Routable> {
history: RefCell<Vec<R>>,
historic_routes: RefCell<Vec<R>>,
history_service: web_sys::History,
base_ur: RefCell<Option<String>>,
}
impl<R: Routable> RouterService<R> {
fn push_route(&self, r: R) {
self.history.borrow_mut().push(r);
self.historic_routes.borrow_mut().push(r);
}
fn get_current_route(&self) -> &str {
@ -61,7 +65,7 @@ impl<R: Routable> RouterService<R> {
/// This hould only be used once per app
///
/// You can manually parse the route if you want, but the derived `parse` method on `Routable` will also work just fine
pub fn use_router<R: Routable>(cx: Context, cfg: impl FnOnce(&str) -> R) -> Option<&R> {
pub fn use_router<R: Routable>(cx: Context, parse: impl FnMut(&str) -> R) -> &R {
// for the web, attach to the history api
cx.use_hook(
|f| {
@ -71,10 +75,13 @@ pub fn use_router<R: Routable>(cx: Context, cfg: impl FnOnce(&str) -> R) -> Opti
let base_url = fetch_base_url();
let service: RouterService<R> = RouterService {
history: RefCell::new(vec![]),
historic_routes: RefCell::new(vec![]),
history_service: web_sys::window().unwrap().history().expect("no history"),
base_ur: RefCell::new(base_url),
};
// service.history_service.push_state(data, title);
cx.provide_state(service);
let regenerate = cx.schedule_update();
@ -105,30 +112,13 @@ pub struct LinkProps<R: Routable> {
children: Element,
}
pub fn Link<'a, R: Routable>(cx: Context, props: &LinkProps<R>) -> Element {
pub fn Link<R: Routable>(cx: Context, props: &LinkProps<R>) -> Element {
let service = use_router_service::<R>(cx)?;
cx.render(rsx! {
a {
href: format_args!("{}", props.to.to_path()),
href: format_args!("{}", props.to.to_string()),
onclick: move |_| service.push_route(props.to.clone()),
{&props.children},
}
})
}
pub trait Routable: Sized + Clone + 'static {
/// Converts path to an instance of the routes enum.
fn from_path(path: &str, params: &HashMap<&str, &str>) -> Option<Self>;
/// Converts the route to a string that can passed to the history API.
fn to_path(&self) -> String;
/// Lists all the available routes
fn routes() -> Vec<&'static str>;
/// The route to redirect to on 404
fn not_found_route() -> Option<Self>;
/// Match a route based on the path
fn recognize(pathname: &str) -> Option<Self>;
}

View File

@ -5,7 +5,7 @@ Render a Dioxus VirtualDOM to a string.
```rust
// Our app:
const App: FC<()> = |(cx, props)| rsx!(cx, div {"hello world!"});
const App: FC<()> = |cx, props| rsx!(cx, div {"hello world!"});
// Build the virtualdom from our app
let mut vdom = VirtualDOM::new(App);

View File

@ -110,7 +110,7 @@ pub fn render_vdom_scope(vdom: &VirtualDom, scope: ScopeId) -> Option<String> {
///
/// ## Example
/// ```ignore
/// static App: FC<()> = |(cx, props)|cx.render(rsx!(div { "hello world" }));
/// static App: FC<()> = |cx, props|cx.render(rsx!(div { "hello world" }));
/// let mut vdom = VirtualDom::new(App);
/// vdom.rebuild();
///

View File

@ -11,10 +11,17 @@ license = "MIT/Apache-2.0"
dioxus-core = { path = "../core", version = "0.1.2" }
dioxus-html = { path = "../html" }
js-sys = "0.3"
# wasm-bindgen-shared = { path = "../../../Tinkering/wasm-bindgen/crates/shared" }
# wasm-bindgen-macro-support = { path = "../../../Tinkering/wasm-bindgen/crates/macro-support" }
# wasm-bindgen = { features = [
wasm-bindgen = { version = "0.2.78", features = ["enable-interning"] }
# wasm-bindgen = { version = "0.2.78", features = ["enable-interning"] }
# wasm-bindgen = { version = "0.2.78", features = ["enable-interning"] }
lazy_static = "1.4.0"
wasm-bindgen-futures = "0.4.20"
log = "0.4.14"
log = { version = "0.4.14", features = ["release_max_level_off"] }
fxhash = "0.2.1"
wasm-logger = "0.2.0"
console_error_panic_hook = "0.1.6"
@ -24,6 +31,11 @@ async-channel = "1.6.1"
anyhow = "1.0"
gloo-timers = { version = "0.2.1", features = ["futures"] }
futures-util = "0.3.15"
smallstr = "0.2.0"
[patch.crates-io]
wasm-bindgen = { path = "../../../Tinkering/wasm-bindgen/" }
[dependencies.web-sys]
version = "0.3.51"
@ -65,6 +77,7 @@ features = [
"IdleDeadline",
]
[lib]
crate-type = ["cdylib", "rlib"]
@ -76,7 +89,12 @@ serde = { version = "1.0.126", features = ["derive"] }
reqwest = { version = "0.11", features = ["json"] }
dioxus-hooks = { path = "../hooks" }
dioxus-core-macro = { path = "../core-macro" }
# rand = { version="0.8.4", features=["small_rng"] }
rand = { version = "0.8.4", features = ["small_rng"] }
[dev-dependencies.getrandom]
version = "0.2"
features = ["js"]
# surf = { version = "2.3.1", default-features = false, features = [
# "wasm-client",
# ] }

View File

@ -0,0 +1,243 @@
use std::cell::Cell;
use dioxus::prelude::*;
use dioxus_core as dioxus;
use dioxus_core_macro::*;
use dioxus_hooks::{use_ref, use_state};
use dioxus_html as dioxus_elements;
use dioxus_web;
use gloo_timers::future::TimeoutFuture;
use rand::prelude::*;
fn main() {
console_error_panic_hook::set_once();
if cfg!(debug_assertions) {
wasm_logger::init(wasm_logger::Config::new(log::Level::Debug));
log::debug!("hello world");
}
for a in ADJECTIVES {
wasm_bindgen::intern(*a);
}
for a in COLOURS {
wasm_bindgen::intern(*a);
}
for a in NOUNS {
wasm_bindgen::intern(*a);
}
for a in [
"container",
"jumbotron",
"row",
"Dioxus",
"col-md-6",
"col-md-1",
"Create 1,000 rows",
"run",
"Create 10,000 rows",
"runlots",
"Append 1,000 rows",
"add",
"Update every 10th row",
"update",
"Clear",
"clear",
"Swap rows",
"swaprows",
"preloadicon glyphicon glyphicon-remove", //
"aria-hidden",
"onclick",
"true",
"false",
"danger",
"type",
"id",
"class",
"glyphicon glyphicon-remove remove",
"dioxus-id",
"dioxus-event-click",
"dioxus",
"click",
"1.10",
"lbl",
"remove",
"dioxus-event",
"col-sm-6 smallpad",
"btn btn-primary btn-block",
"",
" ",
] {
wasm_bindgen::intern(a);
}
for x in 0..100_000 {
wasm_bindgen::intern(&x.to_string());
}
dioxus_web::launch(App, |c| c.rootname("main"));
}
#[derive(Clone, PartialEq, Copy)]
struct Label {
key: usize,
labels: [&'static str; 3],
}
static mut Counter: Cell<usize> = Cell::new(1);
impl Label {
fn new_list(num: usize) -> Vec<Self> {
let mut rng = SmallRng::from_entropy();
let mut labels = Vec::with_capacity(num);
let offset = unsafe { Counter.get() };
unsafe { Counter.set(offset + num) };
for k in offset..(offset + num) {
labels.push(Label {
key: k,
labels: [
ADJECTIVES.choose(&mut rng).unwrap(),
COLOURS.choose(&mut rng).unwrap(),
NOUNS.choose(&mut rng).unwrap(),
],
});
}
labels
}
}
static App: FC<()> = |cx, _props| {
let mut items = use_ref(cx, || vec![]);
let mut selected = use_state(cx, || None);
cx.render(rsx! {
div { class: "container"
div { class: "jumbotron"
div { class: "row"
div { class: "col-md-6", h1 { "Dioxus" } }
div { class: "col-md-6"
div { class: "row"
ActionButton { name: "Create 1,000 rows", id: "run",
onclick: move || items.set(Label::new_list(1_000)),
}
ActionButton { name: "Create 10,000 rows", id: "runlots",
onclick: move || items.set(Label::new_list(10_000)),
}
ActionButton { name: "Append 1,000 rows", id: "add",
onclick: move || items.write().extend(Label::new_list(1_000)),
}
ActionButton { name: "Update every 10th row", id: "update",
onclick: move || items.write().iter_mut().step_by(10).for_each(|item| item.labels[2] = "!!!"),
}
ActionButton { name: "Clear", id: "clear",
onclick: move || items.write().clear(),
}
ActionButton { name: "Swap Rows", id: "swaprows",
onclick: move || items.write().swap(0, 998),
}
}
}
}
}
table { class: "table table-hover table-striped test-data"
tbody { id: "tbody"
{items.read().iter().enumerate().map(|(id, item)| {
let [adj, col, noun] = item.labels;
let is_in_danger = if (*selected).map(|s| s == id).unwrap_or(false) {"danger"} else {""};
rsx!(tr {
class: "{is_in_danger}",
key: "{id}",
td { class:"col-md-1" }
td { class:"col-md-1", "{item.key}" }
td { class:"col-md-1", onclick: move |_| selected.set(Some(id)),
a { class: "lbl", "{adj} {col} {noun}" }
}
td { class: "col-md-1"
a { class: "remove", onclick: move |_| { items.write().remove(id); },
span { class: "glyphicon glyphicon-remove remove" aria_hidden: "true" }
}
}
td { class: "col-md-6" }
})
})}
}
}
span { class: "preloadicon glyphicon glyphicon-remove" aria_hidden: "true" }
}
})
};
#[derive(Props)]
struct ActionButtonProps<'a> {
name: &'static str,
id: &'static str,
onclick: &'a dyn Fn(),
}
fn ActionButton(cx: Context, props: &ActionButtonProps) -> Element {
rsx!(cx, div { class: "col-sm-6 smallpad"
button { class:"btn btn-primary btn-block", r#type: "button", id: "{props.id}", onclick: move |_| (props.onclick)(),
"{props.name}"
}
})
}
static ADJECTIVES: &[&str] = &[
"pretty",
"large",
"big",
"small",
"tall",
"short",
"long",
"handsome",
"plain",
"quaint",
"clean",
"elegant",
"easy",
"angry",
"crazy",
"helpful",
"mushy",
"odd",
"unsightly",
"adorable",
"important",
"inexpensive",
"cheap",
"expensive",
"fancy",
];
static COLOURS: &[&str] = &[
"red", "yellow", "blue", "green", "pink", "brown", "purple", "brown", "white", "black",
"orange",
];
static NOUNS: &[&str] = &[
"table", "chair", "house", "bbq", "desk", "car", "pony", "cookie", "sandwich", "burger",
"pizza", "mouse", "keyboard",
];
// #[derive(PartialEq, Props)]
// struct RowProps<'a> {
// row_id: usize,
// label: &'a Label,
// }
// fn Row(cx: Context, props: &RowProps) -> Element {
// rsx!(cx, tr {
// td { class:"col-md-1", "{props.row_id}" }
// td { class:"col-md-1", onclick: move |_| { /* run onselect */ }
// a { class: "lbl", {props.label.labels} }
// }
// td { class: "col-md-1"
// a { class: "remove", onclick: move |_| {/* remove */}
// span { class: "glyphicon glyphicon-remove remove" aria_hidden: "true" }
// }
// }
// td { class: "col-md-6" }
// })
// }

View File

@ -0,0 +1,46 @@
//! Example: README.md showcase
//!
//! The example from the README.md.
use dioxus::prelude::*;
use dioxus_core as dioxus;
use dioxus_core_macro::*;
use dioxus_hooks::use_state;
use dioxus_html as dioxus_elements;
use dioxus_web;
use gloo_timers::future::TimeoutFuture;
fn main() {
wasm_logger::init(wasm_logger::Config::new(log::Level::Debug));
dioxus_web::launch(App, |c| c);
}
static App: FC<()> = |cx, props| {
let show = use_state(cx, || true);
let inner = match *show {
true => {
rsx!( div {
"hello world"
})
}
false => {
rsx!( div {
// h1 {
"bello world"
// }
})
}
};
rsx!(cx, div {
button {
"toggle"
onclick: move |_| {
let cur = *show;
show.set(!cur);
}
}
{inner}
})
};

View File

@ -7,6 +7,11 @@
/// Eventually we might want to procedurally generate these strings for common words, phrases, and values.
pub(crate) fn intern_cached_strings() {
let cached_words = [
// Important tags to dioxus
"dioxus-id",
"dioxus",
"dioxus-event-click", // todo: more events
"click",
// All the HTML Tags
"a",
"abbr",

View File

@ -17,7 +17,7 @@ impl Default for WebConfig {
fn default() -> Self {
Self {
hydrate: false,
rootname: "dioxusroot".to_string(),
rootname: "main".to_string(),
}
}
}

View File

@ -213,7 +213,7 @@ impl WebsysDom {
fn create_placeholder(&mut self, id: u64) {
self.create_element("pre", None, id);
// self.set_attribute("hidden", "", None);
self.set_attribute("hidden", "", None, id);
}
fn create_text_node(&mut self, text: &str, id: u64) {
@ -246,8 +246,15 @@ impl WebsysDom {
.unwrap(),
};
use smallstr;
use smallstr::SmallString;
use std::fmt::Write;
let mut s: SmallString<[u8; 8]> = smallstr::SmallString::new();
write!(s, "{}", id).unwrap();
let el2 = el.dyn_ref::<Element>().unwrap();
el2.set_attribute("dioxus-id", &format!("{}", id)).unwrap();
el2.set_attribute("dioxus-id", s.as_str()).unwrap();
self.stack.push(el.clone());
self.nodes[(id as usize)] = Some(el);
@ -263,18 +270,21 @@ impl WebsysDom {
let el = self.stack.top();
let el = el
.dyn_ref::<Element>()
.expect(&format!("not an element: {:?}", el));
let el = el.dyn_ref::<Element>().unwrap();
// let el = el.dyn_ref::<Element>().unwrap();
// .expect(&format!("not an element: {:?}", el));
// let scope_id = scope.data().as_ffi();
let scope_id = scope.0 as u64;
// let scope_id = scope.0 as u64;
// "dioxus-event-click",
// "1.10"
// &format!("", scope_id, real_id),
// &format!("dioxus-event-{}", event),
// &format!("{}.{}", scope_id, real_id),
// &format!("dioxus-event-{}", event),
// &format!("{}.{}", scope_id, real_id),
el.set_attribute(
&format!("dioxus-event-{}", event),
&format!("{}.{}", scope_id, real_id),
)
.unwrap();
el.set_attribute("dioxus-event", event).unwrap();
// el.set_attribute(&format!("dioxus-event"), &format!("{}", event))
// .unwrap();
@ -488,6 +498,8 @@ unsafe impl Sync for DioxusWebsysEvent {}
fn virtual_event_from_websys_event(event: web_sys::Event) -> Arc<dyn Any + Send + Sync> {
use dioxus_html::on::*;
use dioxus_html::KeyCode;
// event.prevent_default();
// use dioxus_core::events::on::*;
match event.type_().as_str() {
"copy" | "cut" | "paste" => Arc::new(ClipboardEvent {}),
@ -682,30 +694,40 @@ fn decode_trigger(event: &web_sys::Event) -> anyhow::Result<UserEvent> {
use anyhow::Context;
let element_id = target
.get_attribute("dioxus-id")
.context("Could not find element id on event target")?
.parse()?;
// The error handling here is not very descriptive and needs to be replaced with a zero-cost error system
let val: String = target
.get_attribute(&format!("dioxus-event-{}", typ))
.get_attribute("dioxus-event")
.context(format!("wrong format - received {:#?}", typ))?;
// .get_attribute(&format!("dioxus-event-{}", typ))
// .context(format!("wrong format - received {:#?}", typ))?;
let mut fields = val.splitn(3, ".");
let gi_id = fields
.next()
.and_then(|f| f.parse::<u64>().ok())
.context("failed to parse gi id")?;
// let gi_id = fields
// .next()
// .and_then(|f| f.parse::<u64>().ok())
// .context("failed to parse gi id")?;
let real_id = fields
.next()
.and_then(|raw_id| raw_id.parse::<u64>().ok())
.context("failed to parse real id")?;
// let real_id = fields
// .next()
// .and_then(|raw_id| raw_id.parse::<u64>().ok())
// .context("failed to parse real id")?;
let triggered_scope = gi_id;
// let triggered_scope = gi_id;
Ok(UserEvent {
name: event_name_from_typ(&typ),
data: virtual_event_from_websys_event(event.clone()),
element: Some(ElementId(real_id as usize)),
scope_id: Some(ScopeId(triggered_scope as usize)),
element: Some(ElementId(element_id)),
scope_id: None,
// scope_id: Some(ScopeId(triggered_scope as usize)),
// element: Some(ElementId(real_id as usize)),
// scope_id: Some(ScopeId(triggered_scope as usize)),
priority: dioxus_core::EventPriority::Medium,
})
}

View File

@ -85,7 +85,7 @@ mod ric_raf;
/// dioxus_web::launch(App, |c| c);
/// }
///
/// static App: FC<()> = |(cx, props)| {
/// static App: FC<()> = |cx, props| {
/// rsx!(cx, div {"hello world"})
/// }
/// ```
@ -109,7 +109,7 @@ pub fn launch(root_component: FC<()>, configuration: impl FnOnce(WebConfig) -> W
/// name: String
/// }
///
/// static App: FC<RootProps> = |(cx, props)| {
/// static App: FC<RootProps> = |cx, props| {
/// rsx!(cx, div {"hello {props.name}"})
/// }
/// ```
@ -155,7 +155,7 @@ pub async fn run_with_props<T: 'static + Send>(root: FC<T>, root_props: T, cfg:
// hydrating is simply running the dom for a single render. If the page is already written, then the corresponding
// ElementIds should already line up because the web_sys dom has already loaded elements with the DioxusID into memory
if !should_hydrate {
log::info!("Applying rebuild edits..., {:?}", mutations);
// log::info!("Applying rebuild edits..., {:?}", mutations);
websys_dom.process_edits(&mut mutations.edits);
}
@ -166,17 +166,20 @@ pub async fn run_with_props<T: 'static + Send>(root: FC<T>, root_props: T, cfg:
// if there is work then this future resolves immediately.
dom.wait_for_work().await;
// wait for the mainthread to schedule us in
let mut deadline = work_loop.wait_for_idle_time().await;
// // wait for the mainthread to schedule us in
// let mut deadline = work_loop.wait_for_idle_time().await;
// run the virtualdom work phase until the frame deadline is reached
let mutations = dom.work_with_deadline(|| (&mut deadline).now_or_never().is_some());
let mutations = dom.work_with_deadline(|| false);
// // run the virtualdom work phase until the frame deadline is reached
// let mutations = dom.work_with_deadline(|| (&mut deadline).now_or_never().is_some());
// wait for the animation frame to fire so we can apply our changes
work_loop.wait_for_raf().await;
for mut edit in mutations {
// actually apply our changes during the animation frame
// log::info!("Applying change edits..., {:?}", edit);
websys_dom.process_edits(&mut edit.edits);
}
}

View File

@ -93,7 +93,7 @@
//! Dioxus uses hooks for state management. Hooks are a form of state persisted between calls of the function component.
//!
//! ```
//! pub pub static Example: FC<()> = |(cx, props)|{
//! pub pub static Example: FC<()> = |cx, props|{
//! let (val, set_val) = use_state(cx, || 0);
//! cx.render(rsx!(
//! button { onclick: move |_| set_val(val + 1) }
@ -156,7 +156,7 @@
//! dioxus::web::launch(Example);
//! }
//!
//! pub pub static Example: FC<()> = |(cx, props)|{
//! pub pub static Example: FC<()> = |cx, props|{
//! cx.render(rsx! {
//! div { "Hello World!" }
//! })
@ -190,6 +190,11 @@ pub use dioxus_router as router;
pub mod debug {}
pub mod events {
#[cfg(feature = "html")]
pub use dioxus_html::{on::*, KeyCode};
}
pub mod prelude {
//! A glob import that includes helper types like FC, rsx!, html!, and required traits
pub use dioxus_core::prelude::*;