Feat: building large apps, revamp macro

This commit is contained in:
Jonathan Kelley 2021-03-22 23:52:54 -04:00
parent 5791e49700
commit 9f7f43b661
20 changed files with 386 additions and 293 deletions

View file

@ -6,6 +6,7 @@ members = [
"packages/web", "packages/web",
"packages/liveview", "packages/liveview",
"packages/3d", "packages/3d",
"packages/docsite",
] ]
# Built-in # Built-in

View file

@ -58,11 +58,11 @@ impl ToTokens for HtmlRender {
// create a lazy tree that accepts a bump allocator // create a lazy tree that accepts a bump allocator
let final_tokens = quote! { let final_tokens = quote! {
move |ctx| { dioxus::prelude::LazyNodes::new(move |ctx| {
let bump = ctx.bump; let bump = ctx.bump;
#new_toks #new_toks
} })
}; };
final_tokens.to_tokens(out_tokens); final_tokens.to_tokens(out_tokens);

View file

@ -30,6 +30,10 @@ ctx.render(rsx!{
}) })
``` ```
optionally, include the allocator directly
rsx!(ctx, div { "hello"} )
each element is given by tag { [Attr] } each element is given by tag { [Attr] }
*/ */
@ -50,18 +54,31 @@ use {
// Parse any stream coming from the rsx! macro // Parse any stream coming from the rsx! macro
// ============================================== // ==============================================
pub struct RsxRender { pub struct RsxRender {
custom_context: Option<Ident>,
el: Element, el: Element,
} }
impl Parse for RsxRender { impl Parse for RsxRender {
fn parse(input: ParseStream) -> Result<Self> { fn parse(input: ParseStream) -> Result<Self> {
let fork = input.fork();
let custom_context = fork
.parse::<Ident>()
.and_then(|ident| {
fork.parse::<Token![,]>().map(|_| {
input.advance_to(&fork);
ident
})
})
.ok();
// cannot accept multiple elements // cannot accept multiple elements
// can only accept one root per component // can only accept one root per component
// fragments can be used as // fragments can be used as
// todo // todo
// enable fragements by autocoerrcing into list // enable fragements by autocoerrcing into list
let el: Element = input.parse()?; let el: Element = input.parse()?;
Ok(Self { el }) Ok(Self { el, custom_context })
} }
} }
@ -71,11 +88,19 @@ impl ToTokens for RsxRender {
// let new_toks = ToToksCtx::new(&self.kind).to_token_stream(); // let new_toks = ToToksCtx::new(&self.kind).to_token_stream();
// create a lazy tree that accepts a bump allocator // create a lazy tree that accepts a bump allocator
let final_tokens = quote! { let final_tokens = match &self.custom_context {
move |ctx: &dioxus::prelude::NodeCtx<'_>| -> dioxus::prelude::VNode<'_>{ Some(ident) => quote! {
#ident.render(dioxus::prelude::LazyNodes::new(move |ctx|{
let bump = ctx.bump; let bump = ctx.bump;
#new_toks #new_toks
} }))
},
None => quote! {
dioxus::prelude::LazyNodes::new(move |ctx|{
let bump = ctx.bump;
#new_toks
})
},
}; };
final_tokens.to_tokens(out_tokens); final_tokens.to_tokens(out_tokens);
@ -102,29 +127,32 @@ impl ToTokens for ToToksCtx<&Node> {
} }
impl Parse for Node { impl Parse for Node {
fn parse(s: ParseStream) -> Result<Self> { fn parse(stream: ParseStream) -> Result<Self> {
let fork1 = s.fork(); let fork1 = stream.fork();
let fork2 = s.fork(); let fork2 = stream.fork();
// todo: map issues onto the second fork if any arise // todo: map issues onto the second fork if any arise
// it's unlikely that textnodes or components would fail? // it's unlikely that textnodes or components would fail?
let ret = if let Ok(text) = fork1.parse::<TextNode>() { let ret = if let Ok(text) = fork1.parse::<TextNode>() {
s.advance_to(&fork1); stream.advance_to(&fork1);
Self::Text(text) Self::Text(text)
} else if let Ok(element) = fork2.parse::<Element>() { } else if let Ok(element) = fork2.parse::<Element>() {
s.advance_to(&fork2); stream.advance_to(&fork2);
Self::Element(element) Self::Element(element)
} else if let Ok(comp) = s.parse::<Component>() { } else if let Ok(comp) = stream.parse::<Component>() {
Self::Component(comp) Self::Component(comp)
} else { } else {
return Err(Error::new(s.span(), "Failed to parse as a valid child")); return Err(Error::new(
stream.span(),
"Failed to parse as a valid child",
));
}; };
// consume comma if it exists // consume comma if it exists
// we don't actually care if there *are* commas after elements/text // we don't actually care if there *are* commas after elements/text
if s.peek(Token![,]) { if stream.peek(Token![,]) {
let _ = s.parse::<Token![,]>(); let _ = stream.parse::<Token![,]>();
} }
Ok(ret) Ok(ret)
} }
@ -301,7 +329,7 @@ impl ToTokens for ToToksCtx<&Element> {
} }
match &self.inner.children { match &self.inner.children {
MaybeExpr::Expr(expr) => tokens.append_all(quote! { MaybeExpr::Expr(expr) => tokens.append_all(quote! {
.children(#expr) .iter_child(#expr)
}), }),
MaybeExpr::Literal(nodes) => { MaybeExpr::Literal(nodes) => {
let mut children = nodes.iter(); let mut children = nodes.iter();
@ -340,7 +368,7 @@ impl Parse for Element {
let mut attrs: Vec<Attr> = vec![]; let mut attrs: Vec<Attr> = vec![];
let mut children: Vec<Node> = vec![]; let mut children: Vec<Node> = vec![];
parse_element_content(content, &mut attrs, &mut children); parse_element_content(content, &mut attrs, &mut children)?;
let children = MaybeExpr::Literal(children); let children = MaybeExpr::Literal(children);
@ -353,7 +381,11 @@ impl Parse for Element {
} }
// used by both vcomponet and velement to parse the contents of the elements into attras and children // used by both vcomponet and velement to parse the contents of the elements into attras and children
fn parse_element_content(content: ParseBuffer, attrs: &mut Vec<Attr>, children: &mut Vec<Node>) { fn parse_element_content(
stream: ParseBuffer,
attrs: &mut Vec<Attr>,
children: &mut Vec<Node>,
) -> Result<()> {
'parsing: loop { 'parsing: loop {
// todo move this around into a more functional style // todo move this around into a more functional style
// [1] Break if empty // [1] Break if empty
@ -363,38 +395,47 @@ fn parse_element_content(content: ParseBuffer, attrs: &mut Vec<Attr>, children:
// [last] Fail if none worked // [last] Fail if none worked
// [1] Break if empty // [1] Break if empty
if content.is_empty() { if stream.is_empty() {
break 'parsing; break 'parsing;
} }
// [2] Try to consume an attr // [2] Try to consume an attr
let fork = content.fork(); let fork = stream.fork();
if let Ok(attr) = fork.parse::<Attr>() { if let Ok(attr) = fork.parse::<Attr>() {
// make sure to advance or your computer will become a spaceheater :) // make sure to advance or your computer will become a spaceheater :)
content.advance_to(&fork); stream.advance_to(&fork);
attrs.push(attr); attrs.push(attr);
continue 'parsing; continue 'parsing;
} }
// [3] Try to consume a child node // [3] Try to consume a child node
let fork = content.fork(); let fork = stream.fork();
if let Ok(node) = fork.parse::<Node>() { if let Ok(node) = fork.parse::<Node>() {
// make sure to advance or your computer will become a spaceheater :) // make sure to advance or your computer will become a spaceheater :)
content.advance_to(&fork); stream.advance_to(&fork);
children.push(node); children.push(node);
continue 'parsing; continue 'parsing;
} }
// [4] TODO: Parsing brackets // [4] TODO: Parsing brackets
if stream.peek(token::Brace) {
// this can fail (mismatched brackets)
let content;
syn::braced!(content in &stream);
continue 'parsing;
// let fork = content.fork(); // let fork = content.fork();
// stream.advance_to(fork)
}
// if let Ok(el) = fork.parse() { // if let Ok(el) = fork.parse() {
// children.push(el); // children.push(el);
// continue 'parsing; // continue 'parsing;
// } // }
// todo: pass a descriptive error onto the offending tokens // todo: pass a descriptive error onto the offending tokens
panic!("Entry is not an attr or element\n {}", content) panic!("Entry is not an attr or element\n {}", stream)
} }
Ok(())
} }
/// ======================================= /// =======================================

View file

@ -1,93 +1,17 @@
// //! An alternative function syntax
// //!
// use bumpalo::Bump;
// use dioxus_core::prelude::VNode;
fn main() {} fn main() {}
// struct Context2<'a, P> { use dioxus_core::prelude::*;
// _props: &'a P, // _p: PhantomData<&'a ()>,
// rops: &'a P, // _p: PhantomData<&'a ()>,
// }
// impl<'a, P> Context2<'a, P> {
// fn render(self, _f: impl FnOnce(&'a Bump) -> VNode<'a>) -> DTree {
// DTree {}
// }
// fn props(&self) -> &'a P { static Example: FC<()> = |ctx, props| {
// todo!() ctx.render(dioxus_core::prelude::LazyNodes::new(move |ctx| {
// } let bump = ctx.bump;
dioxus::builder::ElementBuilder::new(ctx, "h1")
// pub fn use_hook<'scope, InternalHookState: 'static, Output: 'a>( .children([{
// &'scope self, use bumpalo::core_alloc::fmt::Write;
// _initializer: impl FnOnce() -> InternalHookState, let mut s = bumpalo::collections::String::new_in(bump);
// _runner: impl FnOnce(&'a mut InternalHookState) -> Output, write!(s, "hello");
// _cleanup: impl FnOnce(InternalHookState), dioxus::builder::text2(s)
// ) -> Output { }])
// todo!() .finish()
// } }))
// } };
// trait Properties {}
// struct DTree;
// // type FC2<'a, T: 'a> = fn(Context2<T>) -> DTree;
// fn virtual_child<'a, T: 'a>(_bump: &'a Bump, _props: T, _f: FC2<T>) -> VNode<'a> {
// todo!()
// }
// struct Props {
// c: String,
// }
// fn Example(ctx: Context2<Props>) -> DTree {
// let val = use_state(&ctx, || String::from("asd"));
// let props = ctx.props();
// ctx.render(move |b| {
// dioxus_core::nodebuilder::div(b)
// .child(dioxus_core::nodebuilder::text(props.c.as_str()))
// .child(virtual_child(b, Props2 { a: val }, AltChild))
// .finish()
// })
// }
// // #[fc]
// fn Example2(ctx: Context2<()>, name: &str, _blah: &str) -> DTree {
// let val = use_state(&ctx, || String::from("asd"));
// ctx.render(move |ctx| {
// dioxus_core::builder::ElementBuilder::new(b, "div")
// .child(dioxus_core::nodebuilder::text(name))
// .child(virtual_child(b, Props2 { a: val }, AltChild))
// .finish()
// })
// }
// type FC2<'a, T> = fn(Context2<T>) -> DTree;
// // still works if you don't take any references in your props (ie, something copy or cloneable)
// static CHILD: FC2<Props2> = |_ctx: Context2<Props2>| {
// //
// todo!()
// };
// struct Props2<'a> {
// a: &'a String,
// }
// impl Properties for Props2<'_> {}
// fn AltChild(ctx: Context2<Props2>) -> DTree {
// ctx.render(|_b| {
// //
// todo!()
// })
// }
// fn use_state<'a, 'c, P, T: 'static, F: FnOnce() -> T>(
// _ctx: &'_ Context2<'a, P>,
// _initial_state_fn: F,
// ) -> &'a T {
// todo!()
// }

View file

@ -22,7 +22,7 @@ struct ListItem {
fn app(ctx: Context, props: &Props) -> DomTree { fn app(ctx: Context, props: &Props) -> DomTree {
let (_f, setter) = use_state(&ctx, || 0); let (_f, setter) = use_state(&ctx, || 0);
ctx.render(move |c| { ctx.render(dioxus::prelude::LazyNodes::new(move |c| {
let mut root = builder::ElementBuilder::new(c, "div"); let mut root = builder::ElementBuilder::new(c, "div");
for child in &props.items { for child in &props.items {
// notice that the child directly borrows from our vec // notice that the child directly borrows from our vec
@ -39,7 +39,7 @@ fn app(ctx: Context, props: &Props) -> DomTree {
)); ));
} }
root.finish() root.finish()
}) }))
} }
type StateSetter<T> = dyn Fn(T); type StateSetter<T> = dyn Fn(T);

View file

@ -13,7 +13,7 @@ struct Props {
static Example: FC<Props> = |ctx, props| { static Example: FC<Props> = |ctx, props| {
let value = ctx.use_context(|c: &SomeContext| c.items.last().unwrap()); let value = ctx.use_context(|c: &SomeContext| c.items.last().unwrap());
ctx.render(move |bump| { ctx.render(LazyNodes::new(move |bump| {
builder::ElementBuilder::new(bump, "button") builder::ElementBuilder::new(bump, "button")
.on("click", move |_| { .on("click", move |_| {
println!("Value is {}", props.name); println!("Value is {}", props.name);
@ -24,7 +24,7 @@ static Example: FC<Props> = |ctx, props| {
println!("Value is {}", props.name); println!("Value is {}", props.name);
}) })
.finish() .finish()
}) }))
// ctx.render(html! { // ctx.render(html! {
// <div> // <div>
// <button onclick={move |_| println!("Value is {}", value)} /> // <button onclick={move |_| println!("Value is {}", value)} />

View file

@ -18,8 +18,54 @@ pub struct ExampleProps {
} }
static SomeComponent: FC<ExampleProps> = |ctx, _props| { static SomeComponent: FC<ExampleProps> = |ctx, _props| {
let blah = rsx! {
div {}
};
let data = match 1 {
1 => ctx.render(rsx! (
div {
h1 {}
h3 {}
}
)),
1 => ctx.render(rsx!( div { "abc" } )),
2 => ctx.render(rsx!( div { "abc" } )),
3 => ctx.render(rsx!( div { "abc" } )),
_ => todo!(),
};
let data = match 1 {
1 => ctx.render(rsx! (
div {
h1 {}
h3 {}
}
)),
1 => ctx.render(rsx!(
div { "abc" }
)),
2 => ctx.render(rsx!(
div { "abc" }
)),
3 => ctx.render(rsx!(
div { "abc" }
)),
_ => todo!(),
};
let i = (0..10).map(|v| {
rsx! {
div {
"{v}"
}
}
});
ctx.render(rsx! { ctx.render(rsx! {
div { } div {
""
}
}) })
}; };

View file

@ -12,7 +12,7 @@ static Header: FC<()> = |ctx, props| {
let handler1 = move || println!("Value is {}", inner.current()); let handler1 = move || println!("Value is {}", inner.current());
ctx.render(|c| { ctx.render(dioxus::prelude::LazyNodes::new(|c| {
builder::ElementBuilder::new(c, "div") builder::ElementBuilder::new(c, "div")
.child(VNode::Component(VComponent::new( .child(VNode::Component(VComponent::new(
Bottom, Bottom,
@ -20,7 +20,7 @@ static Header: FC<()> = |ctx, props| {
c.bump.alloc(()), c.bump.alloc(()),
))) )))
.finish() .finish()
}) }))
}; };
static Bottom: FC<()> = |ctx, props| { static Bottom: FC<()> = |ctx, props| {

View file

@ -1,4 +1,3 @@
use dioxus_core::prelude::*; use dioxus_core::prelude::*;
fn main() {} fn main() {}

View file

@ -1,5 +1,5 @@
use crate::nodes::VNode; use crate::{nodebuilder::IntoDomTree, prelude::*};
use crate::prelude::*; use crate::{nodebuilder::LazyNodes, nodes::VNode};
use bumpalo::Bump; use bumpalo::Bump;
use hooks::Hook; use hooks::Hook;
use std::{cell::RefCell, future::Future, ops::Deref, rc::Rc, sync::atomic::AtomicUsize}; use std::{cell::RefCell, future::Future, ops::Deref, rc::Rc, sync::atomic::AtomicUsize};
@ -95,7 +95,10 @@ impl<'a> Context<'a> {
/// ctx.render(lazy_tree) /// ctx.render(lazy_tree)
/// } /// }
///``` ///```
pub fn render(&self, lazy_nodes: impl FnOnce(&'_ NodeCtx<'a>) -> VNode<'a> + 'a) -> DomTree { pub fn render<F: for<'b> FnOnce(&'b NodeCtx<'a>) -> VNode<'a> + 'a>(
&self,
lazy_nodes: LazyNodes<'a, F>,
) -> DomTree {
let ctx = NodeCtx { let ctx = NodeCtx {
bump: self.bump, bump: self.bump,
scope: self.scope, scope: self.scope,
@ -103,7 +106,7 @@ impl<'a> Context<'a> {
listeners: self.listeners, listeners: self.listeners,
}; };
let safe_nodes = lazy_nodes(&ctx); let safe_nodes = lazy_nodes.into_vnode(&ctx);
let root: VNode<'static> = unsafe { std::mem::transmute(safe_nodes) }; let root: VNode<'static> = unsafe { std::mem::transmute(safe_nodes) };
DomTree { root } DomTree { root }
} }

View file

@ -160,12 +160,7 @@ pub mod on {
#[derive(Debug)] #[derive(Debug)]
pub struct MouseEvent(pub Box<RawMouseEvent>); pub struct MouseEvent(pub Box<RawMouseEvent>);
impl Deref for MouseEvent {
type Target = RawMouseEvent;
fn deref(&self) -> &Self::Target {
self.0.as_ref()
}
}
#[derive(Debug)] #[derive(Debug)]
pub struct RawMouseEvent { pub struct RawMouseEvent {
pub alt_key: bool, pub alt_key: bool,
@ -183,6 +178,12 @@ pub mod on {
pub get_modifier_state: GetModifierKey, pub get_modifier_state: GetModifierKey,
// relatedTarget: DOMEventTarget, // relatedTarget: DOMEventTarget,
} }
impl Deref for MouseEvent {
type Target = RawMouseEvent;
fn deref(&self) -> &Self::Target {
self.0.as_ref()
}
}
event_builder! { event_builder! {
MouseEvent; MouseEvent;
click contextmenu doubleclick drag dragend dragenter dragexit click contextmenu doubleclick drag dragend dragenter dragexit

View file

@ -138,6 +138,8 @@ pub mod prelude {
use crate::nodes; use crate::nodes;
pub use nodes::*; pub use nodes::*;
pub use crate::nodebuilder::LazyNodes;
pub use crate::context::NodeCtx; pub use crate::context::NodeCtx;
// pub use nodes::iterables::IterableNodes; // pub use nodes::iterables::IterableNodes;
/// This type alias is an internal way of abstracting over the static functions that represent components. /// This type alias is an internal way of abstracting over the static functions that represent components.

View file

@ -16,14 +16,13 @@ use crate::{
/// function for building `<div>` elements or the `button` function for building /// function for building `<div>` elements or the `button` function for building
/// `<button>` elements. /// `<button>` elements.
#[derive(Debug)] #[derive(Debug)]
pub struct ElementBuilder<'a, Listeners, Attributes, Children> pub struct ElementBuilder<'a, 'b, Listeners, Attributes, Children>
where where
Listeners: 'a + AsRef<[Listener<'a>]>, Listeners: 'a + AsRef<[Listener<'a>]>,
Attributes: 'a + AsRef<[Attribute<'a>]>, Attributes: 'a + AsRef<[Attribute<'a>]>,
Children: 'a + AsRef<[VNode<'a>]>, Children: 'a + AsRef<[VNode<'a>]>,
{ {
ctx: NodeCtx<'a>, ctx: &'b NodeCtx<'a>,
// ctx: &'b NodeCtx<'a>,
key: NodeKey, key: NodeKey,
tag_name: &'a str, tag_name: &'a str,
listeners: Listeners, listeners: Listeners,
@ -32,9 +31,10 @@ where
namespace: Option<&'a str>, namespace: Option<&'a str>,
} }
impl<'a> impl<'a, 'b>
ElementBuilder< ElementBuilder<
'a, 'a,
'b,
bumpalo::collections::Vec<'a, Listener<'a>>, bumpalo::collections::Vec<'a, Listener<'a>>,
bumpalo::collections::Vec<'a, Attribute<'a>>, bumpalo::collections::Vec<'a, Attribute<'a>>,
bumpalo::collections::Vec<'a, VNode<'a>>, bumpalo::collections::Vec<'a, VNode<'a>>,
@ -63,24 +63,22 @@ impl<'a>
/// let my_element_builder = ElementBuilder::new(&b, tag_name); /// let my_element_builder = ElementBuilder::new(&b, tag_name);
/// # fn flip_coin() -> bool { true } /// # fn flip_coin() -> bool { true }
/// ``` /// ```
pub fn new(ctx: &NodeCtx<'a>, tag_name: &'static str) -> Self { pub fn new(ctx: &'b NodeCtx<'a>, tag_name: &'static str) -> Self {
// pub fn new(ctx: &'b NodeCtx<'a>, tag_name: &'static str) -> Self {
// pub fn new<B>(ctx: &'a mut NodeCtx<'a>, tag_name: &'a str) -> Self {
let bump = ctx.bump; let bump = ctx.bump;
todo!() ElementBuilder {
// ElementBuilder { ctx,
// ctx, key: NodeKey::NONE,
// key: NodeKey::NONE, tag_name,
// tag_name, listeners: bumpalo::collections::Vec::new_in(bump),
// listeners: bumpalo::collections::Vec::new_in(bump), attributes: bumpalo::collections::Vec::new_in(bump),
// attributes: bumpalo::collections::Vec::new_in(bump), children: bumpalo::collections::Vec::new_in(bump),
// children: bumpalo::collections::Vec::new_in(bump), namespace: None,
// namespace: None, }
// }
} }
} }
impl<'a, Listeners, Attributes, Children> ElementBuilder<'a, Listeners, Attributes, Children> impl<'a, 'b, Listeners, Attributes, Children>
ElementBuilder<'a, 'b, Listeners, Attributes, Children>
where where
Listeners: 'a + AsRef<[Listener<'a>]>, Listeners: 'a + AsRef<[Listener<'a>]>,
Attributes: 'a + AsRef<[Attribute<'a>]>, Attributes: 'a + AsRef<[Attribute<'a>]>,
@ -114,7 +112,7 @@ where
/// .finish(); /// .finish();
/// ``` /// ```
#[inline] #[inline]
pub fn listeners<L>(self, listeners: L) -> ElementBuilder<'a, L, Attributes, Children> pub fn listeners<L>(self, listeners: L) -> ElementBuilder<'a, 'b, L, Attributes, Children>
where where
L: 'a + AsRef<[Listener<'a>]>, L: 'a + AsRef<[Listener<'a>]>,
{ {
@ -153,7 +151,7 @@ where
/// .finish(); /// .finish();
/// ``` /// ```
#[inline] #[inline]
pub fn attributes<A>(self, attributes: A) -> ElementBuilder<'a, Listeners, A, Children> pub fn attributes<A>(self, attributes: A) -> ElementBuilder<'a, 'b, Listeners, A, Children>
where where
A: 'a + AsRef<[Attribute<'a>]>, A: 'a + AsRef<[Attribute<'a>]>,
{ {
@ -192,7 +190,7 @@ where
/// .finish(); /// .finish();
/// ``` /// ```
#[inline] #[inline]
pub fn children<C>(self, children: C) -> ElementBuilder<'a, Listeners, Attributes, C> pub fn children<C>(self, children: C) -> ElementBuilder<'a, 'b, Listeners, Attributes, C>
where where
C: 'a + AsRef<[VNode<'a>]>, C: 'a + AsRef<[VNode<'a>]>,
{ {
@ -311,8 +309,8 @@ where
} }
} }
impl<'a, Attributes, Children> impl<'a, 'b, Attributes, Children>
ElementBuilder<'a, bumpalo::collections::Vec<'a, Listener<'a>>, Attributes, Children> ElementBuilder<'a, 'b, bumpalo::collections::Vec<'a, Listener<'a>>, Attributes, Children>
where where
Attributes: 'a + AsRef<[Attribute<'a>]>, Attributes: 'a + AsRef<[Attribute<'a>]>,
Children: 'a + AsRef<[VNode<'a>]>, Children: 'a + AsRef<[VNode<'a>]>,
@ -365,8 +363,8 @@ where
} }
} }
impl<'a, Listeners, Children> impl<'a, 'b, Listeners, Children>
ElementBuilder<'a, Listeners, bumpalo::collections::Vec<'a, Attribute<'a>>, Children> ElementBuilder<'a, 'b, Listeners, bumpalo::collections::Vec<'a, Attribute<'a>>, Children>
where where
Listeners: 'a + AsRef<[Listener<'a>]>, Listeners: 'a + AsRef<[Listener<'a>]>,
Children: 'a + AsRef<[VNode<'a>]>, Children: 'a + AsRef<[VNode<'a>]>,
@ -424,8 +422,8 @@ where
} }
} }
impl<'a, Listeners, Attributes> impl<'a, 'b, Listeners, Attributes>
ElementBuilder<'a, Listeners, Attributes, bumpalo::collections::Vec<'a, VNode<'a>>> ElementBuilder<'a, 'b, Listeners, Attributes, bumpalo::collections::Vec<'a, VNode<'a>>>
where where
Listeners: 'a + AsRef<[Listener<'a>]>, Listeners: 'a + AsRef<[Listener<'a>]>,
Attributes: 'a + AsRef<[Attribute<'a>]>, Attributes: 'a + AsRef<[Attribute<'a>]>,
@ -451,16 +449,26 @@ where
self self
} }
pub fn iter_child(mut self, nodes: impl IntoIterator<Item = DomTree>) -> Self { /// Add multiple children to this element from an iterator.
for item in nodes.into_iter() { ///
self.children.push(item.root); /// # 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 IntoDomTree<'a>>) -> Self {
for item in nodes {
let child = item.into_vnode(&self.ctx);
self.children.push(child);
} }
self self
} }
pub fn into_tomdtree(mut self, nodes: impl IntoDomTree<'a>) -> Self {
self
}
} }
impl IntoIterator for DomTree { impl IntoIterator for DomTree {
@ -472,166 +480,124 @@ impl IntoIterator for DomTree {
} }
pub trait IntoDomTree<'a> { pub trait IntoDomTree<'a> {
type NodeIter: IntoIterator<Item = DomTree>; fn into_vnode(self, ctx: &NodeCtx<'a>) -> VNode<'a>;
fn into_domtree(self, ctx: &NodeCtx<'a>) -> Self::NodeIter;
} }
impl IntoDomTree<'_> for DomTree { // Cover the cases where nodes are pre-rendered.
type NodeIter = std::iter::Once<DomTree>; // Likely used by enums.
fn into_domtree(self, ctx: &NodeCtx) -> Self::NodeIter { // ----
std::iter::once(self) // let nodes = ctx.render(rsx!{ ... };
// rsx! { {nodes } }
impl<'a> IntoDomTree<'a> for DomTree {
fn into_vnode(self, _ctx: &NodeCtx<'a>) -> VNode<'a> {
self.root
} }
} }
impl<'a, G> IntoDomTree<'a> for NodeWrapper<'a, G> // Wrap the the node-builder closure in a concrete type.
// ---
// This is a bit of a hack to implement the IntoDomTree trait for closure types.
pub struct LazyNodes<'a, G>
where where
G: for<'b, 'c> FnOnce(&'b NodeCtx<'c>) -> VNode<'c> + 'a, G: for<'b> FnOnce(&'b NodeCtx<'a>) -> VNode<'a> + 'a,
{
type NodeIter = std::iter::Once<DomTree>;
fn into_domtree(self, ctx: &NodeCtx) -> Self::NodeIter {
let p: VNode<'_> = (self.inner)(ctx);
let root: VNode<'static> = unsafe { std::mem::transmute(p) };
std::iter::once(DomTree { root })
}
}
impl<'a, G, I> IntoDomTree<'a> for I
where
G: for<'b, 'c> FnOnce(&'b NodeCtx<'c>) -> VNode<'c> + 'a,
I: Iterator<Item = NodeWrapper<'a, G>>,
// O: Iterator<Item = DomTree>,
{
type NodeIter = std::iter::Map<I, O>;
fn into_domtree(self, ctx: &NodeCtx) -> Self::NodeIter {
self.map(|f| {
//
let caller = (f.inner);
let r = caller(ctx);
})
// todo!()
// let p: VNode<'_> = (self.inner)(ctx);
// let root: VNode<'static> = unsafe { std::mem::transmute(p) };
// std::iter::once(DomTree { root })
}
}
/*
all possible impls
rsx!{ }
ctx.render(rsx!)
map(rsx!)
map(ctx.render(rsx!))
*/
pub struct NodeWrapper<'a, G>
where
G: for<'b, 'c> FnOnce(&'b NodeCtx<'c>) -> VNode<'c> + 'a,
{ {
inner: G, inner: G,
_p: std::marker::PhantomData<&'a ()>, _p: std::marker::PhantomData<&'a ()>,
} }
fn new_wrapper<'a, G>(f: G) -> NodeWrapper<'a, G> impl<'a, G> LazyNodes<'a, G>
where
G: for<'b> FnOnce(&'b NodeCtx<'a>) -> VNode<'a> + 'a,
{
pub fn new(f: G) -> Self {
Self {
inner: f,
_p: std::default::Default::default(),
}
}
}
// Cover the cases where nodes are used by macro.
// Likely used directly.
// ---
// let nodes = rsx!{ ... };
// rsx! { {nodes } }
impl<'a, G> IntoDomTree<'a> for LazyNodes<'a, G>
where
G: for<'b> FnOnce(&'b NodeCtx<'a>) -> VNode<'a> + 'a,
{
fn into_vnode(self, ctx: &NodeCtx<'a>) -> VNode<'a> {
(self.inner)(ctx)
}
}
// Required because anything that enters brackets in the rsx! macro needs to implement IntoIterator
impl<'a, G> IntoIterator for LazyNodes<'a, G>
where where
G: for<'b, 'c> FnOnce(&'b NodeCtx<'c>) -> VNode<'c> + 'a, G: for<'b, 'c> FnOnce(&'b NodeCtx<'c>) -> VNode<'c> + 'a,
{ {
NodeWrapper { type Item = Self;
inner: f, type IntoIter = std::iter::Once<Self::Item>;
_p: std::default::Default::default(), fn into_iter(self) -> Self::IntoIter {
std::iter::once(self)
} }
} }
#[test] #[test]
fn test_iterator_of_nodes<'b>() { fn test_iterator_of_nodes<'b>() {
let p = (0..10).map(|f| { use crate::prelude::*;
//
new_wrapper(rsx! {
div {
"aaa {f}"
}
})
});
let g = p.into_iter();
for f in g {}
static Example: FC<()> = |ctx, props| { static Example: FC<()> = |ctx, props| {
ctx.render(|c| { let g: LazyNodes<_> = rsx! {
//
ElementBuilder::new(c, "div")
.into_tomdtree({
// rsx!
new_wrapper(move |n: &NodeCtx| -> VNode {
//
ElementBuilder::new(n, "div").finish()
})
// use the macro to be "blind" to the type
// everything gets interpreted as
/*
for node in {expr} {
ctx.push_alloc_node(node)
}
so.... just make sure whatever enters {} is iterable (domtree and nodewrapper need impls, )
*/
//
})
.into_tomdtree({
// render to wrapper -> tree
ctx.render(rsx! {
div {} div {}
}) };
})
.into_tomdtree({ ctx.render(rsx! {
// map rsx! div {
(0..10).map(|f| { h1 {}
new_wrapper(move |n: &NodeCtx| -> VNode { {}
// }
ElementBuilder::new(n, "div").finish()
})
})
})
.finish()
}) })
}; };
use crate::prelude::*; // let p = (0..10).map(|f| {
// static Example: FC<()> = |ctx, props| {
// // // //
// let list = (0..10).map(|f| { // LazyNodes::new(rsx! {
// ctx.render(rsx! { // div {
// div {} // "aaa {f}"
// }
// }) // })
// }); // });
// let g = p.into_iter();
// for f in g {}
// static Example: FC<()> = |ctx, props| {
// ctx.render(|c| { // ctx.render(|c| {
// //
// ElementBuilder::new(c, "div") // ElementBuilder::new(c, "div")
// .iter_child({ // .iter_child({
// // rsx!
// LazyNodes::new(move |n: &NodeCtx| -> VNode {
// // // //
// ElementBuilder::new(n, "div").finish()
// })
// })
// .iter_child({
// // render to wrapper -> tree
// ctx.render(rsx! { // ctx.render(rsx! {
// div {} // div {}
// }) // })
// }) // })
// .iter_child({ // .iter_child({
// // // // map rsx!
// (0..10).map(|f| { // (0..10).map(|f| {
// ctx.render(rsx! { // LazyNodes::new(move |n: &NodeCtx| -> VNode {
// div {} // //
// ElementBuilder::new(n, "div").finish()
// }) // })
// }) // })
// }) // })
// .iter_child(list)
// .finish() // .finish()
// }) // })
// }; // };

View file

@ -5,7 +5,7 @@
use crate::{ use crate::{
events::VirtualEvent, events::VirtualEvent,
innerlude::{Context, Properties, ScopeIdx, FC}, innerlude::{Context, NodeCtx, Properties, ScopeIdx, FC},
}; };
use bumpalo::Bump; use bumpalo::Bump;

View file

@ -0,0 +1,10 @@
[package]
name = "dioxus-docs-site"
version = "0.0.0"
authors = ["Jonathan Kelley <jkelleyrtp@gmail.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
dioxus-ssr = { path = "../ssr" }

View file

@ -0,0 +1,10 @@
# Dioxus docs site
Generate the liveview site powering diouxslabs.com.
We use the dioxus SSR crate and the use_router hook to generate a

View file

View file

@ -0,0 +1,82 @@
use dioxus_ssr::{prelude::*, TextRenderer};
fn main() {
TextRenderer::new(App);
}
#[derive(Debug, PartialEq)]
enum Routes {
Homepage,
ExampleList,
}
#[derive(Debug, PartialEq, Props)]
struct AppProps {
route: Routes,
}
trait Blah {}
impl<'a, G> Blah for LazyNodes<'a, G> where G: for<'b> FnOnce(&'b NodeCtx<'a>) -> VNode<'a> + 'a {}
static App: FC<AppProps> = |ctx, props| {
//
let body = match props.route {
Routes::Homepage => ctx.render(rsx!(
div {
Homepage {}
}
)),
Routes::ExampleList => ctx.render(rsx!(
div {
ExampleList {}
}
)),
};
ctx.render(rsx!(
div {
Header {}
{body}
{
}
Footer {}
}
))
};
#[derive(Debug, PartialEq, Props)]
struct HeaderProp {
selected: Routes,
}
static Header: FC<()> = |ctx, _| {
ctx.render(rsx! {
div {
}
})
};
static Footer: FC<()> = |ctx, _| {
ctx.render(rsx! {
div {
}
})
};
static Homepage: FC<()> = |ctx, _| {
ctx.render(rsx! {
div {
}
})
};
static ExampleList: FC<()> = |ctx, _| {
ctx.render(rsx! {
div {
}
})
};

View file

@ -21,6 +21,10 @@
use dioxus_core::prelude::{VNode, FC}; use dioxus_core::prelude::{VNode, FC};
pub mod prelude {
pub use dioxus_core::prelude::*;
}
/// The `TextRenderer` provides a way of rendering a Dioxus Virtual DOM to a String. /// The `TextRenderer` provides a way of rendering a Dioxus Virtual DOM to a String.
/// ///
/// ///

View file

@ -1,6 +1,6 @@
#![allow(non_snake_case)] #![allow(non_snake_case)]
use dioxus_core as dioxus; use dioxus_core as dioxus;
use dioxus::prelude::*; use dioxus::{events::on::MouseEvent, prelude::*};
use dioxus_web::WebsysRenderer; use dioxus_web::WebsysRenderer;
fn main() { fn main() {
@ -16,6 +16,9 @@ fn main() {
}); });
} }
#[derive(PartialEq, Props)] #[derive(PartialEq, Props)]
struct ExampleProps { struct ExampleProps {
initial_name: &'static str, initial_name: &'static str,
@ -43,13 +46,14 @@ static Example: FC<ExampleProps> = |ctx, props| {
}) })
}; };
#[derive(Props)] #[derive(Props)]
struct ButtonProps<'src> { struct ButtonProps<'src> {
name: &'src str, name: &'src str,
set_name: &'src dyn Fn(String) set_name: &'src dyn Fn(String)
} }
/// this is an awesome component
fn CustomButton<'a>(ctx: Context<'a>, props: &'a ButtonProps<'a>) -> DomTree { fn CustomButton<'a>(ctx: Context<'a>, props: &'a ButtonProps<'a>) -> DomTree {
ctx.render(rsx!{ ctx.render(rsx!{
button { button {