Skip to main content

yggdrasil/components/post/
post_header.rs

1//! 文章页眉组件
2//!
3//! 面包屑、衬线标题、导语、元信息与标签,末尾一条发丝线与正文分开。
4
5use 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/// 文章页眉组件。
15///
16/// Props:
17/// - `post`:页眉展示所需的最小字段集(P4.2:不携带完整正文,调用方用
18///   `PostHeaderData::from(&post)` 构造)
19/// - `sample`:图鉴展示非跳转的样例链接;正式文章默认 false。
20#[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}