Skip to content

Commit

Permalink
Add html_nested! macro to support nested iterable children access (y…
Browse files Browse the repository at this point in the history
…ewstack#843)

* Revert "Revert "Improve nested html! expansion by unwrapping VNodes (yewstack#820)" (yewstack#842)"

This reverts commit 70862a4

* Add minor fix to VList to conform with yewstack#820

* Add unittest for the regression bug yewstack#839

* Resolve yewstack#839 regression issue introduced by yewstack#820

* wip

* Revert "Resolve yewstack#839 regression issue introduced by yewstack#820"

This reverts commit 34dc935.

* Use explicit html_nested

* Remove NodeSeq from prelude

* Clean up tests

* Add list and tag tests

* Revert vtag children change

Co-authored-by: Justin Starry <justin.m.starry@gmail.com>
  • Loading branch information
2 people authored and llebout committed Jan 20, 2020
1 parent 5c52df3 commit 888a6d9
Show file tree
Hide file tree
Showing 10 changed files with 201 additions and 123 deletions.
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

0 comments on commit 888a6d9

Please sign in to comment.