1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
use crate::{ChildrenFn, Show};
use core::time::Duration;
use leptos::component;
use leptos_dom::{helpers::TimeoutHandle, IntoView};
use leptos_macro::view;
use leptos_reactive::{
create_effect, on_cleanup, signal_prelude::*, store_value, StoredValue,
};
/// A component that will show its children when the `when` condition is `true`.
/// Additionally, you need to specify a `hide_delay`. If the `when` condition changes to `false`,
/// the unmounting of the children will be delayed by the specified Duration.
/// If you provide the optional `show_class` and `hide_class`, you can create very easy mount /
/// unmount animations.
///
/// ```rust
/// # use core::time::Duration;
/// # use leptos::*;
/// # #[component]
/// # pub fn App() -> impl IntoView {
/// let show = create_rw_signal(false);
///
/// view! {
/// <div
/// class="hover-me"
/// on:mouseenter=move |_| show.set(true)
/// on:mouseleave=move |_| show.set(false)
/// >
/// "Hover Me"
/// </div>
///
/// <AnimatedShow
/// when=show
/// show_class="fade-in-1000"
/// hide_class="fade-out-1000"
/// hide_delay=Duration::from_millis(1000)
/// >
/// <div class="here-i-am">
/// "Here I Am!"
/// </div>
/// </AnimatedShow>
/// }
/// # }
/// ```
#[cfg_attr(
any(debug_assertions, feature = "ssr"),
tracing::instrument(level = "info", skip_all)
)]
#[component]
pub fn AnimatedShow(
/// The components Show wraps
children: ChildrenFn,
/// If the component should show or not
#[prop(into)]
when: MaybeSignal<bool>,
/// Optional CSS class to apply if `when == true`
#[prop(optional)]
show_class: &'static str,
/// Optional CSS class to apply if `when == false`
#[prop(optional)]
hide_class: &'static str,
/// The timeout after which the component will be unmounted if `when == false`
hide_delay: Duration,
) -> impl IntoView {
let handle: StoredValue<Option<TimeoutHandle>> = store_value(None);
let cls = create_rw_signal(if when.get_untracked() {
show_class
} else {
hide_class
});
let show = create_rw_signal(when.get_untracked());
create_effect(move |_| {
if when.get() {
// clear any possibly active timer
if let Some(h) = handle.get_value() {
h.clear();
}
cls.set(show_class);
show.set(true);
} else {
cls.set(hide_class);
let h = leptos_dom::helpers::set_timeout_with_handle(
move || show.set(false),
hide_delay,
)
.expect("set timeout in AnimatedShow");
handle.set_value(Some(h));
}
});
on_cleanup(move || {
if let Some(Some(h)) = handle.try_get_value() {
h.clear();
}
});
view! {
<Show when=move || show.get() fallback=|| ()>
<div class=move || cls.get()>{children()}</div>
</Show>
}
}