wip: staticify?
This commit is contained in:
1 parent
78d093a945
commit
5ad81885e4
21 files changed
+433
-346
No files matched your search
+1
-1
@@ -4,7 +4,7 @@ members = [
|
||||
"packages/dioxus",
|
||||
"packages/core-macro",
|
||||
"packages/core",
|
||||
# "packages/web",
|
||||
"packages/web",
|
||||
# "packages/router",
|
||||
# "packages/ssr",
|
||||
# "packages/webview",
|
||||
|
||||
@@ -16,7 +16,7 @@ once_cell = "1.7.2"
|
||||
proc-macro-hack = "0.5.19"
|
||||
proc-macro2 = "1.0.6"
|
||||
quote = "1.0"
|
||||
syn = { version = "1.0.11", features = ["full"] }
|
||||
syn = { version = "1.0.11", features = ["full", "extra-traits"] }
|
||||
|
||||
# testing
|
||||
[dev-dependencies]
|
||||
|
||||
@@ -11,7 +11,6 @@ use proc_macro2::TokenStream;
|
||||
use syn::parse::Error;
|
||||
use syn::spanned::Spanned;
|
||||
|
||||
|
||||
use quote::quote;
|
||||
|
||||
pub fn impl_my_derive(ast: &syn::DeriveInput) -> Result<TokenStream, Error> {
|
||||
|
||||
@@ -10,10 +10,8 @@ description = "Core functionality for Dioxus - a concurrent renderer-agnostic Vi
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
# dodrio-derive = { path = "../html-macro-2", version = "0.1.0" }
|
||||
# dioxus-html-macro = { path = "../html-macro", version = "0.1.0" }
|
||||
# dioxus-html-2 = { path = "../html-macro-2", version = "0.1.0" }
|
||||
dioxus-core-macro = { path = "../core-macro", version = "0.1.1" }
|
||||
|
||||
# Backs some static data
|
||||
once_cell = "1.5.2"
|
||||
|
||||
@@ -22,18 +20,12 @@ generational-arena = { version = "0.2.8", features = ["serde"] }
|
||||
# Bumpalo backs the VNode creation
|
||||
bumpalo = { version = "3.6.0", features = ["collections"] }
|
||||
|
||||
owning_ref = "0.4.1"
|
||||
|
||||
# all the arenas 👿
|
||||
typed-arena = "2.0.1"
|
||||
toolshed = "0.8.1"
|
||||
id-arena = "2.2.1"
|
||||
thiserror = "1.0.23"
|
||||
fxhash = "0.2.1"
|
||||
longest-increasing-subsequence = "0.1.0"
|
||||
serde = { version = "1.0.123", features = ["derive"] }
|
||||
log = "0.4.14"
|
||||
pretty_env_logger = "0.4.0"
|
||||
fstrings = "0.2.3"
|
||||
# ouroboros = "0.8.0"
|
||||
# hashbrown = { version = "0.9.1", features = ["bumpalo"] }
|
||||
uuid = {version = "0.8.2", features=["serde", "v4"]}
|
||||
@@ -7,8 +7,6 @@
|
||||
|
||||
fn main() {}
|
||||
|
||||
|
||||
|
||||
use dioxus_core::prelude::*;
|
||||
|
||||
struct Props {
|
||||
@@ -73,9 +71,14 @@ impl PartialEq for ChildProps<'_> {
|
||||
|
||||
impl<'a> Properties for ChildProps<'a> {
|
||||
type Builder = ChildPropsBuilder<'a, ((), ())>;
|
||||
type StaticOutput = ChildProps<'static>;
|
||||
fn builder() -> <Self as Properties>::Builder {
|
||||
ChildProps::builder()
|
||||
}
|
||||
|
||||
unsafe fn into_static(self) -> Self::StaticOutput {
|
||||
unsafe { std::mem::transmute(self) }
|
||||
}
|
||||
}
|
||||
|
||||
fn ChildItem(_ctx: Context, _props: &ChildProps) -> DomTree {
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
use dioxus_core::component::fc_to_builder;
|
||||
use dioxus_core::prelude::*;
|
||||
|
||||
|
||||
|
||||
|
||||
static BLAH: FC<()> = |ctx, _props| {
|
||||
let g = "asd".to_string();
|
||||
ctx.render(rsx! {
|
||||
@@ -30,7 +27,12 @@ fn main() {}
|
||||
|
||||
impl Properties for ExampleProps {
|
||||
type Builder = ExamplePropsBuilder<((),)>;
|
||||
type StaticOutput = ExampleProps;
|
||||
fn builder() -> Self::Builder {
|
||||
ExampleProps::builder()
|
||||
}
|
||||
|
||||
unsafe fn into_static(self) -> Self {
|
||||
self
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,8 @@
|
||||
// #[macro_use]
|
||||
extern crate fstrings;
|
||||
|
||||
|
||||
|
||||
// use dioxus_core::ifmt;
|
||||
// use fstrings::format_args_f;
|
||||
|
||||
|
||||
fn main() {
|
||||
let bump = bumpalo::Bump::new();
|
||||
let _b = ≎
|
||||
|
||||
@@ -27,7 +27,12 @@ static Example: FC<SomeProps> = |ctx, _props| {
|
||||
// toodo: derive this
|
||||
impl Properties for SomeProps {
|
||||
type Builder = SomePropsBuilder<((),)>;
|
||||
type StaticOutput = SomeProps;
|
||||
fn builder() -> Self::Builder {
|
||||
SomeProps::builder()
|
||||
}
|
||||
|
||||
unsafe fn into_static(self) -> Self {
|
||||
self
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,9 @@ pub type ScopeIdx = generational_arena::Index;
|
||||
|
||||
pub trait Properties: PartialEq {
|
||||
type Builder;
|
||||
type StaticOutput: Properties + 'static;
|
||||
fn builder() -> Self::Builder;
|
||||
unsafe fn into_static(self) -> Self::StaticOutput;
|
||||
}
|
||||
|
||||
pub struct EmptyBuilder;
|
||||
@@ -23,10 +25,15 @@ impl EmptyBuilder {
|
||||
|
||||
impl Properties for () {
|
||||
type Builder = EmptyBuilder;
|
||||
type StaticOutput = ();
|
||||
|
||||
fn builder() -> Self::Builder {
|
||||
EmptyBuilder {}
|
||||
}
|
||||
|
||||
unsafe fn into_static(self) -> Self::StaticOutput {
|
||||
std::mem::transmute(self)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fc_to_builder<T: Properties>(_f: FC<T>) -> T::Builder {
|
||||
|
||||
+135
-101
@@ -32,13 +32,18 @@
|
||||
//!
|
||||
//! More info on how to improve this diffing algorithm:
|
||||
//! - https://hacks.mozilla.org/2019/03/fast-bump-allocated-virtual-doms-with-rust-and-wasm/
|
||||
use crate::{
|
||||
innerlude::*,
|
||||
};
|
||||
use crate::{innerlude::*, scope::Scoped};
|
||||
use bumpalo::Bump;
|
||||
use fxhash::{FxHashMap, FxHashSet};
|
||||
use generational_arena::Arena;
|
||||
use uuid::Uuid;
|
||||
|
||||
use std::{cell::RefCell, cmp::Ordering, collections::VecDeque, rc::Rc};
|
||||
use std::{
|
||||
cell::{RefCell, RefMut},
|
||||
cmp::Ordering,
|
||||
collections::VecDeque,
|
||||
rc::Rc,
|
||||
};
|
||||
|
||||
/// The DiffState is a cursor internal to the VirtualDOM's diffing algorithm that allows persistence of state while
|
||||
/// diffing trees of components. This means we can "re-enter" a subtree of a component by queuing a "NeedToDiff" event.
|
||||
@@ -50,20 +55,29 @@ use std::{cell::RefCell, cmp::Ordering, collections::VecDeque, rc::Rc};
|
||||
/// different cursor position.
|
||||
///
|
||||
/// The order of these re-entrances is stored in the DiffState itself. The DiffState comes pre-loaded with a set of components
|
||||
/// that were modified by the eventtrigger. This prevents doubly evaluating components if they wereboth updated via
|
||||
/// that were modified by the eventtrigger. This prevents doubly evaluating components if they were both updated via
|
||||
/// subscriptions and props changes.
|
||||
pub struct DiffMachine<'a> {
|
||||
pub change_list: EditMachine<'a>,
|
||||
pub diffed: FxHashSet<ScopeIdx>,
|
||||
pub need_to_diff: FxHashSet<ScopeIdx>,
|
||||
pub lifecycle_events: VecDeque<LifeCycleEvent>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum LifeCycleEvent {
|
||||
Mount { caller: Caller, id: Uuid },
|
||||
PropsChanged,
|
||||
SameProps,
|
||||
Remove,
|
||||
}
|
||||
|
||||
impl<'a> DiffMachine<'a> {
|
||||
pub fn new(bump: &'a Bump) -> Self {
|
||||
pub fn new() -> Self {
|
||||
// pub fn new(bump: &'a Bump) -> Self {
|
||||
Self {
|
||||
change_list: EditMachine::new(bump),
|
||||
lifecycle_events: VecDeque::new(),
|
||||
change_list: EditMachine::new(),
|
||||
diffed: FxHashSet::default(),
|
||||
need_to_diff: FxHashSet::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,18 +125,31 @@ impl<'a> DiffMachine<'a> {
|
||||
self.diff_children(eold.children, enew.children);
|
||||
}
|
||||
|
||||
(VNode::Component(_cold), VNode::Component(_cnew)) => {
|
||||
// if cold.comp != cnew.comp {
|
||||
// // queue an event to mount this new component
|
||||
(VNode::Component(cold), VNode::Component(cnew)) => {
|
||||
todo!("should not happen")
|
||||
// if cold.caller_ref != cnew.caller_ref {
|
||||
// // todo: create a stable addr
|
||||
// self.lifecycle_events.push_back(LifeCycleEvent::Mount);
|
||||
// return;
|
||||
// }
|
||||
|
||||
// compare props.... somehow.....
|
||||
// let comparator = &cnew.comparator.0;
|
||||
// let old_props = cold.raw_props.as_ref();
|
||||
// let has_changed = comparator(old_props);
|
||||
|
||||
todo!("Usage of component VNode not currently supported");
|
||||
// if has_changed {
|
||||
// self.lifecycle_events
|
||||
// .push_back(LifeCycleEvent::PropsChanged);
|
||||
// return;
|
||||
// }
|
||||
|
||||
// the component is the same and hasn't changed
|
||||
// migrate props over (so the addr remains stable) but don't rerun the component
|
||||
}
|
||||
|
||||
// todo: knock out any listeners
|
||||
(_, VNode::Component(_new)) => {
|
||||
// self.lifecycle_events.push_back(LifeCycleEvent::Mount);
|
||||
// we have no stable reference to work from
|
||||
// push the lifecycle event onto the queue
|
||||
// self.lifecycle_events
|
||||
@@ -151,6 +178,100 @@ impl<'a> DiffMachine<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
// Emit instructions to create the given virtual node.
|
||||
//
|
||||
// The change list stack may have any shape upon entering this function:
|
||||
//
|
||||
// [...]
|
||||
//
|
||||
// When this function returns, the new node is on top of the change list stack:
|
||||
//
|
||||
// [... node]
|
||||
fn create(&mut self, node: &VNode<'a>) {
|
||||
debug_assert!(self.change_list.traversal_is_committed());
|
||||
match node {
|
||||
VNode::Text(VText { text }) => {
|
||||
self.change_list.create_text_node(text);
|
||||
}
|
||||
VNode::Element(&VElement {
|
||||
key: _,
|
||||
tag_name,
|
||||
listeners,
|
||||
attributes,
|
||||
children,
|
||||
namespace,
|
||||
}) => {
|
||||
// log::info!("Creating {:#?}", node);
|
||||
if let Some(namespace) = namespace {
|
||||
self.change_list.create_element_ns(tag_name, namespace);
|
||||
} else {
|
||||
self.change_list.create_element(tag_name);
|
||||
}
|
||||
|
||||
listeners.iter().enumerate().for_each(|(_id, listener)| {
|
||||
self.change_list
|
||||
.new_event_listener(listener.event, listener.scope, listener.id)
|
||||
});
|
||||
|
||||
for attr in attributes {
|
||||
self.change_list
|
||||
.set_attribute(&attr.name, &attr.value, namespace.is_some());
|
||||
}
|
||||
|
||||
// Fast path: if there is a single text child, it is faster to
|
||||
// create-and-append the text node all at once via setting the
|
||||
// parent's `textContent` in a single change list instruction than
|
||||
// to emit three instructions to (1) create a text node, (2) set its
|
||||
// text content, and finally (3) append the text node to this
|
||||
// parent.
|
||||
if children.len() == 1 {
|
||||
if let VNode::Text(VText { text }) = children[0] {
|
||||
self.change_list.set_text(text);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for child in children {
|
||||
self.create(child);
|
||||
self.change_list.append_child();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
todo: integrate re-entrace
|
||||
*/
|
||||
// NodeKind::Cached(ref c) => {
|
||||
// cached_roots.insert(c.id);
|
||||
// let (node, template) = cached_set.get(c.id);
|
||||
// if let Some(template) = template {
|
||||
// create_with_template(
|
||||
// cached_set,
|
||||
// self.change_list,
|
||||
// registry,
|
||||
// template,
|
||||
// node,
|
||||
// cached_roots,
|
||||
// );
|
||||
// } else {
|
||||
// create(cached_set, change_list, registry, node, cached_roots);
|
||||
// }
|
||||
// }
|
||||
VNode::Component(component) => {
|
||||
self.change_list
|
||||
.create_text_node("placeholder for vcomponent");
|
||||
let id = uuid::Uuid::new_v4();
|
||||
self.change_list.save_known_root(id);
|
||||
self.lifecycle_events.push_back(LifeCycleEvent::Mount {
|
||||
caller: component.caller.clone(),
|
||||
id,
|
||||
});
|
||||
}
|
||||
VNode::Suspended => {
|
||||
todo!("Creation of VNode::Suspended not yet supported")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Diff event listeners between `old` and `new`.
|
||||
//
|
||||
// The listeners' node must be on top of the change list stack:
|
||||
@@ -787,93 +908,6 @@ impl<'a> DiffMachine<'a> {
|
||||
// Support methods
|
||||
// ======================
|
||||
|
||||
// Emit instructions to create the given virtual node.
|
||||
//
|
||||
// The change list stack may have any shape upon entering this function:
|
||||
//
|
||||
// [...]
|
||||
//
|
||||
// When this function returns, the new node is on top of the change list stack:
|
||||
//
|
||||
// [... node]
|
||||
fn create(&mut self, node: &VNode<'a>) {
|
||||
debug_assert!(self.change_list.traversal_is_committed());
|
||||
match node {
|
||||
VNode::Text(VText { text }) => {
|
||||
self.change_list.create_text_node(text);
|
||||
}
|
||||
VNode::Element(&VElement {
|
||||
key: _,
|
||||
tag_name,
|
||||
listeners,
|
||||
attributes,
|
||||
children,
|
||||
namespace,
|
||||
}) => {
|
||||
// log::info!("Creating {:#?}", node);
|
||||
if let Some(namespace) = namespace {
|
||||
self.change_list.create_element_ns(tag_name, namespace);
|
||||
} else {
|
||||
self.change_list.create_element(tag_name);
|
||||
}
|
||||
|
||||
listeners.iter().enumerate().for_each(|(_id, listener)| {
|
||||
self.change_list
|
||||
.new_event_listener(listener.event, listener.scope, listener.id)
|
||||
});
|
||||
|
||||
for attr in attributes {
|
||||
self.change_list
|
||||
.set_attribute(&attr.name, &attr.value, namespace.is_some());
|
||||
}
|
||||
|
||||
// Fast path: if there is a single text child, it is faster to
|
||||
// create-and-append the text node all at once via setting the
|
||||
// parent's `textContent` in a single change list instruction than
|
||||
// to emit three instructions to (1) create a text node, (2) set its
|
||||
// text content, and finally (3) append the text node to this
|
||||
// parent.
|
||||
if children.len() == 1 {
|
||||
if let VNode::Text(VText { text }) = children[0] {
|
||||
self.change_list.set_text(text);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for child in children {
|
||||
self.create(child);
|
||||
self.change_list.append_child();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
todo: integrate re-entrace
|
||||
*/
|
||||
// NodeKind::Cached(ref c) => {
|
||||
// cached_roots.insert(c.id);
|
||||
// let (node, template) = cached_set.get(c.id);
|
||||
// if let Some(template) = template {
|
||||
// create_with_template(
|
||||
// cached_set,
|
||||
// self.change_list,
|
||||
// registry,
|
||||
// template,
|
||||
// node,
|
||||
// cached_roots,
|
||||
// );
|
||||
// } else {
|
||||
// create(cached_set, change_list, registry, node, cached_roots);
|
||||
// }
|
||||
// }
|
||||
VNode::Suspended => {
|
||||
todo!("Creation of VNode::Suspended not yet supported")
|
||||
}
|
||||
VNode::Component(_) => {
|
||||
todo!("Creation of VNode::Component not yet supported")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove all of a node's children.
|
||||
//
|
||||
// The change list stack must have this shape upon entry to this function:
|
||||
|
||||
@@ -10,7 +10,11 @@ pub use use_state_def::use_state;
|
||||
|
||||
mod use_state_def {
|
||||
use crate::innerlude::*;
|
||||
use std::{cell::RefCell, ops::DerefMut, rc::Rc};
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
ops::{Deref, DerefMut},
|
||||
rc::Rc,
|
||||
};
|
||||
|
||||
struct UseState<T: 'static> {
|
||||
new_val: Rc<RefCell<Option<T>>>,
|
||||
@@ -18,6 +22,22 @@ mod use_state_def {
|
||||
caller: Box<dyn Fn(T) + 'static>,
|
||||
}
|
||||
|
||||
struct UseStateHandle<'a, T> {
|
||||
inner: &'a T,
|
||||
}
|
||||
impl<'a, T> UseStateHandle<'a, T> {
|
||||
fn set(&self, new_val: T) {}
|
||||
fn modify(&self, f: impl FnOnce(&mut T)) {}
|
||||
}
|
||||
|
||||
impl<'a, T> Deref for UseStateHandle<'a, T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.inner
|
||||
}
|
||||
}
|
||||
|
||||
/// Store state between component renders!
|
||||
/// When called, this hook retrives a stored value and provides a setter to update that value.
|
||||
/// When the setter is called, the component is re-ran with the new value.
|
||||
@@ -175,9 +195,9 @@ mod use_reducer_def {
|
||||
|
||||
// #[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
|
||||
enum Actions {
|
||||
Incr,
|
||||
Decr,
|
||||
|
||||
@@ -91,14 +91,17 @@ pub(crate) mod innerlude {
|
||||
// pub(crate) use crate::component::Properties;
|
||||
pub(crate) use crate::component::Properties;
|
||||
pub(crate) use crate::context::Context;
|
||||
pub(crate) use crate::error::{Result};
|
||||
pub use crate::events::EventTrigger;
|
||||
pub use crate::diff::LifeCycleEvent;
|
||||
pub(crate) use crate::error::Result;
|
||||
pub use crate::events::{EventTrigger, VirtualEvent};
|
||||
use crate::nodes;
|
||||
|
||||
|
||||
pub(crate) use nodes::*;
|
||||
|
||||
pub use crate::component::ScopeIdx;
|
||||
pub use crate::context::hooks::Hook;
|
||||
pub use crate::nodes::VNode;
|
||||
|
||||
pub(crate) use nodes::*;
|
||||
|
||||
pub use crate::diff::DiffMachine;
|
||||
pub use crate::patch::{EditList, EditMachine};
|
||||
// pub use crate::patchdx;
|
||||
|
||||
@@ -1,18 +1,15 @@
|
||||
//! Helpers for building virtual DOM VNodes.
|
||||
|
||||
use std::{borrow::BorrowMut};
|
||||
use std::{borrow::BorrowMut, intrinsics::transmute};
|
||||
|
||||
use crate::{
|
||||
context::NodeCtx,
|
||||
events::VirtualEvent,
|
||||
innerlude::{VComponent, FC},
|
||||
innerlude::{Properties, VComponent, FC},
|
||||
nodes::{Attribute, Listener, NodeKey, VNode},
|
||||
prelude::VElement,
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
/// A virtual DOM element builder.
|
||||
///
|
||||
/// Typically constructed with element-specific constructors, eg the `div`
|
||||
@@ -526,12 +523,10 @@ pub fn attr<'a>(name: &'static str, value: &'a str) -> Attribute<'a> {
|
||||
|
||||
// _f: crate::innerlude::FC<T>,
|
||||
// _props: T
|
||||
pub fn virtual_child<'a, 'b, T: crate::innerlude::Properties>(
|
||||
_ctx: &'b NodeCtx<'a>,
|
||||
_f: FC<T>,
|
||||
_p: T,
|
||||
) -> VNode<'a> {
|
||||
// crate::nodes::VComponent
|
||||
todo!()
|
||||
// VNode::Component()
|
||||
pub fn virtual_child<'a, T: Properties>(ctx: &NodeCtx<'a>, f: FC<T>, p: T) -> VNode<'a> {
|
||||
// alloc props into the bump;
|
||||
|
||||
// erase the lifetime
|
||||
let p_static = unsafe { p.into_static() };
|
||||
VNode::Component(crate::nodes::VComponent::new(f, p_static))
|
||||
}
|
||||
+38
-25
@@ -10,12 +10,7 @@ use crate::{
|
||||
|
||||
use bumpalo::Bump;
|
||||
use std::fmt::Debug;
|
||||
use std::{
|
||||
any::{Any},
|
||||
cell::RefCell,
|
||||
marker::PhantomData,
|
||||
rc::Rc,
|
||||
};
|
||||
use std::{any::Any, cell::RefCell, marker::PhantomData, rc::Rc};
|
||||
|
||||
/// A domtree represents the result of "Viewing" the context
|
||||
/// It's a placeholder over vnodes, to make working with lifetimes easier
|
||||
@@ -271,21 +266,28 @@ impl<'a> VText<'a> {
|
||||
/// Only supports the functional syntax
|
||||
pub type StableScopeAddres = Rc<RefCell<Option<usize>>>;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct VComponent<'src> {
|
||||
pub stable_addr: StableScopeAddres,
|
||||
pub raw_props: Rc<dyn Any>,
|
||||
pub comparator: Comparator,
|
||||
pub caller: Caller,
|
||||
pub caller_ref: *const (),
|
||||
_p: PhantomData<&'src ()>,
|
||||
}
|
||||
pub struct Comparator(Box<dyn Fn(&dyn Any) -> bool>);
|
||||
pub trait PropsComparator {}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Comparator(pub Rc<dyn Fn(&dyn PropsComparator) -> bool>);
|
||||
// pub struct Comparator(pub Rc<dyn Fn(&dyn Any) -> bool>);
|
||||
impl std::fmt::Debug for Comparator {
|
||||
fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Caller(Box<dyn Fn(Context) -> DomTree>);
|
||||
#[derive(Clone)]
|
||||
pub struct Caller(pub Rc<dyn Fn(Context) -> DomTree>);
|
||||
impl std::fmt::Debug for Caller {
|
||||
fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
Ok(())
|
||||
@@ -298,30 +300,41 @@ impl<'a> VComponent<'a> {
|
||||
// - perform comparisons when diffing (memoization)
|
||||
// TODO: lift the requirement that props need to be static
|
||||
// we want them to borrow references... maybe force implementing a "to_static_unsafe" trait
|
||||
pub fn new<P: Properties + 'static>(caller: FC<P>, props: P) -> Self {
|
||||
pub fn new<P0: Properties, P: Properties + 'static>(caller: FC<P0>, props: P) -> Self {
|
||||
let caller_ref = caller as *const ();
|
||||
|
||||
let props = Rc::new(props);
|
||||
|
||||
// used for memoization
|
||||
let p1 = props.clone();
|
||||
let props_comparator = move |new_props: &dyn Any| -> bool {
|
||||
new_props
|
||||
.downcast_ref::<P>()
|
||||
.map(|new_p| p1.as_ref() == new_p)
|
||||
.unwrap_or_else(|| {
|
||||
log::debug!("downcasting failed, this receovered but SHOULD NOT FAIL");
|
||||
false
|
||||
})
|
||||
let props_comparator = move |new_props: ()| -> bool {
|
||||
false
|
||||
// let props_comparator = move |new_props: &dyn Any| -> bool {
|
||||
// new_props
|
||||
// .downcast_ref::<P>()
|
||||
// .map(|new_p| p1.as_ref() == new_p)
|
||||
// .unwrap_or_else(|| {
|
||||
// log::debug!("downcasting failed, this receovered but SHOULD NOT FAIL");
|
||||
// false
|
||||
// })
|
||||
};
|
||||
|
||||
// used for actually rendering the custom component
|
||||
let p2 = props.clone();
|
||||
let caller = move |ctx: Context| -> DomTree { caller(ctx, p2.as_ref()) };
|
||||
let caller = move |ctx: Context| -> DomTree {
|
||||
//
|
||||
// cast back into the right lifetime
|
||||
caller(ctx, p2.as_ref())
|
||||
};
|
||||
|
||||
Self {
|
||||
_p: PhantomData,
|
||||
comparator: Comparator(Box::new(props_comparator)),
|
||||
caller: Caller(Box::new(caller)),
|
||||
stable_addr: Rc::new(RefCell::new(None)),
|
||||
}
|
||||
todo!()
|
||||
// Self {
|
||||
// caller_ref,
|
||||
// raw_props: props,
|
||||
// _p: PhantomData,
|
||||
// comparator: Comparator(Rc::new(props_comparator)),
|
||||
// caller: Caller(Rc::new(caller)),
|
||||
// stable_addr: Rc::new(RefCell::new(None)),
|
||||
// }
|
||||
}
|
||||
}
|
||||
@@ -83,11 +83,13 @@ pub enum Edit<'d> {
|
||||
// ========================================================
|
||||
// push a known node on to the stack
|
||||
TraverseToKnown {
|
||||
node: ScopeIdx,
|
||||
node: uuid::Uuid,
|
||||
// node: ScopeIdx,
|
||||
},
|
||||
// Add the current top of the stack to the known nodes
|
||||
MakeKnown {
|
||||
node: ScopeIdx,
|
||||
node: uuid::Uuid,
|
||||
// node: ScopeIdx,
|
||||
},
|
||||
// Remove the current top of the stack from the known nodes
|
||||
RemoveKnown,
|
||||
@@ -126,18 +128,18 @@ pub struct EditMachine<'src> {
|
||||
pub traversal: Traversal,
|
||||
next_temporary: u32,
|
||||
forcing_new_listeners: bool,
|
||||
|
||||
pub emitter: EditList<'src>,
|
||||
}
|
||||
|
||||
impl<'b> EditMachine<'b> {
|
||||
pub fn new(_bump: &'b bumpalo::Bump) -> Self {
|
||||
pub fn new() -> Self {
|
||||
// pub fn new(_bump: &'b bumpalo::Bump) -> Self {
|
||||
// todo: see if bumpalo is needed for edit list
|
||||
Self {
|
||||
traversal: Traversal::new(),
|
||||
next_temporary: 0,
|
||||
forcing_new_listeners: false,
|
||||
emitter: EditList::default(),
|
||||
emitter: EditList::<'b>::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -364,11 +366,18 @@ impl<'a> EditMachine<'a> {
|
||||
// debug!("emit: remove_event_listener({:?})", event);
|
||||
}
|
||||
|
||||
pub fn save_known_root(&mut self, id: ScopeIdx) {
|
||||
pub fn save_known_root(&mut self, id: uuid::Uuid) {
|
||||
// pub fn save_known_root(&mut self, id: ScopeIdx) {
|
||||
// pub fn save_known_root(&mut self, id: ScopeIdx) {
|
||||
log::debug!("emit: save_known_root({:?})", id);
|
||||
self.emitter.push(Edit::MakeKnown { node: id })
|
||||
}
|
||||
|
||||
pub fn load_known_root(&mut self, id: uuid::Uuid) {
|
||||
log::debug!("emit: TraverseToKnown({:?})", id);
|
||||
self.emitter.push(Edit::TraverseToKnown { node: id })
|
||||
}
|
||||
|
||||
// pub fn save_template(&mut self, id: CacheId) {
|
||||
// debug_assert!(self.traversal_is_committed());
|
||||
// debug_assert!(!self.has_template(id));
|
||||
|
||||
+45
-50
@@ -1,21 +1,12 @@
|
||||
use crate::component::ScopeIdx;
|
||||
use crate::context::hooks::Hook;
|
||||
use crate::innerlude::*;
|
||||
use crate::nodes::VNode;
|
||||
use bumpalo::Bump;
|
||||
|
||||
use std::{
|
||||
any::{Any},
|
||||
cell::RefCell,
|
||||
marker::PhantomData,
|
||||
ops::Deref,
|
||||
};
|
||||
use std::{any::Any, cell::RefCell, collections::HashSet, marker::PhantomData, ops::Deref};
|
||||
|
||||
pub trait Scoped {
|
||||
fn run(&mut self);
|
||||
fn compare_props(&self, new: &dyn std::any::Any) -> bool;
|
||||
// fn compare_props(&self, new: &dyn std::any::Any) -> bool;
|
||||
fn call_listener(&mut self, trigger: EventTrigger);
|
||||
|
||||
fn new_frame<'bump>(&'bump self) -> &'bump VNode<'bump>;
|
||||
fn old_frame<'bump>(&'bump self) -> &'bump VNode<'bump>;
|
||||
}
|
||||
@@ -33,11 +24,16 @@ pub struct Scope<P: Properties> {
|
||||
// our own index
|
||||
pub myidx: ScopeIdx,
|
||||
|
||||
// the props
|
||||
pub props: P,
|
||||
//
|
||||
pub children: HashSet<ScopeIdx>,
|
||||
|
||||
// // the props
|
||||
// pub props: P,
|
||||
|
||||
// and the actual render function
|
||||
pub caller: FC<P>,
|
||||
pub caller: Caller,
|
||||
_p: std::marker::PhantomData<P>,
|
||||
// pub raw_caller: FC<P>,
|
||||
|
||||
// ==========================
|
||||
// slightly unsafe stuff
|
||||
@@ -56,43 +52,31 @@ pub struct Scope<P: Properties> {
|
||||
// - is self-refenrential and therefore needs to point into the bump
|
||||
// Stores references into the listeners attached to the vnodes
|
||||
// NEEDS TO BE PRIVATE
|
||||
listeners: RefCell<Vec<*const dyn Fn(crate::events::VirtualEvent)>>,
|
||||
listeners: RefCell<Vec<*const dyn Fn(VirtualEvent)>>,
|
||||
}
|
||||
|
||||
// instead of having it as a trait method, we use a single function
|
||||
// todo: do the unsafety magic stuff to erase the type of p
|
||||
pub fn create_scoped<P: Properties + 'static>(
|
||||
caller: FC<P>,
|
||||
props: P,
|
||||
pub fn create_scoped(
|
||||
// raw_f: FC<P>,
|
||||
caller: Caller,
|
||||
|
||||
// props: P,
|
||||
myidx: ScopeIdx,
|
||||
parent: Option<ScopeIdx>,
|
||||
) -> Box<dyn Scoped> {
|
||||
let hook_arena = typed_arena::Arena::new();
|
||||
let hooks = RefCell::new(Vec::new());
|
||||
|
||||
let listeners = Default::default();
|
||||
|
||||
let old_frame = BumpFrame {
|
||||
bump: Bump::new(),
|
||||
head_node: VNode::text(""),
|
||||
};
|
||||
|
||||
let new_frame = BumpFrame {
|
||||
bump: Bump::new(),
|
||||
head_node: VNode::text(""),
|
||||
};
|
||||
|
||||
let frames = ActiveFrame::from_frames(old_frame, new_frame);
|
||||
|
||||
Box::new(Scope {
|
||||
Box::new(Scope::<()> {
|
||||
// raw_caller: raw_f,
|
||||
_p: Default::default(),
|
||||
caller,
|
||||
myidx,
|
||||
hook_arena,
|
||||
hooks,
|
||||
frames,
|
||||
listeners,
|
||||
hook_arena: typed_arena::Arena::new(),
|
||||
hooks: RefCell::new(Vec::new()),
|
||||
frames: ActiveFrame::new(),
|
||||
children: HashSet::new(),
|
||||
listeners: Default::default(),
|
||||
parent,
|
||||
props,
|
||||
// props,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -122,9 +106,8 @@ impl<P: Properties + 'static> Scoped for Scope<P> {
|
||||
};
|
||||
|
||||
// Note that the actual modification of the vnode head element occurs during this call
|
||||
// let _: DomTree = caller(ctx, props);
|
||||
// let _: DomTree = P::render (ctx, &self.props);
|
||||
let _: DomTree = (self.caller)(ctx, &self.props);
|
||||
let _: DomTree = (self.caller.0.as_ref())(ctx);
|
||||
// let _: DomTree = (self.raw_caller)(ctx, &self.props);
|
||||
|
||||
/*
|
||||
SAFETY ALERT
|
||||
@@ -145,11 +128,11 @@ impl<P: Properties + 'static> Scoped for Scope<P> {
|
||||
.expect("Viewing did not happen");
|
||||
}
|
||||
|
||||
fn compare_props(&self, new: &dyn Any) -> bool {
|
||||
new.downcast_ref::<P>()
|
||||
.map(|f| &self.props == f)
|
||||
.expect("Props should not be of a different type")
|
||||
}
|
||||
// fn compare_props(&self, new: &dyn Any) -> bool {
|
||||
// new.downcast_ref::<P>()
|
||||
// .map(|f| &self.props == f)
|
||||
// .expect("Props should not be of a different type")
|
||||
// }
|
||||
|
||||
// A safe wrapper around calling listeners
|
||||
// calling listeners will invalidate the list of listeners
|
||||
@@ -194,7 +177,6 @@ impl<P: Properties + 'static> Scoped for Scope<P> {
|
||||
// ==========================
|
||||
// Active-frame related code
|
||||
// ==========================
|
||||
|
||||
// todo, do better with the active frame stuff
|
||||
// somehow build this vnode with a lifetime tied to self
|
||||
// This root node has "static" lifetime, but it's really not static.
|
||||
@@ -212,6 +194,19 @@ pub struct BumpFrame {
|
||||
}
|
||||
|
||||
impl ActiveFrame {
|
||||
pub fn new() -> Self {
|
||||
Self::from_frames(
|
||||
BumpFrame {
|
||||
bump: Bump::new(),
|
||||
head_node: VNode::text(""),
|
||||
},
|
||||
BumpFrame {
|
||||
bump: Bump::new(),
|
||||
head_node: VNode::text(""),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn from_frames(a: BumpFrame, b: BumpFrame) -> Self {
|
||||
Self {
|
||||
idx: 0.into(),
|
||||
|
||||
@@ -7,7 +7,7 @@ use crate::{
|
||||
};
|
||||
use bumpalo::Bump;
|
||||
use generational_arena::Arena;
|
||||
|
||||
use std::{any::TypeId, cell::RefCell, rc::Rc};
|
||||
|
||||
/// An integrated virtual node system that progresses events and diffs UI trees.
|
||||
/// Differences are converted into patches which a renderer can use to draw the UI.
|
||||
@@ -17,8 +17,9 @@ pub struct VirtualDom {
|
||||
///
|
||||
/// eventually, come up with a better datastructure that reuses boxes for known P types
|
||||
/// like a generational typemap bump arena
|
||||
/// -> IE a cache line for each P type with soem heuristics on optimizing layout
|
||||
/// -> IE a cache line for each P type with some heuristics on optimizing layout
|
||||
pub(crate) components: Arena<Box<dyn Scoped>>,
|
||||
// pub(crate) components: RefCell<Arena<Box<dyn Scoped>>>,
|
||||
// pub(crate) components: Rc<RefCell<Arena<Box<dyn Scoped>>>>,
|
||||
/// The index of the root component.
|
||||
/// Will not be ready if the dom is fresh
|
||||
@@ -59,53 +60,87 @@ impl VirtualDom {
|
||||
///
|
||||
/// This is useful when a component tree can be driven by external state (IE SSR) but it would be too expensive
|
||||
/// to toss out the entire tree.
|
||||
pub fn new_with_props<P: Properties + 'static>(_root: FC<P>, _root_props: P) -> Self {
|
||||
// let mut components = Arena::new();
|
||||
// let mut components = Arena::new();
|
||||
pub fn new_with_props<P: Properties + 'static>(root: FC<P>, root_props: P) -> Self {
|
||||
let mut components = Arena::new();
|
||||
|
||||
// Create a reference to the component in the arena
|
||||
// Note: we are essentially running the "Mount" lifecycle event manually while the vdom doesnt yet exist
|
||||
// This puts the dom in a usable state on creation, rather than being potentially invalid
|
||||
let caller = Caller(Rc::new(move |ctx| {
|
||||
//
|
||||
root(ctx, &root_props)
|
||||
// DomTree {}
|
||||
}));
|
||||
let base_scope = components.insert_with(|id| create_scoped(caller, id, None));
|
||||
// let base_scope = components.insert_with(|id| create_scoped(root, root_props, id, None));
|
||||
|
||||
todo!()
|
||||
// Self {
|
||||
// // components: RefCell::new(components),
|
||||
// components: components,
|
||||
// // components: Rc::new(RefCell::new(components)),
|
||||
// base_scope,
|
||||
// // event_queue: RefCell::new(VecDeque::new()),
|
||||
// diff_bump: Bump::new(),
|
||||
// _root_prop_type: TypeId::of::<P>(),
|
||||
// }
|
||||
Self {
|
||||
components,
|
||||
base_scope,
|
||||
diff_bump: Bump::new(),
|
||||
_root_prop_type: TypeId::of::<P>(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Performs a *full* rebuild of the virtual dom, returning every edit required to generate the actual dom.
|
||||
|
||||
// pub fn rebuild<'s>(&'s mut self) -> Result<> {
|
||||
// pub fn rebuild<'s>(&'s mut self) -> Result<std::cell::Ref<'_, Arena<Box<dyn Scoped>>>> {
|
||||
pub fn rebuild<'s>(&'s mut self) -> Result<EditList<'s>> {
|
||||
log::debug!("rebuilding...");
|
||||
// Reset and then build a new diff machine
|
||||
// The previous edit list cannot be around while &mut is held
|
||||
// Make sure variance doesnt break this
|
||||
self.diff_bump.reset();
|
||||
// bump.reset();
|
||||
|
||||
self.components
|
||||
.get_mut(self.base_scope)
|
||||
.expect("Root should always exist")
|
||||
.run();
|
||||
// Diff from the top
|
||||
let mut diff_machine = DiffMachine::new(); // partial borrow
|
||||
{
|
||||
let component = self
|
||||
.components
|
||||
.get_mut(self.base_scope)
|
||||
.expect("failed to acquire base scope");
|
||||
|
||||
let _b = Bump::new();
|
||||
component.run();
|
||||
}
|
||||
|
||||
let mut diff_machine = DiffMachine::new(&self.diff_bump);
|
||||
// let mut diff_machine = DiffMachine::new(self, &self.diff_bump, self.base_scope);
|
||||
{
|
||||
let component = self
|
||||
.components
|
||||
.get(self.base_scope)
|
||||
.expect("failed to acquire base scope");
|
||||
|
||||
// todo!()
|
||||
diff_machine.diff_node(component.old_frame(), component.new_frame());
|
||||
}
|
||||
|
||||
let component = self.components.get(self.base_scope).unwrap();
|
||||
diff_machine.diff_node(component.old_frame(), component.new_frame());
|
||||
let edits = diff_machine.consume();
|
||||
// self.diff_bump = b;
|
||||
// 'render: loop {
|
||||
// for event in &mut diff_machine.lifecycle_events.drain(..) {
|
||||
// log::debug!("event is {:#?}", event);
|
||||
// match event {
|
||||
// LifeCycleEvent::Mount { caller, id } => {
|
||||
// diff_machine.change_list.load_known_root(id);
|
||||
// let idx = self
|
||||
// .components
|
||||
// .insert_with(|f| create_scoped(caller, f, None));
|
||||
// // .insert_with(|f| create_scoped(caller, props, myidx, parent));
|
||||
// }
|
||||
// LifeCycleEvent::PropsChanged => {
|
||||
// //
|
||||
// break 'render;
|
||||
// }
|
||||
// LifeCycleEvent::SameProps => {
|
||||
// //
|
||||
// break 'render;
|
||||
// }
|
||||
// LifeCycleEvent::Remove => {
|
||||
// //
|
||||
// break 'render;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// if diff_machine.lifecycle_events.is_empty() {
|
||||
// break 'render;
|
||||
// }
|
||||
// }
|
||||
|
||||
let edits: Vec<Edit<'s>> = diff_machine.consume();
|
||||
Ok(edits)
|
||||
}
|
||||
|
||||
@@ -133,50 +168,20 @@ impl VirtualDom {
|
||||
/// }
|
||||
///
|
||||
/// ```
|
||||
pub fn progress_with_event(&mut self, _event: EventTrigger) -> Result<EditList<'_>> {
|
||||
// self.components
|
||||
// .borrow_mut()
|
||||
// .get_mut(event.component_id)
|
||||
// .map(|f| {
|
||||
// f.call_listener(event);
|
||||
// f
|
||||
// })
|
||||
// .map(|f| f.run())
|
||||
// .expect("Borrowing should not fail");
|
||||
pub fn progress_with_event(&mut self, event: EventTrigger) -> Result<EditList<'_>> {
|
||||
let component = self
|
||||
.components
|
||||
.get_mut(event.component_id)
|
||||
.expect("Borrowing should not fail");
|
||||
|
||||
// component.call_listener(event);
|
||||
component.call_listener(event);
|
||||
component.run();
|
||||
|
||||
// .expect("Component should exist if an event was triggered");
|
||||
// Reset and then build a new diff machine
|
||||
// The previous edit list cannot be around while &mut is held
|
||||
// Make sure variance doesnt break this
|
||||
// self.diff_bump.reset();
|
||||
// let mut diff_machine = DiffMachine::new(&mut self, event.component_id);
|
||||
// let mut diff_machine =
|
||||
// DiffMachine::new(&self.diff_bump, &mut self.components, event.component_id);
|
||||
let mut diff_machine = DiffMachine::new();
|
||||
// let mut diff_machine = DiffMachine::new(&self.diff_bump);
|
||||
|
||||
// component.run();
|
||||
// diff_machine.diff_node(component.old_frame(), component.new_frame());
|
||||
diff_machine.diff_node(component.old_frame(), component.new_frame());
|
||||
|
||||
todo!()
|
||||
// Ok(diff_machine.consume())
|
||||
// Err(crate::error::Error::NoEvent)
|
||||
// Mark dirty components. Descend from the highest node until all dirty nodes are updated.
|
||||
// let mut affected_components = Vec::new();
|
||||
|
||||
// while let Some(event) = self.pop_event() {
|
||||
// if let Some(component_idx) = event.index() {
|
||||
// affected_components.push(component_idx);
|
||||
// }
|
||||
// self.process_lifecycle(event)?;
|
||||
// }
|
||||
|
||||
// todo!()
|
||||
Ok(diff_machine.consume())
|
||||
}
|
||||
}
|
||||
|
||||
enum LifeCycleEvent {
|
||||
Mount {},
|
||||
}
|
||||
|
||||
// todo: add some "handle" to the vdom. Or a way of giving out closures that can mutate the vdoms internal data.
|
||||
@@ -13,26 +13,27 @@ fn main() {
|
||||
}
|
||||
|
||||
static App: FC<()> = |ctx, _| {
|
||||
log::info!("Ran component");
|
||||
use dioxus::builder::*;
|
||||
todo!()
|
||||
// ctx.render(|b| {
|
||||
// div(b)
|
||||
// .child(text("hello"))
|
||||
// .listeners([on(b, "click", |_| {
|
||||
// //
|
||||
// log::info!("button1 clicked!");
|
||||
// })])
|
||||
// .finish()
|
||||
// })
|
||||
// ctx.render(html! {
|
||||
// <div onclick={move |_| log::info!("button1 clicked!")}>
|
||||
// "Hello"
|
||||
// // <div class="flex items-center justify-center flex-col">
|
||||
// // <div class="font-bold text-xl"> "Count is ..." </div>
|
||||
// // <button onclick={move |_| log::info!("button1 clicked!")}> "increment" </button>
|
||||
// // <button onclick={move |_| log::info!("button2 clicked!")}> "decrement" </button>
|
||||
// // </div>
|
||||
// </div>
|
||||
// })
|
||||
ctx.render(rsx! {
|
||||
div {
|
||||
h1 {"hello"}
|
||||
C1 {}
|
||||
C2 {}
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
static C1: FC<()> = |ctx, props| {
|
||||
ctx.render(rsx! {
|
||||
button {
|
||||
"numba 1"
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
static C2: FC<()> = |ctx, props| {
|
||||
ctx.render(rsx! {
|
||||
button {
|
||||
"numba 2"
|
||||
}
|
||||
})
|
||||
};
|
||||
@@ -6,6 +6,7 @@ fn main() {
|
||||
wasm_logger::init(wasm_logger::Config::new(log::Level::Debug));
|
||||
console_error_panic_hook::set_once();
|
||||
|
||||
log::info!("hello world");
|
||||
wasm_bindgen_futures::spawn_local(WebsysRenderer::start(Example));
|
||||
}
|
||||
|
||||
@@ -18,6 +19,8 @@ static Example: FC<()> = |ctx, _props| {
|
||||
set_event(Some(evt));
|
||||
};
|
||||
|
||||
log::info!("hello world");
|
||||
|
||||
ctx.render(rsx! {
|
||||
div {
|
||||
|
||||
@@ -35,21 +38,35 @@ static Example: FC<()> = |ctx, _props| {
|
||||
id: "json"
|
||||
"{event}"
|
||||
}
|
||||
Example2 { name: "{event}" }
|
||||
// Example2 { name: "{event}".to_string() }
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
struct Props {
|
||||
#[derive(Debug, PartialEq, Props)]
|
||||
struct ExampleProps {
|
||||
name: String
|
||||
}
|
||||
|
||||
static Example2: FC<Props> = |ctx, props| {
|
||||
static Example2: FC<ExampleProps> = |ctx, props| {
|
||||
ctx.render(rsx!{
|
||||
div {
|
||||
h1 {"hello {props.name}"}
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
|
||||
impl Properties for ExampleProps {
|
||||
type Builder = ExamplePropsBuilder<((),)>;
|
||||
type StaticOutput = ExampleProps;
|
||||
fn builder() -> Self::Builder {
|
||||
ExampleProps::builder()
|
||||
}
|
||||
|
||||
|
||||
unsafe fn into_static(self) -> Self::StaticOutput {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,17 @@
|
||||
use bumpalo::Bump;
|
||||
use dioxus::{events::EventTrigger, prelude::*};
|
||||
#![allow(non_snake_case)]
|
||||
use dioxus_core as dioxus;
|
||||
use dioxus::prelude::*;
|
||||
use dioxus_web::WebsysRenderer;
|
||||
|
||||
// creates a proxy address that gets transpiled at compile time
|
||||
// all asset ids are shuttled through the renderer
|
||||
// static IMG: ImgAsset = dioxus_asset!("");
|
||||
|
||||
fn main() {
|
||||
// wasm_logger::init(wasm_logger::Config::new(log::Level::Debug));
|
||||
// console_error_panic_hook::set_once();
|
||||
wasm_logger::init(wasm_logger::Config::new(log::Level::Trace));
|
||||
console_error_panic_hook::set_once();
|
||||
wasm_bindgen_futures::spawn_local(WebsysRenderer::start(Example))
|
||||
}
|
||||
|
||||
static Example: FC<()> = |ctx, props| {
|
||||
static Example: FC<()> = |ctx, _props| {
|
||||
let (name, set_name) = use_state(&ctx, || "...?");
|
||||
|
||||
|
||||
|
||||
ctx.render(rsx! {
|
||||
div {
|
||||
class: "py-12 px-4 text-center w-full max-w-2xl mx-auto"
|
||||
@@ -47,9 +41,3 @@ static Example: FC<()> = |ctx, props| {
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// onclick: {move |_| set_name("jill")}
|
||||
@@ -2,6 +2,7 @@
|
||||
//! --------------
|
||||
//! This crate implements a renderer of the Dioxus Virtual DOM for the web browser using Websys.
|
||||
|
||||
use dioxus::prelude::Properties;
|
||||
use fxhash::FxHashMap;
|
||||
use web_sys::{window, Document, Element, Event, Node};
|
||||
|
||||
@@ -48,7 +49,7 @@ impl WebsysRenderer {
|
||||
/// Automatically progresses the creation of the VNode tree to completion.
|
||||
///
|
||||
/// A VDom is automatically created. If you want more granular control of the VDom, use `from_vdom`
|
||||
pub fn new_with_props<T: 'static>(root: FC<T>, root_props: T) -> Self {
|
||||
pub fn new_with_props<T: Properties + 'static>(root: FC<T>, root_props: T) -> Self {
|
||||
Self::from_vdom(VirtualDom::new_with_props(root, root_props))
|
||||
}
|
||||
|
||||
@@ -59,7 +60,7 @@ impl WebsysRenderer {
|
||||
}
|
||||
|
||||
pub async fn run(&mut self) -> dioxus_core::error::Result<()> {
|
||||
let (mut sender, mut receiver) = mpsc::unbounded::<EventTrigger>();
|
||||
let (sender, mut receiver) = mpsc::unbounded::<EventTrigger>();
|
||||
|
||||
let body_element = prepare_websys_dom();
|
||||
|
||||
@@ -75,8 +76,10 @@ impl WebsysRenderer {
|
||||
|
||||
// todo: initialize the event registry properly on the root
|
||||
|
||||
self.internal_dom.rebuild()?.iter().for_each(|edit| {
|
||||
// log::debug!("patching with {:?}", edit);
|
||||
let edits = self.internal_dom.rebuild()?;
|
||||
log::debug!("Received edits: {:#?}", edits);
|
||||
edits.iter().for_each(|edit| {
|
||||
log::debug!("patching with {:?}", edit);
|
||||
patch_machine.handle_edit(edit);
|
||||
});
|
||||
// log::debug!("patch stack size {:?}", patch_machine.stack);
|
||||
@@ -92,7 +95,7 @@ impl WebsysRenderer {
|
||||
.progress_with_event(event)?
|
||||
.iter()
|
||||
.for_each(|edit| {
|
||||
// log::debug!("edit stream {:?}", edit);
|
||||
log::debug!("edit stream {:?}", edit);
|
||||
patch_machine.handle_edit(edit);
|
||||
});
|
||||
|
||||
|
||||
Reference in new issue
Block a user