Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add html_nested! macro to support nested iterable children access #843

Merged
merged 11 commits into from
Jan 6, 2020
8 changes: 5 additions & 3 deletions crates/macro/src/html_tree/html_component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,9 +127,11 @@ impl ToTokens for HtmlComponent {

let set_children = if !children.is_empty() {
quote! {
.children(::yew::html::ChildrenRenderer::new(
vec![#(#children.into(),)*]
))
.children(::yew::html::ChildrenRenderer::new({
let mut v = ::std::vec::Vec::new();
#(v.extend(::yew::utils::NodeSeq::from(#children));)*
v
}))
}
} else {
quote! {}
Expand Down
8 changes: 6 additions & 2 deletions crates/macro/src/html_tree/html_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,14 @@ impl Parse for HtmlList {

impl ToTokens for HtmlList {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
let html_trees = &self.0;
let children = &self.0;
tokens.extend(quote! {
::yew::virtual_dom::VNode::VList(
::yew::virtual_dom::vlist::VList::new_with_children(vec![#(#html_trees,)*])
::yew::virtual_dom::vlist::VList::new_with_children({
let mut v = ::std::vec::Vec::new();
#(v.extend(::yew::utils::NodeSeq::from(#children));)*
v
})
)
});
}
Expand Down
13 changes: 5 additions & 8 deletions crates/macro/src/html_tree/html_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use quote::{quote, quote_spanned, ToTokens};
use syn::buffer::Cursor;
use syn::parse::{Parse, ParseStream, Result};
use syn::spanned::Spanned;
use syn::Expr;
use syn::Lit;

pub struct HtmlNode(Node);
Expand All @@ -18,7 +19,7 @@ impl Parse for HtmlNode {
}
Node::Literal(lit)
} else {
Node::Raw(input.parse()?)
Node::Expression(input.parse()?)
};

Ok(HtmlNode(node))
Expand Down Expand Up @@ -46,12 +47,8 @@ impl ToTokens for HtmlNode {
impl ToTokens for Node {
fn to_tokens(&self, tokens: &mut TokenStream) {
let node_token = match &self {
Node::Literal(lit) => quote! {
::yew::virtual_dom::VNode::from(#lit)
},
Node::Raw(stream) => quote_spanned! {stream.span()=>
::yew::virtual_dom::VNode::from({#stream})
},
Node::Literal(lit) => quote! {#lit},
Node::Expression(expr) => quote_spanned! {expr.span()=> {#expr} },
};

tokens.extend(node_token);
Expand All @@ -60,5 +57,5 @@ impl ToTokens for Node {

enum Node {
Literal(Lit),
Raw(TokenStream),
Expression(Expr),
}
13 changes: 13 additions & 0 deletions crates/macro/src/html_tree/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,19 @@ impl HtmlTree {
}
}

pub struct HtmlRootNested(HtmlTreeNested);
impl Parse for HtmlRootNested {
fn parse(input: ParseStream) -> Result<Self> {
Ok(HtmlRootNested(HtmlTreeNested::parse(input)?))
}
}

impl ToTokens for HtmlRootNested {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
self.0.to_tokens(tokens);
}
}

pub struct HtmlTreeNested(HtmlTree);
impl Parse for HtmlTreeNested {
fn parse(input: ParseStream) -> Result<Self> {
Expand Down
8 changes: 7 additions & 1 deletion crates/macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ mod derive_props;
mod html_tree;

use derive_props::DerivePropsInput;
use html_tree::HtmlRoot;
use html_tree::{HtmlRoot, HtmlRootNested};
use proc_macro::TokenStream;
use proc_macro_hack::proc_macro_hack;
use quote::{quote, ToTokens};
Expand Down Expand Up @@ -94,6 +94,12 @@ pub fn derive_props(input: TokenStream) -> TokenStream {
TokenStream::from(input.into_token_stream())
}

#[proc_macro_hack]
pub fn html_nested(input: TokenStream) -> TokenStream {
let root = parse_macro_input!(input as HtmlRootNested);
TokenStream::from(quote! {#root})
}

#[proc_macro_hack]
pub fn html(input: TokenStream) -> TokenStream {
let root = parse_macro_input!(input as HtmlRoot);
Expand Down
5 changes: 5 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,14 @@ use proc_macro_hack::proc_macro_hack;
#[proc_macro_hack(support_nested)]
pub use yew_macro::html;

#[doc(hidden)]
#[proc_macro_hack(support_nested)]
pub use yew_macro::html_nested;

/// This module contains macros which implements html! macro and JSX-like templates
pub mod macros {
pub use crate::html;
pub use crate::html_nested;
pub use yew_macro::Properties;
}

Expand Down
29 changes: 29 additions & 0 deletions src/utils.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! This module contains useful utils to get information about the current document.

use failure::{err_msg, Error};
use std::marker::PhantomData;
use stdweb::web::document;

/// Returns `host` for the current document. Useful to connect to a server that server the app.
Expand All @@ -10,3 +11,31 @@ pub fn host() -> Result<String, Error> {
.ok_or_else(|| err_msg("can't get location"))
.and_then(|l| l.host().map_err(Error::from))
}

/// Specialty type necessary for helping flattening components returned from nested html macros.
#[derive(Debug)]
pub struct NodeSeq<IN, OUT>(Vec<OUT>, PhantomData<IN>);

impl<IN: Into<OUT>, OUT> From<IN> for NodeSeq<IN, OUT> {
fn from(val: IN) -> Self {
Self(vec![val.into()], PhantomData::default())
}
}

impl<IN: Into<OUT>, OUT> From<Vec<IN>> for NodeSeq<IN, OUT> {
fn from(val: Vec<IN>) -> Self {
Self(
val.into_iter().map(|x| x.into()).collect(),
PhantomData::default(),
)
}
}

impl<IN: Into<OUT>, OUT> IntoIterator for NodeSeq<IN, OUT> {
type Item = OUT;
type IntoIter = std::vec::IntoIter<Self::Item>;

fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
15 changes: 1 addition & 14 deletions tests/macro/html-component-fail.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

use std::marker::PhantomData;
use yew::prelude::*;
use yew::virtual_dom::{VChild, VComp, VNode};

#[derive(Clone, Properties, PartialEq)]
pub struct ChildProperties {
Expand All @@ -21,18 +20,6 @@ impl Component for Child {
fn view(&self) -> Html { unimplemented!() }
}

impl From<VChild<Child>> for ChildProperties {
fn from(comp: VChild<Child>) -> Self {
comp.props
}
}

impl Into<VNode> for ChildProperties {
fn into(self) -> VNode {
VComp::new::<Child>(self, NodeRef::default()).into()
}
}

#[derive(Clone, Properties)]
pub struct ChildContainerProperties {
#[props(required)]
Expand Down Expand Up @@ -92,7 +79,7 @@ fn compile_fail() {
html! { <ChildContainer></ChildContainer> };
html! { <ChildContainer>{ "Not allowed" }</ChildContainer> };
html! { <ChildContainer><></></ChildContainer> };
html! { <ChildContainer><Child int=1 /><other /></ChildContainer> };
html! { <ChildContainer><other /></ChildContainer> };

html! { <Generic<String>></Generic> };
html! { <Generic<String>></Generic<Vec<String>>> };
Expand Down
Loading