yew_stdweb/html/
mod.rs

1//! The main html module which defines components, listeners, and class helpers.
2
3mod classes;
4mod component;
5mod conversion;
6mod listener;
7
8pub use classes::*;
9pub use component::*;
10pub use conversion::*;
11pub use listener::*;
12
13use crate::virtual_dom::VNode;
14use cfg_if::cfg_if;
15use cfg_match::cfg_match;
16use std::cell::RefCell;
17use std::rc::Rc;
18cfg_if! {
19    if #[cfg(feature = "std_web")] {
20        use stdweb::unstable::TryFrom;
21        use stdweb::web::Node;
22    } else if #[cfg(feature = "web_sys")] {
23        use wasm_bindgen::JsValue;
24        use web_sys::Node;
25    }
26}
27
28/// A type which expected as a result of `view` function implementation.
29pub type Html = VNode;
30
31/// Wrapped Node reference for later use in Component lifecycle methods.
32///
33/// # Example
34/// Focus an `<input>` element on mount.
35/// ```
36/// #[cfg(feature = "std_web")]
37/// use stdweb::web::{html_element::InputElement, IHtmlElement};
38/// #[cfg(feature = "web_sys")]
39/// use web_sys::HtmlInputElement as InputElement;
40///# use yew::prelude::*;
41///
42/// pub struct Input {
43///     node_ref: NodeRef,
44/// }
45///
46/// impl Component for Input {
47///     type Message = ();
48///     type Properties = ();
49///
50///     fn create(_: Self::Properties, _: ComponentLink<Self>) -> Self {
51///         Input {
52///             node_ref: NodeRef::default(),
53///         }
54///     }
55///
56///     fn rendered(&mut self, first_render: bool) {
57///         if first_render {
58///             if let Some(input) = self.node_ref.cast::<InputElement>() {
59///                 input.focus();
60///             }
61///         }
62///     }
63///
64///     fn change(&mut self, _: Self::Properties) -> ShouldRender {
65///         false
66///     }
67///
68///     fn update(&mut self, _: Self::Message) -> ShouldRender {
69///         false
70///     }
71///
72///     fn view(&self) -> Html {
73///         html! {
74///             <input ref=self.node_ref.clone() type="text" />
75///         }
76///     }
77/// }
78/// ```
79/// ## Relevant examples
80/// - [Node Refs](https://github.com/yewstack/yew/tree/master/examples/node_refs)
81#[derive(Debug, Default, Clone)]
82pub struct NodeRef(Rc<RefCell<NodeRefInner>>);
83
84impl PartialEq for NodeRef {
85    fn eq(&self, other: &Self) -> bool {
86        self.0.as_ptr() == other.0.as_ptr() || Some(self) == other.0.borrow().link.as_ref()
87    }
88}
89
90#[derive(PartialEq, Debug, Default, Clone)]
91struct NodeRefInner {
92    node: Option<Node>,
93    link: Option<NodeRef>,
94}
95
96impl NodeRef {
97    /// Get the wrapped Node reference if it exists
98    pub fn get(&self) -> Option<Node> {
99        let inner = self.0.borrow();
100        inner.node.clone().or_else(|| inner.link.as_ref()?.get())
101    }
102
103    /// Try converting the node reference into another form
104    pub fn cast<
105        #[cfg(feature = "std_web")] INTO: TryFrom<Node>,
106        #[cfg(feature = "web_sys")] INTO: AsRef<Node> + From<JsValue>,
107    >(
108        &self,
109    ) -> Option<INTO> {
110        let node = self.get();
111        cfg_match! {
112            feature = "std_web" => node.and_then(|node| INTO::try_from(node).ok()),
113            feature = "web_sys" => node.map(Into::into).map(INTO::from),
114        }
115    }
116
117    /// Wrap an existing `Node` in a `NodeRef`
118    pub(crate) fn new(node: Node) -> Self {
119        let node_ref = NodeRef::default();
120        node_ref.set(Some(node));
121        node_ref
122    }
123
124    /// Place a Node in a reference for later use
125    pub(crate) fn set(&self, node: Option<Node>) {
126        let mut this = self.0.borrow_mut();
127        this.node = node;
128        this.link = None;
129    }
130
131    /// Link a downstream `NodeRef`
132    pub(crate) fn link(&self, node_ref: Self) {
133        // Avoid circular references
134        if self == &node_ref {
135            return;
136        }
137
138        let mut this = self.0.borrow_mut();
139        this.node = None;
140        this.link = Some(node_ref);
141    }
142
143    /// Reuse an existing `NodeRef`
144    pub(crate) fn reuse(&self, node_ref: Self) {
145        // Avoid circular references
146        if self == &node_ref {
147            return;
148        }
149
150        let mut this = self.0.borrow_mut();
151        let existing = node_ref.0.borrow();
152        this.node = existing.node.clone();
153        this.link = existing.link.clone();
154    }
155}
156
157#[cfg(test)]
158mod tests {
159    use super::*;
160    use crate::utils::document;
161
162    #[cfg(feature = "wasm_test")]
163    use wasm_bindgen_test::{wasm_bindgen_test as test, wasm_bindgen_test_configure};
164
165    #[cfg(feature = "wasm_test")]
166    wasm_bindgen_test_configure!(run_in_browser);
167
168    #[test]
169    fn self_linking_node_ref() {
170        let node: Node = document().create_text_node("test node").into();
171        let node_ref = NodeRef::new(node.clone());
172        let node_ref_2 = NodeRef::new(node.clone());
173
174        // Link to self
175        node_ref.link(node_ref.clone());
176        assert_eq!(node, node_ref.get().unwrap());
177
178        // Create cycle of two node refs
179        node_ref.link(node_ref_2.clone());
180        node_ref_2.link(node_ref);
181        assert_eq!(node, node_ref_2.get().unwrap());
182    }
183}