Skip to content

Commit

Permalink
feat: optional fallbacks for Show, Suspense, and Transition (#1817
Browse files Browse the repository at this point in the history
)
  • Loading branch information
maccesch committed Oct 2, 2023
1 parent 6a547cb commit 6d44540
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 27 deletions.
4 changes: 4 additions & 0 deletions leptos/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -230,8 +230,12 @@ pub use wasm_bindgen; // used in islands
feature = "template_macro"
))]
pub use web_sys; // used in islands

mod children;
mod view_fn;
pub use children::*;
pub use view_fn::*;

extern crate self as leptos;

/// A type for taking anything that implements [`IntoAttribute`].
Expand Down
18 changes: 7 additions & 11 deletions leptos/src/show.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
use leptos::{component, ChildrenFn};
use leptos::{component, ChildrenFn, ViewFn};
use leptos_dom::IntoView;
use leptos_reactive::{create_memo, signal_prelude::*};

/// A component that will show its children when the `when` condition is `true`,
/// and show the fallback when it is `false`, without rerendering every time
/// the condition changes.
///
/// *Note*: Because of the nature of generic arguments, it’s not really possible
/// to make the `fallback` optional. If you want an empty fallback state—in other
/// words, if you want to show the children if `when` is true and noting otherwise—use
/// `fallback=|_| ()` (i.e., a fallback function that returns the unit type `()`).
/// The fallback prop is optional and defaults to rendering nothing.
///
/// ```rust
/// # use leptos_reactive::*;
Expand All @@ -34,23 +31,22 @@ use leptos_reactive::{create_memo, signal_prelude::*};
tracing::instrument(level = "info", skip_all)
)]
#[component]
pub fn Show<F, W, IV>(
pub fn Show<W>(
/// The children will be shown whenever the condition in the `when` closure returns `true`.
children: ChildrenFn,
/// A closure that returns a bool that determines whether this thing runs
when: W,
/// A closure that returns what gets rendered if the when statement is false
fallback: F,
/// A closure that returns what gets rendered if the when statement is false. By default this is the empty view.
#[prop(optional, into)]
fallback: ViewFn,
) -> impl IntoView
where
W: Fn() -> bool + 'static,
F: Fn() -> IV + 'static,
IV: IntoView,
{
let memoized_when = create_memo(move |_| when());

move || match memoized_when.get() {
true => children().into_view(),
false => fallback().into_view(),
false => fallback.run(),
}
}
12 changes: 6 additions & 6 deletions leptos/src/suspense_component.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use leptos::ViewFn;
use leptos_dom::{DynChild, HydrationCtx, IntoView};
use leptos_macro::component;
#[cfg(any(feature = "csr", feature = "hydrate"))]
Expand Down Expand Up @@ -57,15 +58,14 @@ use std::rc::Rc;
tracing::instrument(level = "info", skip_all)
)]
#[component]
pub fn Suspense<F, E, V>(
/// Returns a fallback UI that will be shown while `async` [`Resource`](leptos_reactive::Resource)s are still loading.
fallback: F,
pub fn Suspense<V>(
/// Returns a fallback UI that will be shown while `async` [`Resource`](leptos_reactive::Resource)s are still loading. By default this is the empty view.
#[prop(optional, into)]
fallback: ViewFn,
/// Children will be displayed once all `async` [`Resource`](leptos_reactive::Resource)s have resolved.
children: Rc<dyn Fn() -> V>,
) -> impl IntoView
where
F: Fn() -> E + 'static,
E: IntoView,
V: IntoView + 'static,
{
let orig_children = children;
Expand All @@ -89,7 +89,7 @@ where
let fallback = create_memo({
move |_| {
provide_context(context);
fallback().into_view()
fallback.run()
}
});

Expand Down
18 changes: 8 additions & 10 deletions leptos/src/transition.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use leptos::ViewFn;
use leptos_dom::{Fragment, HydrationCtx, IntoView, View};
use leptos_macro::component;
use leptos_reactive::{
Expand Down Expand Up @@ -66,21 +67,18 @@ use std::{
tracing::instrument(level = "info", skip_all)
)]
#[component(transparent)]
pub fn Transition<F, E>(
/// Will be displayed while resources are pending.
fallback: F,
pub fn Transition(
/// Will be displayed while resources are pending. By default this is the empty view.
#[prop(optional, into)]
fallback: ViewFn,
/// A function that will be called when the component transitions into or out of
/// the `pending` state, with its argument indicating whether it is pending (`true`)
/// or not pending (`false`).
#[prop(optional, into)]
set_pending: Option<SignalSetter<bool>>,
/// Will be displayed once all resources have resolved.
children: Box<dyn Fn() -> Fragment>,
) -> impl IntoView
where
F: Fn() -> E + 'static,
E: IntoView,
{
) -> impl IntoView {
let prev_children = Rc::new(RefCell::new(None::<View>));

let first_run = create_rw_signal(true);
Expand All @@ -105,12 +103,12 @@ where

if let Some(prev_children) = &*prev_child.borrow() {
if is_first_run || was_first_run {
fallback().into_view()
fallback.run()
} else {
prev_children.clone()
}
} else {
fallback().into_view()
fallback.run()
}
}
})
Expand Down
30 changes: 30 additions & 0 deletions leptos/src/view_fn.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
use leptos_dom::{IntoView, View};
use std::rc::Rc;

/// New-type wrapper for the a function that returns a view with `From` and `Default` traits implemented
/// to enable optional props in for example `<Show>` and `<Suspense>`.
#[derive(Clone)]
pub struct ViewFn(Rc<dyn Fn() -> View>);

impl Default for ViewFn {
fn default() -> Self {
Self(Rc::new(|| ().into_view()))
}
}

impl<F, IV> From<F> for ViewFn
where
F: Fn() -> IV + 'static,
IV: IntoView,
{
fn from(value: F) -> Self {
Self(Rc::new(move || value().into_view()))
}
}

impl ViewFn {
/// Execute the wrapped function
pub fn run(&self) -> View {
(self.0)()
}
}

0 comments on commit 6d44540

Please sign in to comment.