1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
use crate::Intent;
use yew::prelude::*;
const R: f32 = 45.0;
const PATH_LENGTH: f32 = 280.0;
const SPINNER_MIN_SIZE: f32 = 10.0;
const STROKE_WIDTH: f32 = 4.0;
const MIN_STROKE_WIDTH: f32 = 16.0;
pub const SPINNER_SIZE_SMALL: f32 = 20.0;
pub const SPINNER_SIZE_STANDARD: f32 = 50.0;
pub const SPINNER_SIZE_LARGE: f32 = 100.0;
#[derive(Clone, PartialEq, Properties)]
pub struct SpinnerProps {
#[prop_or_default]
pub intent: Option<Intent>,
#[prop_or_default]
pub class: Classes,
#[prop_or(SPINNER_SIZE_STANDARD)]
pub size: f32,
#[prop_or(0.25)]
pub value: f32,
}
#[function_component(Spinner)]
pub fn spinner(props: &SpinnerProps) -> Html {
let size = f32::max(SPINNER_MIN_SIZE, props.size);
let stroke_width = f32::min(MIN_STROKE_WIDTH, (STROKE_WIDTH * SPINNER_SIZE_LARGE) / size);
let view_box = {
let radius = R + stroke_width / 2.00;
let view_box_x = 50.00 - radius;
let view_box_width = radius * 2.00;
format!(
"{:.2} {:.2} {:.2} {:.2}",
view_box_x, view_box_x, view_box_width, view_box_width,
)
};
let spinner_track = format!(
"M 50,50 m 0,-{R:.0} a {R:.0},{R:.0} 0 1 1 0,{R2:.0} a {R:.0},{R:.0} 0 1 1 0,-{R2:.0}",
R = R,
R2 = R * 2.0,
);
let stroke_offset = PATH_LENGTH - PATH_LENGTH * props.value.clamp(0.0, 1.0);
html! {
<div
class={classes!(
"bp3-spinner",
props.intent,
props.class.clone(),
)}
>
<div
class={classes!("bp3-spinner-animation")}
>
<svg
width={size.to_string()}
height={size.to_string()}
stroke-width={stroke_width.to_string()}
viewBox={view_box}
>
<path
class={classes!("bp3-spinner-track")}
d={spinner_track.clone()}
/>
<path
class={classes!("bp3-spinner-head")}
d={spinner_track}
pathLength={PATH_LENGTH.to_string()}
stroke-dasharray={format!("{} {}", PATH_LENGTH, PATH_LENGTH)}
stroke-dashoffset={stroke_offset.to_string()}
/>
</svg>
</div>
</div>
}
}