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