mirror of
https://github.com/leptos-rs/leptos.git
synced 2025-12-27 07:34:35 -05:00
made <Show> accept signals in addition to closures (#4236)
This commit is contained in:
committed by
GitHub
parent
1d0f668dc3
commit
db9f323f8d
@@ -1,14 +1,18 @@
|
||||
use crate::{children::ChildrenFn, component, control_flow::Show, IntoView};
|
||||
use crate::{
|
||||
children::ChildrenFn, component, control_flow::Show, show::IntoCondition,
|
||||
IntoView,
|
||||
};
|
||||
use core::time::Duration;
|
||||
use leptos_dom::helpers::TimeoutHandle;
|
||||
use leptos_macro::view;
|
||||
use reactive_graph::{
|
||||
diagnostics::SpecialNonReactiveZone,
|
||||
effect::RenderEffect,
|
||||
owner::{on_cleanup, StoredValue},
|
||||
signal::RwSignal,
|
||||
traits::{Get, GetUntracked, GetValue, Set, SetValue},
|
||||
wrappers::read::Signal,
|
||||
traits::{GetValue, Set, SetValue},
|
||||
};
|
||||
use std::marker::PhantomData;
|
||||
use tachys::prelude::*;
|
||||
|
||||
/// A component that will show its children when the `when` condition is `true`.
|
||||
@@ -46,14 +50,16 @@ use tachys::prelude::*;
|
||||
/// }
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// Please note, that unlike `Show`, `AnimatedShow` does not support a `fallback` prop.
|
||||
#[cfg_attr(feature = "tracing", tracing::instrument(level = "trace", skip_all))]
|
||||
#[component]
|
||||
pub fn AnimatedShow(
|
||||
pub fn AnimatedShow<M>(
|
||||
/// The components Show wraps
|
||||
children: ChildrenFn,
|
||||
/// If the component should show or not
|
||||
#[prop(into)]
|
||||
when: Signal<bool>,
|
||||
/// When true the children are shown.
|
||||
/// It accepts a closure that returns a boolean value as well as a boolean signal or plain boolean value.
|
||||
when: impl IntoCondition<M>,
|
||||
/// Optional CSS class to apply if `when == true`
|
||||
#[prop(optional)]
|
||||
show_class: &'static str,
|
||||
@@ -62,17 +68,26 @@ pub fn AnimatedShow(
|
||||
hide_class: &'static str,
|
||||
/// The timeout after which the component will be unmounted if `when == false`
|
||||
hide_delay: Duration,
|
||||
|
||||
/// Marker for generic parameters. Ignore this.
|
||||
#[prop(optional)]
|
||||
_marker: PhantomData<M>,
|
||||
) -> impl IntoView {
|
||||
let when = when.into_condition();
|
||||
|
||||
// Silence warnings about using signals in non-reactive contexts.
|
||||
#[cfg(debug_assertions)]
|
||||
let z = SpecialNonReactiveZone::enter();
|
||||
|
||||
let handle: StoredValue<Option<TimeoutHandle>> = StoredValue::new(None);
|
||||
let cls = RwSignal::new(if when.get_untracked() {
|
||||
show_class
|
||||
} else {
|
||||
hide_class
|
||||
});
|
||||
let show = RwSignal::new(when.get_untracked());
|
||||
let cls = RwSignal::new(if when.run() { show_class } else { hide_class });
|
||||
let show = RwSignal::new(when.run());
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
drop(z);
|
||||
|
||||
let eff = RenderEffect::new(move |_| {
|
||||
if when.get() {
|
||||
if when.run() {
|
||||
// clear any possibly active timer
|
||||
if let Some(h) = handle.get_value() {
|
||||
h.clear();
|
||||
@@ -100,8 +115,8 @@ pub fn AnimatedShow(
|
||||
});
|
||||
|
||||
view! {
|
||||
<Show when=move || show.get() fallback=|| ()>
|
||||
<div class=move || cls.get()>{children()}</div>
|
||||
<Show when=show>
|
||||
<div class=cls>{children()}</div>
|
||||
</Show>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,26 +1,71 @@
|
||||
use crate::{
|
||||
children::{TypedChildrenFn, ViewFn},
|
||||
prelude::{FunctionMarker, SignalMarker},
|
||||
IntoView,
|
||||
};
|
||||
use leptos_macro::component;
|
||||
use reactive_graph::{computed::ArcMemo, traits::Get};
|
||||
use std::{marker::PhantomData, sync::Arc};
|
||||
use tachys::either::Either;
|
||||
|
||||
/// Shows its children whenever the condition `when` prop is `true`.
|
||||
/// Otherwise it renders the `fallback` prop, which defaults to the empty view.
|
||||
///
|
||||
/// The prop `when` can be a closure that returns a bool, a signal of type bool, or a boolean value.
|
||||
///
|
||||
/// ## Usage
|
||||
///
|
||||
/// ```
|
||||
/// # use leptos::prelude::*;
|
||||
/// #
|
||||
/// # #[component]
|
||||
/// # pub fn Demo() -> impl IntoView {
|
||||
/// let (condition, set_condition) = signal(true);
|
||||
///
|
||||
/// view! {
|
||||
/// <Show when=condition>
|
||||
/// <p>"Hello, world!"</p>
|
||||
/// </Show>
|
||||
/// }
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// Or with a closure as the `when` condition:
|
||||
///
|
||||
/// ```
|
||||
/// # use leptos::prelude::*;
|
||||
/// #
|
||||
/// # #[component]
|
||||
/// # pub fn Demo() -> impl IntoView {
|
||||
/// let (condition, set_condition) = signal(true);
|
||||
///
|
||||
/// view! {
|
||||
/// <Show when=move || condition.get()>
|
||||
/// <p>"Hello, world!"</p>
|
||||
/// </Show>
|
||||
/// }
|
||||
/// # }
|
||||
/// ```
|
||||
#[component]
|
||||
pub fn Show<W, C>(
|
||||
pub fn Show<M, C>(
|
||||
/// The children will be shown whenever the condition in the `when` closure returns `true`.
|
||||
children: TypedChildrenFn<C>,
|
||||
/// A closure that returns a bool that determines whether this thing runs
|
||||
when: W,
|
||||
/// When true the children are shown, otherwise the fallback.
|
||||
/// It accepts a closure that returns a boolean value as well as a boolean signal or plain boolean value.
|
||||
when: impl IntoCondition<M>,
|
||||
/// 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,
|
||||
|
||||
/// Marker for generic parameters. Ignore this.
|
||||
#[prop(optional)]
|
||||
_marker: PhantomData<M>,
|
||||
) -> impl IntoView
|
||||
where
|
||||
W: Fn() -> bool + Send + Sync + 'static,
|
||||
C: IntoView + 'static,
|
||||
{
|
||||
let memoized_when = ArcMemo::new(move |_| when());
|
||||
let when = when.into_condition();
|
||||
let memoized_when = ArcMemo::new(move |_| when.run());
|
||||
let children = children.into_inner();
|
||||
|
||||
move || match memoized_when.get() {
|
||||
@@ -28,3 +73,44 @@ where
|
||||
false => Either::Right(fallback.run()),
|
||||
}
|
||||
}
|
||||
|
||||
/// A closure that returns a bool. Can be converted from a closure, a signal, or a boolean value.
|
||||
pub struct Condition(Arc<dyn Fn() -> bool + Send + Sync + 'static>);
|
||||
|
||||
impl Condition {
|
||||
/// Evaluates the condition and returns its result.
|
||||
pub fn run(&self) -> bool {
|
||||
(self.0)()
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait to convert various types into a `Condition`.
|
||||
/// Implemented for closures, signals, and boolean values.
|
||||
pub trait IntoCondition<M> {
|
||||
/// Does the conversion
|
||||
fn into_condition(self) -> Condition;
|
||||
}
|
||||
|
||||
impl<S> IntoCondition<SignalMarker> for S
|
||||
where
|
||||
S: Get<Value = bool> + Send + Sync + 'static,
|
||||
{
|
||||
fn into_condition(self) -> Condition {
|
||||
Condition(Arc::new(move || self.get()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<F> IntoCondition<FunctionMarker> for F
|
||||
where
|
||||
F: Fn() -> bool + Send + Sync + 'static,
|
||||
{
|
||||
fn into_condition(self) -> Condition {
|
||||
Condition(Arc::new(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoCondition<Condition> for Condition {
|
||||
fn into_condition(self) -> Condition {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user