ui_storybook/components/primitives/
slider.rs1use leptos::prelude::*;
8
9#[component]
10pub fn Slider(
11 value: f32,
13 #[prop(optional, default = 0.0)]
15 min: f32,
16 #[prop(optional, default = 1.0)]
18 max: f32,
19 #[prop(optional)]
21 disabled: bool,
22 #[prop(optional, into)]
24 label: Option<String>,
25 #[prop(optional, into)]
27 readout: Option<String>,
28) -> impl IntoView {
29 let percent = slider_percent(value, min, max);
30 let mut class = String::from("slider");
31 if disabled {
32 class.push_str(" slider-disabled");
33 }
34 let fill_style = format!("width:{percent:.2}%");
35 let thumb_style = format!("left:{percent:.2}%");
36 view! {
37 <div class=class>
38 {label.map(|l| view! { <span class="slider-label">{l}</span> })}
39 <div class="slider-track">
40 <div class="slider-fill" style=fill_style></div>
41 <div class="slider-thumb" style=thumb_style></div>
42 </div>
43 {readout.map(|r| view! { <span class="slider-readout">{r}</span> })}
44 </div>
45 }
46}
47
48#[must_use]
52pub fn slider_percent(value: f32, min: f32, max: f32) -> f32 {
53 if max <= min {
54 return 0.0;
55 }
56 ((value - min) / (max - min) * 100.0).clamp(0.0, 100.0)
57}
58
59#[cfg(test)]
60mod tests {
61 use super::*;
62
63 #[test]
64 fn percent_zero_at_min() {
65 assert!((slider_percent(0.0, 0.0, 1.0) - 0.0).abs() < 1e-4);
66 assert!((slider_percent(-5.0, 0.0, 1.0) - 0.0).abs() < 1e-4);
67 }
68
69 #[test]
70 fn percent_one_hundred_at_max() {
71 assert!((slider_percent(1.0, 0.0, 1.0) - 100.0).abs() < 1e-4);
72 assert!((slider_percent(2.0, 0.0, 1.0) - 100.0).abs() < 1e-4);
73 }
74
75 #[test]
76 fn percent_midway() {
77 assert!((slider_percent(0.5, 0.0, 1.0) - 50.0).abs() < 1e-4);
78 assert!((slider_percent(5.0, 0.0, 10.0) - 50.0).abs() < 1e-4);
79 assert!((slider_percent(15.0, 10.0, 20.0) - 50.0).abs() < 1e-4);
80 }
81
82 #[test]
83 fn zero_range_returns_zero() {
84 assert!((slider_percent(1.0, 5.0, 5.0) - 0.0).abs() < 1e-4);
85 }
86}