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/// ```text
20/// empty_state(title: "No records yet", detail: "Create the first one.")
21/// ```
22#[component]
23pub async fn empty_state(
24    /// What is empty, e.g. `"No records yet"`.
25    #[into]
26    title: String,
27    /// Optional muted detail line.
28    #[into]
29    #[default]
30    detail: String,
31    /// Optional controls under the text.
32    #[default]
33    action: Option<Child<'_>>,
34    /// Extra attributes for the container.
35    #[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;