diff --git a/examples/eval.rs b/examples/eval.rs index 495288fcb..ad7a493f0 100644 --- a/examples/eval.rs +++ b/examples/eval.rs @@ -16,7 +16,7 @@ fn app(cx: Scope) -> Element { oninput: move |e| script.set(e.value.clone()), } button { - onclick: move |_| eval(script), + onclick: move |_| eval(script.to_string()), "Execute" } } diff --git a/examples/framework_benchmark.rs b/examples/framework_benchmark.rs index a88946af4..df5884b29 100644 --- a/examples/framework_benchmark.rs +++ b/examples/framework_benchmark.rs @@ -72,7 +72,7 @@ fn app(cx: Scope) -> Element { td { class:"col-md-1" } td { class:"col-md-1", "{item.key}" } td { class:"col-md-1", onclick: move |_| selected.set(Some(id)), - a { class: "lbl", item.labels } + a { class: "lbl", "{item.labels[0]}{item.labels[1]}{item.labels[2]}" } } td { class: "col-md-1", a { class: "remove", onclick: move |_| { items.write().remove(id); }, diff --git a/examples/generic_component.rs b/examples/generic_component.rs new file mode 100644 index 000000000..48751402f --- /dev/null +++ b/examples/generic_component.rs @@ -0,0 +1,19 @@ +use std::fmt::Display; + +use dioxus::prelude::*; + +fn main() { + dioxus_desktop::launch(app); +} + +fn app(cx: Scope) -> Element { + cx.render(rsx! { + generic_child::{} + }) +} + +fn generic_child(cx: Scope) -> Element { + cx.render(rsx! { + div {} + }) +} diff --git a/examples/rsx_usage.rs b/examples/rsx_usage.rs index a610fe334..597c6ab79 100644 --- a/examples/rsx_usage.rs +++ b/examples/rsx_usage.rs @@ -183,7 +183,7 @@ fn app(cx: Scope) -> Element { // Components can be generic too // This component takes i32 type to give you typed input - TypedInput::> {} + TypedInput:: {} // Type inference can be used too TypedInput { initial: 10.0 } diff --git a/examples/simple_list.rs b/examples/simple_list.rs index d615e2371..035244903 100644 --- a/examples/simple_list.rs +++ b/examples/simple_list.rs @@ -8,11 +8,12 @@ fn app(cx: Scope) -> Element { cx.render(rsx!( // Use Map directly to lazily pull elements (0..10).map(|f| rsx! { "{f}" }), - // Collect into an intermediate collection if necessary + // Collect into an intermediate collection if necessary, and call into_iter ["a", "b", "c"] .into_iter() .map(|f| rsx! { "{f}" }) - .collect::>(), + .collect::>() + .into_iter(), // Use optionals Some(rsx! { "Some" }), )) diff --git a/packages/core/src/factory.rs b/packages/core/src/factory.rs index b1d04db38..3344ac1f3 100644 --- a/packages/core/src/factory.rs +++ b/packages/core/src/factory.rs @@ -41,25 +41,11 @@ impl ScopeState { } } - pub fn fragment_from_iter<'a, 'c, I, J>( + pub fn fragment_from_iter<'a, 'c, I>( &'a self, - node_iter: impl IntoVNode<'a, I, J> + 'c, + node_iter: impl IntoDynNode<'a, I> + 'c, ) -> DynamicNode { node_iter.into_vnode(self) - - // let mut bump_vec = bumpalo::vec![in self.bump();]; - - // for item in it { - // bump_vec.push(item.into_vnode(self)); - // } - - // match bump_vec.len() { - // 0 => DynamicNode::Placeholder(Cell::new(ElementId(0))), - // _ => DynamicNode::Fragment { - // inner: false, - // nodes: bump_vec.into_bump_slice(), - // }, - // } } /// Create a new [`Attribute`] @@ -155,46 +141,65 @@ impl<'a> RenderReturn<'a> { } } -pub trait IntoVNode<'a, A = (), J = ()> { +pub trait IntoDynNode<'a, A = ()> { fn into_vnode(self, cx: &'a ScopeState) -> DynamicNode<'a>; } -impl<'a, 'b> IntoVNode<'a> for VNode<'a> { +impl<'a, 'b> IntoDynNode<'a> for () { fn into_vnode(self, _cx: &'a ScopeState) -> DynamicNode<'a> { todo!() // self } } +impl<'a, 'b> IntoDynNode<'a> for VNode<'a> { + fn into_vnode(self, _cx: &'a ScopeState) -> DynamicNode<'a> { + // DynamicNode::Fragment { nodes: cx., inner: () } + todo!() + } +} -impl<'a, 'b> IntoVNode<'a> for LazyNodes<'a, 'b> { +impl<'a, 'b, T: IntoDynNode<'a>> IntoDynNode<'a> for Option { + fn into_vnode(self, _cx: &'a ScopeState) -> DynamicNode<'a> { + // DynamicNode::Fragment { nodes: cx., inner: () } + todo!() + } +} + +impl<'a, 'b, T: IntoDynNode<'a>> IntoDynNode<'a> for &Option { + fn into_vnode(self, _cx: &'a ScopeState) -> DynamicNode<'a> { + // DynamicNode::Fragment { nodes: cx., inner: () } + todo!() + } +} + +impl<'a, 'b> IntoDynNode<'a> for LazyNodes<'a, 'b> { fn into_vnode(self, cx: &'a ScopeState) -> DynamicNode<'a> { - todo!() - // self.call(cx) + DynamicNode::Fragment { + nodes: cx.bump().alloc([self.call(cx)]), + inner: false, + } } } -impl<'b> IntoVNode<'_> for &'b str { +impl<'b> IntoDynNode<'_> for &'b str { fn into_vnode(self, cx: &ScopeState) -> DynamicNode { - // cx.text(format_args!("{}", self)) - todo!() + cx.text(format_args!("{}", self)) } } -impl IntoVNode<'_> for String { +impl IntoDynNode<'_> for String { fn into_vnode(self, cx: &ScopeState) -> DynamicNode { - // cx.text(format_args!("{}", self)) - todo!() + cx.text(format_args!("{}", self)) } } -impl<'b> IntoVNode<'b> for Arguments<'_> { +impl<'b> IntoDynNode<'b> for Arguments<'_> { fn into_vnode(self, cx: &'b ScopeState) -> DynamicNode<'b> { - // cx.text(self) - todo!() + cx.text(self) } } -impl<'a, 'b> IntoVNode<'a> for &VNode<'a> { +impl<'a, 'b> IntoDynNode<'a> for &VNode<'a> { fn into_vnode(self, _cx: &'a ScopeState) -> DynamicNode<'a> { todo!() // VNode { @@ -209,46 +214,43 @@ impl<'a, 'b> IntoVNode<'a> for &VNode<'a> { } } +pub trait IntoTemplate<'a> { + fn into_template(self, _cx: &'a ScopeState) -> VNode<'a>; +} +impl<'a, 'b> IntoTemplate<'a> for VNode<'a> { + fn into_template(self, _cx: &'a ScopeState) -> VNode<'a> { + self + } +} +impl<'a, 'b> IntoTemplate<'a> for LazyNodes<'a, 'b> { + fn into_template(self, cx: &'a ScopeState) -> VNode<'a> { + self.call(cx) + } +} + // Note that we're using the E as a generic but this is never crafted anyways. pub struct FromNodeIterator; -impl<'a, T, I, E> IntoVNode<'a, FromNodeIterator, E> for T +impl<'a, T, I> IntoDynNode<'a, FromNodeIterator> for T where - T: IntoIterator, - I: IntoVNode<'a, E>, + T: Iterator, + I: IntoTemplate<'a>, { fn into_vnode(self, cx: &'a ScopeState) -> DynamicNode<'a> { let mut nodes = bumpalo::collections::Vec::new_in(cx.bump()); for node in self { - nodes.push(node.into_vnode(cx)); + nodes.push(node.into_template(cx)); } let children = nodes.into_bump_slice(); - // if cfg!(debug_assertions) && children.len() > 1 && children.last().unwrap().key().is_none() - // { - // let bt = backtrace::Backtrace::new(); - // let bt = "no backtrace available"; - - // // todo: make the backtrace prettier or remove it altogether - // 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. - // ------------- - // {:?} - // "#, - // bt - // ); - // } - - todo!() - // VNode::Fragment(cx.bump.alloc(VFragment { - // children, - // placeholder: Default::default(), - // key: None, - // })) + match children.len() { + 0 => DynamicNode::Placeholder(Cell::new(ElementId(0))), + _ => DynamicNode::Fragment { + inner: false, + nodes: children, + }, + } } } diff --git a/packages/rsx/src/component.rs b/packages/rsx/src/component.rs index 00fdd2858..e00515e4f 100644 --- a/packages/rsx/src/component.rs +++ b/packages/rsx/src/component.rs @@ -154,7 +154,7 @@ impl ToTokens for Component { } None => { let mut toks = match prop_gen_args { - Some(gen_args) => quote! { fc_to_builder #gen_args(#name) }, + Some(gen_args) => quote! { fc_to_builder(#name #gen_args) }, None => quote! { fc_to_builder(#name) }, }; for field in &self.fields { @@ -187,9 +187,14 @@ impl ToTokens for Component { let fn_name = self.name.segments.last().unwrap().ident.to_string(); + let gen_name = match &self.prop_gen_args { + Some(gen) => quote! { #name #gen }, + None => quote! { #name }, + }; + tokens.append_all(quote! { __cx.component( - #name, + #gen_name, #builder, #fn_name )