Remove ToHtml trait (#3453)

* remove ToHtml trait

* re-add display impls

* make Vec::clone expilit

* fix doc

* fix conflicting impls

Into<Html> and Display can't be implemented on the same type

* update docs

* blanket impl won't work here

* bring back `Vec<VNode>: IntoPropValue<VNode>`

* macro tests

* Revert "fix conflicting impls"

This reverts commit 52f3c1fa8174489ba9cc783d708a49cc7b9c90a4.
These impls are fine now

* make examples compile

* .clone() should be before .into()

* Rc VList

* Make use of ImplicitClone and AttrValue in example

(There is more work to do but it's complicated so I will do it in
another PR)

* Impl ImplicitClone on VChild

---------

Co-authored-by: Cecile Tonglet <cecile.tonglet@cecton.com>
This commit is contained in:
Muhammad Hamza 2023-10-28 16:11:28 +05:00 committed by GitHub
parent 7f45af3a66
commit 0eb167ac78
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 168 additions and 293 deletions

View File

@ -2,6 +2,7 @@ use std::rc::Rc;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use strum_macros::{Display, EnumIter}; use strum_macros::{Display, EnumIter};
use yew::html::IntoPropValue;
use yew::prelude::*; use yew::prelude::*;
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
@ -42,8 +43,8 @@ impl Filter {
} }
} }
impl ToHtml for Filter { impl IntoPropValue<Html> for Filter {
fn to_html(&self) -> Html { fn into_prop_value(self) -> Html {
html! {<>{self.to_string()}</>} html! {<>{self.to_string()}</>}
} }
} }

View File

@ -7,7 +7,7 @@ pub struct Props {
#[prop_or_default] #[prop_or_default]
pub hide: bool, pub hide: bool,
pub on_hover: Callback<Hovered>, pub on_hover: Callback<Hovered>,
pub name: String, pub name: AttrValue,
#[prop_or_default] #[prop_or_default]
pub children: Children, pub children: Children,
} }

View File

@ -73,9 +73,8 @@ impl List {
.filter(|c| !c.props.hide) .filter(|c| !c.props.hide)
.enumerate() .enumerate()
.map(|(i, mut c)| { .map(|(i, mut c)| {
let mut props = (*c.props).clone(); let props = Rc::make_mut(&mut c.props);
props.name = format!("#{} - {}", i + 1, props.name); props.name = format!("#{} - {}", i + 1, props.name).into();
c.props = Rc::new(props);
c c
}) })
.collect::<Html>() .collect::<Html>()

View File

@ -8,7 +8,7 @@ use std::fmt;
use std::ops::Deref; use std::ops::Deref;
use std::rc::Rc; use std::rc::Rc;
use yew::html::{ImplicitClone, Scope}; use yew::html::{ImplicitClone, IntoPropValue, Scope};
use yew::prelude::*; use yew::prelude::*;
pub struct WeakComponentLink<COMP: Component>(Rc<RefCell<Option<Scope<COMP>>>>); pub struct WeakComponentLink<COMP: Component>(Rc<RefCell<Option<Scope<COMP>>>>);
@ -40,14 +40,16 @@ impl<COMP: Component> PartialEq for WeakComponentLink<COMP> {
} }
} }
#[derive(Debug)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Hovered { pub enum Hovered {
Header, Header,
Item(String), Item(AttrValue),
List, List,
None, None,
} }
impl ImplicitClone for Hovered {}
impl fmt::Display for Hovered { impl fmt::Display for Hovered {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!( write!(
@ -63,8 +65,8 @@ impl fmt::Display for Hovered {
} }
} }
impl ToHtml for Hovered { impl IntoPropValue<Html> for &Hovered {
fn to_html(&self) -> yew::Html { fn into_prop_value(self) -> Html {
html! {<>{self.to_string()}</>} html! {<>{self.to_string()}</>}
} }
} }

View File

@ -139,7 +139,7 @@ impl Component for App {
{ &self.time } { &self.time }
</div> </div>
<div id="messages"> <div id="messages">
{ for self.messages.iter().map(|message| html! { <p>{ message }</p> }) } { for self.messages.iter().map(|message| html! { <p>{ *message }</p> }) }
</div> </div>
</div> </div>
</> </>

View File

@ -105,7 +105,7 @@ fn App() -> Html {
.iter() .iter()
.map(|message| { .map(|message| {
key += 1; key += 1;
html! { <p key={ key }>{ message }</p> } html! { <p key={ key }>{ *message }</p> }
}) })
.collect(); .collect();

View File

@ -1,5 +1,6 @@
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use strum_macros::{Display, EnumIter}; use strum_macros::{Display, EnumIter};
use yew::html::IntoPropValue;
use yew::prelude::*; use yew::prelude::*;
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
@ -142,8 +143,8 @@ impl Filter {
} }
} }
impl ToHtml for Filter { impl IntoPropValue<Html> for Filter {
fn to_html(&self) -> yew::Html { fn into_prop_value(self) -> yew::Html {
html! { <>{self.to_string()}</> } html! { <>{self.to_string()}</> }
} }
} }

