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 />