1use crate::component::Component;
14use crate::events::ComponentEventDispatcher;
15use crate::vdom::VNode;
16use std::cell::RefCell;
17use std::rc::Rc;
18
19pub struct ComponentRuntime<C: Component> {
21 #[allow(dead_code)]
22 component: Rc<RefCell<C>>,
23 #[allow(dead_code)]
24 current_vnode: Rc<RefCell<Option<VNode>>>,
25 event_dispatcher: Rc<RefCell<ComponentEventDispatcher>>,
26 #[cfg(target_arch = "wasm32")]
27 root_element: Option<web_sys::Element>,
28}
29
30impl<C: Component + 'static> ComponentRuntime<C> {
31 pub fn new(component: C) -> Self {
33 Self {
34 component: Rc::new(RefCell::new(component)),
35 current_vnode: Rc::new(RefCell::new(None)),
36 event_dispatcher: Rc::new(RefCell::new(ComponentEventDispatcher::new())),
37 #[cfg(target_arch = "wasm32")]
38 root_element: None,
39 }
40 }
41
42 #[cfg(target_arch = "wasm32")]
44 pub fn mount(&mut self, target: web_sys::Element) -> Result<(), String> {
45 use crate::renderer::WebRenderer;
46
47 let vnode = self.component.borrow().render();
49
50 let renderer = WebRenderer::new();
52 let dom_node = renderer.create_element(&vnode)?;
53
54 while let Some(child) = target.first_child() {
56 target
57 .remove_child(&child)
58 .map_err(|_| "Failed to clear target")?;
59 }
60
61 target
62 .append_child(&dom_node)
63 .map_err(|_| "Failed to mount component")?;
64
65 *self.current_vnode.borrow_mut() = Some(vnode);
67 self.root_element = Some(target);
68
69 Ok(())
70 }
71
72 #[cfg(target_arch = "wasm32")]
74 pub fn re_render(&self) -> Result<(), String> {
75 use crate::renderer::{Renderer, WebRenderer};
76 use crate::vdom::diff;
77
78 let new_vnode = self.component.borrow().render();
79
80 let old_vnode = self.current_vnode.borrow();
82
83 if let Some(old) = old_vnode.as_ref() {
84 let patches = diff(old, &new_vnode);
86
87 if !patches.is_empty() {
89 let mut renderer = WebRenderer::new();
90 renderer.patch(&patches)?;
91 }
92 } else {
93 if let Some(root) = &self.root_element {
95 let renderer = WebRenderer::new();
96 let dom_node = renderer.create_element(&new_vnode)?;
97 root.append_child(&dom_node)
98 .map_err(|_| "Failed to append new content")?;
99 }
100 }
101
102 drop(old_vnode); *self.current_vnode.borrow_mut() = Some(new_vnode);
105
106 Ok(())
107 }
108
109 pub fn register_event<F>(&mut self, event_name: String, handler: F)
111 where
112 F: Fn() + 'static,
113 {
114 self.event_dispatcher
115 .borrow_mut()
116 .register(event_name, handler);
117 }
118
119 pub fn dispatcher(&self) -> Rc<RefCell<ComponentEventDispatcher>> {
121 self.event_dispatcher.clone()
122 }
123}
124
125#[cfg(test)]
126mod tests {
127 use super::*;
128 use crate::vdom::{VElement, VNode, VText};
129
130 struct TestComponent {
131 count: i32,
132 }
133
134 impl Component for TestComponent {
135 fn render(&self) -> VNode {
136 VElement::new("div")
137 .child(VNode::Text(VText::new(format!("Count: {}", self.count))))
138 .into()
139 }
140 }
141
142 #[test]
143 fn test_runtime_creation() {
144 let component = TestComponent { count: 0 };
145 let runtime = ComponentRuntime::new(component);
146 assert!(runtime.current_vnode.borrow().is_none());
147 }
148
149 #[test]
150 fn test_event_registration() {
151 let component = TestComponent { count: 0 };
152 let mut runtime = ComponentRuntime::new(component);
153
154 let called = Rc::new(RefCell::new(false));
155 let called_clone = called.clone();
156
157 runtime.register_event("click".to_string(), move || {
158 *called_clone.borrow_mut() = true;
159 });
160
161 assert!(!*called.borrow());
162 runtime.dispatcher().borrow().dispatch("click").unwrap();
163 assert!(*called.borrow());
164 }
165}