View File

@ -463,16 +463,7 @@ error[E0277]: the trait bound `(): IntoPropValue<String>` is not satisfied
| | | |
| required by a bound introduced by this call | required by a bound introduced by this call
| |
= help: the following other types implement trait `IntoPropValue<T>`: = help: the trait `IntoPropValue<VNode>` is implemented for `()`
<&'static [(K, V)] as IntoPropValue<implicit_clone::unsync::IMap<K, V>>>
<&'static [T] as IntoPropValue<implicit_clone::unsync::IArray<T>>>
<&'static str as IntoPropValue<Classes>>
<&'static str as IntoPropValue<Option<String>>>
<&'static str as IntoPropValue<Option<implicit_clone::unsync::IString>>>
<&'static str as IntoPropValue<String>>
<&'static str as IntoPropValue<implicit_clone::unsync::IString>>
<&T as IntoPropValue<Option<T>>>
and $N others
note: required by a bound in `ChildPropertiesBuilder::string` note: required by a bound in `ChildPropertiesBuilder::string`
--> tests/html_macro/component-fail.rs:4:17 --> tests/html_macro/component-fail.rs:4:17
| |
@ -706,7 +697,9 @@ error[E0277]: the trait bound `yew::virtual_dom::VText: IntoPropValue<ChildrenRe
117 | html! { <ChildContainer>{ "Not allowed" }</ChildContainer> }; 117 | html! { <ChildContainer>{ "Not allowed" }</ChildContainer> };
| ^^^^^^^^^^^^^^ the trait `IntoPropValue<ChildrenRenderer<VChild<Child>>>` is not implemented for `yew::virtual_dom::VText` | ^^^^^^^^^^^^^^ the trait `IntoPropValue<ChildrenRenderer<VChild<Child>>>` is not implemented for `yew::virtual_dom::VText`
| |
= help: the trait `IntoPropValue<ChildrenRenderer<VNode>>` is implemented for `yew::virtual_dom::VText` = help: the following other types implement trait `IntoPropValue<T>`:
<yew::virtual_dom::VText as IntoPropValue<ChildrenRenderer<VNode>>>
<yew::virtual_dom::VText as IntoPropValue<VNode>>
note: required by a bound in `ChildContainerPropertiesBuilder::children` note: required by a bound in `ChildContainerPropertiesBuilder::children`
--> tests/html_macro/component-fail.rs:24:17 --> tests/html_macro/component-fail.rs:24:17
| |

View File

@ -434,16 +434,7 @@ error[E0277]: the trait bound `(): IntoPropValue<Option<implicit_clone::unsync::
43 | html! { <input type={()} /> }; 43 | html! { <input type={()} /> };
| ^^ the trait `IntoPropValue<Option<implicit_clone::unsync::IString>>` is not implemented for `()` | ^^ the trait `IntoPropValue<Option<implicit_clone::unsync::IString>>` is not implemented for `()`
| |
= help: the following other types implement trait `IntoPropValue<T>`: = help: the trait `IntoPropValue<VNode>` is implemented for `()`
<&'static [(K, V)] as IntoPropValue<implicit_clone::unsync::IMap<K, V>>>
<&'static [T] as IntoPropValue<implicit_clone::unsync::IArray<T>>>
<&'static str as IntoPropValue<Classes>>
<&'static str as IntoPropValue<Option<String>>>
<&'static str as IntoPropValue<Option<implicit_clone::unsync::IString>>>
<&'static str as IntoPropValue<String>>
<&'static str as IntoPropValue<implicit_clone::unsync::IString>>
<&T as IntoPropValue<Option<T>>>
and $N others
error[E0277]: the trait bound `(): IntoPropValue<Option<implicit_clone::unsync::IString>>` is not satisfied error[E0277]: the trait bound `(): IntoPropValue<Option<implicit_clone::unsync::IString>>` is not satisfied
--> tests/html_macro/element-fail.rs:44:27 --> tests/html_macro/element-fail.rs:44:27
@ -451,16 +442,7 @@ error[E0277]: the trait bound `(): IntoPropValue<Option<implicit_clone::unsync::
44 | html! { <input value={()} /> }; 44 | html! { <input value={()} /> };
| ^^ the trait `IntoPropValue<Option<implicit_clone::unsync::IString>>` is not implemented for `()` | ^^ the trait `IntoPropValue<Option<implicit_clone::unsync::IString>>` is not implemented for `()`
| |
= help: the following other types implement trait `IntoPropValue<T>`: = help: the trait `IntoPropValue<VNode>` is implemented for `()`
<&'static [(K, V)] as IntoPropValue<implicit_clone::unsync::IMap<K, V>>>
<&'static [T] as IntoPropValue<implicit_clone::unsync::IArray<T>>>
<&'static str as IntoPropValue<Classes>>
<&'static str as IntoPropValue<Option<String>>>
<&'static str as IntoPropValue<Option<implicit_clone::unsync::IString>>>
<&'static str as IntoPropValue<String>>
<&'static str as IntoPropValue<implicit_clone::unsync::IString>>
<&T as IntoPropValue<Option<T>>>
and $N others
error[E0277]: the trait bound `(): IntoPropValue<Option<implicit_clone::unsync::IString>>` is not satisfied error[E0277]: the trait bound `(): IntoPropValue<Option<implicit_clone::unsync::IString>>` is not satisfied
--> tests/html_macro/element-fail.rs:45:22 --> tests/html_macro/element-fail.rs:45:22
@ -468,16 +450,7 @@ error[E0277]: the trait bound `(): IntoPropValue<Option<implicit_clone::unsync::
45 | html! { <a href={()} /> }; 45 | html! { <a href={()} /> };
| ^^ the trait `IntoPropValue<Option<implicit_clone::unsync::IString>>` is not implemented for `()` | ^^ the trait `IntoPropValue<Option<implicit_clone::unsync::IString>>` is not implemented for `()`
| |
= help: the following other types implement trait `IntoPropValue<T>`: = help: the trait `IntoPropValue<VNode>` is implemented for `()`
<&'static [(K, V)] as IntoPropValue<implicit_clone::unsync::IMap<K, V>>>
<&'static [T] as IntoPropValue<implicit_clone::unsync::IArray<T>>>
<&'static str as IntoPropValue<Classes>>
<&'static str as IntoPropValue<Option<String>>>
<&'static str as IntoPropValue<Option<implicit_clone::unsync::IString>>>
<&'static str as IntoPropValue<String>>
<&'static str as IntoPropValue<implicit_clone::unsync::IString>>
<&T as IntoPropValue<Option<T>>>
and $N others
error[E0277]: the trait bound `NotToString: IntoPropValue<Option<implicit_clone::unsync::IString>>` is not satisfied error[E0277]: the trait bound `NotToString: IntoPropValue<Option<implicit_clone::unsync::IString>>` is not satisfied
--> tests/html_macro/element-fail.rs:46:28 --> tests/html_macro/element-fail.rs:46:28
@ -493,7 +466,7 @@ error[E0277]: the trait bound `NotToString: IntoPropValue<Option<implicit_clone:
<&'static str as IntoPropValue<Option<implicit_clone::unsync::IString>>> <&'static str as IntoPropValue<Option<implicit_clone::unsync::IString>>>
<&'static str as IntoPropValue<String>> <&'static str as IntoPropValue<String>>
<&'static str as IntoPropValue<implicit_clone::unsync::IString>> <&'static str as IntoPropValue<implicit_clone::unsync::IString>>
<&T as IntoPropValue<Option<T>>> <&String as IntoPropValue<VNode>>
and $N others and $N others
error[E0277]: the trait bound `Option<NotToString>: IntoPropValue<Option<implicit_clone::unsync::IString>>` is not satisfied error[E0277]: the trait bound `Option<NotToString>: IntoPropValue<Option<implicit_clone::unsync::IString>>` is not satisfied
@ -510,6 +483,7 @@ error[E0277]: the trait bound `Option<NotToString>: IntoPropValue<Option<implici
<Option<Rc<str>> as IntoPropValue<Option<implicit_clone::unsync::IString>>> <Option<Rc<str>> as IntoPropValue<Option<implicit_clone::unsync::IString>>>
<Option<String> as IntoPropValue<Option<implicit_clone::unsync::IString>>> <Option<String> as IntoPropValue<Option<implicit_clone::unsync::IString>>>
<Option<VChild<T>> as IntoPropValue<Option<ChildrenRenderer<C>>>> <Option<VChild<T>> as IntoPropValue<Option<ChildrenRenderer<C>>>>
<Option<VNode> as IntoPropValue<VNode>>
error[E0277]: the trait bound `Option<{integer}>: IntoPropValue<Option<implicit_clone::unsync::IString>>` is not satisfied error[E0277]: the trait bound `Option<{integer}>: IntoPropValue<Option<implicit_clone::unsync::IString>>` is not satisfied
--> tests/html_macro/element-fail.rs:48:22 --> tests/html_macro/element-fail.rs:48:22
@ -525,6 +499,7 @@ error[E0277]: the trait bound `Option<{integer}>: IntoPropValue<Option<implicit_
<Option<Rc<str>> as IntoPropValue<Option<implicit_clone::unsync::IString>>> <Option<Rc<str>> as IntoPropValue<Option<implicit_clone::unsync::IString>>>
<Option<String> as IntoPropValue<Option<implicit_clone::unsync::IString>>> <Option<String> as IntoPropValue<Option<implicit_clone::unsync::IString>>>
<Option<VChild<T>> as IntoPropValue<Option<ChildrenRenderer<C>>>> <Option<VChild<T>> as IntoPropValue<Option<ChildrenRenderer<C>>>>
<Option<VNode> as IntoPropValue<VNode>>
error[E0277]: expected a `Fn<(MouseEvent,)>` closure, found `{integer}` error[E0277]: expected a `Fn<(MouseEvent,)>` closure, found `{integer}`
--> tests/html_macro/element-fail.rs:51:28 --> tests/html_macro/element-fail.rs:51:28
@ -613,16 +588,7 @@ error[E0277]: the trait bound `(): IntoPropValue<yew::NodeRef>` is not satisfied
56 | html! { <input ref={()} /> }; 56 | html! { <input ref={()} /> };
| ^^ the trait `IntoPropValue<yew::NodeRef>` is not implemented for `()` | ^^ the trait `IntoPropValue<yew::NodeRef>` is not implemented for `()`
| |
= help: the following other types implement trait `IntoPropValue<T>`: = help: the trait `IntoPropValue<VNode>` is implemented for `()`
<&'static [(K, V)] as IntoPropValue<implicit_clone::unsync::IMap<K, V>>>
<&'static [T] as IntoPropValue<implicit_clone::unsync::IArray<T>>>
<&'static str as IntoPropValue<Classes>>
<&'static str as IntoPropValue<Option<String>>>
<&'static str as IntoPropValue<Option<implicit_clone::unsync::IString>>>
<&'static str as IntoPropValue<String>>
<&'static str as IntoPropValue<implicit_clone::unsync::IString>>
<&T as IntoPropValue<Option<T>>>
and $N others
= note: this error originates in the macro `html` (in Nightly builds, run with -Z macro-backtrace for more info) = note: this error originates in the macro `html` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0277]: the trait bound `Option<yew::NodeRef>: IntoPropValue<yew::NodeRef>` is not satisfied error[E0277]: the trait bound `Option<yew::NodeRef>: IntoPropValue<yew::NodeRef>` is not satisfied
@ -639,6 +605,7 @@ error[E0277]: the trait bound `Option<yew::NodeRef>: IntoPropValue<yew::NodeRef>
<Option<Rc<str>> as IntoPropValue<Option<implicit_clone::unsync::IString>>> <Option<Rc<str>> as IntoPropValue<Option<implicit_clone::unsync::IString>>>
<Option<String> as IntoPropValue<Option<implicit_clone::unsync::IString>>> <Option<String> as IntoPropValue<Option<implicit_clone::unsync::IString>>>
<Option<VChild<T>> as IntoPropValue<Option<ChildrenRenderer<C>>>> <Option<VChild<T>> as IntoPropValue<Option<ChildrenRenderer<C>>>>
<Option<VNode> as IntoPropValue<VNode>>
= note: this error originates in the macro `html` (in Nightly builds, run with -Z macro-backtrace for more info) = note: this error originates in the macro `html` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0277]: expected a `Fn<(MouseEvent,)>` closure, found `yew::Callback<String>` error[E0277]: expected a `Fn<(MouseEvent,)>` closure, found `yew::Callback<String>`
@ -682,7 +649,7 @@ error[E0277]: the trait bound `NotToString: IntoPropValue<Option<implicit_clone:
<&'static str as IntoPropValue<Option<implicit_clone::unsync::IString>>> <&'static str as IntoPropValue<Option<implicit_clone::unsync::IString>>>
<&'static str as IntoPropValue<String>> <&'static str as IntoPropValue<String>>
<&'static str as IntoPropValue<implicit_clone::unsync::IString>> <&'static str as IntoPropValue<implicit_clone::unsync::IString>>
<&T as IntoPropValue<Option<T>>> <&String as IntoPropValue<VNode>>
and $N others and $N others
error[E0277]: the trait bound `(): IntoPropValue<yew::NodeRef>` is not satisfied error[E0277]: the trait bound `(): IntoPropValue<yew::NodeRef>` is not satisfied
@ -691,16 +658,7 @@ error[E0277]: the trait bound `(): IntoPropValue<yew::NodeRef>` is not satisfied
62 | html! { <input ref={()} /> }; 62 | html! { <input ref={()} /> };
| ^^ the trait `IntoPropValue<yew::NodeRef>` is not implemented for `()` | ^^ the trait `IntoPropValue<yew::NodeRef>` is not implemented for `()`
| |
= help: the following other types implement trait `IntoPropValue<T>`: = help: the trait `IntoPropValue<VNode>` is implemented for `()`
<&'static [(K, V)] as IntoPropValue<implicit_clone::unsync::IMap<K, V>>>
<&'static [T] as IntoPropValue<implicit_clone::unsync::IArray<T>>>
<&'static str as IntoPropValue<Classes>>
<&'static str as IntoPropValue<Option<String>>>
<&'static str as IntoPropValue<Option<implicit_clone::unsync::IString>>>
<&'static str as IntoPropValue<String>>
<&'static str as IntoPropValue<implicit_clone::unsync::IString>>
<&T as IntoPropValue<Option<T>>>
and $N others
= note: this error originates in the macro `html` (in Nightly builds, run with -Z macro-backtrace for more info) = note: this error originates in the macro `html` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0277]: the trait bound `implicit_clone::unsync::IString: From<{integer}>` is not satisfied error[E0277]: the trait bound `implicit_clone::unsync::IString: From<{integer}>` is not satisfied

View File

@ -193,10 +193,11 @@ where
/// ``` /// ```
/// # let children = Children::new(Vec::new()); /// # let children = Children::new(Vec::new());
/// # use yew::{classes, html, Children}; /// # use yew::{classes, html, Children};
/// # let _ =
/// children.map(|children| { /// children.map(|children| {
/// html! { /// html! {
/// <div class={classes!("container")}> /// <div class={classes!("container")}>
/// {children} /// {children.clone()}
/// </div> /// </div>
/// } /// }
/// }) /// })

