react_rs_elements/
node.rs1use 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}