Re-factor to separate Option/String handling from conditional render.
This commit is contained in:
parent
d9ddde9436
commit
2541c89161
2 changed files with 33 additions and 36 deletions
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "bootstrap-rs"
|
name = "bootstrap-rs"
|
||||||
version = "0.3.0"
|
version = "0.4.0"
|
||||||
authors = ["Thomas Gideon <cmdln@thecommandline.net>"]
|
authors = ["Thomas Gideon <cmdln@thecommandline.net>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
|
|
|
@ -7,25 +7,35 @@ mod padding;
|
||||||
pub use self::{border::Border, color::Color, edge::Edge, margin::Margin, padding::Padding};
|
pub use self::{border::Border, color::Color, edge::Edge, margin::Margin, padding::Padding};
|
||||||
use yew::prelude::*;
|
use yew::prelude::*;
|
||||||
|
|
||||||
|
/// A newtype that can be helpful to work with `String` values the represent direct user input.
|
||||||
|
///
|
||||||
|
/// This type is convertible into `Option<String>` which makes it helpful for wrapping user input
|
||||||
|
/// for optional fields when sent to the server. Leaving the wrapped type as a `String` value while
|
||||||
|
/// handling input and validation helps preserve the user's text as entered so they can continue to
|
||||||
|
/// edit or correct as needed.
|
||||||
|
pub struct InputString<S: AsRef<str>>(pub S);
|
||||||
|
|
||||||
|
impl<S: AsRef<str>> Into<Option<String>> for InputString<S> {
|
||||||
|
fn into(self) -> Option<String> {
|
||||||
|
let InputString(input) = self;
|
||||||
|
if input.as_ref().is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(input.as_ref().to_owned())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A convenience method for determining `ShouldRender` based on whether a value has changed, i.e.
|
||||||
|
/// the existing and new values are not equal, and its side effect of assigning `argument` to `assign_to` for the same condition.
|
||||||
|
///
|
||||||
|
/// Be careful if you choose to logically `or` calls of this function together for multiple
|
||||||
|
/// properties or state fields since short circuiting of any compound conditional may yield
|
||||||
|
/// surprising results, specifically failure to assign some fields. For this use case, collect the
|
||||||
|
/// results of each call into a collection then use reduce that collection to produce the result of
|
||||||
|
/// applying `or` to all its members.
|
||||||
pub fn render_if_ne<T: PartialEq>(assign_to: &mut T, argument: T) -> ShouldRender {
|
pub fn render_if_ne<T: PartialEq>(assign_to: &mut T, argument: T) -> ShouldRender {
|
||||||
render_if(assign_to, argument, identity, is_ne)
|
if assign_to != &argument {
|
||||||
}
|
|
||||||
|
|
||||||
pub fn render_if_opt_str<S: AsRef<str>>(
|
|
||||||
assign_to: &mut Option<String>,
|
|
||||||
argument: S,
|
|
||||||
) -> ShouldRender {
|
|
||||||
render_if(assign_to, argument, opt_from_str, is_ne)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn render_if<T, A>(
|
|
||||||
assign_to: &mut T,
|
|
||||||
argument: A,
|
|
||||||
map: impl Fn(A) -> T,
|
|
||||||
assign_render: impl Fn(&mut T, &T) -> bool,
|
|
||||||
) -> ShouldRender {
|
|
||||||
let argument = map(argument);
|
|
||||||
if assign_render(assign_to, &argument) {
|
|
||||||
*assign_to = argument;
|
*assign_to = argument;
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
|
@ -33,6 +43,9 @@ pub fn render_if<T, A>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A convenience method for determining which validation class to use for some input field
|
||||||
|
/// including one that has not yet been validating, represented by the argument having a `None`
|
||||||
|
/// value.
|
||||||
pub fn valid_as_class(v: &Option<bool>) -> &'static str {
|
pub fn valid_as_class(v: &Option<bool>) -> &'static str {
|
||||||
match v {
|
match v {
|
||||||
None => "",
|
None => "",
|
||||||
|
@ -41,22 +54,6 @@ pub fn valid_as_class(v: &Option<bool>) -> &'static str {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn identity<T>(t: T) -> T {
|
|
||||||
t
|
|
||||||
}
|
|
||||||
|
|
||||||
fn opt_from_str<S: AsRef<str>>(a: S) -> Option<String> {
|
|
||||||
if a.as_ref().is_empty() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(a.as_ref().to_owned())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_ne<T: PartialEq>(t: &mut T, a: &T) -> bool {
|
|
||||||
t != a
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -80,7 +77,7 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn render_if_opt() {
|
fn render_if_opt() {
|
||||||
let mut t = Some(String::from("test"));
|
let mut t = Some(String::from("test"));
|
||||||
let should = render_if_opt_str(&mut t, String::default());
|
let should = render_if_ne(&mut t, InputString(String::default()).into());
|
||||||
assert!(should, "Should have determined to render");
|
assert!(should, "Should have determined to render");
|
||||||
assert_eq!(t, None);
|
assert_eq!(t, None);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue