diff --git a/demo_markdown/docs/input/mod.md b/demo_markdown/docs/input/mod.md
index 1b00cfd..e1410fa 100644
--- a/demo_markdown/docs/input/mod.md
+++ b/demo_markdown/docs/input/mod.md
@@ -6,7 +6,7 @@ let value = RwSignal::new(String::from("o"));
view! {
-
+
}
```
@@ -83,19 +83,24 @@ view! {
}
```
-### Input attrs
+### Custom parsing
```rust demo
+let value = RwSignal::new(String::from("loren_ipsun"));
+
+let format = move |v: String| {
+ v.replace("_", " ")
+};
+let parser = move |v: String| {
+ Some(v.replace(" ", "_"))
+};
+
view! {
-
-
-
-
+
+
"Underlying value: "{ value }
}
```
-
-
### Input Props
| Name | Type | Default | Description |
diff --git a/demo_markdown/docs/spin_button/mod.md b/demo_markdown/docs/spin_button/mod.md
index 1374443..a8cae4b 100644
--- a/demo_markdown/docs/spin_button/mod.md
+++ b/demo_markdown/docs/spin_button/mod.md
@@ -30,4 +30,48 @@ let value = RwSignal::new(0);
view! {
}
+```
+
+### Custom parsing
+
+```rust demo
+let value = RwSignal::new(0.0);
+
+let format = 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::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::, _>>()
+ .unwrap()
+ .join(".");
+ format!("{}{},{:0<2}", sign, int, dec)
+};
+
+let parser = 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::();
+ let dec_part = v.chars().skip(comma_pos + 1).take(2).filter(|a| a.is_digit(10)).collect::();
+
+ format!("{:0<1}.{:0<2}", int_part, dec_part).parse::().ok()
+};
+
+view! {
+
+ "Underlying value: "{ value }
+}
```
\ No newline at end of file
diff --git a/thaw/src/input/mod.rs b/thaw/src/input/mod.rs
index b87bca2..e431d72 100644
--- a/thaw/src/input/mod.rs
+++ b/thaw/src/input/mod.rs
@@ -34,45 +34,44 @@ pub fn Input(
#[prop(optional)] input_suffix: Option,
#[prop(optional)] comp_ref: ComponentRef,
#[prop(optional, into)] class: MaybeProp,
- #[prop(optional, into)] parser: OptionalProp>,
+ #[prop(optional, into)] parser: OptionalProp>>,
#[prop(optional, into)] format: OptionalProp>,
// #[prop(attrs)] attrs: Vec<(&'static str, Attribute)>,
) -> impl IntoView {
mount_style("input", include_str!("./input.css"));
- let value_trigger = ArcTrigger::new();
let parser_none = parser.is_none();
let on_input = {
- let value_trigger = value_trigger.clone();
let allow_value = allow_value.clone();
-
move |e| {
- if parser_none {
- let input_value = event_target_value(&e);
- if let Some(allow_value) = allow_value.as_ref() {
- if !allow_value(input_value.clone()) {
- value_trigger.trigger();
- return;
- }
- }
- value.set(input_value);
+ if !parser_none {
+ return;
}
+ let input_value = event_target_value(&e);
+ if let Some(allow_value) = allow_value.as_ref() {
+ if !allow_value(input_value.clone()) {
+ value.update(|_| {});
+ return;
+ }
+ }
+ value.set(input_value);
}
};
- let on_change = {
- let value_trigger = value_trigger.clone();
- move |e| {
- if let Some(parser) = parser.as_ref() {
- let parsed_input_value = parser(event_target_value(&e));
- if let Some(allow_value) = allow_value.as_ref() {
- if !allow_value(parsed_input_value.clone()) {
- value_trigger.trigger();
- return;
- }
- }
- value.set(parsed_input_value);
+ let on_change = move |e| {
+ let Some(parser) = parser.as_ref() else {
+ return;
+ };
+ let Some(parsed_input_value) = parser(event_target_value(&e)) else {
+ value.update(|_| {});
+ return;
+ };
+ if let Some(allow_value) = allow_value.as_ref() {
+ if !allow_value(parsed_input_value.clone()) {
+ value.update(|_| {});
+ return;
}
}
+ value.set(parsed_input_value);
};
let is_focus = RwSignal::new(false);
let on_internal_focus = move |ev| {
@@ -152,8 +151,12 @@ pub fn Input(
type=move || input_type.get().as_str()
value=input_value
prop:value=move || {
- value_trigger.track();
- format.as_ref().map_or_else(|| value.get(), |f| f(value.get()))
+ let value = value.get();
+ if let Some(format) = format.as_ref() {
+ format(value)
+ } else {
+ value.to_string()
+ }
}
on:input=on_input
diff --git a/thaw/src/spin_button/mod.rs b/thaw/src/spin_button/mod.rs
index d27122f..8ab9a34 100644
--- a/thaw/src/spin_button/mod.rs
+++ b/thaw/src/spin_button/mod.rs
@@ -2,7 +2,9 @@ use leptos::prelude::*;
use num_traits::Bounded;
use std::ops::{Add, Sub};
use std::str::FromStr;
-use thaw_utils::{class_list, mount_style, with, Model, StoredMaybeSignal};
+use thaw_utils::{
+ class_list, mount_style, with, BoxOneCallback, Model, OptionalProp, StoredMaybeSignal,
+};
#[component]
pub fn SpinButton(
@@ -12,6 +14,8 @@ pub fn SpinButton(
#[prop(default = T::min_value().into(), into)] min: MaybeSignal,
#[prop(default = T::max_value().into(), into)] max: MaybeSignal,
#[prop(optional, into)] disabled: MaybeSignal,
+ #[prop(optional, into)] parser: OptionalProp>>,
+ #[prop(optional, into)] format: OptionalProp>,
) -> impl IntoView
where
T: Send + Sync,
@@ -24,19 +28,6 @@ where
let step_page: StoredMaybeSignal<_> = step_page.into();
let min: StoredMaybeSignal<_> = min.into();
let max: StoredMaybeSignal<_> = max.into();
- let input_value = RwSignal::new(String::new());
-
- Effect::new_isomorphic(move |prev| {
- value.with(|value| {
- if let Some(prev) = prev {
- if value == &prev {
- return prev;
- }
- }
- input_value.set(value.to_string());
- value.clone()
- })
- });
let update_value = move |new_value| {
let min = min.get_untracked();
@@ -53,6 +44,21 @@ where
let increment_disabled = Memo::new(move |_| disabled.get() || value.get() >= max.get());
let decrement_disabled = Memo::new(move |_| disabled.get() || value.get() <= min.get());
+ let on_change = move |e| {
+ let target_value = event_target_value(&e);
+ let v = if let Some(parser) = parser.as_ref() {
+ parser(target_value)
+ } else {
+ target_value.parse::().ok()
+ };
+
+ if let Some(value) = v {
+ update_value(value);
+ } else {
+ value.update(|_| {});
+ }
+ };
+
view! {
() else {
- input_value.update(|_| {});
- return;
- };
- update_value(v);
+ prop:value=move || {
+ let value = value.get();
+ if let Some(format) = format.as_ref() {
+ format(value)
+ } else {
+ value.to_string()
+ }
}
+ class="thaw-spin-button__input"
+ on:change=on_change
/>