feat: update text field to use open-ended structures

This commit is contained in:
alphafetus committed 2025-09-25 09:29:04 -04:00
1 parent 6512f5fe13
commit 7a453ad367
8 files changed
+166 -47

No files matched your search

+3 -2
View File
@@ -14,6 +14,7 @@ fn render_entry_component(entry_data: MultiEntryData, member_info: MultiEntryMem
InputType::Text => view! {
<OutlinedTextField
label=member_info.display_name
data_member=member_info.member_name
placeholder="testing"
value=reactive
/>
@@ -92,11 +93,11 @@ pub fn MultiEntry(
let mut new_entry = MultiEntryData::new();
new_entry.data.insert(
name_member.get().member_name,
ValueType::String(String::from("Testing")),
ValueType::String(Some(String::from("Testing"))),
);
new_entry.data.insert(
info_member.get().member_name,
ValueType::String(new_entry.id.to_string()),
ValueType::String(Some(new_entry.id.to_string())),
);
entries.update(|entry_vec| entry_vec.push(new_entry));
};
+15 -15
View File
@@ -7,11 +7,11 @@ use std::fmt::Display;
#[derive(Clone, Serialize, Deserialize, Debug)]
pub enum ValueType {
/// Indicates a `String` value.
String(String),
String(Option<String>),
/// Indicates an `i32` value.
Number(i32),
Number(Option<i32>),
/// Indicates a `Vec<T>`, where `T` can be any other `ValueType`.
List(Vec<ValueType>),
List(Option<Vec<ValueType>>),
}
impl ValueType {
@@ -21,11 +21,11 @@ impl ValueType {
}
/// Attempts to get this value as a `String` type. This is different from the `to_string` method.
pub fn as_string(&self) -> Option<String> {
pub fn as_string(&self) -> Result<Option<String>, Self> {
if let ValueType::String(v) = self {
Some(v.clone())
Ok(v.clone())
} else {
None
Err(self.clone())
}
}
@@ -35,11 +35,11 @@ impl ValueType {
}
/// Attempts to get this value as an `i32`.
pub fn as_number(&self) -> Option<&i32> {
pub fn as_number(&self) -> Result<Option<i32>, Self> {
if let ValueType::Number(v) = self {
Some(v)
Ok(v.clone())
} else {
None
Err(self.clone())
}
}
@@ -50,26 +50,26 @@ impl ValueType {
/// Attempts to get this value as a `Vec<ValueType>`. Each of the values inside the list must
/// also be converted later, as these values are any of the possible types in the `ValueType`.
pub fn as_list(&self) -> Option<&Vec<ValueType>> {
pub fn as_list(&self) -> Result<Option<Vec<ValueType>>, Self> {
if let ValueType::List(v) = self {
Some(v)
Ok(v.clone())
} else {
None
Err(self.clone())
}
}
}
impl Default for ValueType {
fn default() -> Self {
ValueType::String(String::new())
ValueType::String(Some(String::new()))
}
}
impl Display for ValueType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let str = match self {
ValueType::String(s) => s.clone(),
ValueType::Number(n) => n.to_string(),
ValueType::String(s) => s.clone().unwrap_or("".to_owned()),
ValueType::Number(n) => n.unwrap_or(0).to_string(),
ValueType::List(l) => format!("{l:?}"),
};
write!(f, "{}", str)
+55 -20
View File
@@ -1,3 +1,5 @@
use std::collections::HashMap;
use leptos::leptos_dom::logging::console_log;
use leptos::prelude::*;
use crate::components::ValueType;
@@ -5,33 +7,66 @@ use crate::components::ValueType;
pub fn OutlinedTextField(
#[prop(optional, into)] placeholder: String,
// #[prop()] value: Subfield<TStore, TInner, ValueType>,
#[prop(into)] data_member: String, // should this be an RwSignal??
#[prop(optional)] data_map: RwSignal<HashMap<String, ValueType>>,
#[prop()] value: RwSignal<ValueType>,
#[prop(default = "text".to_owned(), into)] input_type: String,
#[prop(optional)] disabled: RwSignal<bool>,
#[prop(optional)] error: RwSignal<bool>,
#[prop(optional, into)] name: String,
#[prop(optional, into)] label: String,
#[prop(optional, into)] input_type: String,
#[prop(optional, into)] error_text: RwSignal<String>
) -> impl IntoView {
// Check if the type is one of the supported formats. This input will only accept a string
// or a number value.
let on_input = move |e| {
match value.get() {
ValueType::String(_) => {
// Define string parse function.
value.set(ValueType::String(event_target_value(&e)));
},
ValueType::Number(_) => {
// Define number parse function.
let temp = event_target_value(&e);
value.set(ValueType::Number(temp.parse::<i32>().unwrap_or(0)));
},
_ => {
// Create some custom function that reports failure.
let value_type = value.get();
println!("Input {value_type:?} is not a valid type.");
error.set(true);
error_text.set(format!("InputType {value_type:?} is not a valid type."));
// This function parses the input into the correct type. It only accepts numbers or strings as
// valid types, and parses them accordingly.
let on_input = {
let input_type = input_type.clone();
move |e| {
let to_parse = event_target_value(&e);
match input_type.as_str() {
"text" => {
// String parse function. It will always parse, but make sure the error
// state is set properly anyway.
error.set(false);
value.set(ValueType::String(Some(to_parse.clone())));
data_map.update(|map| {
if let Some(val) = map.get_mut(&data_member) {
*val = ValueType::String(Some(to_parse));
} else {
map.insert(data_member.clone(), ValueType::String(Some(to_parse)));
}
});
},
"number" => {
// Define number parse function.
// If the number parses, Some(num), else None, but we want to keep the value that's
// in the text field at this point, just set the error.
console_log(format!("Parsing {to_parse} to a number").as_str());
match to_parse.parse::<i32>().map(|n| ValueType::Number(Some(n))) {
Ok(value_type) => {
error.set(false);
value.set(value_type.clone());
data_map.update(|map| {
if let Some(val) = map.get_mut(&data_member) {
*val = value_type;
} else {
map.insert(data_member.clone(), value_type);
}
});
},
Err(_) => {
error.set(true);
error_text.set("Value is not a valid number.".to_owned());
}
};
},
_ => {
// Create some custom function that reports failure.
let msg = format!("Input {input_type:?} is not a valid type.");
console_log(msg.as_str());
error.set(true);
error_text.set(msg);
}
}
}
};