Skip to main content

ui/components/
spinner.rs

1use crate::{CommonAnimationExt, prelude::*};
2
3/// Size preset for a [`Spinner`].
4#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
5pub enum SpinnerSize {
6    /// 12px
7    Sm,
8    /// 16px
9    #[default]
10    Default,
11    /// 24px
12    Lg,
13}
14
15impl SpinnerSize {
16    fn icon_size(self) -> IconSize {
17        match self {
18            SpinnerSize::Sm => IconSize::XSmall,
19            SpinnerSize::Default => IconSize::Medium,
20            SpinnerSize::Lg => IconSize::Custom(rems_from_px(24.)),
21        }
22    }
23}
24
25/// A rotating loader icon for inline loading states.
26#[derive(IntoElement, RegisterComponent)]
27pub struct Spinner {
28    id: ElementId,
29    size: SpinnerSize,
30}
31
32impl Spinner {
33    pub fn new() -> Self {
34        Self {
35            id: ElementId::Name("spinner".into()),
36            size: SpinnerSize::default(),
37        }
38    }
39
40    /// Overrides the element/animation id (defaults to `"spinner"`). Set
41    /// this to a unique value when rendering multiple `Spinner`s as
42    /// siblings so each gets its own animation key instead of sharing one.
43    pub fn id(mut self, id: impl Into<ElementId>) -> Self {
44        self.id = id.into();
45        self
46    }
47
48    pub fn size(mut self, size: SpinnerSize) -> Self {
49        self.size = size;
50        self
51    }
52}
53
54impl Default for Spinner {
55    fn default() -> Self {
56        Self::new()
57    }
58}
59
60impl RenderOnce for Spinner {
61    fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
62        Icon::new(IconName::LoadCircle)
63            .size(self.size.icon_size())
64            .color(Color::Custom(semantic::text_muted(cx)))
65            .with_keyed_rotate_animation(self.id, 1)
66    }
67}
68
69impl Component for Spinner {
70    fn scope() -> ComponentScope {
71        ComponentScope::Loading
72    }
73
74    fn description() -> Option<&'static str> {
75        Some("A rotating loader icon for inline loading states.")
76    }
77
78    fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
79        Some(
80            h_flex()
81                .gap_4()
82                .items_center()
83                .child(Spinner::new().size(SpinnerSize::Sm))
84                .child(Spinner::new())
85                .child(Spinner::new().size(SpinnerSize::Lg))
86                .into_any_element(),
87        )
88    }
89}