yew/html/component/children.rs
1//! Component children module
2
3use std::fmt;
4use std::rc::Rc;
5
6use crate::html::Html;
7use crate::virtual_dom::{VChild, VComp, VList, VNode};
8use crate::{BaseComponent, Properties};
9
10/// A type used for accepting children elements in Component::Properties.
11///
12/// # Example
13/// **`model.rs`**
14///
15/// In this example, the `Wrapper` component is used to wrap other elements.
16/// ```
17/// # use yew::{Children, Html, Properties, Component, Context, html};
18/// # #[derive(Clone, Properties, PartialEq)]
19/// # struct WrapperProps {
20/// # children: Children,
21/// # }
22/// # struct Wrapper;
23/// # impl Component for Wrapper {
24/// # type Message = ();
25/// # type Properties = WrapperProps;
26/// # fn create(ctx: &Context<Self>) -> Self { Self }
27/// # fn view(&self, ctx: &Context<Self>) -> Html {
28/// html! {
29/// <Wrapper>
30/// <h4>{ "Hi" }</h4>
31/// <div>{ "Hello" }</div>
32/// </Wrapper>
33/// }
34/// # }
35/// # }
36/// ```
37///
38/// **`wrapper.rs`**
39///
40/// The Wrapper component must define a `children` property in order to wrap other elements. The
41/// children property can be used to render the wrapped elements.
42/// ```
43/// # use yew::{Children, Html, Properties, Component, Context, html};
44/// #[derive(Clone, Properties, PartialEq)]
45/// struct WrapperProps {
46/// children: Children,
47/// }
48///
49/// # struct Wrapper;
50/// impl Component for Wrapper {
51/// // ...
52/// # type Message = ();
53/// # type Properties = WrapperProps;
54/// # fn create(ctx: &Context<Self>) -> Self { Self }
55/// fn view(&self, ctx: &Context<Self>) -> Html {
56/// html! {
57/// <div id="container">
58/// { ctx.props().children.clone() }
59/// </div>
60/// }
61/// }
62/// }
63/// ```
64pub type Children = ChildrenRenderer<Html>;
65
66/// A type used for accepting children elements in Component::Properties and accessing their props.
67///
68/// # Example
69/// **`model.rs`**
70///
71/// In this example, the `List` component can wrap `ListItem` components.
72/// ```
73/// # use yew::{html, Component, Html, Context, ChildrenWithProps, Properties};
74/// #
75/// # #[derive(Clone, Properties, PartialEq)]
76/// # struct ListProps {
77/// # children: ChildrenWithProps<ListItem>,
78/// # }
79/// # struct List;
80/// # impl Component for List {
81/// # type Message = ();
82/// # type Properties = ListProps;
83/// # fn create(ctx: &Context<Self>) -> Self { Self }
84/// # fn view(&self, ctx: &Context<Self>) -> Html { unimplemented!() }
85/// # }
86/// # #[derive(Clone, Properties, PartialEq)]
87/// # struct ListItemProps {
88/// # value: String
89/// # }
90/// # struct ListItem;
91/// # impl Component for ListItem {
92/// # type Message = ();
93/// # type Properties = ListItemProps;
94/// # fn create(ctx: &Context<Self>) -> Self { Self }
95/// # fn view(&self, ctx: &Context<Self>) -> Html { unimplemented!() }
96/// # }
97/// # fn view() -> Html {
98/// html! {
99/// <List>
100/// <ListItem value="a" />
101/// <ListItem value="b" />
102/// <ListItem value="c" />
103/// </List>
104/// }
105/// # }
106/// ```
107///
108/// **`list.rs`**
109///
110/// The `List` component must define a `children` property in order to wrap the list items. The
111/// `children` property can be used to filter, mutate, and render the items.
112/// ```
113/// # use yew::{html, Component, Html, ChildrenWithProps, Context, Properties};
114/// # use std::rc::Rc;
115/// #
116/// #[derive(Clone, Properties, PartialEq)]
117/// struct ListProps {
118/// children: ChildrenWithProps<ListItem>,
119/// }
120///
121/// # struct List;
122/// impl Component for List {
123/// # type Message = ();
124/// # type Properties = ListProps;
125/// # fn create(ctx: &Context<Self>) -> Self { Self }
126/// # fn view(&self, ctx: &Context<Self>) -> Html {
127/// html!{{
128/// for ctx.props().children.iter().map(|mut item| {
129/// let mut props = Rc::make_mut(&mut item.props);
130/// props.value = format!("item-{}", props.value);
131/// item
132/// })
133/// }}
134/// }
135/// }
136/// #
137/// # #[derive(Clone, Properties, PartialEq)]
138/// # struct ListItemProps {
139/// # #[prop_or_default]
140/// # value: String
141/// # }
142/// #
143/// # struct ListItem;
144/// # impl Component for ListItem {
145/// # type Message = ();
146/// # type Properties = ListItemProps;
147/// # fn create(ctx: &Context<Self>) -> Self { Self }
148/// # fn view(&self, ctx: &Context<Self>) -> Html { unimplemented!() }
149/// # }
150/// ```
151pub type ChildrenWithProps<CHILD> = ChildrenRenderer<VChild<CHILD>>;
152
153/// A type used for rendering children html.
154#[derive(Clone)]
155pub struct ChildrenRenderer<T> {
156 pub(crate) children: Vec<T>,
157}
158
159impl<T: PartialEq> PartialEq for ChildrenRenderer<T> {
160 fn eq(&self, other: &Self) -> bool {
161 self.children == other.children
162 }
163}
164
165impl<T> ChildrenRenderer<T>
166where
167 T: Clone,
168{
169 /// Create children
170 pub fn new(children: Vec<T>) -> Self {
171 Self { children }
172 }
173
174 /// Children list is empty
175 pub fn is_empty(&self) -> bool {
176 self.children.is_empty()
177 }
178
179 /// Number of children elements
180 pub fn len(&self) -> usize {
181 self.children.len()
182 }
183
184 /// Render children components and return `Iterator`
185 pub fn iter(&self) -> impl Iterator<Item = T> + '_ {
186 // clone each child lazily.
187 // This way `self.iter().next()` only has to clone a single node.
188 self.children.iter().cloned()
189 }
190
191 /// Convert the children elements to another object (if there are any).
192 ///
193 /// ```
194 /// # let children = Children::new(Vec::new());
195 /// # use yew::{classes, html, Children};
196 /// # let _ =
197 /// children.map(|children| {
198 /// html! {
199 /// <div class={classes!("container")}>
200 /// {children.clone()}
201 /// </div>
202 /// }
203 /// })
204 /// # ;
205 /// ```
206 pub fn map<OUT: Default>(&self, closure: impl FnOnce(&Self) -> OUT) -> OUT {
207 if self.is_empty() {
208 Default::default()
209 } else {
210 closure(self)
211 }
212 }
213}
214
215impl<T> Default for ChildrenRenderer<T> {
216 fn default() -> Self {
217 Self {
218 children: Vec::new(),
219 }
220 }
221}
222
223impl<T> fmt::Debug for ChildrenRenderer<T> {
224 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
225 f.write_str("ChildrenRenderer<_>")
226 }
227}
228
229impl<T> IntoIterator for ChildrenRenderer<T> {
230 type IntoIter = std::vec::IntoIter<Self::Item>;
231 type Item = T;
232
233 fn into_iter(self) -> Self::IntoIter {
234 self.children.into_iter()
235 }
236}
237
238impl From<ChildrenRenderer<Html>> for Html {
239 fn from(mut val: ChildrenRenderer<Html>) -> Self {
240 if val.children.len() == 1 {
241 if let Some(m) = val.children.pop() {
242 return m;
243 }
244 }
245
246 Html::VList(Rc::new(val.into()))
247 }
248}
249
250impl From<ChildrenRenderer<Html>> for VList {
251 fn from(val: ChildrenRenderer<Html>) -> Self {
252 if val.is_empty() {
253 return VList::new();
254 }
255 VList::with_children(val.children, None)
256 }
257}
258
259impl<COMP> From<ChildrenRenderer<VChild<COMP>>> for ChildrenRenderer<Html>
260where
261 COMP: BaseComponent,
262{
263 fn from(value: ChildrenRenderer<VChild<COMP>>) -> Self {
264 Self::new(
265 value
266 .into_iter()
267 .map(VComp::from)
268 .map(VNode::from)
269 .collect(),
270 )
271 }
272}
273
274/// A [Properties] type with Children being the only property.
275#[derive(Debug, Properties, PartialEq)]
276pub struct ChildrenProps {
277 /// The Children of a Component.
278 #[prop_or_default]
279 pub children: Html,
280}
281
282#[cfg(test)]
283mod tests {
284 use super::*;
285
286 #[test]
287 fn children_map() {
288 let children = Children::new(vec![]);
289 let res = children.map(|children| Some(children.clone()));
290 assert!(res.is_none());
291 let children = Children::new(vec![Default::default()]);
292 let res = children.map(|children| Some(children.clone()));
293 assert!(res.is_some());
294 }
295}