diff --git a/examples/function_todomvc/src/state.rs b/examples/function_todomvc/src/state.rs index 1a461b59e08..77a9a4ee500 100644 --- a/examples/function_todomvc/src/state.rs +++ b/examples/function_todomvc/src/state.rs @@ -2,6 +2,7 @@ use std::rc::Rc; use serde::{Deserialize, Serialize}; use strum_macros::{Display, EnumIter}; +use yew::html::IntoPropValue; use yew::prelude::*; #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] @@ -42,8 +43,8 @@ impl Filter { } } -impl ToHtml for Filter { - fn to_html(&self) -> Html { +impl IntoPropValue for Filter { + fn into_prop_value(self) -> Html { html! {<>{self.to_string()}} } } diff --git a/examples/nested_list/src/item.rs b/examples/nested_list/src/item.rs index d93cc7a7670..1ae69579775 100644 --- a/examples/nested_list/src/item.rs +++ b/examples/nested_list/src/item.rs @@ -7,7 +7,7 @@ pub struct Props { #[prop_or_default] pub hide: bool, pub on_hover: Callback, - pub name: String, + pub name: AttrValue, #[prop_or_default] pub children: Children, } diff --git a/examples/nested_list/src/list.rs b/examples/nested_list/src/list.rs index 46570610a96..042227750df 100644 --- a/examples/nested_list/src/list.rs +++ b/examples/nested_list/src/list.rs @@ -73,9 +73,8 @@ impl List { .filter(|c| !c.props.hide) .enumerate() .map(|(i, mut c)| { - let mut props = (*c.props).clone(); - props.name = format!("#{} - {}", i + 1, props.name); - c.props = Rc::new(props); + let props = Rc::make_mut(&mut c.props); + props.name = format!("#{} - {}", i + 1, props.name).into(); c }) .collect::() diff --git a/examples/nested_list/src/main.rs b/examples/nested_list/src/main.rs index 3f8a93def78..480b680a3b7 100644 --- a/examples/nested_list/src/main.rs +++ b/examples/nested_list/src/main.rs @@ -8,7 +8,7 @@ use std::fmt; use std::ops::Deref; use std::rc::Rc; -use yew::html::{ImplicitClone, Scope}; +use yew::html::{ImplicitClone, IntoPropValue, Scope}; use yew::prelude::*; pub struct WeakComponentLink(Rc>>>); @@ -40,14 +40,16 @@ impl PartialEq for WeakComponentLink { } } -#[derive(Debug)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum Hovered { Header, - Item(String), + Item(AttrValue), List, None, } +impl ImplicitClone for Hovered {} + impl fmt::Display for Hovered { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( @@ -63,8 +65,8 @@ impl fmt::Display for Hovered { } } -impl ToHtml for Hovered { - fn to_html(&self) -> yew::Html { +impl IntoPropValue for &Hovered { + fn into_prop_value(self) -> Html { html! {<>{self.to_string()}} } } diff --git a/examples/timer/src/main.rs b/examples/timer/src/main.rs index 073c38d0ca3..241f86a9805 100644 --- a/examples/timer/src/main.rs +++ b/examples/timer/src/main.rs @@ -139,7 +139,7 @@ impl Component for App { { &self.time }
- { for self.messages.iter().map(|message| html! {

{ message }

}) } + { for self.messages.iter().map(|message| html! {

{ *message }

}) }
diff --git a/examples/timer_functional/src/main.rs b/examples/timer_functional/src/main.rs index 30da755dd0b..5a717cb5476 100644 --- a/examples/timer_functional/src/main.rs +++ b/examples/timer_functional/src/main.rs @@ -105,7 +105,7 @@ fn App() -> Html { .iter() .map(|message| { key += 1; - html! {

{ message }

} + html! {

{ *message }

} }) .collect(); diff --git a/examples/todomvc/src/state.rs b/examples/todomvc/src/state.rs index 251c4925e62..941ef4d963e 100644 --- a/examples/todomvc/src/state.rs +++ b/examples/todomvc/src/state.rs @@ -1,5 +1,6 @@ use serde_derive::{Deserialize, Serialize}; use strum_macros::{Display, EnumIter}; +use yew::html::IntoPropValue; use yew::prelude::*; #[derive(Debug, Serialize, Deserialize)] @@ -142,8 +143,8 @@ impl Filter { } } -impl ToHtml for Filter { - fn to_html(&self) -> yew::Html { +impl IntoPropValue for Filter { + fn into_prop_value(self) -> yew::Html { html! { <>{self.to_string()} } } } diff --git a/packages/yew-macro/tests/html_macro/component-fail.stderr b/packages/yew-macro/tests/html_macro/component-fail.stderr index 6280ac8d37b..440ffed4c64 100644 --- a/packages/yew-macro/tests/html_macro/component-fail.stderr +++ b/packages/yew-macro/tests/html_macro/component-fail.stderr @@ -463,16 +463,7 @@ error[E0277]: the trait bound `(): IntoPropValue` is not satisfied | | | required by a bound introduced by this call | - = help: the following other types implement trait `IntoPropValue`: - <&'static [(K, V)] as IntoPropValue>> - <&'static [T] as IntoPropValue>> - <&'static str as IntoPropValue> - <&'static str as IntoPropValue>> - <&'static str as IntoPropValue>> - <&'static str as IntoPropValue> - <&'static str as IntoPropValue> - <&T as IntoPropValue>> - and $N others + = help: the trait `IntoPropValue` is implemented for `()` note: required by a bound in `ChildPropertiesBuilder::string` --> tests/html_macro/component-fail.rs:4:17 | @@ -706,7 +697,9 @@ error[E0277]: the trait bound `yew::virtual_dom::VText: IntoPropValue{ "Not allowed" } }; | ^^^^^^^^^^^^^^ the trait `IntoPropValue>>` is not implemented for `yew::virtual_dom::VText` | - = help: the trait `IntoPropValue>` is implemented for `yew::virtual_dom::VText` + = help: the following other types implement trait `IntoPropValue`: + >> + > note: required by a bound in `ChildContainerPropertiesBuilder::children` --> tests/html_macro/component-fail.rs:24:17 | diff --git a/packages/yew-macro/tests/html_macro/element-fail.stderr b/packages/yew-macro/tests/html_macro/element-fail.stderr index 842553fab43..9a413f6ef4e 100644 --- a/packages/yew-macro/tests/html_macro/element-fail.stderr +++ b/packages/yew-macro/tests/html_macro/element-fail.stderr @@ -434,16 +434,7 @@ error[E0277]: the trait bound `(): IntoPropValue }; | ^^ the trait `IntoPropValue>` is not implemented for `()` | - = help: the following other types implement trait `IntoPropValue`: - <&'static [(K, V)] as IntoPropValue>> - <&'static [T] as IntoPropValue>> - <&'static str as IntoPropValue> - <&'static str as IntoPropValue>> - <&'static str as IntoPropValue>> - <&'static str as IntoPropValue> - <&'static str as IntoPropValue> - <&T as IntoPropValue>> - and $N others + = help: the trait `IntoPropValue` is implemented for `()` error[E0277]: the trait bound `(): IntoPropValue>` is not satisfied --> tests/html_macro/element-fail.rs:44:27 @@ -451,16 +442,7 @@ error[E0277]: the trait bound `(): IntoPropValue }; | ^^ the trait `IntoPropValue>` is not implemented for `()` | - = help: the following other types implement trait `IntoPropValue`: - <&'static [(K, V)] as IntoPropValue>> - <&'static [T] as IntoPropValue>> - <&'static str as IntoPropValue> - <&'static str as IntoPropValue>> - <&'static str as IntoPropValue>> - <&'static str as IntoPropValue> - <&'static str as IntoPropValue> - <&T as IntoPropValue>> - and $N others + = help: the trait `IntoPropValue` is implemented for `()` error[E0277]: the trait bound `(): IntoPropValue>` is not satisfied --> tests/html_macro/element-fail.rs:45:22 @@ -468,16 +450,7 @@ error[E0277]: the trait bound `(): IntoPropValue }; | ^^ the trait `IntoPropValue>` is not implemented for `()` | - = help: the following other types implement trait `IntoPropValue`: - <&'static [(K, V)] as IntoPropValue>> - <&'static [T] as IntoPropValue>> - <&'static str as IntoPropValue> - <&'static str as IntoPropValue>> - <&'static str as IntoPropValue>> - <&'static str as IntoPropValue> - <&'static str as IntoPropValue> - <&T as IntoPropValue>> - and $N others + = help: the trait `IntoPropValue` is implemented for `()` error[E0277]: the trait bound `NotToString: IntoPropValue>` is not satisfied --> tests/html_macro/element-fail.rs:46:28 @@ -493,7 +466,7 @@ error[E0277]: the trait bound `NotToString: IntoPropValue>> <&'static str as IntoPropValue> <&'static str as IntoPropValue> - <&T as IntoPropValue>> + <&String as IntoPropValue> and $N others error[E0277]: the trait bound `Option: IntoPropValue>` is not satisfied @@ -510,6 +483,7 @@ error[E0277]: the trait bound `Option: IntoPropValue> as IntoPropValue>> as IntoPropValue>> > as IntoPropValue>>> + as IntoPropValue> error[E0277]: the trait bound `Option<{integer}>: IntoPropValue>` is not satisfied --> tests/html_macro/element-fail.rs:48:22 @@ -525,6 +499,7 @@ error[E0277]: the trait bound `Option<{integer}>: IntoPropValue> as IntoPropValue>> as IntoPropValue>> > as IntoPropValue>>> + as IntoPropValue> error[E0277]: expected a `Fn<(MouseEvent,)>` closure, found `{integer}` --> tests/html_macro/element-fail.rs:51:28 @@ -613,16 +588,7 @@ error[E0277]: the trait bound `(): IntoPropValue` is not satisfied 56 | html! { }; | ^^ the trait `IntoPropValue` is not implemented for `()` | - = help: the following other types implement trait `IntoPropValue`: - <&'static [(K, V)] as IntoPropValue>> - <&'static [T] as IntoPropValue>> - <&'static str as IntoPropValue> - <&'static str as IntoPropValue>> - <&'static str as IntoPropValue>> - <&'static str as IntoPropValue> - <&'static str as IntoPropValue> - <&T as IntoPropValue>> - and $N others + = help: the trait `IntoPropValue` is implemented for `()` = 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: IntoPropValue` is not satisfied @@ -639,6 +605,7 @@ error[E0277]: the trait bound `Option: IntoPropValue > as IntoPropValue>> as IntoPropValue>> > as IntoPropValue>>> + as IntoPropValue> = 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` @@ -682,7 +649,7 @@ error[E0277]: the trait bound `NotToString: IntoPropValue>> <&'static str as IntoPropValue> <&'static str as IntoPropValue> - <&T as IntoPropValue>> + <&String as IntoPropValue> and $N others error[E0277]: the trait bound `(): IntoPropValue` is not satisfied @@ -691,16 +658,7 @@ error[E0277]: the trait bound `(): IntoPropValue` is not satisfied 62 | html! { }; | ^^ the trait `IntoPropValue` is not implemented for `()` | - = help: the following other types implement trait `IntoPropValue`: - <&'static [(K, V)] as IntoPropValue>> - <&'static [T] as IntoPropValue>> - <&'static str as IntoPropValue> - <&'static str as IntoPropValue>> - <&'static str as IntoPropValue>> - <&'static str as IntoPropValue> - <&'static str as IntoPropValue> - <&T as IntoPropValue>> - and $N others + = help: the trait `IntoPropValue` is implemented for `()` = 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 diff --git a/packages/yew/src/html/component/children.rs b/packages/yew/src/html/component/children.rs index af004d96ff9..701f00fd9df 100644 --- a/packages/yew/src/html/component/children.rs +++ b/packages/yew/src/html/component/children.rs @@ -193,10 +193,11 @@ where /// ``` /// # let children = Children::new(Vec::new()); /// # use yew::{classes, html, Children}; + /// # let _ = /// children.map(|children| { /// html! { ///
- /// {children} + /// {children.clone()} ///
/// } /// }) diff --git a/packages/yew/src/html/conversion/into_prop_value.rs b/packages/yew/src/html/conversion/into_prop_value.rs index 15fade778c3..9b2972b5b93 100644 --- a/packages/yew/src/html/conversion/into_prop_value.rs +++ b/packages/yew/src/html/conversion/into_prop_value.rs @@ -1,10 +1,10 @@ use std::borrow::Cow; use std::rc::Rc; +use std::sync::Arc; use implicit_clone::unsync::{IArray, IMap}; pub use implicit_clone::ImplicitClone; -use super::ToHtml; use crate::callback::Callback; use crate::html::{BaseComponent, ChildrenRenderer, Component, NodeRef, Scope}; use crate::virtual_dom::{AttrValue, VChild, VList, VNode, VText}; @@ -130,13 +130,40 @@ where } } -impl IntoPropValue for T +impl IntoPropValue for VChild where - T: ToHtml, + T: BaseComponent, { #[inline] fn into_prop_value(self) -> VNode { - self.into_html() + VNode::from(self) + } +} + +impl IntoPropValue for VList { + #[inline] + fn into_prop_value(self) -> VNode { + VNode::VList(Rc::new(self)) + } +} +impl IntoPropValue for VText { + #[inline] + fn into_prop_value(self) -> VNode { + VNode::VText(self) + } +} + +impl IntoPropValue for () { + #[inline] + fn into_prop_value(self) -> VNode { + VNode::default() + } +} + +impl IntoPropValue for ChildrenRenderer { + #[inline] + fn into_prop_value(self) -> VNode { + VNode::VList(Rc::new(self.into())) } } @@ -168,6 +195,26 @@ impl IntoPropValue for VChild { } } +impl IntoPropValue> for AttrValue { + fn into_prop_value(self) -> ChildrenRenderer { + ChildrenRenderer::new(vec![VNode::VText(VText::new(self))]) + } +} + +impl IntoPropValue for Vec { + #[inline] + fn into_prop_value(self) -> VNode { + VNode::VList(Rc::new(VList::with_children(self, None))) + } +} + +impl IntoPropValue for Option { + #[inline] + fn into_prop_value(self) -> VNode { + self.unwrap_or_default() + } +} + macro_rules! impl_into_prop { (|$value:ident: $from_ty:ty| -> $to_ty:ty { $conversion:expr }) => { // implement V -> T @@ -231,6 +278,57 @@ impl { + impl IntoPropValue 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 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); +impl_into_prop_value_via_display!(Arc); +impl_into_prop_value_via_display!(Rc); +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); +impl_into_prop_value_via_attr_value!(Cow<'static, str>); + #[cfg(test)] mod test { use super::*; @@ -408,4 +506,30 @@ mod test { }; } + + #[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! { {attr_value} }; + } + { + let attr_value = AttrValue::from("foo"); + + let _ = html! { {&attr_value} }; + } + } } diff --git a/packages/yew/src/html/conversion/mod.rs b/packages/yew/src/html/conversion/mod.rs index 70181c553a2..ff9f1401cb5 100644 --- a/packages/yew/src/html/conversion/mod.rs +++ b/packages/yew/src/html/conversion/mod.rs @@ -1,5 +1,2 @@ mod into_prop_value; -mod to_html; - pub use into_prop_value::*; -pub use to_html::*; diff --git a/packages/yew/src/html/conversion/to_html.rs b/packages/yew/src/html/conversion/to_html.rs deleted file mode 100644 index af15d5a179e..00000000000 --- a/packages/yew/src/html/conversion/to_html.rs +++ /dev/null @@ -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 ToHtml for Option -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 ToHtml for Vec -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 { - #[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 { - #[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 { - #[inline(always)] - fn to_html(&self) -> Html { - self.clone().into() - } - - #[inline(always)] - fn into_html(self) -> Html { - self.into() - } -} - -impl ToHtml for VChild -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 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> for $from_ty { - #[inline(always)] - fn into_prop_value(self) -> ChildrenRenderer { - 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); -impl_to_html_via_display!(Rc); -impl_to_html_via_display!(Arc); -impl_to_html_via_display!(Arc); -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); diff --git a/packages/yew/src/lib.rs b/packages/yew/src/lib.rs index 3e47e9a5c30..cd85d32af3e 100644 --- a/packages/yew/src/lib.rs +++ b/packages/yew/src/lib.rs @@ -336,7 +336,7 @@ pub mod prelude { pub use crate::functional::*; pub use crate::html::{ 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::suspense::Suspense; diff --git a/packages/yew/src/virtual_dom/vcomp.rs b/packages/yew/src/virtual_dom/vcomp.rs index 55129017a02..07c5faba0a1 100644 --- a/packages/yew/src/virtual_dom/vcomp.rs +++ b/packages/yew/src/virtual_dom/vcomp.rs @@ -183,6 +183,8 @@ pub struct VChild { key: Option, } +impl implicit_clone::ImplicitClone for VChild {} + impl Clone for VChild { fn clone(&self) -> Self { VChild { diff --git a/website/docs/concepts/function-components/generics.mdx b/website/docs/concepts/function-components/generics.mdx index 758757b5ad6..d36805704b1 100644 --- a/website/docs/concepts/function-components/generics.mdx +++ b/website/docs/concepts/function-components/generics.mdx @@ -10,7 +10,7 @@ The `#[function_component]` attribute also works with generic functions for crea ```rust use std::fmt::Display; -use yew::{function_component, html, Properties, Html, ToHtml}; +use yew::{function_component, html, Properties, Html}; #[derive(Properties, PartialEq)] pub struct Props @@ -23,11 +23,11 @@ where #[function_component] pub fn MyGenericComponent(props: &Props) -> Html where - T: PartialEq + ToHtml, + T: PartialEq + Clone + Into, { html! {

- { &props.data } + { props.data.clone().into() }

} }