Skip to main content

freya_components/
loader.rs

1use freya_animation::prelude::*;
2use freya_core::prelude::*;
3use torin::size::Size;
4
5use crate::{
6    define_theme,
7    get_theme,
8};
9
10define_theme! {
11    %[component]
12    pub CircularLoader {
13        %[fields]
14        primary_color: Color,
15    }
16}
17
18/// Circular loader component.
19///
20/// # Example
21///
22/// ```rust
23/// # use freya::prelude::*;
24/// fn app() -> impl IntoElement {
25///     CircularLoader::new()
26/// }
27///
28/// # use freya_testing::prelude::*;
29/// # launch_doc(|| {
30/// #   rect().spacing(8.).center().expanded().child(app())
31/// # }, "./images/gallery_circular_loader.png").render();
32/// ```
33///
34/// # Preview
35/// ![Circular Loader Preview][circular_loader]
36#[cfg_attr(feature = "docs",
37    doc = embed_doc_image::embed_image!("circular_loader", "images/gallery_circular_loader.png")
38)]
39#[derive(PartialEq)]
40pub struct CircularLoader {
41    pub(crate) theme: Option<CircularLoaderThemePartial>,
42    size: f32,
43    accessibility: AccessibilityData,
44    key: DiffKey,
45}
46
47impl KeyExt for CircularLoader {
48    fn write_key(&mut self) -> &mut DiffKey {
49        &mut self.key
50    }
51}
52
53impl AccessibilityExt for CircularLoader {
54    fn get_accessibility_data(&mut self) -> &mut AccessibilityData {
55        &mut self.accessibility
56    }
57}
58
59impl Default for CircularLoader {
60    fn default() -> Self {
61        Self::new()
62    }
63}
64
65impl CircularLoader {
66    pub fn new() -> Self {
67        Self {
68            size: 32.,
69            theme: None,
70            accessibility: AccessibilityData::default(),
71            key: DiffKey::None,
72        }
73    }
74
75    pub fn size(mut self, size: f32) -> Self {
76        self.size = size;
77        self
78    }
79}
80
81impl Component for CircularLoader {
82    fn render(&self) -> impl IntoElement {
83        let theme = get_theme!(
84            &self.theme,
85            CircularLoaderThemePreference,
86            "circular_loader"
87        );
88
89        let animation = use_animation(|conf| {
90            conf.on_creation(OnCreation::Run);
91            conf.on_finish(OnFinish::restart());
92            AnimNum::new(0.0, 360.0).time(650)
93        });
94
95        svg(Bytes::from_static(
96            r#"<svg viewBox="0 0 600 600" xmlns="http://www.w3.org/2000/svg">
97                <circle class="spin" cx="300" cy="300" fill="none"
98                r="250" stroke-width="64" stroke="{color}"
99                stroke-dasharray="256 1400"
100                stroke-linecap="round" />
101            </svg>"#
102                .as_bytes(),
103        ))
104        .accessibility(self.accessibility.clone())
105        .a11y_role(AccessibilityRole::ProgressIndicator)
106        .width(Size::px(self.size))
107        .height(Size::px(self.size))
108        .stroke(theme.primary_color)
109        .rotate(animation.get().value())
110    }
111
112    fn render_key(&self) -> DiffKey {
113        self.key.clone().or(self.default_key())
114    }
115}