yggdrasil/components/post/
post_header.rs1use leptos::ev::MouseEvent;
6use leptos::prelude::*;
7
8use crate::components::post::breadcrumbs::Breadcrumbs;
9use crate::components::post::post_meta::PostMeta;
10use crate::components::ui::{StatusBadge, TagChip};
11use crate::models::post::{PostHeaderData, PostStatus};
12use crate::routes::Route;
13
14#[component]
21pub fn PostHeader(post: PostHeaderData, #[prop(optional)] sample: bool) -> impl IntoView {
22 let summary = post.summary.clone().filter(|s| !s.trim().is_empty());
23 let is_draft = post.status == PostStatus::Draft;
24 let post_id = post.id.to_string();
25 let title = post.title.clone();
26 let tags = post.tags.clone();
27 let first_tag = tags.first().cloned();
28
29 let tag_list = (!tags.is_empty()).then(|| {
30 let items = tags
31 .into_iter()
32 .map(|tag| {
33 let chip = if sample {
34 view! {
35 <a class="chip" href="#" on:click=|event: MouseEvent| event.prevent_default()>
36 {tag}
37 </a>
38 }
39 .into_any()
40 } else {
41 let to = Route::TagDetail { tag: tag.clone() }.to_string();
42 view! { <TagChip label=tag to/> }.into_any()
43 };
44 view! { <li>{chip}</li> }
45 })
46 .collect_view();
47 view! { <ul class="post-tags" aria-label="文章标签">{items}</ul> }
48 });
49
50 view! {
51 <header class="post-header">
52 <Breadcrumbs tag=first_tag sample/>
53 <h1 class="post-title" data-vt-post-id=post_id data-vt-role="title">
54 {title}
55 {is_draft
56 .then(|| {
57 view! { <StatusBadge color_class="badge-warning" label="草稿" class="post-draft"/> }
58 })}
59 </h1>
60 {summary.map(|summary| view! { <p class="post-lede">{summary}</p> })}
61 <PostMeta post=post/>
62 {tag_list}
63 </header>
64 }
65}