use leptos::*;
use std::any::{
Any,
TypeId,
};
api_planning! {
struct PortalA;
struct PortalB;
view! { cx,
<PortalProvider>
</PortalProvider>
}
view! { cx,
<h1>"Where'd these come from???"</h1>
<PortalOutput id=PortalA />
<PortalOutput id=PortalB />
}
view! { cx,
<PortalInput id=PortalA>
<p>"I used a portal to get here..."</p>
</PortalInput>
<PortalInput id=PortalB>
<p>"The cake was really nice"</p>
</PortalInput>
}
}
type Children = Box<dyn Fn(Scope) -> Fragment>;
const CONTEXT_NOT_FOUND_ERROR_MESSAGE: &str =
"failed to find `PortalCtx`, make sure you are using `<PortalProvider />` \
somewhere near the root of the app";
#[derive(Clone)]
struct PortalCtx(StoredValue<Vec<(TypeId, RwSignal<Option<Children>>)>>);
#[component]
pub fn PortalProvider(
cx: Scope,
children: Children,
) -> impl IntoView {
provide_context(cx, PortalCtx(store_value(cx, Default::default())));
children(cx)
}
#[component]
pub fn PortalInput<T>(
cx: Scope,
id: T,
children: Children,
) -> impl IntoView
where
T: Any,
{
let portal_ctx =
use_context::<PortalCtx>(cx).expect(CONTEXT_NOT_FOUND_ERROR_MESSAGE);
portal_ctx.0.update_value(|portals| {
if let Some(pos) = portals
.iter()
.position(|(type_id, _)| *type_id == id.type_id())
{
portals[pos].1.set(Some(children));
} else {
let children = create_rw_signal(cx, Some(children));
portals.push((id.type_id(), children));
}
});
}
#[component]
pub fn PortalOutput<T>(
cx: Scope,
id: T,
) -> impl IntoView
where
T: Any,
{
let portal_ctx =
use_context::<PortalCtx>(cx).expect(CONTEXT_NOT_FOUND_ERROR_MESSAGE);
let mut children = None;
portal_ctx.0.update_value(|portals| {
let children_signal = if let Some(pos) = portals
.iter()
.position(|(type_id, _)| *type_id == id.type_id())
{
portals[pos].1
} else {
let children = create_rw_signal(cx, None);
portals.push((id.type_id(), children));
children
};
children = Some(children_signal);
});
let children = children.unwrap();
move || {
children.with(|children| {
if let Some(children) = children {
children(cx).into_view(cx)
} else {
().into_view(cx)
}
})
}
}