tuiuiu/primitives/
control_flow.rs

1//! Control Flow Components
2//!
3//! Conditional rendering and iteration helpers.
4
5use crate::core::component::{VNode, Child, children_to_vnodes};
6
7/// Conditional rendering - shows children only when condition is true.
8pub struct When {
9    condition: bool,
10    children: Vec<VNode>,
11    fallback: Option<Vec<VNode>>,
12}
13
14impl When {
15    /// Create a When component.
16    pub fn new(condition: bool) -> Self {
17        Self {
18            condition,
19            children: Vec::new(),
20            fallback: None,
21        }
22    }
23
24    /// Set children to show when true.
25    pub fn children<I, C>(mut self, children: I) -> Self
26    where
27        I: IntoIterator<Item = C>,
28        C: Into<Child>,
29    {
30        self.children = children_to_vnodes(children);
31        self
32    }
33
34    /// Set fallback to show when false.
35    pub fn fallback<I, C>(mut self, children: I) -> Self
36    where
37        I: IntoIterator<Item = C>,
38        C: Into<Child>,
39    {
40        self.fallback = Some(children_to_vnodes(children));
41        self
42    }
43
44    /// Build into a VNode.
45    pub fn build(self) -> VNode {
46        if self.condition {
47            VNode::Fragment(self.children)
48        } else if let Some(fallback) = self.fallback {
49            VNode::Fragment(fallback)
50        } else {
51            VNode::Empty
52        }
53    }
54}
55
56impl From<When> for VNode {
57    fn from(w: When) -> VNode {
58        w.build()
59    }
60}
61
62/// Iterate and render items.
63pub struct Each<T, F>
64where
65    F: Fn(&T, usize) -> VNode,
66{
67    items: Vec<T>,
68    render: F,
69}
70
71impl<T, F> Each<T, F>
72where
73    F: Fn(&T, usize) -> VNode,
74{
75    /// Create an Each component.
76    pub fn new<I: IntoIterator<Item = T>>(items: I, render: F) -> Self {
77        Self {
78            items: items.into_iter().collect(),
79            render,
80        }
81    }
82
83    /// Build into a VNode.
84    pub fn build(self) -> VNode {
85        let children: Vec<_> = self.items
86            .iter()
87            .enumerate()
88            .map(|(i, item)| (self.render)(item, i))
89            .collect();
90        VNode::Fragment(children)
91    }
92}
93
94impl<T, F> From<Each<T, F>> for VNode
95where
96    F: Fn(&T, usize) -> VNode,
97{
98    fn from(e: Each<T, F>) -> VNode {
99        e.build()
100    }
101}
102
103/// Transform children with a function.
104pub struct Transform<F>
105where
106    F: Fn(VNode) -> VNode,
107{
108    transform: F,
109    children: Vec<VNode>,
110}
111
112impl<F> Transform<F>
113where
114    F: Fn(VNode) -> VNode,
115{
116    /// Create a Transform component.
117    pub fn new(transform: F) -> Self {
118        Self {
119            transform,
120            children: Vec::new(),
121        }
122    }
123
124    /// Set children.
125    pub fn children<I, C>(mut self, children: I) -> Self
126    where
127        I: IntoIterator<Item = C>,
128        C: Into<Child>,
129    {
130        self.children = children_to_vnodes(children);
131        self
132    }
133
134    /// Build into a VNode.
135    pub fn build(self) -> VNode {
136        let transformed: Vec<_> = self.children
137            .into_iter()
138            .map(|c| (self.transform)(c))
139            .collect();
140        VNode::Fragment(transformed)
141    }
142}
143
144/// Static content that never re-renders.
145pub struct Static {
146    children: Vec<VNode>,
147}
148
149impl Static {
150    /// Create a Static component.
151    pub fn new() -> Self {
152        Self { children: Vec::new() }
153    }
154
155    /// Set children.
156    pub fn children<I, C>(mut self, children: I) -> Self
157    where
158        I: IntoIterator<Item = C>,
159        C: Into<Child>,
160    {
161        self.children = children_to_vnodes(children);
162        self
163    }
164
165    /// Build into a VNode.
166    pub fn build(self) -> VNode {
167        VNode::Fragment(self.children)
168    }
169}
170
171impl Default for Static {
172    fn default() -> Self {
173        Self::new()
174    }
175}
176
177impl From<Static> for VNode {
178    fn from(s: Static) -> VNode {
179        s.build()
180    }
181}
182
183/// Named slot for content injection.
184pub struct Slot {
185    #[allow(dead_code)]
186    name: String,
187    default: Option<Vec<VNode>>,
188}
189
190impl Slot {
191    /// Create a named slot.
192    pub fn new(name: impl Into<String>) -> Self {
193        Self {
194            name: name.into(),
195            default: None,
196        }
197    }
198
199    /// Set default content.
200    pub fn default<I, C>(mut self, children: I) -> Self
201    where
202        I: IntoIterator<Item = C>,
203        C: Into<Child>,
204    {
205        self.default = Some(children_to_vnodes(children));
206        self
207    }
208
209    /// Build into a VNode (returns default if no injection).
210    pub fn build(self) -> VNode {
211        if let Some(default) = self.default {
212            VNode::Fragment(default)
213        } else {
214            VNode::Empty
215        }
216    }
217}
218
219impl From<Slot> for VNode {
220    fn from(s: Slot) -> VNode {
221        s.build()
222    }
223}
224
225/// Helper function for conditional rendering.
226pub fn when(condition: bool) -> When {
227    When::new(condition)
228}
229
230/// Helper function for iteration.
231pub fn each<T, I, F>(items: I, render: F) -> Each<T, F>
232where
233    I: IntoIterator<Item = T>,
234    F: Fn(&T, usize) -> VNode,
235{
236    Each::new(items, render)
237}