-
-
Notifications
You must be signed in to change notification settings - Fork 689
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
12 changed files
with
226 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
[package] | ||
name = "portal" | ||
version = "0.1.0" | ||
edition = "2021" | ||
|
||
[dependencies] | ||
leptos = { path = "../../leptos", features = ["csr", "nightly"] } | ||
log = "0.4" | ||
console_log = "1" | ||
console_error_panic_hook = "0.1.7" | ||
|
||
[dev-dependencies] | ||
wasm-bindgen-test = "0.3.0" | ||
wasm-bindgen = "0.2" | ||
web-sys = "0.3" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
extend = [ | ||
{ path = "../cargo-make/main.toml" }, | ||
{ path = "../cargo-make/wasm-test.toml" }, | ||
{ path = "../cargo-make/trunk_server.toml" }, | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
# Leptos Portal Example | ||
|
||
This example showcases a basic leptos app with a portal. | ||
|
||
## Getting Started | ||
|
||
See the [Examples README](../README.md) for setup and run instructions. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
<!DOCTYPE html> | ||
<html> | ||
<head> | ||
<link data-trunk rel="rust" data-wasm-opt="z" data-weak-refs/> | ||
</head> | ||
<body> | ||
<div id="app"></div> | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
[toolchain] | ||
channel = "nightly" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
use leptos::*; | ||
|
||
#[component] | ||
pub fn App() -> impl IntoView { | ||
let (show_overlay, set_show_overlay) = create_signal(false); | ||
let (show_inside_overlay, set_show_inside_overlay) = create_signal(false); | ||
|
||
view! { | ||
<div> | ||
<button id="btn-show" on:click=move |_| set_show_overlay(true)> | ||
Show Overlay | ||
</button> | ||
|
||
<Show when=show_overlay fallback=|| ()> | ||
<div>Show</div> | ||
<Portal mount=document().get_element_by_id("app").unwrap()> | ||
<div style="position: fixed; z-index: 10; width: 100vw; height: 100vh; top: 0; left: 0; background: rgba(0, 0, 0, 0.8); color: white;"> | ||
<p>This is in the body element</p> | ||
<button id="btn-hide" on:click=move |_| set_show_overlay(false)> | ||
Close Overlay | ||
</button> | ||
<button id="btn-toggle" on:click=move |_| set_show_inside_overlay(!show_inside_overlay())> | ||
Toggle inner | ||
</button> | ||
|
||
<Show when=show_inside_overlay fallback=|| view! { "Hidden" }> | ||
Visible | ||
</Show> | ||
</div> | ||
</Portal> | ||
</Show> | ||
</div> | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
use leptos::*; | ||
use portal::App; | ||
use wasm_bindgen::JsCast; | ||
|
||
fn main() { | ||
_ = console_log::init_with_level(log::Level::Debug); | ||
console_error_panic_hook::set_once(); | ||
mount_to( | ||
leptos::document() | ||
.get_element_by_id("app") | ||
.unwrap() | ||
.unchecked_into(), | ||
|| view! { <App/> }, | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
use wasm_bindgen::JsCast; | ||
use wasm_bindgen_test::*; | ||
|
||
wasm_bindgen_test_configure!(run_in_browser); | ||
use leptos::*; | ||
use portal::App; | ||
use web_sys::HtmlButtonElement; | ||
|
||
#[wasm_bindgen_test] | ||
fn portal() { | ||
let document = leptos::document(); | ||
let body = document.body().unwrap(); | ||
|
||
let div = document.create_element("div").unwrap(); | ||
div.set_id("app"); | ||
let _ = body.append_child(&div); | ||
|
||
mount_to(div.clone().unchecked_into(), || view! { <App/> }); | ||
|
||
let show_button = document | ||
.get_element_by_id("btn-show") | ||
.unwrap() | ||
.unchecked_into::<HtmlButtonElement>(); | ||
|
||
show_button.click(); | ||
|
||
// next_tick().await; | ||
|
||
// check HTML | ||
assert_eq!( | ||
div.inner_html(), | ||
"<!-- <App> --><div><button id=\"btn-show\">\n Show Overlay\n </button><!-- <Show> --><!-- <DynChild> --><!-- <> --><div>Show</div><!-- <Portal> --><!-- <() /> --><!-- </Portal> --><!-- </> --><!-- </DynChild> --><!-- </Show> --></div><!-- </App> --><div><!-- <> --><div style=\"position: fixed; z-index: 10; width: 100vw; height: 100vh; top: 0; left: 0; background: rgba(0, 0, 0, 0.8); color: white;\"><p>This is in the body element</p><button id=\"btn-hide\">\n Close Overlay\n </button><button id=\"btn-toggle\">\n Toggle inner\n </button><!-- <Show> --><!-- <DynChild> -->Hidden<!-- </DynChild> --><!-- </Show> --></div><!-- </> --></div>" | ||
); | ||
|
||
let toggle_button = document | ||
.get_element_by_id("btn-toggle") | ||
.unwrap() | ||
.unchecked_into::<HtmlButtonElement>(); | ||
|
||
toggle_button.click(); | ||
|
||
assert_eq!( | ||
div.inner_html(), | ||
"<!-- <App> --><div><button id=\"btn-show\">\n Show Overlay\n </button><!-- <Show> --><!-- <DynChild> --><!-- <> --><div>Show</div><!-- <Portal> --><!-- <() /> --><!-- </Portal> --><!-- </> --><!-- </DynChild> --><!-- </Show> --></div><!-- </App> --><div><!-- <> --><div style=\"position: fixed; z-index: 10; width: 100vw; height: 100vh; top: 0; left: 0; background: rgba(0, 0, 0, 0.8); color: white;\"><p>This is in the body element</p><button id=\"btn-hide\">\n Close Overlay\n </button><button id=\"btn-toggle\">\n Toggle inner\n </button><!-- <Show> --><!-- <DynChild> --><!-- <> -->\n Visible\n <!-- </> --><!-- </DynChild> --><!-- </Show> --></div><!-- </> --></div>" | ||
); | ||
|
||
let hide_button = document | ||
.get_element_by_id("btn-hide") | ||
.unwrap() | ||
.unchecked_into::<HtmlButtonElement>(); | ||
|
||
hide_button.click(); | ||
|
||
assert_eq!( | ||
div.inner_html(), | ||
"<!-- <App> --><div><button id=\"btn-show\">\n Show \ | ||
Overlay\n </button><!-- <Show> --><!-- <DynChild> --><!-- \ | ||
<() /> --><!-- </DynChild> --><!-- </Show> --></div><!-- </App> -->" | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
use crate::ChildrenFn; | ||
use cfg_if::cfg_if; | ||
use leptos_dom::IntoView; | ||
use leptos_macro::component; | ||
|
||
/// Renders components somewhere else in the DOM. | ||
/// | ||
/// Useful for inserting modals and tooltips outside of a cropping layout. | ||
/// If no mount point is given, the portal is inserted in `document.body`; | ||
/// it is wrapped in a `<div>` unless `is_svg` is `true` in which case it's wrappend in a `<g>`. | ||
/// Setting `use_shadow` to `true` places the element in a shadow root to isolate styles. | ||
#[cfg_attr( | ||
any(debug_assertions, feature = "ssr"), | ||
tracing::instrument(level = "info", skip_all) | ||
)] | ||
#[component] | ||
pub fn Portal( | ||
/// Target element where the children will be appended | ||
#[prop(into, optional)] | ||
mount: Option<web_sys::Element>, | ||
/// Whether to use a shadow DOM inside `mount`. Defaults to `false`. | ||
#[prop(optional)] | ||
use_shadow: bool, | ||
/// When using SVG this has to be set to `true`. Defaults to `false`. | ||
#[prop(optional)] | ||
is_svg: bool, | ||
/// The children to teleport into the `mount` element | ||
children: ChildrenFn, | ||
) -> impl IntoView { | ||
cfg_if! { if #[cfg(all(target_arch = "wasm32", any(feature = "hydrate", feature = "csr")))] { | ||
use leptos_dom::{document, Mountable}; | ||
use leptos_reactive::{create_render_effect, on_cleanup}; | ||
use wasm_bindgen::JsCast; | ||
|
||
let mount = mount | ||
.unwrap_or_else(|| document().body().expect("body to exist").unchecked_into()); | ||
|
||
create_render_effect(move |_| { | ||
let tag = if is_svg { "g" } else { "div" }; | ||
|
||
let container = document() | ||
.create_element(tag) | ||
.expect("element creation to work"); | ||
|
||
let render_root = if use_shadow { | ||
container | ||
.attach_shadow(&web_sys::ShadowRootInit::new( | ||
web_sys::ShadowRootMode::Open, | ||
)) | ||
.map(|root| root.unchecked_into()) | ||
.unwrap_or(container.clone()) | ||
} else { | ||
container.clone() | ||
}; | ||
|
||
let _ = render_root.append_child(&children().into_view().get_mountable_node()); | ||
|
||
let _ = mount.append_child(&container); | ||
|
||
on_cleanup({ | ||
let mount = mount.clone(); | ||
|
||
move || { | ||
let _ = mount.remove_child(&container); | ||
} | ||
}) | ||
}); | ||
} else { | ||
let _ = mount; | ||
let _ = use_shadow; | ||
let _ = is_svg; | ||
let _ = children; | ||
}} | ||
} |