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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
use crate::{document, Element};
use cfg_if::cfg_if;
use leptos_reactive::Scope;
use wasm_bindgen::UnwrapThrowExt;

/// Describes a type that can be mounted to a parent element in the DOM.
pub trait Mountable {
    /// Injects the element into the parent as its next child.
    fn mount(&self, parent: &web_sys::Element);
}

impl Mountable for Element {
    fn mount(&self, parent: &web_sys::Element) {
        cfg_if! {
            if #[cfg(any(feature = "csr", feature = "hydrate"))] {
                parent.append_child(self).unwrap_throw();
            } else {
                let _ = parent;
            }
        }
    }
}

impl Mountable for Vec<Element> {
    fn mount(&self, parent: &web_sys::Element) {
        cfg_if! {
            if #[cfg(any(feature = "csr", feature = "hydrate"))] {
                for element in self {
                    parent.append_child(element).unwrap_throw();
                }
            } else {
                _ = parent; // to clear warning
            }
        }
    }
}

/// Runs the given function to mount something to the `<body>` element in the DOM.
///
/// ```
/// // the simplest Leptos application
/// # use leptos_dom::*; use leptos_dom::wasm_bindgen::JsCast;
/// # use leptos_macro::view;
/// # if false { // can't actually run as a doctest on any feature
/// mount_to_body(|cx| view! { cx,  <p>"Hello, world!"</p> });
/// # }
/// ```
pub fn mount_to_body<T, F>(f: F)
where
    F: Fn(Scope) -> T + 'static,
    T: Mountable,
{
    mount(document().body().unwrap_throw(), f)
}

/// Runs the given function to mount something to the given element in the DOM.
///
/// ```
/// // a very simple Leptos application
/// # use leptos_dom::*; use leptos_dom::wasm_bindgen::JsCast;
/// # use leptos_macro::view;
/// # if false { // can't actually run as a doctest on any feature
/// mount(
///   document().get_element_by_id("root").unwrap().unchecked_into(),
///   |cx| view! { cx,  <p>"Hello, world!"</p> }
/// );
/// # }
/// ```
pub fn mount<T, F>(parent: web_sys::HtmlElement, f: F)
where
    F: Fn(Scope) -> T + 'static,
    T: Mountable,
{
    use leptos_reactive::{create_runtime, create_scope};

    // this is not a leak
    // CSR and hydrate mode define a single, thread-local Runtime
    let _ = create_scope(create_runtime(), move |cx| {
        (f(cx)).mount(&parent);
    });
}

/// “Hydrates” server-rendered HTML, attaching event listeners and setting up reactivity
/// while reusing the existing DOM nodes, by running the given function beginning with
/// the parent node.
///
/// ```
/// // rehydrate a very simple Leptos application
/// # use leptos_dom::*; use leptos_dom::wasm_bindgen::JsCast;
/// # use leptos_macro::view;
/// # if false { // can't actually run as a doctest on any feature
/// if let Some(body) = body() {
///   hydrate(body, |cx| view! { cx,  <p>"Hello, world!"</p> });
/// }
/// # }
/// ```
#[cfg(feature = "hydrate")]
pub fn hydrate<T, F>(parent: web_sys::HtmlElement, f: F)
where
    F: Fn(Scope) -> T + 'static,
    T: Mountable,
{
    use leptos_reactive::create_runtime;

    // this is not a leak
    // CSR and hydrate mode define a single, thread-local Runtime
    let _ = leptos_reactive::create_scope(create_runtime(), move |cx| {
        cx.start_hydration(&parent);
        (f(cx));
        cx.end_hydration();
    });
}