Skip to main content

liora_components/
loading.rs

1use crate::motion::{fade_in, spin_icon};
2use gpui::{App, Component, IntoElement, RenderOnce, SharedString, Window, div, prelude::*, px};
3use liora_core::Config;
4use liora_icons::Icon;
5use liora_icons_lucide::IconName;
6
7pub struct Loading {
8    text: Option<SharedString>,
9    full_screen: bool,
10}
11
12impl Loading {
13    pub fn new() -> Self {
14        Self {
15            text: None,
16            full_screen: false,
17        }
18    }
19
20    pub fn text(mut self, text: impl Into<SharedString>) -> Self {
21        self.text = Some(text.into());
22        self
23    }
24
25    pub fn full_screen(mut self) -> Self {
26        self.full_screen = true;
27        self
28    }
29}
30
31impl RenderOnce for Loading {
32    fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
33        let theme = cx.global::<Config>().theme.clone();
34
35        let spinner_icon = spin_icon(
36            "liora-loading-spinner-motion",
37            Icon::new(IconName::LoaderCircle)
38                .size(px(32.0))
39                .color(theme.primary.base),
40        );
41
42        let spinner = div()
43            .flex()
44            .flex_col()
45            .items_center()
46            .gap_2()
47            .child(spinner_icon)
48            .when_some(self.text, |s, t| {
49                s.child(div().text_sm().text_color(theme.primary.base).child(t))
50            });
51
52        if self.full_screen {
53            fade_in(
54                "liora-loading-fullscreen-motion",
55                div()
56                    .absolute()
57                    .size_full()
58                    .bg(theme.neutral.mask)
59                    .flex()
60                    .items_center()
61                    .justify_center()
62                    .child(spinner),
63            )
64        } else {
65            fade_in("liora-loading-inline-motion", spinner)
66        }
67    }
68}
69
70impl IntoElement for Loading {
71    type Element = Component<Self>;
72    fn into_element(self) -> Self::Element {
73        Component::new(self)
74    }
75}
76
77#[cfg(test)]
78mod tests {
79    #[test]
80    fn loading_uses_spin_and_fade_motion() {
81        let source = include_str!("loading.rs")
82            .split("#[cfg(test)]")
83            .next()
84            .unwrap();
85
86        assert!(source.contains("spin_icon("));
87        assert!(source.contains("fade_in("));
88        assert!(source.contains("liora-loading-spinner-motion"));
89    }
90}