diff --git a/thaw/src/field/field.css b/thaw/src/field/field.css index 90c6c1a..a05d04e 100644 --- a/thaw/src/field/field.css +++ b/thaw/src/field/field.css @@ -41,6 +41,10 @@ color: var(--colorPaletteRedForeground1); } +.thaw-field--error .thaw-input:not(:focus-within) { + border-color: var(--colorPaletteRedBorder2); +} + .thaw-field__validation-message-icon { display: inline-block; font-size: 12px; diff --git a/thaw/src/field/field.rs b/thaw/src/field/field.rs index 97f1ecf..1f6d7fc 100644 --- a/thaw/src/field/field.rs +++ b/thaw/src/field/field.rs @@ -9,6 +9,8 @@ pub fn Field( /// The label associated with the field. #[prop(optional, into)] label: MaybeProp, + /// A string specifying a name for the input control. + /// This name is submitted along with the control's value when the form data is submitted. #[prop(optional, into)] name: MaybeProp, /// The orientation of the label relative to the field component. #[prop(optional, into)] @@ -176,7 +178,8 @@ impl FieldInjection { (id, name) } - pub fn update_validation_state(&self, state: Option) { + pub fn update_validation_state(&self, state: Result<(), FieldValidationState>) { + let state = state.err(); self.validation_state.try_maybe_update(|validation_state| { if validation_state == &state { (false, ()) diff --git a/thaw/src/input/mod.rs b/thaw/src/input/mod.rs index 15d1bee..07924a5 100644 --- a/thaw/src/input/mod.rs +++ b/thaw/src/input/mod.rs @@ -63,25 +63,42 @@ pub fn Input( let (id, name) = FieldInjection::use_id_and_name(id, name); let field_injection = FieldInjection::use_context(); let rules = StoredValue::new(rules); - let validate = Callback::new(move |trigger| { + let validate = Callback::new(move |trigger: Option| { let state = rules.with_value(move |rules| { if rules.is_empty() { - return None; + return Some(Ok(())); } let mut rules_iter = rules.iter(); + let mut call_count = 0; loop { let Some(rule) = rules_iter.next() else { - return None; + if call_count == 0 { + return None; + } else { + return Some(Ok(())); + } }; - if let Err(state) = rule.call_validator(trigger, value, name) { + if let Some(trigger) = trigger.as_ref() { + if &rule.trigger != trigger { + continue; + } + } + call_count += 1; + + let state = rule.call_validator(value, name); + if state.is_err() { return Some(state); } } }); - let rt = state.is_none(); + let Some(state) = state else { + return true; + }; + + let rt = state.is_ok(); if let Some(field_injection) = field_injection.as_ref() { field_injection.update_validation_state(state); }; @@ -335,16 +352,9 @@ impl InputRule { fn call_validator( &self, - trigger: Option, value: Model, name: Signal>, ) -> Result<(), FieldValidationState> { - if let Some(trigger) = trigger { - if self.trigger != trigger { - return Ok(()); - } - } - value.with_untracked(|value| match &self.validator { InputRuleValidator::Required(required) => { if required.get_untracked() && value.is_empty() {