impulse_thaw/progress_bar/
progress_circle.rs

1use leptos::prelude::*;
2use thaw_utils::{class_list, mount_style};
3
4#[component]
5pub fn ProgressCircle(
6    #[prop(optional, into)] class: MaybeProp<String>,
7    /// Percentage value.
8    #[prop(into, optional)]
9    value: Signal<f64>,
10    /// ProgressCircle color.
11    #[prop(into, optional)]
12    color: Signal<ProgressCircleColor>,
13    /// ProgressCircle size.
14    #[prop(into, default = "120px".into())]
15    size: Signal<String>,
16    #[prop(optional)] children: Option<Children>,
17) -> impl IntoView {
18    mount_style("progress-circle", include_str!("./progress-circle.css"));
19
20    let stroke_width = 7;
21    let view_box_width = 100;
22
23    let radius = 50;
24    let begin_position_x = 0;
25    let begin_position_y = radius;
26    let end_position_x = 0;
27    let end_position_y = 2 * radius;
28    let center_x = 50 + stroke_width / 2;
29    let rail_path = format!("M {center_x},{center_x} m {begin_position_x},{begin_position_y} a {radius},{radius} 0 1 1 {end_position_x},{} a {radius},{radius} 0 1 1 {},{end_position_y}", -end_position_y, -end_position_x);
30
31    let len = std::f64::consts::PI * 2.0 * f64::from(radius);
32    let rail_stroke_dasharray = format!("{len}px {}px", view_box_width * 8);
33
34    let fill_path = rail_path.clone();
35    let fill_stroke_dasharray = Memo::new(move |_| {
36        let percentage = value.get().max(0.0).min(100.0);
37
38        format!("{}px {}px", percentage / 100.0 * len, view_box_width * 8)
39    });
40    let fill_stroke_color = move || match color.get() {
41        ProgressCircleColor::Brand => "var(--colorCompoundBrandBackground)",
42        ProgressCircleColor::Error => "var(--colorPaletteRedBackground3)",
43        ProgressCircleColor::Warning => "var(--colorPaletteDarkOrangeBackground3)",
44        ProgressCircleColor::Success => "var(--colorPaletteGreenBackground3)",
45    };
46
47    view! {
48        <div
49            class=class_list!["thaw-progress-circle", class]
50            role="progressbar"
51            aria-valuemax="100"
52            aria-valuemin="0"
53            aria-valuenow=move || value.get()
54            style=("--thaw-size", move || size.get())
55        >
56
57            <svg viewBox="0 0 107 107">
58                <g>
59                    <path
60                        d=rail_path
61                        stroke-width=stroke_width
62                        stroke-linecap="round"
63                        fill="none"
64                        style:stroke="var(--colorNeutralBackground6)"
65                        style:stroke-dasharray=rail_stroke_dasharray
66                    ></path>
67                </g>
68                <g>
69                    <path
70                        class=("thaw-progress-circle__fill", true)
71                        class=("thaw-progress-circle__fill--empty", move || value.get() <= 0.0)
72                        d=fill_path
73                        stroke-width=stroke_width
74                        stroke-linecap="round"
75                        fill="none"
76                        style:stroke=fill_stroke_color
77                        style:stroke-dasharray=move || fill_stroke_dasharray.get()
78                    ></path>
79                </g>
80            </svg>
81
82            {if let Some(children) = children {
83                view! { <div class="thaw-progress-circle__content">{children()}</div> }.into_any()
84            } else {
85                view! {
86                    <div class="thaw-progress-circle__content thaw-progress-circle__content--text">
87                        {move || value.get()} "%"
88                    </div>
89                }
90                    .into_any()
91            }}
92
93        </div>
94    }
95}
96
97#[derive(Default, Clone)]
98pub enum ProgressCircleColor {
99    #[default]
100    Brand,
101    Error,
102    Warning,
103    Success,
104}