yggdrasil/components/comments/card.rs
1//! 评论卡片外壳组件
2//!
3//! `CommentItem`(已审核)与 `PendingCommentItem`(待审核)共享的卡片骨架:
4//! 深度缩进(孤儿评论归零、`--comment-indent` 阶梯、子评论左边框)、
5//! 头像 + 内容两栏布局、作者名/时间头部行。两个调用方只负责各自的差异部分
6//! (作者徽章、状态徽章、时间展示、正文 HTML、正文下方的附加区域)。
7
8use leptos::prelude::*;
9
10use crate::components::ui::UserAvatar;
11
12/// 评论卡片外壳组件。
13///
14/// Props:
15/// - `depth`:归一化后的嵌套深度(孤儿评论已由调用方按 0 处理)
16/// - `muted`:是否降低整体透明度展示(待审核评论用,缺省 `false`)
17/// - `avatar_url` / `author_name`:头像 URL 与无障碍 alt 文本
18/// - `author_element`:作者名展示节点(链接或纯文本,由调用方根据 `author_url` 构建)
19/// - `author_badge`:作者名与分隔符之间的徽章(如已审核评论的"作者"标签);
20/// 无需展示时传 `().into_any()`
21/// - `timestamp`:时间展示节点(由调用方构建)
22/// - `status_badge`:时间之后的状态徽章(如待审核评论的"审核中"标签);
23/// 无需展示时传 `().into_any()`
24/// - `content_html`:已渲染为安全 HTML 的评论正文
25/// - `content_extra_class`:正文容器附加 CSS 类(已审核评论需额外挂 `md-content`
26/// 以命中全局 Markdown 排版/代码高亮 CSS 作用域,缺省空串)
27/// - `children`:正文下方的附加区域(如已审核评论的回复按钮与回复表单)
28///
29/// 样式见 `styles/components/comments.css`:顶层评论之间以发丝线分隔,回复左侧一条
30/// 细线并按 `--comment-indent` 缩进。
31#[component]
32pub fn CommentCardShell(
33 depth: i32,
34 #[prop(optional)] muted: bool,
35 #[prop(into)] avatar_url: String,
36 #[prop(into)] author_name: String,
37 author_element: AnyView,
38 author_badge: AnyView,
39 timestamp: AnyView,
40 status_badge: AnyView,
41 #[prop(into)] content_html: String,
42 #[prop(optional)] content_extra_class: &'static str,
43 #[prop(optional)] children: Option<Children>,
44) -> impl IntoView {
45 let mut class = String::from("comment");
46 if depth > 0 {
47 class.push_str(" is-child");
48 }
49 if muted {
50 class.push_str(" is-pending");
51 }
52 // 回复按层级缩进,五层后不再加深,深层回复不至于被挤到最右。
53 let style = (depth > 1).then(|| format!("--comment-indent: {};", depth.min(5) - 1));
54
55 view! {
56 <div class=class style=style>
57 <UserAvatar name=author_name avatar_url=Some(avatar_url) class="comment-avatar"/>
58 <div class="comment-main">
59 <div class="comment-head">
60 {author_element}
61 {author_badge}
62 {timestamp}
63 {status_badge}
64 </div>
65 <div
66 class=format!("comment-body {content_extra_class}")
67 inner_html=content_html
68 ></div>
69 {children.map(|children| children())}
70 </div>
71 </div>
72 }
73}