mirror of
https://github.com/adoyle0/thaw.git
synced 2025-01-23 06:19:22 -05:00
feat: slider improvements (#40)
* feat: slider improvements * feat: slider improvements * feat: change the Slider step property to f64 --------- Co-authored-by: Cristobal Andrada <kandrelczyk@gmail.com> Co-authored-by: luoxiao <luoxiaozero@163.com> Co-authored-by: luoxiaozero <48741584+luoxiaozero@users.noreply.github.com>
This commit is contained in:
parent
6ab334cc94
commit
69dd0c5086
5 changed files with 183 additions and 18 deletions
|
@ -6,6 +6,8 @@ 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! {
|
||||
<div style="width: 896px; margin: 0 auto;">
|
||||
|
@ -25,6 +27,58 @@ pub fn SliderPage() -> impl IntoView {
|
|||
|
||||
</DemoCode>
|
||||
</Demo>
|
||||
<h3>"step"</h3>
|
||||
<Demo>
|
||||
<Slider step=10.0 value=stepped_value/>
|
||||
<DemoCode slot>
|
||||
|
||||
{highlight_str!(
|
||||
r#"
|
||||
let value = create_rw_signal(0.0);
|
||||
|
||||
<Slider step=10.0 value/>
|
||||
"#,
|
||||
"rust"
|
||||
)}
|
||||
|
||||
</DemoCode>
|
||||
</Demo>
|
||||
<h2>"SliderLabel"</h2>
|
||||
<Demo>
|
||||
<Slider value=labeled_value max=10.0 step=1.0>
|
||||
<SliderLabel value=0.0>
|
||||
"0"
|
||||
</SliderLabel>
|
||||
<SliderLabel value=5.0>
|
||||
"5"
|
||||
</SliderLabel>
|
||||
<SliderLabel value=10.0>
|
||||
"10"
|
||||
</SliderLabel>
|
||||
</Slider>
|
||||
<DemoCode slot>
|
||||
|
||||
{highlight_str!(
|
||||
r#"
|
||||
let value = create_rw_signal(0.0);
|
||||
|
||||
<Slider value max=10.0 step=1.0>
|
||||
<SliderLabel value=0>
|
||||
"0"
|
||||
</SliderLabel>
|
||||
<SliderLabel value=5>
|
||||
"5"
|
||||
</SliderLabel>
|
||||
<SliderLabel value=10>
|
||||
"10"
|
||||
</SliderLabel>
|
||||
</Slider>
|
||||
"#,
|
||||
"rust"
|
||||
)}
|
||||
|
||||
</DemoCode>
|
||||
</Demo>
|
||||
<h3>"Slider Props"</h3>
|
||||
<Table single_column=true>
|
||||
<thead>
|
||||
|
@ -48,6 +102,43 @@ pub fn SliderPage() -> impl IntoView {
|
|||
<td>"100"</td>
|
||||
<td>"Max value of the slider."</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>"step"</td>
|
||||
<td>"MaybeSignal<f64>"</td>
|
||||
<td></td>
|
||||
<td>"The step in which value is incremented."</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>"children"</td>
|
||||
<td>"Option<Children>"</td>
|
||||
<td></td>
|
||||
<td>"Slider labels."</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</Table>
|
||||
<h3>"SliderLabel Props"</h3>
|
||||
<Table single_column=true>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>"Name"</th>
|
||||
<th>"Type"</th>
|
||||
<th>"Default"</th>
|
||||
<th>"Description"</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>"value"</td>
|
||||
<td>"ReadSignal<f64>"</td>
|
||||
<td></td>
|
||||
<td>"Value at which label will be placed."</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>"children"</td>
|
||||
<td>"Children"</td>
|
||||
<td></td>
|
||||
<td>"Content of the lable."</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</Table>
|
||||
</div>
|
||||
|
|
|
@ -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<f64>,
|
||||
#[prop(default = MaybeSignal::Static(100f64), into)] max: MaybeSignal<f64>,
|
||||
#[prop(optional, into)] step: MaybeSignal<f64>,
|
||||
#[prop(optional)] children: Option<Children>,
|
||||
) -> 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! {
|
||||
<div class="thaw-slider" style=move || css_vars.get()>
|
||||
<div
|
||||
class="thaw-slider"
|
||||
style=move || css_vars.get()
|
||||
on:click=on_mouse_click
|
||||
>
|
||||
<div class="thaw-slider-rail" ref=rail_ref>
|
||||
<div
|
||||
class="thaw-slider-rail__fill"
|
||||
style=move || format!("width: {}%", percentage.get())
|
||||
></div>
|
||||
</div>
|
||||
<OptionComp value=children let:children>
|
||||
{children()}
|
||||
</OptionComp>
|
||||
<div
|
||||
on:mousedown=on_mouse_down
|
||||
class="thaw-slider-handle"
|
||||
style=move || {
|
||||
format!(
|
||||
"left: {}%; transform: translateX(-{}%)", percentage.get(), percentage
|
||||
.get(),
|
||||
)
|
||||
format!("left: {}%", percentage.get())
|
||||
}
|
||||
>
|
||||
</div>
|
||||
|
|
|
@ -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%);
|
||||
}
|
||||
|
|
6
src/slider/slider_label.css
Normal file
6
src/slider/slider_label.css
Normal file
|
@ -0,0 +1,6 @@
|
|||
.thaw-slider-label {
|
||||
position: absolute;
|
||||
display: inline-block;
|
||||
transform: translateX(-50%);
|
||||
margin-top: 8px;
|
||||
}
|
23
src/slider/slider_label.rs
Normal file
23
src/slider/slider_label.rs
Normal file
|
@ -0,0 +1,23 @@
|
|||
use leptos::*;
|
||||
|
||||
use crate::utils::mount_style;
|
||||
|
||||
#[component]
|
||||
pub fn SliderLabel(
|
||||
#[prop(into)] value: MaybeSignal<f64>,
|
||||
children: Children,
|
||||
) -> impl IntoView {
|
||||
|
||||
mount_style("slider-label", include_str!("./slider_label.css"));
|
||||
|
||||
view! {
|
||||
<div
|
||||
class:thaw-slider-label=true
|
||||
style=move || {
|
||||
format!("left: calc(calc({} / var(--thaw-slider-max)) * 100%)", value.get())
|
||||
}>
|
||||
{children()}
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
Loading…
Add table
Reference in a new issue