View File

@ -1,10 +1,10 @@
use std::borrow::Cow; use std::borrow::Cow;
use std::rc::Rc; use std::rc::Rc;
use std::sync::Arc;
use implicit_clone::unsync::{IArray, IMap}; use implicit_clone::unsync::{IArray, IMap};
pub use implicit_clone::ImplicitClone; pub use implicit_clone::ImplicitClone;
use super::ToHtml;
use crate::callback::Callback; use crate::callback::Callback;
use crate::html::{BaseComponent, ChildrenRenderer, Component, NodeRef, Scope}; use crate::html::{BaseComponent, ChildrenRenderer, Component, NodeRef, Scope};
use crate::virtual_dom::{AttrValue, VChild, VList, VNode, VText}; use crate::virtual_dom::{AttrValue, VChild, VList, VNode, VText};
@ -130,13 +130,40 @@ where
} }
} }
impl<T> IntoPropValue<VNode> for T impl<T> IntoPropValue<VNode> for VChild<T>
where where
T: ToHtml, T: BaseComponent,
{ {
#[inline] #[inline]
fn into_prop_value(self) -> VNode { fn into_prop_value(self) -> VNode {
self.into_html() VNode::from(self)
}
}
impl IntoPropValue<VNode> for VList {
#[inline]
fn into_prop_value(self) -> VNode {
VNode::VList(Rc::new(self))
}
}
impl IntoPropValue<VNode> for VText {
#[inline]
fn into_prop_value(self) -> VNode {
VNode::VText(self)
}
}
impl IntoPropValue<VNode> for () {
#[inline]
fn into_prop_value(self) -> VNode {
VNode::default()
}
}
impl IntoPropValue<VNode> for ChildrenRenderer<VNode> {
#[inline]
fn into_prop_value(self) -> VNode {
VNode::VList(Rc::new(self.into()))
} }
} }
@ -168,6 +195,26 @@ impl<C: BaseComponent> IntoPropValue<VList> for VChild<C> {
} }
} }
impl IntoPropValue<ChildrenRenderer<VNode>> for AttrValue {
fn into_prop_value(self) -> ChildrenRenderer<VNode> {
ChildrenRenderer::new(vec![VNode::VText(VText::new(self))])
}
}
impl IntoPropValue<VNode> for Vec<VNode> {
#[inline]
fn into_prop_value(self) -> VNode {
VNode::VList(Rc::new(VList::with_children(self, None)))
}
}
impl IntoPropValue<VNode> for Option<VNode> {
#[inline]
fn into_prop_value(self) -> VNode {
self.unwrap_or_default()
}
}
macro_rules! impl_into_prop { macro_rules! impl_into_prop {
(|$value:ident: $from_ty:ty| -> $to_ty:ty { $conversion:expr }) => { (|$value:ident: $from_ty:ty| -> $to_ty:ty { $conversion:expr }) => {
// implement V -> T // implement V -> T
@ -231,6 +278,57 @@ impl<K: Eq + std::hash::Hash + ImplicitClone + 'static, V: PartialEq + ImplicitC
} }
} }
macro_rules! impl_into_prop_value_via_display {
($from_ty: ty) => {
impl IntoPropValue<VNode> for $from_ty {
#[inline(always)]
fn into_prop_value(self) -> VNode {
VText::from(self).into()
}
}
};
}
// go through AttrValue::from where possible
macro_rules! impl_into_prop_value_via_attr_value {
($from_ty: ty) => {
impl IntoPropValue<VNode> for $from_ty {
#[inline(always)]
fn into_prop_value(self) -> VNode {
VText::new(self).into()
}
}
};
}
// These are a selection of types implemented via display.
impl_into_prop_value_via_display!(bool);
impl_into_prop_value_via_display!(char);
impl_into_prop_value_via_display!(&String);
impl_into_prop_value_via_display!(&str);
impl_into_prop_value_via_display!(Arc<str>);
impl_into_prop_value_via_display!(Arc<String>);
impl_into_prop_value_via_display!(Rc<String>);
impl_into_prop_value_via_display!(u8);
impl_into_prop_value_via_display!(u16);
impl_into_prop_value_via_display!(u32);
impl_into_prop_value_via_display!(u64);
impl_into_prop_value_via_display!(u128);
impl_into_prop_value_via_display!(usize);
impl_into_prop_value_via_display!(i8);
impl_into_prop_value_via_display!(i16);
impl_into_prop_value_via_display!(i32);
impl_into_prop_value_via_display!(i64);
impl_into_prop_value_via_display!(i128);
impl_into_prop_value_via_display!(isize);
impl_into_prop_value_via_display!(f32);
impl_into_prop_value_via_display!(f64);
impl_into_prop_value_via_attr_value!(String);
impl_into_prop_value_via_attr_value!(AttrValue);
impl_into_prop_value_via_attr_value!(Rc<str>);
impl_into_prop_value_via_attr_value!(Cow<'static, str>);
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;
@ -408,4 +506,30 @@ mod test {
</Parent> </Parent>
}; };
} }
#[test]
fn attr_value_children() {
use crate::prelude::*;
#[derive(PartialEq, Properties)]
pub struct ChildProps {
#[prop_or_default]
pub children: AttrValue,
}
#[function_component]
fn Child(_props: &ChildProps) -> Html {
html!()
}
{
let attr_value = AttrValue::from("foo");
let _ = html! { <Child>{attr_value}</Child> };
}
{
let attr_value = AttrValue::from("foo");
let _ = html! { <Child>{&attr_value}</Child> };
}
}
} }

View File

@ -1,5 +1,2 @@
mod into_prop_value; mod into_prop_value;
mod to_html;
pub use into_prop_value::*; pub use into_prop_value::*;
pub use to_html::*;

View File

@ -1,203 +0,0 @@
use std::borrow::Cow;
use std::rc::Rc;
use std::sync::Arc;
use crate::html::{ChildrenRenderer, IntoPropValue};
use crate::virtual_dom::{VChild, VList, VNode, VText};
use crate::{AttrValue, BaseComponent, Html};
/// A trait implemented for types be rendered as a part of a Html.
///
/// Types that implements this trait can define a virtual dom layout that itself should be rendered
/// into via `html!` and can be referenced / consumed as `{value}` in an `html!` macro invocation.
pub trait ToHtml {
/// Converts this type to a [`Html`].
fn to_html(&self) -> Html;
/// Converts this type into a [`Html`].
fn into_html(self) -> Html
where
Self: Sized,
{
self.to_html()
}
}
// Implementations for common data types.
impl<T> ToHtml for Option<T>
where
T: ToHtml,
{
#[inline(always)]
fn to_html(&self) -> Html {
self.as_ref().map(ToHtml::to_html).unwrap_or_default()
}
#[inline(always)]
fn into_html(self) -> Html {
self.map(ToHtml::into_html).unwrap_or_default()
}
}
impl<T> ToHtml for Vec<T>
where
T: ToHtml,
{
#[inline(always)]
fn to_html(&self) -> Html {
Html::VList(Rc::new(VList::with_children(
self.iter().map(ToHtml::to_html).collect(),
None,
)))
}
#[inline(always)]
fn into_html(self) -> Html {
Html::VList(Rc::new(VList::with_children(
self.into_iter().map(ToHtml::into_html).collect(),
None,
)))
}
}
impl ToHtml for Option<VNode> {
#[inline(always)]
fn to_html(&self) -> Html {
self.clone().into_html()
}
#[inline(always)]
fn into_html(self) -> Html {
self.unwrap_or_default()
}
}
impl ToHtml for Vec<VNode> {
#[inline(always)]
fn to_html(&self) -> Html {
self.clone().into_html()
}
#[inline(always)]
fn into_html(self) -> Html {
Html::VList(Rc::new(VList::with_children(self, None)))
}
}
impl ToHtml for VText {
#[inline(always)]
fn to_html(&self) -> Html {
self.clone().into()
}
#[inline(always)]
fn into_html(self) -> Html {
Html::VText(self)
}
}
impl ToHtml for VList {
#[inline(always)]
fn to_html(&self) -> Html {
self.clone().into()
}
#[inline(always)]
fn into_html(self) -> Html {
Html::VList(Rc::new(self))
}
}
impl ToHtml for ChildrenRenderer<VNode> {
#[inline(always)]
fn to_html(&self) -> Html {
self.clone().into()
}
#[inline(always)]
fn into_html(self) -> Html {
self.into()
}
}
impl<T> ToHtml for VChild<T>
where
T: BaseComponent,
{
#[inline(always)]
fn to_html(&self) -> Html {
self.clone().into()
}
#[inline(always)]
fn into_html(self) -> Html {
VNode::VComp(Rc::new(self.into()))
}
}
impl ToHtml for () {
#[inline(always)]
fn to_html(&self) -> Html {
VNode::default()
}
#[inline(always)]
fn into_html(self) -> Html {
VNode::default()
}
}
impl<T> ToHtml for &'_ T
where
T: ToHtml,
{
fn to_html(&self) -> Html {
(*self).to_html()
}
}
macro_rules! impl_to_html_via_display {
($from_ty: ty) => {
impl ToHtml for $from_ty {
#[inline(always)]
fn to_html(&self) -> Html {
Html::VText(VText::from(self))
}
}
// Mirror ToHtml to Children implementation.
impl IntoPropValue<ChildrenRenderer<VNode>> for $from_ty {
#[inline(always)]
fn into_prop_value(self) -> ChildrenRenderer<VNode> {
ChildrenRenderer::new(vec![VText::from(self).into()])
}
}
};
}
// These are a selection of types implemented via display.
impl_to_html_via_display!(bool);
impl_to_html_via_display!(char);
impl_to_html_via_display!(String);
impl_to_html_via_display!(&str);
impl_to_html_via_display!(Rc<str>);
impl_to_html_via_display!(Rc<String>);
impl_to_html_via_display!(Arc<str>);
impl_to_html_via_display!(Arc<String>);
impl_to_html_via_display!(AttrValue);
impl_to_html_via_display!(Cow<'_, str>);
impl_to_html_via_display!(u8);
impl_to_html_via_display!(u16);
impl_to_html_via_display!(u32);
impl_to_html_via_display!(u64);
impl_to_html_via_display!(u128);
impl_to_html_via_display!(usize);
impl_to_html_via_display!(i8);
impl_to_html_via_display!(i16);
impl_to_html_via_display!(i32);
impl_to_html_via_display!(i64);
impl_to_html_via_display!(i128);
impl_to_html_via_display!(isize);
impl_to_html_via_display!(f32);
impl_to_html_via_display!(f64);

View File

@ -336,7 +336,7 @@ pub mod prelude {
pub use crate::functional::*; pub use crate::functional::*;
pub use crate::html::{ pub use crate::html::{
create_portal, BaseComponent, Children, ChildrenWithProps, Classes, Component, Context, create_portal, BaseComponent, Children, ChildrenWithProps, Classes, Component, Context,
Html, HtmlResult, NodeRef, Properties, ToHtml, Html, HtmlResult, NodeRef, Properties,
}; };
pub use crate::macros::{classes, html, html_nested}; pub use crate::macros::{classes, html, html_nested};
pub use crate::suspense::Suspense; pub use crate::suspense::Suspense;

View File

@ -183,6 +183,8 @@ pub struct VChild<COMP: BaseComponent> {
key: Option<Key>, key: Option<Key>,
} }
impl<COMP: BaseComponent> implicit_clone::ImplicitClone for VChild<COMP> {}
impl<COMP: BaseComponent> Clone for VChild<COMP> { impl<COMP: BaseComponent> Clone for VChild<COMP> {
fn clone(&self) -> Self { fn clone(&self) -> Self {
VChild { VChild {

View File

@ -10,7 +10,7 @@ The `#[function_component]` attribute also works with generic functions for crea
```rust ```rust
use std::fmt::Display; use std::fmt::Display;
use yew::{function_component, html, Properties, Html, ToHtml}; use yew::{function_component, html, Properties, Html};
#[derive(Properties, PartialEq)] #[derive(Properties, PartialEq)]
pub struct Props<T> pub struct Props<T>
@ -23,11 +23,11 @@ where
#[function_component] #[function_component]
pub fn MyGenericComponent<T>(props: &Props<T>) -> Html pub fn MyGenericComponent<T>(props: &Props<T>) -> Html
where where
T: PartialEq + ToHtml, T: PartialEq + Clone + Into<Html>,
{ {
html! { html! {
<p> <p>
{ &props.data } { props.data.clone().into() }
</p> </p>
} }
} }