diff --git a/demo_markdown/docs/slider/mod.md b/demo_markdown/docs/slider/mod.md index f37522a..d007071 100644 --- a/demo_markdown/docs/slider/mod.md +++ b/demo_markdown/docs/slider/mod.md @@ -1,7 +1,7 @@ # Slider ```rust demo -let value = create_rw_signal(0.0); +let value = RwSignal::new(0.0); view! { @@ -11,20 +11,20 @@ view! { ### Step ```rust demo -let value = create_rw_signal(0.0); +let value = RwSignal::new(0.0); view! { - + } ``` ## Slider Label ```rust demo -let value = create_rw_signal(0.0); +let value = RwSignal::new(0.0); view! { - + "0" diff --git a/thaw/src/slider/mod.rs b/thaw/src/slider/mod.rs index 2719ec1..ee277d3 100644 --- a/thaw/src/slider/mod.rs +++ b/thaw/src/slider/mod.rs @@ -5,114 +5,106 @@ pub use slider_label::SliderLabel; use leptos::*; use thaw_components::OptionComp; use thaw_utils::{class_list, mount_style, Model, OptionalProp}; -use web_sys::DomRect; #[component] pub fn Slider( #[prop(optional, into)] value: Model, - #[prop(default = MaybeSignal::Static(100f64), into)] max: MaybeSignal, - #[prop(optional, into)] step: MaybeSignal, + #[prop(default = 0f64.into(), into)] min: MaybeSignal, + #[prop(default = 100f64.into(), into)] max: MaybeSignal, + #[prop(optional, into)] step: MaybeProp, #[prop(optional, into)] class: OptionalProp>, #[prop(optional)] children: Option, ) -> impl IntoView { mount_style("slider", include_str!("./slider.css")); - let percentage = create_memo(move |_| { - if value.get() < 0.0 || max.get() <= 0.0 { - 0f64 - } else if value.get() >= max.get() { - 100f64 + let is_chldren = children.is_some(); + let list_id = is_chldren.then(|| uuid::Uuid::new_v4().to_string()); + let current_value = Memo::new(move |_| { + let max = max.get(); + let min = min.get(); + let v = value.get(); + if v > max { + max + } else if v < min { + min } else { - value.get() / max.get() * 100.0 + v } }); - 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; - } + let on_input = move |e: ev::Event| { + if let Ok(range_value) = event_target_value(&e).parse::() { + value.set(range_value); + } + }; + + let css_vars = move || { + let max = max.get(); + let min = min.get(); + let mut css_vars = format!( + "--thaw-slider--direction: 90deg;--thaw-slider--progress: {:.2}%;", + if max == min { + 0.0 + } else { + (current_value.get() - min) / (max - min) * 100.0 + } + ); + + if is_chldren { + css_vars.push_str(&format!("--thaw-slider--max: {:.2};", max)); + css_vars.push_str(&format!("--thaw-slider--min: {:.2};", min)); + } + + if let Some(step) = step.get() { + if step > 0.0 { + css_vars.push_str(&format!( + "--thaw-slider--steps-percent: {:.2}%", + step * 100.0 / (max - min) + )); } } - value.set(val); + css_vars }; - let rail_ref = create_node_ref::(); - let (mouse_move_value, set_mouse_move_value) = create_signal::>(None); - let (is_mouse_move, set_mouse_move) = create_signal(false); - let on_mouse_down = move |_| { - 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| { - if is_mouse_move.get_untracked() { - 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); - }; - } - }); - on_cleanup(move || on_mouse_move.remove()); - view! { -
- -
-
-
-
- - {children()} - +
- -
+ class=class_list!["thaw-slider", class.map(| c | move || c.get())] + style=css_vars + > + +
+
+
+
+ + + {children()} + + + + + } +} + +#[derive(Clone)] +pub(crate) struct SliderInjection { + pub max: MaybeSignal, + pub min: MaybeSignal, +} + +impl SliderInjection { + pub fn use_() -> Self { + expect_context() } } diff --git a/thaw/src/slider/slider.css b/thaw/src/slider/slider.css index 835782b..afa6a74 100644 --- a/thaw/src/slider/slider.css +++ b/thaw/src/slider/slider.css @@ -125,26 +125,9 @@ var(--colorNeutralStroke1); } -.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 { +.thaw-slider__datalist { + display: block; position: absolute; - top: 0px; - width: 16px; - height: 16px; - border-radius: 50%; - background-color: white; - box-shadow: 0px 0px 4px #2224; - transform: translateX(-50%); + width: 100%; + top: calc(var(--thaw-slider__thumb--size) + 4px); } diff --git a/thaw/src/slider/slider_label.css b/thaw/src/slider/slider_label.css index b562b20..725911c 100644 --- a/thaw/src/slider/slider_label.css +++ b/thaw/src/slider/slider_label.css @@ -1,6 +1,5 @@ .thaw-slider-label { position: absolute; - display: inline-block; + display: inline-block; transform: translateX(-50%); - margin-top: 8px; } diff --git a/thaw/src/slider/slider_label.rs b/thaw/src/slider/slider_label.rs index a18860e..9d24004 100644 --- a/thaw/src/slider/slider_label.rs +++ b/thaw/src/slider/slider_label.rs @@ -1,19 +1,27 @@ use leptos::*; use thaw_utils::mount_style; +use crate::SliderInjection; + #[component] pub fn SliderLabel(#[prop(into)] value: MaybeSignal, children: Children) -> impl IntoView { mount_style("slider-label", include_str!("./slider_label.css")); + let slider = SliderInjection::use_(); + + let style = move || { + let value = (value.get() - slider.min.get()) / (slider.max.get() - slider.min.get()); + format!("left: calc({} * (100% - var(--thaw-slider__thumb--size)) + var(--thaw-slider__thumb--size) / 2)", value) + }; + view! { -
{children()} -
+ } }