feat: upgrade syntax

This commit is contained in:
Jonathan Kelley 2021-11-10 17:09:52 -05:00
parent 574d7fdb9e
commit fd93ee89c1
64 changed files with 522 additions and 437 deletions

View file

@ -5,7 +5,7 @@ fn main() {
dom.rebuild();
}
const App: FC<()> = |(cx, props)| {
const App: FC<()> = |cx, props| {
let id = cx.scope_id();
// cx.submit_task(Box::pin(async move { id }));

View file

@ -6,7 +6,7 @@ struct SomeContext {
}
#[allow(unused)]
static Example: FC<()> = |(cx, props)| {
static Example: FC<()> = |cx, props| {
todo!()
// let value = cx.use_context(|c: &SomeContext| c.items.last().unwrap());

View file

@ -33,7 +33,7 @@ fn html_usage() {
static App2: FC<()> = |(cx, _)| cx.render(rsx!("hello world!"));
static App: FC<()> = |(cx, props)| {
static App: FC<()> = |cx, props| {
let name = cx.use_state(|| 0);
cx.render(rsx!(div {
@ -99,7 +99,7 @@ impl<'a> Children<'a> {
}
}
static Bapp: FC<()> = |(cx, props)| {
static Bapp: FC<()> = |cx, props| {
let name = cx.use_state(|| 0);
cx.render(rsx!(
@ -114,7 +114,7 @@ static Bapp: FC<()> = |(cx, props)| {
))
};
static Match: FC<()> = |(cx, props)| {
static Match: FC<()> = |cx, props| {
//
let b: Box<dyn Fn(NodeFactory) -> VNode> = Box::new(|f| todo!());

View file

@ -5,7 +5,7 @@ use dioxus_core::{lazynodes::LazyNodes, prelude::*};
// #[async_std::main]
fn main() {
static App: FC<()> =
|(cx, props)| cx.render(Some(LazyNodes::new(move |f| f.text(format_args!("hello")))));
|cx, props| cx.render(Some(LazyNodes::new(move |f| f.text(format_args!("hello")))));
let mut dom = VirtualDom::new(App);

View file

@ -32,7 +32,7 @@ use dioxus::prelude::*;
struct NoKeysProps {
data: std::collections::HashMap<u32, String>,
}
static AntipatternNoKeys: FC<NoKeysProps> = |(cx, props)| {
static AntipatternNoKeys: FC<NoKeysProps> = |cx, props| {
// WRONG: Make sure to add keys!
rsx!(cx, ul {
{props.data.iter().map(|(k, v)| rsx!(li { "List item: {v}" }))}
@ -54,7 +54,7 @@ static AntipatternNoKeys: FC<NoKeysProps> = |(cx, props)| {
///
/// Only Component and Fragment nodes are susceptible to this issue. Dioxus mitigates this with components by providing
/// an API for registering shared state without the ContextProvider pattern.
static AntipatternNestedFragments: FC<()> = |(cx, props)| {
static AntipatternNestedFragments: FC<()> = |cx, props| {
// Try to avoid heavily nesting fragments
rsx!(cx,
Fragment {
@ -82,7 +82,7 @@ static AntipatternNestedFragments: FC<()> = |(cx, props)| {
/// However, calling set_state will *not* update the current version of state in the component. This should be easy to
/// recognize from the function signature, but Dioxus will not update the "live" version of state. Calling `set_state`
/// merely places a new value in the queue and schedules the component for a future update.
static AntipatternRelyingOnSetState: FC<()> = |(cx, props)| {
static AntipatternRelyingOnSetState: FC<()> = |cx, props| {
let (state, set_state) = use_state(cx, || "Hello world").classic();
set_state("New state");
// This will return false! `state` will *still* be "Hello world"
@ -99,7 +99,7 @@ static AntipatternRelyingOnSetState: FC<()> = |(cx, props)| {
/// - All components must start with an uppercase character
///
/// i.e.: the following component will be rejected when attempted to be used in the rsx! macro
static antipattern_component: FC<()> = |(cx, props)| todo!();
static antipattern_component: FC<()> = |cx, props| todo!();
/// Antipattern: Misusing hooks
/// ---------------------------
@ -120,7 +120,7 @@ static antipattern_component: FC<()> = |(cx, props)| todo!();
struct MisuedHooksProps {
should_render_state: bool,
}
static AntipatternMisusedHooks: FC<MisuedHooksProps> = |(cx, props)| {
static AntipatternMisusedHooks: FC<MisuedHooksProps> = |cx, props| {
if props.should_render_state {
// do not place a hook in the conditional!
// prefer to move it out of the conditional
@ -153,7 +153,7 @@ static AntipatternMisusedHooks: FC<MisuedHooksProps> = |(cx, props)| {
/// }
/// }
/// })
static _example: FC<()> = |(cx, props)| todo!();
static _example: FC<()> = |cx, props| todo!();
/// Antipattern: publishing components and hooks with all features enabled
/// ----------------------------------------------------------------------
@ -171,9 +171,9 @@ static _example: FC<()> = |(cx, props)| todo!();
///
/// This will only include the `core` dioxus crate which is relatively slim and fast to compile and avoids target-specific
/// libraries.
static __example: FC<()> = |(cx, props)| todo!();
static __example: FC<()> = |cx, props| todo!();
pub static Example: FC<()> = |(cx, props)| {
pub static Example: FC<()> = |cx, props| {
cx.render(rsx! {
AntipatternNoKeys { data: std::collections::HashMap::new() }
AntipatternNestedFragments {}

View file

@ -9,7 +9,7 @@
use dioxus::prelude::*;
pub static Example: FC<()> = |(cx, props)| {
pub static Example: FC<()> = |cx, props| {
cx.render(rsx! {
div {
Greeting {
@ -25,7 +25,7 @@ struct GreetingProps {
name: &'static str,
}
static Greeting: FC<GreetingProps> = |(cx, props)| {
static Greeting: FC<GreetingProps> = |cx, props| {
cx.render(rsx! {
div {
h1 { "Hello, {props.name}!" }

View file

@ -18,7 +18,7 @@
use dioxus::prelude::*;
pub static Example: FC<()> = |(cx, props)| {
pub static Example: FC<()> = |cx, props| {
cx.render(rsx! {
div {
Banner {
@ -31,7 +31,7 @@ pub static Example: FC<()> = |(cx, props)| {
})
};
pub static Banner: FC<()> = |(cx, props)| {
pub static Banner: FC<()> = |cx, props| {
cx.render(rsx! {
div {
h1 { "This is a great banner!" }

View file

@ -16,7 +16,7 @@ use dioxus::prelude::*;
pub struct MyProps {
should_show: bool,
}
pub static Example0: FC<MyProps> = |(cx, props)| {
pub static Example0: FC<MyProps> = |cx, props| {
cx.render(rsx! {
div {
{props.should_show.then(|| rsx!{
@ -39,7 +39,7 @@ pub static Example0: FC<MyProps> = |(cx, props)| {
pub struct MyProps1 {
should_show: bool,
}
pub static Example1: FC<MyProps1> = |(cx, props)| {
pub static Example1: FC<MyProps1> = |cx, props| {
cx.render(rsx! {
div {
// With matching
@ -77,7 +77,7 @@ pub enum Color {
pub struct MyProps2 {
color: Color,
}
pub static Example2: FC<MyProps2> = |(cx, props)| {
pub static Example2: FC<MyProps2> = |cx, props| {
cx.render(rsx! {
div {
{match props.color {
@ -89,7 +89,7 @@ pub static Example2: FC<MyProps2> = |(cx, props)| {
})
};
pub static Example: FC<()> = |(cx, props)| {
pub static Example: FC<()> = |cx, props| {
let should_show = use_state(cx, || false);
let mut color_index = use_state(cx, || 0);
let color = match *color_index % 2 {

View file

@ -1,7 +1,7 @@
use dioxus::prelude::*;
fn main() {}
pub static Example: FC<()> = |(cx, props)| {
pub static Example: FC<()> = |cx, props| {
cx.render(rsx! {
div {
@ -10,7 +10,7 @@ pub static Example: FC<()> = |(cx, props)| {
};
// A controlled component:
static ControlledSelect: FC<()> = |(cx, props)| {
static ControlledSelect: FC<()> = |cx, props| {
let value = use_state(cx, || String::from("Grapefruit"));
cx.render(rsx! {
select { value: "{value}", onchange: move |evt| value.set(evt.value()),
@ -23,7 +23,7 @@ static ControlledSelect: FC<()> = |(cx, props)| {
};
// TODO - how do uncontrolled things work?
static UncontrolledSelect: FC<()> = |(cx, props)| {
static UncontrolledSelect: FC<()> = |cx, props| {
let value = use_state(cx, || String::new());
cx.render(rsx! {

View file

@ -11,7 +11,7 @@
use dioxus::prelude::*;
pub static Example: FC<()> = |(cx, props)| {
pub static Example: FC<()> = |cx, props| {
cx.render(rsx! {
div {
custom_element {

View file

@ -5,4 +5,4 @@
use dioxus::prelude::*;
pub static Example: FC<()> = |(cx, props)| cx.render(rsx! { Fragment {} });
pub static Example: FC<()> = |cx, props| cx.render(rsx! { Fragment {} });

View file

@ -23,14 +23,14 @@ fn main() {}
/// This is one way to go about error handling (just toss things away with unwrap).
/// However, if you get it wrong, the whole app will crash.
/// This is pretty flimsy.
static App: FC<()> = |(cx, props)| {
static App: FC<()> = |cx, props| {
let data = get_data().unwrap();
cx.render(rsx!( div { "{data}" } ))
};
/// This is a pretty verbose way of error handling
/// However, it's still pretty good since we don't panic, just fail to render anything
static App1: FC<()> = |(cx, props)| {
static App1: FC<()> = |cx, props| {
let data = match get_data() {
Some(data) => data,
None => return None,
@ -46,7 +46,7 @@ static App1: FC<()> = |(cx, props)| {
/// a user is logged in.
///
/// Dioxus will throw an error in the console if the None-path is ever taken.
static App2: FC<()> = |(cx, props)| {
static App2: FC<()> = |cx, props| {
let data = get_data()?;
cx.render(rsx!( div { "{data}" } ))
};
@ -54,14 +54,14 @@ static App2: FC<()> = |(cx, props)| {
/// This is top-tier error handling since it displays a failure state.
///
/// However, the error is lacking in context.
static App3: FC<()> = |(cx, props)| match get_data() {
static App3: FC<()> = |cx, props| match get_data() {
Some(data) => cx.render(rsx!( div { "{data}" } )),
None => cx.render(rsx!( div { "Failed to load data :(" } )),
};
/// For errors that return results, it's possible to short-circuit the match-based error handling with `.ok()` which converts
/// a Result<T, V> into an Option<T> and lets you abort rendering by early-returning `None`
static App4: FC<()> = |(cx, props)| {
static App4: FC<()> = |cx, props| {
let data = get_data_err().ok()?;
cx.render(rsx!( div { "{data}" } ))
};
@ -69,7 +69,7 @@ static App4: FC<()> = |(cx, props)| {
/// This is great error handling since it displays a failure state... with context!
///
/// Hopefully you'll never need to display a screen like this. It's rather bad taste
static App5: FC<()> = |(cx, props)| match get_data_err() {
static App5: FC<()> = |cx, props| match get_data_err() {
Ok(data) => cx.render(rsx!( div { "{data}" } )),
Err(c) => cx.render(rsx!( div { "Failed to load data: {c}" } )),
};

View file

@ -11,7 +11,7 @@
use dioxus::prelude::*;
// Returning multiple elements with rsx! or html!
static App1: FC<()> = |(cx, props)| {
static App1: FC<()> = |cx, props| {
cx.render(rsx! {
h1 { }
h2 { }
@ -20,7 +20,7 @@ static App1: FC<()> = |(cx, props)| {
};
// Using the Fragment component
static App2: FC<()> = |(cx, props)| {
static App2: FC<()> = |cx, props| {
cx.render(rsx! {
Fragment {
div {}
@ -31,7 +31,7 @@ static App2: FC<()> = |(cx, props)| {
};
// Using the `fragment` method on the NodeFactory
static App3: FC<()> = |(cx, props)| {
static App3: FC<()> = |cx, props| {
cx.render(LazyNodes::new(move |fac| {
fac.fragment_from_iter([
fac.text(format_args!("A")),
@ -42,7 +42,7 @@ static App3: FC<()> = |(cx, props)| {
}))
};
pub static Example: FC<()> = |(cx, props)| {
pub static Example: FC<()> = |cx, props| {
cx.render(rsx! {
App1 {}
App2 {}

View file

@ -19,7 +19,7 @@ h1 {color: blue;}
p {color: red;}
"#;
pub static Example: FC<()> = |(cx, props)| {
pub static Example: FC<()> = |cx, props| {
cx.render(rsx! {
head { style { "{STYLE}" } }
body {

View file

@ -10,7 +10,7 @@
use dioxus::prelude::*;
pub static Example: FC<()> = |(cx, props)| {
pub static Example: FC<()> = |cx, props| {
cx.render(rsx! {
head {
style: { background_color: "powderblue" }
@ -29,7 +29,7 @@ pub static Example: FC<()> = |(cx, props)| {
// .... technically the rsx! macro is slightly broken at the moment and allows styles not wrapped in style {}
// I haven't noticed any name collisions yet, and am tentatively leaving this behavior in..
// Don't rely on it.
static Example2: FC<()> = |(cx, props)| {
static Example2: FC<()> = |cx, props| {
cx.render(rsx! {
div { color: "red"
"hello world!"

View file

@ -12,7 +12,7 @@
use dioxus::prelude::*;
pub static Example: FC<()> = |(cx, props)| {
pub static Example: FC<()> = |cx, props| {
let example_data = use_state(cx, || 0);
let v = (0..10).map(|f| {

View file

@ -7,7 +7,7 @@
use dioxus::prelude::*;
pub static Example: FC<()> = |(cx, props)| {
pub static Example: FC<()> = |cx, props| {
cx.render(rsx! {
ButtonList {}
NonUpdatingEvents {}
@ -16,7 +16,7 @@ pub static Example: FC<()> = |(cx, props)| {
};
/// We can use `set_name` in multiple closures; the closures automatically *copy* the reference to set_name.
static ButtonList: FC<()> = |(cx, props)| {
static ButtonList: FC<()> = |cx, props| {
let name = use_state(cx, || "...?");
let names = ["jack", "jill", "john", "jane"]
@ -33,7 +33,7 @@ static ButtonList: FC<()> = |(cx, props)| {
/// This shows how listeners may be without a visible change in the display.
/// Check the console.
static NonUpdatingEvents: FC<()> = |(cx, props)| {
static NonUpdatingEvents: FC<()> = |cx, props| {
rsx!(cx, div {
button {
onclick: move |_| log::info!("Did not cause any updates!")
@ -42,7 +42,7 @@ static NonUpdatingEvents: FC<()> = |(cx, props)| {
})
};
static DisablePropagation: FC<()> = |(cx, props)| {
static DisablePropagation: FC<()> = |cx, props| {
rsx!(cx,
div {
onclick: move |_| log::info!("event propagated to the div!")

View file

@ -21,7 +21,7 @@ use dioxus::prelude::*;
// By default, components with no props are always memoized.
// A props of () is considered empty.
pub static Example: FC<()> = |(cx, props)| {
pub static Example: FC<()> = |cx, props| {
cx.render(rsx! {
div { "100% memoized!" }
})
@ -35,7 +35,7 @@ pub struct MyProps1 {
name: String,
}
pub static Example1: FC<MyProps1> = |(cx, props)| {
pub static Example1: FC<MyProps1> = |cx, props| {
cx.render(rsx! {
div { "100% memoized! {props.name}" }
})
@ -49,7 +49,7 @@ pub struct MyProps2 {
name: std::rc::Rc<str>,
}
pub static Example2: FC<MyProps2> = |(cx, props)| {
pub static Example2: FC<MyProps2> = |cx, props| {
cx.render(rsx! {
div { "100% memoized! {props.name}" }
})

View file

@ -1,7 +1,7 @@
use dioxus::prelude::*;
fn main() {}
pub static Example: FC<()> = |(cx, props)| {
pub static Example: FC<()> = |cx, props| {
let p = 10;
cx.render(rsx! {

View file

@ -1,7 +1,7 @@
use dioxus::prelude::*;
fn main() {}
pub static Example: FC<()> = |(cx, props)| {
pub static Example: FC<()> = |cx, props| {
cx.render(rsx! {
div {

View file

@ -9,7 +9,7 @@
use dioxus::prelude::*;
pub static Example: FC<()> = |(cx, props)| {
pub static Example: FC<()> = |cx, props| {
let props = MyProps {
count: 0,
live: true,

View file

@ -1,7 +1,7 @@
use dioxus::prelude::*;
fn main() {}
pub static Example: FC<()> = |(cx, props)| {
pub static Example: FC<()> = |cx, props| {
cx.render(rsx! {
div {

View file

@ -14,7 +14,7 @@ struct DogApi {
}
const ENDPOINT: &str = "https://dog.ceo/api/breeds/image/random";
pub static Example: FC<()> = |(cx, props)| {
pub static Example: FC<()> = |cx, props| {
let doggo = use_suspense(
cx,
|| surf::get(ENDPOINT).recv_json::<DogApi>(),

View file

@ -24,7 +24,7 @@
use dioxus::prelude::*;
pub static Example: FC<()> = |(cx, props)| {
pub static Example: FC<()> = |cx, props| {
let count = use_state(cx, || 0);
let mut direction = use_state(cx, || 1);

View file

@ -1,6 +1,6 @@
use dioxus::prelude::*;
pub static Example: FC<()> = |(cx, props)| {
pub static Example: FC<()> = |cx, props| {
cx.render(rsx! {
div {

View file

@ -1,7 +1,7 @@
use dioxus::prelude::*;
use dioxus::ssr;
pub static Example: FC<()> = |(cx, props)| {
pub static Example: FC<()> = |cx, props| {
let as_string = use_state(cx, || {
// Currently, SSR is only supported for whole VirtualDOMs
// This is an easy/low hanging fruit to improve upon
@ -15,7 +15,7 @@ pub static Example: FC<()> = |(cx, props)| {
})
};
static SomeApp: FC<()> = |(cx, props)| {
static SomeApp: FC<()> = |cx, props| {
cx.render(rsx! {
div { style: {background_color: "blue"}
h1 {"Some amazing app or component"}

View file

@ -27,7 +27,7 @@ fn main() {
use dioxus::prelude::*;
static App: FC<()> = |(cx, props)| {
static App: FC<()> = |cx, props| {
let p1 = use_state(cx, || 0);
let p2 = use_state(cx, || 0);

View file

@ -8,7 +8,7 @@ fn main() {
dioxus_desktop::launch(App, |c| c);
}
static App: FC<()> = |(cx, props)| {
static App: FC<()> = |cx, props| {
cx.render(rsx!(
div {
"hello world!"

View file

@ -18,7 +18,7 @@ fn main() {
});
}
static App: FC<()> = |(cx, props)| {
static App: FC<()> = |cx, props| {
let file_manager = use_ref(cx, || Files::new());
let files = file_manager.read();

View file

@ -19,7 +19,7 @@ fn main() {
dioxus::desktop::launch(App, |c| c.with_prerendered(content));
}
static App: FC<()> = |(cx, props)| {
static App: FC<()> = |cx, props| {
let mut val = use_state(cx, || 0);
cx.render(rsx! {

View file

@ -31,7 +31,7 @@ fn main() {
});
}
static App: FC<()> = |(cx, props)| {
static App: FC<()> = |cx, props| {
let state = use_ref(cx, || Calculator::new());
let clear_display = state.read().display_value.eq("0");

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 mut count = use_state(cx, || 0);
cx.render(rsx! {

View file

@ -23,7 +23,7 @@ pub enum Route {
NotFound,
}
static App: FC<()> = |(cx, props)| {
static App: FC<()> = |cx, props| {
let route = use_router(cx, Route::parse)?;
cx.render(rsx! {

View file

@ -49,7 +49,7 @@ const NONE_ELEMENT: Option<()> = None;
use baller::Baller;
use dioxus::prelude::*;
pub static Example: FC<()> = |(cx, props)| {
pub static Example: FC<()> = |cx, props| {
let formatting = "formatting!";
let formatting_tuple = ("a", "b");
let lazy_fmt = format_args!("lazily formatted text");

View file

@ -9,7 +9,7 @@ fn main() {
println!("{}", ssr::render_vdom(&vdom, |c| c));
}
static App: FC<()> = |(cx, props)| {
static App: FC<()> = |cx, props| {
cx.render(rsx!(
div {
h1 { "Title" }

View file

@ -12,7 +12,7 @@ fn main() {
)
}
pub static App: FC<()> = |(cx, props)| {
pub static App: FC<()> = |cx, props| {
cx.render(rsx!(
div {
class: "overflow-hidden"

View file

@ -42,7 +42,7 @@ fn main() {}
// initial_name: String,
// }
// static Example: FC<ExampleProps> = |(cx, props)| {
// static Example: FC<ExampleProps> = |cx, props| {
// let dispaly_name = use_state(cx, move || props.initial_name.clone());
// cx.render(rsx! {

View file

@ -24,7 +24,7 @@ fn main() {
.unwrap();
}
pub static App: FC<()> = |(cx, props)| {
pub static App: FC<()> = |cx, props| {
cx.render(rsx!(
div { class: "overflow-hidden"
link { href:"https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css" rel:"stylesheet" }
@ -39,7 +39,7 @@ pub static App: FC<()> = |(cx, props)| {
))
};
pub static Header: FC<()> = |(cx, props)| {
pub static Header: FC<()> = |cx, props| {
cx.render(rsx! {
div {
header { class: "text-gray-400 bg-gray-900 body-font"
@ -65,7 +65,7 @@ pub static Header: FC<()> = |(cx, props)| {
})
};
pub static Hero: FC<()> = |(cx, props)| {
pub static Hero: FC<()> = |cx, props| {
//
cx.render(rsx! {
section{ class: "text-gray-400 bg-gray-900 body-font"
@ -103,7 +103,7 @@ pub static Hero: FC<()> = |(cx, props)| {
}
})
};
pub static Entry: FC<()> = |(cx, props)| {
pub static Entry: FC<()> = |cx, props| {
//
cx.render(rsx! {
section{ class: "text-gray-400 bg-gray-900 body-font"
@ -116,7 +116,7 @@ pub static Entry: FC<()> = |(cx, props)| {
})
};
pub static StacksIcon: FC<()> = |(cx, props)| {
pub static StacksIcon: FC<()> = |cx, props| {
cx.render(rsx!(
svg {
xmlns: "http://www.w3.org/2000/svg"
@ -131,7 +131,7 @@ pub static StacksIcon: FC<()> = |(cx, props)| {
}
))
};
pub static RightArrowIcon: FC<()> = |(cx, props)| {
pub static RightArrowIcon: FC<()> = |cx, props| {
cx.render(rsx!(
svg {
fill: "none"

View file

@ -14,7 +14,7 @@ fn main() {
const STYLE: &str = "body {overflow:hidden;}";
pub static App: FC<()> = |(cx, props)| {
pub static App: FC<()> = |cx, props| {
cx.render(rsx!(
div { class: "overflow-hidden"
style { "{STYLE}" }
@ -30,7 +30,7 @@ pub static App: FC<()> = |(cx, props)| {
))
};
pub static Header: FC<()> = |(cx, props)| {
pub static Header: FC<()> = |cx, props| {
cx.render(rsx! {
div {
header { class: "text-gray-400 bg-gray-900 body-font"
@ -56,7 +56,7 @@ pub static Header: FC<()> = |(cx, props)| {
})
};
pub static Hero: FC<()> = |(cx, props)| {
pub static Hero: FC<()> = |cx, props| {
//
cx.render(rsx! {
section{ class: "text-gray-400 bg-gray-900 body-font"
@ -94,7 +94,7 @@ pub static Hero: FC<()> = |(cx, props)| {
}
})
};
pub static Entry: FC<()> = |(cx, props)| {
pub static Entry: FC<()> = |cx, props| {
//
cx.render(rsx! {
section{ class: "text-gray-400 bg-gray-900 body-font"
@ -107,7 +107,7 @@ pub static Entry: FC<()> = |(cx, props)| {
})
};
pub static StacksIcon: FC<()> = |(cx, props)| {
pub static StacksIcon: FC<()> = |cx, props| {
cx.render(rsx!(
svg {
// xmlns: "http://www.w3.org/2000/svg"
@ -122,7 +122,7 @@ pub static StacksIcon: FC<()> = |(cx, props)| {
}
))
};
pub static RightArrowIcon: FC<()> = |(cx, props)| {
pub static RightArrowIcon: FC<()> = |cx, props| {
cx.render(rsx!(
svg {
fill: "none"

23
examples/tasks.rs Normal file
View file

@ -0,0 +1,23 @@
//! Example: README.md showcase
//!
//! The example from the README.md.
use dioxus::prelude::*;
fn main() {
dioxus::desktop::launch(App, |c| c);
}
static App: FC<()> = |cx, props| {
let mut count = use_state(cx, || 0);
cx.push_task(async {
panic!("polled future");
//
});
cx.render(rsx! {
div {
h1 { "High-Five counter: {count}" }
}
})
};

View file

@ -22,7 +22,7 @@ pub struct TodoItem {
}
const STYLE: &str = include_str!("./assets/todomvc.css");
const App: FC<()> = |(cx, props)| {
const App: FC<()> = |cx, props| {
let mut draft = use_state(cx, || "".to_string());
let mut todos = use_state(cx, || HashMap::<u32, Rc<TodoItem>>::new());
let mut filter = use_state(cx, || FilterState::All);
@ -85,7 +85,7 @@ pub struct TodoEntryProps {
todo: Rc<TodoItem>,
}
pub fn TodoEntry((cx, props): Scope<TodoEntryProps>) -> Element {
pub fn TodoEntry(cx: Context, props: &TodoEntryProps) -> Element {
let mut is_editing = use_state(cx, || false);
let mut contents = use_state(cx, || String::from(""));
let todo = &props.todo;

View file

@ -12,7 +12,7 @@ fn main() {
const ENDPOINT: &str = "https://api.openweathermap.org/data/2.5/weather";
static App: FC<()> = |(cx, props)| {
static App: FC<()> = |cx, props| {
//
let body = use_suspense(
cx,
@ -40,7 +40,7 @@ static App: FC<()> = |(cx, props)| {
#[derive(PartialEq, Props)]
struct WeatherProps {}
static WeatherDisplay: FC<WeatherProps> = |(cx, props)| {
static WeatherDisplay: FC<WeatherProps> = |cx, props| {
//
cx.render(rsx!(
div { class: "flex items-center justify-center flex-col"

View file

@ -24,7 +24,7 @@ fn main() {
dioxus_web::launch(App, |c| c)
}
static App: FC<()> = |(cx, props)| {
static App: FC<()> = |cx, props| {
let mut state = use_state(cx, || 0);
cx.render(rsx! {
div {

View file

@ -23,7 +23,7 @@ fn main() {
// dioxus::web::launch(App, |c| c);
}
static App: FC<()> = |(cx, props)| {
static App: FC<()> = |cx, props| {
dbg!("rednering parent");
cx.render(rsx! {
div {
@ -40,7 +40,7 @@ static App: FC<()> = |(cx, props)| {
})
};
static But: FC<()> = |(cx, props)| {
static But: FC<()> = |cx, props| {
let mut count = use_state(cx, || 0);
// let d = Dropper { name: "asd" };

View file

@ -24,7 +24,7 @@ fn main() {
dioxus_web::launch(App, |c| c)
}
pub static App: FC<()> = |(cx, props)| {
pub static App: FC<()> = |cx, props| {
cx.render(rsx!(
div { class: "overflow-hidden"
link { href:"https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css" rel:"stylesheet" }
@ -39,7 +39,7 @@ pub static App: FC<()> = |(cx, props)| {
))
};
pub static Header: FC<()> = |(cx, props)| {
pub static Header: FC<()> = |cx, props| {
cx.render(rsx! {
div {
header { class: "text-gray-400 bg-gray-900 body-font"
@ -65,7 +65,7 @@ pub static Header: FC<()> = |(cx, props)| {
})
};
pub static Hero: FC<()> = |(cx, props)| {
pub static Hero: FC<()> = |cx, props| {
//
cx.render(rsx! {
section{ class: "text-gray-400 bg-gray-900 body-font"
@ -103,7 +103,7 @@ pub static Hero: FC<()> = |(cx, props)| {
}
})
};
pub static Entry: FC<()> = |(cx, props)| {
pub static Entry: FC<()> = |cx, props| {
//
cx.render(rsx! {
section{ class: "text-gray-400 bg-gray-900 body-font"
@ -116,7 +116,7 @@ pub static Entry: FC<()> = |(cx, props)| {
})
};
pub static StacksIcon: FC<()> = |(cx, props)| {
pub static StacksIcon: FC<()> = |cx, props| {
cx.render(rsx!(
svg {
// xmlns: "http://www.w3.org/2000/svg"
@ -131,7 +131,7 @@ pub static StacksIcon: FC<()> = |(cx, props)| {
}
))
};
pub static RightArrowIcon: FC<()> = |(cx, props)| {
pub static RightArrowIcon: FC<()> = |cx, props| {
cx.render(rsx!(
svg {
fill: "none"

View file

@ -19,7 +19,7 @@ fn main() {
dioxus::web::launch(App, |c| c);
}
static App: FC<()> = |(cx, props)| {
static App: FC<()> = |cx, props| {
let mut rng = SmallRng::from_entropy();
let rows = (0..1_000).map(|f| {
let label = Label::new(&mut rng);

View file

@ -16,7 +16,7 @@ fn main() {
dioxus::web::launch(App, |c| c);
}
static App: FC<()> = |(cx, props)| {
static App: FC<()> = |cx, props| {
let mut count = use_state(cx, || 0);
cx.render(rsx! {

View file

@ -173,7 +173,7 @@ impl ToTokens for Component {
if !self.children.is_empty() {
let childs = &self.children;
toks.append_all(quote! {
.children(ScopeChildren::new(__cx.fragment_from_iter([ #( #childs ),* ])))
.children(__cx.create_children([ #( #childs ),* ]))
});
}

View file

@ -321,14 +321,15 @@ impl<'bump> DiffState<'bump> {
}
MountType::Replace { old } => {
// todo: a bug here where we remove a node that is alread being replaced
if let Some(old_id) = old.try_mounted_id() {
self.mutations.replace_with(old_id, nodes_created as u32);
self.remove_nodes(Some(old), true);
self.remove_nodes(Some(old), false);
} else {
if let Some(id) = self.find_first_element_id(old) {
self.mutations.replace_with(id, nodes_created as u32);
}
self.remove_nodes(Some(old), true);
self.remove_nodes(Some(old), false);
}
}
@ -487,7 +488,11 @@ impl<'bump> DiffState<'bump> {
}
fn create_linked_node(&mut self, link: &'bump NodeLink) {
todo!()
if let Some(cur_scope) = self.stack.current_scope() {
link.scope_id.set(Some(cur_scope));
let node: &'bump VNode<'static> = unsafe { &*link.node };
self.create_node(unsafe { std::mem::transmute(node) });
}
}
// =================================
@ -532,7 +537,6 @@ impl<'bump> DiffState<'bump> {
fn diff_element_nodes(
&mut self,
old: &'bump VElement<'bump>,
new: &'bump VElement<'bump>,
old_node: &'bump VNode<'bump>,
@ -629,10 +633,8 @@ impl<'bump> DiffState<'bump> {
fn diff_component_nodes(
&mut self,
old_node: &'bump VNode<'bump>,
new_node: &'bump VNode<'bump>,
old: &'bump VComponent<'bump>,
new: &'bump VComponent<'bump>,
) {
@ -648,21 +650,20 @@ impl<'bump> DiffState<'bump> {
new.associated_scope.set(Some(scope_addr));
// make sure the component's caller function is up to date
let scope = self.scopes.get_scope(&scope_addr).unwrap();
let mut items = scope.items.borrow_mut();
let scope = unsafe { self.scopes.get_scope_mut(&scope_addr).unwrap() };
scope.caller = unsafe { std::mem::transmute(new.caller) };
// React doesn't automatically memoize, but we do.
let props_are_the_same = todo!("reworking component memoization");
// let props_are_the_same = todo!("reworking component memoization");
// let props_are_the_same = old.comparator.unwrap();
let props_are_the_same = old.comparator.unwrap();
// if self.cfg.force_diff || !props_are_the_same(new) {
// let succeeded = scope.run_scope(self);
// if succeeded {
// self.diff_node(scope.frames.wip_head(), scope.frames.fin_head());
// }
// }
if self.force_diff || !props_are_the_same(new) {
if self.scopes.run_scope(&scope_addr) {
self.diff_node(
self.scopes.wip_head(&scope_addr),
self.scopes.fin_head(&scope_addr),
);
}
}
self.stack.scope_stack.pop();
} else {
@ -1198,7 +1199,6 @@ impl<'bump> DiffState<'bump> {
/// remove can happen whenever
fn remove_nodes(
&mut self,
nodes: impl IntoIterator<Item = &'bump VNode<'bump>>,
gen_muts: bool,
) {
@ -1244,18 +1244,16 @@ impl<'bump> DiffState<'bump> {
}
VNode::Linked(l) => {
todo!()
let node = unsafe { std::mem::transmute(&*l.node) };
self.remove_nodes(Some(node), gen_muts);
}
VNode::Component(c) => {
let scope_id = c.associated_scope.get().unwrap();
// let scope = self.scopes.get_scope(&scope_id).unwrap();
let root = self.scopes.root_node(&scope_id);
self.remove_nodes(Some(root), gen_muts);
log::debug!("Destroying scope {:?}", scope_id);
let mut s = self.scopes.try_remove(&scope_id).unwrap();
s.hooks.clear_hooks();
self.scopes.try_remove(&scope_id).unwrap();
}
}
}

View file

@ -245,7 +245,7 @@ fn it_drops() {
simple_logger::init().unwrap();
let factory = NodeFactory { bump: &bump };
// let factory = NodeFactory { scope: &bump };
struct DropInner {
id: i32,

View file

@ -3,21 +3,32 @@
//! This module contains an internal API to generate these instructions.
use crate::innerlude::*;
use std::any::Any;
use std::{any::Any, fmt::Debug};
#[derive(Debug)]
pub struct Mutations<'a> {
pub edits: Vec<DomEdit<'a>>,
pub noderefs: Vec<NodeRefMutation<'a>>,
pub effects: Vec<&'a dyn FnMut()>,
}
impl Debug for Mutations<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Mutations")
.field("edits", &self.edits)
.field("noderefs", &self.noderefs)
// .field("effects", &self.effects)
.finish()
}
}
use DomEdit::*;
impl<'a> Mutations<'a> {
pub(crate) fn new() -> Self {
let edits = Vec::new();
let noderefs = Vec::new();
Self { edits, noderefs }
Self {
edits: Vec::new(),
noderefs: Vec::new(),
effects: Vec::new(),
}
}
// Navigation

View file

@ -181,7 +181,7 @@ impl<'src> VNode<'src> {
pub(crate) fn children(&self) -> &[VNode<'src>] {
match &self {
VNode::Fragment(f) => f.children,
VNode::Component(c) => todo!("children are not accessible through this"),
VNode::Component(_c) => todo!("children are not accessible through this"),
_ => &[],
}
}
@ -199,9 +199,9 @@ impl<'src> VNode<'src> {
key: f.key,
}),
VNode::Linked(c) => VNode::Linked(NodeLink {
gen_id: c.gen_id,
scope_id: c.scope_id,
link_idx: c.link_idx,
scope_id: c.scope_id.clone(),
link_idx: c.link_idx.clone(),
node: c.node.clone(),
}),
}
}
@ -224,11 +224,7 @@ impl Debug for VNode<'_> {
}
VNode::Suspended { .. } => write!(s, "VNode::VSuspended"),
VNode::Component(comp) => write!(s, "VNode::VComponent {{ fc: {:?}}}", comp.user_fc),
VNode::Linked(c) => write!(
s,
"VNode::VCached {{ gen_id: {}, scope_id: {:?} }}",
c.gen_id, c.scope_id
),
VNode::Linked(c) => write!(s, "VNode::VCached {{ scope_id: {:?} }}", c.scope_id.get()),
}
}
}
@ -419,9 +415,25 @@ pub struct VSuspended<'a> {
/// an `rsx!` call.
#[derive(Debug)]
pub struct NodeLink {
pub(crate) link_idx: usize,
pub(crate) gen_id: u32,
pub(crate) scope_id: ScopeId,
pub(crate) link_idx: Cell<usize>,
pub(crate) scope_id: Cell<Option<ScopeId>>,
pub(crate) node: *const VNode<'static>,
}
impl PartialEq for NodeLink {
fn eq(&self, other: &Self) -> bool {
self.node == other.node
}
}
impl NodeLink {
// we don't want to let users clone NodeLinks
pub(crate) fn clone_inner(&self) -> Self {
Self {
link_idx: self.link_idx.clone(),
scope_id: self.scope_id.clone(),
node: self.node.clone(),
}
}
}
/// This struct provides an ergonomic API to quickly build VNodes.
@ -440,7 +452,7 @@ impl<'a> NodeFactory<'a> {
#[inline]
pub fn bump(&self) -> &'a bumpalo::Bump {
self.bump
&self.bump
}
/// Directly pass in text blocks without the need to use the format_args macro.
@ -460,7 +472,7 @@ impl<'a> NodeFactory<'a> {
Some(static_str) => (static_str, true),
None => {
use bumpalo::core_alloc::fmt::Write;
let mut str_buf = bumpalo::collections::String::new_in(self.bump());
let mut str_buf = bumpalo::collections::String::new_in(self.bump);
str_buf.write_fmt(args).unwrap();
(str_buf.into_bump_str(), false)
}
@ -516,18 +528,18 @@ impl<'a> NodeFactory<'a> {
A: 'a + AsRef<[Attribute<'a>]>,
V: 'a + AsRef<[VNode<'a>]>,
{
let listeners: &'a L = self.bump().alloc(listeners);
let listeners: &'a L = self.bump.alloc(listeners);
let listeners = listeners.as_ref();
let attributes: &'a A = self.bump().alloc(attributes);
let attributes: &'a A = self.bump.alloc(attributes);
let attributes = attributes.as_ref();
let children: &'a V = self.bump().alloc(children);
let children: &'a V = self.bump.alloc(children);
let children = children.as_ref();
let key = key.map(|f| self.raw_text(f).0);
VNode::Element(self.bump().alloc(VElement {
VNode::Element(self.bump.alloc(VElement {
tag_name,
key,
namespace,
@ -565,7 +577,7 @@ impl<'a> NodeFactory<'a> {
where
P: Properties + 'a,
{
let bump = self.bump();
let bump = self.bump;
let props = bump.alloc(props);
let bump_props = props as *mut P as *mut ();
let user_fc = component as *const ();
@ -598,7 +610,6 @@ impl<'a> NodeFactory<'a> {
let drop_props: &mut dyn FnMut() = bump.alloc_with(|| {
move || unsafe {
log::debug!("dropping props!");
if !has_dropped {
let real_other = bump_props as *mut _ as *mut P;
let b = BumpBox::from_raw(real_other);
@ -620,7 +631,6 @@ impl<'a> NodeFactory<'a> {
let caller: &'a mut dyn Fn(&'a Scope) -> Element =
bump.alloc(move |scope: &Scope| -> Element {
log::debug!("calling component renderr {:?}", scope.our_arena_idx);
let props: &'_ P = unsafe { &*(bump_props as *const P) };
let res = component(scope, props);
res
@ -657,7 +667,7 @@ impl<'a> NodeFactory<'a> {
self,
node_iter: impl IntoIterator<Item = impl IntoVNode<'a>>,
) -> VNode<'a> {
let bump = self.bump();
let bump = self.bump;
let mut nodes = bumpalo::collections::Vec::new_in(bump);
for node in node_iter {
@ -697,6 +707,58 @@ impl<'a> NodeFactory<'a> {
})
}
// this isn't quite feasible yet
// I think we need some form of interior mutability or state on nodefactory that stores which subtree was created
pub fn create_children(
self,
node_iter: impl IntoIterator<Item = impl IntoVNode<'a>>,
) -> Element {
let bump = self.bump;
let mut nodes = bumpalo::collections::Vec::new_in(bump);
for node in node_iter {
nodes.push(node.into_vnode(self));
}
if nodes.is_empty() {
nodes.push(VNode::Anchor(bump.alloc(VAnchor {
dom_id: empty_cell(),
})));
}
let children = nodes.into_bump_slice();
// TODO
// We need a dedicated path in the rsx! macro that will trigger the "you need keys" warning
//
// if cfg!(debug_assertions) {
// if children.len() > 1 {
// if children.last().unwrap().key().is_none() {
// log::error!(
// r#"
// Warning: Each child in an array or iterator should have a unique "key" prop.
// Not providing a key will lead to poor performance with lists.
// See docs.rs/dioxus for more information.
// ---
// To help you identify where this error is coming from, we've generated a backtrace.
// "#,
// );
// }
// }
// }
let frag = VNode::Fragment(VFragment {
children,
key: None,
});
let ptr = self.bump.alloc(frag) as *const _;
Some(NodeLink {
link_idx: Default::default(),
scope_id: Default::default(),
node: unsafe { std::mem::transmute(ptr) },
})
}
pub fn annotate_lazy<'z, 'b>(
f: impl FnOnce(NodeFactory<'z>) -> VNode<'z> + 'b,
) -> Option<LazyNodes<'z, 'b>> {
@ -796,28 +858,48 @@ impl IntoVNode<'_> for Arguments<'_> {
}
}
// called cx.render from a helper function
impl IntoVNode<'_> for Option<NodeLink> {
fn into_vnode(self, cx: NodeFactory) -> VNode {
todo!()
fn into_vnode(self, _cx: NodeFactory) -> VNode {
match self {
Some(node) => VNode::Linked(node),
None => {
todo!()
}
}
}
}
// essentially passing elements through props
// just build a new element in place
impl IntoVNode<'_> for &Option<NodeLink> {
fn into_vnode(self, cx: NodeFactory) -> VNode {
todo!()
fn into_vnode(self, _cx: NodeFactory) -> VNode {
match self {
Some(node) => VNode::Linked(NodeLink {
link_idx: node.link_idx.clone(),
scope_id: node.scope_id.clone(),
node: node.node,
}),
None => {
//
todo!()
}
}
}
}
// Conveniently, we also support "None"
impl IntoVNode<'_> for NodeLink {
fn into_vnode(self, cx: NodeFactory) -> VNode {
todo!()
fn into_vnode(self, _cx: NodeFactory) -> VNode {
VNode::Linked(self)
}
}
// Conveniently, we also support "None"
impl IntoVNode<'_> for &NodeLink {
fn into_vnode(self, cx: NodeFactory) -> VNode {
todo!()
fn into_vnode(self, _cx: NodeFactory) -> VNode {
VNode::Linked(NodeLink {
link_idx: self.link_idx.clone(),
scope_id: self.scope_id.clone(),
node: self.node,
})
}
}

View file

@ -2,6 +2,7 @@ use crate::innerlude::*;
use futures_channel::mpsc::UnboundedSender;
use fxhash::FxHashMap;
use smallvec::SmallVec;
use std::{
any::{Any, TypeId},
cell::{Cell, RefCell},
@ -62,9 +63,6 @@ pub struct Scope {
// The double-buffering situation that we will use
pub(crate) frames: [BumpFrame; 2],
pub(crate) old_root: RefCell<Option<NodeLink>>,
pub(crate) new_root: RefCell<Option<NodeLink>>,
pub(crate) caller: *const dyn Fn(&Scope) -> Element,
/*
@ -241,19 +239,22 @@ impl Scope {
/// }
///```
pub fn render<'src>(&'src self, lazy_nodes: Option<LazyNodes<'src, '_>>) -> Option<NodeLink> {
let bump = &self.wip_frame().bump;
let frame = self.wip_frame();
let bump = &frame.bump;
let factory = NodeFactory { bump };
let node = lazy_nodes.map(|f| f.call(factory))?;
let node = bump.alloc(node);
let idx = self
.wip_frame()
.add_node(unsafe { std::mem::transmute(node) });
let node_ptr = node as *mut _;
let node_ptr = unsafe { std::mem::transmute(node_ptr) };
Some(NodeLink {
gen_id: self.generation.get(),
scope_id: self.our_arena_idx,
link_idx: idx,
})
let link = NodeLink {
scope_id: Cell::new(Some(self.our_arena_idx)),
link_idx: Cell::new(0),
node: node_ptr,
};
Some(link)
}
/// Push an effect to be ran after the component has been successfully mounted to the dom
@ -435,6 +436,7 @@ impl Scope {
false => &self.frames[1],
}
}
pub(crate) fn fin_frame(&self) -> &BumpFrame {
match self.generation.get() & 1 == 1 {
true => &self.frames[0],
@ -496,28 +498,42 @@ impl Scope {
effect();
}
}
pub fn root_node(&self) -> &VNode {
let node = *self.wip_frame().nodes.borrow().get(0).unwrap();
unsafe { std::mem::transmute(&*node) }
}
}
pub struct BumpFrame {
pub(crate) struct BumpFrame {
pub bump: Bump,
pub nodes: RefCell<Vec<VNode<'static>>>,
pub nodes: RefCell<Vec<*const VNode<'static>>>,
}
impl BumpFrame {
pub fn new() -> Self {
let bump = Bump::new();
pub fn new(capacity: usize) -> Self {
let bump = Bump::with_capacity(capacity);
let node = &*bump.alloc(VText {
text: "asd",
dom_id: Default::default(),
is_static: false,
});
let nodes = RefCell::new(vec![VNode::Text(unsafe { std::mem::transmute(node) })]);
let node = bump.alloc(VNode::Text(unsafe { std::mem::transmute(node) }));
let nodes = RefCell::new(vec![node as *const _]);
Self { bump, nodes }
}
fn add_node(&self, node: VNode<'static>) -> usize {
pub fn allocated_bytes(&self) -> usize {
self.bump.allocated_bytes()
}
pub fn assign_nodelink(&self, node: &NodeLink) {
let mut nodes = self.nodes.borrow_mut();
nodes.push(node);
nodes.len() - 1
let len = nodes.len();
nodes.push(node.node);
node.link_idx.set(len);
}
}
@ -531,11 +547,18 @@ impl BumpFrame {
#[derive(Default)]
pub(crate) struct HookList {
arena: Bump,
vals: RefCell<Vec<*mut dyn Any>>,
vals: RefCell<SmallVec<[*mut dyn Any; 5]>>,
idx: Cell<usize>,
}
impl HookList {
pub fn new(capacity: usize) -> Self {
Self {
arena: Bump::with_capacity(capacity),
..Default::default()
}
}
pub(crate) fn next<T: 'static>(&self) -> Option<&mut T> {
self.vals.borrow().get(self.idx.get()).and_then(|inn| {
self.idx.set(self.idx.get() + 1);
@ -571,7 +594,7 @@ impl HookList {
self.cur_idx() >= self.len()
}
pub fn clear_hooks(&mut self) {
pub fn clear(&mut self) {
self.vals.borrow_mut().drain(..).for_each(|state| {
let as_mut = unsafe { &mut *state };
let boxed = unsafe { bumpalo::boxed::Box::from_raw(as_mut) };

View file

@ -1,3 +1,4 @@
use fxhash::FxHashMap;
use slab::Slab;
use std::cell::{Cell, RefCell};
@ -7,7 +8,6 @@ use futures_channel::mpsc::UnboundedSender;
use crate::innerlude::*;
pub type FcSlot = *const ();
// pub heuristics: FxHashMap<FcSlot, Heuristic>,
pub struct Heuristic {
hook_arena_size: usize,
@ -21,6 +21,7 @@ pub struct Heuristic {
pub(crate) struct ScopeArena {
bump: Bump,
scopes: RefCell<Vec<*mut Scope>>,
pub heuristics: RefCell<FxHashMap<FcSlot, Heuristic>>,
free_scopes: RefCell<Vec<ScopeId>>,
nodes: RefCell<Slab<*const VNode<'static>>>,
pub(crate) sender: UnboundedSender<SchedulerMsg>,
@ -31,6 +32,7 @@ impl ScopeArena {
Self {
bump: Bump::new(),
scopes: RefCell::new(Vec::new()),
heuristics: RefCell::new(FxHashMap::default()),
free_scopes: RefCell::new(Vec::new()),
nodes: RefCell::new(Slab::new()),
sender,
@ -68,33 +70,53 @@ impl ScopeArena {
} else {
let scope_id = ScopeId(self.scopes.borrow().len());
let old_root = NodeLink {
link_idx: 0,
gen_id: 0,
scope_id,
};
let new_root = NodeLink {
link_idx: 0,
gen_id: 0,
scope_id,
let (node_capacity, hook_capacity) = {
let heuristics = self.heuristics.borrow();
if let Some(heuristic) = heuristics.get(&fc_ptr) {
(heuristic.node_arena_size, heuristic.hook_arena_size)
} else {
(0, 0)
}
};
let new_scope = Scope {
let mut frames = [BumpFrame::new(node_capacity), BumpFrame::new(node_capacity)];
frames[0].nodes.get_mut().push({
let vnode = frames[0]
.bump
.alloc(VNode::Text(frames[0].bump.alloc(VText {
dom_id: Default::default(),
is_static: false,
text: "",
})));
unsafe { std::mem::transmute(vnode as *mut VNode) }
});
frames[1].nodes.get_mut().push({
let vnode = frames[1]
.bump
.alloc(VNode::Text(frames[1].bump.alloc(VText {
dom_id: Default::default(),
is_static: false,
text: "",
})));
unsafe { std::mem::transmute(vnode as *mut VNode) }
});
let scope = self.bump.alloc(Scope {
sender: self.sender.clone(),
parent_scope,
our_arena_idx: scope_id,
height,
subtree: Cell::new(subtree),
is_subtree_root: Cell::new(false),
frames: [BumpFrame::new(), BumpFrame::new()],
frames,
hooks: Default::default(),
hooks: HookList::new(hook_capacity),
shared_contexts: Default::default(),
caller,
generation: 0.into(),
old_root: RefCell::new(Some(old_root)),
new_root: RefCell::new(Some(new_root)),
items: RefCell::new(SelfReferentialItems {
listeners: Default::default(),
borrowed_props: Default::default(),
@ -102,16 +124,42 @@ impl ScopeArena {
tasks: Default::default(),
pending_effects: Default::default(),
}),
};
});
let stable = self.bump.alloc(new_scope);
self.scopes.borrow_mut().push(stable);
self.scopes.borrow_mut().push(scope);
scope_id
}
}
pub fn try_remove(&self, id: &ScopeId) -> Option<Scope> {
todo!()
pub fn try_remove(&self, id: &ScopeId) -> Option<()> {
self.ensure_drop_safety(id);
let mut scope = unsafe { &mut *self.get_scope_raw(id)? };
// we're just reusing scopes so we need to clear it out
scope.hooks.clear();
scope.shared_contexts.get_mut().clear();
scope.parent_scope = None;
scope.generation.set(0);
scope.is_subtree_root.set(false);
scope.subtree.set(0);
let SelfReferentialItems {
borrowed_props,
listeners,
pending_effects,
suspended_nodes,
tasks,
} = scope.items.get_mut();
borrowed_props.clear();
listeners.clear();
pending_effects.clear();
suspended_nodes.clear();
tasks.clear();
self.free_scopes.borrow_mut().push(*id);
Some(())
}
pub fn reserve_node(&self, node: &VNode) -> ElementId {
@ -123,16 +171,10 @@ impl ScopeArena {
let node = unsafe { std::mem::transmute::<*const VNode, *const VNode>(node) };
entry.insert(node);
id
// let nodes = self.nodes.borrow_mut();
// let id = nodes.insert(());
// let node_id = ElementId(id);
// node = Some(node_id);
// node_id
}
pub fn collect_garbage(&self, id: ElementId) {
todo!()
self.nodes.borrow_mut().remove(id.0);
}
// These methods would normally exist on `scope` but they need access to *all* of the scopes
@ -152,35 +194,25 @@ impl ScopeArena {
pub(crate) fn ensure_drop_safety(&self, scope_id: &ScopeId) {
let scope = self.get_scope(scope_id).unwrap();
let mut items = scope.items.borrow_mut();
// make sure we drop all borrowed props manually to guarantee that their drop implementation is called before we
// run the hooks (which hold an &mut Reference)
// right now, we don't drop
scope
.items
.borrow_mut()
.borrowed_props
.drain(..)
.for_each(|comp| {
// First drop the component's undropped references
let scope_id = comp
.associated_scope
.get()
.expect("VComponents should be associated with a valid Scope");
// recursively call ensure_drop_safety on all children
items.borrowed_props.drain(..).for_each(|comp| {
let scope_id = comp
.associated_scope
.get()
.expect("VComponents should be associated with a valid Scope");
todo!("move this onto virtualdom");
// let scope = unsafe { &mut *scope_id };
self.ensure_drop_safety(&scope_id);
// scope.ensure_drop_safety();
todo!("drop the component's props");
// let mut drop_props = comp.drop_props.borrow_mut().take().unwrap();
// drop_props();
});
let mut drop_props = comp.drop_props.borrow_mut().take().unwrap();
drop_props();
});
// Now that all the references are gone, we can safely drop our own references in our listeners.
scope
.items
.borrow_mut()
items
.listeners
.drain(..)
.for_each(|listener| drop(listener.callback.borrow_mut().take()));
@ -212,59 +244,58 @@ impl ScopeArena {
// just forget about our suspended nodes while we're at it
items.suspended_nodes.clear();
items.tasks.clear();
items.pending_effects.clear();
// guarantee that we haven't screwed up - there should be no latent references anywhere
debug_assert!(items.listeners.is_empty());
debug_assert!(items.suspended_nodes.is_empty());
debug_assert!(items.borrowed_props.is_empty());
debug_assert!(items.suspended_nodes.is_empty());
debug_assert!(items.tasks.is_empty());
debug_assert!(items.pending_effects.is_empty());
log::debug!("Borrowed stuff is successfully cleared");
// temporarily cast the vcomponent to the right lifetime
// let vcomp = scope.load_vcomp();
// Todo: see if we can add stronger guarantees around internal bookkeeping and failed component renders.
scope.wip_frame().nodes.borrow_mut().clear();
}
let render: &dyn Fn(&Scope) -> Element = unsafe { &*scope.caller };
// Todo: see if we can add stronger guarantees around internal bookkeeping and failed component renders.
scope.wip_frame().nodes.borrow_mut().clear();
if let Some(key) = render(scope) {
dbg!(key);
if let Some(link) = render(scope) {
// right now, it's a panic to render a nodelink from another scope
// todo: enable this. it should (reasonably) work even if it doesnt make much sense
assert_eq!(link.scope_id.get(), Some(*id));
dbg!(&scope.wip_frame().nodes.borrow_mut());
// let mut old = scope.old_root.borrow_mut();
// let mut new = scope.new_root.borrow_mut();
// nodelinks are not assigned when called and must be done so through the create/diff phase
// however, we need to link this one up since it will never be used in diffing
scope.wip_frame().assign_nodelink(&link);
debug_assert_eq!(scope.wip_frame().nodes.borrow().len(), 1);
// let new_old = new.clone();
// *old = new_old;
// *new = Some(key);
if !scope.items.borrow().tasks.is_empty() {
// self.
}
// dbg!(&old);
// dbg!(&new);
// the user's component succeeded. We can safely cycle to the next frame
// scope.frames.wip_frame_mut().head_node = unsafe { std::mem::transmute(new_head) };
scope.cycle_frame();
true
} else {
false
}
}
// The head of the bumpframe is the first linked NodeLink
pub fn wip_head(&self, id: &ScopeId) -> &VNode {
let scope = self.get_scope(id).unwrap();
let wip_frame = scope.wip_frame();
let nodes = wip_frame.nodes.borrow();
let node = nodes.get(0).unwrap();
let node: &VNode = unsafe { &**nodes.get(0).unwrap() };
unsafe { std::mem::transmute::<&VNode, &VNode>(node) }
}
// The head of the bumpframe is the first linked NodeLink
pub fn fin_head(&self, id: &ScopeId) -> &VNode {
let scope = self.get_scope(id).unwrap();
let wip_frame = scope.fin_frame();
let nodes = wip_frame.nodes.borrow();
let node: &VNode = nodes.get(0).unwrap();
let node: &VNode = unsafe { &**nodes.get(0).unwrap() };
unsafe { std::mem::transmute::<&VNode, &VNode>(node) }
}

View file

@ -13,6 +13,9 @@ use std::{any::Any, collections::VecDeque};
/// A virtual node system that progresses user events and diffs UI trees.
///
///
/// ## Guide
///
/// Components are defined as simple functions that take [`Context`] and a [`Properties`] type and return an [`Element`].
///
/// ```rust
@ -105,14 +108,16 @@ pub struct VirtualDom {
_root_caller: *mut dyn Fn(&Scope) -> Element,
pub(crate) scopes: Box<ScopeArena>,
scopes: Box<ScopeArena>,
receiver: UnboundedReceiver<SchedulerMsg>,
pub(crate) sender: UnboundedSender<SchedulerMsg>,
// Every component that has futures that need to be polled
sender: UnboundedSender<SchedulerMsg>,
pending_futures: FxHashSet<ScopeId>,
pending_messages: VecDeque<SchedulerMsg>,
dirty_scopes: IndexSet<ScopeId>,
}
@ -130,7 +135,7 @@ impl VirtualDom {
///
/// # Example
/// ```
/// fn Example(cx: Context<()>) -> DomTree {
/// fn Example(cx: Context, props: &()) -> Element {
/// cx.render(rsx!( div { "hello world" } ))
/// }
///
@ -159,7 +164,7 @@ impl VirtualDom {
/// name: &'static str
/// }
///
/// fn Example(cx: Context<SomeProps>) -> DomTree {
/// fn Example(cx: Context, props: &SomeProps) -> Element {
/// cx.render(rsx!{ div{ "hello {cx.name}" } })
/// }
///
@ -172,7 +177,7 @@ impl VirtualDom {
/// let mut dom = VirtualDom::new_with_props(Example, SomeProps { name: "jane" });
/// let mutations = dom.rebuild();
/// ```
pub fn new_with_props<P: 'static + Send>(root: FC<P>, root_props: P) -> Self {
pub fn new_with_props<P: 'static>(root: FC<P>, root_props: P) -> Self {
let (sender, receiver) = futures_channel::mpsc::unbounded::<SchedulerMsg>();
Self::new_with_props_and_scheduler(root, root_props, sender, receiver)
}
@ -230,8 +235,10 @@ impl VirtualDom {
///
/// # Example
///
/// ```rust
///
///
///
/// ```
pub fn get_scheduler_channel(&self) -> futures_channel::mpsc::UnboundedSender<SchedulerMsg> {
self.sender.clone()
}
@ -240,8 +247,10 @@ impl VirtualDom {
///
/// # Example
///
/// ```rust
///
///
/// ```
pub fn has_any_work(&self) -> bool {
!(self.dirty_scopes.is_empty() && self.pending_messages.is_empty())
}
@ -347,7 +356,9 @@ impl VirtualDom {
/// # Example
///
/// ```no_run
/// static App: FC<()> = |cx, props|rsx!(cx, div {"hello"} );
/// fn App(cx: Context, props: &()) -> Element {
/// cx.render(rsx!( div {"hello"} ))
/// }
///
/// let mut dom = VirtualDom::new(App);
///
@ -368,6 +379,7 @@ impl VirtualDom {
/// applied the edits.
///
/// Mutations are the only link between the RealDOM and the VirtualDOM.
///
pub fn work_with_deadline(&mut self, mut deadline: impl FnMut() -> bool) -> Vec<Mutations> {
let mut committed_mutations = vec![];
@ -436,10 +448,7 @@ impl VirtualDom {
}
}
// let scopes = &mut self.scopes;
let work_completed = diff_state.work(&mut deadline);
// let work_completed = crate::diff::work(&mut diff_state, &mut deadline);
// let work_completed = crate::diff::work(&mut diff_state, &mut deadline);
if work_completed {
let DiffState {
@ -454,7 +463,7 @@ impl VirtualDom {
}
// I think the stack should be empty at the end of diffing?
debug_assert_eq!(stack.scope_stack.len(), 0);
debug_assert_eq!(stack.scope_stack.len(), 1);
committed_mutations.push(mutations);
} else {
@ -536,74 +545,45 @@ impl VirtualDom {
/// let edits = dom.diff();
/// ```
pub fn hard_diff<'a>(&'a mut self, scope_id: &ScopeId) -> Option<Mutations<'a>> {
log::debug!("hard diff {:?}", scope_id);
let mut diff_machine = DiffState::new(&self.scopes);
if self.scopes.run_scope(scope_id) {
let mut diff_machine = DiffState::new(&self.scopes);
diff_machine.force_diff = true;
diff_machine.work(|| false);
// let scopes = &mut self.scopes;
// crate::diff::diff_scope(&mut diff_machine, scope_id);
// self.scopes.diff_scope(&mut diff_machine, scope_id);
// dbg!(&diff_machine.mutations);
// let DiffState {
// mutations,
// stack,
// seen_scopes,
// force_diff,
// ..
// } = mutations;
// let mutations = diff_machine.mutations;
// Some(unsafe { std::mem::transmute(mutations) })
todo!()
} else {
None
diff_machine.diff_scope(scope_id);
}
Some(diff_machine.mutations)
}
/// Renders an `rsx` call into the Base Scope's allocator.
///
/// Useful when needing to render nodes from outside the VirtualDom, such as in a test.
pub fn render_vnodes<'a>(&'a self, lazy_nodes: Option<LazyNodes<'a, '_>>) -> &'a VNode<'a> {
todo!()
let scope = self.scopes.get_scope(&self.base_scope).unwrap();
let frame = scope.wip_frame();
let factory = NodeFactory { bump: &frame.bump };
let node = lazy_nodes.unwrap().call(factory);
frame.bump.alloc(node)
}
/// Renders an `rsx` call into the Base Scope's allocator.
///
/// Useful when needing to render nodes from outside the VirtualDom, such as in a test.
pub fn diff_vnodes<'a>(&'a self, old: &'a VNode<'a>, new: &'a VNode<'a>) -> Mutations<'a> {
todo!()
// let mutations = Mutations::new();
// let mut machine: DiffState = todo!();
// // let mut machine = DiffState::new(mutations);
// // let mut machine = DiffState::new(mutations);
// machine.stack.push(DiffInstruction::Diff { new, old });
// machine.mutations
let mut machine = DiffState::new(&self.scopes);
machine.stack.push(DiffInstruction::Diff { new, old });
machine.stack.scope_stack.push(self.base_scope);
machine.work(|| false);
machine.mutations
}
/// Renders an `rsx` call into the Base Scope's allocator.
///
/// Useful when needing to render nodes from outside the VirtualDom, such as in a test.
pub fn create_vnodes<'a>(&'a self, left: Option<LazyNodes<'a, '_>>) -> Mutations<'a> {
todo!()
// let old = self.bump.alloc(self.render_direct(left));
// let mut machine: DiffState = todo!();
// // let mut machine = DiffState::new(Mutations::new());
// // let mut machine = DiffState::new(Mutations::new());
// machine.stack.create_node(old, MountType::Append);
// todo!()
// // machine.work(&mut || false);
// // machine.mutations
let nodes = self.render_vnodes(left);
let mut machine = DiffState::new(&self.scopes);
machine.stack.create_node(nodes, MountType::Append);
machine.work(|| false);
machine.mutations
}
/// Renders an `rsx` call into the Base Scope's allocator.
@ -616,26 +596,17 @@ impl VirtualDom {
) -> (Mutations<'a>, Mutations<'a>) {
let (old, new) = (self.render_vnodes(left), self.render_vnodes(right));
// let mut machine: DiffState = todo!();
// let mut machine = DiffState::new(Mutations::new());
let mut create = DiffState::new(&self.scopes);
create.stack.scope_stack.push(self.base_scope);
create.stack.create_node(old, MountType::Append);
create.work(|| false);
// machine.stack.create_node(old, MountType::Append);
let mut edit = DiffState::new(&self.scopes);
create.stack.scope_stack.push(self.base_scope);
edit.stack.push(DiffInstruction::Diff { old, new });
edit.work(&mut || false);
todo!()
// machine.work(|| false);
// let create_edits = machine.mutations;
// let mut machine: DiffState = todo!();
// // let mut machine = DiffState::new(Mutations::new());
// machine.stack.push(DiffInstruction::Diff { old, new });
// machine.work(&mut || false);
// let edits = machine.mutations;
// (create_edits, edits)
(create.mutations, edit.mutations)
}
}

View file

@ -215,12 +215,12 @@ fn create_components() {
})
};
#[derive(Props)]
struct ChildProps<'a> {
children: ScopeChildren<'a>,
#[derive(Props, PartialEq)]
struct ChildProps {
children: Element,
}
fn Child<'a>(cx: Context<'a>, props: &ChildProps<'a>) -> Element {
fn Child(cx: Context, props: &ChildProps) -> Element {
cx.render(rsx! {
h1 {}
div { {&props.children} }
@ -299,21 +299,3 @@ fn anchors() {
]
);
}
#[test]
fn suspended() {
todo!()
// static App: FC<()> = |cx, props| {
// let val = use_suspense(cx, || async {}, |p| todo!());
// cx.render(rsx! { {val} })
// };
// let mut dom = new_dom(App, ());
// let mutations = dom.rebuild();
// assert_eq!(
// mutations.edits,
// [CreatePlaceholder { root: 0 }, AppendChildren { many: 1 },]
// );
}

View file

@ -257,12 +257,12 @@ fn many_items_become_fragment() {
]
);
// hmmmmmmmmm worried about reusing IDs that we shouldnt be
// note: the ID gets reused
assert_eq!(
change.edits,
[
Remove { root: 2 },
CreatePlaceholder { root: 4 },
CreatePlaceholder { root: 3 },
ReplaceWith { root: 0, m: 1 },
]
);
@ -433,7 +433,7 @@ fn keyed_diffing_order() {
let dom = new_dom();
let left = rsx!(
// {(0..5).map(|f| {rsx! { div { key: "{f}" }}})}
{(0..5).map(|f| {rsx! { div { key: "{f}" }}})}
p {"e"}
);
let right = rsx!(
@ -802,22 +802,3 @@ fn controlled_keyed_diffing_out_of_order_max_test() {
]
);
}
#[test]
fn suspense() {
let dom = new_dom();
todo!()
// let edits = dom.create_vnodes(Some(LazyNodes::new(|f| {
// use std::cell::{Cell, RefCell};
// VNode::Suspended(f.bump().alloc(VSuspended {
// task_id: 0,
// callback: RefCell::new(None),
// dom_id: Cell::new(None),
// }))
// })));
// assert_eq!(
// edits.edits,
// [CreatePlaceholder { root: 0 }, AppendChildren { many: 1 }]
// );
}

View file

@ -1,7 +1,6 @@
use std::cell::RefCell;
use dioxus::prelude::Scope;
use dioxus::ScopeChildren;
use dioxus_core as dioxus;
use dioxus_core::{Context, Element, LazyNodes, NodeFactory, Properties};
use dioxus_core_macro::Props;
@ -44,7 +43,7 @@ pub struct WebviewWindowProps<'a> {
/// focuse me
onfocused: &'a dyn FnMut(()),
children: ScopeChildren<'a>,
children: Element,
}
/// A handle to a
@ -57,7 +56,7 @@ pub struct WebviewWindowProps<'a> {
///
///
///
pub fn WebviewWindow((cx, props): Scope<WebviewWindowProps>) -> Element {
pub fn WebviewWindow(cx: Context, props: &WebviewWindowProps) -> Element {
let dtcx = cx.consume_state::<RefCell<DesktopContext>>()?;
cx.use_hook(
@ -91,7 +90,7 @@ fn syntax_works() {
use dioxus_hooks::*;
use dioxus_html as dioxus_elements;
static App: FC<()> = |(cx, props)| {
static App: FC<()> = |cx, props| {
cx.render(rsx! {
// left window
WebviewWindow {

View file

@ -166,7 +166,7 @@ pub fn run<T: 'static + Send + Sync>(
sender.unbounded_send(SchedulerMsg::UiEvent(event)).unwrap();
if let Some(BridgeEvent::Update(edits)) = rx.blocking_recv() {
log::info!("bridge received message {:?}", edits);
log::info!("bridge received message");
Some(RpcResponse::new_result(req.id.take(), Some(edits)))
} else {
log::info!("none received message");
@ -276,7 +276,7 @@ pub(crate) fn launch_vdom_with_tokio<P: Send + 'static>(
runtime.block_on(async move {
let mut vir = VirtualDom::new_with_props_and_scheduler(root, props, sender, receiver);
let _ = vir.get_event_sender();
let _ = vir.get_scheduler_channel();
let edits = vir.rebuild();
@ -298,14 +298,13 @@ pub(crate) fn launch_vdom_with_tokio<P: Send + 'static>(
// todo: maybe we want to schedule ourselves in
// on average though, the virtualdom running natively is stupid fast
let mut muts = vir.run_with_deadline(|| false);
let mut muts = vir.work_with_deadline(|| false);
log::debug!("finished running with deadline");
let mut edits = vec![];
while let Some(edit) = muts.pop() {
log::debug!("sending message on channel with edit {:?}", edit);
let edit_string = serde_json::to_value(Evt { edits: edit.edits })
.expect("serializing edits should never fail");
edits.push(edit_string);

1
packages/ssr/index.html Normal file
View file

@ -0,0 +1 @@
asd

View file

@ -14,7 +14,7 @@ use std::fmt::{Display, Formatter};
use dioxus_core::exports::bumpalo;
use dioxus_core::exports::bumpalo::Bump;
use dioxus_core::nodes::IntoVNode;
use dioxus_core::IntoVNode;
use dioxus_core::*;
/// A memory pool for rendering
@ -94,7 +94,7 @@ pub fn render_vdom_scope(vdom: &VirtualDom, scope: ScopeId) -> Option<String> {
"{:}",
TextRenderer {
cfg: SsrConfig::default(),
root: vdom.get_scope(scope).unwrap().root_node(),
root: vdom.get_scope(&scope).unwrap().root_node(),
vdom: Some(vdom)
}
))
@ -158,6 +158,9 @@ impl<'a> TextRenderer<'a, '_> {
}
write!(f, "<!-- -->")?;
}
VNode::Linked(link) => {
todo!();
}
VNode::Element(el) => {
if self.cfg.indent {
for _ in 0..il {
@ -244,7 +247,7 @@ impl<'a> TextRenderer<'a, '_> {
let idx = vcomp.associated_scope.get().unwrap();
if let (Some(vdom), false) = (self.vdom, self.cfg.skip_components) {
let new_node = vdom.get_scope(idx).unwrap().root_node();
let new_node = vdom.get_scope(&idx).unwrap().root_node();
self.html_render(new_node, f, il + 1)?;
} else {
}
@ -297,18 +300,17 @@ impl SsrConfig {
mod tests {
use super::*;
use dioxus_core as dioxus;
use dioxus_core::prelude::*;
use dioxus_core_macro::*;
use dioxus_html as dioxus_elements;
static SIMPLE_APP: FC<()> = |(cx, _)| {
static SIMPLE_APP: FC<()> = |cx, _| {
cx.render(rsx!(div {
"hello world!"
}))
};
static SLIGHTLY_MORE_COMPLEX: FC<()> = |(cx, _)| {
static SLIGHTLY_MORE_COMPLEX: FC<()> = |cx, _| {
cx.render(rsx! {
div {
title: "About W3Schools"
@ -327,14 +329,14 @@ mod tests {
})
};
static NESTED_APP: FC<()> = |(cx, _)| {
static NESTED_APP: FC<()> = |cx, _| {
cx.render(rsx!(
div {
SIMPLE_APP {}
}
))
};
static FRAGMENT_APP: FC<()> = |(cx, _)| {
static FRAGMENT_APP: FC<()> = |cx, _| {
cx.render(rsx!(
div { "f1" }
div { "f2" }
@ -390,7 +392,7 @@ mod tests {
#[test]
fn styles() {
static STLYE_APP: FC<()> = |(cx, _)| {
static STLYE_APP: FC<()> = |cx, _| {
cx.render(rsx! {
div { color: "blue", font_size: "46px" }
})

View file

@ -7,17 +7,12 @@
//! - tests to ensure dyn_into works for various event types.
//! - Partial delegation?>
use dioxus_core::{
events::{KeyCode, UserEvent},
mutations::NodeRefMutation,
scheduler::SchedulerMsg,
DomEdit, ElementId, ScopeId,
};
use dioxus_core::{DomEdit, ElementId, SchedulerMsg, ScopeId, UserEvent};
use fxhash::FxHashMap;
use std::{any::Any, fmt::Debug, rc::Rc, sync::Arc};
use std::{any::Any, fmt::Debug, rc::Rc};
use wasm_bindgen::{closure::Closure, JsCast};
use web_sys::{
Attr, CssStyleDeclaration, Document, Element, Event, HtmlElement, HtmlInputElement,
CssStyleDeclaration, Document, Element, Event, HtmlElement, HtmlInputElement,
HtmlOptionElement, HtmlTextAreaElement, Node, NodeList,
};
@ -55,7 +50,7 @@ impl WebsysDom {
let document = load_document();
let mut nodes = NodeSlab::new(2000);
let mut listeners = FxHashMap::default();
let listeners = FxHashMap::default();
// re-hydrate the page - only supports one virtualdom per page
if cfg.hydrate {
@ -90,17 +85,17 @@ impl WebsysDom {
}
}
pub fn apply_refs(&mut self, refs: &[NodeRefMutation]) {
for item in refs {
if let Some(bla) = &item.element {
let node = self.nodes[item.element_id.as_u64() as usize]
.as_ref()
.unwrap()
.clone();
bla.set(Box::new(node)).unwrap();
}
}
}
// pub fn apply_refs(&mut self, refs: &[NodeRefMutation]) {
// for item in refs {
// if let Some(bla) = &item.element {
// let node = self.nodes[item.element_id.as_u64() as usize]
// .as_ref()
// .unwrap()
// .clone();
// bla.set(Box::new(node)).unwrap();
// }
// }
// }
pub fn process_edits(&mut self, edits: &mut Vec<DomEdit>) {
for edit in edits.drain(..) {
@ -309,8 +304,8 @@ impl WebsysDom {
}
}
fn remove_event_listener(&mut self, event: &str, root: u64) {
// todo!()
fn remove_event_listener(&mut self, _event: &str, _root: u64) {
todo!()
}
fn set_text(&mut self, text: &str, root: u64) {
@ -489,8 +484,9 @@ unsafe impl Sync for DioxusWebsysEvent {}
// todo: some of these events are being casted to the wrong event type.
// We need tests that simulate clicks/etc and make sure every event type works.
fn virtual_event_from_websys_event(event: web_sys::Event) -> Box<dyn Any + Send> {
use crate::events::*;
use dioxus_core::events::on::*;
use dioxus_html::on::*;
use dioxus_html::KeyCode;
// use dioxus_core::events::on::*;
match event.type_().as_str() {
"copy" | "cut" | "paste" => Box::new(ClipboardEvent {}),
"compositionend" | "compositionstart" | "compositionupdate" => {
@ -682,15 +678,6 @@ fn decode_trigger(event: &web_sys::Event) -> anyhow::Result<UserEvent> {
let typ = event.type_();
// TODO: clean this up
if cfg!(debug_assertions) {
let attrs = target.attributes();
for x in 0..attrs.length() {
let attr: Attr = attrs.item(x).unwrap();
// log::debug!("attrs include: {:#?}, {:#?}", attr.name(), attr.value());
}
}
use anyhow::Context;
// The error handling here is not very descriptive and needs to be replaced with a zero-cost error system
@ -716,7 +703,8 @@ fn decode_trigger(event: &web_sys::Event) -> anyhow::Result<UserEvent> {
name: event_name_from_typ(&typ),
event: virtual_event_from_websys_event(event.clone()),
mounted_dom_id: Some(ElementId(real_id as usize)),
scope: ScopeId(triggered_scope as usize),
scope_id: ScopeId(triggered_scope as usize),
priority: dioxus_core::EventPriority::Medium,
})
}

View file

@ -1,5 +0,0 @@
//! Ported events into Dioxus Synthetic Event system
use dioxus_core::events::on::*;
use wasm_bindgen::JsCast;
use web_sys::{Event, UiEvent};

View file

@ -57,8 +57,8 @@ use std::rc::Rc;
pub use crate::cfg::WebConfig;
use crate::dom::load_document;
use cache::intern_cached_strings;
use dioxus::prelude::Properties;
use dioxus::virtual_dom::VirtualDom;
use dioxus::SchedulerMsg;
use dioxus::VirtualDom;
pub use dioxus_core as dioxus;
use dioxus_core::prelude::FC;
use futures_util::FutureExt;
@ -66,7 +66,6 @@ use futures_util::FutureExt;
mod cache;
mod cfg;
mod dom;
mod events;
mod nodeslab;
mod ric_raf;
@ -144,9 +143,10 @@ pub async fn run_with_props<T: 'static + Send>(root: FC<T>, root_props: T, cfg:
let root_el = load_document().get_element_by_id(&cfg.rootname).unwrap();
let tasks = dom.get_event_sender();
let tasks = dom.get_scheduler_channel();
let sender_callback = Rc::new(move |event| tasks.unbounded_send(event).unwrap());
let sender_callback: Rc<dyn Fn(SchedulerMsg)> =
Rc::new(move |event| tasks.unbounded_send(event).unwrap());
let mut websys_dom = dom::WebsysDom::new(root_el, cfg, sender_callback);
@ -170,13 +170,12 @@ pub async fn run_with_props<T: 'static + Send>(root: FC<T>, root_props: T, cfg:
let mut deadline = work_loop.wait_for_idle_time().await;
// run the virtualdom work phase until the frame deadline is reached
let mutations = dom.run_with_deadline(|| (&mut deadline).now_or_never().is_some());
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 {
// log::debug!("edits are {:#?}", edit);
// actually apply our changes during the animation frame
websys_dom.process_edits(&mut edit.edits);
}