1use crate::{CommonAnimationExt, prelude::*};
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
5pub enum SpinnerSize {
6 Sm,
8 #[default]
10 Default,
11 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#[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 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}