Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,219 changes: 763 additions & 456 deletions examples/axum_js_ssr/src/app.rs

Large diffs are not rendered by default.

27 changes: 11 additions & 16 deletions examples/hackernews/src/routes/story.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,24 +23,20 @@ pub fn Story() -> impl IntoView {
None => Either::Left("Story not found."),
Some(story) => {
Either::Right(view! {
<Meta name="description" content=story.title.clone()/>
<Meta name="description" content=story.title.clone() />
<div class="item-view">
<div class="item-view-header">
<a href=story.url target="_blank">
<h1>{story.title}</h1>
</a>
<span class="host">"(" {story.domain} ")"</span>
{story
.user
.map(|user| {
view! {
<p class="meta">
{story.points} " points | by "
<A href=format!("/users/{user}")>{user.clone()}</A>
{format!(" {}", story.time_ago)}
</p>
}
})}
<ShowLet some=move || story.user.clone() let:user>
<p class="meta">
{story.points} " points | by "
<A href=format!("/users/{user}")>{user.clone()}</A>
{format!(" {}", story.time_ago)}
</p>
</ShowLet>
</div>
<div class="item-view-comments">
<p class="item-view-comments-header">
Expand All @@ -57,7 +53,7 @@ pub fn Story() -> impl IntoView {
key=|comment| comment.id
let:comment
>
<Comment comment/>
<Comment comment />
</For>
</ul>
</div>
Expand Down Expand Up @@ -110,8 +106,7 @@ pub fn Comment(comment: api::Comment) -> impl IntoView {
</a>
</div>
{move || {
open
.get()
open.get()
.then({
let comments = comment.comments.clone();
move || {
Expand All @@ -122,7 +117,7 @@ pub fn Comment(comment: api::Comment) -> impl IntoView {
key=|comment| comment.id
let:comment
>
<Comment comment/>
<Comment comment />
</For>
</ul>
}
Expand Down
126 changes: 73 additions & 53 deletions examples/hackernews_axum/src/routes/stories.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,30 +50,41 @@ pub fn Stories() -> impl IntoView {
<div class="news-view">
<div class="news-list-nav">
<span>
{move || if page() > 1 {
Either::Left(view! {
<a class="page-link"
href=move || format!("/{}?page={}", story_type(), page() - 1)
aria-label="Previous Page"
>
"< prev"
</a>
})
} else {
Either::Right(view! {
<span class="page-link disabled" aria-hidden="true">
"< prev"
</span>
})
{move || {
if page() > 1 {
Either::Left(
view! {
<a
class="page-link"
href=move || {
format!("/{}?page={}", story_type(), page() - 1)
}
aria-label="Previous Page"
>
"< prev"
</a>
},
)
} else {
Either::Right(
view! {
<span class="page-link disabled" aria-hidden="true">
"< prev"
</span>
},
)
}
}}
</span>
<span>"page " {page}</span>
<Suspense>
<span class="page-link"
<span
class="page-link"
class:disabled=hide_more_link
aria-hidden=hide_more_link
>
<a href=move || format!("/{}?page={}", story_type(), page() + 1)
<a
href=move || format!("/{}?page={}", story_type(), page() + 1)
aria-label="Next Page"
>
"more >"
Expand All @@ -83,21 +94,17 @@ pub fn Stories() -> impl IntoView {
</div>
<main class="news-list">
<div>
<Transition
fallback=move || view! { <p>"Loading..."</p> }
set_pending
>
<Show when=move || stories.read().as_ref().map(Option::is_none).unwrap_or(false)>
>
<p>"Error loading stories."</p>
</Show>
<Transition fallback=move || view! { <p>"Loading..."</p> } set_pending>
<Show when=move || {
stories.read().as_ref().map(Option::is_none).unwrap_or(false)
}>> <p>"Error loading stories."</p></Show>
<ul>
<For
each=move || stories.get().unwrap_or_default().unwrap_or_default()
key=|story| story.id
let:story
>
<Story story/>
<Story story />
</For>
</ul>
</Transition>
Expand All @@ -110,18 +117,20 @@ pub fn Stories() -> impl IntoView {
#[component]
fn Story(story: api::Story) -> impl IntoView {
view! {
<li class="news-item">
<li class="news-item">
<span class="score">{story.points}</span>
<span class="title">
{if !story.url.starts_with("item?id=") {
Either::Left(view! {
<span>
<a href=story.url target="_blank" rel="noreferrer">
{story.title.clone()}
</a>
<span class="host">"("{story.domain}")"</span>
</span>
})
Either::Left(
view! {
<span>
<a href=story.url target="_blank" rel="noreferrer">
{story.title.clone()}
</a>
<span class="host">"("{story.domain}")"</span>
</span>
},
)
} else {
let title = story.title.clone();
Either::Right(view! { <A href=format!("/stories/{}", story.id)>{title}</A> })
Expand All @@ -130,29 +139,40 @@ fn Story(story: api::Story) -> impl IntoView {
<br />
<span class="meta">
{if story.story_type != "job" {
Either::Left(view! {
<span>
{"by "}
{story.user.map(|user| view ! { <A href=format!("/users/{user}")>{user.clone()}</A>})}
{format!(" {} | ", story.time_ago)}
<A href=format!("/stories/{}", story.id)>
{if story.comments_count.unwrap_or_default() > 0 {
format!("{} comments", story.comments_count.unwrap_or_default())
} else {
"discuss".into()
}}
</A>
</span>
})
Either::Left(
view! {
<span>
{"by "} <ShowLet some=move || story.user.clone() let:user>
<A href=format!("/users/{user}")>{user.clone()}</A>
</ShowLet> {format!(" {} | ", story.time_ago)}
<A href=format!(
"/stories/{}",
story.id,
)>
{if story.comments_count.unwrap_or_default() > 0 {
format!(
"{} comments",
story.comments_count.unwrap_or_default(),
)
} else {
"discuss".into()
}}
</A>
</span>
},
)
} else {
let title = story.title.clone();
Either::Right(view! { <A href=format!("/item/{}", story.id)>{title}</A> })
}}
</span>
{(story.story_type != "link").then(|| view! {
" "
<span class="label">{story.story_type}</span>
})}
{(story.story_type != "link")
.then(|| {
view! {
" "
<span class="label">{story.story_type}</span>
}
})}
</li>
}
}
47 changes: 31 additions & 16 deletions leptos/src/animated_show.rs
Original file line number Diff line number Diff line change
@@ -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`.
Expand Down Expand Up @@ -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,
Expand All @@ -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();
Expand Down Expand Up @@ -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>
}
}
Loading
Loading