wip: remove portals completely
This commit is contained in:
1 parent
8daf7a6ed8
commit
2fd56e7619
17 files changed
+259
-404
No files matched your search
@@ -0,0 +1,16 @@
|
|||||||
|
use dioxus::prelude::*;
|
||||||
|
use dioxus_core as dioxus;
|
||||||
|
use dioxus_core_macro::*;
|
||||||
|
use dioxus_hooks::use_state;
|
||||||
|
use dioxus_html as dioxus_elements;
|
||||||
|
|
||||||
|
fn main() {}
|
||||||
|
|
||||||
|
fn App(cx: Scope<()>) -> Element {
|
||||||
|
let color = use_state(&cx, || "white");
|
||||||
|
|
||||||
|
cx.render(rsx!(
|
||||||
|
div { onclick: move |_| color.set("red"), "red" }
|
||||||
|
div { onclick: move |_| color.set("blue"), "blue" }
|
||||||
|
))
|
||||||
|
}
|
||||||
@@ -20,7 +20,7 @@ use dioxus_html as dioxus_elements;
|
|||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
static App: Component<()> = |cx, _| {
|
static App: Component<()> = |cx| {
|
||||||
let mut rng = SmallRng::from_entropy();
|
let mut rng = SmallRng::from_entropy();
|
||||||
let rows = (0..10_000_usize).map(|f| {
|
let rows = (0..10_000_usize).map(|f| {
|
||||||
let label = Label::new(&mut rng);
|
let label = Label::new(&mut rng);
|
||||||
@@ -50,11 +50,11 @@ struct RowProps {
|
|||||||
row_id: usize,
|
row_id: usize,
|
||||||
label: Label,
|
label: Label,
|
||||||
}
|
}
|
||||||
fn Row(cx: Scope, props: &RowProps) -> Element {
|
fn Row(cx: Scope<RowProps>) -> Element {
|
||||||
let [adj, col, noun] = props.label.0;
|
let [adj, col, noun] = cx.props.label.0;
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
tr {
|
tr {
|
||||||
td { class:"col-md-1", "{props.row_id}" }
|
td { class:"col-md-1", "{cx.props.row_id}" }
|
||||||
td { class:"col-md-1", onclick: move |_| { /* run onselect */ }
|
td { class:"col-md-1", onclick: move |_| { /* run onselect */ }
|
||||||
a { class: "lbl", "{adj}" "{col}" "{noun}" }
|
a { class: "lbl", "{adj}" "{col}" "{noun}" }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ fn main() {
|
|||||||
let _ = VirtualDom::new(Parent);
|
let _ = VirtualDom::new(Parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn Parent(cx: Scope, _props: &()) -> Element {
|
fn Parent(cx: Scope<()>) -> Element {
|
||||||
let value = cx.use_hook(|_| String::new(), |f| f);
|
let value = cx.use_hook(|_| String::new(), |f| f);
|
||||||
|
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
@@ -25,11 +25,11 @@ struct ChildProps<'a> {
|
|||||||
name: &'a str,
|
name: &'a str,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn Child(cx: Scope, props: &ChildProps) -> Element {
|
fn Child<'a>(cx: Scope<'a, ChildProps<'a>>) -> Element {
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
div {
|
div {
|
||||||
h1 { "it's nested" }
|
h1 { "it's nested" }
|
||||||
Child2 { name: props.name }
|
Child2 { name: cx.props.name }
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -39,8 +39,8 @@ struct Grandchild<'a> {
|
|||||||
name: &'a str,
|
name: &'a str,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn Child2(cx: Scope, props: &Grandchild) -> Element {
|
fn Child2<'a>(cx: Scope<'a, Grandchild<'a>>) -> Element {
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
div { "Hello {props.name}!" }
|
div { "Hello {cx.props.name}!" }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
//! if the type supports PartialEq. The Properties trait is used by the rsx! and html! macros to generate the type-safe builder
|
//! if the type supports PartialEq. The Properties trait is used by the rsx! and html! macros to generate the type-safe builder
|
||||||
//! that ensures compile-time required and optional fields on cx.
|
//! that ensures compile-time required and optional fields on cx.
|
||||||
|
|
||||||
use crate::innerlude::{Element, LazyNodes, Scope, VPortal};
|
use crate::innerlude::{Element, LazyNodes, Scope};
|
||||||
|
|
||||||
pub struct FragmentProps<'a>(Element<'a>);
|
pub struct FragmentProps<'a>(Element<'a>);
|
||||||
pub struct FragmentBuilder<'a, const BUILT: bool>(Element<'a>);
|
pub struct FragmentBuilder<'a, const BUILT: bool>(Element<'a>);
|
||||||
|
|||||||
+22
-71
@@ -239,10 +239,10 @@ impl<'bump> DiffStack<'bump> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'bump> DiffState<'bump> {
|
impl<'bump> DiffState<'bump> {
|
||||||
pub fn diff_scope(&mut self, id: &ScopeId) {
|
pub fn diff_scope(&mut self, id: ScopeId) {
|
||||||
let (old, new) = (self.scopes.wip_head(id), self.scopes.fin_head(id));
|
let (old, new) = (self.scopes.wip_head(id), self.scopes.fin_head(id));
|
||||||
self.stack.push(DiffInstruction::Diff { old, new });
|
self.stack.push(DiffInstruction::Diff { old, new });
|
||||||
self.stack.scope_stack.push(*id);
|
self.stack.scope_stack.push(id);
|
||||||
let scope = self.scopes.get_scope(id).unwrap();
|
let scope = self.scopes.get_scope(id).unwrap();
|
||||||
self.stack.element_stack.push(scope.container);
|
self.stack.element_stack.push(scope.container);
|
||||||
self.work(|| false);
|
self.work(|| false);
|
||||||
@@ -288,12 +288,6 @@ impl<'bump> DiffState<'bump> {
|
|||||||
1
|
1
|
||||||
}
|
}
|
||||||
|
|
||||||
VNode::Portal(linked) => {
|
|
||||||
let node = unsafe { &*linked.node };
|
|
||||||
let node: &VNode = unsafe { std::mem::transmute(node) };
|
|
||||||
self.push_all_nodes(node)
|
|
||||||
}
|
|
||||||
|
|
||||||
VNode::Fragment(_) | VNode::Component(_) => {
|
VNode::Fragment(_) | VNode::Component(_) => {
|
||||||
//
|
//
|
||||||
let mut added = 0;
|
let mut added = 0;
|
||||||
@@ -357,7 +351,6 @@ impl<'bump> DiffState<'bump> {
|
|||||||
VNode::Element(element) => self.create_element_node(element, node),
|
VNode::Element(element) => self.create_element_node(element, node),
|
||||||
VNode::Fragment(frag) => self.create_fragment_node(frag),
|
VNode::Fragment(frag) => self.create_fragment_node(frag),
|
||||||
VNode::Component(component) => self.create_component_node(*component),
|
VNode::Component(component) => self.create_component_node(*component),
|
||||||
VNode::Portal(linked) => self.create_linked_node(linked),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -406,7 +399,7 @@ impl<'bump> DiffState<'bump> {
|
|||||||
self.stack.add_child_count(1);
|
self.stack.add_child_count(1);
|
||||||
|
|
||||||
if let Some(cur_scope_id) = self.stack.current_scope() {
|
if let Some(cur_scope_id) = self.stack.current_scope() {
|
||||||
let scope = self.scopes.get_scope(&cur_scope_id).unwrap();
|
let scope = self.scopes.get_scope(cur_scope_id).unwrap();
|
||||||
|
|
||||||
for listener in *listeners {
|
for listener in *listeners {
|
||||||
self.attach_listener_to_scope(listener, scope);
|
self.attach_listener_to_scope(listener, scope);
|
||||||
@@ -443,11 +436,11 @@ impl<'bump> DiffState<'bump> {
|
|||||||
let parent_idx = self.stack.current_scope().unwrap();
|
let parent_idx = self.stack.current_scope().unwrap();
|
||||||
|
|
||||||
// Insert a new scope into our component list
|
// Insert a new scope into our component list
|
||||||
let parent_scope = self.scopes.get_scope(&parent_idx).unwrap();
|
let parent_scope = self.scopes.get_scope(parent_idx).unwrap();
|
||||||
let height = parent_scope.height + 1;
|
let height = parent_scope.height + 1;
|
||||||
let subtree = parent_scope.subtree.get();
|
let subtree = parent_scope.subtree.get();
|
||||||
|
|
||||||
let parent_scope = unsafe { self.scopes.get_scope_raw(&parent_idx) };
|
let parent_scope = unsafe { self.scopes.get_scope_raw(parent_idx) };
|
||||||
let caller = unsafe { std::mem::transmute(vcomponent.caller as *const _) };
|
let caller = unsafe { std::mem::transmute(vcomponent.caller as *const _) };
|
||||||
let fc_ptr = vcomponent.user_fc;
|
let fc_ptr = vcomponent.user_fc;
|
||||||
|
|
||||||
@@ -461,7 +454,7 @@ impl<'bump> DiffState<'bump> {
|
|||||||
vcomponent.associated_scope.set(Some(new_idx));
|
vcomponent.associated_scope.set(Some(new_idx));
|
||||||
|
|
||||||
if !vcomponent.can_memoize {
|
if !vcomponent.can_memoize {
|
||||||
let cur_scope = self.scopes.get_scope(&parent_idx).unwrap();
|
let cur_scope = self.scopes.get_scope(parent_idx).unwrap();
|
||||||
let extended = unsafe { std::mem::transmute(vcomponent) };
|
let extended = unsafe { std::mem::transmute(vcomponent) };
|
||||||
cur_scope.items.borrow_mut().borrowed_props.push(extended);
|
cur_scope.items.borrow_mut().borrowed_props.push(extended);
|
||||||
} else {
|
} else {
|
||||||
@@ -469,7 +462,7 @@ impl<'bump> DiffState<'bump> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: add noderefs to current noderef list Noderefs
|
// TODO: add noderefs to current noderef list Noderefs
|
||||||
let _new_component = self.scopes.get_scope(&new_idx).unwrap();
|
let _new_component = self.scopes.get_scope(new_idx).unwrap();
|
||||||
|
|
||||||
log::debug!(
|
log::debug!(
|
||||||
"initializing component {:?} with height {:?}",
|
"initializing component {:?} with height {:?}",
|
||||||
@@ -478,9 +471,9 @@ impl<'bump> DiffState<'bump> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Run the scope for one iteration to initialize it
|
// Run the scope for one iteration to initialize it
|
||||||
if self.scopes.run_scope(&new_idx) {
|
if self.scopes.run_scope(new_idx) {
|
||||||
// Take the node that was just generated from running the component
|
// Take the node that was just generated from running the component
|
||||||
let nextnode = self.scopes.fin_head(&new_idx);
|
let nextnode = self.scopes.fin_head(new_idx);
|
||||||
self.stack.create_component(new_idx, nextnode);
|
self.stack.create_component(new_idx, nextnode);
|
||||||
|
|
||||||
// todo: subtrees
|
// todo: subtrees
|
||||||
@@ -493,16 +486,6 @@ impl<'bump> DiffState<'bump> {
|
|||||||
self.mutations.dirty_scopes.insert(new_idx);
|
self.mutations.dirty_scopes.insert(new_idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_linked_node(&mut self, link: &'bump VPortal) {
|
|
||||||
if link.scope_id.get().is_none() {
|
|
||||||
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) });
|
|
||||||
}
|
|
||||||
|
|
||||||
// =================================
|
// =================================
|
||||||
// Tools for diffing nodes
|
// Tools for diffing nodes
|
||||||
// =================================
|
// =================================
|
||||||
@@ -520,12 +503,11 @@ impl<'bump> DiffState<'bump> {
|
|||||||
(Fragment(old), Fragment(new)) => self.diff_fragment_nodes(old, new),
|
(Fragment(old), Fragment(new)) => self.diff_fragment_nodes(old, new),
|
||||||
(Placeholder(old), Placeholder(new)) => new.dom_id.set(old.dom_id.get()),
|
(Placeholder(old), Placeholder(new)) => new.dom_id.set(old.dom_id.get()),
|
||||||
(Element(old), Element(new)) => self.diff_element_nodes(old, new, old_node, new_node),
|
(Element(old), Element(new)) => self.diff_element_nodes(old, new, old_node, new_node),
|
||||||
(Portal(old), Portal(new)) => self.diff_linked_nodes(old, new),
|
|
||||||
|
|
||||||
// Anything else is just a basic replace and create
|
// Anything else is just a basic replace and create
|
||||||
(
|
(
|
||||||
Portal(_) | Component(_) | Fragment(_) | Text(_) | Element(_) | Placeholder(_),
|
Component(_) | Fragment(_) | Text(_) | Element(_) | Placeholder(_),
|
||||||
Portal(_) | Component(_) | Fragment(_) | Text(_) | Element(_) | Placeholder(_),
|
Component(_) | Fragment(_) | Text(_) | Element(_) | Placeholder(_),
|
||||||
) => self
|
) => self
|
||||||
.stack
|
.stack
|
||||||
.create_node(new_node, MountType::Replace { old: old_node }),
|
.create_node(new_node, MountType::Replace { old: old_node }),
|
||||||
@@ -613,7 +595,7 @@ impl<'bump> DiffState<'bump> {
|
|||||||
//
|
//
|
||||||
// TODO: take a more efficient path than this
|
// TODO: take a more efficient path than this
|
||||||
if let Some(cur_scope_id) = self.stack.current_scope() {
|
if let Some(cur_scope_id) = self.stack.current_scope() {
|
||||||
let scope = self.scopes.get_scope(&cur_scope_id).unwrap();
|
let scope = self.scopes.get_scope(cur_scope_id).unwrap();
|
||||||
|
|
||||||
if old.listeners.len() == new.listeners.len() {
|
if old.listeners.len() == new.listeners.len() {
|
||||||
for (old_l, new_l) in old.listeners.iter().zip(new.listeners.iter()) {
|
for (old_l, new_l) in old.listeners.iter().zip(new.listeners.iter()) {
|
||||||
@@ -736,7 +718,7 @@ impl<'bump> DiffState<'bump> {
|
|||||||
// make sure the component's caller function is up to date
|
// make sure the component's caller function is up to date
|
||||||
let scope = unsafe {
|
let scope = unsafe {
|
||||||
self.scopes
|
self.scopes
|
||||||
.get_scope_mut(&scope_addr)
|
.get_scope_mut(scope_addr)
|
||||||
.unwrap_or_else(|| panic!("could not find {:?}", scope_addr))
|
.unwrap_or_else(|| panic!("could not find {:?}", scope_addr))
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -745,10 +727,10 @@ impl<'bump> DiffState<'bump> {
|
|||||||
// React doesn't automatically memoize, but we do.
|
// React doesn't automatically memoize, but we do.
|
||||||
let props_are_the_same = old.comparator.unwrap();
|
let props_are_the_same = old.comparator.unwrap();
|
||||||
|
|
||||||
if (self.force_diff || !props_are_the_same(new)) && self.scopes.run_scope(&scope_addr) {
|
if (self.force_diff || !props_are_the_same(new)) && self.scopes.run_scope(scope_addr) {
|
||||||
self.diff_node(
|
self.diff_node(
|
||||||
self.scopes.wip_head(&scope_addr),
|
self.scopes.wip_head(scope_addr),
|
||||||
self.scopes.fin_head(&scope_addr),
|
self.scopes.fin_head(scope_addr),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -773,19 +755,6 @@ impl<'bump> DiffState<'bump> {
|
|||||||
self.diff_children(old.children, new.children);
|
self.diff_children(old.children, new.children);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn diff_linked_nodes(&mut self, old: &'bump VPortal, new: &'bump VPortal) {
|
|
||||||
if !std::ptr::eq(old.node, new.node) {
|
|
||||||
// if the ptrs are the same then theyr're the same
|
|
||||||
let old: &VNode = unsafe { std::mem::transmute(&*old.node) };
|
|
||||||
let new: &VNode = unsafe { std::mem::transmute(&*new.node) };
|
|
||||||
self.diff_node(old, new);
|
|
||||||
}
|
|
||||||
|
|
||||||
if new.scope_id.get().is_none() {
|
|
||||||
todo!("attach the link to the scope - when children are not created");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// =============================================
|
// =============================================
|
||||||
// Utilities for creating new diff instructions
|
// Utilities for creating new diff instructions
|
||||||
// =============================================
|
// =============================================
|
||||||
@@ -1226,16 +1195,12 @@ impl<'bump> DiffState<'bump> {
|
|||||||
VNode::Text(t) => break t.dom_id.get(),
|
VNode::Text(t) => break t.dom_id.get(),
|
||||||
VNode::Element(t) => break t.dom_id.get(),
|
VNode::Element(t) => break t.dom_id.get(),
|
||||||
VNode::Placeholder(t) => break t.dom_id.get(),
|
VNode::Placeholder(t) => break t.dom_id.get(),
|
||||||
VNode::Portal(l) => {
|
|
||||||
let node: &VNode = unsafe { std::mem::transmute(&*l.node) };
|
|
||||||
self.find_last_element(node);
|
|
||||||
}
|
|
||||||
VNode::Fragment(frag) => {
|
VNode::Fragment(frag) => {
|
||||||
search_node = frag.children.last();
|
search_node = frag.children.last();
|
||||||
}
|
}
|
||||||
VNode::Component(el) => {
|
VNode::Component(el) => {
|
||||||
let scope_id = el.associated_scope.get().unwrap();
|
let scope_id = el.associated_scope.get().unwrap();
|
||||||
search_node = Some(self.scopes.root_node(&scope_id));
|
search_node = Some(self.scopes.root_node(scope_id));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1252,11 +1217,7 @@ impl<'bump> DiffState<'bump> {
|
|||||||
}
|
}
|
||||||
VNode::Component(el) => {
|
VNode::Component(el) => {
|
||||||
let scope_id = el.associated_scope.get().unwrap();
|
let scope_id = el.associated_scope.get().unwrap();
|
||||||
search_node = Some(self.scopes.root_node(&scope_id));
|
search_node = Some(self.scopes.root_node(scope_id));
|
||||||
}
|
|
||||||
VNode::Portal(link) => {
|
|
||||||
let node = unsafe { std::mem::transmute(&*link.node) };
|
|
||||||
search_node = Some(node);
|
|
||||||
}
|
}
|
||||||
VNode::Text(t) => break t.dom_id.get(),
|
VNode::Text(t) => break t.dom_id.get(),
|
||||||
VNode::Element(t) => break t.dom_id.get(),
|
VNode::Element(t) => break t.dom_id.get(),
|
||||||
@@ -1292,17 +1253,12 @@ impl<'bump> DiffState<'bump> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
VNode::Component(c) => {
|
VNode::Component(c) => {
|
||||||
let node = self.scopes.fin_head(&c.associated_scope.get().unwrap());
|
let node = self.scopes.fin_head(c.associated_scope.get().unwrap());
|
||||||
self.replace_node(node, nodes_created);
|
self.replace_node(node, nodes_created);
|
||||||
|
|
||||||
let scope_id = c.associated_scope.get().unwrap();
|
let scope_id = c.associated_scope.get().unwrap();
|
||||||
log::debug!("Destroying scope {:?}", scope_id);
|
log::debug!("Destroying scope {:?}", scope_id);
|
||||||
self.scopes.try_remove(&scope_id).unwrap();
|
self.scopes.try_remove(scope_id).unwrap();
|
||||||
}
|
|
||||||
|
|
||||||
VNode::Portal(l) => {
|
|
||||||
let node: &'bump VNode<'bump> = unsafe { std::mem::transmute(&*l.node) };
|
|
||||||
self.replace_node(node, nodes_created);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1349,11 +1305,6 @@ impl<'bump> DiffState<'bump> {
|
|||||||
self.remove_nodes(f.children, gen_muts);
|
self.remove_nodes(f.children, gen_muts);
|
||||||
}
|
}
|
||||||
|
|
||||||
VNode::Portal(l) => {
|
|
||||||
let node = unsafe { std::mem::transmute(&*l.node) };
|
|
||||||
self.remove_nodes(Some(node), gen_muts);
|
|
||||||
}
|
|
||||||
|
|
||||||
VNode::Component(c) => {
|
VNode::Component(c) => {
|
||||||
self.destroy_vomponent(c, gen_muts);
|
self.destroy_vomponent(c, gen_muts);
|
||||||
}
|
}
|
||||||
@@ -1363,10 +1314,10 @@ impl<'bump> DiffState<'bump> {
|
|||||||
|
|
||||||
fn destroy_vomponent(&mut self, vc: &VComponent, gen_muts: bool) {
|
fn destroy_vomponent(&mut self, vc: &VComponent, gen_muts: bool) {
|
||||||
let scope_id = vc.associated_scope.get().unwrap();
|
let scope_id = vc.associated_scope.get().unwrap();
|
||||||
let root = self.scopes.root_node(&scope_id);
|
let root = self.scopes.root_node(scope_id);
|
||||||
self.remove_nodes(Some(root), gen_muts);
|
self.remove_nodes(Some(root), gen_muts);
|
||||||
log::debug!("Destroying scope {:?}", scope_id);
|
log::debug!("Destroying scope {:?}", scope_id);
|
||||||
self.scopes.try_remove(&scope_id).unwrap();
|
self.scopes.try_remove(scope_id).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds a listener closure to a scope during diff.
|
/// Adds a listener closure to a scope during diff.
|
||||||
|
|||||||
@@ -114,22 +114,6 @@ pub enum VNode<'src> {
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
Placeholder(&'src VPlaceholder),
|
Placeholder(&'src VPlaceholder),
|
||||||
|
|
||||||
/// A VNode that is actually a pointer to some nodes rather than the nodes directly. Useful when rendering portals
|
|
||||||
/// or eliding lifetimes on VNodes through runtime checks.
|
|
||||||
///
|
|
||||||
/// Linked VNodes can only be made through the [`Context::render`] method
|
|
||||||
///
|
|
||||||
/// Typically, linked nodes are found *not* in a VNode. When NodeLinks are in a VNode, the NodeLink was passed into
|
|
||||||
/// an `rsx!` call.
|
|
||||||
///
|
|
||||||
/// # Example
|
|
||||||
/// ```rust, ignore
|
|
||||||
/// let mut vdom = VirtualDom::new();
|
|
||||||
///
|
|
||||||
/// let node: NodeLink = vdom.render_vnode(rsx!( "hello" ));
|
|
||||||
/// ```
|
|
||||||
Portal(VPortal),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'src> VNode<'src> {
|
impl<'src> VNode<'src> {
|
||||||
@@ -141,7 +125,6 @@ impl<'src> VNode<'src> {
|
|||||||
VNode::Fragment(f) => f.key,
|
VNode::Fragment(f) => f.key,
|
||||||
VNode::Text(_t) => None,
|
VNode::Text(_t) => None,
|
||||||
VNode::Placeholder(_f) => None,
|
VNode::Placeholder(_f) => None,
|
||||||
VNode::Portal(_c) => None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -160,7 +143,6 @@ impl<'src> VNode<'src> {
|
|||||||
VNode::Text(el) => el.dom_id.get(),
|
VNode::Text(el) => el.dom_id.get(),
|
||||||
VNode::Element(el) => el.dom_id.get(),
|
VNode::Element(el) => el.dom_id.get(),
|
||||||
VNode::Placeholder(el) => el.dom_id.get(),
|
VNode::Placeholder(el) => el.dom_id.get(),
|
||||||
VNode::Portal(_) => None,
|
|
||||||
VNode::Fragment(_) => None,
|
VNode::Fragment(_) => None,
|
||||||
VNode::Component(_) => None,
|
VNode::Component(_) => None,
|
||||||
}
|
}
|
||||||
@@ -184,11 +166,6 @@ impl<'src> VNode<'src> {
|
|||||||
children: f.children,
|
children: f.children,
|
||||||
key: f.key,
|
key: f.key,
|
||||||
}),
|
}),
|
||||||
VNode::Portal(c) => VNode::Portal(VPortal {
|
|
||||||
scope_id: c.scope_id.clone(),
|
|
||||||
link_idx: c.link_idx.clone(),
|
|
||||||
node: c.node,
|
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -207,7 +184,6 @@ impl Debug for VNode<'_> {
|
|||||||
write!(s, "VNode::VFragment {{ children: {:?} }}", frag.children)
|
write!(s, "VNode::VFragment {{ children: {:?} }}", frag.children)
|
||||||
}
|
}
|
||||||
VNode::Component(comp) => write!(s, "VNode::VComponent {{ fc: {:?}}}", comp.user_fc),
|
VNode::Component(comp) => write!(s, "VNode::VComponent {{ fc: {:?}}}", comp.user_fc),
|
||||||
VNode::Portal(c) => write!(s, "VNode::VCached {{ scope_id: {:?} }}", c.scope_id.get()),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -368,32 +344,6 @@ impl Clone for EventHandler<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A cached node is a "pointer" to a "rendered" node in a particular scope
|
|
||||||
///
|
|
||||||
/// It does not provide direct access to the node, so it doesn't carry any lifetime information with it
|
|
||||||
///
|
|
||||||
/// It is used during the diffing/rendering process as a runtime key into an existing set of nodes. The "render" key
|
|
||||||
/// is essentially a unique key to guarantee safe usage of the Node.
|
|
||||||
///
|
|
||||||
/// Linked VNodes can only be made through the [`Context::render`] method
|
|
||||||
///
|
|
||||||
/// Typically, NodeLinks are found *not* in a VNode. When NodeLinks are in a VNode, the NodeLink was passed into
|
|
||||||
/// an `rsx!` call.
|
|
||||||
///
|
|
||||||
/// todo: remove the raw pointer and use runtime checks instead
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct VPortal {
|
|
||||||
pub(crate) link_idx: Cell<usize>,
|
|
||||||
pub(crate) scope_id: Cell<Option<ScopeId>>,
|
|
||||||
pub(crate) node: *const VNode<'static>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialEq for VPortal {
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
self.node == other.node
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Virtual Components for custom user-defined components
|
/// Virtual Components for custom user-defined components
|
||||||
/// Only supports the functional syntax
|
/// Only supports the functional syntax
|
||||||
pub struct VComponent<'src> {
|
pub struct VComponent<'src> {
|
||||||
@@ -824,49 +774,3 @@ impl IntoVNode<'_> for Arguments<'_> {
|
|||||||
cx.text(self)
|
cx.text(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// called cx.render from a helper function
|
|
||||||
impl IntoVNode<'_> for Option<VPortal> {
|
|
||||||
fn into_vnode(self, _cx: NodeFactory) -> VNode {
|
|
||||||
match self {
|
|
||||||
Some(node) => VNode::Portal(node),
|
|
||||||
None => {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// essentially passing elements through props
|
|
||||||
// just build a new element in place
|
|
||||||
impl IntoVNode<'_> for &Option<VPortal> {
|
|
||||||
fn into_vnode(self, _cx: NodeFactory) -> VNode {
|
|
||||||
match self {
|
|
||||||
Some(node) => VNode::Portal(VPortal {
|
|
||||||
link_idx: node.link_idx.clone(),
|
|
||||||
scope_id: node.scope_id.clone(),
|
|
||||||
node: node.node,
|
|
||||||
}),
|
|
||||||
None => {
|
|
||||||
//
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IntoVNode<'_> for VPortal {
|
|
||||||
fn into_vnode(self, _cx: NodeFactory) -> VNode {
|
|
||||||
VNode::Portal(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IntoVNode<'_> for &VPortal {
|
|
||||||
fn into_vnode(self, _cx: NodeFactory) -> VNode {
|
|
||||||
VNode::Portal(VPortal {
|
|
||||||
link_idx: self.link_idx.clone(),
|
|
||||||
scope_id: self.scope_id.clone(),
|
|
||||||
node: self.node,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+32
-66
@@ -36,33 +36,7 @@ pub struct Scope<'a, P> {
|
|||||||
pub props: &'a P,
|
pub props: &'a P,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, P> Scope<'a, P> {
|
impl<P> Copy for Scope<'_, P> {}
|
||||||
/// Take a lazy VNode structure and actually build it with the context of the VDom's efficient VNode allocator.
|
|
||||||
///
|
|
||||||
/// This function consumes the context and absorb the lifetime, so these VNodes *must* be returned.
|
|
||||||
///
|
|
||||||
/// ## Example
|
|
||||||
///
|
|
||||||
/// ```ignore
|
|
||||||
/// fn Component(cx: Scope, props: &Props) -> Element {
|
|
||||||
/// // Lazy assemble the VNode tree
|
|
||||||
/// let lazy_nodes = rsx!("hello world");
|
|
||||||
///
|
|
||||||
/// // Actually build the tree and allocate it
|
|
||||||
/// cx.render(lazy_tree)
|
|
||||||
/// }
|
|
||||||
///```
|
|
||||||
pub fn render(self, rsx: Option<LazyNodes<'a, '_>>) -> Option<VNode<'a>> {
|
|
||||||
let fac = NodeFactory {
|
|
||||||
bump: &self.scope.wip_frame().bump,
|
|
||||||
};
|
|
||||||
match rsx {
|
|
||||||
Some(s) => Some(s.call(fac)),
|
|
||||||
None => todo!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<P> Clone for Scope<'_, P> {
|
impl<P> Clone for Scope<'_, P> {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
Self {
|
Self {
|
||||||
@@ -71,9 +45,10 @@ impl<P> Clone for Scope<'_, P> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<P> Copy for Scope<'_, P> {}
|
|
||||||
impl<P> std::ops::Deref for Scope<'_, P> {
|
impl<'a, P> std::ops::Deref for Scope<'a, P> {
|
||||||
type Target = ScopeState;
|
// rust will auto deref again to the original 'a lifetime at the call site
|
||||||
|
type Target = &'a ScopeState;
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
&self.scope
|
&self.scope
|
||||||
}
|
}
|
||||||
@@ -362,31 +337,31 @@ impl ScopeState {
|
|||||||
items.tasks.len() - 1
|
items.tasks.len() - 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// /// Take a lazy VNode structure and actually build it with the context of the VDom's efficient VNode allocator.
|
/// Take a lazy VNode structure and actually build it with the context of the VDom's efficient VNode allocator.
|
||||||
// ///
|
///
|
||||||
// /// This function consumes the context and absorb the lifetime, so these VNodes *must* be returned.
|
/// This function consumes the context and absorb the lifetime, so these VNodes *must* be returned.
|
||||||
// ///
|
///
|
||||||
// /// ## Example
|
/// ## Example
|
||||||
// ///
|
///
|
||||||
// /// ```ignore
|
/// ```ignore
|
||||||
// /// fn Component(cx: Scope, props: &Props) -> Element {
|
/// fn Component(cx: Scope, props: &Props) -> Element {
|
||||||
// /// // Lazy assemble the VNode tree
|
/// // Lazy assemble the VNode tree
|
||||||
// /// let lazy_nodes = rsx!("hello world");
|
/// let lazy_nodes = rsx!("hello world");
|
||||||
// ///
|
///
|
||||||
// /// // Actually build the tree and allocate it
|
/// // Actually build the tree and allocate it
|
||||||
// /// cx.render(lazy_tree)
|
/// cx.render(lazy_tree)
|
||||||
// /// }
|
/// }
|
||||||
// ///```
|
///```
|
||||||
// pub fn render<'src>(&self, rsx: Option<LazyNodes<'src, '_>>) -> Option<VNode<'src>> {
|
pub fn render<'src>(&'src self, rsx: Option<LazyNodes<'src, '_>>) -> Option<VNode<'src>> {
|
||||||
// let fac = NodeFactory {
|
let fac = NodeFactory {
|
||||||
// bump: &self.wip_frame().bump,
|
bump: &self.wip_frame().bump,
|
||||||
// };
|
};
|
||||||
// match rsx {
|
match rsx {
|
||||||
// Some(s) => Some(s.call(fac)),
|
Some(s) => Some(s.call(fac)),
|
||||||
// None => todo!(),
|
None => todo!(),
|
||||||
// }
|
}
|
||||||
// // rsx.map(|f| f.call(fac))
|
// rsx.map(|f| f.call(fac))
|
||||||
// }
|
}
|
||||||
|
|
||||||
/// Store a value between renders
|
/// Store a value between renders
|
||||||
///
|
///
|
||||||
@@ -492,7 +467,7 @@ impl ScopeState {
|
|||||||
|
|
||||||
pub(crate) struct BumpFrame {
|
pub(crate) struct BumpFrame {
|
||||||
pub bump: Bump,
|
pub bump: Bump,
|
||||||
pub nodes: RefCell<Vec<*const VNode<'static>>>,
|
pub nodes: Cell<*const VNode<'static>>,
|
||||||
}
|
}
|
||||||
impl BumpFrame {
|
impl BumpFrame {
|
||||||
pub(crate) fn new(capacity: usize) -> Self {
|
pub(crate) fn new(capacity: usize) -> Self {
|
||||||
@@ -504,18 +479,9 @@ impl BumpFrame {
|
|||||||
is_static: false,
|
is_static: false,
|
||||||
});
|
});
|
||||||
let node = bump.alloc(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 _]);
|
let nodes = Cell::new(node as *const _);
|
||||||
Self { bump, nodes }
|
Self { bump, nodes }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn assign_nodelink(&self, node: &VPortal) {
|
|
||||||
let mut nodes = self.nodes.borrow_mut();
|
|
||||||
|
|
||||||
let len = nodes.len();
|
|
||||||
nodes.push(node.node);
|
|
||||||
|
|
||||||
node.link_idx.set(len);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -68,16 +68,16 @@ impl ScopeArena {
|
|||||||
/// Safety:
|
/// Safety:
|
||||||
/// - Obtaining a mutable refernece to any Scope is unsafe
|
/// - Obtaining a mutable refernece to any Scope is unsafe
|
||||||
/// - Scopes use interior mutability when sharing data into components
|
/// - Scopes use interior mutability when sharing data into components
|
||||||
pub(crate) fn get_scope(&self, id: &ScopeId) -> Option<&ScopeState> {
|
pub(crate) fn get_scope(&self, id: ScopeId) -> Option<&ScopeState> {
|
||||||
unsafe { self.scopes.borrow().get(id).map(|f| &**f) }
|
unsafe { self.scopes.borrow().get(&id).map(|f| &**f) }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) unsafe fn get_scope_raw(&self, id: &ScopeId) -> Option<*mut ScopeState> {
|
pub(crate) unsafe fn get_scope_raw(&self, id: ScopeId) -> Option<*mut ScopeState> {
|
||||||
self.scopes.borrow().get(id).copied()
|
self.scopes.borrow().get(&id).copied()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) unsafe fn get_scope_mut(&self, id: &ScopeId) -> Option<&mut ScopeState> {
|
pub(crate) unsafe fn get_scope_mut(&self, id: ScopeId) -> Option<&mut ScopeState> {
|
||||||
self.scopes.borrow().get(id).map(|s| &mut **s)
|
self.scopes.borrow().get(&id).map(|s| &mut **s)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn new_with_key(
|
pub(crate) fn new_with_key(
|
||||||
@@ -102,27 +102,27 @@ impl ScopeArena {
|
|||||||
scope.our_arena_idx = new_scope_id;
|
scope.our_arena_idx = new_scope_id;
|
||||||
scope.container = container;
|
scope.container = container;
|
||||||
|
|
||||||
scope.frames[0].nodes.get_mut().push({
|
// scope.frames[0].nodes.get_mut().push({
|
||||||
let vnode = scope.frames[0]
|
// let vnode = scope.frames[0]
|
||||||
.bump
|
// .bump
|
||||||
.alloc(VNode::Text(scope.frames[0].bump.alloc(VText {
|
// .alloc(VNode::Text(scope.frames[0].bump.alloc(VText {
|
||||||
dom_id: Default::default(),
|
// dom_id: Default::default(),
|
||||||
is_static: false,
|
// is_static: false,
|
||||||
text: "",
|
// text: "",
|
||||||
})));
|
// })));
|
||||||
unsafe { std::mem::transmute(vnode as *mut VNode) }
|
// unsafe { std::mem::transmute(vnode as *mut VNode) }
|
||||||
});
|
// });
|
||||||
|
|
||||||
scope.frames[1].nodes.get_mut().push({
|
// scope.frames[1].nodes.get_mut().push({
|
||||||
let vnode = scope.frames[1]
|
// let vnode = scope.frames[1]
|
||||||
.bump
|
// .bump
|
||||||
.alloc(VNode::Text(scope.frames[1].bump.alloc(VText {
|
// .alloc(VNode::Text(scope.frames[1].bump.alloc(VText {
|
||||||
dom_id: Default::default(),
|
// dom_id: Default::default(),
|
||||||
is_static: false,
|
// is_static: false,
|
||||||
text: "",
|
// text: "",
|
||||||
})));
|
// })));
|
||||||
unsafe { std::mem::transmute(vnode as *mut VNode) }
|
// unsafe { std::mem::transmute(vnode as *mut VNode) }
|
||||||
});
|
// });
|
||||||
|
|
||||||
let any_item = self.scopes.borrow_mut().insert(new_scope_id, scope);
|
let any_item = self.scopes.borrow_mut().insert(new_scope_id, scope);
|
||||||
debug_assert!(any_item.is_none());
|
debug_assert!(any_item.is_none());
|
||||||
@@ -138,27 +138,28 @@ impl ScopeArena {
|
|||||||
|
|
||||||
let mut frames = [BumpFrame::new(node_capacity), BumpFrame::new(node_capacity)];
|
let mut frames = [BumpFrame::new(node_capacity), BumpFrame::new(node_capacity)];
|
||||||
|
|
||||||
frames[0].nodes.get_mut().push({
|
// todo - add the node
|
||||||
let vnode = frames[0]
|
// frames[0].nodes.get_mut().push({
|
||||||
.bump
|
// let vnode = frames[0]
|
||||||
.alloc(VNode::Text(frames[0].bump.alloc(VText {
|
// .bump
|
||||||
dom_id: Default::default(),
|
// .alloc(VNode::Text(frames[0].bump.alloc(VText {
|
||||||
is_static: false,
|
// dom_id: Default::default(),
|
||||||
text: "",
|
// is_static: false,
|
||||||
})));
|
// text: "",
|
||||||
unsafe { std::mem::transmute(vnode as *mut VNode) }
|
// })));
|
||||||
});
|
// unsafe { std::mem::transmute(vnode as *mut VNode) }
|
||||||
|
// });
|
||||||
|
|
||||||
frames[1].nodes.get_mut().push({
|
// frames[1].nodes.get_mut().push({
|
||||||
let vnode = frames[1]
|
// let vnode = frames[1]
|
||||||
.bump
|
// .bump
|
||||||
.alloc(VNode::Text(frames[1].bump.alloc(VText {
|
// .alloc(VNode::Text(frames[1].bump.alloc(VText {
|
||||||
dom_id: Default::default(),
|
// dom_id: Default::default(),
|
||||||
is_static: false,
|
// is_static: false,
|
||||||
text: "",
|
// text: "",
|
||||||
})));
|
// })));
|
||||||
unsafe { std::mem::transmute(vnode as *mut VNode) }
|
// unsafe { std::mem::transmute(vnode as *mut VNode) }
|
||||||
});
|
// });
|
||||||
|
|
||||||
let scope = self.bump.alloc(ScopeState {
|
let scope = self.bump.alloc(ScopeState {
|
||||||
sender: self.sender.clone(),
|
sender: self.sender.clone(),
|
||||||
@@ -193,13 +194,13 @@ impl ScopeArena {
|
|||||||
new_scope_id
|
new_scope_id
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn try_remove(&self, id: &ScopeId) -> Option<()> {
|
pub fn try_remove(&self, id: ScopeId) -> Option<()> {
|
||||||
self.ensure_drop_safety(id);
|
self.ensure_drop_safety(id);
|
||||||
|
|
||||||
// Safety:
|
// Safety:
|
||||||
// - ensure_drop_safety ensures that no references to this scope are in use
|
// - ensure_drop_safety ensures that no references to this scope are in use
|
||||||
// - this raw pointer is removed from the map
|
// - this raw pointer is removed from the map
|
||||||
let scope = unsafe { &mut *self.scopes.borrow_mut().remove(id).unwrap() };
|
let scope = unsafe { &mut *self.scopes.borrow_mut().remove(&id).unwrap() };
|
||||||
|
|
||||||
// we're just reusing scopes so we need to clear it out
|
// we're just reusing scopes so we need to clear it out
|
||||||
scope.hook_vals.get_mut().drain(..).for_each(|state| {
|
scope.hook_vals.get_mut().drain(..).for_each(|state| {
|
||||||
@@ -216,8 +217,8 @@ impl ScopeArena {
|
|||||||
scope.is_subtree_root.set(false);
|
scope.is_subtree_root.set(false);
|
||||||
scope.subtree.set(0);
|
scope.subtree.set(0);
|
||||||
|
|
||||||
scope.frames[0].nodes.get_mut().clear();
|
// scope.frames[0].nodes.get_mut().clear();
|
||||||
scope.frames[1].nodes.get_mut().clear();
|
// scope.frames[1].nodes.get_mut().clear();
|
||||||
|
|
||||||
scope.frames[0].bump.reset();
|
scope.frames[0].bump.reset();
|
||||||
scope.frames[1].bump.reset();
|
scope.frames[1].bump.reset();
|
||||||
@@ -271,7 +272,7 @@ impl ScopeArena {
|
|||||||
///
|
///
|
||||||
/// This also makes sure that drop order is consistent and predictable. All resources that rely on being dropped will
|
/// This also makes sure that drop order is consistent and predictable. All resources that rely on being dropped will
|
||||||
/// be dropped.
|
/// be dropped.
|
||||||
pub(crate) fn ensure_drop_safety(&self, scope_id: &ScopeId) {
|
pub(crate) fn ensure_drop_safety(&self, scope_id: ScopeId) {
|
||||||
if let Some(scope) = self.get_scope(scope_id) {
|
if let Some(scope) = self.get_scope(scope_id) {
|
||||||
let mut items = scope.items.borrow_mut();
|
let mut items = scope.items.borrow_mut();
|
||||||
|
|
||||||
@@ -284,7 +285,7 @@ impl ScopeArena {
|
|||||||
.get()
|
.get()
|
||||||
.expect("VComponents should be associated with a valid Scope");
|
.expect("VComponents should be associated with a valid Scope");
|
||||||
|
|
||||||
self.ensure_drop_safety(&scope_id);
|
self.ensure_drop_safety(scope_id);
|
||||||
|
|
||||||
let mut drop_props = comp.drop_props.borrow_mut().take().unwrap();
|
let mut drop_props = comp.drop_props.borrow_mut().take().unwrap();
|
||||||
drop_props();
|
drop_props();
|
||||||
@@ -298,7 +299,7 @@ impl ScopeArena {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn run_scope(&self, id: &ScopeId) -> bool {
|
pub(crate) fn run_scope(&self, id: ScopeId) -> bool {
|
||||||
// Cycle to the next frame and then reset it
|
// Cycle to the next frame and then reset it
|
||||||
// This breaks any latent references, invalidating every pointer referencing into it.
|
// This breaks any latent references, invalidating every pointer referencing into it.
|
||||||
// Remove all the outdated listeners
|
// Remove all the outdated listeners
|
||||||
@@ -327,16 +328,20 @@ impl ScopeArena {
|
|||||||
debug_assert!(items.tasks.is_empty());
|
debug_assert!(items.tasks.is_empty());
|
||||||
|
|
||||||
// Todo: see if we can add stronger guarantees around internal bookkeeping and failed component renders.
|
// Todo: see if we can add stronger guarantees around internal bookkeeping and failed component renders.
|
||||||
scope.wip_frame().nodes.borrow_mut().clear();
|
// scope.wip_frame().nodes
|
||||||
}
|
}
|
||||||
|
|
||||||
let render: &dyn Fn(&ScopeState) -> Element = unsafe { &*scope.caller };
|
let render: &dyn Fn(&ScopeState) -> Element = unsafe { &*scope.caller };
|
||||||
|
|
||||||
if let Some(link) = render(scope) {
|
if let Some(node) = render(scope) {
|
||||||
if !scope.items.borrow().tasks.is_empty() {
|
if !scope.items.borrow().tasks.is_empty() {
|
||||||
self.pending_futures.borrow_mut().insert(*id);
|
self.pending_futures.borrow_mut().insert(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let frame = scope.wip_frame();
|
||||||
|
let node = frame.bump.alloc(node);
|
||||||
|
frame.nodes.set(unsafe { std::mem::transmute(node) });
|
||||||
|
|
||||||
// make the "wip frame" contents the "finished frame"
|
// make the "wip frame" contents the "finished frame"
|
||||||
// any future dipping into completed nodes after "render" will go through "fin head"
|
// any future dipping into completed nodes after "render" will go through "fin head"
|
||||||
scope.cycle_frame();
|
scope.cycle_frame();
|
||||||
@@ -371,24 +376,23 @@ impl ScopeArena {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// The head of the bumpframe is the first linked NodeLink
|
// The head of the bumpframe is the first linked NodeLink
|
||||||
pub fn wip_head(&self, id: &ScopeId) -> &VNode {
|
pub fn wip_head(&self, id: ScopeId) -> &VNode {
|
||||||
let scope = self.get_scope(id).unwrap();
|
let scope = self.get_scope(id).unwrap();
|
||||||
let frame = scope.wip_frame();
|
let frame = scope.wip_frame();
|
||||||
let nodes = frame.nodes.borrow();
|
let nodes = frame.nodes.borrow();
|
||||||
let node: &VNode = unsafe { &**nodes.get(0).unwrap() };
|
let node = unsafe { &*frame.nodes.get() };
|
||||||
unsafe { std::mem::transmute::<&VNode, &VNode>(node) }
|
unsafe { std::mem::transmute::<&VNode, &VNode>(node) }
|
||||||
}
|
}
|
||||||
|
|
||||||
// The head of the bumpframe is the first linked NodeLink
|
// The head of the bumpframe is the first linked NodeLink
|
||||||
pub fn fin_head(&self, id: &ScopeId) -> &VNode {
|
pub fn fin_head(&self, id: ScopeId) -> &VNode {
|
||||||
let scope = self.get_scope(id).unwrap();
|
let scope = self.get_scope(id).unwrap();
|
||||||
let frame = scope.fin_frame();
|
let frame = scope.fin_frame();
|
||||||
let nodes = frame.nodes.borrow();
|
let node = unsafe { &*frame.nodes.get() };
|
||||||
let node: &VNode = unsafe { &**nodes.get(0).unwrap() };
|
|
||||||
unsafe { std::mem::transmute::<&VNode, &VNode>(node) }
|
unsafe { std::mem::transmute::<&VNode, &VNode>(node) }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn root_node(&self, id: &ScopeId) -> &VNode {
|
pub fn root_node(&self, id: ScopeId) -> &VNode {
|
||||||
self.fin_head(id)
|
self.fin_head(id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -105,7 +105,8 @@ use std::{any::Any, collections::VecDeque, pin::Pin, sync::Arc, task::Poll};
|
|||||||
pub struct VirtualDom {
|
pub struct VirtualDom {
|
||||||
base_scope: ScopeId,
|
base_scope: ScopeId,
|
||||||
|
|
||||||
_root_props: Box<dyn Any>,
|
// it's stored here so the props are dropped when the VirtualDom is dropped
|
||||||
|
_root_caller: Box<dyn for<'r> Fn(&'r ScopeState) -> Element<'r> + 'static>,
|
||||||
|
|
||||||
scopes: Box<ScopeArena>,
|
scopes: Box<ScopeArena>,
|
||||||
|
|
||||||
@@ -132,7 +133,7 @@ impl VirtualDom {
|
|||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
/// ```rust, ignore
|
/// ```rust, ignore
|
||||||
/// fn Example(cx: Context, props: &()) -> Element {
|
/// fn Example(cx: Scope<()>) -> Element {
|
||||||
/// cx.render(rsx!( div { "hello world" } ))
|
/// cx.render(rsx!( div { "hello world" } ))
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
@@ -161,8 +162,8 @@ impl VirtualDom {
|
|||||||
/// name: &'static str
|
/// name: &'static str
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// fn Example(cx: Context, props: &SomeProps) -> Element {
|
/// fn Example(cx: Scope<SomeProps>) -> Element {
|
||||||
/// cx.render(rsx!{ div{ "hello {cx.name}" } })
|
/// cx.render(rsx!{ div{ "hello {cx.props.name}" } })
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// let dom = VirtualDom::new(Example);
|
/// let dom = VirtualDom::new(Example);
|
||||||
@@ -194,31 +195,36 @@ impl VirtualDom {
|
|||||||
sender: UnboundedSender<SchedulerMsg>,
|
sender: UnboundedSender<SchedulerMsg>,
|
||||||
receiver: UnboundedReceiver<SchedulerMsg>,
|
receiver: UnboundedReceiver<SchedulerMsg>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let scopes = ScopeArena::new(sender.clone());
|
// move these two things onto the heap so we have stable ptrs
|
||||||
|
let scopes = Box::new(ScopeArena::new(sender.clone()));
|
||||||
let root_props = Box::new(root_props);
|
let root_props = Box::new(root_props);
|
||||||
let props_ref: *const P = root_props.as_ref();
|
|
||||||
let mut caller: Box<dyn Fn(&ScopeState) -> Element> =
|
|
||||||
Box::new(move |scp: &ScopeState| -> Element {
|
|
||||||
let p = unsafe { &*props_ref };
|
|
||||||
todo!()
|
|
||||||
// root(Context {
|
|
||||||
// scope: scp,
|
|
||||||
// props: p,
|
|
||||||
// })
|
|
||||||
});
|
|
||||||
let caller_ref: *mut dyn Fn(&ScopeState) -> Element = caller.as_mut();
|
|
||||||
let base_scope = scopes.new_with_key(root as _, caller_ref, None, ElementId(0), 0, 0);
|
|
||||||
|
|
||||||
let pending_messages = VecDeque::new();
|
// create the root caller which will properly drop its props when the VirtualDom is dropped
|
||||||
|
let mut _root_caller: Box<dyn for<'r> Fn(&'r ScopeState) -> Element<'r> + 'static> =
|
||||||
|
Box::new(move |scope: &ScopeState| -> Element {
|
||||||
|
// Safety: The props at this pointer can never be moved.
|
||||||
|
// Also, this closure will never be ran when the VirtualDom is destroyed.
|
||||||
|
// This is where the root lifetime of the VirtualDom originates.
|
||||||
|
let props: *const P = root_props.as_ref();
|
||||||
|
let props = unsafe { &*props };
|
||||||
|
root(Scope { scope, props })
|
||||||
|
});
|
||||||
|
|
||||||
|
// safety: the raw pointer is aliased or used after this point.
|
||||||
|
let caller: *mut dyn Fn(&ScopeState) -> Element = _root_caller.as_mut();
|
||||||
|
let base_scope = scopes.new_with_key(root as _, caller, None, ElementId(0), 0, 0);
|
||||||
|
|
||||||
let mut dirty_scopes = IndexSet::new();
|
let mut dirty_scopes = IndexSet::new();
|
||||||
dirty_scopes.insert(base_scope);
|
dirty_scopes.insert(base_scope);
|
||||||
|
|
||||||
|
// todo: add a pending message to the scheduler to start the scheduler?
|
||||||
|
let pending_messages = VecDeque::new();
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
scopes: Box::new(scopes),
|
scopes,
|
||||||
base_scope,
|
base_scope,
|
||||||
receiver,
|
receiver,
|
||||||
_root_props: root_props,
|
_root_caller,
|
||||||
pending_messages,
|
pending_messages,
|
||||||
dirty_scopes,
|
dirty_scopes,
|
||||||
sender,
|
sender,
|
||||||
@@ -232,16 +238,16 @@ impl VirtualDom {
|
|||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
pub fn base_scope(&self) -> &ScopeState {
|
pub fn base_scope(&self) -> &ScopeState {
|
||||||
self.get_scope(&self.base_scope).unwrap()
|
self.get_scope(self.base_scope).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the [`Scope`] for a component given its [`ScopeId`]
|
/// Get the [`ScopeState`] for a component given its [`ScopeId`]
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
///
|
///
|
||||||
///
|
///
|
||||||
pub fn get_scope<'a>(&'a self, id: &ScopeId) -> Option<&'a ScopeState> {
|
pub fn get_scope<'a>(&'a self, id: ScopeId) -> Option<&'a ScopeState> {
|
||||||
self.scopes.get_scope(id)
|
self.scopes.get_scope(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -398,11 +404,11 @@ impl VirtualDom {
|
|||||||
|
|
||||||
// Sort the scopes by height. Theoretically, we'll de-duplicate scopes by height
|
// Sort the scopes by height. Theoretically, we'll de-duplicate scopes by height
|
||||||
self.dirty_scopes
|
self.dirty_scopes
|
||||||
.retain(|id| scopes.get_scope(id).is_some());
|
.retain(|id| scopes.get_scope(*id).is_some());
|
||||||
|
|
||||||
self.dirty_scopes.sort_by(|a, b| {
|
self.dirty_scopes.sort_by(|a, b| {
|
||||||
let h1 = scopes.get_scope(a).unwrap().height;
|
let h1 = scopes.get_scope(*a).unwrap().height;
|
||||||
let h2 = scopes.get_scope(b).unwrap().height;
|
let h2 = scopes.get_scope(*b).unwrap().height;
|
||||||
h1.cmp(&h2).reverse()
|
h1.cmp(&h2).reverse()
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -410,15 +416,13 @@ impl VirtualDom {
|
|||||||
if !ran_scopes.contains(&scopeid) {
|
if !ran_scopes.contains(&scopeid) {
|
||||||
ran_scopes.insert(scopeid);
|
ran_scopes.insert(scopeid);
|
||||||
|
|
||||||
if self.scopes.run_scope(&scopeid) {
|
if self.scopes.run_scope(scopeid) {
|
||||||
let (old, new) = (
|
let (old, new) =
|
||||||
self.scopes.wip_head(&scopeid),
|
(self.scopes.wip_head(scopeid), self.scopes.fin_head(scopeid));
|
||||||
self.scopes.fin_head(&scopeid),
|
|
||||||
);
|
|
||||||
diff_state.stack.push(DiffInstruction::Diff { new, old });
|
diff_state.stack.push(DiffInstruction::Diff { new, old });
|
||||||
diff_state.stack.scope_stack.push(scopeid);
|
diff_state.stack.scope_stack.push(scopeid);
|
||||||
|
|
||||||
let scope = scopes.get_scope(&scopeid).unwrap();
|
let scope = scopes.get_scope(scopeid).unwrap();
|
||||||
diff_state.stack.element_stack.push(scope.container);
|
diff_state.stack.element_stack.push(scope.container);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -465,10 +469,10 @@ impl VirtualDom {
|
|||||||
let mut diff_state = DiffState::new(&self.scopes);
|
let mut diff_state = DiffState::new(&self.scopes);
|
||||||
|
|
||||||
let scope_id = self.base_scope;
|
let scope_id = self.base_scope;
|
||||||
if self.scopes.run_scope(&scope_id) {
|
if self.scopes.run_scope(scope_id) {
|
||||||
diff_state
|
diff_state
|
||||||
.stack
|
.stack
|
||||||
.create_node(self.scopes.fin_head(&scope_id), MountType::Append);
|
.create_node(self.scopes.fin_head(scope_id), MountType::Append);
|
||||||
|
|
||||||
diff_state.stack.element_stack.push(ElementId(0));
|
diff_state.stack.element_stack.push(ElementId(0));
|
||||||
diff_state.stack.scope_stack.push(scope_id);
|
diff_state.stack.scope_stack.push(scope_id);
|
||||||
@@ -507,7 +511,7 @@ impl VirtualDom {
|
|||||||
///
|
///
|
||||||
/// let edits = dom.diff();
|
/// let edits = dom.diff();
|
||||||
/// ```
|
/// ```
|
||||||
pub fn hard_diff<'a>(&'a mut self, scope_id: &ScopeId) -> Option<Mutations<'a>> {
|
pub fn hard_diff<'a>(&'a mut self, scope_id: ScopeId) -> Option<Mutations<'a>> {
|
||||||
let mut diff_machine = DiffState::new(&self.scopes);
|
let mut diff_machine = DiffState::new(&self.scopes);
|
||||||
if self.scopes.run_scope(scope_id) {
|
if self.scopes.run_scope(scope_id) {
|
||||||
diff_machine.force_diff = true;
|
diff_machine.force_diff = true;
|
||||||
@@ -529,7 +533,7 @@ impl VirtualDom {
|
|||||||
/// let nodes = dom.render_nodes(rsx!("div"));
|
/// let nodes = dom.render_nodes(rsx!("div"));
|
||||||
/// ```
|
/// ```
|
||||||
pub fn render_vnodes<'a>(&'a self, lazy_nodes: Option<LazyNodes<'a, '_>>) -> &'a VNode<'a> {
|
pub fn render_vnodes<'a>(&'a self, lazy_nodes: Option<LazyNodes<'a, '_>>) -> &'a VNode<'a> {
|
||||||
let scope = self.scopes.get_scope(&self.base_scope).unwrap();
|
let scope = self.scopes.get_scope(self.base_scope).unwrap();
|
||||||
let frame = scope.wip_frame();
|
let frame = scope.wip_frame();
|
||||||
let factory = NodeFactory { bump: &frame.bump };
|
let factory = NodeFactory { bump: &frame.bump };
|
||||||
let node = lazy_nodes.unwrap().call(factory);
|
let node = lazy_nodes.unwrap().call(factory);
|
||||||
@@ -741,7 +745,7 @@ impl<'a> Future for PollTasks<'a> {
|
|||||||
let mut scopes_to_clear: SmallVec<[_; 10]> = smallvec::smallvec![];
|
let mut scopes_to_clear: SmallVec<[_; 10]> = smallvec::smallvec![];
|
||||||
|
|
||||||
// Poll every scope manually
|
// Poll every scope manually
|
||||||
for fut in self.0.pending_futures.borrow().iter() {
|
for fut in self.0.pending_futures.borrow().iter().copied() {
|
||||||
let scope = self.0.get_scope(fut).expect("Scope should never be moved");
|
let scope = self.0.get_scope(fut).expect("Scope should never be moved");
|
||||||
|
|
||||||
let mut items = scope.items.borrow_mut();
|
let mut items = scope.items.borrow_mut();
|
||||||
@@ -756,7 +760,7 @@ impl<'a> Future for PollTasks<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if unfinished_tasks.is_empty() {
|
if unfinished_tasks.is_empty() {
|
||||||
scopes_to_clear.push(*fut);
|
scopes_to_clear.push(fut);
|
||||||
}
|
}
|
||||||
|
|
||||||
items.tasks.extend(unfinished_tasks.drain(..));
|
items.tasks.extend(unfinished_tasks.drain(..));
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ fn test_borrowed_state() {
|
|||||||
let _ = VirtualDom::new(Parent);
|
let _ = VirtualDom::new(Parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn Parent(cx: Scope, _props: &()) -> Element {
|
fn Parent(cx: Scope<()>) -> Element {
|
||||||
let value = cx.use_hook(|_| String::new(), |f| &*f);
|
let value = cx.use_hook(|_| String::new(), |f| &*f);
|
||||||
|
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
@@ -28,11 +28,11 @@ struct ChildProps<'a> {
|
|||||||
name: &'a str,
|
name: &'a str,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn Child(cx: Scope, props: &ChildProps) -> Element {
|
fn Child<'a>(cx: Scope<'a, ChildProps<'a>>) -> Element {
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
div {
|
div {
|
||||||
h1 { "it's nested" }
|
h1 { "it's nested" }
|
||||||
Child2 { name: props.name }
|
Child2 { name: cx.props.name }
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -42,8 +42,8 @@ struct Grandchild<'a> {
|
|||||||
name: &'a str,
|
name: &'a str,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn Child2(cx: Scope, props: &Grandchild) -> Element {
|
fn Child2<'a>(cx: Scope<'a, Grandchild<'a>>) -> Element {
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
div { "Hello {props.name}!" }
|
div { "Hello {cx.props.name}!" }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -21,7 +21,7 @@ fn new_dom<P: 'static + Send>(app: Component<P>, props: P) -> VirtualDom {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_original_diff() {
|
fn test_original_diff() {
|
||||||
static APP: Component<()> = |cx, props| {
|
static APP: Component<()> = |cx| {
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
div {
|
div {
|
||||||
div {
|
div {
|
||||||
@@ -57,7 +57,7 @@ fn test_original_diff() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn create() {
|
fn create() {
|
||||||
static APP: Component<()> = |cx, props| {
|
static APP: Component<()> = |cx| {
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
div {
|
div {
|
||||||
div {
|
div {
|
||||||
@@ -120,7 +120,7 @@ fn create() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn create_list() {
|
fn create_list() {
|
||||||
static APP: Component<()> = |cx, props| {
|
static APP: Component<()> = |cx| {
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
{(0..3).map(|f| rsx!{ div {
|
{(0..3).map(|f| rsx!{ div {
|
||||||
"hello"
|
"hello"
|
||||||
@@ -169,7 +169,7 @@ fn create_list() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn create_simple() {
|
fn create_simple() {
|
||||||
static APP: Component<()> = |cx, props| {
|
static APP: Component<()> = |cx| {
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
div {}
|
div {}
|
||||||
div {}
|
div {}
|
||||||
@@ -207,7 +207,7 @@ fn create_simple() {
|
|||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn create_components() {
|
fn create_components() {
|
||||||
static App: Component<()> = |cx, props| {
|
static App: Component<()> = |cx| {
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
Child { "abc1" }
|
Child { "abc1" }
|
||||||
Child { "abc2" }
|
Child { "abc2" }
|
||||||
@@ -215,15 +215,16 @@ fn create_components() {
|
|||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Props, PartialEq)]
|
#[derive(Props)]
|
||||||
struct ChildProps {
|
struct ChildProps<'a> {
|
||||||
children: Element,
|
children: Element<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn Child(cx: Scope, props: &ChildProps) -> Element {
|
fn Child<'a>(cx: Scope<'a, ChildProps<'a>>) -> Element {
|
||||||
|
todo!();
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
h1 {}
|
h1 {}
|
||||||
div { {&props.children} }
|
// div { {&cx.props.children} }
|
||||||
p {}
|
p {}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -273,7 +274,7 @@ fn create_components() {
|
|||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn anchors() {
|
fn anchors() {
|
||||||
static App: Component<()> = |cx, props| {
|
static App: Component<()> = |cx| {
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
{true.then(|| rsx!{ div { "hello" } })}
|
{true.then(|| rsx!{ div { "hello" } })}
|
||||||
{false.then(|| rsx!{ div { "goodbye" } })}
|
{false.then(|| rsx!{ div { "goodbye" } })}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ mod test_logging;
|
|||||||
fn new_dom() -> VirtualDom {
|
fn new_dom() -> VirtualDom {
|
||||||
const IS_LOGGING_ENABLED: bool = false;
|
const IS_LOGGING_ENABLED: bool = false;
|
||||||
test_logging::set_up_logging(IS_LOGGING_ENABLED);
|
test_logging::set_up_logging(IS_LOGGING_ENABLED);
|
||||||
VirtualDom::new(|cx, props| todo!())
|
VirtualDom::new(|cx| todo!())
|
||||||
}
|
}
|
||||||
|
|
||||||
use DomEdit::*;
|
use DomEdit::*;
|
||||||
|
|||||||
@@ -22,8 +22,8 @@ fn manual_diffing() {
|
|||||||
value: Shared<&'static str>,
|
value: Shared<&'static str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
static App: Component<AppProps> = |cx, props| {
|
static App: Component<AppProps> = |cx| {
|
||||||
let val = props.value.lock().unwrap();
|
let val = cx.props.value.lock().unwrap();
|
||||||
cx.render(rsx! { div { "{val}" } })
|
cx.render(rsx! { div { "{val}" } })
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -46,8 +46,8 @@ fn manual_diffing() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn events_generate() {
|
fn events_generate() {
|
||||||
static App: Component<()> = |cx, _| {
|
static App: Component<()> = |cx| {
|
||||||
let mut count = use_state(cx, || 0);
|
let mut count = use_state(&cx, || 0);
|
||||||
|
|
||||||
let inner = match *count {
|
let inner = match *count {
|
||||||
0 => {
|
0 => {
|
||||||
@@ -105,8 +105,8 @@ fn events_generate() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn components_generate() {
|
fn components_generate() {
|
||||||
static App: Component<()> = |cx, _| {
|
static App: Component<()> = |cx| {
|
||||||
let mut render_phase = use_state(cx, || 0);
|
let mut render_phase = use_state(&cx, || 0);
|
||||||
render_phase += 1;
|
render_phase += 1;
|
||||||
|
|
||||||
cx.render(match *render_phase {
|
cx.render(match *render_phase {
|
||||||
@@ -122,7 +122,7 @@ fn components_generate() {
|
|||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
static Child: Component<()> = |cx, _| {
|
static Child: Component<()> = |cx| {
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
h1 {}
|
h1 {}
|
||||||
})
|
})
|
||||||
@@ -222,8 +222,8 @@ fn components_generate() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn component_swap() {
|
fn component_swap() {
|
||||||
// simple_logger::init();
|
// simple_logger::init();
|
||||||
static App: Component<()> = |cx, _| {
|
static App: Component<()> = |cx| {
|
||||||
let mut render_phase = use_state(cx, || 0);
|
let mut render_phase = use_state(&cx, || 0);
|
||||||
render_phase += 1;
|
render_phase += 1;
|
||||||
|
|
||||||
cx.render(match *render_phase {
|
cx.render(match *render_phase {
|
||||||
@@ -261,7 +261,7 @@ fn component_swap() {
|
|||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
static NavBar: Component<()> = |cx, _| {
|
static NavBar: Component<()> = |cx| {
|
||||||
println!("running navbar");
|
println!("running navbar");
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
h1 {
|
h1 {
|
||||||
@@ -271,7 +271,7 @@ fn component_swap() {
|
|||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
static NavLink: Component<()> = |cx, _| {
|
static NavLink: Component<()> = |cx| {
|
||||||
println!("running navlink");
|
println!("running navlink");
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
h1 {
|
h1 {
|
||||||
@@ -280,7 +280,7 @@ fn component_swap() {
|
|||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
static Dashboard: Component<()> = |cx, _| {
|
static Dashboard: Component<()> = |cx| {
|
||||||
println!("running dashboard");
|
println!("running dashboard");
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
div {
|
div {
|
||||||
@@ -289,7 +289,7 @@ fn component_swap() {
|
|||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
static Results: Component<()> = |cx, _| {
|
static Results: Component<()> = |cx| {
|
||||||
println!("running results");
|
println!("running results");
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
div {
|
div {
|
||||||
|
|||||||
@@ -13,12 +13,12 @@ mod test_logging;
|
|||||||
fn shared_state_test() {
|
fn shared_state_test() {
|
||||||
struct MySharedState(&'static str);
|
struct MySharedState(&'static str);
|
||||||
|
|
||||||
static App: Component<()> = |cx, props| {
|
static App: Component<()> = |cx| {
|
||||||
cx.provide_state(MySharedState("world!"));
|
cx.provide_state(MySharedState("world!"));
|
||||||
cx.render(rsx!(Child {}))
|
cx.render(rsx!(Child {}))
|
||||||
};
|
};
|
||||||
|
|
||||||
static Child: Component<()> = |cx, props| {
|
static Child: Component<()> = |cx| {
|
||||||
let shared = cx.consume_state::<MySharedState>()?;
|
let shared = cx.consume_state::<MySharedState>()?;
|
||||||
cx.render(rsx!("Hello, {shared.0}"))
|
cx.render(rsx!("Hello, {shared.0}"))
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ use DomEdit::*;
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn app_runs() {
|
fn app_runs() {
|
||||||
static App: Component<()> = |cx, props| rsx!(cx, div{"hello"} );
|
static App: Component<()> = |cx| rsx!(cx, div{"hello"} );
|
||||||
|
|
||||||
let mut vdom = VirtualDom::new(App);
|
let mut vdom = VirtualDom::new(App);
|
||||||
let edits = vdom.rebuild();
|
let edits = vdom.rebuild();
|
||||||
@@ -27,7 +27,7 @@ fn app_runs() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn fragments_work() {
|
fn fragments_work() {
|
||||||
static App: Component<()> = |cx, props| {
|
static App: Component<()> = |cx| {
|
||||||
cx.render(rsx!(
|
cx.render(rsx!(
|
||||||
div{"hello"}
|
div{"hello"}
|
||||||
div{"goodbye"}
|
div{"goodbye"}
|
||||||
@@ -41,7 +41,7 @@ fn fragments_work() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn lists_work() {
|
fn lists_work() {
|
||||||
static App: Component<()> = |cx, props| {
|
static App: Component<()> = |cx| {
|
||||||
cx.render(rsx!(
|
cx.render(rsx!(
|
||||||
h1 {"hello"}
|
h1 {"hello"}
|
||||||
{(0..6).map(|f| rsx!(span{ "{f}" }))}
|
{(0..6).map(|f| rsx!(span{ "{f}" }))}
|
||||||
@@ -54,7 +54,7 @@ fn lists_work() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn conditional_rendering() {
|
fn conditional_rendering() {
|
||||||
static App: Component<()> = |cx, props| {
|
static App: Component<()> = |cx| {
|
||||||
cx.render(rsx!(
|
cx.render(rsx!(
|
||||||
h1 {"hello"}
|
h1 {"hello"}
|
||||||
{true.then(|| rsx!(span{ "a" }))}
|
{true.then(|| rsx!(span{ "a" }))}
|
||||||
@@ -87,13 +87,13 @@ fn conditional_rendering() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn child_components() {
|
fn child_components() {
|
||||||
static App: Component<()> = |cx, props| {
|
static App: Component<()> = |cx| {
|
||||||
cx.render(rsx!(
|
cx.render(rsx!(
|
||||||
{true.then(|| rsx!(Child { }))}
|
{true.then(|| rsx!(Child { }))}
|
||||||
{false.then(|| rsx!(Child { }))}
|
{false.then(|| rsx!(Child { }))}
|
||||||
))
|
))
|
||||||
};
|
};
|
||||||
static Child: Component<()> = |cx, props| {
|
static Child: Component<()> = |cx| {
|
||||||
cx.render(rsx!(
|
cx.render(rsx!(
|
||||||
h1 {"hello"}
|
h1 {"hello"}
|
||||||
h1 {"goodbye"}
|
h1 {"goodbye"}
|
||||||
|
|||||||
@@ -6,11 +6,21 @@ use std::{
|
|||||||
rc::Rc,
|
rc::Rc,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub trait UseStateA<'a, T> {
|
||||||
|
fn use_state(&self, initial_state_fn: impl FnOnce() -> T) -> UseState<'a, T>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, P, T> UseStateA<'a, T> for Scope<'a, P> {
|
||||||
|
fn use_state(&self, initial_state_fn: impl FnOnce() -> T) -> UseState<'a, T> {
|
||||||
|
use_state(self.scope, initial_state_fn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Store state between component renders!
|
/// Store state between component renders!
|
||||||
///
|
///
|
||||||
/// ## Dioxus equivalent of useState, designed for Rust
|
/// ## Dioxus equivalent of useState, designed for Rust
|
||||||
///
|
///
|
||||||
/// The Dioxus version of `useState` is the "king daddy" of state management. It allows you to ergonomically store and
|
/// The Dioxus version of `useState` for state management inside components. It allows you to ergonomically store and
|
||||||
/// modify state between component renders. When the state is updated, the component will re-render.
|
/// modify state between component renders. When the state is updated, the component will re-render.
|
||||||
///
|
///
|
||||||
/// Dioxus' use_state basically wraps a RefCell with helper methods and integrates it with the VirtualDOM update system.
|
/// Dioxus' use_state basically wraps a RefCell with helper methods and integrates it with the VirtualDOM update system.
|
||||||
@@ -34,19 +44,18 @@ use std::{
|
|||||||
///
|
///
|
||||||
///
|
///
|
||||||
/// Usage:
|
/// Usage:
|
||||||
/// ```ignore
|
|
||||||
/// const Example: FC<()> = |cx, props|{
|
|
||||||
/// let counter = use_state(cx, || 0);
|
|
||||||
/// let increment = |_| counter += 1;
|
|
||||||
/// let decrement = |_| counter += 1;
|
|
||||||
///
|
///
|
||||||
/// html! {
|
/// ```ignore
|
||||||
/// <div>
|
/// const Example: Component<()> = |cx| {
|
||||||
/// <h1>"Counter: {counter}" </h1>
|
/// let counter = use_state(&cx, || 0);
|
||||||
/// <button onclick={increment}> "Increment" </button>
|
///
|
||||||
/// <button onclick={decrement}> "Decrement" </button>
|
/// cx.render(rsx! {
|
||||||
/// </div>
|
/// div {
|
||||||
/// }
|
/// h1 { "Counter: {counter}" }
|
||||||
|
/// button { onclick: move |_| counter += 1, "Increment" }
|
||||||
|
/// button { onclick: move |_| counter -= 1, "Decrement" }
|
||||||
|
/// }
|
||||||
|
/// ))
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn use_state<'a, T: 'static>(
|
pub fn use_state<'a, T: 'static>(
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ impl<R: Routable> RouterService<R> {
|
|||||||
/// This hould only be used once per app
|
/// This hould only be used once per app
|
||||||
///
|
///
|
||||||
/// You can manually parse the route if you want, but the derived `parse` method on `Routable` will also work just fine
|
/// You can manually parse the route if you want, but the derived `parse` method on `Routable` will also work just fine
|
||||||
pub fn use_router<R: Routable>(cx: Scope, mut parse: impl FnMut(&str) -> R + 'static) -> &R {
|
pub fn use_router<R: Routable>(cx: &ScopeState, mut parse: impl FnMut(&str) -> R + 'static) -> &R {
|
||||||
// for the web, attach to the history api
|
// for the web, attach to the history api
|
||||||
cx.use_hook(
|
cx.use_hook(
|
||||||
|f| {
|
|f| {
|
||||||
@@ -133,12 +133,12 @@ pub fn use_router<R: Routable>(cx: Scope, mut parse: impl FnMut(&str) -> R + 'st
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn use_router_service<R: Routable>(cx: Scope) -> Option<&Rc<RouterService<R>>> {
|
pub fn use_router_service<R: Routable>(cx: &ScopeState) -> Option<&Rc<RouterService<R>>> {
|
||||||
cx.use_hook(|_| cx.consume_state::<RouterService<R>>(), |f| f.as_ref())
|
cx.use_hook(|_| cx.consume_state::<RouterService<R>>(), |f| f.as_ref())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Props)]
|
#[derive(Props)]
|
||||||
pub struct LinkProps<R: Routable> {
|
pub struct LinkProps<'a, R: Routable> {
|
||||||
to: R,
|
to: R,
|
||||||
|
|
||||||
/// The url that gets pushed to the history stack
|
/// The url that gets pushed to the history stack
|
||||||
@@ -157,16 +157,16 @@ pub struct LinkProps<R: Routable> {
|
|||||||
href: fn(&R) -> String,
|
href: fn(&R) -> String,
|
||||||
|
|
||||||
#[builder(default)]
|
#[builder(default)]
|
||||||
children: Element,
|
children: Element<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn Link<R: Routable>(cx: Scope, props: &LinkProps<R>) -> Element {
|
pub fn Link<'a, R: Routable>(cx: Scope<'a, LinkProps<'a, R>>) -> Element {
|
||||||
let service = use_router_service::<R>(cx)?;
|
let service = use_router_service::<R>(&cx)?;
|
||||||
cx.render(rsx! {
|
cx.render(rsx! {
|
||||||
a {
|
a {
|
||||||
href: format_args!("{}", (props.href)(&props.to)),
|
href: format_args!("{}", (cx.props.href)(&cx.props.to)),
|
||||||
onclick: move |_| service.push_route(props.to.clone()),
|
onclick: move |_| service.push_route(cx.props.to.clone()),
|
||||||
{&props.children},
|
// todo!() {&cx.props.children},
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user