Skip to main content

tablo_ui/components/composites/
empty_state.rs

1use 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/// Renders zero-data content inside the region that holds no data.
18///
19/// ```rust
20/// # use tablo_ui::empty_state;
21/// # use topcoat::{context::Cx, view::{BoxView, ViewExt, view}};
22/// # fn empty(cx: &Cx) -> BoxView<'_> {
23/// view! { cx => empty_state(title: "No records yet", detail: "Create the first one.") }
24/// # .boxed()
25/// # }
26/// ```
27#[component]
28pub async fn empty_state(
29    /// What is empty, e.g. `"No records yet"`.
30    #[into]
31    title: String,
32    /// Optional muted detail line.
33    #[into]
34    #[default]
35    detail: String,
36    /// Optional controls under the text.
37    #[default]
38    action: Option<Child<'_>>,
39    /// Extra attributes for the container.
40    #[default]
41    mut attrs: Attributes,
42) -> Result<impl View> {
43    Ok(view! {
44        <div class=(class!(EMPTY_STATE, attrs.remove("class"))) (attrs)>
45            <div class=(EMPTY_STATE_ICON) aria-hidden="true">
46                icon(data: crate::icons::INBOX)
47            </div>
48            <p class=(EMPTY_STATE_TITLE)>(title)</p>
49            if !detail.is_empty() {
50                <p class=(EMPTY_STATE_DETAIL)>(detail)</p>
51            }
52            if let Some(action) = action {
53                <div class=(EMPTY_STATE_ACTION)>(action)</div>
54            }
55        </div>
56    })
57}