liora_components/
result.rs1use gpui::{AnyElement, App, IntoElement, RenderOnce, SharedString, Window, div, prelude::*, px};
2use liora_core::Config;
3use liora_icons::Icon;
4use liora_icons_lucide::IconName;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
7pub enum ResultStatus {
8 #[default]
9 Info,
10 Success,
11 Warning,
12 Error,
13}
14
15pub struct Result {
16 status: ResultStatus,
17 title: SharedString,
18 sub_title: Option<SharedString>,
19 icon: Option<AnyElement>,
20 extra: Option<Box<dyn Fn(&mut Window, &mut App) -> AnyElement + 'static>>,
21}
22
23impl Result {
24 pub fn new(title: impl Into<SharedString>) -> Self {
25 Self {
26 status: ResultStatus::Info,
27 title: title.into(),
28 sub_title: None,
29 icon: None,
30 extra: None,
31 }
32 }
33
34 pub fn status(mut self, s: ResultStatus) -> Self {
35 self.status = s;
36 self
37 }
38
39 pub fn sub_title(mut self, sub: impl Into<SharedString>) -> Self {
40 self.sub_title = Some(sub.into());
41 self
42 }
43
44 pub fn icon(mut self, icon: impl IntoElement) -> Self {
45 self.icon = Some(icon.into_any_element());
46 self
47 }
48
49 pub fn extra<F>(mut self, f: F) -> Self
50 where
51 F: Fn(&mut Window, &mut App) -> AnyElement + 'static,
52 {
53 self.extra = Some(Box::new(f));
54 self
55 }
56}
57
58impl RenderOnce for Result {
59 fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
60 let theme = cx.global::<Config>().theme.clone();
61
62 let (icon_name, color) = match self.status {
63 ResultStatus::Success => (IconName::CircleCheckBig, theme.success.base),
64 ResultStatus::Warning => (IconName::TriangleAlert, theme.warning.base),
65 ResultStatus::Error => (IconName::CircleX, theme.danger.base),
66 ResultStatus::Info => (IconName::Info, theme.primary.base),
67 };
68
69 div()
70 .flex()
71 .flex_col()
72 .items_center()
73 .justify_center()
74 .w_full()
75 .p_12()
76 .gap_4()
77 .child(
78 div()
79 .flex()
80 .items_center()
81 .justify_center()
82 .w(px(72.0))
83 .h(px(72.0))
84 .child(match self.icon {
85 Some(icon) => icon,
86 None => Icon::new(icon_name)
87 .size(px(72.0))
88 .color(color)
89 .into_any_element(),
90 }),
91 )
92 .child(
93 div()
94 .text_xl()
95 .font_weight(gpui::FontWeight::BOLD)
96 .text_color(theme.neutral.text_1)
97 .child(self.title),
98 )
99 .when_some(self.sub_title, |s, sub| {
100 s.child(div().text_sm().text_color(theme.neutral.text_3).child(sub))
101 })
102 .when_some(self.extra, |s, extra| {
103 s.child(div().mt_4().child((extra)(window, cx)))
104 })
105 }
106}
107
108impl IntoElement for Result {
109 type Element = gpui::Component<Self>;
110 fn into_element(self) -> Self::Element {
111 gpui::Component::new(self)
112 }
113}