1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
use leptos_reactive::{
    create_rw_signal, RwSignal, Scope
};

/// Contains a shared reference to a DOM node creating while using the [view](leptos::view)
/// macro to create your UI.
///
/// ```
/// # use leptos::*;
/// #[component]
/// pub fn MyComponent(cx: Scope) -> Element {
///   let input_ref = NodeRef::new(cx);
///
///   let on_click = move |_| {
///     let node = input_ref
///       .get()
///       .expect("input_ref should be loaded by now")
///       .unchecked_into::<web_sys::HtmlInputElement>();
///     log!("value is {:?}", node.value())
///   };
///
///   view! {
///     cx,
///     <div>
///     // `node_ref` loads the input
///     <input _ref=input_ref type="text"/>
///     // the button consumes it
///     <button on:click=on_click>"Click me"</button>
///     </div>
///   }
/// }
/// ```
#[derive(Copy, Clone, PartialEq)]
pub struct NodeRef(RwSignal<Option<web_sys::Element>>);

impl NodeRef {
    /// Creates an empty reference.
    pub fn new(cx: Scope) -> Self {
        Self(create_rw_signal(cx, None))
    }

    /// Gets the element that is currently stored in the reference.
    ///
    /// This tracks reactively, so that node references can be used in effects.
    /// Initially, the value will be `None`, but once it is loaded the effect
    /// will rerun and its value will be `Some(Element)`.
    pub fn get(&self) -> Option<web_sys::Element> {
        self.0.get()
    }

    #[doc(hidden)]
    /// Loads an element into the reference. This tracks reactively,
    /// so that effects that use the node reference will rerun once it is loaded,
    /// i.e., effects can be forward-declared.
    pub fn load(&self, node: &web_sys::Element) {
        self.0.set(Some(node.clone()))
    }
}

cfg_if::cfg_if! {
    if #[cfg(not(feature = "stable"))] {
        impl FnOnce<()> for NodeRef {
            type Output = Option<web_sys::Element>;

            extern "rust-call" fn call_once(self, _args: ()) -> Self::Output {
                self.get()
            }
        }

        impl FnMut<()> for NodeRef {
            extern "rust-call" fn call_mut(&mut self, _args: ()) -> Self::Output {
                self.get()
            }
        }

        impl Fn<()> for NodeRef {
            extern "rust-call" fn call(&self, _args: ()) -> Self::Output {
                self.get()
            }
        }
    }
}