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