windjammer_ui/
simple_renderer.rs

1//! Simple UI renderer for basic elements
2//! This provides a minimal but functional UI rendering system
3
4use crate::vdom::{VElement, VNode};
5
6/// Simple UI renderer that converts VNodes to console output
7/// This is a minimal implementation for testing and demos
8pub struct SimpleRenderer {
9    output: Vec<String>,
10}
11
12impl SimpleRenderer {
13    pub fn new() -> Self {
14        Self { output: Vec::new() }
15    }
16
17    /// Render a VNode tree to console output
18    pub fn render(&mut self, vnode: &VNode) {
19        self.output.clear();
20        self.render_node(vnode, 0);
21    }
22
23    fn render_node(&mut self, vnode: &VNode, depth: usize) {
24        let indent = "  ".repeat(depth);
25
26        match vnode {
27            VNode::Element(element) => {
28                self.render_element(element, depth);
29            }
30            VNode::Text(text) => {
31                self.output
32                    .push(format!("{}Text: {}", indent, text.content));
33            }
34            VNode::Component(component) => {
35                self.output
36                    .push(format!("{}Component: {}", indent, component.name));
37            }
38            VNode::Empty => {
39                // Empty nodes don't render anything
40            }
41        }
42    }
43
44    fn render_element(&mut self, element: &VElement, depth: usize) {
45        let indent = "  ".repeat(depth);
46
47        // Render opening tag
48        let mut tag_str = format!("{}<{}", indent, element.tag);
49
50        // Add attributes
51        for (key, value) in &element.attrs {
52            tag_str.push_str(&format!(" {}=\"{}\"", key, value));
53        }
54        tag_str.push('>');
55
56        self.output.push(tag_str);
57
58        // Render children
59        for child in &element.children {
60            self.render_node(child, depth + 1);
61        }
62
63        // Render closing tag
64        self.output.push(format!("{}</{}>", indent, element.tag));
65    }
66
67    /// Get the rendered output as a string
68    pub fn get_output(&self) -> String {
69        self.output.join("\n")
70    }
71
72    /// Print the rendered output
73    pub fn print(&self) {
74        println!("{}", self.get_output());
75    }
76}
77
78impl Default for SimpleRenderer {
79    fn default() -> Self {
80        Self::new()
81    }
82}
83
84#[cfg(test)]
85mod tests {
86    use super::*;
87    use crate::vdom::{VComponent, VElement, VText};
88    use std::collections::HashMap;
89
90    #[test]
91    fn test_render_text() {
92        let mut renderer = SimpleRenderer::new();
93        let vnode = VNode::Text(VText::new("Hello, World!"));
94
95        renderer.render(&vnode);
96        let output = renderer.get_output();
97
98        assert!(output.contains("Hello, World!"));
99    }
100
101    #[test]
102    fn test_render_element() {
103        let mut renderer = SimpleRenderer::new();
104        let element = VElement {
105            tag: "div".to_string(),
106            attrs: HashMap::new(),
107            children: vec![VNode::Text(VText::new("Content"))],
108        };
109
110        renderer.render(&VNode::Element(element));
111        let output = renderer.get_output();
112
113        assert!(output.contains("<div>"));
114        assert!(output.contains("Content"));
115        assert!(output.contains("</div>"));
116    }
117
118    #[test]
119    fn test_render_component() {
120        let mut renderer = SimpleRenderer::new();
121        let component = VComponent {
122            name: "Counter".to_string(),
123            props: HashMap::new(),
124        };
125
126        renderer.render(&VNode::Component(component));
127        let output = renderer.get_output();
128
129        assert!(output.contains("Component: Counter"));
130    }
131}
132
133/// Render a VNode to an HTML string (for WASM/web rendering)
134#[cfg(target_arch = "wasm32")]
135pub fn render_to_html(vnode: &crate::simple_vnode::VNode) -> String {
136    use crate::simple_vnode::{VAttr, VNode};
137
138    match vnode {
139        VNode::Element {
140            tag,
141            attrs,
142            children,
143        } => {
144            let mut html = format!("<{}", tag);
145            for (key, value) in attrs {
146                match value {
147                    VAttr::Static(v) | VAttr::Dynamic(v) => {
148                        html.push_str(&format!(" {}=\"{}\"", key, v));
149                    }
150                    VAttr::Event(_) => {
151                        // Skip event handlers in HTML rendering
152                    }
153                }
154            }
155            html.push('>');
156            for child in children {
157                html.push_str(&render_to_html(child));
158            }
159            html.push_str(&format!("</{}>", tag));
160            html
161        }
162        VNode::Text(text) => text.clone(),
163    }
164}