Skip to main content

react_rs_elements/
node.rs

1use crate::head::Head;
2use crate::reactive::ReactiveValue;
3use crate::suspense::{ErrorBoundaryData, SuspenseData};
4use crate::Element;
5use std::rc::Rc;
6
7pub enum Node {
8    Element(Element),
9    Text(String),
10    ReactiveText(ReactiveValue<String>),
11    Fragment(Vec<Node>),
12    Conditional(ReactiveValue<bool>, Box<Node>, Option<Box<Node>>),
13    ReactiveList(Rc<dyn Fn() -> Vec<Node>>),
14    Head(Head),
15    Suspense(SuspenseData),
16    ErrorBoundary(ErrorBoundaryData),
17}
18
19pub trait IntoNode {
20    fn into_node(self) -> Node;
21}
22
23impl IntoNode for Element {
24    fn into_node(self) -> Node {
25        Node::Element(self)
26    }
27}
28
29impl IntoNode for String {
30    fn into_node(self) -> Node {
31        Node::Text(self)
32    }
33}
34
35impl IntoNode for &str {
36    fn into_node(self) -> Node {
37        Node::Text(self.to_string())
38    }
39}
40
41impl<T: IntoNode> IntoNode for Vec<T> {
42    fn into_node(self) -> Node {
43        Node::Fragment(self.into_iter().map(|n| n.into_node()).collect())
44    }
45}
46
47impl IntoNode for Node {
48    fn into_node(self) -> Node {
49        self
50    }
51}
52
53impl IntoNode for Head {
54    fn into_node(self) -> Node {
55        Node::Head(self)
56    }
57}
58
59pub fn each<T, F>(items: react_rs_core::signal::ReadSignal<Vec<T>>, render: F) -> Node
60where
61    T: Clone + 'static,
62    F: Fn(&T, usize) -> Node + 'static,
63{
64    Node::ReactiveList(Rc::new(move || {
65        items.with(|list| {
66            list.iter()
67                .enumerate()
68                .map(|(i, item)| render(item, i))
69                .collect()
70        })
71    }))
72}
73
74#[cfg(test)]
75mod tests {
76    use super::*;
77    use crate::html;
78    use react_rs_core::create_signal;
79
80    #[test]
81    fn test_conditional_creates_node() {
82        let node = html::div().text("test").show_when(true);
83        assert!(matches!(node, Node::Conditional(_, _, None)));
84    }
85
86    #[test]
87    fn test_conditional_else_creates_node() {
88        let node = html::div()
89            .text("yes")
90            .show_when_else(false, html::span().text("no"));
91        assert!(matches!(node, Node::Conditional(_, _, Some(_))));
92    }
93
94    #[test]
95    fn test_each_creates_reactive_list() {
96        let (items, _) = create_signal(vec![1, 2, 3]);
97        let node = each(items, |item, _| {
98            html::li().text(item.to_string()).into_node()
99        });
100        assert!(matches!(node, Node::ReactiveList(_)));
101    }
102}