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
//! Portal API.

use std::any::Any;

use wasm_bindgen::prelude::*;

use crate::component::Children;
use crate::prelude::*;

/// Props for [`Portal`].
#[derive(Prop, Debug)]
pub struct PortalProps<'a, G>
where
    G: GenericNode,
{
    children: Children<'a, G>,
    selector: &'a str,
}

/// A portal into another part of the DOM.
#[component]
pub fn Portal<'a, G: Html>(cx: Scope<'a>, props: PortalProps<'a, G>) -> View<G> {
    let PortalProps { children, selector } = props;

    if G::IS_BROWSER {
        let window = web_sys::window().unwrap_throw();
        let document = window.document().unwrap_throw();
        let container = document
            .query_selector(selector)
            .unwrap_throw()
            .expect_throw("could not find element matching selector");

        let children = children.call(cx).flatten();

        for child in &children {
            container
                .append_child(
                    &<dyn Any>::downcast_ref::<DomNode>(child)
                        .unwrap_throw()
                        .inner_element(),
                )
                .unwrap_throw();
        }

        on_cleanup(cx, move || {
            for child in &children {
                container
                    .remove_child(
                        &<dyn Any>::downcast_ref::<DomNode>(child)
                            .unwrap_throw()
                            .inner_element(),
                    )
                    .unwrap_throw();
            }
        });
    } else {
        // TODO: Support for other types of nodes.
    }

    view! { cx, }
}