1#[cfg(feature = "web")]
5pub use self::create_element::VIRTUAL_NODE_MARKER_PROPERTY;
6#[cfg(feature = "web")]
7pub use self::event::EventAttribFn;
8pub use self::iterable_nodes::*;
9pub use self::velement::*;
10pub use self::vtext::*;
11use crate::event::{EventHandler, RealDom};
12use std::fmt;
13
14pub mod event;
15pub mod test_utils;
16
17#[cfg(feature = "web")]
18mod create_element;
19
20mod iterable_nodes;
21mod velement;
22mod vtext;
23
24#[cfg(feature = "web")]
26pub type VirtualNodeWebSys = VirtualNode<web_sys::Window>;
27
28pub enum VirtualNode<Dom: RealDom> {
47 Element(VirtualElement<Dom>),
49 Text(VirtualText),
55}
56
57impl<Dom: RealDom> PartialEq for VirtualNode<Dom> {
58 fn eq(&self, other: &Self) -> bool {
59 match (self, other) {
60 (Self::Element(lhs), Self::Element(rhs)) => lhs == rhs,
61 (Self::Text(lhs), Self::Text(rhs)) => lhs == rhs,
62 _ => false,
63 }
64 }
65}
66
67impl<Dom: RealDom> VirtualNode<Dom> {
68 pub fn new_element<S>(tag: S) -> Self
77 where
78 S: Into<String>,
79 {
80 VirtualNode::Element(VirtualElement::new(tag))
81 }
82
83 pub fn new_text<S>(text: S) -> Self
92 where
93 S: Into<String>,
94 {
95 VirtualNode::Text(VirtualText::new(text.into()))
96 }
97
98 pub fn as_elem(&self) -> Option<&VirtualElement<Dom>> {
103 match self {
104 VirtualNode::Element(ref element_node) => Some(element_node),
105 _ => None,
106 }
107 }
108
109 pub fn as_elem_mut(&mut self) -> Option<&mut VirtualElement<Dom>> {
114 match self {
115 VirtualNode::Element(ref mut element_node) => Some(element_node),
116 _ => None,
117 }
118 }
119
120 pub fn as_text(&self) -> Option<&VirtualText> {
125 match self {
126 VirtualNode::Text(ref text_node) => Some(text_node),
127 _ => None,
128 }
129 }
130
131 pub fn as_text_mut(&mut self) -> Option<&mut VirtualText> {
136 match self {
137 VirtualNode::Text(ref mut text_node) => Some(text_node),
138 _ => None,
139 }
140 }
141
142 pub fn map_real_dom<New: RealDom>(
144 self,
145 convert_event: &dyn Fn(EventHandler<Dom>) -> EventHandler<New>,
150 convert_lifecycle: &dyn Fn(Box<dyn FnMut(Dom::Element)>) -> Box<dyn FnMut(New::Element)>,
151 ) -> VirtualNode<New> {
152 match self {
153 VirtualNode::Text(text) => VirtualNode::Text(text),
154 VirtualNode::Element(elem) => {
155 let children: Vec<VirtualNode<New>> = elem
156 .children
157 .into_iter()
158 .map(|old| old.map_real_dom::<New>(convert_event, convert_lifecycle))
159 .collect();
160
161 VirtualNode::Element(VirtualElement {
162 tag: elem.tag,
163 attrs: elem.attrs,
164 events: elem.events.convert_all(convert_event),
165 children,
166 special_attributes: elem.special_attributes.map_dom(convert_lifecycle),
167 })
168 }
169 }
170 }
171
172 pub fn insert_space_before_text(&mut self) {
179 match self {
180 VirtualNode::Text(text_node) => {
181 text_node.text = " ".to_string() + &text_node.text;
182 }
183 _ => {}
184 }
185 }
186
187 pub fn insert_space_after_text(&mut self) {
195 match self {
196 VirtualNode::Text(text_node) => {
197 text_node.text += " ";
198 }
199 _ => {}
200 }
201 }
202}
203
204#[cfg(feature = "web")]
205impl VirtualNode<web_sys::Window> {
206 pub fn create_dom_node(
208 &self,
209 events: &mut self::event::VirtualEvents<web_sys::Window>,
210 ) -> (web_sys::Node, crate::event::VirtualEventNode) {
211 match self {
212 VirtualNode::Text(text_node) => (
213 text_node.create_text_node().into(),
214 events.create_text_node(),
215 ),
216 VirtualNode::Element(element_node) => {
217 let (elem, events) = element_node.create_element_node(events);
218 (elem.into(), events)
219 }
220 }
221 }
222}
223
224pub trait View<Dom: RealDom> {
231 fn render(&self) -> VirtualNode<Dom>;
233}
234
235impl<V, Dom: RealDom> From<&V> for VirtualNode<Dom>
236where
237 V: View<Dom>,
238{
239 fn from(v: &V) -> Self {
240 v.render()
241 }
242}
243
244impl<Dom: RealDom> From<VirtualText> for VirtualNode<Dom> {
245 fn from(other: VirtualText) -> Self {
246 VirtualNode::Text(other)
247 }
248}
249
250impl<Dom: RealDom> From<VirtualElement<Dom>> for VirtualNode<Dom> {
251 fn from(other: VirtualElement<Dom>) -> Self {
252 VirtualNode::Element(other)
253 }
254}
255
256impl<Dom: RealDom> From<&str> for VirtualNode<Dom> {
257 fn from(other: &str) -> Self {
258 VirtualNode::new_text(other)
259 }
260}
261
262impl<Dom: RealDom> From<String> for VirtualNode<Dom> {
263 fn from(other: String) -> Self {
264 VirtualNode::new_text(other.as_str())
265 }
266}
267
268impl<Dom: RealDom> IntoIterator for VirtualNode<Dom> {
269 type Item = VirtualNode<Dom>;
270 type IntoIter = ::std::vec::IntoIter<VirtualNode<Dom>>;
272
273 fn into_iter(self) -> Self::IntoIter {
274 vec![self].into_iter()
275 }
276}
277
278impl<Dom: RealDom> Into<::std::vec::IntoIter<VirtualNode<Dom>>> for VirtualNode<Dom> {
279 fn into(self) -> ::std::vec::IntoIter<VirtualNode<Dom>> {
280 self.into_iter()
281 }
282}
283
284impl<Dom: RealDom> fmt::Debug for VirtualNode<Dom> {
285 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
286 match self {
287 VirtualNode::Element(e) => write!(f, "Node::{:?}", e),
288 VirtualNode::Text(t) => write!(f, "Node::{:?}", t),
289 }
290 }
291}
292
293impl<Dom: RealDom> fmt::Display for VirtualNode<Dom> {
295 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
296 match self {
297 VirtualNode::Element(element) => write!(f, "{}", element),
298 VirtualNode::Text(text) => write!(f, "{}", text),
299 }
300 }
301}
302
303#[cfg(test)]
304mod tests {
305 use super::*;
306
307 #[test]
308 fn self_closing_tag_to_string() {
309 let node = VirtualNode::<()>::new_element("br");
310
311 assert_eq!(&node.to_string(), "<br>");
313 }
314
315 #[test]
316 fn to_string() {
317 let mut node = VirtualNode::Element(VirtualElement::<()>::new("div"));
318 node.as_elem_mut()
319 .unwrap()
320 .attrs
321 .insert("id".into(), "some-id".into());
322
323 let mut child = VirtualNode::Element(VirtualElement::new("span"));
324
325 let text = VirtualNode::Text(VirtualText::new("Hello world"));
326
327 child.as_elem_mut().unwrap().children.push(text);
328
329 node.as_elem_mut().unwrap().children.push(child);
330
331 let expected = r#"<div id="some-id"><span>Hello world</span></div>"#;
332
333 assert_eq!(node.to_string(), expected);
334 }
335
336 #[test]
338 fn boolean_attribute_true_shown() {
339 let mut button = VirtualElement::<()>::new("button");
340 button.attrs.insert("disabled".into(), true.into());
341
342 let expected = "<button disabled></button>";
343 let button = VirtualNode::Element(button).to_string();
344
345 assert_eq!(button.to_string(), expected);
346 }
347
348 #[test]
350 fn boolean_attribute_false_ignored() {
351 let mut button = VirtualElement::<()>::new("button");
352 button.attrs.insert("disabled".into(), false.into());
353
354 let expected = "<button></button>";
355 let button = VirtualNode::Element(button).to_string();
356
357 assert_eq!(button.to_string(), expected);
358 }
359}