1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
use dioxus::prelude::*;
use crate::types::{Color, Size, SpinnerStyle};
/// Bootstrap Spinner (loading indicator) component.
///
/// # Bootstrap HTML → Dioxus
///
/// | HTML | Dioxus |
/// |---|---|
/// | `<div class="spinner-border">` | `Spinner {}` |
/// | `<div class="spinner-grow text-success">` | `Spinner { style: SpinnerStyle::Grow, color: Color::Success }` |
/// | `<div class="spinner-border spinner-border-sm">` | `Spinner { size: Size::Sm }` |
///
/// ```rust,no_run
/// rsx! {
/// Spinner {}
/// Spinner { color: Color::Success, style: SpinnerStyle::Grow }
/// Spinner { size: Size::Sm, "Loading..." }
/// }
/// ```
#[derive(Clone, PartialEq, Props)]
pub struct SpinnerProps {
/// Spinner color.
#[props(default)]
pub color: Option<Color>,
/// Spinner size.
#[props(default)]
pub size: Size,
/// Spinner style (border or grow).
#[props(default)]
pub style: SpinnerStyle,
/// Additional CSS classes.
#[props(default)]
pub class: String,
/// Any additional HTML attributes.
#[props(extends = GlobalAttributes)]
attributes: Vec<Attribute>,
/// Screen reader text (child elements).
#[props(default)]
pub children: Element,
}
#[component]
pub fn Spinner(props: SpinnerProps) -> Element {
let base = match props.style {
SpinnerStyle::Border => "spinner-border",
SpinnerStyle::Grow => "spinner-grow",
};
let size_class = match props.size {
Size::Sm => format!(" {base}-sm"),
_ => String::new(),
};
let color_class = match &props.color {
Some(c) => format!(" text-{c}"),
None => String::new(),
};
let full_class = if props.class.is_empty() {
format!("{base}{size_class}{color_class}")
} else {
format!("{base}{size_class}{color_class} {}", props.class)
};
rsx! {
div {
class: "{full_class}",
role: "status",
..props.attributes,
span { class: "visually-hidden", {props.children} }
}
}
}