tablo_ui/components/composites/
empty_state.rs1use topcoat::{
2 Result,
3 icon::icon,
4 view::{Attributes, Child, StaticClass, View, class, component, view},
5};
6
7const EMPTY_STATE: StaticClass = class!("flex flex-col items-center gap-2 px-6 py-12 text-center");
8const EMPTY_STATE_ICON: StaticClass = class!(
9 "mb-1 flex size-10 items-center justify-center rounded-full bg-muted \
10 text-muted-foreground [&>svg]:size-5"
11);
12const EMPTY_STATE_TITLE: StaticClass = class!("text-sm font-medium text-foreground");
13const EMPTY_STATE_DETAIL: StaticClass = class!("text-sm text-muted-foreground");
14const EMPTY_STATE_ACTION: StaticClass =
15 class!("mt-2 flex flex-wrap items-center justify-center gap-2");
16
17#[component]
23pub async fn empty_state(
24 #[into]
26 title: String,
27 #[into]
29 #[default]
30 detail: String,
31 #[default]
33 action: Option<Child<'_>>,
34 #[default]
36 mut attrs: Attributes,
37) -> Result<impl View> {
38 Ok(view! {
39 <div class=(class!(EMPTY_STATE, attrs.remove("class"))) (attrs)>
40 <div class=(EMPTY_STATE_ICON) aria-hidden="true">
41 icon(data: crate::icons::INBOX)
42 </div>
43 <p class=(EMPTY_STATE_TITLE)>(title)</p>
44 if !detail.is_empty() {
45 <p class=(EMPTY_STATE_DETAIL)>(detail)</p>
46 }
47 if let Some(action) = action {
48 <div class=(EMPTY_STATE_ACTION)>(action)</div>
49 }
50 </div>
51 })
52}
53
54#[cfg(test)]
55mod tests;