mirror of
https://github.com/yewstack/yew.git
synced 2025-12-08 21:26:25 +00:00
Allow VNode props to be converted to Children. (#2749)
* Allow VNode props to be converted to Children. * Also implement ChildrenWithProps as well. * Update macro tests.
This commit is contained in:
parent
59beb2ad01
commit
a5973bdca6
@ -349,7 +349,7 @@ error[E0277]: the trait bound `{integer}: IntoPropValue<String>` is not satisfie
|
||||
<&'static str as IntoPropValue<Classes>>
|
||||
<&'static str as IntoPropValue<Option<AttrValue>>>
|
||||
<&'static str as IntoPropValue<Option<String>>>
|
||||
and 18 others
|
||||
and 27 others
|
||||
|
||||
error[E0277]: the trait bound `{integer}: IntoPropValue<String>` is not satisfied
|
||||
--> tests/html_macro/component-fail.rs:79:34
|
||||
@ -362,7 +362,7 @@ error[E0277]: the trait bound `{integer}: IntoPropValue<String>` is not satisfie
|
||||
<&'static str as IntoPropValue<Classes>>
|
||||
<&'static str as IntoPropValue<Option<AttrValue>>>
|
||||
<&'static str as IntoPropValue<Option<String>>>
|
||||
and 18 others
|
||||
and 27 others
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> tests/html_macro/component-fail.rs:80:31
|
||||
|
||||
@ -305,7 +305,7 @@ error[E0277]: the trait bound `Option<NotToString>: IntoPropValue<Option<AttrVal
|
||||
<Option<&'static str> as IntoPropValue<Option<String>>>
|
||||
<Option<F> as IntoPropValue<Option<yew::Callback<I, O>>>>
|
||||
<Option<String> as IntoPropValue<Option<AttrValue>>>
|
||||
<Option<std::rc::Rc<str>> as IntoPropValue<Option<AttrValue>>>
|
||||
and 4 others
|
||||
note: required by `into_prop_value`
|
||||
--> $WORKSPACE/packages/yew/src/html/conversion.rs
|
||||
|
|
||||
@ -323,7 +323,7 @@ error[E0277]: the trait bound `Option<{integer}>: IntoPropValue<Option<AttrValue
|
||||
<Option<&'static str> as IntoPropValue<Option<String>>>
|
||||
<Option<F> as IntoPropValue<Option<yew::Callback<I, O>>>>
|
||||
<Option<String> as IntoPropValue<Option<AttrValue>>>
|
||||
<Option<std::rc::Rc<str>> as IntoPropValue<Option<AttrValue>>>
|
||||
and 4 others
|
||||
note: required by `into_prop_value`
|
||||
--> $WORKSPACE/packages/yew/src/html/conversion.rs
|
||||
|
|
||||
@ -420,7 +420,7 @@ error[E0277]: the trait bound `Option<yew::NodeRef>: IntoPropValue<yew::NodeRef>
|
||||
<Option<&'static str> as IntoPropValue<Option<String>>>
|
||||
<Option<F> as IntoPropValue<Option<yew::Callback<I, O>>>>
|
||||
<Option<String> as IntoPropValue<Option<AttrValue>>>
|
||||
<Option<std::rc::Rc<str>> as IntoPropValue<Option<AttrValue>>>
|
||||
and 4 others
|
||||
note: required by `into_prop_value`
|
||||
--> $WORKSPACE/packages/yew/src/html/conversion.rs
|
||||
|
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
use super::super::callback::Callback;
|
||||
use super::{Component, NodeRef, Scope};
|
||||
use crate::virtual_dom::AttrValue;
|
||||
use super::{BaseComponent, Children, ChildrenRenderer, Component, NodeRef, Scope};
|
||||
use crate::virtual_dom::{AttrValue, VChild, VNode};
|
||||
|
||||
/// Marker trait for types that the [`html!`](macro@crate::html) macro may clone implicitly.
|
||||
pub trait ImplicitClone: Clone {}
|
||||
@ -40,6 +40,7 @@ impl<T> IntoPropValue<T> for T {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> IntoPropValue<T> for &T
|
||||
where
|
||||
T: ImplicitClone,
|
||||
@ -56,6 +57,7 @@ impl<T> IntoPropValue<Option<T>> for T {
|
||||
Some(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> IntoPropValue<Option<T>> for &T
|
||||
where
|
||||
T: ImplicitClone,
|
||||
@ -70,6 +72,7 @@ impl<I, O, F> IntoPropValue<Callback<I, O>> for F
|
||||
where
|
||||
F: 'static + Fn(I) -> O,
|
||||
{
|
||||
#[inline]
|
||||
fn into_prop_value(self) -> Callback<I, O> {
|
||||
Callback::from(self)
|
||||
}
|
||||
@ -79,6 +82,7 @@ impl<I, O, F> IntoPropValue<Option<Callback<I, O>>> for F
|
||||
where
|
||||
F: 'static + Fn(I) -> O,
|
||||
{
|
||||
#[inline]
|
||||
fn into_prop_value(self) -> Option<Callback<I, O>> {
|
||||
Some(Callback::from(self))
|
||||
}
|
||||
@ -88,8 +92,69 @@ impl<I, O, F> IntoPropValue<Option<Callback<I, O>>> for Option<F>
|
||||
where
|
||||
F: 'static + Fn(I) -> O,
|
||||
{
|
||||
#[inline]
|
||||
fn into_prop_value(self) -> Option<Callback<I, O>> {
|
||||
self.map(|f| Callback::from(f))
|
||||
self.map(Callback::from)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> IntoPropValue<ChildrenRenderer<VChild<T>>> for VChild<T>
|
||||
where
|
||||
T: BaseComponent,
|
||||
{
|
||||
#[inline]
|
||||
fn into_prop_value(self) -> ChildrenRenderer<VChild<T>> {
|
||||
ChildrenRenderer::new(vec![self])
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> IntoPropValue<Option<ChildrenRenderer<VChild<T>>>> for VChild<T>
|
||||
where
|
||||
T: BaseComponent,
|
||||
{
|
||||
#[inline]
|
||||
fn into_prop_value(self) -> Option<ChildrenRenderer<VChild<T>>> {
|
||||
Some(ChildrenRenderer::new(vec![self]))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> IntoPropValue<Option<ChildrenRenderer<VChild<T>>>> for Option<VChild<T>>
|
||||
where
|
||||
T: BaseComponent,
|
||||
{
|
||||
#[inline]
|
||||
fn into_prop_value(self) -> Option<ChildrenRenderer<VChild<T>>> {
|
||||
self.map(|m| ChildrenRenderer::new(vec![m]))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> IntoPropValue<ChildrenRenderer<VChild<T>>> for Vec<VChild<T>>
|
||||
where
|
||||
T: BaseComponent,
|
||||
{
|
||||
#[inline]
|
||||
fn into_prop_value(self) -> ChildrenRenderer<VChild<T>> {
|
||||
ChildrenRenderer::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> IntoPropValue<Option<ChildrenRenderer<VChild<T>>>> for Vec<VChild<T>>
|
||||
where
|
||||
T: BaseComponent,
|
||||
{
|
||||
#[inline]
|
||||
fn into_prop_value(self) -> Option<ChildrenRenderer<VChild<T>>> {
|
||||
Some(ChildrenRenderer::new(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> IntoPropValue<Option<ChildrenRenderer<VChild<T>>>> for Option<Vec<VChild<T>>>
|
||||
where
|
||||
T: BaseComponent,
|
||||
{
|
||||
#[inline]
|
||||
fn into_prop_value(self) -> Option<ChildrenRenderer<VChild<T>>> {
|
||||
self.map(ChildrenRenderer::new)
|
||||
}
|
||||
}
|
||||
|
||||
@ -127,6 +192,7 @@ impl_into_prop!(|value: &'static str| -> String { value.to_owned() });
|
||||
impl_into_prop!(|value: &'static str| -> AttrValue { AttrValue::Static(value) });
|
||||
impl_into_prop!(|value: String| -> AttrValue { AttrValue::Rc(Rc::from(value)) });
|
||||
impl_into_prop!(|value: Rc<str>| -> AttrValue { AttrValue::Rc(value) });
|
||||
impl_into_prop!(|value: VNode| -> Children { Children::new(vec![value]) });
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
@ -150,4 +216,105 @@ mod test {
|
||||
let _: Option<Callback<String, String>> = (|s: String| s).into_prop_value();
|
||||
let _: Option<Callback<String, String>> = Some(|s: String| s).into_prop_value();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_html_to_children_compiles() {
|
||||
use crate::prelude::*;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Properties)]
|
||||
pub struct Props {
|
||||
#[prop_or_default]
|
||||
pub header: Children,
|
||||
#[prop_or_default]
|
||||
pub children: Children,
|
||||
#[prop_or_default]
|
||||
pub footer: Children,
|
||||
}
|
||||
|
||||
#[function_component]
|
||||
pub fn App(props: &Props) -> Html {
|
||||
let Props {
|
||||
header,
|
||||
children,
|
||||
footer,
|
||||
} = props.clone();
|
||||
|
||||
html! {
|
||||
<div>
|
||||
<header>
|
||||
{header}
|
||||
</header>
|
||||
<main>
|
||||
{children}
|
||||
</main>
|
||||
<footer>
|
||||
{footer}
|
||||
</footer>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
let header = html! { <div>{"header"}</div> };
|
||||
let footer = html! { <div>{"footer"}</div> };
|
||||
let children = html! { <div>{"main"}</div> };
|
||||
|
||||
let _ = html! {
|
||||
<App {header} {footer}>
|
||||
{children}
|
||||
</App>
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vchild_to_children_with_props_compiles() {
|
||||
use crate::prelude::*;
|
||||
|
||||
#[function_component]
|
||||
pub fn Comp() -> Html {
|
||||
Html::default()
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Properties)]
|
||||
pub struct Props {
|
||||
#[prop_or_default]
|
||||
pub header: ChildrenWithProps<Comp>,
|
||||
#[prop_or_default]
|
||||
pub children: Children,
|
||||
#[prop_or_default]
|
||||
pub footer: ChildrenWithProps<Comp>,
|
||||
}
|
||||
|
||||
#[function_component]
|
||||
pub fn App(props: &Props) -> Html {
|
||||
let Props {
|
||||
header,
|
||||
children,
|
||||
footer,
|
||||
} = props.clone();
|
||||
|
||||
html! {
|
||||
<div>
|
||||
<header>
|
||||
{header}
|
||||
</header>
|
||||
<main>
|
||||
{children}
|
||||
</main>
|
||||
<footer>
|
||||
{footer}
|
||||
</footer>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
let header = VChild::new((), NodeRef::default(), None);
|
||||
let footer = html_nested! { <Comp /> };
|
||||
let children = html! { <div>{"main"}</div> };
|
||||
|
||||
let _ = html! {
|
||||
<App {header} {footer}>
|
||||
{children}
|
||||
</App>
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user