Skip to main content

ui/components/
empty_state.rs

1use gpui::{AnyElement, FontWeight};
2
3use crate::prelude::*;
4
5/// A centered placeholder shown when a list/table/collection has no content.
6#[derive(IntoElement, RegisterComponent)]
7pub struct EmptyState {
8    icon: IconName,
9    heading: SharedString,
10    description: Option<SharedString>,
11    action: Option<AnyElement>,
12}
13
14impl EmptyState {
15    pub fn new(heading: impl Into<SharedString>) -> Self {
16        Self {
17            icon: IconName::User,
18            heading: heading.into(),
19            description: None,
20            action: None,
21        }
22    }
23
24    pub fn icon(mut self, icon: IconName) -> Self {
25        self.icon = icon;
26        self
27    }
28
29    pub fn description(mut self, description: impl Into<SharedString>) -> Self {
30        self.description = Some(description.into());
31        self
32    }
33
34    pub fn action(mut self, action: impl IntoElement) -> Self {
35        self.action = Some(action.into_any_element());
36        self
37    }
38}
39
40impl RenderOnce for EmptyState {
41    fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
42        v_flex()
43            .w_full()
44            .items_center()
45            .justify_center()
46            .gap_2()
47            .py_12()
48            .child(
49                Icon::new(self.icon)
50                    .size(IconSize::XLarge)
51                    .color(Color::Custom(semantic::text_muted(cx))),
52            )
53            .child(Label::new(self.heading).weight(FontWeight::MEDIUM))
54            .children(
55                self.description
56                    .map(|d| Label::new(d).size(LabelSize::Small).color(Color::Muted)),
57            )
58            .children(self.action)
59    }
60}
61
62impl Component for EmptyState {
63    fn scope() -> ComponentScope {
64        ComponentScope::DataDisplay
65    }
66
67    fn description() -> Option<&'static str> {
68        Some("A centered placeholder shown when a list/table/collection has no content.")
69    }
70
71    fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
72        Some(
73            v_flex()
74                .gap_6()
75                .children(vec![
76                    example_group_with_title(
77                        "Basic",
78                        vec![single_example(
79                            "No description",
80                            EmptyState::new("No projects").into_any_element(),
81                        )],
82                    ),
83                    example_group_with_title(
84                        "With description and action",
85                        vec![single_example(
86                            "Full",
87                            EmptyState::new("No projects")
88                                .icon(IconName::File)
89                                .description("Get started by creating a new project.")
90                                .action(Button::new("new_project", "New Project"))
91                                .into_any_element(),
92                        )],
93                    ),
94                ])
95                .into_any_element(),
96        )
97    }
98}