use crate::common::ValueType; use crate::components::utils::create_unique_id; use leptos::prelude::*; use std::collections::HashMap; use crate::components::{use_validation_context, InputState, ValidationState}; #[component] pub fn Radio( #[prop()] checked: Signal, #[prop(into)] on_change: Callback<(), ()>, #[prop(into)] value: Signal, #[prop(into)] name: Signal, #[prop(optional, into)] disabled: Signal, ) -> impl IntoView { let id = create_unique_id(&name.get_untracked(), &value.get_untracked()); view! { } } fn validate(value: String, required: bool) -> ValidationState { if required && value.is_empty() { ValidationState::Invalid("This field is required".into()) } else { ValidationState::Valid } } #[component] pub fn RadioList( #[prop(into)] data_member: Signal, #[prop()] data_map: RwSignal>, #[prop()] items: Vec, #[prop(optional, into)] disabled: Signal, #[prop(optional, into)] label: String, #[prop(optional, into)] required: Signal, ) -> impl IntoView { //#region Value Logic // Store a single signal that is the currently selected value. We can only have one selected // value, so there's no list needed. The default selected value is whatever the map has selected // already, or nothing. let default_value = match data_map.get_untracked().get(&data_member.get_untracked()) { Some(ValueType::String(Some(value))) => value.clone(), _ => String::new() }; let selected_value = RwSignal::new(default_value); // When the selected value changes, update the data map at the correct location Effect::new(move || { data_map.update(|map| { map.insert(data_member.get(), ValueType::String(Some(selected_value.get()))); }); }); //#endregion //#region Validation Logic let validation_context = use_validation_context() .expect("Could not find FormValidationRegistry context"); let error = RwSignal::new(validate(selected_value.get_untracked(), required.get_untracked())); let dirty = RwSignal::new(false); let show_errors = Signal::derive(move || { dirty.get() && matches!(error.get(), ValidationState::Invalid(_)) }); let input_state = RwSignal::new(InputState::new( data_member.get_untracked(), error.clone(), dirty.clone() )); validation_context.validators.update(|list| list.push(input_state)); on_cleanup(move || { validation_context.validators.update(|list| { list.retain(|v| *v.get_untracked().input_name != *input_state.get_untracked().input_name) }); }); //#endregion //#region Render Logic view! {
{label} {items .into_iter() .map(|item| { let item = RwSignal::new(item); let checked_signal = Signal::derive(move || { selected_value.get() == item.get() }); let on_change = move || { // on_change only runs when the radio button is checked, not when unchecked. // Change the selected value to the new one. selected_value.set(item.get()); error.set(validate(selected_value.get_untracked(), required.get_untracked())); dirty.set(true); }; view! { } }) .collect::>()}
{move || { match error.get() { ValidationState::Invalid(msg) => msg, _ => "There is no error - should not see this message.".to_string() } }}
} //#endregion }