mirror of
https://github.com/adoyle0/thaw.git
synced 2025-02-02 08:34:15 -05:00
refactor: Slider
This commit is contained in:
parent
add2a0bae3
commit
f5c0d2a522
5 changed files with 105 additions and 123 deletions
|
@ -1,7 +1,7 @@
|
||||||
# Slider
|
# Slider
|
||||||
|
|
||||||
```rust demo
|
```rust demo
|
||||||
let value = create_rw_signal(0.0);
|
let value = RwSignal::new(0.0);
|
||||||
|
|
||||||
view! {
|
view! {
|
||||||
<Slider value/>
|
<Slider value/>
|
||||||
|
@ -11,20 +11,20 @@ view! {
|
||||||
### Step
|
### Step
|
||||||
|
|
||||||
```rust demo
|
```rust demo
|
||||||
let value = create_rw_signal(0.0);
|
let value = RwSignal::new(0.0);
|
||||||
|
|
||||||
view! {
|
view! {
|
||||||
<Slider step=10.0 value/>
|
<Slider step=25.0 value/>
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Slider Label
|
## Slider Label
|
||||||
|
|
||||||
```rust demo
|
```rust demo
|
||||||
let value = create_rw_signal(0.0);
|
let value = RwSignal::new(0.0);
|
||||||
|
|
||||||
view! {
|
view! {
|
||||||
<Slider value max=10.0 step=1.0>
|
<Slider value max=10.0 step=5.0>
|
||||||
<SliderLabel value=0.0>
|
<SliderLabel value=0.0>
|
||||||
"0"
|
"0"
|
||||||
</SliderLabel>
|
</SliderLabel>
|
||||||
|
|
|
@ -5,114 +5,106 @@ pub use slider_label::SliderLabel;
|
||||||
use leptos::*;
|
use leptos::*;
|
||||||
use thaw_components::OptionComp;
|
use thaw_components::OptionComp;
|
||||||
use thaw_utils::{class_list, mount_style, Model, OptionalProp};
|
use thaw_utils::{class_list, mount_style, Model, OptionalProp};
|
||||||
use web_sys::DomRect;
|
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn Slider(
|
pub fn Slider(
|
||||||
#[prop(optional, into)] value: Model<f64>,
|
#[prop(optional, into)] value: Model<f64>,
|
||||||
#[prop(default = MaybeSignal::Static(100f64), into)] max: MaybeSignal<f64>,
|
#[prop(default = 0f64.into(), into)] min: MaybeSignal<f64>,
|
||||||
#[prop(optional, into)] step: MaybeSignal<f64>,
|
#[prop(default = 100f64.into(), into)] max: MaybeSignal<f64>,
|
||||||
|
#[prop(optional, into)] step: MaybeProp<f64>,
|
||||||
#[prop(optional, into)] class: OptionalProp<MaybeSignal<String>>,
|
#[prop(optional, into)] class: OptionalProp<MaybeSignal<String>>,
|
||||||
#[prop(optional)] children: Option<Children>,
|
#[prop(optional)] children: Option<Children>,
|
||||||
) -> impl IntoView {
|
) -> impl IntoView {
|
||||||
mount_style("slider", include_str!("./slider.css"));
|
mount_style("slider", include_str!("./slider.css"));
|
||||||
|
|
||||||
let percentage = create_memo(move |_| {
|
let is_chldren = children.is_some();
|
||||||
if value.get() < 0.0 || max.get() <= 0.0 {
|
let list_id = is_chldren.then(|| uuid::Uuid::new_v4().to_string());
|
||||||
0f64
|
let current_value = Memo::new(move |_| {
|
||||||
} else if value.get() >= max.get() {
|
let max = max.get();
|
||||||
100f64
|
let min = min.get();
|
||||||
|
let v = value.get();
|
||||||
|
if v > max {
|
||||||
|
max
|
||||||
|
} else if v < min {
|
||||||
|
min
|
||||||
} else {
|
} else {
|
||||||
value.get() / max.get() * 100.0
|
v
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let do_update_value = move |mut val| {
|
let on_input = move |e: ev::Event| {
|
||||||
let step = step.get_untracked();
|
if let Ok(range_value) = event_target_value(&e).parse::<f64>() {
|
||||||
|
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 {
|
if step > 0.0 {
|
||||||
let result: f64 = val / step;
|
css_vars.push_str(&format!(
|
||||||
if result.fract() != 0.0 {
|
"--thaw-slider--steps-percent: {:.2}%",
|
||||||
let prev_multiple = (result.floor() * step).abs();
|
step * 100.0 / (max - min)
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
css_vars
|
||||||
value.set(val);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let rail_ref = create_node_ref::<html::Div>();
|
|
||||||
let (mouse_move_value, set_mouse_move_value) = create_signal::<Option<f64>>(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! {
|
view! {
|
||||||
|
<Provider value=SliderInjection { max, min }>
|
||||||
<div
|
<div
|
||||||
class=class_list!["thaw-slider", class.map(| c | move || c.get())]
|
class=class_list!["thaw-slider", class.map(| c | move || c.get())]
|
||||||
style=move || format!("--thaw-slider--direction: 90deg;--thaw-slider--progress: {:.2}%", value.get())
|
style=css_vars
|
||||||
on:click=on_mouse_click
|
|
||||||
>
|
>
|
||||||
<input type="range" class="thaw-slider__input"/>
|
<input
|
||||||
<div class="thaw-slider__rail" ref=rail_ref>
|
min=move || min.get()
|
||||||
|
max=move || max.get()
|
||||||
|
step=move || step.get()
|
||||||
|
type="range"
|
||||||
|
class="thaw-slider__input"
|
||||||
|
on:input=on_input
|
||||||
|
value=current_value.get_untracked()
|
||||||
|
prop:value=move || current_value.get()
|
||||||
|
list=list_id.clone()
|
||||||
|
/>
|
||||||
|
<div class="thaw-slider__rail">
|
||||||
</div>
|
</div>
|
||||||
<div class="thaw-slider__thumb">
|
<div class="thaw-slider__thumb">
|
||||||
</div>
|
</div>
|
||||||
<OptionComp value=children let:children>
|
<OptionComp value=children let:children>
|
||||||
|
<datalist id=list_id class="thaw-slider__datalist">
|
||||||
{children()}
|
{children()}
|
||||||
|
</datalist>
|
||||||
</OptionComp>
|
</OptionComp>
|
||||||
<div
|
|
||||||
on:mousedown=on_mouse_down
|
|
||||||
class="thaw-slider-handle"
|
|
||||||
style=move || { format!("left: {}%", percentage.get()) }
|
|
||||||
></div>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
</Provider>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub(crate) struct SliderInjection {
|
||||||
|
pub max: MaybeSignal<f64>,
|
||||||
|
pub min: MaybeSignal<f64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SliderInjection {
|
||||||
|
pub fn use_() -> Self {
|
||||||
|
expect_context()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -125,26 +125,9 @@
|
||||||
var(--colorNeutralStroke1);
|
var(--colorNeutralStroke1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.thaw-slider-rail {
|
.thaw-slider__datalist {
|
||||||
height: 4px;
|
display: block;
|
||||||
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;
|
position: absolute;
|
||||||
top: 0px;
|
width: 100%;
|
||||||
width: 16px;
|
top: calc(var(--thaw-slider__thumb--size) + 4px);
|
||||||
height: 16px;
|
|
||||||
border-radius: 50%;
|
|
||||||
background-color: white;
|
|
||||||
box-shadow: 0px 0px 4px #2224;
|
|
||||||
transform: translateX(-50%);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,5 +2,4 @@
|
||||||
position: absolute;
|
position: absolute;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
transform: translateX(-50%);
|
transform: translateX(-50%);
|
||||||
margin-top: 8px;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +1,27 @@
|
||||||
use leptos::*;
|
use leptos::*;
|
||||||
use thaw_utils::mount_style;
|
use thaw_utils::mount_style;
|
||||||
|
|
||||||
|
use crate::SliderInjection;
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn SliderLabel(#[prop(into)] value: MaybeSignal<f64>, children: Children) -> impl IntoView {
|
pub fn SliderLabel(#[prop(into)] value: MaybeSignal<f64>, children: Children) -> impl IntoView {
|
||||||
mount_style("slider-label", include_str!("./slider_label.css"));
|
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! {
|
view! {
|
||||||
<div
|
<option
|
||||||
class:thaw-slider-label=true
|
class:thaw-slider-label=true
|
||||||
style=move || {
|
style=style
|
||||||
format!("left: calc(calc({} / var(--thaw-slider-max)) * 100%)", value.get())
|
value=move || value.get()
|
||||||
}
|
|
||||||
>
|
>
|
||||||
|
|
||||||
{children()}
|
{children()}
|
||||||
</div>
|
</option>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue