maud_ui/primitives/
empty_state.rs1use maud::{html, Markup};
4
5#[derive(Debug, Clone)]
7pub struct Props {
8 pub icon: Option<String>,
10 pub title: String,
12 pub description: Option<String>,
14 pub action: Option<Markup>,
16}
17
18impl Props {
19 pub fn new(title: impl Into<String>) -> Self {
20 Self {
21 icon: None,
22 title: title.into(),
23 description: None,
24 action: None,
25 }
26 }
27
28 pub fn with_icon(mut self, icon: impl Into<String>) -> Self {
29 self.icon = Some(icon.into());
30 self
31 }
32
33 pub fn with_description(mut self, description: impl Into<String>) -> Self {
34 self.description = Some(description.into());
35 self
36 }
37
38 pub fn with_action(mut self, action: Markup) -> Self {
39 self.action = Some(action);
40 self
41 }
42}
43
44pub fn render(props: Props) -> Markup {
46 let icon = props.icon.unwrap_or_else(|| "📭".to_string());
47
48 html! {
49 div.mui-empty-state {
50 div.mui-empty-state__icon aria-hidden="true" { (icon) }
51 h3.mui-empty-state__title { (props.title) }
52 @if let Some(desc) = props.description {
53 p.mui-empty-state__description { (desc) }
54 }
55 @if let Some(action) = props.action {
56 div.mui-empty-state__action { (action) }
57 }
58 }
59 }
60}
61
62pub fn showcase() -> Markup {
64 html! {
65 div.mui-showcase__grid {
66 div {
68 p.mui-showcase__caption { "No results" }
69 div style="border:1px solid var(--mui-border);border-radius:var(--mui-radius-lg);background:var(--mui-bg-card)" {
70 (render(
71 Props::new("No results found")
72 .with_icon("\u{1F50D}")
73 .with_description("Try adjusting your search query or removing some filters to find what you're looking for.")
74 .with_action(html! {
75 button type="button" class="mui-btn mui-btn--outline mui-btn--md" { "Clear filters" }
76 })
77 ))
78 }
79 }
80
81 div {
83 p.mui-showcase__caption { "Empty inbox" }
84 div style="border:1px solid var(--mui-border);border-radius:var(--mui-radius-lg);background:var(--mui-bg-card)" {
85 (render(
86 Props::new("Your inbox is empty")
87 .with_icon("\u{2709}\u{FE0F}")
88 .with_description("Messages you receive will appear here.")
89 ))
90 }
91 }
92
93 div {
95 p.mui-showcase__caption { "First run" }
96 div style="border:1px solid var(--mui-border);border-radius:var(--mui-radius-lg);background:var(--mui-bg-card)" {
97 (render(
98 Props::new("Create your first project")
99 .with_icon("\u{1F680}")
100 .with_description("Projects help you organize your work and collaborate with your team.")
101 .with_action(html! {
102 button type="button" class="mui-btn mui-btn--default mui-btn--md" { "New project" }
103 })
104 ))
105 }
106 }
107
108 div {
110 p.mui-showcase__caption { "Minimal" }
111 (render(Props::new("Nothing here yet")))
112 }
113 }
114 }
115}