mirror of
https://github.com/DioxusLabs/dioxus
synced 2024-11-10 06:34:20 +00:00
feat: code quality improvements for core
This commit is contained in:
parent
37ed4bed8c
commit
00231adfa2
12 changed files with 124 additions and 758 deletions
|
@ -1,16 +1,11 @@
|
|||
use std::{
|
||||
cell::{RefCell, UnsafeCell},
|
||||
collections::HashMap,
|
||||
rc::Rc,
|
||||
sync::Arc,
|
||||
};
|
||||
use std::{cell::UnsafeCell, rc::Rc};
|
||||
|
||||
use crate::innerlude::*;
|
||||
use slotmap::{DefaultKey, SlotMap};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SharedArena {
|
||||
pub components: Arc<UnsafeCell<ScopeMap>>,
|
||||
pub components: Rc<UnsafeCell<ScopeMap>>,
|
||||
}
|
||||
pub type ScopeMap = SlotMap<DefaultKey, Scope>;
|
||||
|
||||
|
@ -21,7 +16,7 @@ enum MutStatus {
|
|||
|
||||
impl SharedArena {
|
||||
pub fn new(arena: ScopeMap) -> Self {
|
||||
let components = Arc::new(UnsafeCell::new(arena));
|
||||
let components = Rc::new(UnsafeCell::new(arena));
|
||||
SharedArena { components }
|
||||
}
|
||||
|
||||
|
|
|
@ -1,15 +1,11 @@
|
|||
use crate::innerlude::*;
|
||||
|
||||
use futures_util::FutureExt;
|
||||
|
||||
use std::any::Any;
|
||||
use std::cell::Cell;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use std::{
|
||||
any::TypeId,
|
||||
cell::RefCell,
|
||||
any::{Any, TypeId},
|
||||
cell::{Cell, RefCell},
|
||||
future::Future,
|
||||
marker::PhantomData,
|
||||
ops::Deref,
|
||||
rc::{Rc, Weak},
|
||||
};
|
||||
|
@ -316,23 +312,7 @@ Any function prefixed with "use" should not be called conditionally.
|
|||
///
|
||||
///
|
||||
pub fn submit_task(&self, task: FiberTask) -> TaskHandle {
|
||||
let r = (self.scope.task_submitter)(task);
|
||||
// self.scope.submit_task(task);
|
||||
// let r = task.then(|f| async {
|
||||
// //
|
||||
// });
|
||||
// self.use_hook(|| Box::new(r), |_| {}, |_| {});
|
||||
// *task = task.then(|f| async {
|
||||
// //
|
||||
// t
|
||||
// // ()
|
||||
// });
|
||||
// let new_fut = task.then(|f| async {
|
||||
// //
|
||||
// ()
|
||||
// });
|
||||
// self.tasks.borrow_mut().push(new_fut);
|
||||
|
||||
(self.scope.task_submitter)(task);
|
||||
TaskHandle { _p: PhantomData {} }
|
||||
}
|
||||
|
||||
|
|
|
@ -76,35 +76,3 @@ impl DebugRenderer {
|
|||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DebugVNodeSource {
|
||||
bump: Bump,
|
||||
}
|
||||
impl DebugVNodeSource {
|
||||
fn new() -> Self {
|
||||
Self { bump: Bump::new() }
|
||||
}
|
||||
|
||||
fn render_nodes(&self) -> VNode {
|
||||
// let cx = NodeFactory
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn ensure_creation() -> Result<(), ()> {
|
||||
// static Example: FC<()> = |cx| {
|
||||
// //
|
||||
// cx.render(html! { <div> "hello world" </div> })
|
||||
// };
|
||||
|
||||
// let mut dom = VirtualDom::new(Example);
|
||||
// let machine = DiffMachine::new();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ use crate::{innerlude::ScopeId, RealDomNode};
|
|||
/// However, the DomEditor only builds a change list - it does not apply them. In contrast with the "RealDom", the DomEditor
|
||||
/// is cancellable and flushable. At any moment in time, Dioxus may choose to completely clear the edit list and start over.
|
||||
///
|
||||
/// This behavior is used in the cooperative scheduling algorithm
|
||||
/// This behavior is used in the cooperative scheduling algorithm.
|
||||
pub struct DomEditor<'real, 'bump> {
|
||||
pub edits: &'real mut Vec<DomEdit<'bump>>,
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ pub mod prelude {
|
|||
|
||||
// types used internally that are important
|
||||
pub(crate) mod innerlude {
|
||||
pub use crate::arena::*;
|
||||
pub use crate::bumpframe::*;
|
||||
pub use crate::component::*;
|
||||
pub use crate::context::*;
|
||||
|
@ -33,6 +34,7 @@ pub(crate) mod innerlude {
|
|||
pub use crate::editor::*;
|
||||
pub use crate::error::*;
|
||||
pub use crate::events::*;
|
||||
pub use crate::hooklist::*;
|
||||
pub use crate::nodes::*;
|
||||
pub use crate::scope::*;
|
||||
pub use crate::tasks::*;
|
||||
|
@ -46,6 +48,7 @@ pub(crate) mod innerlude {
|
|||
|
||||
pub mod exports {
|
||||
// export important things here
|
||||
pub use bumpalo;
|
||||
}
|
||||
|
||||
pub mod arena;
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
//! Virtual Node Support
|
||||
//! --------------------
|
||||
//! VNodes represent lazily-constructed VDom trees that support diffing and event handlers.
|
||||
//!
|
||||
//! These VNodes should be *very* cheap and *very* fast to construct - building a full tree should be insanely quick.
|
||||
|
||||
use crate::{
|
||||
events::VirtualEvent,
|
||||
innerlude::{Context, Properties, RealDomNode, Scope, ScopeId, FC},
|
||||
|
@ -16,8 +16,13 @@ use std::{
|
|||
|
||||
pub struct VNode<'src> {
|
||||
pub kind: VNodeKind<'src>,
|
||||
pub dom_id: Cell<RealDomNode>,
|
||||
pub key: Option<&'src str>,
|
||||
pub(crate) dom_id: Cell<RealDomNode>,
|
||||
pub(crate) key: Option<&'src str>,
|
||||
}
|
||||
impl VNode<'_> {
|
||||
fn key(&self) -> Option<&str> {
|
||||
self.key
|
||||
}
|
||||
}
|
||||
|
||||
/// Tools for the base unit of the virtual dom - the VNode
|
||||
|
@ -27,9 +32,13 @@ pub struct VNode<'src> {
|
|||
/// limit the amount of heap allocations / overly large enum sizes.
|
||||
pub enum VNodeKind<'src> {
|
||||
Text(VText<'src>),
|
||||
|
||||
Element(&'src VElement<'src>),
|
||||
|
||||
Fragment(VFragment<'src>),
|
||||
|
||||
Component(&'src VComponent<'src>),
|
||||
|
||||
Suspended { node: Rc<Cell<RealDomNode>> },
|
||||
}
|
||||
|
||||
|
@ -55,6 +64,7 @@ pub trait DioxusElement {
|
|||
Self::NAME_SPACE
|
||||
}
|
||||
}
|
||||
|
||||
pub struct VElement<'a> {
|
||||
// tag is always static
|
||||
pub tag_name: &'static str,
|
||||
|
@ -75,9 +85,13 @@ pub struct VElement<'a> {
|
|||
#[derive(Clone, Debug)]
|
||||
pub struct Attribute<'a> {
|
||||
pub name: &'static str,
|
||||
|
||||
pub value: &'a str,
|
||||
|
||||
pub is_static: bool,
|
||||
|
||||
pub is_volatile: bool,
|
||||
|
||||
// Doesn't exist in the html spec, mostly used to denote "style" tags - could be for any type of group
|
||||
pub namespace: Option<&'static str>,
|
||||
}
|
||||
|
@ -87,8 +101,11 @@ pub struct Attribute<'a> {
|
|||
pub struct Listener<'bump> {
|
||||
/// The type of event to listen for.
|
||||
pub(crate) event: &'static str,
|
||||
|
||||
pub scope: ScopeId,
|
||||
pub mounted_node: &'bump Cell<RealDomNode>,
|
||||
|
||||
pub mounted_node: &'bump mut Cell<RealDomNode>,
|
||||
|
||||
pub(crate) callback: &'bump dyn FnMut(VirtualEvent),
|
||||
}
|
||||
|
||||
|
@ -96,9 +113,13 @@ pub struct Listener<'bump> {
|
|||
/// Only supports the functional syntax
|
||||
pub struct VComponent<'src> {
|
||||
pub ass_scope: Cell<Option<ScopeId>>,
|
||||
|
||||
pub(crate) caller: Rc<dyn Fn(&Scope) -> VNode>,
|
||||
|
||||
pub(crate) children: &'src [VNode<'src>],
|
||||
|
||||
pub(crate) comparator: Option<&'src dyn Fn(&VComponent) -> bool>,
|
||||
|
||||
pub is_static: bool,
|
||||
|
||||
// a pointer into the bump arena (given by the 'src lifetime)
|
||||
|
@ -124,20 +145,7 @@ impl<'a> NodeFactory<'a> {
|
|||
&self.scope_ref.cur_frame().bump
|
||||
}
|
||||
|
||||
pub const fn const_text(&self, text: &'static str) -> VNodeKind<'static> {
|
||||
VNodeKind::Text(VText {
|
||||
is_static: true,
|
||||
text,
|
||||
})
|
||||
}
|
||||
|
||||
pub const fn const_fragment(&self, children: &'static [VNode<'static>]) -> VNodeKind<'static> {
|
||||
VNodeKind::Fragment(VFragment {
|
||||
children,
|
||||
is_static: true,
|
||||
})
|
||||
}
|
||||
|
||||
/// Used in a place or two to make it easier to build vnodes from dummy text
|
||||
pub fn static_text(text: &'static str) -> VNode {
|
||||
VNode {
|
||||
dom_id: RealDomNode::empty_cell(),
|
||||
|
@ -149,6 +157,9 @@ impl<'a> NodeFactory<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Parses a lazy text Arguments and returns a string and a flag indicating if the text is 'static
|
||||
///
|
||||
/// Text that's static may be pointer compared, making it cheaper to diff
|
||||
pub fn raw_text(&self, args: Arguments) -> (&'a str, bool) {
|
||||
match args.as_str() {
|
||||
Some(static_str) => (static_str, true),
|
||||
|
@ -162,6 +173,7 @@ impl<'a> NodeFactory<'a> {
|
|||
}
|
||||
|
||||
/// Create some text that's allocated along with the other vnodes
|
||||
///
|
||||
pub fn text(&self, args: Arguments) -> VNode<'a> {
|
||||
let (text, is_static) = self.raw_text(args);
|
||||
VNode {
|
||||
|
@ -171,27 +183,40 @@ impl<'a> NodeFactory<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn raw_element(
|
||||
&self,
|
||||
_tag: &'static str,
|
||||
_listeners: &[Listener],
|
||||
_attributes: &[Attribute],
|
||||
_children: &'a [VNode<'a>],
|
||||
) -> VNode<'a> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn element(
|
||||
&self,
|
||||
el: impl DioxusElement,
|
||||
listeners: &'a [Listener<'a>],
|
||||
listeners: &'a mut [Listener<'a>],
|
||||
attributes: &'a [Attribute<'a>],
|
||||
children: &'a [VNode<'a>],
|
||||
key: Option<&'a str>,
|
||||
) -> VNode<'a> {
|
||||
self.raw_element(
|
||||
el.tag_name(),
|
||||
el.namespace(),
|
||||
listeners,
|
||||
attributes,
|
||||
children,
|
||||
key,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn raw_element(
|
||||
&self,
|
||||
tag: &'static str,
|
||||
namespace: Option<&'static str>,
|
||||
listeners: &'a mut [Listener],
|
||||
attributes: &'a [Attribute],
|
||||
children: &'a [VNode<'a>],
|
||||
key: Option<&'a str>,
|
||||
) -> VNode<'a> {
|
||||
// We take the references directly from the bump arena
|
||||
// TODO: this code shouldn't necessarily be here of all places
|
||||
// It would make more sense to do this in diffing
|
||||
|
||||
let mut queue = self.scope_ref.listeners.borrow_mut();
|
||||
for listener in listeners {
|
||||
let mounted = listener.mounted_node as *const _ as *mut _;
|
||||
for listener in listeners.iter_mut() {
|
||||
let mounted = listener.mounted_node as *mut _;
|
||||
let callback = listener.callback as *const _ as *mut _;
|
||||
queue.push((mounted, callback))
|
||||
}
|
||||
|
@ -200,14 +225,16 @@ impl<'a> NodeFactory<'a> {
|
|||
dom_id: RealDomNode::empty_cell(),
|
||||
key,
|
||||
kind: VNodeKind::Element(self.bump().alloc(VElement {
|
||||
tag_name: el.tag_name(),
|
||||
namespace: el.namespace(),
|
||||
static_listeners: false,
|
||||
tag_name: tag,
|
||||
namespace,
|
||||
listeners,
|
||||
static_attrs: false,
|
||||
attributes,
|
||||
static_children: false,
|
||||
children,
|
||||
|
||||
// todo: wire up more constization
|
||||
static_listeners: false,
|
||||
static_attrs: false,
|
||||
static_children: false,
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
@ -243,12 +270,15 @@ impl<'a> NodeFactory<'a> {
|
|||
&self,
|
||||
component: FC<P>,
|
||||
props: P,
|
||||
key: Option<&'a str>, // key: NodeKey<'a>,
|
||||
key: Option<&'a str>,
|
||||
children: &'a [VNode<'a>],
|
||||
) -> VNode<'a>
|
||||
where
|
||||
P: Properties + 'a,
|
||||
{
|
||||
// TODO
|
||||
// It's somewhat wrong to go about props like this
|
||||
|
||||
// We don't want the fat part of the fat pointer
|
||||
// This function does static dispatch so we don't need any VTable stuff
|
||||
let props = self.bump().alloc(props);
|
||||
|
@ -271,6 +301,8 @@ impl<'a> NodeFactory<'a> {
|
|||
}
|
||||
}));
|
||||
|
||||
let is_static = children.len() == 0 && P::IS_STATIC && key.is_none();
|
||||
|
||||
VNode {
|
||||
key,
|
||||
dom_id: Cell::new(RealDomNode::empty()),
|
||||
|
@ -280,7 +312,7 @@ impl<'a> NodeFactory<'a> {
|
|||
raw_props,
|
||||
children,
|
||||
caller: NodeFactory::create_component_caller(component, raw_props),
|
||||
is_static: children.len() == 0 && P::IS_STATIC && key.is_none(),
|
||||
is_static,
|
||||
ass_scope: Cell::new(None),
|
||||
})),
|
||||
}
|
||||
|
@ -313,10 +345,26 @@ impl<'a> NodeFactory<'a> {
|
|||
node_iter: impl IntoIterator<Item = impl IntoVNode<'a>>,
|
||||
) -> VNode<'a> {
|
||||
let mut nodes = bumpalo::collections::Vec::new_in(self.bump());
|
||||
// TODO throw an error if there are nodes without keys
|
||||
|
||||
for node in node_iter.into_iter() {
|
||||
nodes.push(node.into_vnode(self));
|
||||
}
|
||||
|
||||
if cfg!(debug_assertions) {
|
||||
if nodes.len() > 1 {
|
||||
if nodes.last().unwrap().key().is_none() {
|
||||
log::error!(
|
||||
r#"
|
||||
Warning: Each child in an array or iterator should have a unique "key" prop.
|
||||
Not providing a key will lead to poor performance with lists.
|
||||
See docs.rs/dioxus for more information.
|
||||
---
|
||||
To help you identify where this error is coming from, we've generated a backtrace.
|
||||
"#,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
VNode {
|
||||
dom_id: RealDomNode::empty_cell(),
|
||||
key: None,
|
||||
|
|
|
@ -1,484 +0,0 @@
|
|||
/// Typically constructed with element-specific constructors, eg the `div`
|
||||
/// function for building `<div>` elements or the `button` function for building
|
||||
/// `<button>` elements.
|
||||
#[derive(Debug)]
|
||||
pub struct ElementBuilder<'a, 'b, Listeners, Attributes, Children>
|
||||
where
|
||||
Listeners: 'a + AsRef<[Listener<'a>]>,
|
||||
Attributes: 'a + AsRef<[Attribute<'a>]>,
|
||||
Children: 'a + AsRef<[VNode<'a>]>,
|
||||
{
|
||||
cx: &'b NodeFactory<'a>,
|
||||
key: NodeKey<'a>,
|
||||
tag_name: &'static str,
|
||||
listeners: Listeners,
|
||||
attributes: Attributes,
|
||||
children: Children,
|
||||
namespace: Option<&'static str>,
|
||||
}
|
||||
|
||||
impl<'a, 'b>
|
||||
ElementBuilder<
|
||||
'a,
|
||||
'b,
|
||||
bumpalo::collections::Vec<'a, Listener<'a>>,
|
||||
bumpalo::collections::Vec<'a, Attribute<'a>>,
|
||||
bumpalo::collections::Vec<'a, VNode<'a>>,
|
||||
>
|
||||
{
|
||||
/// Create a new `ElementBuilder` for an element with the given tag name.
|
||||
///
|
||||
/// In general, only use this constructor if the tag is dynamic (i.e. you
|
||||
/// might build a `<div>` or you might build a `<span>` and you don't know
|
||||
/// until runtime). Prefer using the tag-specific constructors instead:
|
||||
/// `div(bump)` or `span(bump)`, etc.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use dioxus::{builder::*, bumpalo::Bump};
|
||||
///
|
||||
/// let b = Bump::new();
|
||||
///
|
||||
/// let tag_name = if flip_coin() {
|
||||
/// "div"
|
||||
/// } else {
|
||||
/// "span"
|
||||
/// };
|
||||
///
|
||||
/// let my_element_builder = ElementBuilder::new(&b, tag_name);
|
||||
/// # fn flip_coin() -> bool { true }
|
||||
/// ```
|
||||
pub fn new(cx: &'b NodeFactory<'a>, tag_name: &'static str) -> Self {
|
||||
let bump = cx.bump();
|
||||
ElementBuilder {
|
||||
cx,
|
||||
key: NodeKey::NONE,
|
||||
tag_name,
|
||||
listeners: bumpalo::collections::Vec::new_in(bump),
|
||||
attributes: bumpalo::collections::Vec::new_in(bump),
|
||||
children: bumpalo::collections::Vec::new_in(bump),
|
||||
namespace: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b, Listeners, Attributes, Children>
|
||||
ElementBuilder<'a, 'b, Listeners, Attributes, Children>
|
||||
where
|
||||
Listeners: 'a + AsRef<[Listener<'a>]>,
|
||||
Attributes: 'a + AsRef<[Attribute<'a>]>,
|
||||
Children: 'a + AsRef<[VNode<'a>]>,
|
||||
{
|
||||
/// Set the listeners for this element.
|
||||
///
|
||||
/// You can use this method to customize the backing storage for listeners,
|
||||
/// for example to use a fixed-size array instead of the default
|
||||
/// dynamically-sized `bumpalo::collections::Vec`.
|
||||
///
|
||||
/// Any listeners already added to the builder will be overridden.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// use dioxus::{builder::*, bumpalo::Bump};
|
||||
///
|
||||
/// let b = Bump::new();
|
||||
///
|
||||
/// // Create a `<div>` with a fixed-size array of two listeners.
|
||||
/// let my_div = div(&b)
|
||||
/// .listeners([
|
||||
/// on(&b, "click", |root, vdom, event| {
|
||||
/// // ...
|
||||
/// }),
|
||||
/// on(&b, "dblclick", |root, vdom, event| {
|
||||
/// // ...
|
||||
/// }),
|
||||
/// ])
|
||||
/// .finish();
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn listeners<L>(self, listeners: L) -> ElementBuilder<'a, 'b, L, Attributes, Children>
|
||||
where
|
||||
L: 'a + AsRef<[Listener<'a>]>,
|
||||
{
|
||||
ElementBuilder {
|
||||
cx: self.cx,
|
||||
key: self.key,
|
||||
tag_name: self.tag_name,
|
||||
listeners,
|
||||
attributes: self.attributes,
|
||||
children: self.children,
|
||||
namespace: self.namespace,
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the attributes for this element.
|
||||
///
|
||||
/// You can use this method to customize the backing storage for attributes,
|
||||
/// for example to use a fixed-size array instead of the default
|
||||
/// dynamically-sized `bumpalo::collections::Vec`.
|
||||
///
|
||||
/// Any attributes already added to the builder will be overridden.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// use dioxus::{builder::*, bumpalo::Bump, Attribute};
|
||||
///
|
||||
/// let b = Bump::new();
|
||||
///
|
||||
/// // Create a `<div>` with a fixed-size array of two attributes.
|
||||
/// let my_div = div(&b)
|
||||
/// .attributes([
|
||||
/// attr("id", "my-div"),
|
||||
/// attr("class", "notification"),
|
||||
/// ])
|
||||
/// .finish();
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn attributes<A>(self, attributes: A) -> ElementBuilder<'a, 'b, Listeners, A, Children>
|
||||
where
|
||||
A: 'a + AsRef<[Attribute<'a>]>,
|
||||
{
|
||||
ElementBuilder {
|
||||
cx: self.cx,
|
||||
key: self.key,
|
||||
tag_name: self.tag_name,
|
||||
listeners: self.listeners,
|
||||
attributes,
|
||||
children: self.children,
|
||||
namespace: self.namespace,
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the children for this element.
|
||||
///
|
||||
/// You can use this method to customize the backing storage for children,
|
||||
/// for example to use a fixed-size array instead of the default
|
||||
/// dynamically-sized `bumpalo::collections::Vec`.
|
||||
///
|
||||
/// Any children already added to the builder will be overridden.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// use dioxus::{builder::*, bumpalo::Bump};
|
||||
///
|
||||
/// let b = Bump::new();
|
||||
///
|
||||
/// // Create a `<div>` with a fixed-size array of two `<span>` children.
|
||||
/// let my_div = div(&b)
|
||||
/// .children([
|
||||
/// span(&b).finish(),
|
||||
/// span(&b).finish(),
|
||||
/// ])
|
||||
/// .finish();
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn children<C>(self, children: C) -> ElementBuilder<'a, 'b, Listeners, Attributes, C>
|
||||
where
|
||||
C: 'a + AsRef<[VNode<'a>]>,
|
||||
{
|
||||
ElementBuilder {
|
||||
cx: self.cx,
|
||||
key: self.key,
|
||||
tag_name: self.tag_name,
|
||||
listeners: self.listeners,
|
||||
attributes: self.attributes,
|
||||
children,
|
||||
namespace: self.namespace,
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the namespace for this element.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// use dioxus::{builder::*, bumpalo::Bump};
|
||||
///
|
||||
/// let b = Bump::new();
|
||||
///
|
||||
/// // Create a `<td>` tag with an xhtml namespace
|
||||
/// let my_td = td(&b)
|
||||
/// .namespace(Some("http://www.w3.org/1999/xhtml"))
|
||||
/// .finish();
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn namespace(self, namespace: Option<&'static str>) -> Self {
|
||||
ElementBuilder {
|
||||
cx: self.cx,
|
||||
key: self.key,
|
||||
tag_name: self.tag_name,
|
||||
listeners: self.listeners,
|
||||
attributes: self.attributes,
|
||||
children: self.children,
|
||||
namespace,
|
||||
}
|
||||
}
|
||||
|
||||
/// Set this element's key.
|
||||
///
|
||||
/// When diffing sets of siblings, if an old sibling and new sibling share a
|
||||
/// key, then they will always reuse the same physical DOM VNode. This is
|
||||
/// important when using CSS animations, web components, third party JS, or
|
||||
/// anything else that makes the diffing implementation observable.
|
||||
///
|
||||
/// Do not use keys if such a scenario does not apply. Keyed diffing is
|
||||
/// generally more expensive than not, since it is putting greater
|
||||
/// constraints on the diffing algorithm.
|
||||
///
|
||||
/// # Invariants You Must Uphold
|
||||
///
|
||||
/// The key may not be `u32::MAX`, which is a reserved key value.
|
||||
///
|
||||
/// Keys must be unique among siblings.
|
||||
///
|
||||
/// All sibling VNodes must be keyed, or they must all not be keyed. You may
|
||||
/// not mix keyed and unkeyed siblings.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// use dioxus::{builder::*, bumpalo::Bump};
|
||||
///
|
||||
/// let b = Bump::new();
|
||||
///
|
||||
/// let my_li = li(&b)
|
||||
/// .key(1337)
|
||||
/// .finish();
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn key(mut self, key: &'a str) -> Self {
|
||||
self.key = NodeKey(Some(key));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn key2(mut self, args: Arguments) -> Self {
|
||||
let key = match args.as_str() {
|
||||
Some(static_str) => static_str,
|
||||
None => {
|
||||
use bumpalo::core_alloc::fmt::Write;
|
||||
let mut s = bumpalo::collections::String::new_in(self.cx.bump());
|
||||
s.write_fmt(args).unwrap();
|
||||
s.into_bump_str()
|
||||
}
|
||||
};
|
||||
self.key = NodeKey(Some(key));
|
||||
self
|
||||
}
|
||||
|
||||
/// Create the virtual DOM VNode described by this builder.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// use dioxus::{builder::*, bumpalo::Bump, VNode};
|
||||
///
|
||||
/// let b = Bump::new();
|
||||
///
|
||||
/// // Start with a builder...
|
||||
/// let builder: ElementBuilder<_, _, _> = div(&b);
|
||||
///
|
||||
/// // ...and finish it to create a virtual DOM VNode!
|
||||
/// let my_div: VNode = builder.finish();
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn finish(mut self) -> VNode<'a> {
|
||||
let bump = self.cx.bump();
|
||||
|
||||
let children: &'a Children = bump.alloc(self.children);
|
||||
let children: &'a [VNode<'a>] = children.as_ref();
|
||||
|
||||
let listeners: &'a Listeners = bump.alloc(self.listeners);
|
||||
let listeners: &'a [Listener<'a>] = listeners.as_ref();
|
||||
|
||||
for listener in listeners {
|
||||
// bump the context id forward
|
||||
let id = self.cx.listener_id.get();
|
||||
self.cx.listener_id.set(id + 1);
|
||||
|
||||
// Add this listener to the context list
|
||||
// This casts the listener to a self-referential pointer
|
||||
// This is okay because the bump arena is stable
|
||||
|
||||
// TODO: maybe not add it into CTX immediately
|
||||
let r = unsafe { std::mem::transmute::<&Listener<'a>, &Listener<'static>>(listener) };
|
||||
self.cx.scope_ref.listeners.borrow_mut().push((
|
||||
r.mounted_node as *const _ as *mut _,
|
||||
r.callback as *const _ as *mut _,
|
||||
));
|
||||
}
|
||||
|
||||
let attributes: &'a Attributes = bump.alloc(self.attributes);
|
||||
let attributes: &'a [Attribute<'a>] = attributes.as_ref();
|
||||
|
||||
VNode::element(
|
||||
bump,
|
||||
self.key,
|
||||
self.tag_name,
|
||||
listeners,
|
||||
attributes,
|
||||
children,
|
||||
self.namespace,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b, Attributes, Children>
|
||||
ElementBuilder<'a, 'b, bumpalo::collections::Vec<'a, Listener<'a>>, Attributes, Children>
|
||||
where
|
||||
Attributes: 'a + AsRef<[Attribute<'a>]>,
|
||||
Children: 'a + AsRef<[VNode<'a>]>,
|
||||
{
|
||||
// / Add a new event listener to this element.
|
||||
// /
|
||||
// / The `event` string specifies which event will be listened for. The
|
||||
// / `callback` function is the function that will be invoked if the
|
||||
// / specified event occurs.
|
||||
// /
|
||||
// / # Example
|
||||
// /
|
||||
// / ```no_run
|
||||
// / use dioxus::{builder::*, bumpalo::Bump};
|
||||
// /
|
||||
// / let b = Bump::new();
|
||||
// /
|
||||
// / // A button that does something when clicked!
|
||||
// / let my_button = button(&b)
|
||||
// / .on("click", |event| {
|
||||
// / // ...
|
||||
// / })
|
||||
// / .finish();
|
||||
// / ```
|
||||
// pub fn on(self, event: &'static str, callback: impl FnMut(VirtualEvent) + 'a) -> Self {
|
||||
// let bump = &self.cx.bump();
|
||||
// let listener = Listener {
|
||||
// event,
|
||||
// callback: bump.alloc(callback),
|
||||
// scope: self.cx.scope_ref.arena_idx,
|
||||
// mounted_node: bump.alloc(Cell::new(RealDomNode::empty())),
|
||||
// };
|
||||
// self.add_listener(listener)
|
||||
// }
|
||||
|
||||
// pub fn add_listener(mut self, listener: Listener<'a>) -> Self {
|
||||
// self.listeners.push(listener);
|
||||
|
||||
// // bump the context id forward
|
||||
// let id = self.cx.listener_id.get();
|
||||
// self.cx.listener_id.set(id + 1);
|
||||
|
||||
// // Add this listener to the context list
|
||||
// // This casts the listener to a self-referential pointer
|
||||
// // This is okay because the bump arena is stable
|
||||
// self.listeners.last().map(|g| {
|
||||
// let r = unsafe { std::mem::transmute::<&Listener<'a>, &Listener<'static>>(g) };
|
||||
// self.cx.scope_ref.listeners.borrow_mut().push((
|
||||
// r.mounted_node as *const _ as *mut _,
|
||||
// r.callback as *const _ as *mut _,
|
||||
// ));
|
||||
// });
|
||||
|
||||
// self
|
||||
// }
|
||||
}
|
||||
|
||||
impl<'a, 'b, Listeners, Children>
|
||||
ElementBuilder<'a, 'b, Listeners, bumpalo::collections::Vec<'a, Attribute<'a>>, Children>
|
||||
where
|
||||
Listeners: 'a + AsRef<[Listener<'a>]>,
|
||||
Children: 'a + AsRef<[VNode<'a>]>,
|
||||
{
|
||||
/// Add a new attribute to this element.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// use dioxus::{builder::*, bumpalo::Bump};
|
||||
///
|
||||
/// let b = Bump::new();
|
||||
///
|
||||
/// // Create the `<div id="my-div"/>` element.
|
||||
/// let my_div = div(&b).attr("id", "my-div").finish();
|
||||
/// ```
|
||||
pub fn attr(mut self, name: &'static str, args: std::fmt::Arguments) -> Self {
|
||||
let (value, is_static) = raw_text(self.cx.bump(), args);
|
||||
|
||||
self.attributes.push(Attribute {
|
||||
name,
|
||||
value,
|
||||
is_static,
|
||||
namespace: None,
|
||||
});
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b, Listeners, Attributes>
|
||||
ElementBuilder<'a, 'b, Listeners, Attributes, bumpalo::collections::Vec<'a, VNode<'a>>>
|
||||
where
|
||||
Listeners: 'a + AsRef<[Listener<'a>]>,
|
||||
Attributes: 'a + AsRef<[Attribute<'a>]>,
|
||||
{
|
||||
/// Add a new child to this element.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// use dioxus::{builder::*, bumpalo::Bump};
|
||||
/// use js_sys::Math;
|
||||
///
|
||||
/// let b = Bump::new();
|
||||
///
|
||||
/// // Create `<p><span></span></p>`.
|
||||
/// let my_div = p(&b)
|
||||
/// .child(span(&b).finish())
|
||||
/// .finish();
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn child(mut self, child: VNode<'a>) -> Self {
|
||||
self.children.push(child);
|
||||
self
|
||||
}
|
||||
|
||||
/// Add multiple children to this element from an iterator.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// use dioxus::{builder::*, bumpalo::Bump};
|
||||
///
|
||||
/// let b = Bump::new();
|
||||
///
|
||||
/// let my_div = p(&b)
|
||||
/// .iter_child((0..10).map(|f| span(&b).finish())
|
||||
/// .finish();
|
||||
/// ```
|
||||
pub fn iter_child(mut self, nodes: impl IntoIterator<Item = impl IntoVNode<'a>>) -> Self {
|
||||
todo!();
|
||||
let len_before = self.children.len();
|
||||
for item in nodes {
|
||||
todo!()
|
||||
// let child = item.into_vnode(&self.cx);
|
||||
// self.children.push(child);
|
||||
}
|
||||
if cfg!(debug_assertions) {
|
||||
if self.children.len() > len_before + 1 {
|
||||
if self.children.last().unwrap().key().is_none() {
|
||||
log::error!(
|
||||
r#"
|
||||
Warning: Each child in an array or iterator should have a unique "key" prop.
|
||||
Not providing a key will lead to poor performance with lists.
|
||||
See docs.rs/dioxus for more information.
|
||||
---
|
||||
To help you identify where this error is coming from, we've generated a backtrace.
|
||||
"#,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
self
|
||||
}
|
||||
}
|
|
@ -1,6 +1,4 @@
|
|||
use crate::hooklist::HookList;
|
||||
use crate::{arena::SharedArena, innerlude::*};
|
||||
|
||||
use crate::innerlude::*;
|
||||
use std::{
|
||||
any::{Any, TypeId},
|
||||
cell::{Cell, RefCell},
|
||||
|
@ -10,17 +8,15 @@ use std::{
|
|||
rc::Rc,
|
||||
};
|
||||
|
||||
// We need to pin the hook so it doesn't move as we initialize the list of hooks
|
||||
type Hook = Box<dyn std::any::Any>;
|
||||
type EventChannel = Rc<dyn Fn()>;
|
||||
pub type WrappedCaller = dyn for<'b> Fn(&'b Scope) -> VNode<'b>;
|
||||
|
||||
/// Every component in Dioxus is represented by a `Scope`.
|
||||
///
|
||||
/// Scopes contain the state for hooks, the component's props, and other lifecycle information.
|
||||
///
|
||||
/// Scopes are allocated in a generational arena. As components are mounted/unmounted, they will replace slots of dead components.
|
||||
/// The actual contents of the hooks, though, will be allocated with the standard allocator. These should not allocate as frequently.
|
||||
///
|
||||
/// We expose the `Scope` type so downstream users can traverse the Dioxus VirtualDOM for whatever
|
||||
/// usecase they might have.
|
||||
pub struct Scope {
|
||||
// Book-keeping about the arena
|
||||
pub(crate) parent_idx: Option<ScopeId>,
|
||||
|
@ -47,13 +43,19 @@ pub struct Scope {
|
|||
|
||||
// Tasks
|
||||
pub(crate) task_submitter: TaskSubmitter,
|
||||
pub(crate) suspended_tasks: Vec<*mut Pin<Box<dyn Future<Output = VNode<'static>>>>>,
|
||||
|
||||
// A reference to the list of components.
|
||||
// This lets us traverse the component list whenever we need to access our parent or children.
|
||||
pub(crate) arena_link: SharedArena,
|
||||
}
|
||||
|
||||
// The type of the channel function
|
||||
type EventChannel = Rc<dyn Fn()>;
|
||||
|
||||
// The type of closure that wraps calling components
|
||||
pub type WrappedCaller = dyn for<'b> Fn(&'b Scope) -> VNode<'b>;
|
||||
|
||||
// The type of task that gets sent to the task scheduler
|
||||
pub type FiberTask = Pin<Box<dyn Future<Output = EventTrigger>>>;
|
||||
|
||||
impl Scope {
|
||||
|
@ -74,14 +76,7 @@ impl Scope {
|
|||
child_nodes: &'creator_node [VNode<'creator_node>],
|
||||
task_submitter: TaskSubmitter,
|
||||
) -> Self {
|
||||
log::debug!(
|
||||
"New scope created, height is {}, idx is {:?}",
|
||||
height,
|
||||
arena_idx
|
||||
);
|
||||
|
||||
let child_nodes = unsafe { std::mem::transmute(child_nodes) };
|
||||
|
||||
Self {
|
||||
child_nodes,
|
||||
caller,
|
||||
|
@ -97,15 +92,14 @@ impl Scope {
|
|||
shared_contexts: Default::default(),
|
||||
listeners: Default::default(),
|
||||
descendents: Default::default(),
|
||||
suspended_tasks: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_caller<'creator_node>(&mut self, caller: Rc<WrappedCaller>) {
|
||||
pub(crate) fn update_caller<'creator_node>(&mut self, caller: Rc<WrappedCaller>) {
|
||||
self.caller = caller;
|
||||
}
|
||||
|
||||
pub fn update_children<'creator_node>(
|
||||
pub(crate) fn update_children<'creator_node>(
|
||||
&mut self,
|
||||
child_nodes: &'creator_node [VNode<'creator_node>],
|
||||
) {
|
||||
|
@ -113,13 +107,14 @@ impl Scope {
|
|||
self.child_nodes = child_nodes;
|
||||
}
|
||||
|
||||
pub fn run_scope<'sel>(&'sel mut self) -> Result<()> {
|
||||
pub(crate) fn run_scope<'sel>(&'sel mut self) -> Result<()> {
|
||||
// Cycle to the next frame and then reset it
|
||||
// This breaks any latent references, invalidating every pointer referencing into it.
|
||||
// Remove all the outdated listeners
|
||||
|
||||
// This is a very dangerous operation
|
||||
self.frames.next().bump.reset();
|
||||
|
||||
log::debug!("clearing listeners!");
|
||||
// Remove all the outdated listeners
|
||||
self.listeners.borrow_mut().clear();
|
||||
|
||||
unsafe { self.hooks.reset() };
|
||||
|
@ -133,17 +128,6 @@ impl Scope {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Progress a suspended node
|
||||
pub fn progress_suspended(&mut self) -> Result<()> {
|
||||
// load the hook
|
||||
// downcast to our special state
|
||||
// run just this hook
|
||||
// create a new vnode
|
||||
// diff this new vnode with the original suspended vnode
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// this is its own function so we can preciesly control how lifetimes flow
|
||||
unsafe fn call_user_component<'a>(&'a self, caller: &WrappedCaller) -> VNode<'static> {
|
||||
let new_head: VNode<'a> = caller(self);
|
||||
|
@ -153,7 +137,7 @@ impl Scope {
|
|||
// A safe wrapper around calling listeners
|
||||
// calling listeners will invalidate the list of listeners
|
||||
// The listener list will be completely drained because the next frame will write over previous listeners
|
||||
pub fn call_listener(&mut self, trigger: EventTrigger) -> Result<()> {
|
||||
pub(crate) fn call_listener(&mut self, trigger: EventTrigger) -> Result<()> {
|
||||
let EventTrigger {
|
||||
real_node_id,
|
||||
event,
|
||||
|
@ -165,11 +149,6 @@ impl Scope {
|
|||
return Ok(());
|
||||
}
|
||||
|
||||
// todo: implement scanning for outdated events
|
||||
|
||||
// Convert the raw ptr into an actual object
|
||||
// This operation is assumed to be safe
|
||||
|
||||
log::debug!(
|
||||
"There are {:?} listeners associated with this scope {:#?}",
|
||||
self.listeners.borrow().len(),
|
||||
|
@ -178,7 +157,6 @@ impl Scope {
|
|||
|
||||
let listners = self.listeners.borrow_mut();
|
||||
|
||||
// let listener = listners.get(trigger);
|
||||
let raw_listener = listners.iter().find(|(domptr, _)| {
|
||||
let search = unsafe { &**domptr };
|
||||
let search_id = search.get();
|
||||
|
@ -201,7 +179,7 @@ impl Scope {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn submit_task(&self, task: FiberTask) {
|
||||
pub(crate) fn submit_task(&self, task: FiberTask) {
|
||||
log::debug!("Task submitted into scope");
|
||||
(self.task_submitter)(task);
|
||||
}
|
||||
|
@ -221,6 +199,7 @@ impl Scope {
|
|||
self.frames.cur_frame()
|
||||
}
|
||||
|
||||
/// Get the root VNode of this component
|
||||
#[inline]
|
||||
pub fn root<'a>(&'a self) -> &'a VNode<'a> {
|
||||
&self.frames.cur_frame().head_node
|
||||
|
|
|
@ -2,7 +2,4 @@
|
|||
//! -----------------------------
|
||||
//!
|
||||
//!
|
||||
//!
|
||||
//!
|
||||
//!
|
||||
//!
|
||||
//! TODO!
|
||||
|
|
|
@ -135,45 +135,3 @@ impl DTask {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// #[cfg(test)]
|
||||
// mod tests {
|
||||
// use std::time::Duration;
|
||||
|
||||
// use super::*;
|
||||
// use bumpalo::Bump;
|
||||
|
||||
// #[async_std::test]
|
||||
// async fn example() {
|
||||
// let bump = Bump::new();
|
||||
// type RawTask = Pin<Box<dyn Future<Output = ()>>>;
|
||||
// // build the three
|
||||
// let f1 = bump.alloc(Box::pin(async {
|
||||
// //
|
||||
// async_std::task::sleep(Duration::from_secs(3)).await;
|
||||
// println!("3 sec")
|
||||
// }) as RawTask);
|
||||
|
||||
// let f2 = bump.alloc(Box::pin(async {
|
||||
// //
|
||||
// async_std::task::sleep(Duration::from_secs(2)).await;
|
||||
// println!("2 sec")
|
||||
// }) as RawTask);
|
||||
|
||||
// let f3 = bump.alloc(Box::pin(async {
|
||||
// //
|
||||
// async_std::task::sleep(Duration::from_secs(1)).await;
|
||||
// println!("1 sec");
|
||||
// }) as RawTask);
|
||||
|
||||
// let mut queue = TaskQueue::new();
|
||||
// queue.submit_task(DTask::debug_new(f1));
|
||||
// queue.submit_task(DTask::debug_new(f2));
|
||||
// queue.submit_task(DTask::debug_new(f3));
|
||||
|
||||
// while !queue.is_empty() {
|
||||
// let next = queue.next().await;
|
||||
// println!("Event received {:#?}", next);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
|
|
@ -63,8 +63,6 @@ pub struct RealDomNode(pub u64);
|
|||
impl RealDomNode {
|
||||
#[inline]
|
||||
pub fn empty() -> Self {
|
||||
// let data = KeyData::from_ffi(u64::MIN);
|
||||
// let key: DefaultKey = data.into();
|
||||
Self(u64::MIN)
|
||||
}
|
||||
#[inline]
|
||||
|
@ -73,101 +71,31 @@ impl RealDomNode {
|
|||
}
|
||||
#[inline]
|
||||
pub fn from_u64(id: u64) -> Self {
|
||||
// let data = KeyData::from_ffi(id);
|
||||
// let key: DefaultKey = data.into();
|
||||
Self(id)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn as_u64(&self) -> u64 {
|
||||
// self.0.data().as_ffi()
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DebugDom {
|
||||
counter: u64,
|
||||
logging: bool,
|
||||
}
|
||||
impl DebugDom {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
counter: 0,
|
||||
logging: false,
|
||||
}
|
||||
}
|
||||
pub fn with_logging_enabled() -> Self {
|
||||
Self {
|
||||
counter: 0,
|
||||
logging: true,
|
||||
}
|
||||
Self { counter: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RealDom<'a> for DebugDom {
|
||||
fn raw_node_as_any(&self) -> &mut dyn std::any::Any {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn request_available_node(&mut self) -> RealDomNode {
|
||||
todo!()
|
||||
self.counter += 1;
|
||||
RealDomNode::from_u64(self.counter)
|
||||
}
|
||||
}
|
||||
|
||||
async fn launch_demo(app: FC<()>) {
|
||||
let mut dom = VirtualDom::new(app);
|
||||
let mut real_dom = DebugDom::new();
|
||||
let mut edits = Vec::new();
|
||||
dom.rebuild(&mut real_dom, &mut edits).unwrap();
|
||||
|
||||
while let Some(evt) = dom.tasks.next().await {
|
||||
//
|
||||
log::debug!("Event triggered! {:#?}", evt);
|
||||
}
|
||||
}
|
||||
|
||||
// #[cfg(test)]
|
||||
// mod tests {
|
||||
// use super::*;
|
||||
// use crate as dioxus;
|
||||
// use std::{pin::Pin, time::Duration};
|
||||
|
||||
// use crate::builder::DioxusElement;
|
||||
// use dioxus::prelude::*;
|
||||
// use futures::Future;
|
||||
|
||||
// #[async_std::test]
|
||||
// async fn async_tick() {
|
||||
// static App: FC<()> = |cx| {
|
||||
// // let mut count = use_state(cx, || 0);
|
||||
// let mut fut = cx.use_hook(
|
||||
// move || {
|
||||
// Box::pin(async {
|
||||
// //
|
||||
// let mut tick: i32 = 0;
|
||||
// loop {
|
||||
// async_std::task::sleep(Duration::from_millis(250)).await;
|
||||
// log::debug!("ticking forward... {}", tick);
|
||||
// tick += 1;
|
||||
// // match surf::get(ENDPOINT).recv_json::<DogApi>().await {
|
||||
// // Ok(_) => (),
|
||||
// // Err(_) => (),
|
||||
// // }
|
||||
// }
|
||||
// }) as Pin<Box<dyn Future<Output = ()> + 'static>>
|
||||
// },
|
||||
// |h| h,
|
||||
// |_| {},
|
||||
// );
|
||||
|
||||
// cx.submit_task(fut);
|
||||
|
||||
// cx.render(LazyNodes::new(move |f| {
|
||||
// f.text(format_args!("it's sorta working"))
|
||||
// }))
|
||||
// };
|
||||
|
||||
// std::env::set_var("RUST_LOG", "debug");
|
||||
// env_logger::init();
|
||||
// launch_demo(App).await;
|
||||
// }
|
||||
// }
|
||||
|
|
|
@ -120,13 +120,7 @@ impl<'a> TextRenderer<'a> {
|
|||
VNodeKind::Component(vcomp) => {
|
||||
let idx = vcomp.ass_scope.get().unwrap();
|
||||
|
||||
let new_node = self
|
||||
.vdom
|
||||
.components
|
||||
.try_get(idx)
|
||||
.unwrap()
|
||||
.frames
|
||||
.current_head_node();
|
||||
let new_node = self.vdom.components.try_get(idx).unwrap().root();
|
||||
|
||||
self.html_render(new_node, f, il + 1)?;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue