yew_stdweb/virtual_dom/
vtext.rs

1//! This module contains the implementation of a virtual text node `VText`.
2
3use super::{AttrValue, VDiff, VNode};
4use crate::html::{AnyScope, NodeRef};
5use crate::utils::document;
6use cfg_if::cfg_if;
7use log::warn;
8use std::cmp::PartialEq;
9cfg_if! {
10    if #[cfg(feature = "std_web")] {
11        use stdweb::web::{Element, INode, TextNode};
12    } else if #[cfg(feature = "web_sys")] {
13        use web_sys::{Element, Text as TextNode};
14    }
15}
16
17/// A type for a virtual
18/// [`TextNode`](https://developer.mozilla.org/en-US/docs/Web/API/Document/createTextNode)
19/// representation.
20#[derive(Clone, Debug)]
21pub struct VText {
22    /// Contains a text of the node.
23    pub text: AttrValue,
24    /// A reference to the `TextNode`.
25    pub reference: Option<TextNode>,
26}
27
28impl VText {
29    /// Creates new virtual text node with a content.
30    pub fn new(text: impl Into<AttrValue>) -> Self {
31        VText {
32            text: text.into(),
33            reference: None,
34        }
35    }
36}
37
38impl VDiff for VText {
39    /// Remove VText from parent.
40    fn detach(&mut self, parent: &Element) {
41        let node = self
42            .reference
43            .take()
44            .expect("tried to remove not rendered VText from DOM");
45        if parent.remove_child(&node).is_err() {
46            warn!("Node not found to remove VText");
47        }
48    }
49
50    /// Renders virtual node over existing `TextNode`, but only if value of text has changed.
51    fn apply(
52        &mut self,
53        _parent_scope: &AnyScope,
54        parent: &Element,
55        next_sibling: NodeRef,
56        ancestor: Option<VNode>,
57    ) -> NodeRef {
58        if let Some(mut ancestor) = ancestor {
59            if let VNode::VText(mut vtext) = ancestor {
60                self.reference = vtext.reference.take();
61                let text_node = self
62                    .reference
63                    .clone()
64                    .expect("Rendered VText nodes should have a ref");
65                if self.text != vtext.text {
66                    text_node.set_node_value(Some(&self.text));
67                }
68
69                return NodeRef::new(text_node.into());
70            }
71
72            ancestor.detach(parent);
73        }
74
75        let text_node = document().create_text_node(&self.text);
76        super::insert_node(&text_node, parent, next_sibling.get());
77        self.reference = Some(text_node.clone());
78        NodeRef::new(text_node.into())
79    }
80}
81
82impl PartialEq for VText {
83    fn eq(&self, other: &VText) -> bool {
84        self.text == other.text
85    }
86}
87
88#[cfg(test)]
89mod test {
90    extern crate self as yew;
91
92    use crate::html;
93
94    #[cfg(feature = "wasm_test")]
95    use wasm_bindgen_test::{wasm_bindgen_test as test, wasm_bindgen_test_configure};
96
97    #[cfg(feature = "wasm_test")]
98    wasm_bindgen_test_configure!(run_in_browser);
99
100    #[test]
101    fn text_as_root() {
102        html! {
103            "Text Node As Root"
104        };
105
106        html! {
107            { "Text Node As Root" }
108        };
109    }
110}
111
112#[cfg(all(test, feature = "web_sys"))]
113mod layout_tests {
114    extern crate self as yew;
115
116    use crate::html;
117    use crate::virtual_dom::layout_tests::{diff_layouts, TestLayout};
118
119    #[cfg(feature = "wasm_test")]
120    use wasm_bindgen_test::{wasm_bindgen_test as test, wasm_bindgen_test_configure};
121
122    #[cfg(feature = "wasm_test")]
123    wasm_bindgen_test_configure!(run_in_browser);
124
125    #[test]
126    fn diff() {
127        let layout1 = TestLayout {
128            name: "1",
129            node: html! { "a" },
130            expected: "a",
131        };
132
133        let layout2 = TestLayout {
134            name: "2",
135            node: html! { "b" },
136            expected: "b",
137        };
138
139        let layout3 = TestLayout {
140            name: "3",
141            node: html! {
142                <>
143                    {"a"}
144                    {"b"}
145                </>
146            },
147            expected: "ab",
148        };
149
150        let layout4 = TestLayout {
151            name: "4",
152            node: html! {
153                <>
154                    {"b"}
155                    {"a"}
156                </>
157            },
158            expected: "ba",
159        };
160
161        diff_layouts(vec![layout1, layout2, layout3, layout4]);
162    }
163}