fix: add hydration effect to selectable lists
This commit is contained in:
1 parent
c693ed9190
commit
c1f0c0cb81
3 files changed
+34
-21
No files matched your search
@@ -41,11 +41,11 @@ pub fn Checkbox(
|
|||||||
#[prop(into)] value: Signal<String>,
|
#[prop(into)] value: Signal<String>,
|
||||||
/// The name of the checkbox. This is used to group checkboxes together, so every checkbox
|
/// The name of the checkbox. This is used to group checkboxes together, so every checkbox
|
||||||
/// in a group should have the same name.
|
/// in a group should have the same name.
|
||||||
#[prop(into)] name: String,
|
#[prop(into)] name: Signal<String>,
|
||||||
/// A [`Signal`] indicating whether the checkbox is disabled.
|
/// A [`Signal`] indicating whether the checkbox is disabled.
|
||||||
#[prop(into)] disabled: Signal<bool>,
|
#[prop(into)] disabled: Signal<bool>,
|
||||||
) -> impl IntoView {
|
) -> impl IntoView {
|
||||||
let id = create_unique_id(&name, &value.get_untracked());
|
let id = create_unique_id(&name.get(), &value.get_untracked());
|
||||||
|
|
||||||
view! {
|
view! {
|
||||||
<label for=id class="flex items-center">
|
<label for=id class="flex items-center">
|
||||||
@@ -104,7 +104,7 @@ pub fn Checkbox(
|
|||||||
#[component]
|
#[component]
|
||||||
pub fn CheckboxList(
|
pub fn CheckboxList(
|
||||||
/// The data member that this list should edit/display from the `data_map`.
|
/// The data member that this list should edit/display from the `data_map`.
|
||||||
#[prop(into)] data_member: String,
|
#[prop(into)] data_member: Signal<String>,
|
||||||
/// A [`Signal`] allowing access to a data store.
|
/// A [`Signal`] allowing access to a data store.
|
||||||
#[prop()] data_map: RwSignal<HashMap<String, ValueType>>,
|
#[prop()] data_map: RwSignal<HashMap<String, ValueType>>,
|
||||||
/// The available choices for this list.
|
/// The available choices for this list.
|
||||||
@@ -159,7 +159,7 @@ pub fn CheckboxList(
|
|||||||
on_change=on_change
|
on_change=on_change
|
||||||
// The actual selected values are tracked by this element, not by the checkboxes themselves.
|
// The actual selected values are tracked by this element, not by the checkboxes themselves.
|
||||||
value=item
|
value=item
|
||||||
name=data_member.clone()
|
name=data_member
|
||||||
disabled=disabled
|
disabled=disabled
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,14 +27,14 @@ use super::utils::use_selectable_list;
|
|||||||
pub fn Chip(
|
pub fn Chip(
|
||||||
#[prop(into)] display_text: Signal<String>,
|
#[prop(into)] display_text: Signal<String>,
|
||||||
#[prop(into)] value: Signal<String>,
|
#[prop(into)] value: Signal<String>,
|
||||||
#[prop(into)] name: String,
|
#[prop(into)] name: Signal<String>,
|
||||||
#[prop(into)] on_change: Callback<(), ()>,
|
#[prop(into)] on_change: Callback<(), ()>,
|
||||||
#[prop()] checked: Signal<bool>,
|
#[prop()] checked: Signal<bool>,
|
||||||
#[prop(optional, into)] disabled: Signal<bool>,
|
#[prop(optional, into)] disabled: Signal<bool>,
|
||||||
) -> impl IntoView {
|
) -> impl IntoView {
|
||||||
// Generate a unique id - ensure that the value doesn't contain spaces.
|
// Generate a unique id - ensure that the value doesn't contain spaces.
|
||||||
// Without this id, checkbox/radio inputs can interfere with each other.
|
// Without this id, checkbox/radio inputs can interfere with each other.
|
||||||
let id = create_unique_id(&name, &value.get_untracked());
|
let id = create_unique_id(&name.get(), &value.get_untracked());
|
||||||
|
|
||||||
view! {
|
view! {
|
||||||
<label for=id>
|
<label for=id>
|
||||||
@@ -71,7 +71,7 @@ pub fn Chip(
|
|||||||
#[component]
|
#[component]
|
||||||
pub fn ChipsList(
|
pub fn ChipsList(
|
||||||
/// The member in the data map that this input affects.
|
/// The member in the data map that this input affects.
|
||||||
#[prop(into)] data_member: String,
|
#[prop(into)] data_member: Signal<String>,
|
||||||
/// The data map that contains the list of values. This will be edited as the input changes.
|
/// The data map that contains the list of values. This will be edited as the input changes.
|
||||||
#[prop()] data_map: RwSignal<HashMap<String, ValueType>>,
|
#[prop()] data_map: RwSignal<HashMap<String, ValueType>>,
|
||||||
/// The list of strings that should be displayed in the list.
|
/// The list of strings that should be displayed in the list.
|
||||||
@@ -132,7 +132,7 @@ pub fn ChipsList(
|
|||||||
on_change=on_change
|
on_change=on_change
|
||||||
value=value
|
value=value
|
||||||
display_text=text
|
display_text=text
|
||||||
name=data_member.clone()
|
name=data_member
|
||||||
disabled=disabled
|
disabled=disabled
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,24 +23,37 @@ fn value_type_to_vec(value_type: &ValueType) -> Vec<String> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn use_selectable_list(
|
pub fn use_selectable_list(
|
||||||
data_member: String,
|
data_member: Signal<String>,
|
||||||
data_map: RwSignal<HashMap<String, ValueType>>,
|
data_map: RwSignal<HashMap<String, ValueType>>,
|
||||||
required: Signal<bool>
|
required: Signal<bool>
|
||||||
) -> ListController {
|
) -> ListController {
|
||||||
//#region Central List Logic
|
//#region Central List Logic
|
||||||
|
|
||||||
// Get default values from the data map. This will only run once, when the input is created
|
|
||||||
// and mounted to the DOM.
|
|
||||||
let temp_map = data_map.get_untracked();
|
|
||||||
let origin_value_type = temp_map.get(&data_member)
|
|
||||||
.unwrap_or(&ValueType::List(None));
|
|
||||||
let origin_list = value_type_to_vec(origin_value_type);
|
|
||||||
|
|
||||||
// "Raw" selected values - just strings, no ValueTyping
|
// "Raw" selected values - just strings, no ValueTyping
|
||||||
let selected_list = RwSignal::new(origin_list);
|
let selected_list = RwSignal::new(Vec::new());
|
||||||
|
let hydrated = RwSignal::new(false);
|
||||||
|
let dirty = RwSignal::new(false);
|
||||||
|
|
||||||
|
// Hydration effect - runs only once, to hydrate data.
|
||||||
|
Effect::new(move || {
|
||||||
|
if hydrated.get() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// React to data_map changes
|
||||||
|
let map = data_map.get();
|
||||||
|
let key = data_member.get();
|
||||||
|
|
||||||
|
let list = map
|
||||||
|
.get(&key)
|
||||||
|
.unwrap_or(&ValueType::List(None));
|
||||||
|
|
||||||
|
selected_list.set(value_type_to_vec(list));
|
||||||
|
|
||||||
|
hydrated.set(true);
|
||||||
|
});
|
||||||
|
|
||||||
Effect::new({
|
Effect::new({
|
||||||
let data_member = data_member.clone();
|
|
||||||
move || {
|
move || {
|
||||||
// Convert selected strings into ValueTypes
|
// Convert selected strings into ValueTypes
|
||||||
let typed_list = selected_list.with(|list|
|
let typed_list = selected_list.with(|list|
|
||||||
@@ -51,7 +64,7 @@ pub fn use_selectable_list(
|
|||||||
|
|
||||||
// Update the map
|
// Update the map
|
||||||
data_map.update(|map| {
|
data_map.update(|map| {
|
||||||
map.insert(data_member.clone(), ValueType::List(Some(typed_list)));
|
map.insert(data_member.get(), ValueType::List(Some(typed_list)));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -69,11 +82,11 @@ pub fn use_selectable_list(
|
|||||||
ValidationState::Valid
|
ValidationState::Valid
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
let dirty = RwSignal::new(false);
|
|
||||||
let show_errors = Signal::derive(move || dirty.get() && matches!(error.get(), ValidationState::Invalid(_)));
|
let show_errors = Signal::derive(move || dirty.get() && matches!(error.get(), ValidationState::Invalid(_)));
|
||||||
|
|
||||||
let validator = RwSignal::new(InputState::new(
|
let validator = RwSignal::new(InputState::new(
|
||||||
data_member.clone(),
|
data_member.get(),
|
||||||
error.clone(),
|
error.clone(),
|
||||||
dirty.clone()
|
dirty.clone()
|
||||||
));
|
));
|
||||||
|
|||||||
Reference in new issue
Block a user