Skip to main content

dioxus_bootstrap_css/
progress.rs

1use dioxus::prelude::*;
2
3use crate::types::Color;
4
5/// Bootstrap Progress container.
6///
7/// # Bootstrap HTML → Dioxus
8///
9/// ```html
10/// <!-- Bootstrap HTML -->
11/// <div class="progress">
12///   <div class="progress-bar bg-success" style="width: 75%">75%</div>
13/// </div>
14/// <!-- Stacked bars -->
15/// <div class="progress">
16///   <div class="progress-bar" style="width: 30%"></div>
17///   <div class="progress-bar bg-warning" style="width: 20%"></div>
18/// </div>
19/// ```
20///
21/// ```rust,no_run
22/// # use dioxus::prelude::*;
23/// # use dioxus_bootstrap_css::prelude::*;
24/// # fn _doctest() -> Element {
25/// rsx! {
26///     Progress {
27///         ProgressBar { value: 75.0, color: Color::Success, show_label: true }
28///     }
29///     // Stacked bars
30///     Progress {
31///         ProgressBar { value: 30.0, color: Color::Primary }
32///         ProgressBar { value: 20.0, color: Color::Warning }
33///     }
34///     // Striped animated
35///     Progress {
36///         ProgressBar { value: 50.0, striped: true, animated: true }
37///     }
38/// }
39/// # }
40/// ```
41#[derive(Clone, PartialEq, Props)]
42pub struct ProgressProps {
43    /// Additional CSS classes.
44    #[props(default)]
45    pub class: String,
46    /// Any additional HTML attributes.
47    #[props(extends = GlobalAttributes)]
48    attributes: Vec<Attribute>,
49    /// Child elements (ProgressBar components).
50    pub children: Element,
51}
52
53#[component]
54pub fn Progress(props: ProgressProps) -> Element {
55    let full_class = if props.class.is_empty() {
56        "progress".to_string()
57    } else {
58        format!("progress {}", props.class)
59    };
60
61    rsx! {
62        div { class: "{full_class}", ..props.attributes, {props.children} }
63    }
64}
65
66/// Bootstrap ProgressBar component (goes inside Progress).
67#[derive(Clone, PartialEq, Props)]
68pub struct ProgressBarProps {
69    /// Progress value (0.0 to 100.0).
70    #[props(default)]
71    pub value: f64,
72    /// Bar color.
73    #[props(default)]
74    pub color: Option<Color>,
75    /// Show striped pattern.
76    #[props(default)]
77    pub striped: bool,
78    /// Animate the stripes.
79    #[props(default)]
80    pub animated: bool,
81    /// Show the value as text inside the bar.
82    #[props(default)]
83    pub show_label: bool,
84    /// Additional CSS classes.
85    #[props(default)]
86    pub class: String,
87    /// Any additional HTML attributes.
88    #[props(extends = GlobalAttributes)]
89    attributes: Vec<Attribute>,
90}
91
92#[component]
93pub fn ProgressBar(props: ProgressBarProps) -> Element {
94    let color_class = match &props.color {
95        Some(c) => format!(" bg-{c}"),
96        None => String::new(),
97    };
98    let striped = if props.striped || props.animated {
99        " progress-bar-striped"
100    } else {
101        ""
102    };
103    let animated = if props.animated {
104        " progress-bar-animated"
105    } else {
106        ""
107    };
108
109    let full_class = if props.class.is_empty() {
110        format!("progress-bar{color_class}{striped}{animated}")
111    } else {
112        format!(
113            "progress-bar{color_class}{striped}{animated} {}",
114            props.class
115        )
116    };
117
118    let width = format!("width: {}%", props.value);
119    let label = if props.show_label {
120        format!("{}%", props.value as u32)
121    } else {
122        String::new()
123    };
124
125    rsx! {
126        div {
127            class: "{full_class}",
128            role: "progressbar",
129            style: "{width}",
130            "aria-valuenow": "{props.value}",
131            "aria-valuemin": "0",
132            "aria-valuemax": "100",
133            ..props.attributes,
134            "{label}"
135        }
136    }
137}