tablo_ui/components/composites/
stat.rs1use topcoat::{
2 Result,
3 icon::{IconData, icon as icon_view},
5 view::{Attributes, StaticClass, View, class, component, view},
6};
7
8const STAT: StaticClass = class!(
9 "flex flex-col gap-3 rounded-xl border border-border bg-card p-5 text-card-foreground",
10 "shadow-xs outline-none transition-colors focus-visible:ring-2 focus-visible:ring-ring",
11);
12const STAT_LINK: StaticClass = class!("hover:border-foreground/20 hover:bg-muted/40");
14const STAT_LABEL: StaticClass = class!(
15 "flex items-center gap-2 text-sm font-medium text-muted-foreground",
16 "[&>svg]:size-4 [&>svg]:shrink-0",
17);
18const STAT_VALUE: StaticClass =
19 class!("text-3xl leading-none font-semibold tracking-tight text-foreground tabular-nums");
20const STAT_DETAIL: StaticClass = class!("text-sm text-muted-foreground");
21
22#[component]
48pub async fn stat(
49 #[into]
51 label: String,
52 #[into]
54 value: String,
55 #[into]
57 #[default]
58 detail: String,
59 #[into]
61 #[default]
62 icon: Option<IconData>,
63 #[into]
65 #[default]
66 href: Option<String>,
67 #[default]
69 mut attrs: Attributes,
70) -> Result<impl View> {
71 let body = view! {
72 <p class=(STAT_LABEL)>
73 if let Some(glyph) = icon {
74 icon_view(
75 data: glyph,
76 attrs: topcoat::view::attributes! { aria-hidden="true" }
77 )
78 }
79 (label)
80 </p>
81 <p class=(STAT_VALUE)>(value)</p>
82 if !detail.is_empty() {
83 <p class=(STAT_DETAIL)>(detail)</p>
84 }
85 };
86 Ok(view! {
87 if let Some(href) = href {
88 <a
89 href=(href)
90 class=(class!(STAT, STAT_LINK, attrs.remove("class")))
91 (attrs)
92 >
93 (body)
94 </a>
95 } else {
96 <div class=(class!(STAT, attrs.remove("class"))) (attrs)>(body)</div>
97 }
98 })
99}