Remove generics from virtual dom (#783)

* Fix tests

* Remove generics from virtual dom

* Prep for degenerify

* Fix examples

* Remove props cloning

* Fix tests
This commit is contained in:
Justin Starry 2019-12-08 10:47:51 -08:00 committed by GitHub
parent def59a0079
commit f61667be97
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
67 changed files with 459 additions and 688 deletions

View File

@ -89,7 +89,7 @@ impl Component for Model {
} }
} }
fn view(&self) -> Html<Self> { fn view(&self) -> Html {
let onclick = self.link.callback(|_| Msg::DoIt); let onclick = self.link.callback(|_| Msg::DoIt);
html! { html! {
// Render your model here // Render your model here
@ -323,8 +323,8 @@ Use external crates and put values from them into the template:
extern crate chrono; extern crate chrono;
use chrono::prelude::*; use chrono::prelude::*;
impl Renderable<Model> for Model { impl Renderable for Model {
fn render(&self) -> Html<Self> { fn render(&self) -> Html {
html! { html! {
<p>{ Local::now() }</p> <p>{ Local::now() }</p>
} }

View File

@ -83,7 +83,6 @@ impl ToTokens for HtmlComponent {
props, props,
children, children,
} = self; } = self;
let vcomp_scope = Ident::new("__yew_vcomp_scope", Span::call_site());
let validate_props = if let Props::List(ListProps { props, .. }) = props { let validate_props = if let Props::List(ListProps { props, .. }) = props {
let prop_ref = Ident::new("__yew_prop_ref", Span::call_site()); let prop_ref = Ident::new("__yew_prop_ref", Span::call_site());
@ -139,9 +138,11 @@ impl ToTokens for HtmlComponent {
let init_props = match props { let init_props = match props {
Props::List(ListProps { props, .. }) => { Props::List(ListProps { props, .. }) => {
let set_props = props.iter().map(|HtmlProp { label, value }| { let set_props = props.iter().map(|HtmlProp { label, value }| {
quote_spanned! { value.span()=> quote_spanned! { value.span()=> .#label(
.#label(<::yew::virtual_dom::vcomp::VComp<_> as ::yew::virtual_dom::Transformer<_, _, _>>::transform(#vcomp_scope.clone(), #value)) <::yew::virtual_dom::vcomp::VComp as ::yew::virtual_dom::Transformer<_, _>>::transform(
} #value
)
)}
}); });
quote! { quote! {
@ -181,9 +182,7 @@ impl ToTokens for HtmlComponent {
#validate_props #validate_props
} }
let #vcomp_scope: ::yew::html::ScopeHolder<_> = ::std::default::Default::default(); ::yew::virtual_dom::VChild::<#ty>::new(#init_props, #node_ref)
let __yew_node_ref: ::yew::html::NodeRef = #node_ref;
::yew::virtual_dom::VChild::<#ty, _>::new(#init_props, #vcomp_scope, __yew_node_ref)
}}); }});
} }
} }

View File

@ -49,18 +49,12 @@ impl Parse for HtmlPropSuffix {
if let TokenTree::Punct(punct) = &next { if let TokenTree::Punct(punct) = &next {
match punct.as_char() { match punct.as_char() {
'>' => { '>' => {
let possible_tag_end = input.peek(Token![<]) angle_count -= 1;
|| input.peek(syn::token::Brace) if angle_count == 0 {
|| input.is_empty(); gt = Some(syn::token::Gt {
spans: [punct.span()],
if angle_count > 1 || possible_tag_end { });
angle_count -= 1; break;
if angle_count == 0 {
gt = Some(syn::token::Gt {
spans: [punct.span()],
});
break;
}
} }
} }
'<' => angle_count += 1, '<' => angle_count += 1,
@ -73,14 +67,6 @@ impl Parse for HtmlPropSuffix {
break; break;
} }
} }
'-' => {
if input.peek(Token![>]) {
// Handle explicit return types in callbacks (#560)
// We increase angle_count here in order to ignore
// the following >.
angle_count += 1;
}
}
_ => {} _ => {}
}; };
} }

View File

@ -102,7 +102,6 @@ impl ToTokens for HtmlTag {
} = &attributes; } = &attributes;
let vtag = Ident::new("__yew_vtag", tag_name.span()); let vtag = Ident::new("__yew_vtag", tag_name.span());
let vtag_scope = Ident::new("__yew_vtag_scope", Span::call_site());
let attr_pairs = attributes.iter().map(|TagAttribute { label, value }| { let attr_pairs = attributes.iter().map(|TagAttribute { label, value }| {
let label_str = label.to_string(); let label_str = label.to_string();
quote_spanned! {value.span() => (#label_str.to_owned(), (#value).to_string()) } quote_spanned! {value.span() => (#label_str.to_owned(), (#value).to_string()) }
@ -155,16 +154,15 @@ impl ToTokens for HtmlTag {
quote_spanned! {name.span()=> { quote_spanned! {name.span()=> {
::yew::html::#name::Wrapper::new( ::yew::html::#name::Wrapper::new(
<::yew::virtual_dom::vtag::VTag<_> as ::yew::virtual_dom::Transformer<_, _, _>>::transform( <::yew::virtual_dom::vtag::VTag as ::yew::virtual_dom::Transformer<_, _>>::transform(
#vtag_scope.clone(), #callback #callback
) )
) )
}} }}
}); });
tokens.extend(quote! {{ tokens.extend(quote! {{
let #vtag_scope: ::yew::html::ScopeHolder<_> = ::std::default::Default::default(); let mut #vtag = ::yew::virtual_dom::vtag::VTag::new(#name);
let mut #vtag = ::yew::virtual_dom::vtag::VTag::new_with_scope(#name, #vtag_scope.clone());
#(#set_kind)* #(#set_kind)*
#(#set_value)* #(#set_value)*
#(#add_href)* #(#add_href)*

View File

@ -32,7 +32,7 @@
//! # unimplemented!() //! # unimplemented!()
//! # } //! # }
//! # //! #
//! # fn view(&self) -> Html<Self> { //! # fn view(&self) -> Html {
//! # //! #
//! // ... //! // ...
//! //!

View File

@ -48,7 +48,7 @@ impl Component for Model {
true true
} }
fn view(&self) -> Html<Self> { fn view(&self) -> Html {
html! { html! {
<div> <div>
<nav class="menu"> <nav class="menu">

View File

@ -146,7 +146,7 @@ impl Component for Model {
true true
} }
fn view(&self) -> Html<Self> { fn view(&self) -> Html {
match self.scene { match self.scene {
Scene::ClientsList => html! { Scene::ClientsList => html! {
<div class="crm"> <div class="crm">
@ -179,8 +179,8 @@ impl Component for Model {
} }
} }
impl Renderable<Model> for Client { impl Renderable for Client {
fn render(&self) -> Html<Model> { fn render(&self) -> Html {
html! { html! {
<div class="client"> <div class="client">
<p>{ format!("First Name: {}", self.first_name) }</p> <p>{ format!("First Name: {}", self.first_name) }</p>
@ -193,7 +193,7 @@ impl Renderable<Model> for Client {
} }
impl Client { impl Client {
fn view_first_name_input(&self, link: &ComponentLink<Model>) -> Html<Model> { fn view_first_name_input(&self, link: &ComponentLink<Model>) -> Html {
html! { html! {
<input class="new-client firstname" <input class="new-client firstname"
placeholder="First name" placeholder="First name"
@ -202,7 +202,7 @@ impl Client {
} }
} }
fn view_last_name_input(&self, link: &ComponentLink<Model>) -> Html<Model> { fn view_last_name_input(&self, link: &ComponentLink<Model>) -> Html {
html! { html! {
<input class="new-client lastname" <input class="new-client lastname"
placeholder="Last name" placeholder="Last name"
@ -210,7 +210,7 @@ impl Client {
oninput=link.callback(|e: InputData| Msg::UpdateLastName(e.value)) /> oninput=link.callback(|e: InputData| Msg::UpdateLastName(e.value)) />
} }
} }
fn view_description_textarea(&self, link: &ComponentLink<Model>) -> Html<Model> { fn view_description_textarea(&self, link: &ComponentLink<Model>) -> Html {
html! { html! {
<textarea class=("new-client", "description") <textarea class=("new-client", "description")
placeholder="Description" placeholder="Description"

View File

@ -2,14 +2,11 @@
/// Source: https://github.com/acmumn/mentoring/blob/master/web-client/src/view/markdown.rs /// Source: https://github.com/acmumn/mentoring/blob/master/web-client/src/view/markdown.rs
use pulldown_cmark::{Alignment, Event, Parser, Tag, OPTION_ENABLE_TABLES}; use pulldown_cmark::{Alignment, Event, Parser, Tag, OPTION_ENABLE_TABLES};
use yew::virtual_dom::{VNode, VTag, VText}; use yew::virtual_dom::{VNode, VTag, VText};
use yew::{html, Component, Html}; use yew::{html, Html};
/// Renders a string of Markdown to HTML with the default options (footnotes /// Renders a string of Markdown to HTML with the default options (footnotes
/// disabled, tables enabled). /// disabled, tables enabled).
pub fn render_markdown<COMP>(src: &str) -> Html<COMP> pub fn render_markdown(src: &str) -> Html {
where
COMP: Component,
{
let mut elems = vec![]; let mut elems = vec![];
let mut spine = vec![]; let mut spine = vec![];
@ -81,10 +78,7 @@ where
} }
} }
fn make_tag<COMP>(t: Tag) -> VTag<COMP> fn make_tag(t: Tag) -> VTag {
where
COMP: Component,
{
match t { match t {
Tag::Paragraph => VTag::new("p"), Tag::Paragraph => VTag::new("p"),
Tag::Rule => VTag::new("hr"), Tag::Rule => VTag::new("hr"),

View File

@ -51,7 +51,7 @@ impl Component for Barrier {
true true
} }
fn view(&self) -> Html<Self> { fn view(&self) -> Html {
let onsignal = &self.link.callback(|_| Msg::ChildClicked); let onsignal = &self.link.callback(|_| Msg::ChildClicked);
html! { html! {
<div class="barrier"> <div class="barrier">

View File

@ -44,7 +44,7 @@ impl Component for Button {
true true
} }
fn view(&self) -> Html<Self> { fn view(&self) -> Html {
html! { html! {
<button onclick=self.link.callback(|_| Msg::Clicked)> <button onclick=self.link.callback(|_| Msg::Clicked)>
{ &self.title } { &self.title }

View File

@ -61,7 +61,7 @@ impl Component for Counter {
true true
} }
fn view(&self) -> Html<Self> { fn view(&self) -> Html {
let colorize = { let colorize = {
match self.color { match self.color {
Color::Red => "background: red;", Color::Red => "background: red;",

View File

@ -46,7 +46,7 @@ impl Component for Model {
} }
} }
fn view(&self) -> Html<Self> { fn view(&self) -> Html {
let counter = |x| { let counter = |x| {
html! { html! {
<Counter initial=x color=&self.color <Counter initial=x color=&self.color
@ -64,7 +64,7 @@ impl Component for Model {
} }
impl Model { impl Model {
fn view_barrier(&self) -> Html<Self> { fn view_barrier(&self) -> Html {
if self.with_barrier { if self.with_barrier {
html! { html! {
<Barrier limit=10 onsignal=self.link.callback(|_| Msg::Repaint) /> <Barrier limit=10 onsignal=self.link.callback(|_| Msg::Repaint) />

View File

@ -169,7 +169,7 @@ impl Component for Model {
true true
} }
fn view(&self) -> Html<Self> { fn view(&self) -> Html {
html! { html! {
<div> <div>
<nav class="menu"> <nav class="menu">
@ -206,7 +206,7 @@ impl Component for Model {
} }
impl Model { impl Model {
fn view_data(&self) -> Html<Self> { fn view_data(&self) -> Html {
if let Some(value) = self.data { if let Some(value) = self.data {
html! { html! {
<p>{ value }</p> <p>{ value }</p>

View File

@ -65,7 +65,7 @@ impl Component for Model {
true true
} }
fn view(&self) -> Html<Self> { fn view(&self) -> Html {
let flag = self.by_chunks; let flag = self.by_chunks;
html! { html! {
<div> <div>
@ -91,7 +91,7 @@ impl Component for Model {
} }
impl Model { impl Model {
fn view_file(&self, data: &str) -> Html<Self> { fn view_file(&self, data: &str) -> Html {
html! { html! {
<li>{ data }</li> <li>{ data }</li>
} }

View File

@ -34,7 +34,7 @@ impl Component for Model {
true true
} }
fn view(&self) -> Html<Self> { fn view(&self) -> Html {
html! { html! {
<> <>
<nav class="menu">{ self.view_menu() }</nav> <nav class="menu">{ self.view_menu() }</nav>
@ -54,7 +54,7 @@ impl Component for Model {
} }
impl Model { impl Model {
fn view_cols(&self) -> Html<Self> { fn view_cols(&self) -> Html {
let render = |idx| { let render = |idx| {
html! { html! {
<td>{ idx }</td> <td>{ idx }</td>
@ -65,7 +65,7 @@ impl Model {
} }
} }
fn view_menu(&self) -> Html<Self> { fn view_menu(&self) -> Html {
html! { html! {
<> <>
<button onclick=self.link.callback(|_| Msg::More)>{ "More" }</button> <button onclick=self.link.callback(|_| Msg::More)>{ "More" }</button>

View File

@ -100,7 +100,7 @@ impl Component for Model {
} }
} }
fn view(&self) -> Html<Self> { fn view(&self) -> Html {
match &self.markdown { match &self.markdown {
FetchState::NotFetching => html! { FetchState::NotFetching => html! {
<button onclick=self.link.callback(|_| Msg::GetMarkdown)> <button onclick=self.link.callback(|_| Msg::GetMarkdown)>

View File

@ -208,7 +208,7 @@ impl Component for Model {
true true
} }
fn view(&self) -> Html<Self> { fn view(&self) -> Html {
html! { html! {
<div> <div>
<section class="game-container"> <section class="game-container">
@ -241,7 +241,7 @@ impl Component for Model {
} }
impl Model { impl Model {
fn view_cellule(&self, (idx, cellule): (usize, &Cellule)) -> Html<Self> { fn view_cellule(&self, (idx, cellule): (usize, &Cellule)) -> Html {
let cellule_status = { let cellule_status = {
if cellule.life_state == LifeState::Alive { if cellule.life_state == LifeState::Alive {
"cellule-live" "cellule-live"

View File

@ -33,7 +33,7 @@ impl Component for Model {
true true
} }
fn view(&self) -> Html<Self> { fn view(&self) -> Html {
let js_svg = js! { let js_svg = js! {
var div = document.createElement("div"); var div = document.createElement("div");
div.innerHTML = @{SVG.to_string()}; div.innerHTML = @{SVG.to_string()};

View File

@ -53,7 +53,7 @@ impl Component for Model {
false false
} }
fn view(&self) -> Html<Self> { fn view(&self) -> Html {
html! { html! {
<div> <div>
<textarea oninput=|input| Msg::Payload(input.value) <textarea oninput=|input| Msg::Payload(input.value)

View File

@ -33,17 +33,17 @@ impl Component for Model {
true true
} }
fn view(&self) -> Html<Self> { fn view(&self) -> Html {
html! { html! {
<table> <table>
{ (0..99).map(|row| self.view_row(row)).collect::<Html<Self>>() } { (0..99).map(|row| self.view_row(row)).collect::<Html>() }
</table> </table>
} }
} }
} }
impl Model { impl Model {
fn view_square(&self, row: u32, column: u32) -> Html<Self> { fn view_square(&self, row: u32, column: u32) -> Html {
html! { html! {
<td class=square_class((column, row), self.selected) <td class=square_class((column, row), self.selected)
onclick=self.link.callback(move |_| Msg::Select(column, row))> onclick=self.link.callback(move |_| Msg::Select(column, row))>
@ -51,7 +51,7 @@ impl Model {
} }
} }
fn view_row(&self, row: u32) -> Html<Self> { fn view_row(&self, row: u32) -> Html {
html! { html! {
<tr> <tr>
{for (0..99).map(|column| { {for (0..99).map(|column| {

View File

@ -23,7 +23,7 @@ impl Component for Model {
true true
} }
fn view(&self) -> Html<Self> { fn view(&self) -> Html {
html! { html! {
<div> <div>
<button onclick=self.link.callback(|_| Msg::Click)>{ "Click" }</button> <button onclick=self.link.callback(|_| Msg::Click)>{ "Click" }</button>

View File

@ -29,7 +29,7 @@ impl Component for Model {
true true
} }
fn view(&self) -> Html<Self> { fn view(&self) -> Html {
html! { html! {
<div> <div>
<input <input

View File

@ -68,7 +68,7 @@ impl Component for Model {
true true
} }
fn view(&self) -> Html<Self> { fn view(&self) -> Html {
html! { html! {
<div> <div>
<nav class="menu"> <nav class="menu">

View File

@ -1,4 +1,4 @@
use crate::list::Hovered; use crate::Hovered;
use yew::prelude::*; use yew::prelude::*;
pub struct ListHeader { pub struct ListHeader {
@ -13,30 +13,22 @@ pub struct Props {
pub text: String, pub text: String,
} }
pub enum Msg {
Hover,
}
impl Component for ListHeader { impl Component for ListHeader {
type Message = Msg; type Message = ();
type Properties = Props; type Properties = Props;
fn create(props: Self::Properties, _: ComponentLink<Self>) -> Self { fn create(props: Self::Properties, _: ComponentLink<Self>) -> Self {
ListHeader { props } ListHeader { props }
} }
fn update(&mut self, msg: Self::Message) -> ShouldRender { fn update(&mut self, _: Self::Message) -> ShouldRender {
match msg {
Msg::Hover => {
self.props.on_hover.emit(Hovered::Header);
}
}
false false
} }
fn view(&self) -> Html<Self> { fn view(&self) -> Html {
let onmouseover = self.props.on_hover.reform(|_| Hovered::Header);
html! { html! {
<div class="list-header" onmouseover=|_| Msg::Hover> <div class="list-header" onmouseover=onmouseover>
{ &self.props.text } { &self.props.text }
</div> </div>
} }

View File

@ -1,4 +1,4 @@
use crate::list::Hovered; use crate::Hovered;
use yew::html::Children; use yew::html::Children;
use yew::prelude::*; use yew::prelude::*;
@ -13,35 +13,26 @@ pub struct Props {
pub on_hover: Callback<Hovered>, pub on_hover: Callback<Hovered>,
#[props(required)] #[props(required)]
pub name: String, pub name: String,
pub children: Children<ListItem>, pub children: Children,
}
pub enum Msg {
Hover,
} }
impl Component for ListItem { impl Component for ListItem {
type Message = Msg; type Message = ();
type Properties = Props; type Properties = Props;
fn create(props: Self::Properties, _: ComponentLink<Self>) -> Self { fn create(props: Self::Properties, _: ComponentLink<Self>) -> Self {
ListItem { props } ListItem { props }
} }
fn update(&mut self, msg: Self::Message) -> ShouldRender { fn update(&mut self, _msg: Self::Message) -> ShouldRender {
match msg {
Msg::Hover => {
self.props
.on_hover
.emit(Hovered::Item(self.props.name.clone()));
}
}
false false
} }
fn view(&self) -> Html<Self> { fn view(&self) -> Html {
let name = self.props.name.clone();
let onmouseover = self.props.on_hover.reform(move |_| Hovered::Item(name.clone()));
html! { html! {
<div class="list-item" onmouseover=|_| Msg::Hover> <div class="list-item" onmouseover=onmouseover>
{ &self.props.name } { &self.props.name }
{ self.view_details() } { self.view_details() }
</div> </div>
@ -50,7 +41,7 @@ impl Component for ListItem {
} }
impl ListItem { impl ListItem {
fn view_details(&self) -> Html<Self> { fn view_details(&self) -> Html {
if self.props.children.is_empty() { if self.props.children.is_empty() {
return html! {}; return html! {};
} }

View File

@ -6,36 +6,88 @@ mod list;
use header::ListHeader; use header::ListHeader;
use item::ListItem; use item::ListItem;
use list::{List, Msg as ListMsg}; use list::List;
use yew::prelude::*; use yew::prelude::*;
use std::fmt;
pub struct Model; pub struct Model {
link: ComponentLink<Self>,
hovered: Hovered,
}
#[derive(Debug)]
pub enum Hovered {
Header,
Item(String),
List,
None,
}
pub enum Msg {
Hover(Hovered),
}
impl Component for Model { impl Component for Model {
type Message = (); type Message = Msg;
type Properties = (); type Properties = ();
fn create(_: Self::Properties, _: ComponentLink<Self>) -> Self { fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self {
Model Model { link,
hovered: Hovered::None,
}
} }
fn update(&mut self, _: Self::Message) -> ShouldRender { fn update(&mut self, msg: Self::Message) -> ShouldRender {
match msg {
Msg::Hover(hovered) => self.hovered = hovered,
}
true true
} }
fn view(&self) -> Html<Self> { fn view(&self) -> Html {
let on_hover = self.link.callback(Msg::Hover);
html! { html! {
<div class="main"> <div class="main">
<h1>{ "Nested List Demo" }</h1> <h1>{ "Nested List Demo" }</h1>
<List> <List on_hover=on_hover.clone()>
<ListHeader text="Calling all Rusties!" on_hover=ListMsg::Hover /> <ListHeader text="Calling all Rusties!" on_hover=on_hover.clone() />
<ListItem name="Rustin" on_hover=ListMsg::Hover /> <ListItem name="Rustin" on_hover=on_hover.clone() />
<ListItem hide={true} name="Rustaroo" on_hover=ListMsg::Hover /> <ListItem hide={true} name="Rustaroo" on_hover=on_hover.clone() />
<ListItem name="Rustifer" on_hover=ListMsg::Hover> <ListItem name="Rustifer" on_hover=on_hover.clone()>
<span>{"Hello!"}</span> <span>{"Hello!"}</span>
</ListItem> </ListItem>
</List> </List>
{self.view_last_hovered()}
</div> </div>
} }
} }
} }
impl Model {
fn view_last_hovered(&self) -> Html {
html! {
<div class="last-hovered">
{ "Last hovered:"}
<span class="last-hovered-text">
{ &self.hovered }
</span>
</div>
}
}
}
impl fmt::Display for Hovered {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}",
match self {
Hovered::Header => "Header",
Hovered::Item(name) => name,
Hovered::List => "List container",
Hovered::None => "Nothing",
}
)
}
}

View File

@ -1,22 +1,10 @@
use crate::{header::Props as HeaderProps, ListHeader}; use crate::{header::Props as HeaderProps, ListHeader};
use crate::{item::Props as ItemProps, ListItem}; use crate::{item::Props as ItemProps, ListItem};
use std::fmt; use crate::Hovered;
use yew::html::{ChildrenRenderer, NodeRef, ScopeHolder}; use yew::html::{ChildrenRenderer, NodeRef};
use yew::prelude::*; use yew::prelude::*;
use yew::virtual_dom::{VChild, VComp, VNode}; use yew::virtual_dom::{VChild, VComp, VNode};
#[derive(Debug)]
pub enum Hovered {
Header,
Item(String),
List,
None,
}
pub enum Msg {
Hover(Hovered),
}
pub enum Variants { pub enum Variants {
Item(<ListItem as Component>::Properties), Item(<ListItem as Component>::Properties),
Header(<ListHeader as Component>::Properties), Header(<ListHeader as Component>::Properties),
@ -36,59 +24,50 @@ impl From<HeaderProps> for Variants {
pub struct ListVariant { pub struct ListVariant {
props: Variants, props: Variants,
scope: ScopeHolder<List>,
} }
#[derive(Properties)] #[derive(Properties)]
pub struct Props { pub struct Props {
#[props(required)] #[props(required)]
pub children: ChildrenRenderer<ListVariant>, pub children: ChildrenRenderer<ListVariant>,
#[props(required)]
pub on_hover: Callback<Hovered>,
} }
pub struct List { pub struct List {
props: Props, props: Props,
hovered: Hovered,
} }
impl Component for List { impl Component for List {
type Message = Msg; type Message = ();
type Properties = Props; type Properties = Props;
fn create(props: Self::Properties, _: ComponentLink<Self>) -> Self { fn create(props: Self::Properties, _: ComponentLink<Self>) -> Self {
List { List { props }
props,
hovered: Hovered::None,
}
} }
fn update(&mut self, msg: Self::Message) -> ShouldRender { fn update(&mut self, _msg: Self::Message) -> ShouldRender {
match msg { false
Msg::Hover(hovered) => self.hovered = hovered,
}
true
} }
fn view(&self) -> Html<Self> { fn view(&self) -> Html {
let onmouseover = self.props.on_hover.reform(|_| Hovered::List);
let onmouseout = self.props.on_hover.reform(|_| Hovered::None);
html! { html! {
<div <div class="list-container" onmouseout=onmouseout onmouseover=onmouseover>
class="list-container"
onmouseout=|_| Msg::Hover(Hovered::None)
onmouseover=|_| Msg::Hover(Hovered::List)
>
<div class="list"> <div class="list">
{self.view_header()} {self.view_header()}
<div class="items"> <div class="items">
{self.view_items()} {self.view_items()}
</div> </div>
</div> </div>
{self.view_last_hovered()}
</div> </div>
} }
} }
} }
impl List { impl List {
fn view_header(&self) -> Html<Self> { fn view_header(&self) -> Html {
html! {{ html! {{
for self.props.children.iter().filter(|c| match c.props { for self.props.children.iter().filter(|c| match c.props {
Variants::Header(_) => true, Variants::Header(_) => true,
@ -97,7 +76,7 @@ impl List {
}} }}
} }
fn view_items(&self) -> Html<Self> { fn view_items(&self) -> Html {
html! {{ html! {{
for self.props.children.iter().filter(|c| match &c.props { for self.props.children.iter().filter(|c| match &c.props {
Variants::Item(props) => !props.hide, Variants::Item(props) => !props.hide,
@ -110,52 +89,25 @@ impl List {
}) })
}} }}
} }
fn view_last_hovered(&self) -> Html<Self> {
html! {
<div class="last-hovered">
{ "Last hovered:"}
<span class="last-hovered-text">
{ &self.hovered }
</span>
</div>
}
}
} }
impl fmt::Display for Hovered { impl<CHILD> From<VChild<CHILD>> for ListVariant
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}",
match self {
Hovered::Header => "Header",
Hovered::Item(name) => name,
Hovered::List => "List container",
Hovered::None => "Nothing",
}
)
}
}
impl<CHILD> From<VChild<CHILD, List>> for ListVariant
where where
CHILD: Component, CHILD: Component,
CHILD::Properties: Into<Variants>, CHILD::Properties: Into<Variants>,
{ {
fn from(vchild: VChild<CHILD, List>) -> Self { fn from(vchild: VChild<CHILD>) -> Self {
ListVariant { ListVariant {
props: vchild.props.into(), props: vchild.props.into(),
scope: vchild.scope,
} }
} }
} }
impl Into<VNode<List>> for ListVariant { impl Into<VNode> for ListVariant {
fn into(self) -> VNode<List> { fn into(self) -> VNode {
match self.props { match self.props {
Variants::Header(props) => VComp::new::<ListHeader>(props, self.scope, NodeRef::default()).into(), Variants::Header(props) => VComp::new::<ListHeader>(props, NodeRef::default()).into(),
Variants::Item(props) => VComp::new::<ListItem>(props, self.scope, NodeRef::default()).into(), Variants::Item(props) => VComp::new::<ListItem>(props, NodeRef::default()).into(),
} }
} }
} }

View File

@ -32,7 +32,7 @@ impl Component for InputComponent {
false false
} }
fn view(&self) -> Html<Self> { fn view(&self) -> Html {
html! { html! {
<input <input
type="text" type="text"

View File

@ -46,7 +46,7 @@ impl Component for Model {
true true
} }
fn view(&self) -> Html<Self> { fn view(&self) -> Html {
html! { html! {
<div class="main"> <div class="main">
<h1>{ "Node Refs Demo" }</h1> <h1>{ "Node Refs Demo" }</h1>

View File

@ -67,7 +67,7 @@ impl Component for Model {
true true
} }
fn view(&self) -> Html<Self> { fn view(&self) -> Html {
let view_exchange = |exchange| { let view_exchange = |exchange| {
html! { html! {
<li>{ exchange }</li> <li>{ exchange }</li>

View File

@ -108,7 +108,7 @@ impl Component for BModel {
true true
} }
fn view(&self) -> Html<Self> { fn view(&self) -> Html {
html! { html! {
<div> <div>
<div> <div>
@ -132,7 +132,7 @@ impl BModel {
format!("Number: None") format!("Number: None")
} }
} }
fn display_subpath_input(&self) -> Html<Self> { fn display_subpath_input(&self) -> Html {
let sub_path = self.sub_path.clone(); let sub_path = self.sub_path.clone();
html! { html! {
<input placeholder="subpath" <input placeholder="subpath"

View File

@ -68,7 +68,7 @@ impl Component for Model {
} }
} }
fn view(&self) -> Html<Self> { fn view(&self) -> Html {
html! { html! {
<div> <div>
<nav class="menu"> <nav class="menu">
@ -83,8 +83,8 @@ impl Component for Model {
} }
} }
impl Renderable<Model> for Child { impl Renderable for Child {
fn render(&self) -> Html<Model> { fn render(&self) -> Html {
match self { match self {
Child::A => html! { Child::A => html! {
<> <>

View File

@ -71,7 +71,7 @@ impl Component for RouterButton {
true true
} }
fn view(&self) -> Html<Self> { fn view(&self) -> Html {
html! { html! {
<button <button
class=self.props.classes.clone(), class=self.props.classes.clone(),

View File

@ -68,7 +68,7 @@ impl Component for Model {
} }
} }
fn view(&self) -> Html<Self> { fn view(&self) -> Html {
html! { html! {
<div id="fullscreen"> <div id="fullscreen">
<div id="left_pane"> <div id="left_pane">
@ -87,7 +87,7 @@ impl Component for Model {
} }
impl Model { impl Model {
fn view_scene(&self) -> Html<Self> { fn view_scene(&self) -> Html {
if let Some(scene) = self.scene.as_ref() { if let Some(scene) = self.scene.as_ref() {
match scene { match scene {
Scene::Counter => html! { <Counter /> }, Scene::Counter => html! { <Counter /> },

View File

@ -35,7 +35,7 @@ impl Component for Model {
true true
} }
fn view(&self) -> Html<Self> { fn view(&self) -> Html {
html! { html! {
<div> <div>
<div> <div>

View File

@ -99,7 +99,7 @@ impl Component for Model {
true true
} }
fn view(&self) -> Html<Self> { fn view(&self) -> Html {
let view_message = |message| { let view_message = |message| {
html! { <p>{ message }</p> } html! { <p>{ message }</p> }
}; };

View File

@ -121,7 +121,7 @@ impl Component for Model {
true true
} }
fn view(&self) -> Html<Self> { fn view(&self) -> Html {
html! { html! {
<div class="todomvc-wrapper"> <div class="todomvc-wrapper">
<section class="todoapp"> <section class="todoapp">
@ -163,7 +163,7 @@ impl Component for Model {
} }
impl Model { impl Model {
fn view_filter(&self, filter: Filter) -> Html<Self> { fn view_filter(&self, filter: Filter) -> Html {
let flt = filter.clone(); let flt = filter.clone();
html! { html! {
<li> <li>
@ -176,7 +176,7 @@ impl Model {
} }
} }
fn view_input(&self) -> Html<Self> { fn view_input(&self) -> Html {
html! { html! {
// You can use standard Rust comments. One line: // You can use standard Rust comments. One line:
// <li></li> // <li></li>
@ -195,7 +195,7 @@ impl Model {
} }
} }
fn view_entry(&self, (idx, entry): (usize, &Entry)) -> Html<Self> { fn view_entry(&self, (idx, entry): (usize, &Entry)) -> Html {
let mut class = "todo".to_string(); let mut class = "todo".to_string();
if entry.editing { if entry.editing {
class.push_str(" editing"); class.push_str(" editing");
@ -219,7 +219,7 @@ impl Model {
} }
} }
fn view_entry_edit_input(&self, (idx, entry): (usize, &Entry)) -> Html<Self> { fn view_entry_edit_input(&self, (idx, entry): (usize, &Entry)) -> Html {
if entry.editing { if entry.editing {
html! { html! {
<input class="edit" <input class="edit"

View File

@ -69,7 +69,7 @@ impl Component for Model {
true true
} }
fn view(&self) -> Html<Self> { fn view(&self) -> Html {
html! { html! {
<div> <div>
<h3>{ format!("{} received <{}>", self.selector, self.title) }</h3> <h3>{ format!("{} received <{}>", self.selector, self.title) }</h3>

View File

@ -14,7 +14,7 @@
//!# type Message = ();type Properties = (); //!# type Message = ();type Properties = ();
//!# fn create(props: Self::Properties,link: ComponentLink<Self>) -> Self {unimplemented!()} //!# fn create(props: Self::Properties,link: ComponentLink<Self>) -> Self {unimplemented!()}
//!# fn update(&mut self,msg: Self::Message) -> bool {unimplemented!()} //!# fn update(&mut self,msg: Self::Message) -> bool {unimplemented!()}
//!# fn view(&self) -> Html<Self> {unimplemented!()}} //!# fn view(&self) -> Html {unimplemented!()}}
//! impl ToString for Scene { //! impl ToString for Scene {
//! fn to_string(&self) -> String { //! fn to_string(&self) -> String {
//! match self { //! match self {
@ -24,7 +24,7 @@
//! } //! }
//! } //! }
//! //!
//! fn view(link: ComponentLink<Model>) -> Html<Model> { //! fn view(link: ComponentLink<Model>) -> Html {
//! let scenes = vec![Scene::First, Scene::Second]; //! let scenes = vec![Scene::First, Scene::Second];
//! html! { //! html! {
//! <Select<Scene> options=scenes onchange=link.callback(|_| ()) /> //! <Select<Scene> options=scenes onchange=link.callback(|_| ()) />
@ -94,7 +94,7 @@ where
true true
} }
fn view(&self) -> Html<Self> { fn view(&self) -> Html {
let selected = self.props.selected.as_ref(); let selected = self.props.selected.as_ref();
let view_option = |value: &T| { let view_option = |value: &T| {
let flag = selected == Some(value); let flag = selected == Some(value);

View File

@ -51,13 +51,13 @@ pub trait Component: Sized + 'static {
TypeId::of::<Self::Properties>() != TypeId::of::<()>() TypeId::of::<Self::Properties>() != TypeId::of::<()>()
} }
/// Called by rendering loop. /// Called by rendering loop.
fn view(&self) -> Html<Self>; fn view(&self) -> Html;
/// Called for finalization on the final point of the component's lifetime. /// Called for finalization on the final point of the component's lifetime.
fn destroy(&mut self) {} // TODO Replace with `Drop` fn destroy(&mut self) {} // TODO Replace with `Drop`
} }
/// A type which expected as a result of `view` function implementation. /// A type which expected as a result of `view` function implementation.
pub type Html<MSG> = VNode<MSG>; pub type Html = VNode;
/// A type used for accepting children elements in Component::Properties. /// A type used for accepting children elements in Component::Properties.
/// ///
@ -69,7 +69,7 @@ pub type Html<MSG> = VNode<MSG>;
///# use yew::{Children, Html, Properties, Component, ComponentLink, html}; ///# use yew::{Children, Html, Properties, Component, ComponentLink, html};
///# #[derive(Properties)] ///# #[derive(Properties)]
///# struct WrapperProps { ///# struct WrapperProps {
///# children: Children<Wrapper>, ///# children: Children,
///# } ///# }
///# struct Wrapper; ///# struct Wrapper;
///# impl Component for Wrapper{ ///# impl Component for Wrapper{
@ -78,7 +78,7 @@ pub type Html<MSG> = VNode<MSG>;
///# fn create(props: Self::Properties,link: ComponentLink<Self>) -> Self {unimplemented!()} ///# fn create(props: Self::Properties,link: ComponentLink<Self>) -> Self {unimplemented!()}
///# fn update(&mut self,msg: Self::Message) -> bool {unimplemented!()} ///# fn update(&mut self,msg: Self::Message) -> bool {unimplemented!()}
///# // This is not a valid implementation. This is done for space convenience. ///# // This is not a valid implementation. This is done for space convenience.
///# fn view(&self) -> Html<Self> { ///# fn view(&self) -> Html {
/// html! { /// html! {
/// <Wrapper> /// <Wrapper>
/// <h4>{ "Hi" }</h4> /// <h4>{ "Hi" }</h4>
@ -97,7 +97,7 @@ pub type Html<MSG> = VNode<MSG>;
///# use yew::{Children, Html, Properties, Renderable, Component, ComponentLink, html}; ///# use yew::{Children, Html, Properties, Renderable, Component, ComponentLink, html};
/// #[derive(Properties)] /// #[derive(Properties)]
/// struct WrapperProps { /// struct WrapperProps {
/// children: Children<Wrapper>, /// children: Children,
/// } /// }
/// ///
///# struct Wrapper {props: WrapperProps}; ///# struct Wrapper {props: WrapperProps};
@ -107,7 +107,7 @@ pub type Html<MSG> = VNode<MSG>;
///# type Properties = WrapperProps; ///# type Properties = WrapperProps;
///# fn create(props: Self::Properties,link: ComponentLink<Self>) -> Self {unimplemented!()} ///# fn create(props: Self::Properties,link: ComponentLink<Self>) -> Self {unimplemented!()}
///# fn update(&mut self,msg: Self::Message) -> bool {unimplemented!()} ///# fn update(&mut self,msg: Self::Message) -> bool {unimplemented!()}
/// fn view(&self) -> Html<Wrapper> { /// fn view(&self) -> Html {
/// html! { /// html! {
/// <div id="container"> /// <div id="container">
/// { self.props.children.render() } /// { self.props.children.render() }
@ -116,7 +116,7 @@ pub type Html<MSG> = VNode<MSG>;
/// } /// }
/// } /// }
/// ``` /// ```
pub type Children<T> = ChildrenRenderer<Html<T>>; pub type Children = ChildrenRenderer<Html>;
/// A type used for accepting children elements in Component::Properties and accessing their props. /// A type used for accepting children elements in Component::Properties and accessing their props.
/// ///
@ -129,7 +129,7 @@ pub type Children<T> = ChildrenRenderer<Html<T>>;
///# ///#
///# #[derive(Properties)] ///# #[derive(Properties)]
///# struct ListProps { ///# struct ListProps {
///# children: ChildrenWithProps<ListItem, List>, ///# children: ChildrenWithProps<ListItem>,
///# } ///# }
///# struct List; ///# struct List;
///# impl Component for List { ///# impl Component for List {
@ -137,7 +137,7 @@ pub type Children<T> = ChildrenRenderer<Html<T>>;
///# type Properties = ListProps; ///# type Properties = ListProps;
///# fn create(props: Self::Properties, link: ComponentLink<Self>) -> Self {unimplemented!()} ///# fn create(props: Self::Properties, link: ComponentLink<Self>) -> Self {unimplemented!()}
///# fn update(&mut self, msg: Self::Message) -> bool {unimplemented!()} ///# fn update(&mut self, msg: Self::Message) -> bool {unimplemented!()}
///# fn view(&self) -> Html<List> {unimplemented!()} ///# fn view(&self) -> Html {unimplemented!()}
///# } ///# }
///# #[derive(Properties)] ///# #[derive(Properties)]
///# struct ListItemProps { ///# struct ListItemProps {
@ -149,9 +149,9 @@ pub type Children<T> = ChildrenRenderer<Html<T>>;
///# type Properties = ListItemProps; ///# type Properties = ListItemProps;
///# fn create(props: Self::Properties, link: ComponentLink<Self>) -> Self {unimplemented!()} ///# fn create(props: Self::Properties, link: ComponentLink<Self>) -> Self {unimplemented!()}
///# fn update(&mut self, msg: Self::Message) -> bool {unimplemented!()} ///# fn update(&mut self, msg: Self::Message) -> bool {unimplemented!()}
///# fn view(&self) -> Html<Self> {unimplemented!()} ///# fn view(&self) -> Html {unimplemented!()}
///# } ///# }
///# fn view() -> Html<List> { ///# fn view() -> Html {
/// html!{ /// html!{
/// <List> /// <List>
/// <ListItem value="a" /> /// <ListItem value="a" />
@ -171,7 +171,7 @@ pub type Children<T> = ChildrenRenderer<Html<T>>;
///# ///#
/// #[derive(Properties)] /// #[derive(Properties)]
/// struct ListProps { /// struct ListProps {
/// children: ChildrenWithProps<ListItem, List>, /// children: ChildrenWithProps<ListItem>,
/// } /// }
/// ///
///# struct List {props: ListProps}; ///# struct List {props: ListProps};
@ -181,7 +181,7 @@ pub type Children<T> = ChildrenRenderer<Html<T>>;
///# fn create(props: Self::Properties,link: ComponentLink<Self>) -> Self {unimplemented!()} ///# fn create(props: Self::Properties,link: ComponentLink<Self>) -> Self {unimplemented!()}
///# fn update(&mut self,msg: Self::Message) -> bool {unimplemented!()} ///# fn update(&mut self,msg: Self::Message) -> bool {unimplemented!()}
/// // ... /// // ...
/// fn view(&self) -> Html<Self> { /// fn view(&self) -> Html {
/// html!{{ /// html!{{
/// for self.props.children.iter().map(|mut item| { /// for self.props.children.iter().map(|mut item| {
/// item.props.value = format!("item-{}", item.props.value); /// item.props.value = format!("item-{}", item.props.value);
@ -202,10 +202,10 @@ pub type Children<T> = ChildrenRenderer<Html<T>>;
///# type Properties = ListItemProps; ///# type Properties = ListItemProps;
///# fn create(props: Self::Properties, link: ComponentLink<Self>) -> Self {unimplemented!()} ///# fn create(props: Self::Properties, link: ComponentLink<Self>) -> Self {unimplemented!()}
///# fn update(&mut self, msg: Self::Message) -> bool {unimplemented!()} ///# fn update(&mut self, msg: Self::Message) -> bool {unimplemented!()}
///# fn view(&self) -> Html<ListItem> {unimplemented!()} ///# fn view(&self) -> Html {unimplemented!()}
///# } ///# }
/// ``` /// ```
pub type ChildrenWithProps<C, P> = ChildrenRenderer<VChild<C, P>>; pub type ChildrenWithProps<CHILD> = ChildrenRenderer<VChild<CHILD>>;
/// A type used for rendering children html. /// A type used for rendering children html.
pub struct ChildrenRenderer<T> { pub struct ChildrenRenderer<T> {
@ -236,7 +236,7 @@ impl<T> ChildrenRenderer<T> {
/// Render children components and return `Iterator` /// Render children components and return `Iterator`
pub fn iter(&self) -> impl Iterator<Item = T> { pub fn iter(&self) -> impl Iterator<Item = T> {
(&self.boxed_render)().into_iter() self.to_vec().into_iter()
} }
} }
@ -245,6 +245,7 @@ impl<T> Default for ChildrenRenderer<T> {
// False positive: https://github.com/rust-lang/rust-clippy/issues/4002 // False positive: https://github.com/rust-lang/rust-clippy/issues/4002
#[allow(clippy::redundant_closure)] #[allow(clippy::redundant_closure)]
let boxed_render = Box::new(|| Vec::new()); let boxed_render = Box::new(|| Vec::new());
Self { Self {
len: 0, len: 0,
boxed_render, boxed_render,
@ -258,11 +259,11 @@ impl<T> fmt::Debug for ChildrenRenderer<T> {
} }
} }
impl<T, COMP: Component> Renderable<COMP> for ChildrenRenderer<T> impl<T> Renderable for ChildrenRenderer<T>
where where
T: Into<VNode<COMP>>, T: Into<VNode>,
{ {
fn render(&self) -> Html<COMP> { fn render(&self) -> Html {
VList { VList {
no_siblings: true, no_siblings: true,
children: self.iter().map(|c| c.into()).collect(), children: self.iter().map(|c| c.into()).collect(),
@ -305,7 +306,7 @@ where
/// false /// false
/// } /// }
/// ///
/// fn view(&self) -> Html<Self> { /// fn view(&self) -> Html {
/// html! { /// html! {
/// <input ref=self.node_ref.clone() type="text" /> /// <input ref=self.node_ref.clone() type="text" />
/// } /// }
@ -332,13 +333,13 @@ impl NodeRef {
} }
/// Trait for rendering virtual DOM elements /// Trait for rendering virtual DOM elements
pub trait Renderable<COMP: Component> { pub trait Renderable {
/// Called by rendering loop. /// Called by rendering loop.
fn render(&self) -> Html<COMP>; fn render(&self) -> Html;
} }
impl<COMP: Component> Renderable<COMP> for COMP { impl<COMP: Component> Renderable for COMP {
fn render(&self) -> Html<COMP> { fn render(&self) -> Html {
self.view() self.view()
} }
} }
@ -394,7 +395,7 @@ where
let scope = self.scope.clone(); let scope = self.scope.clone();
let closure = move |input| { let closure = move |input| {
let messages = function(input); let messages = function(input);
scope.clone().send_message_batch(messages); scope.send_message_batch(messages);
}; };
closure.into() closure.into()
} }
@ -431,6 +432,7 @@ where
scope.send_message(future.await); scope.send_message(future.await);
Ok(JsValue::NULL) Ok(JsValue::NULL)
}; };
future_to_promise(js_future); future_to_promise(js_future);
} }

View File

@ -55,14 +55,13 @@ impl<COMP: Component> Scope<COMP> {
pub(crate) fn mount_in_place( pub(crate) fn mount_in_place(
self, self,
element: Element, element: Element,
ancestor: Option<VNode<COMP>>, ancestor: Option<VNode>,
node_ref: NodeRef, node_ref: NodeRef,
props: COMP::Properties, props: COMP::Properties,
) -> Scope<COMP> { ) -> Scope<COMP> {
let mut scope = self.clone(); let mut scope = self.clone();
let link = ComponentLink::connect(&scope); let link = ComponentLink::connect(&scope);
let ready_state = ReadyState { let ready_state = ReadyState {
scope: self.clone(),
element, element,
node_ref, node_ref,
link, link,
@ -138,19 +137,17 @@ impl<COMP: Component> fmt::Display for ComponentState<COMP> {
} }
struct ReadyState<COMP: Component> { struct ReadyState<COMP: Component> {
scope: Scope<COMP>,
element: Element, element: Element,
node_ref: NodeRef, node_ref: NodeRef,
props: COMP::Properties, props: COMP::Properties,
link: ComponentLink<COMP>, link: ComponentLink<COMP>,
ancestor: Option<VNode<COMP>>, ancestor: Option<VNode>,
} }
impl<COMP: Component> ReadyState<COMP> { impl<COMP: Component> ReadyState<COMP> {
fn create(self) -> CreatedState<COMP> { fn create(self) -> CreatedState<COMP> {
CreatedState { CreatedState {
component: COMP::create(self.props, self.link), component: COMP::create(self.props, self.link),
scope: self.scope,
element: self.element, element: self.element,
last_frame: self.ancestor, last_frame: self.ancestor,
node_ref: self.node_ref, node_ref: self.node_ref,
@ -159,10 +156,9 @@ impl<COMP: Component> ReadyState<COMP> {
} }
struct CreatedState<COMP: Component> { struct CreatedState<COMP: Component> {
scope: Scope<COMP>,
element: Element, element: Element,
component: COMP, component: COMP,
last_frame: Option<VNode<COMP>>, last_frame: Option<VNode>,
node_ref: NodeRef, node_ref: NodeRef,
} }
@ -177,10 +173,10 @@ impl<COMP: Component> CreatedState<COMP> {
} }
fn update(mut self) -> Self { fn update(mut self) -> Self {
let mut next_frame = self.component.render(); let mut vnode = self.component.render();
let node = next_frame.apply(&self.element, None, self.last_frame, &self.scope); let node = vnode.apply(&self.element, None, self.last_frame);
self.node_ref.set(node); self.node_ref.set(node);
self.last_frame = Some(next_frame); self.last_frame = Some(vnode);
self self
} }
} }

View File

@ -36,7 +36,7 @@
//! true //! true
//! } //! }
//! //!
//! fn view(&self) -> Html<Self> { //! fn view(&self) -> Html {
//! html! { //! html! {
//! <div> //! <div>
//! <button onclick=self.link.callback(|_| Msg::DoIt)>{ "+1" }</button> //! <button onclick=self.link.callback(|_| Msg::DoIt)>{ "+1" }</button>

View File

@ -148,7 +148,7 @@ impl FetchService {
///# type Message = Msg;type Properties = (); ///# type Message = Msg;type Properties = ();
///# fn create(props: Self::Properties,link: ComponentLink<Self>) -> Self {unimplemented!()} ///# fn create(props: Self::Properties,link: ComponentLink<Self>) -> Self {unimplemented!()}
///# fn update(&mut self,msg: Self::Message) -> bool {unimplemented!()} ///# fn update(&mut self,msg: Self::Message) -> bool {unimplemented!()}
///# fn view(&self) -> Html<Comp> {unimplemented!()} ///# fn view(&self) -> Html {unimplemented!()}
///# } ///# }
///# enum Msg { ///# enum Msg {
///# Noop, ///# Noop,
@ -187,7 +187,7 @@ impl FetchService {
///# type Message = Msg;type Properties = (); ///# type Message = Msg;type Properties = ();
///# fn create(props: Self::Properties,link: ComponentLink<Self>) -> Self {unimplemented!()} ///# fn create(props: Self::Properties,link: ComponentLink<Self>) -> Self {unimplemented!()}
///# fn update(&mut self,msg: Self::Message) -> bool {unimplemented!()} ///# fn update(&mut self,msg: Self::Message) -> bool {unimplemented!()}
///# fn view(&self) -> Html<Comp> {unimplemented!()} ///# fn view(&self) -> Html {unimplemented!()}
///# } ///# }
///# enum Msg { ///# enum Msg {
///# FetchResourceComplete(Data), ///# FetchResourceComplete(Data),
@ -240,7 +240,7 @@ impl FetchService {
///# type Properties = (); ///# type Properties = ();
///# fn create(props: Self::Properties, link: ComponentLink<Self>) -> Self {unimplemented!()} ///# fn create(props: Self::Properties, link: ComponentLink<Self>) -> Self {unimplemented!()}
///# fn update(&mut self, msg: Self::Message) -> bool {unimplemented!()} ///# fn update(&mut self, msg: Self::Message) -> bool {unimplemented!()}
///# fn view(&self) -> Html<Comp> {unimplemented!()} ///# fn view(&self) -> Html {unimplemented!()}
///# } ///# }
///# pub enum Msg {} ///# pub enum Msg {}
///# fn dont_execute() { ///# fn dont_execute() {

View File

@ -16,7 +16,6 @@ pub use self::vlist::VList;
pub use self::vnode::VNode; pub use self::vnode::VNode;
pub use self::vtag::VTag; pub use self::vtag::VTag;
pub use self::vtext::VText; pub use self::vtext::VText;
use crate::html::{Component, Scope, ScopeHolder};
/// `Listener` trait is an universal implementation of an event listener /// `Listener` trait is an universal implementation of an event listener
/// which helps to bind Rust-listener to JS-listener (DOM). /// which helps to bind Rust-listener to JS-listener (DOM).
@ -165,9 +164,6 @@ enum Reform {
/// This trait provides features to update a tree by calculating a difference against another tree. /// This trait provides features to update a tree by calculating a difference against another tree.
pub trait VDiff { pub trait VDiff {
/// The component which this instance put into.
type Component: Component;
/// Remove itself from parent and return the next sibling. /// Remove itself from parent and return the next sibling.
fn detach(&mut self, parent: &Element) -> Option<Node>; fn detach(&mut self, parent: &Element) -> Option<Node>;
@ -196,13 +192,12 @@ pub trait VDiff {
&mut self, &mut self,
parent: &Element, parent: &Element,
previous_sibling: Option<&Node>, previous_sibling: Option<&Node>,
ancestor: Option<VNode<Self::Component>>, ancestor: Option<VNode>,
parent_scope: &Scope<Self::Component>,
) -> Option<Node>; ) -> Option<Node>;
} }
/// Transforms properties and attaches a parent scope holder to callbacks for sending messages. /// Transform properties to the expected type.
pub trait Transformer<PARENT: Component, FROM, TO> { pub trait Transformer<FROM, TO> {
/// Transforms one type to another. /// Transforms one type to another.
fn transform(scope_holder: ScopeHolder<PARENT>, from: FROM) -> TO; fn transform(from: FROM) -> TO;
} }

View File

@ -1,8 +1,7 @@
//! This module contains the implementation of a virtual component `VComp`. //! This module contains the implementation of a virtual component `VComp`.
use super::{Transformer, VDiff, VNode}; use super::{Transformer, VDiff, VNode};
use crate::callback::Callback; use crate::html::{Component, ComponentUpdate, HiddenScope, NodeRef, Scope};
use crate::html::{Component, ComponentUpdate, HiddenScope, NodeRef, Scope, ScopeHolder};
use std::any::TypeId; use std::any::TypeId;
use std::cell::RefCell; use std::cell::RefCell;
use std::fmt; use std::fmt;
@ -10,7 +9,7 @@ use std::rc::Rc;
use stdweb::web::{document, Element, INode, Node, TextNode}; use stdweb::web::{document, Element, INode, Node, TextNode};
/// The method generates an instance of a component. /// The method generates an instance of a component.
type Generator<PARENT> = dyn FnOnce(GeneratorType, Scope<PARENT>) -> Mounted; type Generator = dyn FnOnce(GeneratorType) -> Mounted;
/// Components can be generated by mounting or by overwriting an old component. /// Components can be generated by mounting or by overwriting an old component.
enum GeneratorType { enum GeneratorType {
@ -19,56 +18,48 @@ enum GeneratorType {
} }
/// A virtual component. /// A virtual component.
pub struct VComp<PARENT: Component> { pub struct VComp {
type_id: TypeId, type_id: TypeId,
state: Rc<RefCell<MountState<PARENT>>>, state: Rc<RefCell<MountState>>,
} }
/// A virtual child component. /// A virtual child component.
pub struct VChild<SELF: Component, PARENT: Component> { pub struct VChild<COMP: Component> {
/// The component properties /// The component properties
pub props: SELF::Properties, pub props: COMP::Properties,
/// The parent component scope
pub scope: ScopeHolder<PARENT>,
/// Reference to the mounted node /// Reference to the mounted node
node_ref: NodeRef, node_ref: NodeRef,
} }
impl<SELF, PARENT> VChild<SELF, PARENT> impl<COMP> VChild<COMP>
where where
SELF: Component, COMP: Component,
PARENT: Component,
{ {
/// Creates a child component that can be accessed and modified by its parent. /// Creates a child component that can be accessed and modified by its parent.
pub fn new(props: SELF::Properties, scope: ScopeHolder<PARENT>, node_ref: NodeRef) -> Self { pub fn new(props: COMP::Properties, node_ref: NodeRef) -> Self {
Self { Self { props, node_ref }
props,
scope,
node_ref,
}
} }
} }
impl<SELF, PARENT> From<VChild<SELF, PARENT>> for VComp<PARENT> impl<COMP> From<VChild<COMP>> for VComp
where where
SELF: Component, COMP: Component,
PARENT: Component,
{ {
fn from(vchild: VChild<SELF, PARENT>) -> Self { fn from(vchild: VChild<COMP>) -> Self {
VComp::new::<SELF>(vchild.props, vchild.scope, vchild.node_ref) VComp::new::<COMP>(vchild.props, vchild.node_ref)
} }
} }
enum MountState<PARENT: Component> { enum MountState {
Unmounted(Unmounted<PARENT>), Unmounted(Unmounted),
Mounted(Mounted), Mounted(Mounted),
Mounting, Mounting,
Detached, Detached,
Overwritten, Overwritten,
} }
struct Unmounted<PARENT: Component> { struct Unmounted {
generator: Box<Generator<PARENT>>, generator: Box<Generator>,
} }
struct Mounted { struct Mounted {
@ -77,21 +68,16 @@ struct Mounted {
destroyer: Box<dyn FnOnce()>, destroyer: Box<dyn FnOnce()>,
} }
impl<PARENT: Component> VComp<PARENT> { impl VComp {
/// This method prepares a generator to make a new instance of the `Component`. /// This method prepares a generator to make a new instance of the `Component`.
pub fn new<SELF>( pub fn new<COMP>(props: COMP::Properties, node_ref: NodeRef) -> Self
props: SELF::Properties,
scope_holder: ScopeHolder<PARENT>,
node_ref: NodeRef,
) -> Self
where where
SELF: Component, COMP: Component,
{ {
let generator = move |generator_type: GeneratorType, parent: Scope<PARENT>| -> Mounted { let generator = move |generator_type: GeneratorType| -> Mounted {
*scope_holder.borrow_mut() = Some(parent);
match generator_type { match generator_type {
GeneratorType::Mount(element, dummy_node) => { GeneratorType::Mount(element, dummy_node) => {
let scope: Scope<SELF> = Scope::new(); let scope: Scope<COMP> = Scope::new();
let mut scope = scope.mount_in_place( let mut scope = scope.mount_in_place(
element, element,
@ -107,7 +93,7 @@ impl<PARENT: Component> VComp<PARENT> {
} }
} }
GeneratorType::Overwrite(hidden_scope) => { GeneratorType::Overwrite(hidden_scope) => {
let mut scope: Scope<SELF> = hidden_scope.into(); let mut scope: Scope<COMP> = hidden_scope.into();
scope.update(ComponentUpdate::Properties(props)); scope.update(ComponentUpdate::Properties(props));
Mounted { Mounted {
@ -120,7 +106,7 @@ impl<PARENT: Component> VComp<PARENT> {
}; };
VComp { VComp {
type_id: TypeId::of::<SELF>(), type_id: TypeId::of::<COMP>(),
state: Rc::new(RefCell::new(MountState::Unmounted(Unmounted { state: Rc::new(RefCell::new(MountState::Unmounted(Unmounted {
generator: Box::new(generator), generator: Box::new(generator),
}))), }))),
@ -128,71 +114,15 @@ impl<PARENT: Component> VComp<PARENT> {
} }
} }
impl<PARENT, T> Transformer<PARENT, T, T> for VComp<PARENT> impl Unmounted {
where
PARENT: Component,
{
fn transform(_: ScopeHolder<PARENT>, from: T) -> T {
from
}
}
impl<'a, PARENT, T> Transformer<PARENT, &'a T, T> for VComp<PARENT>
where
PARENT: Component,
T: Clone,
{
fn transform(_: ScopeHolder<PARENT>, from: &'a T) -> T {
from.clone()
}
}
impl<'a, PARENT> Transformer<PARENT, &'a str, String> for VComp<PARENT>
where
PARENT: Component,
{
fn transform(_: ScopeHolder<PARENT>, from: &'a str) -> String {
from.to_owned()
}
}
impl<'a, PARENT, F, IN> Transformer<PARENT, F, Callback<IN>> for VComp<PARENT>
where
PARENT: Component,
F: Fn(IN) -> PARENT::Message + 'static,
{
fn transform(scope: ScopeHolder<PARENT>, from: F) -> Callback<IN> {
let callback = move |arg| {
let msg = from(arg);
if let Some(ref mut sender) = *scope.borrow_mut() {
sender.send_message(msg);
} else {
panic!("Parent component hasn't activated this callback yet");
}
};
callback.into()
}
}
impl<'a, PARENT, F, IN> Transformer<PARENT, F, Option<Callback<IN>>> for VComp<PARENT>
where
PARENT: Component,
F: Fn(IN) -> PARENT::Message + 'static,
{
fn transform(scope: ScopeHolder<PARENT>, from: F) -> Option<Callback<IN>> {
Some(VComp::<PARENT>::transform(scope, from))
}
}
impl<PARENT: Component> Unmounted<PARENT> {
/// Mount a virtual component using a generator. /// Mount a virtual component using a generator.
fn mount(self, parent: Element, dummy_node: TextNode, parent_scope: Scope<PARENT>) -> Mounted { fn mount(self, parent: Element, dummy_node: TextNode) -> Mounted {
(self.generator)(GeneratorType::Mount(parent, dummy_node), parent_scope) (self.generator)(GeneratorType::Mount(parent, dummy_node))
} }
/// Overwrite an existing virtual component using a generator. /// Overwrite an existing virtual component using a generator.
fn replace(self, old: Mounted, parent_scope: Scope<PARENT>) -> Mounted { fn replace(self, old: Mounted) -> Mounted {
(self.generator)(GeneratorType::Overwrite(old.scope), parent_scope) (self.generator)(GeneratorType::Overwrite(old.scope))
} }
} }
@ -201,12 +131,8 @@ enum Reform {
Before(Option<Node>), Before(Option<Node>),
} }
impl<COMP> VDiff for VComp<COMP> impl VDiff for VComp {
where /// Remove VComp from parent.
COMP: Component + 'static,
{
type Component = COMP;
fn detach(&mut self, parent: &Element) -> Option<Node> { fn detach(&mut self, parent: &Element) -> Option<Node> {
match self.state.replace(MountState::Detached) { match self.state.replace(MountState::Detached) {
MountState::Mounted(this) => { MountState::Mounted(this) => {
@ -227,8 +153,7 @@ where
&mut self, &mut self,
parent: &Element, parent: &Element,
previous_sibling: Option<&Node>, previous_sibling: Option<&Node>,
ancestor: Option<VNode<Self::Component>>, ancestor: Option<VNode>,
parent_scope: &Scope<Self::Component>,
) -> Option<Node> { ) -> Option<Node> {
match self.state.replace(MountState::Mounting) { match self.state.replace(MountState::Mounting) {
MountState::Unmounted(this) => { MountState::Unmounted(this) => {
@ -256,7 +181,7 @@ where
let mounted = match reform { let mounted = match reform {
Reform::Keep(mounted) => { Reform::Keep(mounted) => {
// Send properties update when the component is already rendered. // Send properties update when the component is already rendered.
this.replace(mounted, parent_scope.clone()) this.replace(mounted)
} }
Reform::Before(before) => { Reform::Before(before) => {
// Temporary node which will be replaced by a component's root node. // Temporary node which will be replaced by a component's root node.
@ -276,7 +201,7 @@ where
parent.append_child(&dummy_node); parent.append_child(&dummy_node);
} }
} }
this.mount(parent.to_owned(), dummy_node, parent_scope.clone()) this.mount(parent.to_owned(), dummy_node)
} }
}; };
@ -292,20 +217,41 @@ where
} }
} }
impl<C: Component> PartialEq for VComp<C> { impl<T> Transformer<T, T> for VComp {
fn eq(&self, other: &VComp<C>) -> bool { fn transform(from: T) -> T {
from
}
}
impl<'a, T> Transformer<&'a T, T> for VComp
where
T: Clone,
{
fn transform(from: &'a T) -> T {
from.clone()
}
}
impl<'a> Transformer<&'a str, String> for VComp {
fn transform(from: &'a str) -> String {
from.to_owned()
}
}
impl PartialEq for VComp {
fn eq(&self, other: &VComp) -> bool {
self.type_id == other.type_id self.type_id == other.type_id
} }
} }
impl<C: Component> fmt::Debug for VComp<C> { impl fmt::Debug for VComp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("VComp<_>") f.write_str("VComp")
} }
} }
impl<SELF: Component, PARENT: Component> fmt::Debug for VChild<SELF, PARENT> { impl<COMP: Component> fmt::Debug for VChild<COMP> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("VChild<_,_>") f.write_str("VChild<_>")
} }
} }

View File

@ -1,45 +1,32 @@
//! This module contains fragments implementation. //! This module contains fragments implementation.
use super::{VDiff, VNode, VText}; use super::{VDiff, VNode, VText};
use crate::html::{Component, Scope};
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
use stdweb::web::{Element, Node}; use stdweb::web::{Element, Node};
/// This struct represents a fragment of the Virtual DOM tree. /// This struct represents a fragment of the Virtual DOM tree.
#[derive(Debug)] #[derive(Debug, PartialEq, Default)]
pub struct VList<COMP: Component> { pub struct VList {
/// Whether the fragment has siblings or not. /// Whether the fragment has siblings or not.
pub no_siblings: bool, pub no_siblings: bool,
/// The list of children nodes. Which also could have their own children. /// The list of children nodes. Which also could have their own children.
pub children: Vec<VNode<COMP>>, pub children: Vec<VNode>,
} }
impl<COMP: Component> Deref for VList<COMP> { impl Deref for VList {
type Target = Vec<VNode<COMP>>; type Target = Vec<VNode>;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
&self.children &self.children
} }
} }
impl<COMP: Component> DerefMut for VList<COMP> { impl DerefMut for VList {
fn deref_mut(&mut self) -> &mut Self::Target { fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.children &mut self.children
} }
} }
impl<COMP: Component> PartialEq for VList<COMP> { impl VList {
fn eq(&self, other: &Self) -> bool {
self.children == other.children
}
}
impl<COMP: Component> Default for VList<COMP> {
fn default() -> Self {
VList::new(false)
}
}
impl<COMP: Component> VList<COMP> {
/// Creates a new empty `VList` instance. /// Creates a new empty `VList` instance.
pub fn new(no_siblings: bool) -> Self { pub fn new(no_siblings: bool) -> Self {
VList { VList {
@ -49,14 +36,12 @@ impl<COMP: Component> VList<COMP> {
} }
/// Add `VNode` child. /// Add `VNode` child.
pub fn add_child(&mut self, child: VNode<COMP>) { pub fn add_child(&mut self, child: VNode) {
self.children.push(child); self.children.push(child);
} }
} }
impl<COMP: Component> VDiff for VList<COMP> { impl VDiff for VList {
type Component = COMP;
fn detach(&mut self, parent: &Element) -> Option<Node> { fn detach(&mut self, parent: &Element) -> Option<Node> {
let mut last_sibling = None; let mut last_sibling = None;
for mut child in self.children.drain(..) { for mut child in self.children.drain(..) {
@ -69,8 +54,7 @@ impl<COMP: Component> VDiff for VList<COMP> {
&mut self, &mut self,
parent: &Element, parent: &Element,
previous_sibling: Option<&Node>, previous_sibling: Option<&Node>,
ancestor: Option<VNode<Self::Component>>, ancestor: Option<VNode>,
parent_scope: &Scope<Self::Component>,
) -> Option<Node> { ) -> Option<Node> {
// Reuse previous_sibling, because fragment reuse parent // Reuse previous_sibling, because fragment reuse parent
let mut previous_sibling = previous_sibling.cloned(); let mut previous_sibling = previous_sibling.cloned();
@ -105,16 +89,10 @@ impl<COMP: Component> VDiff for VList<COMP> {
loop { loop {
match (lefts.next(), rights.next()) { match (lefts.next(), rights.next()) {
(Some(left), Some(right)) => { (Some(left), Some(right)) => {
previous_sibling = left.apply( previous_sibling = left.apply(parent, previous_sibling.as_ref(), Some(right));
parent,
previous_sibling.as_ref(),
Some(right),
&parent_scope,
);
} }
(Some(left), None) => { (Some(left), None) => {
previous_sibling = previous_sibling = left.apply(parent, previous_sibling.as_ref(), None);
left.apply(parent, previous_sibling.as_ref(), None, &parent_scope);
} }
(None, Some(ref mut right)) => { (None, Some(ref mut right)) => {
right.detach(parent); right.detach(parent);

View File

@ -1,29 +1,27 @@
//! This module contains the implementation of abstract virtual node. //! This module contains the implementation of abstract virtual node.
use super::{VChild, VComp, VDiff, VList, VTag, VText}; use super::{VChild, VComp, VDiff, VList, VTag, VText};
use crate::html::{Component, Renderable, Scope}; use crate::html::{Component, Renderable};
use std::cmp::PartialEq; use std::cmp::PartialEq;
use std::fmt; use std::fmt;
use std::iter::FromIterator; use std::iter::FromIterator;
use stdweb::web::{Element, INode, Node}; use stdweb::web::{Element, INode, Node};
/// Bind virtual element to a DOM reference. /// Bind virtual element to a DOM reference.
pub enum VNode<COMP: Component> { pub enum VNode {
/// A bind between `VTag` and `Element`. /// A bind between `VTag` and `Element`.
VTag(Box<VTag<COMP>>), VTag(Box<VTag>),
/// A bind between `VText` and `TextNode`. /// A bind between `VText` and `TextNode`.
VText(VText<COMP>), VText(VText),
/// A bind between `VComp` and `Element`. /// A bind between `VComp` and `Element`.
VComp(VComp<COMP>), VComp(VComp),
/// A holder for a list of other nodes. /// A holder for a list of other nodes.
VList(VList<COMP>), VList(VList),
/// A holder for any `Node` (necessary for replacing node). /// A holder for any `Node` (necessary for replacing node).
VRef(Node), VRef(Node),
} }
impl<COMP: Component> VDiff for VNode<COMP> { impl VDiff for VNode {
type Component = COMP;
/// Remove VNode from parent. /// Remove VNode from parent.
fn detach(&mut self, parent: &Element) -> Option<Node> { fn detach(&mut self, parent: &Element) -> Option<Node> {
match *self { match *self {
@ -45,22 +43,13 @@ impl<COMP: Component> VDiff for VNode<COMP> {
&mut self, &mut self,
parent: &Element, parent: &Element,
previous_sibling: Option<&Node>, previous_sibling: Option<&Node>,
ancestor: Option<VNode<Self::Component>>, ancestor: Option<VNode>,
parent_scope: &Scope<Self::Component>,
) -> Option<Node> { ) -> Option<Node> {
match *self { match *self {
VNode::VTag(ref mut vtag) => { VNode::VTag(ref mut vtag) => vtag.apply(parent, previous_sibling, ancestor),
vtag.apply(parent, previous_sibling, ancestor, parent_scope) VNode::VText(ref mut vtext) => vtext.apply(parent, previous_sibling, ancestor),
} VNode::VComp(ref mut vcomp) => vcomp.apply(parent, previous_sibling, ancestor),
VNode::VText(ref mut vtext) => { VNode::VList(ref mut vlist) => vlist.apply(parent, previous_sibling, ancestor),
vtext.apply(parent, previous_sibling, ancestor, parent_scope)
}
VNode::VComp(ref mut vcomp) => {
vcomp.apply(parent, previous_sibling, ancestor, parent_scope)
}
VNode::VList(ref mut vlist) => {
vlist.apply(parent, previous_sibling, ancestor, parent_scope)
}
VNode::VRef(ref mut node) => { VNode::VRef(ref mut node) => {
let sibling = match ancestor { let sibling = match ancestor {
Some(mut n) => n.detach(parent), Some(mut n) => n.detach(parent),
@ -80,59 +69,58 @@ impl<COMP: Component> VDiff for VNode<COMP> {
} }
} }
impl<COMP: Component> Default for VNode<COMP> { impl Default for VNode {
fn default() -> Self { fn default() -> Self {
VNode::VList(VList::default()) VNode::VList(VList::default())
} }
} }
impl<COMP: Component> From<VText<COMP>> for VNode<COMP> { impl From<VText> for VNode {
fn from(vtext: VText<COMP>) -> Self { fn from(vtext: VText) -> Self {
VNode::VText(vtext) VNode::VText(vtext)
} }
} }
impl<COMP: Component> From<VList<COMP>> for VNode<COMP> { impl From<VList> for VNode {
fn from(vlist: VList<COMP>) -> Self { fn from(vlist: VList) -> Self {
VNode::VList(vlist) VNode::VList(vlist)
} }
} }
impl<COMP: Component> From<VTag<COMP>> for VNode<COMP> { impl From<VTag> for VNode {
fn from(vtag: VTag<COMP>) -> Self { fn from(vtag: VTag) -> Self {
VNode::VTag(Box::new(vtag)) VNode::VTag(Box::new(vtag))
} }
} }
impl<COMP: Component> From<VComp<COMP>> for VNode<COMP> { impl From<VComp> for VNode {
fn from(vcomp: VComp<COMP>) -> Self { fn from(vcomp: VComp) -> Self {
VNode::VComp(vcomp) VNode::VComp(vcomp)
} }
} }
impl<COMP, CHILD> From<VChild<CHILD, COMP>> for VNode<COMP> impl<COMP> From<VChild<COMP>> for VNode
where where
COMP: Component, COMP: Component,
CHILD: Component,
{ {
fn from(vchild: VChild<CHILD, COMP>) -> Self { fn from(vchild: VChild<COMP>) -> Self {
VNode::VComp(VComp::from(vchild)) VNode::VComp(VComp::from(vchild))
} }
} }
impl<COMP: Component, T: ToString> From<T> for VNode<COMP> { impl<T: ToString> From<T> for VNode {
fn from(value: T) -> Self { fn from(value: T) -> Self {
VNode::VText(VText::new(value.to_string())) VNode::VText(VText::new(value.to_string()))
} }
} }
impl<'a, COMP: Component> From<&'a dyn Renderable<COMP>> for VNode<COMP> { impl<'a> From<&'a dyn Renderable> for VNode {
fn from(value: &'a dyn Renderable<COMP>) -> Self { fn from(value: &'a dyn Renderable) -> Self {
value.render() value.render()
} }
} }
impl<COMP: Component, A: Into<VNode<COMP>>> FromIterator<A> for VNode<COMP> { impl<A: Into<VNode>> FromIterator<A> for VNode {
fn from_iter<T: IntoIterator<Item = A>>(iter: T) -> Self { fn from_iter<T: IntoIterator<Item = A>>(iter: T) -> Self {
let vlist = iter.into_iter().fold(VList::default(), |mut acc, x| { let vlist = iter.into_iter().fold(VList::default(), |mut acc, x| {
acc.add_child(x.into()); acc.add_child(x.into());
@ -142,7 +130,7 @@ impl<COMP: Component, A: Into<VNode<COMP>>> FromIterator<A> for VNode<COMP> {
} }
} }
impl<COMP: Component> fmt::Debug for VNode<COMP> { impl fmt::Debug for VNode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self { match *self {
VNode::VTag(ref vtag) => vtag.fmt(f), VNode::VTag(ref vtag) => vtag.fmt(f),
@ -154,8 +142,8 @@ impl<COMP: Component> fmt::Debug for VNode<COMP> {
} }
} }
impl<COMP: Component> PartialEq for VNode<COMP> { impl PartialEq for VNode {
fn eq(&self, other: &VNode<COMP>) -> bool { fn eq(&self, other: &VNode) -> bool {
match (self, other) { match (self, other) {
(VNode::VTag(vtag_a), VNode::VTag(vtag_b)) => vtag_a == vtag_b, (VNode::VTag(vtag_a), VNode::VTag(vtag_b)) => vtag_a == vtag_b,
(VNode::VText(vtext_a), VNode::VText(vtext_b)) => vtext_a == vtext_b, (VNode::VText(vtext_a), VNode::VText(vtext_b)) => vtext_a == vtext_b,

View File

@ -3,8 +3,7 @@
use super::{ use super::{
Attributes, Classes, Listener, Listeners, Patch, Reform, Transformer, VDiff, VList, VNode, Attributes, Classes, Listener, Listeners, Patch, Reform, Transformer, VDiff, VList, VNode,
}; };
use crate::callback::Callback; use crate::html::NodeRef;
use crate::html::{Component, NodeRef, Scope, ScopeHolder};
use log::warn; use log::warn;
use std::borrow::Cow; use std::borrow::Cow;
use std::cmp::PartialEq; use std::cmp::PartialEq;
@ -25,7 +24,7 @@ pub const HTML_NAMESPACE: &str = "http://www.w3.org/1999/xhtml";
/// A type for a virtual /// A type for a virtual
/// [Element](https://developer.mozilla.org/en-US/docs/Web/API/Element) /// [Element](https://developer.mozilla.org/en-US/docs/Web/API/Element)
/// representation. /// representation.
pub struct VTag<PARENT: Component> { pub struct VTag {
/// A tag of the element. /// A tag of the element.
tag: Cow<'static, str>, tag: Cow<'static, str>,
/// A reference to the `Element`. /// A reference to the `Element`.
@ -35,7 +34,7 @@ pub struct VTag<PARENT: Component> {
/// List of attributes. /// List of attributes.
pub attributes: Attributes, pub attributes: Attributes,
/// List of children nodes /// List of children nodes
pub children: VList<PARENT>, pub children: VList,
/// List of attached classes. /// List of attached classes.
pub classes: Classes, pub classes: Classes,
/// Contains a value of an /// Contains a value of an
@ -55,22 +54,11 @@ pub struct VTag<PARENT: Component> {
pub node_ref: NodeRef, pub node_ref: NodeRef,
/// Keeps handler for attached listeners to have an opportunity to drop them later. /// Keeps handler for attached listeners to have an opportunity to drop them later.
captured: Vec<EventListenerHandle>, captured: Vec<EventListenerHandle>,
/// Holds a reference to the parent component scope for callback activation.
scope_holder: ScopeHolder<PARENT>,
} }
impl<PARENT: Component> VTag<PARENT> { impl VTag {
/// Creates a new `VTag` instance with `tag` name (cannot be changed later in DOM). /// Creates a new `VTag` instance with `tag` name (cannot be changed later in DOM).
pub fn new<S: Into<Cow<'static, str>>>(tag: S) -> Self { pub fn new<S: Into<Cow<'static, str>>>(tag: S) -> Self {
Self::new_with_scope(tag, ScopeHolder::default())
}
/// Creates a new `VTag` instance with `tag` name (cannot be changed later in DOM) and parent
/// scope holder for callback activation.
pub fn new_with_scope<S: Into<Cow<'static, str>>>(
tag: S,
scope_holder: ScopeHolder<PARENT>,
) -> Self {
VTag { VTag {
tag: tag.into(), tag: tag.into(),
reference: None, reference: None,
@ -85,7 +73,6 @@ impl<PARENT: Component> VTag<PARENT> {
// In HTML node `checked` attribute sets `defaultChecked` parameter, // In HTML node `checked` attribute sets `defaultChecked` parameter,
// but we use own field to control real `checked` parameter // but we use own field to control real `checked` parameter
checked: false, checked: false,
scope_holder,
} }
} }
@ -95,12 +82,12 @@ impl<PARENT: Component> VTag<PARENT> {
} }
/// Add `VNode` child. /// Add `VNode` child.
pub fn add_child(&mut self, child: VNode<PARENT>) { pub fn add_child(&mut self, child: VNode) {
self.children.add_child(child); self.children.add_child(child);
} }
/// Add multiple `VNode` children. /// Add multiple `VNode` children.
pub fn add_children(&mut self, children: Vec<VNode<PARENT>>) { pub fn add_children(&mut self, children: Vec<VNode>) {
for child in children { for child in children {
self.add_child(child); self.add_child(child);
} }
@ -360,9 +347,7 @@ impl<PARENT: Component> VTag<PARENT> {
} }
} }
impl<PARENT: Component> VDiff for VTag<PARENT> { impl VDiff for VTag {
type Component = PARENT;
/// Remove VTag from parent. /// Remove VTag from parent.
fn detach(&mut self, parent: &Element) -> Option<Node> { fn detach(&mut self, parent: &Element) -> Option<Node> {
let node = self let node = self
@ -386,8 +371,7 @@ impl<PARENT: Component> VDiff for VTag<PARENT> {
&mut self, &mut self,
parent: &Element, parent: &Element,
previous_sibling: Option<&Node>, previous_sibling: Option<&Node>,
ancestor: Option<VNode<Self::Component>>, ancestor: Option<VNode>,
parent_scope: &Scope<Self::Component>,
) -> Option<Node> { ) -> Option<Node> {
assert!( assert!(
self.reference.is_none(), self.reference.is_none(),
@ -473,16 +457,9 @@ impl<PARENT: Component> VDiff for VTag<PARENT> {
self.captured.push(handle); self.captured.push(handle);
} }
// Activate scope
*self.scope_holder.borrow_mut() = Some(parent_scope.clone());
// Process children // Process children
self.children.apply( self.children
&element, .apply(&element, None, ancestor.map(|a| a.children.into()));
None,
ancestor.map(|a| a.children.into()),
parent_scope,
);
let node = self.reference.as_ref().map(|e| e.as_node().to_owned()); let node = self.reference.as_ref().map(|e| e.as_node().to_owned());
self.node_ref.set(node.clone()); self.node_ref.set(node.clone());
@ -490,7 +467,7 @@ impl<PARENT: Component> VDiff for VTag<PARENT> {
} }
} }
impl<PARENT: Component> fmt::Debug for VTag<PARENT> { impl fmt::Debug for VTag {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "VTag {{ tag: {} }}", self.tag) write!(f, "VTag {{ tag: {} }}", self.tag)
} }
@ -512,8 +489,8 @@ fn set_checked(input: &InputElement, value: bool) {
js!( @(no_return) @{input}.checked = @{value}; ); js!( @(no_return) @{input}.checked = @{value}; );
} }
impl<PARENT: Component> PartialEq for VTag<PARENT> { impl PartialEq for VTag {
fn eq(&self, other: &VTag<PARENT>) -> bool { fn eq(&self, other: &VTag) -> bool {
self.tag == other.tag self.tag == other.tag
&& self.value == other.value && self.value == other.value
&& self.kind == other.kind && self.kind == other.kind
@ -539,39 +516,17 @@ pub(crate) fn not<T>(option: &Option<T>) -> &Option<()> {
} }
} }
impl<PARENT, T> Transformer<PARENT, T, T> for VTag<PARENT> impl<T> Transformer<T, T> for VTag {
where fn transform(from: T) -> T {
PARENT: Component,
{
fn transform(_: ScopeHolder<PARENT>, from: T) -> T {
from from
} }
} }
impl<'a, PARENT, T> Transformer<PARENT, &'a T, T> for VTag<PARENT> impl<'a, T> Transformer<&'a T, T> for VTag
where where
PARENT: Component,
T: Clone, T: Clone,
{ {
fn transform(_: ScopeHolder<PARENT>, from: &'a T) -> T { fn transform(from: &'a T) -> T {
from.clone() from.clone()
} }
} }
impl<'a, PARENT, F, IN> Transformer<PARENT, F, Callback<IN>> for VTag<PARENT>
where
PARENT: Component,
F: Fn(IN) -> PARENT::Message + 'static,
{
fn transform(scope: ScopeHolder<PARENT>, from: F) -> Callback<IN> {
let callback = move |arg| {
let msg = from(arg);
if let Some(ref mut sender) = *scope.borrow_mut() {
sender.send_message(msg);
} else {
panic!("Parent component hasn't activated this callback yet");
}
};
callback.into()
}
}

View File

@ -1,38 +1,32 @@
//! This module contains the implementation of a virtual text node `VText`. //! This module contains the implementation of a virtual text node `VText`.
use super::{Reform, VDiff, VNode}; use super::{Reform, VDiff, VNode};
use crate::html::{Component, Scope};
use log::warn; use log::warn;
use std::cmp::PartialEq; use std::cmp::PartialEq;
use std::fmt; use std::fmt;
use std::marker::PhantomData;
use stdweb::web::{document, Element, INode, Node, TextNode}; use stdweb::web::{document, Element, INode, Node, TextNode};
/// A type for a virtual /// A type for a virtual
/// [`TextNode`](https://developer.mozilla.org/en-US/docs/Web/API/Document/createTextNode) /// [`TextNode`](https://developer.mozilla.org/en-US/docs/Web/API/Document/createTextNode)
/// representation. /// representation.
pub struct VText<COMP: Component> { pub struct VText {
/// Contains a text of the node. /// Contains a text of the node.
pub text: String, pub text: String,
/// A reference to the `TextNode`. /// A reference to the `TextNode`.
pub reference: Option<TextNode>, pub reference: Option<TextNode>,
_comp: PhantomData<COMP>,
} }
impl<COMP: Component> VText<COMP> { impl VText {
/// Creates new virtual text node with a content. /// Creates new virtual text node with a content.
pub fn new(text: String) -> Self { pub fn new(text: String) -> Self {
VText { VText {
text, text,
reference: None, reference: None,
_comp: PhantomData,
} }
} }
} }
impl<COMP: Component> VDiff for VText<COMP> { impl VDiff for VText {
type Component = COMP;
/// Remove VText from parent. /// Remove VText from parent.
fn detach(&mut self, parent: &Element) -> Option<Node> { fn detach(&mut self, parent: &Element) -> Option<Node> {
let node = self let node = self
@ -51,8 +45,7 @@ impl<COMP: Component> VDiff for VText<COMP> {
&mut self, &mut self,
parent: &Element, parent: &Element,
previous_sibling: Option<&Node>, previous_sibling: Option<&Node>,
ancestor: Option<VNode<Self::Component>>, ancestor: Option<VNode>,
_: &Scope<Self::Component>,
) -> Option<Node> { ) -> Option<Node> {
assert!( assert!(
self.reference.is_none(), self.reference.is_none(),
@ -101,14 +94,14 @@ impl<COMP: Component> VDiff for VText<COMP> {
} }
} }
impl<COMP: Component> fmt::Debug for VText<COMP> { impl fmt::Debug for VText {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "VText {{ text: {} }}", self.text) write!(f, "VText {{ text: {} }}", self.text)
} }
} }
impl<COMP: Component> PartialEq for VText<COMP> { impl PartialEq for VText {
fn eq(&self, other: &VText<COMP>) -> bool { fn eq(&self, other: &VText) -> bool {
self.text == other.text self.text == other.text
} }
} }

View File

@ -29,7 +29,3 @@ error[E0599]: no method named `b` found for type `t4::PropsBuilder<t4::PropsBuil
... ...
48 | Props::builder().b(1).a(2).build(); 48 | Props::builder().b(1).a(2).build();
| ^ help: there is a method with a similar name: `a` | ^ help: there is a method with a similar name: `a`
Some errors have detailed explanations: E0277, E0599.
For more information about an error, try `rustc --explain E0277`.
error: could not compile `yew-tests`.

View File

@ -1,5 +1,5 @@
#[allow(dead_code)] #[allow(dead_code)]
#[rustversion::attr(beta, cfg_attr(not(feature = "web_test"), test))] #[rustversion::attr(stable(1.39.0), cfg_attr(not(feature = "web_test"), test))]
fn tests() { fn tests() {
let t = trybuild::TestCases::new(); let t = trybuild::TestCases::new();
t.pass("tests/derive_props/pass.rs"); t.pass("tests/derive_props/pass.rs");

View File

@ -2,7 +2,7 @@
macro_rules! pass_helper { macro_rules! pass_helper {
( @html ) => { html! {} }; ( @html ) => { html! {} };
( @html html! { $($view:tt)* }; $($tail:tt)* ) => { ( @html html! { $($view:tt)* }; $($tail:tt)* ) => {
let _: Html<TestComponent> = html! { $($view)* }; html! { $($view)* };
pass_helper! { @ html $($tail)* } pass_helper! { @ html $($tail)* }
}; };
( @html $head:stmt; $($tail:tt)* ) => { ( @html $head:stmt; $($tail:tt)* ) => {
@ -11,11 +11,12 @@ macro_rules! pass_helper {
}; };
( $($content:tt)* ) => { ( $($content:tt)* ) => {
mod test_component; mod test_component;
use test_component::TestComponent;
use yew::prelude::*; use yew::prelude::*;
#[allow(unused)]
use test_component::TestComponent;
struct SubComponent; struct SubComponent;
impl Renderable<TestComponent> for SubComponent { impl Renderable for SubComponent {
fn render(&self) -> Html<TestComponent> { fn render(&self) -> Html {
pass_helper! { @ html $($content)* } pass_helper! { @ html $($content)* }
} }
} }

View File

@ -7,7 +7,7 @@ error[E0277]: `()` doesn't implement `std::fmt::Display`
= help: the trait `std::fmt::Display` is not implemented for `()` = help: the trait `std::fmt::Display` is not implemented for `()`
= note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
= note: required because of the requirements on the impl of `std::string::ToString` for `()` = note: required because of the requirements on the impl of `std::string::ToString` for `()`
= note: required because of the requirements on the impl of `std::convert::From<()>` for `yew::virtual_dom::vnode::VNode<_>` = note: required because of the requirements on the impl of `std::convert::From<()>` for `yew::virtual_dom::vnode::VNode`
= note: required by `std::convert::From::from` = note: required by `std::convert::From::from`
error[E0277]: `()` doesn't implement `std::fmt::Display` error[E0277]: `()` doesn't implement `std::fmt::Display`
@ -19,7 +19,7 @@ error[E0277]: `()` doesn't implement `std::fmt::Display`
= help: the trait `std::fmt::Display` is not implemented for `()` = help: the trait `std::fmt::Display` is not implemented for `()`
= note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
= note: required because of the requirements on the impl of `std::string::ToString` for `()` = note: required because of the requirements on the impl of `std::string::ToString` for `()`
= note: required because of the requirements on the impl of `std::convert::From<()>` for `yew::virtual_dom::vnode::VNode<_>` = note: required because of the requirements on the impl of `std::convert::From<()>` for `yew::virtual_dom::vnode::VNode`
= note: required by `std::convert::From::from` = note: required by `std::convert::From::from`
error[E0277]: `()` doesn't implement `std::fmt::Display` error[E0277]: `()` doesn't implement `std::fmt::Display`
@ -31,8 +31,5 @@ error[E0277]: `()` doesn't implement `std::fmt::Display`
= help: the trait `std::fmt::Display` is not implemented for `()` = help: the trait `std::fmt::Display` is not implemented for `()`
= note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
= note: required because of the requirements on the impl of `std::string::ToString` for `()` = note: required because of the requirements on the impl of `std::string::ToString` for `()`
= note: required because of the requirements on the impl of `std::convert::From<()>` for `yew::virtual_dom::vnode::VNode<_>` = note: required because of the requirements on the impl of `std::convert::From<()>` for `yew::virtual_dom::vnode::VNode`
= note: required because of the requirements on the impl of `std::convert::Into<yew::virtual_dom::vnode::VNode<_>>` for `()` = note: required because of the requirements on the impl of `std::convert::Into<yew::virtual_dom::vnode::VNode>` for `()`
For more information about this error, try `rustc --explain E0277`.
error: could not compile `yew-tests`.

View File

@ -3,6 +3,3 @@ error[E0277]: the trait bound `std::string::String: yew::html::Component` is not
| |
6 | html! { <String /> }; 6 | html! { <String /> };
| ^^^^^^ the trait `yew::html::Component` is not implemented for `std::string::String` | ^^^^^^ the trait `yew::html::Component` is not implemented for `std::string::String`
For more information about this error, try `rustc --explain E0277`.
error: could not compile `yew-tests`.

View File

@ -22,14 +22,14 @@ impl Component for Child {
unimplemented!() unimplemented!()
} }
fn view(&self) -> Html<Self> { fn view(&self) -> Html {
unimplemented!() unimplemented!()
} }
} }
#[derive(Properties)] #[derive(Properties)]
pub struct ChildContainerProperties { pub struct ChildContainerProperties {
pub children: ChildrenWithProps<Child, ChildContainer>, pub children: ChildrenWithProps<Child>,
} }
pub struct ChildContainer; pub struct ChildContainer;
@ -45,7 +45,7 @@ impl Component for ChildContainer {
unimplemented!() unimplemented!()
} }
fn view(&self) -> Html<Self> { fn view(&self) -> Html {
unimplemented!() unimplemented!()
} }
} }

View File

@ -187,47 +187,47 @@ error[E0599]: no method named `children` found for type `ChildPropertiesBuilder<
| |
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
error[E0277]: the trait bound `yew::virtual_dom::vcomp::VChild<Child, ChildContainer>: std::convert::From<yew::virtual_dom::vnode::VNode<_>>` is not satisfied error[E0277]: the trait bound `yew::virtual_dom::vcomp::VChild<Child>: std::convert::From<yew::virtual_dom::vnode::VNode>` is not satisfied
--> $DIR/html-component-fail.rs:78:5 --> $DIR/html-component-fail.rs:78:5
| |
78 | html! { <ChildContainer>{ "Not allowed" }</ChildContainer> }; 78 | html! { <ChildContainer>{ "Not allowed" }</ChildContainer> };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::convert::From<yew::virtual_dom::vnode::VNode<_>>` is not implemented for `yew::virtual_dom::vcomp::VChild<Child, ChildContainer>` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::convert::From<yew::virtual_dom::vnode::VNode>` is not implemented for `yew::virtual_dom::vcomp::VChild<Child>`
| |
= note: required because of the requirements on the impl of `std::convert::Into<yew::virtual_dom::vcomp::VChild<Child, ChildContainer>>` for `yew::virtual_dom::vnode::VNode<_>` = note: required because of the requirements on the impl of `std::convert::Into<yew::virtual_dom::vcomp::VChild<Child>>` for `yew::virtual_dom::vnode::VNode`
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
error[E0277]: the trait bound `yew::virtual_dom::vcomp::VChild<Child, ChildContainer>: std::convert::From<yew::virtual_dom::vnode::VNode<_>>` is not satisfied error[E0277]: the trait bound `yew::virtual_dom::vcomp::VChild<Child>: std::convert::From<yew::virtual_dom::vnode::VNode>` is not satisfied
--> $DIR/html-component-fail.rs:79:5 --> $DIR/html-component-fail.rs:79:5
| |
79 | html! { <ChildContainer><></></ChildContainer> }; 79 | html! { <ChildContainer><></></ChildContainer> };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::convert::From<yew::virtual_dom::vnode::VNode<_>>` is not implemented for `yew::virtual_dom::vcomp::VChild<Child, ChildContainer>` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::convert::From<yew::virtual_dom::vnode::VNode>` is not implemented for `yew::virtual_dom::vcomp::VChild<Child>`
| |
= note: required because of the requirements on the impl of `std::convert::Into<yew::virtual_dom::vcomp::VChild<Child, ChildContainer>>` for `yew::virtual_dom::vnode::VNode<_>` = note: required because of the requirements on the impl of `std::convert::Into<yew::virtual_dom::vcomp::VChild<Child>>` for `yew::virtual_dom::vnode::VNode`
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
error[E0277]: the trait bound `yew::virtual_dom::vcomp::VChild<Child, ChildContainer>: std::convert::From<yew::virtual_dom::vcomp::VChild<ChildContainer, _>>` is not satisfied error[E0277]: the trait bound `yew::virtual_dom::vcomp::VChild<Child>: std::convert::From<yew::virtual_dom::vcomp::VChild<ChildContainer>>` is not satisfied
--> $DIR/html-component-fail.rs:80:5 --> $DIR/html-component-fail.rs:80:5
| |
80 | html! { <ChildContainer><ChildContainer /></ChildContainer> }; 80 | html! { <ChildContainer><ChildContainer /></ChildContainer> };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::convert::From<yew::virtual_dom::vcomp::VChild<ChildContainer, _>>` is not implemented for `yew::virtual_dom::vcomp::VChild<Child, ChildContainer>` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::convert::From<yew::virtual_dom::vcomp::VChild<ChildContainer>>` is not implemented for `yew::virtual_dom::vcomp::VChild<Child>`
| |
= note: required because of the requirements on the impl of `std::convert::Into<yew::virtual_dom::vcomp::VChild<Child, ChildContainer>>` for `yew::virtual_dom::vcomp::VChild<ChildContainer, _>` = note: required because of the requirements on the impl of `std::convert::Into<yew::virtual_dom::vcomp::VChild<Child>>` for `yew::virtual_dom::vcomp::VChild<ChildContainer>`
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
error[E0277]: the trait bound `yew::virtual_dom::vcomp::VChild<Child, ChildContainer>: std::convert::From<yew::virtual_dom::vcomp::VChild<ChildContainer, _>>` is not satisfied error[E0277]: the trait bound `yew::virtual_dom::vcomp::VChild<Child>: std::convert::From<yew::virtual_dom::vcomp::VChild<ChildContainer>>` is not satisfied
--> $DIR/html-component-fail.rs:81:5 --> $DIR/html-component-fail.rs:81:5
| |
81 | html! { <ChildContainer><ChildContainer /></ChildContainer> }; 81 | html! { <ChildContainer><ChildContainer /></ChildContainer> };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::convert::From<yew::virtual_dom::vcomp::VChild<ChildContainer, _>>` is not implemented for `yew::virtual_dom::vcomp::VChild<Child, ChildContainer>` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::convert::From<yew::virtual_dom::vcomp::VChild<ChildContainer>>` is not implemented for `yew::virtual_dom::vcomp::VChild<Child>`
| |
= note: required because of the requirements on the impl of `std::convert::Into<yew::virtual_dom::vcomp::VChild<Child, ChildContainer>>` for `yew::virtual_dom::vcomp::VChild<ChildContainer, _>` = note: required because of the requirements on the impl of `std::convert::Into<yew::virtual_dom::vcomp::VChild<Child>>` for `yew::virtual_dom::vcomp::VChild<ChildContainer>`
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
error[E0277]: the trait bound `yew::virtual_dom::vcomp::VChild<Child, ChildContainer>: std::convert::From<yew::virtual_dom::vnode::VNode<_>>` is not satisfied error[E0277]: the trait bound `yew::virtual_dom::vcomp::VChild<Child>: std::convert::From<yew::virtual_dom::vnode::VNode>` is not satisfied
--> $DIR/html-component-fail.rs:82:5 --> $DIR/html-component-fail.rs:82:5
| |
82 | html! { <ChildContainer><Child int=1 /><other /></ChildContainer> }; 82 | html! { <ChildContainer><Child int=1 /><other /></ChildContainer> };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::convert::From<yew::virtual_dom::vnode::VNode<_>>` is not implemented for `yew::virtual_dom::vcomp::VChild<Child, ChildContainer>` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::convert::From<yew::virtual_dom::vnode::VNode>` is not implemented for `yew::virtual_dom::vcomp::VChild<Child>`
| |
= note: required because of the requirements on the impl of `std::convert::Into<yew::virtual_dom::vcomp::VChild<Child, ChildContainer>>` for `yew::virtual_dom::vnode::VNode<_>` = note: required because of the requirements on the impl of `std::convert::Into<yew::virtual_dom::vcomp::VChild<Child>>` for `yew::virtual_dom::vnode::VNode`
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)

View File

@ -27,7 +27,7 @@ impl Component for Child {
unimplemented!() unimplemented!()
} }
fn view(&self) -> Html<Self> { fn view(&self) -> Html {
unimplemented!() unimplemented!()
} }
} }
@ -36,7 +36,7 @@ impl Component for Child {
pub struct ContainerProperties { pub struct ContainerProperties {
#[props(required)] #[props(required)]
pub int: i32, pub int: i32,
pub children: Children<Container>, pub children: Children,
} }
pub struct Container; pub struct Container;
@ -52,7 +52,7 @@ impl Component for Container {
unimplemented!() unimplemented!()
} }
fn view(&self) -> Html<Self> { fn view(&self) -> Html {
unimplemented!() unimplemented!()
} }
} }
@ -61,7 +61,7 @@ impl Component for Container {
pub struct ChildContainerProperties { pub struct ChildContainerProperties {
#[props(required)] #[props(required)]
pub int: i32, pub int: i32,
pub children: ChildrenWithProps<Child, ChildContainer>, pub children: ChildrenWithProps<Child>,
} }
pub struct ChildContainer; pub struct ChildContainer;
@ -77,7 +77,7 @@ impl Component for ChildContainer {
unimplemented!() unimplemented!()
} }
fn view(&self) -> Html<Self> { fn view(&self) -> Html {
unimplemented!() unimplemented!()
} }
} }
@ -136,7 +136,8 @@ pass_helper! {
html! { html! {
<> <>
<Child int=1 /> <Child int=1 />
<Child int=1 optional_callback=|_| () /> <Child int=1 optional_callback=Some(Callback::from(|_| ())) />
<Child int=1 optional_callback=None />
</> </>
}; };

View File

@ -31,8 +31,8 @@ error[E0277]: `()` doesn't implement `std::fmt::Display`
= help: the trait `std::fmt::Display` is not implemented for `()` = help: the trait `std::fmt::Display` is not implemented for `()`
= note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
= note: required because of the requirements on the impl of `std::string::ToString` for `()` = note: required because of the requirements on the impl of `std::string::ToString` for `()`
= note: required because of the requirements on the impl of `std::convert::From<()>` for `yew::virtual_dom::vnode::VNode<_>` = note: required because of the requirements on the impl of `std::convert::From<()>` for `yew::virtual_dom::vnode::VNode`
= note: required because of the requirements on the impl of `std::convert::Into<yew::virtual_dom::vnode::VNode<_>>` for `()` = note: required because of the requirements on the impl of `std::convert::Into<yew::virtual_dom::vnode::VNode>` for `()`
error[E0277]: `()` doesn't implement `std::fmt::Display` error[E0277]: `()` doesn't implement `std::fmt::Display`
--> $DIR/html-iterable-fail.rs:10:17 --> $DIR/html-iterable-fail.rs:10:17
@ -43,8 +43,8 @@ error[E0277]: `()` doesn't implement `std::fmt::Display`
= help: the trait `std::fmt::Display` is not implemented for `()` = help: the trait `std::fmt::Display` is not implemented for `()`
= note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
= note: required because of the requirements on the impl of `std::string::ToString` for `()` = note: required because of the requirements on the impl of `std::string::ToString` for `()`
= note: required because of the requirements on the impl of `std::convert::From<()>` for `yew::virtual_dom::vnode::VNode<_>` = note: required because of the requirements on the impl of `std::convert::From<()>` for `yew::virtual_dom::vnode::VNode`
= note: required because of the requirements on the impl of `std::convert::Into<yew::virtual_dom::vnode::VNode<_>>` for `()` = note: required because of the requirements on the impl of `std::convert::Into<yew::virtual_dom::vnode::VNode>` for `()`
error[E0277]: `()` doesn't implement `std::fmt::Display` error[E0277]: `()` doesn't implement `std::fmt::Display`
--> $DIR/html-iterable-fail.rs:13:17 --> $DIR/html-iterable-fail.rs:13:17
@ -56,8 +56,5 @@ error[E0277]: `()` doesn't implement `std::fmt::Display`
= note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
= note: required because of the requirements on the impl of `std::fmt::Display` for `&()` = note: required because of the requirements on the impl of `std::fmt::Display` for `&()`
= note: required because of the requirements on the impl of `std::string::ToString` for `&()` = note: required because of the requirements on the impl of `std::string::ToString` for `&()`
= note: required because of the requirements on the impl of `std::convert::From<&()>` for `yew::virtual_dom::vnode::VNode<_>` = note: required because of the requirements on the impl of `std::convert::From<&()>` for `yew::virtual_dom::vnode::VNode`
= note: required because of the requirements on the impl of `std::convert::Into<yew::virtual_dom::vnode::VNode<_>>` for `&()` = note: required because of the requirements on the impl of `std::convert::Into<yew::virtual_dom::vnode::VNode>` for `&()`
For more information about this error, try `rustc --explain E0277`.
error: could not compile `yew-tests`.

View File

@ -4,12 +4,12 @@ mod helpers;
use std::iter; use std::iter;
pass_helper! { pass_helper! {
html! { for iter::empty::<Html<TestComponent>>() }; html! { for iter::empty::<Html>() };
html! { for Vec::<Html<TestComponent>>::new().into_iter() }; html! { for Vec::<Html>::new().into_iter() };
html! { for (0..3).map(|num| { html! { <span>{num}</span> } }) }; html! { for (0..3).map(|num| { html! { <span>{num}</span> } }) };
html! { for {iter::empty::<Html<TestComponent>>()} }; html! { for {iter::empty::<Html>()} };
let empty: Vec<Html<TestComponent>> = Vec::new(); let empty: Vec<Html> = Vec::new();
html! { for empty.into_iter() }; html! { for empty.into_iter() };
} }

View File

@ -39,5 +39,3 @@ error: expected valid html element
| |
10 | html! { <>invalid</> }; 10 | html! { <>invalid</> };
| ^^^^^^^ | ^^^^^^^
error: could not compile `yew-tests`.

View File

@ -61,7 +61,7 @@ error[E0277]: `()` doesn't implement `std::fmt::Display`
= help: the trait `std::fmt::Display` is not implemented for `()` = help: the trait `std::fmt::Display` is not implemented for `()`
= note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
= note: required because of the requirements on the impl of `std::string::ToString` for `()` = note: required because of the requirements on the impl of `std::string::ToString` for `()`
= note: required because of the requirements on the impl of `std::convert::From<()>` for `yew::virtual_dom::vnode::VNode<_>` = note: required because of the requirements on the impl of `std::convert::From<()>` for `yew::virtual_dom::vnode::VNode`
= note: required by `std::convert::From::from` = note: required by `std::convert::From::from`
error[E0277]: `()` doesn't implement `std::fmt::Display` error[E0277]: `()` doesn't implement `std::fmt::Display`
@ -73,9 +73,5 @@ error[E0277]: `()` doesn't implement `std::fmt::Display`
= help: the trait `std::fmt::Display` is not implemented for `()` = help: the trait `std::fmt::Display` is not implemented for `()`
= note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
= note: required because of the requirements on the impl of `std::string::ToString` for `()` = note: required because of the requirements on the impl of `std::string::ToString` for `()`
= note: required because of the requirements on the impl of `std::convert::From<()>` for `yew::virtual_dom::vnode::VNode<_>` = note: required because of the requirements on the impl of `std::convert::From<()>` for `yew::virtual_dom::vnode::VNode`
= note: required by `std::convert::From::from` = note: required by `std::convert::From::from`
Some errors have detailed explanations: E0277, E0425.
For more information about an error, try `rustc --explain E0277`.
error: could not compile `yew-tests`.

View File

@ -52,13 +52,11 @@ error: only one root html element allowed
14 | html! { <img /></img> }; 14 | html! { <img /></img> };
| ^^^^^^ | ^^^^^^
error: unexpected end of input, expected token tree error: expected valid html element
--> $DIR/html-tag-fail.rs:15:5 --> $DIR/html-tag-fail.rs:15:18
| |
15 | html! { <div>Invalid</div> }; 15 | html! { <div>Invalid</div> };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^
|
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
error: only one `attr` attribute allowed error: only one `attr` attribute allowed
--> $DIR/html-tag-fail.rs:17:27 --> $DIR/html-tag-fail.rs:17:27

View File

@ -23,7 +23,7 @@ impl Component for TestComponent {
unimplemented!() unimplemented!()
} }
fn view(&self) -> Html<Self> { fn view(&self) -> Html {
unimplemented!() unimplemented!()
} }
} }

View File

@ -1,7 +1,6 @@
#[cfg(feature = "wasm_test")] #[cfg(feature = "wasm_test")]
use wasm_bindgen_test::{wasm_bindgen_test as test, wasm_bindgen_test_configure}; use wasm_bindgen_test::{wasm_bindgen_test as test, wasm_bindgen_test_configure};
use yew::macros::Properties; use yew::macros::Properties;
use yew::virtual_dom::VNode;
use yew::{html, Component, ComponentLink, Html, ShouldRender}; use yew::{html, Component, ComponentLink, Html, ShouldRender};
#[cfg(feature = "wasm_test")] #[cfg(feature = "wasm_test")]
@ -27,26 +26,26 @@ impl Component for Comp {
unimplemented!(); unimplemented!();
} }
fn view(&self) -> Html<Self> { fn view(&self) -> Html {
unimplemented!(); unimplemented!();
} }
} }
#[test] #[test]
fn set_properties_to_component() { fn set_properties_to_component() {
let _: VNode<Comp> = html! { let _ = html! {
<Comp /> <Comp />
}; };
let _: VNode<Comp> = html! { let _ = html! {
<Comp field_1=1 /> <Comp field_1=1 />
}; };
let _: VNode<Comp> = html! { let _ = html! {
<Comp field_2=2 /> <Comp field_2=2 />
}; };
let _: VNode<Comp> = html! { let _ = html! {
<Comp field_1=1 field_2=2 /> <Comp field_1=1 field_2=2 />
}; };
@ -55,7 +54,7 @@ fn set_properties_to_component() {
field_2: 1, field_2: 1,
}; };
let _: VNode<Comp> = html! { let _ = html! {
<Comp with props /> <Comp with props />
}; };
} }

View File

@ -1,6 +1,5 @@
#[cfg(feature = "wasm_test")] #[cfg(feature = "wasm_test")]
use wasm_bindgen_test::{wasm_bindgen_test as test, wasm_bindgen_test_configure}; use wasm_bindgen_test::{wasm_bindgen_test as test, wasm_bindgen_test_configure};
use yew::virtual_dom::VNode;
use yew::{html, Component, ComponentLink, Html, ShouldRender}; use yew::{html, Component, ComponentLink, Html, ShouldRender};
#[cfg(feature = "wasm_test")] #[cfg(feature = "wasm_test")]
@ -20,18 +19,18 @@ impl Component for Comp {
unimplemented!(); unimplemented!();
} }
fn view(&self) -> Html<Self> { fn view(&self) -> Html {
unimplemented!(); unimplemented!();
} }
} }
#[test] #[test]
fn check_fragments() { fn check_fragments() {
let fragment: VNode<Comp> = html! { let fragment = html! {
<> <>
</> </>
}; };
let _: VNode<Comp> = html! { html! {
<div> <div>
{ fragment } { fragment }
</div> </div>

View File

@ -2,7 +2,6 @@
use stdweb::web::{document, IElement}; use stdweb::web::{document, IElement};
#[cfg(feature = "wasm_test")] #[cfg(feature = "wasm_test")]
use wasm_bindgen_test::{wasm_bindgen_test as test, wasm_bindgen_test_configure}; use wasm_bindgen_test::{wasm_bindgen_test as test, wasm_bindgen_test_configure};
use yew::html::Scope;
use yew::virtual_dom::vtag::{VTag, HTML_NAMESPACE, SVG_NAMESPACE}; use yew::virtual_dom::vtag::{VTag, HTML_NAMESPACE, SVG_NAMESPACE};
use yew::virtual_dom::{VDiff, VNode}; use yew::virtual_dom::{VDiff, VNode};
use yew::{html, Component, ComponentLink, Html, ShouldRender}; use yew::{html, Component, ComponentLink, Html, ShouldRender};
@ -24,7 +23,7 @@ impl Component for Comp {
unimplemented!(); unimplemented!();
} }
fn view(&self) -> Html<Self> { fn view(&self) -> Html {
unimplemented!(); unimplemented!();
} }
} }
@ -43,7 +42,7 @@ impl Component for CompInt {
unimplemented!(); unimplemented!();
} }
fn view(&self) -> Html<Self> { fn view(&self) -> Html {
unimplemented!(); unimplemented!();
} }
} }
@ -62,22 +61,22 @@ impl Component for CompBool {
unimplemented!(); unimplemented!();
} }
fn view(&self) -> Html<Self> { fn view(&self) -> Html {
unimplemented!(); unimplemented!();
} }
} }
#[test] #[test]
fn it_compares_tags() { fn it_compares_tags() {
let a: VNode<Comp> = html! { let a = html! {
<div></div> <div></div>
}; };
let b: VNode<Comp> = html! { let b = html! {
<div></div> <div></div>
}; };
let c: VNode<Comp> = html! { let c = html! {
<p></p> <p></p>
}; };
@ -87,15 +86,15 @@ fn it_compares_tags() {
#[test] #[test]
fn it_compares_text() { fn it_compares_text() {
let a: VNode<Comp> = html! { let a = html! {
<div>{ "correct" }</div> <div>{ "correct" }</div>
}; };
let b: VNode<Comp> = html! { let b = html! {
<div>{ "correct" }</div> <div>{ "correct" }</div>
}; };
let c: VNode<Comp> = html! { let c = html! {
<div>{ "incorrect" }</div> <div>{ "incorrect" }</div>
}; };
@ -105,15 +104,15 @@ fn it_compares_text() {
#[test] #[test]
fn it_compares_attributes() { fn it_compares_attributes() {
let a: VNode<Comp> = html! { let a = html! {
<div a="test"></div> <div a="test"></div>
}; };
let b: VNode<Comp> = html! { let b = html! {
<div a="test"></div> <div a="test"></div>
}; };
let c: VNode<Comp> = html! { let c = html! {
<div a="fail"></div> <div a="fail"></div>
}; };
@ -123,19 +122,19 @@ fn it_compares_attributes() {
#[test] #[test]
fn it_compares_children() { fn it_compares_children() {
let a: VNode<Comp> = html! { let a = html! {
<div> <div>
<p></p> <p></p>
</div> </div>
}; };
let b: VNode<Comp> = html! { let b = html! {
<div> <div>
<p></p> <p></p>
</div> </div>
}; };
let c: VNode<Comp> = html! { let c = html! {
<div> <div>
<span></span> <span></span>
</div> </div>
@ -147,19 +146,19 @@ fn it_compares_children() {
#[test] #[test]
fn it_compares_classes() { fn it_compares_classes() {
let a: VNode<Comp> = html! { let a = html! {
<div class="test"></div> <div class="test"></div>
}; };
let b: VNode<Comp> = html! { let b = html! {
<div class="test"></div> <div class="test"></div>
}; };
let c: VNode<Comp> = html! { let c = html! {
<div class="fail"></div> <div class="fail"></div>
}; };
let d: VNode<Comp> = html! { let d = html! {
<div class=format!("fail")></div> <div class=format!("fail")></div>
}; };
@ -170,17 +169,17 @@ fn it_compares_classes() {
#[test] #[test]
fn classes_from_local_variables() { fn classes_from_local_variables() {
let a: VNode<Comp> = html! { let a = html! {
<div class=("class-1", "class-2")></div> <div class=("class-1", "class-2")></div>
}; };
let class_2 = "class-2"; let class_2 = "class-2";
let b: VNode<Comp> = html! { let b = html! {
<div class=("class-1", class_2)></div> <div class=("class-1", class_2)></div>
}; };
let class_2_fmt = format!("class-{}", 2); let class_2_fmt = format!("class-{}", 2);
let c: VNode<Comp> = html! { let c = html! {
<div class=("class-1", class_2_fmt)></div> <div class=("class-1", class_2_fmt)></div>
}; };
@ -190,11 +189,11 @@ fn classes_from_local_variables() {
#[test] #[test]
fn supports_multiple_classes_string() { fn supports_multiple_classes_string() {
let a: VNode<Comp> = html! { let a = html! {
<div class="class-1 class-2 class-3"></div> <div class="class-1 class-2 class-3"></div>
}; };
let b: VNode<Comp> = html! { let b = html! {
<div class="class-2 class-3 class-1"></div> <div class="class-2 class-3 class-1"></div>
}; };
@ -214,7 +213,7 @@ fn supports_multiple_classes_string() {
fn supports_multiple_classes_vec() { fn supports_multiple_classes_vec() {
let mut classes = vec!["class-1"]; let mut classes = vec!["class-1"];
classes.push("class-2"); classes.push("class-2");
let a: VNode<Comp> = html! { let a = html! {
<div class=classes></div> <div class=classes></div>
}; };
@ -232,9 +231,9 @@ fn supports_multiple_classes_vec() {
fn filter_empty_string_classes_vec() { fn filter_empty_string_classes_vec() {
let mut classes = vec![""]; let mut classes = vec![""];
classes.push("class-2"); classes.push("class-2");
let a: VNode<Comp> = html! { <div class=vec![""]></div> }; let a = html! { <div class=vec![""]></div> };
let b: VNode<Comp> = html! { <div class=("")></div> }; let b = html! { <div class=("")></div> };
let c: VNode<Comp> = html! { <div class=""></div> }; let c = html! { <div class=""></div> };
if let VNode::VTag(vtag) = a { if let VNode::VTag(vtag) = a {
assert!(vtag.classes.is_empty()); assert!(vtag.classes.is_empty());
@ -255,14 +254,14 @@ fn filter_empty_string_classes_vec() {
} }
} }
fn assert_vtag(node: &mut VNode<Comp>) -> &mut VTag<Comp> { fn assert_vtag(node: &mut VNode) -> &mut VTag {
if let VNode::VTag(vtag) = node { if let VNode::VTag(vtag) = node {
return vtag; return vtag;
} }
panic!("should be vtag"); panic!("should be vtag");
} }
fn assert_namespace(vtag: &VTag<Comp>, namespace: &'static str) { fn assert_namespace(vtag: &VTag, namespace: &'static str) {
assert_eq!( assert_eq!(
vtag.reference.as_ref().unwrap().namespace_uri().unwrap(), vtag.reference.as_ref().unwrap().namespace_uri().unwrap(),
namespace namespace
@ -271,32 +270,31 @@ fn assert_namespace(vtag: &VTag<Comp>, namespace: &'static str) {
#[test] #[test]
fn supports_svg() { fn supports_svg() {
let scope = Scope::new();
let div_el = document().create_element("div").unwrap(); let div_el = document().create_element("div").unwrap();
let svg_el = document().create_element_ns(SVG_NAMESPACE, "svg").unwrap(); let svg_el = document().create_element_ns(SVG_NAMESPACE, "svg").unwrap();
let mut g_node: VNode<Comp> = html! { <g></g> }; let mut g_node = html! { <g></g> };
let path_node: VNode<Comp> = html! { <path></path> }; let path_node = html! { <path></path> };
let mut svg_node: VNode<Comp> = html! { <svg>{path_node}</svg> }; let mut svg_node = html! { <svg>{path_node}</svg> };
let svg_tag = assert_vtag(&mut svg_node); let svg_tag = assert_vtag(&mut svg_node);
svg_tag.apply(&div_el, None, None, &scope); svg_tag.apply(&div_el, None, None);
assert_namespace(svg_tag, SVG_NAMESPACE); assert_namespace(svg_tag, SVG_NAMESPACE);
let path_tag = assert_vtag(svg_tag.children.get_mut(0).unwrap()); let path_tag = assert_vtag(svg_tag.children.get_mut(0).unwrap());
assert_namespace(path_tag, SVG_NAMESPACE); assert_namespace(path_tag, SVG_NAMESPACE);
let g_tag = assert_vtag(&mut g_node); let g_tag = assert_vtag(&mut g_node);
g_tag.apply(&div_el, None, None, &scope); g_tag.apply(&div_el, None, None);
assert_namespace(g_tag, HTML_NAMESPACE); assert_namespace(g_tag, HTML_NAMESPACE);
g_tag.reference = None; g_tag.reference = None;
g_tag.apply(&svg_el, None, None, &scope); g_tag.apply(&svg_el, None, None);
assert_namespace(g_tag, SVG_NAMESPACE); assert_namespace(g_tag, SVG_NAMESPACE);
} }
#[test] #[test]
fn keeps_order_of_classes() { fn keeps_order_of_classes() {
let a: VNode<Comp> = html! { let a = html! {
<div class="class-1 class-2 class-3",></div> <div class="class-1 class-2 class-3",></div>
}; };
@ -308,15 +306,15 @@ fn keeps_order_of_classes() {
#[test] #[test]
fn it_compares_values() { fn it_compares_values() {
let a: VNode<Comp> = html! { let a = html! {
<input value="test"/> <input value="test"/>
}; };
let b: VNode<Comp> = html! { let b = html! {
<input value="test"/> <input value="test"/>
}; };
let c: VNode<Comp> = html! { let c = html! {
<input value="fail"/> <input value="fail"/>
}; };
@ -326,15 +324,15 @@ fn it_compares_values() {
#[test] #[test]
fn it_compares_kinds() { fn it_compares_kinds() {
let a: VNode<Comp> = html! { let a = html! {
<input type="text"/> <input type="text"/>
}; };
let b: VNode<Comp> = html! { let b = html! {
<input type="text"/> <input type="text"/>
}; };
let c: VNode<Comp> = html! { let c = html! {
<input type="hidden"/> <input type="hidden"/>
}; };
@ -344,15 +342,15 @@ fn it_compares_kinds() {
#[test] #[test]
fn it_compares_checked() { fn it_compares_checked() {
let a: VNode<Comp> = html! { let a = html! {
<input type="checkbox" checked=false /> <input type="checkbox" checked=false />
}; };
let b: VNode<Comp> = html! { let b = html! {
<input type="checkbox" checked=false /> <input type="checkbox" checked=false />
}; };
let c: VNode<Comp> = html! { let c = html! {
<input type="checkbox" checked=true /> <input type="checkbox" checked=true />
}; };
@ -362,7 +360,7 @@ fn it_compares_checked() {
#[test] #[test]
fn it_allows_aria_attributes() { fn it_allows_aria_attributes() {
let a: VNode<Comp> = html! { let a = html! {
<p aria-controls="it-works"> <p aria-controls="it-works">
<a class="btn btn-primary" <a class="btn btn-primary"
data-toggle="collapse" data-toggle="collapse"
@ -396,38 +394,16 @@ fn it_allows_aria_attributes() {
#[test] #[test]
fn it_checks_mixed_closing_tags() { fn it_checks_mixed_closing_tags() {
let a: VNode<Comp> = html! { <div> <div/> </div> }; let a = html! { <div> <div/> </div> };
let b: VNode<Comp> = html! { <div> <div></div> </div> }; let b = html! { <div> <div></div> </div> };
assert_eq!(a, b);
let a: VNode<Comp> = html! { <div> <div data-val={ 2 / 1 }/> </div> };
let b: VNode<Comp> = html! { <div> <div data-val={ 2 }></div> </div> };
assert_eq!(a, b);
let a: VNode<Comp> = html! { <div> <div data-val={ 2 > 1 }/> </div> };
let b: VNode<Comp> = html! { <div> <div data-val={ true }></div> </div> };
assert_eq!(a, b);
let a: VNode<CompInt> = html! { <div> <div onblur=|_| 2 / 1/> </div> };
let b: VNode<CompInt> = html! { <div> <div onblur=|_| 2></div> </div> };
assert_eq!(a, b); assert_eq!(a, b);
} }
#[test] #[test]
fn it_checks_misleading_gt() { fn it_checks_misleading_gt() {
let a: VNode<CompBool> = html! { <div> <div onblur=|_| 2 > 1 /> </div> }; html! { <div data-val=<u32 as Default>::default()></div> };
let b: VNode<CompBool> = html! { <div> <div onblur=|_| { 2 > 1 } /> </div> }; html! { <div data-val=Box::<u32>::default()></div> };
let c: VNode<CompBool> = html! { <div> <div onblur=|_| ( 2 > 1 ) /> </div> };
let d: VNode<CompBool> = html! { <div> <div onblur=|_| true ></div> </div> };
assert_eq!(a, b);
assert_eq!(a, c);
assert_eq!(a, d);
let a: VNode<CompBool> = html! { <div><div onblur=|_| 2 > 1 /> </div> }; html! { <div><a data-val=<u32 as Default>::default() /> </div> };
let b: VNode<CompBool> = html! { <div><div onblur=|_| { true }></div></div> }; html! { <div><a data-val=Box::<u32>::default() /></div> };
assert_eq!(a, b);
let a: VNode<CompInt> = html! { <div> <a onblur=|_| -> u32 { 0 } /> </div> };
let b: VNode<CompInt> = html! { <div> <a onblur=|_| 0></a> </div> };
assert_eq!(a, b);
} }

View File

@ -1,6 +1,5 @@
#[cfg(feature = "wasm_test")] #[cfg(feature = "wasm_test")]
use wasm_bindgen_test::{wasm_bindgen_test as test, wasm_bindgen_test_configure}; use wasm_bindgen_test::{wasm_bindgen_test as test, wasm_bindgen_test_configure};
use yew::virtual_dom::VNode;
use yew::{html, Component, ComponentLink, Html, ShouldRender}; use yew::{html, Component, ComponentLink, Html, ShouldRender};
#[cfg(feature = "wasm_test")] #[cfg(feature = "wasm_test")]
@ -20,18 +19,18 @@ impl Component for Comp {
unimplemented!(); unimplemented!();
} }
fn view(&self) -> Html<Self> { fn view(&self) -> Html {
unimplemented!(); unimplemented!();
} }
} }
#[test] #[test]
fn text_as_root() { fn text_as_root() {
let _: VNode<Comp> = html! { html! {
"Text Node As Root" "Text Node As Root"
}; };
let _: VNode<Comp> = html! { html! {
{ "Text Node As Root" } { "Text Node As Root" }
}; };
} }