Skip to main content

freya_components/
loader.rs

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