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]
|
#[component]
|
||||||
pub fn SliderPage() -> impl IntoView {
|
pub fn SliderPage() -> impl IntoView {
|
||||||
let value = create_rw_signal(0.0);
|
let value = create_rw_signal(0.0);
|
||||||
|
let stepped_value = create_rw_signal(0.0);
|
||||||
|
let labeled_value = create_rw_signal(0.0);
|
||||||
|
|
||||||
view! {
|
view! {
|
||||||
<div style="width: 896px; margin: 0 auto;">
|
<div style="width: 896px; margin: 0 auto;">
|
||||||
|
@ -25,6 +27,58 @@ pub fn SliderPage() -> impl IntoView {
|
||||||
|
|
||||||
</DemoCode>
|
</DemoCode>
|
||||||
</Demo>
|
</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>
|
<h3>"Slider Props"</h3>
|
||||||
<Table single_column=true>
|
<Table single_column=true>
|
||||||
<thead>
|
<thead>
|
||||||
|
@ -48,6 +102,43 @@ pub fn SliderPage() -> impl IntoView {
|
||||||
<td>"100"</td>
|
<td>"100"</td>
|
||||||
<td>"Max value of the slider."</td>
|
<td>"Max value of the slider."</td>
|
||||||
</tr>
|
</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>
|
</tbody>
|
||||||
</Table>
|
</Table>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,18 +1,25 @@
|
||||||
|
mod slider_label;
|
||||||
mod theme;
|
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 leptos::*;
|
||||||
|
use web_sys::DomRect;
|
||||||
|
|
||||||
|
pub use slider_label::SliderLabel;
|
||||||
pub use theme::SliderTheme;
|
pub use theme::SliderTheme;
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn Slider(
|
pub fn Slider(
|
||||||
#[prop(optional, into)] value: RwSignal<f64>,
|
#[prop(optional, into)] value: RwSignal<f64>,
|
||||||
#[prop(default = MaybeSignal::Static(100f64), into)] max: MaybeSignal<f64>,
|
#[prop(default = MaybeSignal::Static(100f64), into)] max: MaybeSignal<f64>,
|
||||||
|
#[prop(optional, into)] step: MaybeSignal<f64>,
|
||||||
|
#[prop(optional)] children: Option<Children>,
|
||||||
) -> impl IntoView {
|
) -> impl IntoView {
|
||||||
mount_style("slider", include_str!("./slider.css"));
|
mount_style("slider", include_str!("./slider.css"));
|
||||||
let theme = use_theme(Theme::light);
|
let theme = use_theme(Theme::light);
|
||||||
let css_vars = create_memo(move |_| {
|
let css_vars = create_memo(move |_| {
|
||||||
let mut css_vars = String::new();
|
let mut css_vars = String::new();
|
||||||
|
css_vars.push_str(&format!("--thaw-slider-max: {};", max.get()));
|
||||||
theme.with(|theme| {
|
theme.with(|theme| {
|
||||||
css_vars.push_str(&format!(
|
css_vars.push_str(&format!(
|
||||||
"--thaw-background-color: {};",
|
"--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);
|
value.set(val);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -48,16 +72,7 @@ pub fn Slider(
|
||||||
set_mouse_move.set(true);
|
set_mouse_move.set(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
let on_mouse_up = window_event_listener(ev::mouseup, move |_| {
|
let check_value_and_update = move |ev_x: f64, rect: DomRect| {
|
||||||
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());
|
|
||||||
if ev_x <= rect.x() {
|
if ev_x <= rect.x() {
|
||||||
set_mouse_move_value.set(Some(0.0));
|
set_mouse_move_value.set(Some(0.0));
|
||||||
} else if ev_x >= rect.x() + rect.width() {
|
} else if ev_x >= rect.x() + rect.width() {
|
||||||
|
@ -69,26 +84,52 @@ pub fn Slider(
|
||||||
do_update_value(value);
|
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());
|
on_cleanup(move || on_mouse_move.remove());
|
||||||
|
|
||||||
view! {
|
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" ref=rail_ref>
|
||||||
<div
|
<div
|
||||||
class="thaw-slider-rail__fill"
|
class="thaw-slider-rail__fill"
|
||||||
style=move || format!("width: {}%", percentage.get())
|
style=move || format!("width: {}%", percentage.get())
|
||||||
></div>
|
></div>
|
||||||
</div>
|
</div>
|
||||||
|
<OptionComp value=children let:children>
|
||||||
|
{children()}
|
||||||
|
</OptionComp>
|
||||||
<div
|
<div
|
||||||
on:mousedown=on_mouse_down
|
on:mousedown=on_mouse_down
|
||||||
class="thaw-slider-handle"
|
class="thaw-slider-handle"
|
||||||
style=move || {
|
style=move || {
|
||||||
format!(
|
format!("left: {}%", percentage.get())
|
||||||
"left: {}%; transform: translateX(-{}%)", percentage.get(), percentage
|
|
||||||
.get(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -2,17 +2,20 @@
|
||||||
position: relative;
|
position: relative;
|
||||||
padding: 6px 0;
|
padding: 6px 0;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
}
|
}
|
||||||
.thaw-slider-rail {
|
.thaw-slider-rail {
|
||||||
height: 4px;
|
height: 4px;
|
||||||
background-color: var(--thaw-background-color);
|
background-color: var(--thaw-background-color);
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
.thaw-slider-rail__fill {
|
.thaw-slider-rail__fill {
|
||||||
width: 0%;
|
width: 0%;
|
||||||
height: 4px;
|
height: 4px;
|
||||||
background-color: var(--thaw-background-color-fill);
|
background-color: var(--thaw-background-color-fill);
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
.thaw-slider-handle {
|
.thaw-slider-handle {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
@ -22,4 +25,5 @@
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
background-color: white;
|
background-color: white;
|
||||||
box-shadow: 0px 0px 4px #2224;
|
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