Rustelo/features/content-graph/templates/components/related_content.rs
Jesús Pérez c0281e759c
chore: update
2026-07-18 20:14:32 +01:00

79 lines
3.2 KiB
Rust

// content_graph/related_content.rs
//
// Sidebar widget showing content related to the current node.
// Pure HTML — no JS, fully SSR-safe.
// Data comes from RELATED_NODES static map generated at build time.
use leptos::prelude::*;
use super::generated::related::{RelatedNode, RELATED_NODES};
// ── Kind display helpers ──────────────────────────────────────────────────────
fn kind_label(kind: &str) -> &'static str {
match kind {
"Implements" | "ImplementedBy" => "implements",
"RelatedTo" | "RelatedFrom" => "related",
"PartOf" | "ParentOf" => "part of",
"References" | "ReferencedBy" => "references",
"Extends" | "ExtendedBy" => "extends",
"ManifestsIn" => "manifests in",
"Resolves" => "resolves",
"Complements" => "complements",
"DependsOn" => "depends on",
"DocumentedIn" => "documented in",
_ => kind,
}
}
fn kind_css(kind: &str) -> &'static str {
match kind {
"Implements" | "ImplementedBy" => "ds-badge-primary",
"RelatedTo" | "RelatedFrom" => "ds-badge-secondary",
"PartOf" | "ParentOf" => "ds-badge-accent",
"References" | "ReferencedBy" => "ds-badge-neutral",
_ => "ds-badge-ghost",
}
}
// ── Component ─────────────────────────────────────────────────────────────────
/// Sidebar list of related content items for a given node.
///
/// Renders nothing if the node has no relationships.
#[component]
pub fn RelatedContent(node_id: String) -> impl IntoView {
let related: &'static [RelatedNode] = RELATED_NODES
.iter()
.find(|(id, _)| *id == node_id.as_str())
.map(|(_, nodes)| *nodes)
.unwrap_or(&[]);
if related.is_empty() {
return view! { <></> }.into_any();
}
view! {
<aside class="content-graph-related ds-card ds-bg-base-200 p-4 space-y-3">
<h3 class="text-sm font-semibold ds-text uppercase tracking-wide opacity-60">
"Related"
</h3>
<ul class="space-y-2">
{related.iter().map(|node| {
let badge = kind_css(node.kind);
let label = kind_label(node.kind);
view! {
<li class="flex items-start gap-2">
<span class=format!("ds-badge text-xs mt-0.5 shrink-0 {badge}")>
{label}
</span>
<span class="text-sm ds-text leading-snug">
{node.title}
</span>
</li>
}
}).collect_view()}
</ul>
</aside>
}.into_any()
}