Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added parser and formatter props to input and input_number components #209

Merged
merged 4 commits into from
Jul 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions demo_markdown/docs/input/mod.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

| Name | Type | Default | Description |
Expand All @@ -116,6 +135,8 @@ view! {
| 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. |
| 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

Expand Down
45 changes: 45 additions & 0 deletions demo_markdown/docs/input_number/mod.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

| Name | Type | Default | Description |
Expand All @@ -55,6 +98,8 @@ view! {
| disabled | `MaybeSignal<bool>` | `false` | Whether the input is disabled. |
| 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. |
| 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

Expand Down
31 changes: 24 additions & 7 deletions thaw/src/input/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,20 +53,36 @@ pub fn Input(
#[prop(optional)] comp_ref: ComponentRef<InputRef>,
#[prop(optional, into)] class: OptionalProp<MaybeSignal<String>>,
#[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 {
let theme = use_theme(Theme::light);
mount_style("input", include_str!("./input.css"));

let value_trigger = create_trigger();
let on_input = move |ev| {
let input_value = event_target_value(&ev);
if let Some(allow_value) = allow_value.as_ref() {
if !allow_value.call(input_value.clone()) {
value_trigger.notify();
return;
if parser.is_none() {
let input_value = event_target_value(&ev);
if let Some(allow_value) = allow_value.as_ref() {
if !allow_value.call(input_value.clone()) {
value_trigger.notify();
return;
}
}
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);
}
value.set(input_value);
};
let is_focus = create_rw_signal(false);
let on_internal_focus = move |ev| {
Expand Down Expand Up @@ -186,9 +202,10 @@ pub fn Input(
value=input_value
prop:value=move || {
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:focus=on_internal_focus
on:blur=on_internal_blur
Expand Down
11 changes: 11 additions & 0 deletions thaw/src/input_number/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ pub fn InputNumber<T>(
#[prop(optional, into)] invalid: MaybeSignal<bool>,
#[prop(optional, into)] class: OptionalProp<MaybeSignal<String>>,
#[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(default = MaybeSignal::Static(T::min_value()), into)] min: MaybeSignal<T>,
#[prop(default = MaybeSignal::Static(T::max_value()), into)] max: MaybeSignal<T>,
Expand Down Expand Up @@ -77,6 +79,13 @@ where
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! {
<Input
attrs
Expand All @@ -88,6 +97,8 @@ where
invalid
comp_ref=input_ref
on_blur=set_within_range
parser
formatter
>
<InputSuffix slot>
<Button disabled=minus_disabled variant=ButtonVariant::Link on_click=sub>
Expand Down
2 changes: 2 additions & 0 deletions thaw_utils/src/optional_prop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ impl<T> From<Option<T>> for OptionalProp<T> {
}
}

impl<T: Copy> Copy for OptionalProp<T> {}

#[cfg(test)]
mod test {
use super::OptionalProp;
Expand Down
Loading