diff --git a/demo/src/pages/slider/mod.rs b/demo/src/pages/slider/mod.rs index f9e9718..007dc0c 100644 --- a/demo/src/pages/slider/mod.rs +++ b/demo/src/pages/slider/mod.rs @@ -6,7 +6,9 @@ use thaw::*; #[component] pub fn SliderPage() -> impl IntoView { let value = create_rw_signal(0.0); - + let stepped_value = create_rw_signal(0.0); + let labeled_value = create_rw_signal(0.0); + view! {

"Slider"

@@ -25,6 +27,58 @@ pub fn SliderPage() -> impl IntoView { +

"step"

+ + + + + {highlight_str!( + r#" + let value = create_rw_signal(0.0); + + + "#, + "rust" + )} + + + +

"SliderLabel"

+ + + + "0" + + + "5" + + + "10" + + + + + {highlight_str!( + r#" + let value = create_rw_signal(0.0); + + + + "0" + + + "5" + + + "10" + + + "#, + "rust" + )} + + +

"Slider Props"

@@ -48,6 +102,43 @@ pub fn SliderPage() -> impl IntoView { + + + + + + + + + + + + + +
"100" "Max value of the slider."
"step""MaybeSignal""The step in which value is incremented."
"children""Option""Slider labels."
+

"SliderLabel Props"

+ + + + + + + + + + + + + + + + + + + + + +
"Name""Type""Default""Description"
"value""ReadSignal""Value at which label will be placed."
"children""Children""Content of the lable."
diff --git a/src/slider/mod.rs b/src/slider/mod.rs index 75f9cc6..b43ab39 100644 --- a/src/slider/mod.rs +++ b/src/slider/mod.rs @@ -1,18 +1,25 @@ +mod slider_label; mod theme; -use crate::{theme::use_theme, utils::mount_style, Theme}; +use crate::{components::OptionComp, theme::use_theme, utils::mount_style, Theme}; use leptos::*; +use web_sys::DomRect; + +pub use slider_label::SliderLabel; pub use theme::SliderTheme; #[component] pub fn Slider( #[prop(optional, into)] value: RwSignal, #[prop(default = MaybeSignal::Static(100f64), into)] max: MaybeSignal, + #[prop(optional, into)] step: MaybeSignal, + #[prop(optional)] children: Option, ) -> impl IntoView { mount_style("slider", include_str!("./slider.css")); let theme = use_theme(Theme::light); let css_vars = create_memo(move |_| { let mut css_vars = String::new(); + css_vars.push_str(&format!("--thaw-slider-max: {};", max.get())); theme.with(|theme| { css_vars.push_str(&format!( "--thaw-background-color: {};", @@ -37,7 +44,24 @@ pub fn Slider( } }); - let do_update_value = move |val| { + let do_update_value = move |mut val| { + let step = step.get_untracked(); + if step > 0.0 { + let result: f64 = val / step; + if result.fract() != 0.0 { + let prev_multiple = (result.floor() * step).abs(); + let mut next_multiple = (result.ceil() * step).abs(); + let max = max.get_untracked(); + if next_multiple > max { + next_multiple = max; + } + if val - prev_multiple > next_multiple - val { + val = next_multiple; + } else { + val = prev_multiple; + } + } + } value.set(val); }; @@ -48,9 +72,31 @@ pub fn Slider( set_mouse_move.set(true); }; + let check_value_and_update = move |ev_x: f64, rect: DomRect| { + if ev_x <= rect.x() { + set_mouse_move_value.set(Some(0.0)); + } else if ev_x >= rect.x() + rect.width() { + set_mouse_move_value.set(Some(max.get())); + } else { + set_mouse_move_value.set(Some(((ev_x - rect.x()) / rect.width()) * max.get())); + } + if let Some(value) = mouse_move_value.get_untracked() { + do_update_value(value); + } + }; + + let on_mouse_click = move |ev: ev::MouseEvent| { + if let Some(rail) = rail_ref.get_untracked() { + let rect = rail.get_bounding_client_rect(); + let ev_x = f64::from(ev.x()); + check_value_and_update(ev_x, rect); + }; + }; + let on_mouse_up = window_event_listener(ev::mouseup, move |_| { set_mouse_move.set(false); }); + on_cleanup(move || on_mouse_up.remove()); let on_mouse_move = window_event_listener(ev::mousemove, move |ev| { @@ -58,37 +104,32 @@ pub fn Slider( if let Some(rail) = rail_ref.get_untracked() { let rect = rail.get_bounding_client_rect(); let ev_x = f64::from(ev.x()); - if ev_x <= rect.x() { - set_mouse_move_value.set(Some(0.0)); - } else if ev_x >= rect.x() + rect.width() { - set_mouse_move_value.set(Some(max.get())); - } else { - set_mouse_move_value.set(Some(((ev_x - rect.x()) / rect.width()) * max.get())); - } - if let Some(value) = mouse_move_value.get_untracked() { - do_update_value(value); - } + check_value_and_update(ev_x, rect); }; } }); on_cleanup(move || on_mouse_move.remove()); view! { -
+
+ + {children()} +
diff --git a/src/slider/slider.css b/src/slider/slider.css index c137e80..84d2708 100644 --- a/src/slider/slider.css +++ b/src/slider/slider.css @@ -2,17 +2,20 @@ position: relative; padding: 6px 0; cursor: pointer; + user-select: none; } .thaw-slider-rail { height: 4px; background-color: var(--thaw-background-color); border-radius: 2px; + cursor: pointer; } .thaw-slider-rail__fill { width: 0%; height: 4px; background-color: var(--thaw-background-color-fill); border-radius: 2px; + cursor: pointer; } .thaw-slider-handle { position: absolute; @@ -22,4 +25,5 @@ border-radius: 50%; background-color: white; box-shadow: 0px 0px 4px #2224; + transform: translateX(-50%); } diff --git a/src/slider/slider_label.css b/src/slider/slider_label.css new file mode 100644 index 0000000..b562b20 --- /dev/null +++ b/src/slider/slider_label.css @@ -0,0 +1,6 @@ +.thaw-slider-label { + position: absolute; + display: inline-block; + transform: translateX(-50%); + margin-top: 8px; +} diff --git a/src/slider/slider_label.rs b/src/slider/slider_label.rs new file mode 100644 index 0000000..89b8f1f --- /dev/null +++ b/src/slider/slider_label.rs @@ -0,0 +1,23 @@ +use leptos::*; + +use crate::utils::mount_style; + +#[component] +pub fn SliderLabel( + #[prop(into)] value: MaybeSignal, + children: Children, +) -> impl IntoView { + + mount_style("slider-label", include_str!("./slider_label.css")); + + view! { +
+ {children()} +
+ } +} +