impulse_thaw/progress_bar/
progress_circle.rs1use leptos::prelude::*;
2use thaw_utils::{class_list, mount_style};
3
4#[component]
5pub fn ProgressCircle(
6 #[prop(optional, into)] class: MaybeProp<String>,
7 #[prop(into, optional)]
9 value: Signal<f64>,
10 #[prop(into, optional)]
12 color: Signal<ProgressCircleColor>,
13 #[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}