use super::types::FieldType;
use super::validators::{ValidationError, ValidatorFn, Validators};
use crate::reactive::{computed, signal, Computed, Signal};
use std::sync::Arc;
pub struct FormFieldBuilder {
field_type: FieldType,
label: String,
placeholder: String,
helper_text: String,
initial_value: String,
validators: Vec<ValidatorFn>,
disabled: bool,
matches_field: Option<String>,
}
impl Default for FormFieldBuilder {
fn default() -> Self {
Self::new()
}
}
impl FormFieldBuilder {
pub fn new() -> Self {
Self {
field_type: FieldType::Text,
label: String::new(),
placeholder: String::new(),
helper_text: String::new(),
initial_value: String::new(),
validators: Vec::new(),
disabled: false,
matches_field: None,
}
}
pub fn text(mut self) -> Self {
self.field_type = FieldType::Text;
self
}
pub fn password(mut self) -> Self {
self.field_type = FieldType::Password;
self
}
pub fn email(mut self) -> Self {
self.field_type = FieldType::Email;
self.validators.push(Validators::email());
self
}
pub fn number(mut self) -> Self {
self.field_type = FieldType::Number;
self.validators.push(Validators::numeric());
self
}
pub fn integer(mut self) -> Self {
self.field_type = FieldType::Integer;
self.validators.push(Validators::integer());
self
}
pub fn textarea(mut self) -> Self {
self.field_type = FieldType::TextArea;
self
}
pub fn label(mut self, label: impl Into<String>) -> Self {
self.label = label.into();
self
}
pub fn placeholder(mut self, placeholder: impl Into<String>) -> Self {
self.placeholder = placeholder.into();
self
}
pub fn helper_text(mut self, text: impl Into<String>) -> Self {
self.helper_text = text.into();
self
}
pub fn initial_value(mut self, value: impl Into<String>) -> Self {
self.initial_value = value.into();
self
}
pub fn required(mut self) -> Self {
self.validators.insert(0, Validators::required());
self
}
pub fn min_length(mut self, min: usize) -> Self {
self.validators.push(Validators::min_length(min));
self
}
pub fn max_length(mut self, max: usize) -> Self {
self.validators.push(Validators::max_length(max));
self
}
pub fn min(mut self, min: f64) -> Self {
self.validators.push(Validators::min_value(min));
self
}
pub fn max(mut self, max: f64) -> Self {
self.validators.push(Validators::max_value(max));
self
}
pub fn validator(mut self, validator: ValidatorFn) -> Self {
self.validators.push(validator);
self
}
pub fn disabled(mut self, disabled: bool) -> Self {
self.disabled = disabled;
self
}
pub fn matches(mut self, field_name: impl Into<String>) -> Self {
self.matches_field = Some(field_name.into());
self
}
pub fn build(self) -> FormField {
self.build_with_match(None)
}
pub(crate) fn build_with_match(self, match_signal: Option<Signal<String>>) -> FormField {
let value = signal(self.initial_value);
let touched = signal(false);
let validators = Arc::new(self.validators);
let value_for_errors = value.clone();
let validators_for_errors = validators.clone();
let match_signal_clone = match_signal.clone();
let errors = computed(move || {
let val = value_for_errors.get();
let mut errs: Vec<ValidationError> = validators_for_errors
.iter()
.filter_map(|v| v(&val).err())
.collect();
if let Some(ref match_sig) = match_signal_clone {
let match_val = match_sig.get();
if val != match_val {
errs.push(ValidationError::new("Fields do not match"));
}
}
errs
});
FormField {
field_type: self.field_type,
label: self.label,
placeholder: self.placeholder,
helper_text: self.helper_text,
value,
errors,
touched,
disabled: self.disabled,
validators,
}
}
pub(crate) fn get_matches_field(&self) -> Option<&str> {
self.matches_field.as_deref()
}
}
#[derive(Clone)]
pub struct FormField {
pub field_type: FieldType,
pub label: String,
pub placeholder: String,
pub helper_text: String,
value: Signal<String>,
errors: Computed<Vec<ValidationError>>,
touched: Signal<bool>,
pub disabled: bool,
#[allow(dead_code)]
validators: Arc<Vec<ValidatorFn>>,
}
impl Default for FormField {
fn default() -> Self {
FormFieldBuilder::new().build()
}
}
impl FormField {
pub fn text() -> FormFieldBuilder {
FormFieldBuilder::new().text()
}
pub fn password() -> FormFieldBuilder {
FormFieldBuilder::new().password()
}
pub fn email() -> FormFieldBuilder {
FormFieldBuilder::new().email()
}
pub fn number() -> FormFieldBuilder {
FormFieldBuilder::new().number()
}
pub fn integer() -> FormFieldBuilder {
FormFieldBuilder::new().integer()
}
pub fn textarea() -> FormFieldBuilder {
FormFieldBuilder::new().textarea()
}
pub fn helper_text(&self) -> &str {
&self.helper_text
}
pub fn value(&self) -> String {
self.value.get()
}
pub fn value_signal(&self) -> &Signal<String> {
&self.value
}
pub fn set_value(&self, value: impl Into<String>) {
self.value.set(value.into());
self.touched.set(true);
}
pub fn update_value(&self, f: impl FnOnce(&mut String)) {
self.value.update(f);
self.touched.set(true);
}
pub fn errors(&self) -> Vec<ValidationError> {
self.errors.get()
}
pub fn is_valid(&self) -> bool {
self.errors.get().is_empty()
}
pub fn has_errors(&self) -> bool {
!self.errors.get().is_empty()
}
pub fn first_error(&self) -> Option<String> {
self.errors.get().first().map(|e| e.message.clone())
}
pub fn is_touched(&self) -> bool {
self.touched.get()
}
pub fn touch(&self) {
self.touched.set(true);
}
pub fn reset(&self) {
self.value.set(String::new());
self.touched.set(false);
}
pub fn touched_signal(&self) -> &Signal<bool> {
&self.touched
}
}