feat: Slider adds rules prop

This commit is contained in:
luoxiao 2024-08-22 16:37:27 +08:00 committed by luoxiaozero
parent aba7e8818a
commit 9fbf507a36
4 changed files with 78 additions and 12 deletions

View file

@ -71,6 +71,22 @@ view! {
<Field label="Time" name="time"> <Field label="Time" name="time">
<TimePicker rules=vec![TimePickerRule::required(true.into())]/> <TimePicker rules=vec![TimePickerRule::required(true.into())]/>
</Field> </Field>
<Field label="Slider" name="slider">
<Slider
step=25.0
rules=vec![SliderRule::validator(move |v, _| {
if v % 2.0 == 0.0 {
Err(FieldValidationState::Error("It has to be odd!".to_string()))
} else {
Ok(())
}
})]
>
<SliderLabel value=50.0>
"50"
</SliderLabel>
</Slider>
</Field>
<div style="margin-top: 8px"> <div style="margin-top: 8px">
<Button <Button
button_type=ButtonType::Submit button_type=ButtonType::Submit

View file

@ -40,14 +40,17 @@ view! {
### Slider Props ### Slider Props
| Name | Type | Default | Description | | Name | Type | Default | Description |
| -------- | ------------------- | -------------------- | ------------------------------------------- | | --- | --- | --- | --- |
| class | `MaybeProp<String>` | `Default::default()` | | | class | `MaybeProp<String>` | `Default::default()` | |
| value | `MaybeSignal<f64>` | `0` | The current value of the controlled Slider. | | id | `MaybeProp<String>` | `Default::default()` | |
| min | `MaybeSignal<f64>` | `0` | Min value of the slider. | | name | `MaybeProp<String>` | `Default::default()` | A string specifying a name for the input control. This name is submitted along with the control's value when the form data is submitted. |
| max | `MaybeSignal<f64>` | `100` | Max value of the slider. | | rules | `Vec<InputRule>` | `vec![]` | The rules to validate Field. |
| step | `MaybeSignal<f64>` | `0` | The step in which value is incremented. | | value | `MaybeSignal<f64>` | `0` | The current value of the controlled Slider. |
| children | `Option<Children>` | `None` | | | min | `MaybeSignal<f64>` | `0` | Min value of the slider. |
| max | `MaybeSignal<f64>` | `100` | Max value of the slider. |
| step | `MaybeSignal<f64>` | `0` | The step in which value is incremented. |
| children | `Option<Children>` | `None` | |
### SliderLabel props ### SliderLabel props

View file

@ -2,13 +2,23 @@ mod slider_label;
pub use slider_label::SliderLabel; pub use slider_label::SliderLabel;
use crate::{FieldInjection, FieldValidationState, Rule};
use leptos::{context::Provider, ev, prelude::*}; use leptos::{context::Provider, ev, prelude::*};
use std::ops::Deref;
use thaw_components::OptionComp; use thaw_components::OptionComp;
use thaw_utils::{class_list, mount_style, Model}; use thaw_utils::{class_list, mount_style, Model};
#[component] #[component]
pub fn Slider( pub fn Slider(
#[prop(optional, into)] class: MaybeProp<String>, #[prop(optional, into)] class: MaybeProp<String>,
#[prop(optional, into)] id: MaybeProp<String>,
/// A string specifying a name for the input control.
/// This name is submitted along with the control's value when the form data is submitted.
#[prop(optional, into)]
name: MaybeProp<String>,
/// The rules to validate Field.
#[prop(optional, into)]
rules: Vec<SliderRule>,
/// The current value of the controlled Slider. /// The current value of the controlled Slider.
#[prop(optional, into)] #[prop(optional, into)]
value: Model<f64>, value: Model<f64>,
@ -24,7 +34,8 @@ pub fn Slider(
#[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 (id, name) = FieldInjection::use_id_and_name(id, name);
let validate = Rule::validate(rules, value, name);
let is_chldren = children.is_some(); let is_chldren = children.is_some();
let list_id = is_chldren.then(|| uuid::Uuid::new_v4().to_string()); let list_id = is_chldren.then(|| uuid::Uuid::new_v4().to_string());
let current_value = Memo::new(move |_| { let current_value = Memo::new(move |_| {
@ -43,6 +54,7 @@ pub fn Slider(
let on_input = move |e: ev::Event| { let on_input = move |e: ev::Event| {
if let Ok(range_value) = event_target_value(&e).parse::<f64>() { if let Ok(range_value) = event_target_value(&e).parse::<f64>() {
value.set(range_value); value.set(range_value);
validate.run(Some(SliderRuleTrigger::Input));
} }
}; };
@ -83,6 +95,8 @@ pub fn Slider(
step=move || step.get() step=move || step.get()
type="range" type="range"
class="thaw-slider__input" class="thaw-slider__input"
id=id
name=name
on:input=on_input on:input=on_input
value=current_value.get_untracked() value=current_value.get_untracked()
prop:value=move || current_value.get() prop:value=move || current_value.get()
@ -111,3 +125,34 @@ impl SliderInjection {
expect_context() expect_context()
} }
} }
#[derive(Debug, Default, PartialEq, Clone, Copy)]
pub enum SliderRuleTrigger {
#[default]
Input,
}
pub struct SliderRule(Rule<f64, SliderRuleTrigger>);
impl SliderRule {
pub fn validator(
f: impl Fn(&f64, Signal<Option<String>>) -> Result<(), FieldValidationState>
+ Send
+ Sync
+ 'static,
) -> Self {
Self(Rule::validator(f))
}
pub fn with_trigger(self, trigger: SliderRuleTrigger) -> Self {
Self(Rule::with_trigger(self.0, trigger))
}
}
impl Deref for SliderRule {
type Target = Rule<f64, SliderRuleTrigger>;
fn deref(&self) -> &Self::Target {
&self.0
}
}

View file

@ -1,13 +1,15 @@
use crate::{ use crate::{
Button, ButtonSize, ConfigInjection, Icon, Input, InputSuffix, Scrollbar, ScrollbarRef, FieldInjection, FieldValidationState, Rule Button, ButtonSize, ConfigInjection, FieldInjection, FieldValidationState, Icon, Input,
InputSuffix, Rule, Scrollbar, ScrollbarRef,
}; };
use chrono::{Local, NaiveTime, Timelike}; use chrono::{Local, NaiveTime, Timelike};
use leptos::{html, prelude::*}; use leptos::{html, prelude::*};
use std::ops::Deref;
use thaw_components::{Binder, CSSTransition, Follower, FollowerPlacement}; use thaw_components::{Binder, CSSTransition, Follower, FollowerPlacement};
use thaw_utils::{ use thaw_utils::{
class_list, mount_style, ArcOneCallback, ComponentRef, OptionModel, OptionModelWithValue, SignalWatch class_list, mount_style, ArcOneCallback, ComponentRef, OptionModel, OptionModelWithValue,
SignalWatch,
}; };
use std::ops::Deref;
#[component] #[component]
pub fn TimePicker( pub fn TimePicker(