yggdrasil/components/
post_card.rs1use dioxus::prelude::*;
6use dioxus::router::components::Link;
7
8use crate::models::post::PostListItem;
9use crate::router::Route;
10
11#[component]
28pub fn PostCard(post: PostListItem) -> Element {
29 let post_slug = post.slug.clone();
30 let date_str = post.formatted_date();
31 let cover_src = post.cover_image.clone().unwrap_or_default();
32 let has_cover = post.cover_image.is_some();
33
34 rsx! {
35 article { class: "group relative mb-12 flex flex-col bg-[var(--color-paper-entry)] rounded-[2rem] border border-transparent hover:border-[var(--color-paper-border)] hover:shadow-sm overflow-hidden transition-all duration-300",
36 if has_cover {
37 div { class: "w-full overflow-hidden",
38 div { class: "blur-img post-card-cover-blur !rounded-none",
39 img {
40 class: "blur-img-placeholder",
41 src: "{cover_src}?w=20",
42 alt: "{post.title}",
43 loading: "lazy",
44 }
45 img {
46 class: "blur-img-full is-loaded",
47 src: "{cover_src}?thumb=420x180",
48 alt: "{post.title}",
49 }
50 }
51 }
52 }
53 div { class: "p-8 flex flex-col gap-3",
54 h2 { class: "text-2xl md:text-3xl font-extrabold tracking-tight leading-tight text-[var(--color-paper-primary)] group-hover:text-[var(--color-paper-accent)] transition-colors duration-200",
55 "{post.title}"
56 }
57 div { class: "mt-1 text-base text-[var(--color-paper-secondary)] leading-relaxed line-clamp-2",
58 "{post.summary.as_deref().unwrap_or_default()}"
59 }
60 div { class: "mt-4 flex flex-wrap items-center gap-3 text-sm font-medium text-[var(--color-paper-tertiary)]",
61 span { class: "tracking-wide", "{date_str}" }
62 if !post.tags.is_empty() {
63 span { "·" }
64 for tag in post.tags.clone().into_iter() {
65 span { key: "{tag}",
66 Link {
67 class: "relative z-10 px-3 py-1 rounded-full border border-[var(--color-paper-border)] hover:bg-[var(--color-paper-accent)] hover:border-[var(--color-paper-accent)] hover:text-white transition-all duration-200",
68 to: Route::TagDetail {
69 tag: tag.clone(),
70 },
71 onclick: move |evt: dioxus::events::MouseEvent| evt.stop_propagation(),
72 "{tag}"
73 }
74 }
75 }
76 }
77 }
78 }
79 Link {
84 class: "absolute inset-0 z-[2]",
85 aria_label: "post link to {post.title}",
86 to: Route::PostDetail {
87 slug: post_slug,
88 },
89 }
90 }
91 }
92}