mirror of
https://github.com/DioxusLabs/dioxus
synced 2024-11-23 12:43:08 +00:00
feat: keys
This commit is contained in:
parent
dddbcfd5e2
commit
584504feb7
10 changed files with 113 additions and 94 deletions
|
@ -1,7 +1,7 @@
|
|||
use crate::mutations::Mutation;
|
||||
use crate::mutations::Mutation::*;
|
||||
use crate::nodes::VNode;
|
||||
use crate::nodes::{DynamicNode, DynamicNodeKind, TemplateNode};
|
||||
use crate::nodes::{DynamicNode, TemplateNode};
|
||||
use crate::virtualdom::VirtualDom;
|
||||
use crate::{AttributeValue, TemplateAttribute};
|
||||
|
||||
|
@ -30,8 +30,8 @@ impl VirtualDom {
|
|||
|
||||
// Walk the roots backwards, creating nodes and assigning IDs
|
||||
// todo: adjust dynamic nodes to be in the order of roots and then leaves (ie BFS)
|
||||
let mut dynamic_attrs = template.dynamic_attrs.iter().peekable();
|
||||
let mut dynamic_nodes = template.dynamic_nodes.iter().peekable();
|
||||
let mut dynamic_attrs = template.template.attr_paths.iter().enumerate().peekable();
|
||||
let mut dynamic_nodes = template.template.node_paths.iter().enumerate().peekable();
|
||||
|
||||
let mut on_stack = 0;
|
||||
for (root_idx, root) in template.template.roots.iter().enumerate() {
|
||||
|
@ -46,7 +46,7 @@ impl VirtualDom {
|
|||
}
|
||||
|
||||
TemplateNode::Dynamic(id) => {
|
||||
self.create_dynamic_node(mutations, template, &template.dynamic_nodes[*id])
|
||||
self.create_dynamic_node(mutations, template, &template.dynamic_nodes[*id], *id)
|
||||
}
|
||||
|
||||
TemplateNode::DynamicText { .. } => 1,
|
||||
|
@ -56,9 +56,11 @@ impl VirtualDom {
|
|||
|
||||
// we're on top of a node that has a dynamic attribute for a descendant
|
||||
// Set that attribute now before the stack gets in a weird state
|
||||
while let Some(attr) = dynamic_attrs.next_if(|a| a.path[0] == root_idx as u8) {
|
||||
while let Some((idx, path)) = dynamic_attrs.next_if(|(_, p)| p[0] == root_idx as u8) {
|
||||
let attr = &template.dynamic_attrs[idx];
|
||||
|
||||
if cur_route.is_none() {
|
||||
cur_route = Some((self.next_element(template), &attr.path[1..]));
|
||||
cur_route = Some((self.next_element(template), &path[1..]));
|
||||
}
|
||||
|
||||
// Attach all the elementIDs to the nodes with dynamic content
|
||||
|
@ -91,11 +93,12 @@ impl VirtualDom {
|
|||
}
|
||||
|
||||
// We're on top of a node that has a dynamic child for a descendant
|
||||
while let Some(node) = dynamic_nodes.next_if(|f| f.path[0] == root_idx as u8) {
|
||||
let m = self.create_dynamic_node(mutations, template, node);
|
||||
while let Some((idx, path)) = dynamic_nodes.next_if(|(_, p)| p[0] == root_idx as u8) {
|
||||
let node = &template.dynamic_nodes[idx];
|
||||
let m = self.create_dynamic_node(mutations, template, node, idx);
|
||||
mutations.push(ReplacePlaceholder {
|
||||
m,
|
||||
path: &node.path[1..],
|
||||
path: &path[1..],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -151,21 +154,22 @@ impl VirtualDom {
|
|||
mutations: &mut Vec<Mutation<'a>>,
|
||||
template: &'a VNode<'a>,
|
||||
node: &'a DynamicNode<'a>,
|
||||
idx: usize,
|
||||
) -> usize {
|
||||
match &node.kind {
|
||||
DynamicNodeKind::Text { id, value } => {
|
||||
match &node {
|
||||
DynamicNode::Text { id, value } => {
|
||||
let new_id = self.next_element(template);
|
||||
id.set(new_id);
|
||||
mutations.push(HydrateText {
|
||||
id: new_id,
|
||||
path: &node.path[1..],
|
||||
path: &template.template.node_paths[idx][1..],
|
||||
value,
|
||||
});
|
||||
|
||||
1
|
||||
}
|
||||
|
||||
DynamicNodeKind::Component { props, .. } => {
|
||||
DynamicNode::Component { props, .. } => {
|
||||
let id = self.new_scope(*props);
|
||||
|
||||
let template = self.run_scope(id);
|
||||
|
@ -180,7 +184,7 @@ impl VirtualDom {
|
|||
created
|
||||
}
|
||||
|
||||
DynamicNodeKind::Fragment { children } => {
|
||||
DynamicNode::Fragment { children } => {
|
||||
//
|
||||
children
|
||||
.iter()
|
||||
|
|
|
@ -115,14 +115,16 @@ impl VirtualDom {
|
|||
|
||||
while let Some((raw_parent, dyn_index)) = index {
|
||||
let parent = unsafe { &mut *raw_parent };
|
||||
let path = parent.dynamic_nodes[dyn_index].path;
|
||||
let path = parent.template.node_paths[dyn_index];
|
||||
|
||||
listeners.extend(
|
||||
parent
|
||||
.dynamic_attrs
|
||||
.iter()
|
||||
.filter(|attr| is_path_ascendant(attr.path, path))
|
||||
.filter(|attr| attr.name == event.name),
|
||||
.enumerate()
|
||||
.filter(|(idx, attr)| is_path_ascendant(parent.template.node_paths[*idx], path))
|
||||
.filter(|(idx, attr)| attr.name == event.name)
|
||||
.map(|(_, attr)| attr),
|
||||
);
|
||||
|
||||
index = parent.parent;
|
||||
|
|
|
@ -5,25 +5,24 @@ use bumpalo::Bump;
|
|||
use crate::{
|
||||
any_props::{AnyProps, VComponentProps},
|
||||
arena::ElementId,
|
||||
innerlude::{DynamicNode, DynamicNodeKind},
|
||||
innerlude::DynamicNode,
|
||||
Attribute, AttributeValue, Element, LazyNodes, Properties, Scope, ScopeState, VNode,
|
||||
};
|
||||
|
||||
impl ScopeState {
|
||||
/// Create some text that's allocated along with the other vnodes
|
||||
///
|
||||
pub fn text<'a>(&'a self, args: Arguments) -> DynamicNode<'a> {
|
||||
let (text, _) = self.raw_text(args);
|
||||
|
||||
DynamicNode {
|
||||
kind: DynamicNodeKind::Text {
|
||||
id: Cell::new(ElementId(0)),
|
||||
value: text,
|
||||
},
|
||||
path: &[0],
|
||||
DynamicNode::Text {
|
||||
id: Cell::new(ElementId(0)),
|
||||
value: text,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn raw_text_inline<'a>(&'a self, args: Arguments) -> &'a str {
|
||||
self.raw_text(args).0
|
||||
}
|
||||
|
||||
pub fn raw_text<'a>(&'a self, args: Arguments) -> (&'a str, bool) {
|
||||
match args.as_str() {
|
||||
Some(static_str) => (static_str, true),
|
||||
|
@ -46,11 +45,8 @@ impl ScopeState {
|
|||
bump_vec.push(item.into_dynamic_node(self));
|
||||
}
|
||||
|
||||
DynamicNode {
|
||||
path: &[0, 0],
|
||||
kind: crate::innerlude::DynamicNodeKind::Fragment {
|
||||
children: bump_vec.into_bump_slice(),
|
||||
},
|
||||
DynamicNode::Fragment {
|
||||
children: bump_vec.into_bump_slice(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -60,14 +56,14 @@ impl ScopeState {
|
|||
name: &'static str,
|
||||
val: impl IntoAttributeValue<'a>,
|
||||
namespace: Option<&'static str>,
|
||||
is_volatile: bool,
|
||||
volatile: bool,
|
||||
) -> Attribute<'a> {
|
||||
Attribute {
|
||||
name,
|
||||
namespace,
|
||||
mounted_element: Cell::new(ElementId(0)),
|
||||
path: &[0],
|
||||
volatile,
|
||||
value: val.into_value(self.bump()),
|
||||
mounted_element: Cell::new(ElementId(0)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -94,13 +90,10 @@ impl ScopeState {
|
|||
// self.scope.items.borrow_mut().borrowed_props.push(vcomp);
|
||||
// }
|
||||
|
||||
DynamicNode {
|
||||
path: &[0],
|
||||
kind: DynamicNodeKind::Component {
|
||||
name: fn_name,
|
||||
can_memoize: P::IS_STATIC,
|
||||
props: detached_dyn,
|
||||
},
|
||||
DynamicNode::Component {
|
||||
name: fn_name,
|
||||
can_memoize: P::IS_STATIC,
|
||||
props: detached_dyn,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -116,17 +109,19 @@ impl<'a, 'b> IntoVnode<'a> for LazyNodes<'a, 'b> {
|
|||
}
|
||||
|
||||
impl<'a, 'b> IntoVnode<'a> for VNode<'a> {
|
||||
fn into_dynamic_node(self, cx: &'a ScopeState) -> VNode<'a> {
|
||||
fn into_dynamic_node(self, _cx: &'a ScopeState) -> VNode<'a> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> IntoVnode<'a> for &'a VNode<'a> {
|
||||
fn into_dynamic_node(self, cx: &'a ScopeState) -> VNode<'a> {
|
||||
fn into_dynamic_node(self, _cx: &'a ScopeState) -> VNode<'a> {
|
||||
VNode {
|
||||
node_id: self.node_id.clone(),
|
||||
parent: self.parent,
|
||||
template: self.template,
|
||||
root_ids: self.root_ids,
|
||||
key: self.key,
|
||||
dynamic_nodes: self.dynamic_nodes,
|
||||
dynamic_attrs: self.dynamic_attrs,
|
||||
}
|
||||
|
|
|
@ -1,8 +1,4 @@
|
|||
use crate::{
|
||||
nodes::{DynamicNodeKind, VNode},
|
||||
scopes::ScopeId,
|
||||
virtualdom::VirtualDom,
|
||||
};
|
||||
use crate::{nodes::VNode, scopes::ScopeId, virtualdom::VirtualDom, DynamicNode};
|
||||
|
||||
impl VirtualDom {
|
||||
pub fn drop_scope(&mut self, id: ScopeId) {
|
||||
|
@ -16,14 +12,14 @@ impl VirtualDom {
|
|||
|
||||
pub fn drop_template<'a>(&'a mut self, template: &'a VNode<'a>) {
|
||||
for node in template.dynamic_nodes.iter() {
|
||||
match &node.kind {
|
||||
DynamicNodeKind::Text { id, .. } => {}
|
||||
match node {
|
||||
DynamicNode::Text { id, .. } => {}
|
||||
|
||||
DynamicNodeKind::Component { .. } => {
|
||||
DynamicNode::Component { .. } => {
|
||||
todo!()
|
||||
}
|
||||
|
||||
DynamicNodeKind::Fragment { children } => {}
|
||||
DynamicNode::Fragment { children } => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -72,7 +72,6 @@ pub use crate::innerlude::{
|
|||
Attribute,
|
||||
AttributeValue,
|
||||
DynamicNode,
|
||||
DynamicNodeKind,
|
||||
Element,
|
||||
EventPriority,
|
||||
LazyNodes,
|
||||
|
@ -98,9 +97,9 @@ pub use crate::innerlude::{
|
|||
/// This includes types like [`Scope`], [`Element`], and [`Component`].
|
||||
pub mod prelude {
|
||||
pub use crate::innerlude::{
|
||||
fc_to_builder, Attribute, DynamicNode, DynamicNodeKind, Element, EventPriority, LazyNodes,
|
||||
NodeFactory, Properties, Scope, ScopeId, ScopeState, TaskId, Template, TemplateAttribute,
|
||||
TemplateNode, UiEvent, VNode, VirtualDom,
|
||||
fc_to_builder, Attribute, DynamicNode, Element, EventPriority, LazyNodes, NodeFactory,
|
||||
Properties, Scope, ScopeId, ScopeState, TaskId, Template, TemplateAttribute, TemplateNode,
|
||||
UiEvent, VNode, VirtualDom,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,8 @@ pub struct VNode<'a> {
|
|||
// The ID assigned for the root of this template
|
||||
pub node_id: Cell<ElementId>,
|
||||
|
||||
pub key: Option<&'a str>,
|
||||
|
||||
// When rendered, this template will be linked to its parent manually
|
||||
pub parent: Option<(*mut VNode<'static>, usize)>,
|
||||
|
||||
|
@ -28,6 +30,8 @@ pub struct VNode<'a> {
|
|||
pub struct Template<'a> {
|
||||
pub id: &'a str,
|
||||
pub roots: &'a [TemplateNode<'a>],
|
||||
pub node_paths: &'a [&'a [u8]],
|
||||
pub attr_paths: &'a [&'a [u8]],
|
||||
}
|
||||
|
||||
impl<'a> std::hash::Hash for Template<'a> {
|
||||
|
@ -68,12 +72,7 @@ pub enum TemplateNode<'a> {
|
|||
DynamicText(usize),
|
||||
}
|
||||
|
||||
pub struct DynamicNode<'a> {
|
||||
pub path: &'static [u8],
|
||||
pub kind: DynamicNodeKind<'a>,
|
||||
}
|
||||
|
||||
pub enum DynamicNodeKind<'a> {
|
||||
pub enum DynamicNode<'a> {
|
||||
// Anything declared in component form
|
||||
// IE in caps or with underscores
|
||||
Component {
|
||||
|
@ -102,10 +101,7 @@ pub enum TemplateAttribute<'a> {
|
|||
namespace: Option<&'static str>,
|
||||
volatile: bool,
|
||||
},
|
||||
Dynamic {
|
||||
name: &'static str,
|
||||
index: usize,
|
||||
},
|
||||
Dynamic(usize),
|
||||
}
|
||||
|
||||
pub struct Attribute<'a> {
|
||||
|
@ -113,7 +109,7 @@ pub struct Attribute<'a> {
|
|||
pub value: AttributeValue<'a>,
|
||||
pub namespace: Option<&'static str>,
|
||||
pub mounted_element: Cell<ElementId>,
|
||||
pub path: &'static [u8],
|
||||
pub volatile: bool,
|
||||
}
|
||||
|
||||
pub enum AttributeValue<'a> {
|
||||
|
|
|
@ -5,11 +5,10 @@ fn basic_syntax_is_a_template(cx: Scope) -> Element {
|
|||
let var = 123;
|
||||
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
div { key: "12345",
|
||||
class: "asd",
|
||||
class: "{asd}",
|
||||
onclick: move |_| {},
|
||||
|
||||
div { "{var}" }
|
||||
div {
|
||||
h1 { "var" }
|
||||
|
@ -25,8 +24,9 @@ fn basic_syntax_is_a_template(cx: Scope) -> Element {
|
|||
}
|
||||
|
||||
fn basic_template(cx: Scope) -> Element {
|
||||
let val = 123;
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
div { class: "{val}", class: "{val}", class: "{val}", class: "{val}",
|
||||
(0..2).map(|i| rsx! { div { "asd {i}" } })
|
||||
basic_child { }
|
||||
}
|
||||
|
|
|
@ -55,6 +55,18 @@ impl Component {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn key(&self) -> Option<&IfmtInput> {
|
||||
match self
|
||||
.fields
|
||||
.iter()
|
||||
.find(|f| f.name.to_string() == "key")
|
||||
.map(|f| &f.content)
|
||||
{
|
||||
Some(ContentField::Formatted(fmt)) => Some(fmt),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for Component {
|
||||
|
|
|
@ -99,6 +99,18 @@ impl<'a> ToTokens for TemplateRenderer<'a> {
|
|||
dynamic_nodes: vec![],
|
||||
dynamic_attributes: vec![],
|
||||
current_path: vec![],
|
||||
attr_paths: vec![],
|
||||
node_paths: vec![],
|
||||
};
|
||||
|
||||
let key = match self.roots.get(0) {
|
||||
Some(BodyNode::Element(el)) if self.roots.len() == 1 => el.key.clone(),
|
||||
Some(BodyNode::Component(comp)) if self.roots.len() == 1 => comp.key().cloned(),
|
||||
_ => None,
|
||||
};
|
||||
let key_tokens = match key {
|
||||
Some(tok) => quote! { Some( __cx.raw_text_inline(#tok) ) },
|
||||
None => quote! { None },
|
||||
};
|
||||
|
||||
let root_printer = self.roots.iter().enumerate().map(|(idx, root)| {
|
||||
|
@ -112,15 +124,27 @@ impl<'a> ToTokens for TemplateRenderer<'a> {
|
|||
let roots = quote! { #( #root_printer ),* };
|
||||
let node_printer = &context.dynamic_nodes;
|
||||
let dyn_attr_printer = &context.dynamic_attributes;
|
||||
let node_paths = context
|
||||
.node_paths
|
||||
.iter()
|
||||
.map(|items| quote!(&[#(#items),*]));
|
||||
|
||||
let attr_paths = context
|
||||
.attr_paths
|
||||
.iter()
|
||||
.map(|items| quote!(&[#(#items),*]));
|
||||
|
||||
out_tokens.append_all(quote! {
|
||||
static TEMPLATE: ::dioxus::core::Template = ::dioxus::core::Template {
|
||||
id: ::dioxus::core::get_line_num!(),
|
||||
roots: &[ #roots ]
|
||||
roots: &[ #roots ],
|
||||
node_paths: &[ #(#node_paths),* ],
|
||||
attr_paths: &[ #(#attr_paths),* ],
|
||||
};
|
||||
::dioxus::core::VNode {
|
||||
node_id: Default::default(),
|
||||
parent: None,
|
||||
key: #key_tokens,
|
||||
template: TEMPLATE,
|
||||
root_ids: __cx.bump().alloc([]),
|
||||
dynamic_nodes: __cx.bump().alloc([ #( #node_printer ),* ]),
|
||||
|
@ -135,6 +159,9 @@ pub struct DynamicContext<'a> {
|
|||
dynamic_nodes: Vec<&'a BodyNode>,
|
||||
dynamic_attributes: Vec<&'a ElementAttrNamed>,
|
||||
current_path: Vec<u8>,
|
||||
|
||||
node_paths: Vec<Vec<u8>>,
|
||||
attr_paths: Vec<Vec<u8>>,
|
||||
}
|
||||
|
||||
impl<'a> DynamicContext<'a> {
|
||||
|
@ -180,22 +207,12 @@ impl<'a> DynamicContext<'a> {
|
|||
ElementAttr::AttrExpression { .. }
|
||||
| ElementAttr::AttrText { .. }
|
||||
| ElementAttr::CustomAttrText { .. }
|
||||
| ElementAttr::CustomAttrExpression { .. } => {
|
||||
| ElementAttr::CustomAttrExpression { .. }
|
||||
| ElementAttr::EventTokens { .. } => {
|
||||
let ct = self.dynamic_attributes.len();
|
||||
self.dynamic_attributes.push(attr);
|
||||
Some(quote! { ::dioxus::core::TemplateAttribute::Dynamic {
|
||||
name: "asd",
|
||||
index: #ct
|
||||
} })
|
||||
}
|
||||
|
||||
ElementAttr::EventTokens { .. } => {
|
||||
let ct = self.dynamic_attributes.len();
|
||||
self.dynamic_attributes.push(attr);
|
||||
Some(quote! { ::dioxus::core::TemplateAttribute::Dynamic {
|
||||
name: "asd",
|
||||
index: #ct
|
||||
} })
|
||||
self.attr_paths.push(self.current_path.clone());
|
||||
Some(quote! { ::dioxus::core::TemplateAttribute::Dynamic(#ct) })
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -228,6 +245,7 @@ impl<'a> DynamicContext<'a> {
|
|||
BodyNode::RawExpr(_) | BodyNode::Text(_) | BodyNode::Component(_) => {
|
||||
let ct = self.dynamic_nodes.len();
|
||||
self.dynamic_nodes.push(root);
|
||||
self.node_paths.push(self.current_path.clone());
|
||||
quote! { ::dioxus::core::TemplateNode::Dynamic(#ct) }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -72,7 +72,7 @@ impl StringCache {
|
|||
TemplateAttribute::Static { name, value, .. } => {
|
||||
write!(chain, " {}=\"{}\"", name, value)?;
|
||||
}
|
||||
TemplateAttribute::Dynamic { index, .. } => {
|
||||
TemplateAttribute::Dynamic(index) => {
|
||||
chain.segments.push(Segment::Attr(*index))
|
||||
}
|
||||
}
|
||||
|
@ -126,18 +126,18 @@ impl SsrRender {
|
|||
_ => {}
|
||||
};
|
||||
}
|
||||
Segment::Node(idx) => match &template.dynamic_nodes[*idx].kind {
|
||||
DynamicNodeKind::Text { value, .. } => {
|
||||
Segment::Node(idx) => match &template.dynamic_nodes[*idx] {
|
||||
DynamicNode::Text { value, .. } => {
|
||||
// todo: escape the text
|
||||
write!(buf, "{}", value)?
|
||||
}
|
||||
DynamicNodeKind::Fragment { children } => {
|
||||
DynamicNode::Fragment { children } => {
|
||||
for child in *children {
|
||||
self.render_template(buf, child)?;
|
||||
}
|
||||
//
|
||||
}
|
||||
DynamicNodeKind::Component { .. } => {
|
||||
DynamicNode::Component { .. } => {
|
||||
//
|
||||
}
|
||||
},
|
||||
|
@ -194,10 +194,7 @@ fn children_processes_properly() {
|
|||
render! {
|
||||
div {
|
||||
ChildWithChildren {
|
||||
p {
|
||||
"{d}"
|
||||
"hii"
|
||||
}
|
||||
p { "{d}" "hii" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue