maple_core/
render.rs

1//! Trait for describing how something should be rendered into DOM nodes.
2
3use std::fmt;
4use std::rc::Rc;
5
6use wasm_bindgen::JsCast;
7use web_sys::{Node, Text};
8
9use crate::reactive::VecDiff;
10use crate::TemplateResult;
11use crate::{internal::*, TemplateList};
12
13/// Trait for describing how something should be rendered into DOM nodes.
14pub trait Render {
15    /// Called during the initial render when creating the DOM nodes. Should return a [`Node`].
16    fn render(&self) -> Node;
17
18    /// Called when the node should be updated with new state.
19    /// The default implementation of this will replace the child node completely with the result of calling `render` again.
20    /// Another implementation might be better suited to some specific types.
21    /// For example, text nodes can simply replace the inner text instead of recreating a new node.
22    ///
23    /// Returns the new node. If the node is reused instead of replaced, the returned node is simply the node passed in.
24    fn update_node(&self, parent: &Node, node: &Node) -> Node {
25        let new_node = self.render();
26        parent.replace_child(&new_node, &node).unwrap();
27        new_node
28    }
29}
30
31impl<T: fmt::Display + ?Sized> Render for T {
32    fn render(&self) -> Node {
33        web_sys::window()
34            .unwrap()
35            .document()
36            .unwrap()
37            .create_text_node(&format!("{}", self))
38            .into()
39    }
40
41    fn update_node(&self, _parent: &Node, node: &Node) -> Node {
42        // replace `textContent` of `node` instead of recreating
43        node.clone()
44            .dyn_into::<Text>()
45            .unwrap()
46            .set_text_content(Some(&format!("{}", self)));
47
48        node.clone()
49    }
50}
51
52impl Render for TemplateList {
53    fn render(&self) -> Node {
54        let fragment = fragment();
55
56        for item in self
57            .templates
58            .inner_signal()
59            .get()
60            .borrow()
61            .clone()
62            .into_iter()
63        {
64            append_render(
65                &fragment,
66                Box::new(move || {
67                    let item = item.clone();
68                    Box::new(item)
69                }),
70            );
71        }
72
73        fragment.into()
74    }
75
76    fn update_node(&self, parent: &Node, node: &Node) -> Node {
77        let templates = self.templates.inner_signal().get(); // subscribe to templates
78        let changes = Rc::clone(&self.templates.changes());
79
80        for change in changes.borrow().iter() {
81            match change {
82                VecDiff::Replace { values } => {
83                    let first = templates.borrow().first().map(|x| x.node.clone());
84
85                    for value in values {
86                        parent.insert_before(&value.node, first.as_ref()).unwrap();
87                    }
88
89                    for template in templates.borrow().iter() {
90                        parent.remove_child(&template.node).unwrap();
91                    }
92                }
93                VecDiff::Insert { index, value } => {
94                    parent
95                        .insert_before(
96                            &value.node,
97                            templates
98                                .borrow()
99                                .get(*index)
100                                .map(|template| template.node.next_sibling())
101                                .flatten()
102                                .as_ref(),
103                        )
104                        .unwrap();
105                }
106                VecDiff::Update { index, value } => {
107                    parent
108                        .replace_child(&templates.borrow()[*index].node, &value.node)
109                        .unwrap();
110                }
111                VecDiff::Remove { index } => {
112                    parent
113                        .remove_child(&templates.borrow()[*index].node)
114                        .unwrap();
115                }
116                VecDiff::Swap { index1, index2 } => {
117                    let child1 = &templates.borrow()[*index1].node;
118                    let child2 = &templates.borrow()[*index2].node;
119                    parent.replace_child(child1, child2).unwrap();
120                    parent.replace_child(child2, child1).unwrap();
121                }
122                VecDiff::Push { value } => {
123                    parent
124                        .insert_before(
125                            &value.node,
126                            templates
127                                .borrow()
128                                .last()
129                                .map(|last| last.node.next_sibling())
130                                .flatten()
131                                .as_ref(),
132                        )
133                        .unwrap();
134                }
135                VecDiff::Pop => {
136                    if let Some(last) = templates.borrow().last() {
137                        parent.remove_child(&last.node).unwrap();
138                    }
139                }
140                VecDiff::Clear => {
141                    for template in templates.borrow().iter() {
142                        parent.remove_child(&template.node).unwrap();
143                    }
144                }
145            }
146        }
147
148        node.clone()
149    }
150}
151
152impl Render for TemplateResult {
153    fn render(&self) -> Node {
154        self.node.clone()
155    }
156}