mirror of
https://github.com/adoyle0/thaw.git
synced 2025-01-23 06:19:22 -05:00
Added parser and formatter props to input and input_number components (#209)
* Added parser and formatter properties to input and input_number components * Implemented copy trait for OptionalProp * InputNumber's formatter and parser using its generic type * Fixed InputNumber's docs
This commit is contained in:
parent
7685e99c8b
commit
089b3a024d
5 changed files with 103 additions and 7 deletions
|
@ -102,6 +102,25 @@ view! {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Formatter
|
||||||
|
|
||||||
|
```rust demo
|
||||||
|
let value = create_rw_signal(String::from("loren_ipsun"));
|
||||||
|
|
||||||
|
let formatter = Callback::<String, String>::new(move |v: String| {
|
||||||
|
v.replace("_", " ")
|
||||||
|
});
|
||||||
|
|
||||||
|
let parser = Callback::<String, String>::new(move |v: String| {
|
||||||
|
v.replace(" ", "_")
|
||||||
|
});
|
||||||
|
|
||||||
|
view! {
|
||||||
|
<Input value parser formatter />
|
||||||
|
<p>"Underlying value: "{ value }</p>
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### Input Props
|
### Input Props
|
||||||
|
|
||||||
| Name | Type | Default | Description |
|
| Name | Type | Default | Description |
|
||||||
|
@ -116,6 +135,8 @@ view! {
|
||||||
| on_focus | `Option<Callback<ev::FocusEvent>>` | `None` | Callback triggered when the input is focussed on. |
|
| on_focus | `Option<Callback<ev::FocusEvent>>` | `None` | Callback triggered when the input is focussed on. |
|
||||||
| on_blur | `Option<Callback<ev::FocusEvent>>` | `None` | Callback triggered when the input is blurred. |
|
| on_blur | `Option<Callback<ev::FocusEvent>>` | `None` | Callback triggered when the input is blurred. |
|
||||||
| attr: | `Vec<(&'static str, Attribute)>` | `Default::default()` | The dom attrs of the input element inside the component. |
|
| attr: | `Vec<(&'static str, Attribute)>` | `Default::default()` | The dom attrs of the input element inside the component. |
|
||||||
|
| parser | `OptionalProp<Callback<String, String>>` | `Default::default()` | Modifies the user input before assigning it to the value |
|
||||||
|
| formatter | `OptionalProp<Callback<String, String>>` | `Default::default()` | Formats the value to be shown to the user |
|
||||||
|
|
||||||
### Input Slots
|
### Input Slots
|
||||||
|
|
||||||
|
|
|
@ -42,6 +42,49 @@ view! {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Formatter
|
||||||
|
|
||||||
|
```rust demo
|
||||||
|
let value = create_rw_signal(0.0);
|
||||||
|
let value_2 = create_rw_signal(0.0);
|
||||||
|
|
||||||
|
let formatter = Callback::<f64, String>::new(move |v: f64| {
|
||||||
|
let v = v.to_string();
|
||||||
|
let dot_pos = v.chars().position(|c| c == '.').unwrap_or_else(|| v.chars().count());
|
||||||
|
let mut int: String = v.chars().take(dot_pos).collect();
|
||||||
|
|
||||||
|
let sign: String = if v.chars().take(1).collect::<String>() == String::from("-") {
|
||||||
|
int = int.chars().skip(1).collect();
|
||||||
|
String::from("-")
|
||||||
|
} else {
|
||||||
|
String::from("")
|
||||||
|
};
|
||||||
|
|
||||||
|
let dec: String = v.chars().skip(dot_pos + 1).take(2).collect();
|
||||||
|
|
||||||
|
let int = int
|
||||||
|
.as_bytes()
|
||||||
|
.rchunks(3)
|
||||||
|
.rev()
|
||||||
|
.map(std::str::from_utf8)
|
||||||
|
.collect::<Result<Vec<&str>, _>>()
|
||||||
|
.unwrap()
|
||||||
|
.join(".");
|
||||||
|
format!("{}{},{:0<2}", sign, int, dec)
|
||||||
|
});
|
||||||
|
|
||||||
|
let parser = Callback::<String, f64>::new(move |v: String| {
|
||||||
|
let comma_pos = v.chars().position(|c| c == ',').unwrap_or_else(|| v.chars().count());
|
||||||
|
let int_part = v.chars().take(comma_pos).filter(|a| a.is_digit(10)).collect::<String>();
|
||||||
|
let dec_part = v.chars().skip(comma_pos + 1).take(2).filter(|a| a.is_digit(10)).collect::<String>();
|
||||||
|
format!("{:0<1}.{:0<2}", int_part, dec_part).parse::<f64>().unwrap_or_default()
|
||||||
|
});
|
||||||
|
|
||||||
|
view! {
|
||||||
|
<InputNumber value parser formatter step=1.0 />
|
||||||
|
<p>"Underlying value: "{ value }</p>
|
||||||
|
}
|
||||||
|
```
|
||||||
### InputNumber Props
|
### InputNumber Props
|
||||||
|
|
||||||
| Name | Type | Default | Description |
|
| Name | Type | Default | Description |
|
||||||
|
@ -55,6 +98,8 @@ view! {
|
||||||
| disabled | `MaybeSignal<bool>` | `false` | Whether the input is disabled. |
|
| disabled | `MaybeSignal<bool>` | `false` | Whether the input is disabled. |
|
||||||
| invalid | `MaybeSignal<bool>` | `false` | Whether the input is invalid. |
|
| invalid | `MaybeSignal<bool>` | `false` | Whether the input is invalid. |
|
||||||
| attr: | `Vec<(&'static str, Attribute)>` | `Default::default()` | The dom attrs of the input element inside the component. |
|
| attr: | `Vec<(&'static str, Attribute)>` | `Default::default()` | The dom attrs of the input element inside the component. |
|
||||||
|
| parser | `OptionalProp<Callback<String, T>>` | `Default::default()` | Modifies the user input before assigning it to the value |
|
||||||
|
| formatter | `OptionalProp<Callback<T, String>>` | `Default::default()` | Formats the value to be shown to the user |
|
||||||
|
|
||||||
#### T impl
|
#### T impl
|
||||||
|
|
||||||
|
|
|
@ -53,12 +53,15 @@ pub fn Input(
|
||||||
#[prop(optional)] comp_ref: ComponentRef<InputRef>,
|
#[prop(optional)] comp_ref: ComponentRef<InputRef>,
|
||||||
#[prop(optional, into)] class: OptionalProp<MaybeSignal<String>>,
|
#[prop(optional, into)] class: OptionalProp<MaybeSignal<String>>,
|
||||||
#[prop(attrs)] attrs: Vec<(&'static str, Attribute)>,
|
#[prop(attrs)] attrs: Vec<(&'static str, Attribute)>,
|
||||||
|
#[prop(optional, into)] parser: OptionalProp<Callback<String, String>>,
|
||||||
|
#[prop(optional, into)] formatter: OptionalProp<Callback<String, String>>,
|
||||||
) -> impl IntoView {
|
) -> impl IntoView {
|
||||||
let theme = use_theme(Theme::light);
|
let theme = use_theme(Theme::light);
|
||||||
mount_style("input", include_str!("./input.css"));
|
mount_style("input", include_str!("./input.css"));
|
||||||
|
|
||||||
let value_trigger = create_trigger();
|
let value_trigger = create_trigger();
|
||||||
let on_input = move |ev| {
|
let on_input = move |ev| {
|
||||||
|
if parser.is_none() {
|
||||||
let input_value = event_target_value(&ev);
|
let input_value = event_target_value(&ev);
|
||||||
if let Some(allow_value) = allow_value.as_ref() {
|
if let Some(allow_value) = allow_value.as_ref() {
|
||||||
if !allow_value.call(input_value.clone()) {
|
if !allow_value.call(input_value.clone()) {
|
||||||
|
@ -67,6 +70,19 @@ pub fn Input(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
value.set(input_value);
|
value.set(input_value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let on_change = move |ev| {
|
||||||
|
if let Some(parser) = parser.or_else(|| None) {
|
||||||
|
let parsed_input_value = parser.call(event_target_value(&ev));
|
||||||
|
if let Some(allow_value) = allow_value.as_ref() {
|
||||||
|
if !allow_value.call(parsed_input_value.clone()) {
|
||||||
|
value_trigger.notify();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
value.set(parsed_input_value);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
let is_focus = create_rw_signal(false);
|
let is_focus = create_rw_signal(false);
|
||||||
let on_internal_focus = move |ev| {
|
let on_internal_focus = move |ev| {
|
||||||
|
@ -186,9 +202,10 @@ pub fn Input(
|
||||||
value=input_value
|
value=input_value
|
||||||
prop:value=move || {
|
prop:value=move || {
|
||||||
value_trigger.track();
|
value_trigger.track();
|
||||||
value.get()
|
formatter.map_or_else(|| value.get(), |c| c.call(value.get()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
on:change=on_change
|
||||||
on:input=on_input
|
on:input=on_input
|
||||||
on:focus=on_internal_focus
|
on:focus=on_internal_focus
|
||||||
on:blur=on_internal_blur
|
on:blur=on_internal_blur
|
||||||
|
|
|
@ -14,6 +14,8 @@ pub fn InputNumber<T>(
|
||||||
#[prop(optional, into)] invalid: MaybeSignal<bool>,
|
#[prop(optional, into)] invalid: MaybeSignal<bool>,
|
||||||
#[prop(optional, into)] class: OptionalProp<MaybeSignal<String>>,
|
#[prop(optional, into)] class: OptionalProp<MaybeSignal<String>>,
|
||||||
#[prop(optional)] comp_ref: ComponentRef<InputNumberRef>,
|
#[prop(optional)] comp_ref: ComponentRef<InputNumberRef>,
|
||||||
|
#[prop(optional, into)] parser: OptionalProp<Callback<String, T>>,
|
||||||
|
#[prop(optional, into)] formatter: OptionalProp<Callback<T, String>>,
|
||||||
#[prop(attrs)] attrs: Vec<(&'static str, Attribute)>,
|
#[prop(attrs)] attrs: Vec<(&'static str, Attribute)>,
|
||||||
#[prop(default = MaybeSignal::Static(T::min_value()), into)] min: MaybeSignal<T>,
|
#[prop(default = MaybeSignal::Static(T::min_value()), into)] min: MaybeSignal<T>,
|
||||||
#[prop(default = MaybeSignal::Static(T::max_value()), into)] max: MaybeSignal<T>,
|
#[prop(default = MaybeSignal::Static(T::max_value()), into)] max: MaybeSignal<T>,
|
||||||
|
@ -77,6 +79,13 @@ where
|
||||||
invalid.get() || value < min.get() || value > max.get()
|
invalid.get() || value < min.get() || value > max.get()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let parser = parser.map(|parser| {
|
||||||
|
Callback::new(move |v| parser.call(v).to_string())
|
||||||
|
});
|
||||||
|
let formatter = formatter.map(|formatter| {
|
||||||
|
Callback::new(move |v: String| formatter.call(v.parse::<T>().unwrap_or_default()))
|
||||||
|
});
|
||||||
|
|
||||||
view! {
|
view! {
|
||||||
<Input
|
<Input
|
||||||
attrs
|
attrs
|
||||||
|
@ -88,6 +97,8 @@ where
|
||||||
invalid
|
invalid
|
||||||
comp_ref=input_ref
|
comp_ref=input_ref
|
||||||
on_blur=set_within_range
|
on_blur=set_within_range
|
||||||
|
parser
|
||||||
|
formatter
|
||||||
>
|
>
|
||||||
<InputSuffix slot>
|
<InputSuffix slot>
|
||||||
<Button disabled=minus_disabled variant=ButtonVariant::Link on_click=sub>
|
<Button disabled=minus_disabled variant=ButtonVariant::Link on_click=sub>
|
||||||
|
|
|
@ -96,6 +96,8 @@ impl<T> From<Option<T>> for OptionalProp<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T: Copy> Copy for OptionalProp<T> {}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::OptionalProp;
|
use super::OptionalProp;
|
||||||
|
|
Loading…
Add table
Reference in a new issue