1use std::cell::RefCell;
2use std::rc::Rc;
3use wasm_bindgen::prelude::*;
4use web_sys::*;
5
6pub mod mount;
7pub mod patch;
8pub mod children;
9
10#[derive(Clone, Debug, PartialEq)]
17pub enum VNodeType {
18 Element(String), Text,
20 Fragment,
21 Dynamic,
22 Empty,
23}
24
25pub enum VNode {
31 Element(VElement),
33 Text(String),
35 Fragment(Vec<VNode>),
37 Dynamic(
39 Rc<RefCell<Box<dyn Fn() -> VNode>>>,
40 Rc<RefCell<Option<web_sys::Node>>>,
41 ),
42 Empty,
44}
45
46impl VNode {
47 pub fn element(tag: &str) -> VElementBuilder {
49 VElementBuilder::new(tag)
50 }
51
52 pub fn text(content: &str) -> Self {
54 VNode::Text(content.to_string())
55 }
56
57 pub fn fragment(children: Vec<VNode>) -> Self {
59 VNode::Fragment(children)
60 }
61
62 pub fn empty() -> Self {
64 VNode::Empty
65 }
66
67 pub fn node_type(&self) -> VNodeType {
73 match self {
74 VNode::Element(el) => VNodeType::Element(el.tag.clone()),
75 VNode::Text(_) => VNodeType::Text,
76 VNode::Fragment(_) => VNodeType::Fragment,
77 VNode::Dynamic(_, _) => VNodeType::Dynamic,
78 VNode::Empty => VNodeType::Empty,
79 }
80 }
81
82 pub fn same_type(&self, other: &VNode) -> bool {
86 match (self, other) {
87 (VNode::Element(a), VNode::Element(b)) => a.tag == b.tag,
88 (VNode::Text(_), VNode::Text(_)) => true,
89 (VNode::Fragment(_), VNode::Fragment(_)) => true,
90 (VNode::Dynamic(_, _), VNode::Dynamic(_, _)) => true,
91 (VNode::Empty, VNode::Empty) => true,
92 _ => false,
93 }
94 }
95
96 pub fn key(&self) -> Option<&str> {
99 match self {
100 VNode::Element(el) => el.key.as_deref(),
101 _ => None,
102 }
103 }
104
105 pub fn dom_node(&self) -> Option<web_sys::Node> {
108 match self {
109 VNode::Element(el) => el.dom_ref.borrow().clone(),
110 VNode::Dynamic(_, dom_ref) => dom_ref.borrow().clone(),
111 VNode::Text(_) | VNode::Fragment(_) | VNode::Empty => None,
112 }
113 }
114
115 pub fn set_dom_node(&self, node: web_sys::Node) {
117 match self {
118 VNode::Element(el) => {
119 *el.dom_ref.borrow_mut() = Some(node);
120 }
121 VNode::Dynamic(_, dom_ref) => {
122 *dom_ref.borrow_mut() = Some(node);
123 }
124 VNode::Text(_) | VNode::Fragment(_) | VNode::Empty => {
125 }
128 }
129 }
130
131 pub fn keyed_children(&self) -> Vec<(usize, &VNode)> {
134 match self {
135 VNode::Element(el) => el
136 .children
137 .iter()
138 .enumerate()
139 .filter(|(_, c)| c.key().is_some())
140 .collect(),
141 _ => vec![],
142 }
143 }
144}
145
146pub struct VElement {
152 pub tag: String,
153 pub attrs: Vec<(&'static str, String)>,
154 pub events: Vec<(&'static str, EventHandler)>,
155 pub children: Vec<VNode>,
156 pub key: Option<String>,
157 pub dom_ref: Rc<RefCell<Option<web_sys::Node>>>,
159 pub listener_closures: Rc<RefCell<Vec<(&'static str, Closure<dyn FnMut(web_sys::Event)>)>>>,
162}
163
164pub struct EventHandler(Rc<dyn Fn(web_sys::Event)>);
170
171impl EventHandler {
172 pub fn new<F: Fn(web_sys::Event) + 'static>(f: F) -> Self {
173 EventHandler(Rc::new(f))
174 }
175
176 pub fn call(&self, event: web_sys::Event) {
177 (self.0)(event)
178 }
179}
180
181impl Clone for EventHandler {
182 fn clone(&self) -> Self {
183 EventHandler(self.0.clone())
184 }
185}
186
187pub struct VElementBuilder {
193 tag: String,
194 attrs: Vec<(&'static str, String)>,
195 events: Vec<(&'static str, EventHandler)>,
196 children: Vec<VNode>,
197 key: Option<String>,
198}
199
200impl VElementBuilder {
201 fn new(tag: &str) -> Self {
202 VElementBuilder {
203 tag: tag.to_string(),
204 attrs: Vec::new(),
205 events: Vec::new(),
206 children: Vec::new(),
207 key: None,
208 }
209 }
210
211 pub fn attr(mut self, name: &'static str, value: &str) -> Self {
213 self.attrs.push((name, value.to_string()));
214 self
215 }
216
217 pub fn class(mut self, value: &str) -> Self {
219 self.attrs.push(("class", value.to_string()));
220 self
221 }
222
223 pub fn id(mut self, value: &str) -> Self {
225 self.attrs.push(("id", value.to_string()));
226 self
227 }
228
229 pub fn key(mut self, value: &str) -> Self {
231 self.key = Some(value.to_string());
232 self
233 }
234
235 pub fn on<F: Fn(web_sys::Event) + 'static>(mut self, event: &'static str, handler: F) -> Self {
237 self.events.push((event, EventHandler::new(handler)));
238 self
239 }
240
241 pub fn child(mut self, child: VNode) -> Self {
243 self.children.push(child);
244 self
245 }
246
247 pub fn children(mut self, children: Vec<VNode>) -> Self {
249 self.children.extend(children);
250 self
251 }
252
253 pub fn text(mut self, content: &str) -> Self {
255 self.children.push(VNode::Text(content.to_string()));
256 self
257 }
258
259 pub fn build(self) -> VNode {
261 VNode::Element(VElement {
262 tag: self.tag,
263 attrs: self.attrs,
264 events: self.events,
265 children: self.children,
266 key: self.key,
267 dom_ref: Rc::new(RefCell::new(None)),
268 listener_closures: Rc::new(RefCell::new(Vec::new())),
269 })
270 }
271}
272
273pub fn render_to_dom(vnode: &VNode) -> Option<web_sys::Node> {
281 let document = web_sys::window()?.document()?;
283 let temp = document.create_element("div").ok()?;
284 let parent: web_sys::Node = temp.into();
285 mount::mount_to_dom(vnode, &parent, None)
286}