1use crate::{children::TypedChildrenFn, mount, IntoView};
2use leptos_dom::helpers::document;
3use leptos_macro::component;
4use reactive_graph::{effect::Effect, graph::untrack, owner::Owner};
5use std::sync::Arc;
6
7#[cfg_attr(feature = "tracing", tracing::instrument(level = "trace", skip_all))]
14#[component]
15pub fn Portal<V>(
16 #[prop(into, optional)]
18 mount: Option<web_sys::Element>,
19 #[prop(optional)]
21 use_shadow: bool,
22 #[prop(optional)]
24 is_svg: bool,
25 children: TypedChildrenFn<V>,
27) -> impl IntoView
28where
29 V: IntoView + 'static,
30{
31 if cfg!(target_arch = "wasm32")
32 && Owner::current_shared_context()
33 .map(|sc| sc.is_browser())
34 .unwrap_or(true)
35 {
36 use send_wrapper::SendWrapper;
37 use wasm_bindgen::JsCast;
38
39 let mount = mount.unwrap_or_else(|| {
40 document().body().expect("body to exist").unchecked_into()
41 });
42 let children = children.into_inner();
43
44 Effect::new(move |_| {
45 let container = if is_svg {
46 document()
47 .create_element_ns(Some("http://www.w3.org/2000/svg"), "g")
48 .expect("SVG element creation to work")
49 } else {
50 document()
51 .create_element("div")
52 .expect("HTML element creation to work")
53 };
54
55 let render_root = if use_shadow {
56 container
57 .attach_shadow(&web_sys::ShadowRootInit::new(
58 web_sys::ShadowRootMode::Open,
59 ))
60 .map(|root| root.unchecked_into())
61 .unwrap_or(container.clone())
62 } else {
63 container.clone()
64 };
65
66 let _ = mount.append_child(&container);
67 let handle = SendWrapper::new((
68 mount::mount_to(render_root.unchecked_into(), {
69 let children = Arc::clone(&children);
70 move || untrack(|| children())
71 }),
72 mount.clone(),
73 container,
74 ));
75
76 Owner::on_cleanup({
77 move || {
78 let (handle, mount, container) = handle.take();
79 drop(handle);
80 let _ = mount.remove_child(&container);
81 }
82 })
83 });
84 }
85}