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    /// The bar's content, for a label Bootstrap allows but `show_label` cannot
85    /// express — "3 of 7", a unit, an icon. Takes precedence over `show_label`.
86    #[props(default)]
87    pub children: Option<Element>,
88    /// Additional CSS classes.
89    #[props(default)]
90    pub class: String,
91    /// Any additional HTML attributes.
92    #[props(extends = GlobalAttributes)]
93    attributes: Vec<Attribute>,
94}
95
96#[component]
97pub fn ProgressBar(props: ProgressBarProps) -> Element {
98    let color_class = match &props.color {
99        Some(c) => format!(" bg-{c}"),
100        None => String::new(),
101    };
102    let striped = if props.striped || props.animated {
103        " progress-bar-striped"
104    } else {
105        ""
106    };
107    let animated = if props.animated {
108        " progress-bar-animated"
109    } else {
110        ""
111    };
112
113    let full_class = if props.class.is_empty() {
114        format!("progress-bar{color_class}{striped}{animated}")
115    } else {
116        format!(
117            "progress-bar{color_class}{striped}{animated} {}",
118            props.class
119        )
120    };
121
122    let width = format!("width: {}%", props.value);
123    let label = if props.show_label {
124        format!("{}%", props.value as u32)
125    } else {
126        String::new()
127    };
128
129    rsx! {
130        div {
131            class: "{full_class}",
132            role: "progressbar",
133            style: "{width}",
134            "aria-valuenow": "{props.value}",
135            "aria-valuemin": "0",
136            "aria-valuemax": "100",
137            ..props.attributes,
138            if let Some(children) = props.children {
139                {children}
140            } else {
141                "{label}"
142            }
143        }
144    }
145}