yew_bs/components/
spinners.rs

1use yew::prelude::*;
2use crate::components::common::{Variant, Size};
3#[derive(Clone, Copy, PartialEq, Debug)]
4pub enum SpinnerType {
5    Border,
6    Grow,
7}
8impl SpinnerType {
9    pub fn as_str(&self) -> &'static str {
10        match self {
11            SpinnerType::Border => "border",
12            SpinnerType::Grow => "grow",
13        }
14    }
15}
16/// Props for the Spinner component
17#[derive(Properties, PartialEq)]
18pub struct SpinnerProps {
19    /// Spinner type (border or grow)
20    #[prop_or(SpinnerType::Border)]
21    pub spinner_type: SpinnerType,
22    /// Spinner variant (color)
23    #[prop_or(Variant::Primary)]
24    pub variant: Variant,
25    /// Spinner size
26    #[prop_or_default]
27    pub size: Option<Size>,
28    /// Accessible label for screen readers
29    #[prop_or_default]
30    pub label: Option<AttrValue>,
31    /// Whether the spinner should be centered
32    #[prop_or_default]
33    pub centered: bool,
34    /// Additional CSS classes
35    #[prop_or_default]
36    pub class: Option<AttrValue>,
37    /// Additional HTML attributes
38    #[prop_or_default]
39    pub node_ref: NodeRef,
40}
41/// Bootstrap Spinner component
42#[function_component(Spinner)]
43pub fn spinner(props: &SpinnerProps) -> Html {
44    let mut classes = Classes::new();
45    classes.push("spinner");
46    classes.push(format!("spinner-{}", props.spinner_type.as_str()));
47    if props.variant != Variant::Primary {
48        classes.push(format!("text-{}", props.variant.as_str()));
49    }
50    if let Some(size) = &props.size {
51        match size {
52            Size::Small => {
53                classes.push(format!("spinner-{}-sm", props.spinner_type.as_str()))
54            }
55            Size::Large => {}
56            Size::Medium => {}
57        }
58    }
59    if let Some(class) = &props.class {
60        classes.push(class.to_string());
61    }
62    let container_classes = if props.centered {
63        classes!("d-flex", "justify-content-center")
64    } else {
65        Classes::new()
66    };
67    let default_label = match props.spinner_type {
68        SpinnerType::Border => "Loading...",
69        SpinnerType::Grow => "Loading...",
70    };
71    let label_attr = if let Some(label) = &props.label {
72        label.clone()
73    } else {
74        AttrValue::from(default_label)
75    };
76    html! {
77        if props.centered { < div class = { container_classes } > < div class = { classes
78        } role = "status" ref = { props.node_ref.clone() } > < span class =
79        "visually-hidden" > { & label_attr } </ span > </ div > </ div > } else { < div
80        class = { classes } role = "status" ref = { props.node_ref.clone() } > < span
81        class = "visually-hidden" > { & label_attr } </ span > </ div > }
82    }
83}