1use gpui::Hsla;
2
3use crate::prelude::*;
4
5fn role(severity: Severity, step: u16) -> Hsla {
6 match severity {
7 Severity::Info => palette::info(step),
8 Severity::Success => palette::success(step),
9 Severity::Warning => palette::warning(step),
10 Severity::Error => palette::danger(step),
11 }
12}
13
14fn icon_for(severity: Severity) -> IconName {
15 match severity {
16 Severity::Info => IconName::Info,
17 Severity::Success => IconName::CheckCircle,
18 Severity::Warning => IconName::ExclamationTriangle,
19 Severity::Error => IconName::XCircle,
20 }
21}
22
23#[derive(IntoElement, RegisterComponent)]
25pub struct Alert {
26 severity: Severity,
27 title: SharedString,
28 message: Option<SharedString>,
29}
30
31impl Alert {
32 pub fn new(severity: Severity, title: impl Into<SharedString>) -> Self {
33 Self {
34 severity,
35 title: title.into(),
36 message: None,
37 }
38 }
39
40 pub fn info(title: impl Into<SharedString>) -> Self {
41 Self::new(Severity::Info, title)
42 }
43
44 pub fn success(title: impl Into<SharedString>) -> Self {
45 Self::new(Severity::Success, title)
46 }
47
48 pub fn warning(title: impl Into<SharedString>) -> Self {
49 Self::new(Severity::Warning, title)
50 }
51
52 pub fn error(title: impl Into<SharedString>) -> Self {
53 Self::new(Severity::Error, title)
54 }
55
56 pub fn message(mut self, message: impl Into<SharedString>) -> Self {
57 self.message = Some(message.into());
58 self
59 }
60}
61
62impl RenderOnce for Alert {
63 fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
64 h_flex()
65 .items_start()
66 .gap_3()
67 .w_full()
68 .p_4()
69 .rounded_md()
70 .border_l_4()
71 .border_color(role(self.severity, 500))
72 .bg(role(self.severity, 50))
73 .child(
74 Icon::new(icon_for(self.severity))
75 .size(IconSize::Small)
76 .color(Color::Custom(role(self.severity, 500))),
77 )
78 .child(
79 v_flex()
80 .gap_0p5()
81 .child(
82 Label::new(self.title)
83 .size(LabelSize::Small)
84 .color(Color::Custom(role(self.severity, 800))),
85 )
86 .children(self.message.map(|m| {
87 Label::new(m)
88 .size(LabelSize::XSmall)
89 .color(Color::Custom(role(self.severity, 700)))
90 })),
91 )
92 }
93}
94
95impl Component for Alert {
96 fn scope() -> ComponentScope {
97 ComponentScope::Notification
98 }
99
100 fn description() -> Option<&'static str> {
101 Some("A tinted, icon-led message box for info/success/warning/error.")
102 }
103
104 fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
105 Some(
106 v_flex()
107 .gap_3()
108 .w(px(420.))
109 .child(Alert::info("Heads up").message("This is an informational alert."))
110 .child(
111 Alert::success("Saved").message("Your changes to \"Q3 Roadmap\" were saved."),
112 )
113 .child(Alert::warning("Careful").message("This action needs review."))
114 .child(
115 Alert::error("Failed").message("Payment could not be processed. Please retry."),
116 )
117 .child(Alert::info("Maintenance scheduled for Sunday, 2 AM UTC."))
118 .into_any_element(),
119 )
120 }
121}