#[cfg(debug_assertions)]
use crate::logging;
use crate::IntoView;
use any_spawner::Executor;
use reactive_graph::owner::Owner;
#[cfg(debug_assertions)]
use std::cell::Cell;
use tachys::{
dom::body,
view::{Mountable, Render},
};
#[cfg(feature = "hydrate")]
use tachys::{
hydration::Cursor,
view::{PositionState, RenderHtml},
};
#[cfg(feature = "hydrate")]
use wasm_bindgen::JsCast;
use web_sys::HtmlElement;
#[cfg(feature = "hydrate")]
pub fn hydrate_body<F, N>(f: F)
where
F: FnOnce() -> N + 'static,
N: IntoView,
{
let owner = hydrate_from(body(), f);
owner.forget();
}
#[cfg(feature = "hydrate")]
pub fn hydrate_lazy<F, N>(f: F)
where
F: FnOnce() -> N + 'static,
N: IntoView,
{
_ = Executor::init_wasm_bindgen();
crate::task::spawn_local(async move {
let owner = hydrate_from_async(body(), f).await;
owner.forget();
})
}
#[cfg(debug_assertions)]
thread_local! {
static FIRST_CALL: Cell<bool> = const { Cell::new(true) };
}
#[cfg(feature = "hydrate")]
pub fn hydrate_from<F, N>(parent: HtmlElement, f: F) -> UnmountHandle<N::State>
where
F: FnOnce() -> N + 'static,
N: IntoView,
{
use hydration_context::HydrateSharedContext;
use std::sync::Arc;
_ = Executor::init_wasm_bindgen();
#[cfg(debug_assertions)]
{
if !cfg!(feature = "hydrate") && FIRST_CALL.get() {
logging::warn!(
"It seems like you're trying to use Leptos in hydration mode, \
but the `hydrate` feature is not enabled on the `leptos` \
crate. Add `features = [\"hydrate\"]` to your Cargo.toml for \
the crate to work properly.\n\nNote that hydration and \
client-side rendering now use separate functions from \
leptos::mount: you are calling a hydration function."
);
}
FIRST_CALL.set(false);
}
let owner = Owner::new_root(Some(Arc::new(HydrateSharedContext::new())));
let mountable = owner.with(move || {
let view = f().into_view();
view.hydrate::<true>(
&Cursor::new(parent.unchecked_into()),
&PositionState::default(),
)
});
if let Some(sc) = Owner::current_shared_context() {
sc.hydration_complete();
}
UnmountHandle { owner, mountable }
}
#[cfg(feature = "hydrate")]
pub async fn hydrate_from_async<F, N>(
parent: HtmlElement,
f: F,
) -> UnmountHandle<N::State>
where
F: FnOnce() -> N + 'static,
N: IntoView,
{
use hydration_context::HydrateSharedContext;
use std::sync::Arc;
_ = Executor::init_wasm_bindgen();
#[cfg(debug_assertions)]
{
if !cfg!(feature = "hydrate") && FIRST_CALL.get() {
logging::warn!(
"It seems like you're trying to use Leptos in hydration mode, \
but the `hydrate` feature is not enabled on the `leptos` \
crate. Add `features = [\"hydrate\"]` to your Cargo.toml for \
the crate to work properly.\n\nNote that hydration and \
client-side rendering now use separate functions from \
leptos::mount: you are calling a hydration function."
);
}
FIRST_CALL.set(false);
}
let owner = Owner::new_root(Some(Arc::new(HydrateSharedContext::new())));
let mountable = owner
.with(move || {
use reactive_graph::computed::ScopedFuture;
ScopedFuture::new(async move {
let view = f().into_view();
view.hydrate_async(
&Cursor::new(parent.unchecked_into()),
&PositionState::default(),
)
.await
})
})
.await;
if let Some(sc) = Owner::current_shared_context() {
sc.hydration_complete();
}
UnmountHandle { owner, mountable }
}
pub fn mount_to_body<F, N>(f: F)
where
F: FnOnce() -> N + 'static,
N: IntoView,
{
let owner = mount_to(body(), f);
owner.forget();
}
pub fn mount_to<F, N>(parent: HtmlElement, f: F) -> UnmountHandle<N::State>
where
F: FnOnce() -> N + 'static,
N: IntoView,
{
_ = Executor::init_wasm_bindgen();
#[cfg(debug_assertions)]
{
if !cfg!(feature = "csr") && FIRST_CALL.get() {
logging::warn!(
"It seems like you're trying to use Leptos in client-side \
rendering mode, but the `csr` feature is not enabled on the \
`leptos` crate. Add `features = [\"csr\"]` to your \
Cargo.toml for the crate to work properly.\n\nNote that \
hydration and client-side rendering now use different \
functions from leptos::mount. You are using a client-side \
rendering mount function."
);
}
FIRST_CALL.set(false);
}
let owner = Owner::new();
let mountable = owner.with(move || {
let view = f().into_view();
let mut mountable = view.build();
mountable.mount(&parent, None);
mountable
});
UnmountHandle { owner, mountable }
}
pub fn mount_to_renderer<F, N>(
parent: &tachys::renderer::types::Element,
f: F,
) -> UnmountHandle<N::State>
where
F: FnOnce() -> N + 'static,
N: Render,
{
_ = Executor::init_wasm_bindgen();
let owner = Owner::new();
let mountable = owner.with(move || {
let view = f();
let mut mountable = view.build();
mountable.mount(parent, None);
mountable
});
UnmountHandle { owner, mountable }
}
#[cfg(feature = "hydrate")]
pub fn hydrate_islands() {
use hydration_context::{HydrateSharedContext, SharedContext};
use std::sync::Arc;
_ = Executor::init_wasm_bindgen();
#[cfg(debug_assertions)]
FIRST_CALL.set(false);
let sc = HydrateSharedContext::new();
sc.set_is_hydrating(false); let owner = Owner::new_root(Some(Arc::new(sc)));
owner.set();
std::mem::forget(owner);
}
#[must_use = "Dropping an `UnmountHandle` will unmount the view and cancel the \
reactive system. You should either call `.forget()` to keep the \
view permanently mounted, or store the `UnmountHandle` somewhere \
and drop it when you'd like to unmount the view."]
pub struct UnmountHandle<M>
where
M: Mountable,
{
#[allow(dead_code)]
owner: Owner,
mountable: M,
}
impl<M> UnmountHandle<M>
where
M: Mountable,
{
pub fn forget(self) {
std::mem::forget(self);
}
}
impl<M> Drop for UnmountHandle<M>
where
M: Mountable,
{
fn drop(&mut self) {
self.mountable.unmount();
}
}