mirror of
https://github.com/adoyle0/thaw.git
synced 2025-01-22 22:09:22 -05:00
refactor: OptionModel and VecModel with
This commit is contained in:
parent
3bba024540
commit
d33b4e27e8
12 changed files with 104 additions and 67 deletions
|
@ -2,7 +2,7 @@ use crate::{Button, ButtonGroup};
|
|||
use chrono::{Datelike, Days, Local, Month, Months, NaiveDate};
|
||||
use leptos::prelude::*;
|
||||
use std::ops::Deref;
|
||||
use thaw_utils::{class_list, mount_style, OptionModel};
|
||||
use thaw_utils::{class_list, mount_style, OptionModel, OptionModelWithValue};
|
||||
|
||||
#[component]
|
||||
pub fn Calendar(
|
||||
|
@ -136,7 +136,12 @@ fn CalendarItem(
|
|||
) -> impl IntoView {
|
||||
let is_selected = Memo::new({
|
||||
let date = date.clone();
|
||||
move |_| value.with(|value_date| value_date == Some(date.deref()))
|
||||
move |_| value.with(|value_date| {
|
||||
match value_date {
|
||||
OptionModelWithValue::T(v) => v == date.deref(),
|
||||
OptionModelWithValue::Option(v) => v.as_ref() == Some(date.deref()),
|
||||
}
|
||||
})
|
||||
});
|
||||
let weekday_str = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
|
||||
let on_click = {
|
||||
|
|
|
@ -3,7 +3,7 @@ use crate::_aria::use_active_descendant;
|
|||
use leptos::{context::Provider, ev, html, prelude::*};
|
||||
use std::collections::HashMap;
|
||||
use thaw_components::{Binder, Follower, FollowerPlacement, FollowerWidth};
|
||||
use thaw_utils::{add_event_listener, class_list, mount_style, Model, VecModel};
|
||||
use thaw_utils::{add_event_listener, class_list, mount_style, Model, VecModel, VecModelWithValue};
|
||||
|
||||
#[component]
|
||||
pub fn Combobox(
|
||||
|
@ -34,10 +34,9 @@ pub fn Combobox(
|
|||
let is_show_clear_icon = Memo::new(move |_| {
|
||||
if clearable {
|
||||
selected_options.with(|options| match options {
|
||||
(None, None, Some(v)) => !v.is_empty(),
|
||||
(None, Some(v), None) => v.is_some(),
|
||||
(Some(v), None, None) => !v.is_empty(),
|
||||
_ => unreachable!(),
|
||||
VecModelWithValue::T(v) => !v.is_empty(),
|
||||
VecModelWithValue::Option(v) => v.is_some(),
|
||||
VecModelWithValue::Vec(v) => !v.is_empty(),
|
||||
})
|
||||
} else {
|
||||
false
|
||||
|
@ -94,16 +93,15 @@ pub fn Combobox(
|
|||
let on_input = move |ev| {
|
||||
let input_value = event_target_value(&ev);
|
||||
if selected_options.with_untracked(|options| match options {
|
||||
(None, None, Some(_)) => false,
|
||||
(None, Some(v), None) => {
|
||||
VecModelWithValue::T(v) => v != &input_value,
|
||||
VecModelWithValue::Option(v) => {
|
||||
if let Some(v) = v.as_ref() {
|
||||
v != &input_value
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
(Some(v), None, None) => v != &input_value,
|
||||
_ => unreachable!(),
|
||||
VecModelWithValue::Vec(_) => false,
|
||||
}) {
|
||||
selected_options.set(vec![]);
|
||||
}
|
||||
|
@ -125,18 +123,17 @@ pub fn Combobox(
|
|||
let active_descendant_controller = active_descendant_controller.clone();
|
||||
move |_| {
|
||||
selected_options.with_untracked(|options| match options {
|
||||
(None, None, Some(_)) => value.set(String::new()),
|
||||
(None, Some(v), None) => {
|
||||
if v.is_none() {
|
||||
value.set(String::new())
|
||||
}
|
||||
}
|
||||
(Some(v), None, None) => {
|
||||
VecModelWithValue::T(v) => {
|
||||
if v.is_empty() {
|
||||
value.set(String::new())
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
VecModelWithValue::Option(v) => {
|
||||
if v.is_none() {
|
||||
value.set(String::new())
|
||||
}
|
||||
}
|
||||
VecModelWithValue::Vec(_) => value.set(String::new()),
|
||||
});
|
||||
active_descendant_controller.blur();
|
||||
}
|
||||
|
@ -297,16 +294,15 @@ impl ComboboxInjection {
|
|||
|
||||
pub fn is_selected(&self, value: &String) -> bool {
|
||||
self.selected_options.with(|options| match options {
|
||||
(None, None, Some(v)) => v.contains(value),
|
||||
(None, Some(v), None) => {
|
||||
VecModelWithValue::T(v) => v == value,
|
||||
VecModelWithValue::Option(v) => {
|
||||
if let Some(v) = v.as_ref() {
|
||||
v == value
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
(Some(v), None, None) => v == value,
|
||||
_ => unreachable!(),
|
||||
VecModelWithValue::Vec(v) => v.contains(value),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ use chrono::NaiveDate;
|
|||
use leptos::{html, prelude::*};
|
||||
use panel::{Panel, PanelRef};
|
||||
use thaw_components::{Binder, Follower, FollowerPlacement};
|
||||
use thaw_utils::{class_list, mount_style, now_date, ComponentRef, OptionModel};
|
||||
use thaw_utils::{class_list, mount_style, now_date, ComponentRef, OptionModel, OptionModelWithValue};
|
||||
|
||||
#[component]
|
||||
pub fn DatePicker(
|
||||
|
@ -20,9 +20,13 @@ pub fn DatePicker(
|
|||
let show_date_format = "%Y-%m-%d";
|
||||
let update_show_date_text = move || {
|
||||
value.with_untracked(move |date| {
|
||||
let text = date.map_or(String::new(), |date| {
|
||||
let text = match date {
|
||||
OptionModelWithValue::T(v) => v.format(show_date_format).to_string(),
|
||||
OptionModelWithValue::Option(v) => v.map_or(String::new(), |date| {
|
||||
date.format(show_date_format).to_string()
|
||||
});
|
||||
}),
|
||||
};
|
||||
|
||||
show_date_text.set(text);
|
||||
});
|
||||
};
|
||||
|
|
|
@ -2,7 +2,7 @@ use super::{FieldContextInjection, FieldInjection, FieldValidationState};
|
|||
use leptos::prelude::*;
|
||||
use send_wrapper::SendWrapper;
|
||||
use std::ops::Deref;
|
||||
use thaw_utils::{Model, OptionModel};
|
||||
use thaw_utils::{Model, OptionModel, OptionModelWithValue};
|
||||
|
||||
type RuleValidator<T> = Box<dyn Fn(&T, Signal<Option<String>>) -> Result<(), FieldValidationState>>;
|
||||
|
||||
|
@ -112,14 +112,20 @@ impl<T: Send + Sync> RuleValueWithUntracked<T> for Model<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Send + Sync + Clone> RuleValueWithUntracked<Option<T>> for OptionModel<T> {
|
||||
impl RuleValueWithUntracked<Option<String>> for OptionModel<String> {
|
||||
fn value_with_untracked(
|
||||
&self,
|
||||
f: impl FnOnce(&Option<T>) -> Result<(), FieldValidationState>,
|
||||
f: impl FnOnce(&Option<String>) -> Result<(), FieldValidationState>,
|
||||
) -> Result<(), FieldValidationState> {
|
||||
self.with_untracked(move |v| {
|
||||
let v = v.map(|v| v.clone());
|
||||
f(&v)
|
||||
self.with_untracked(move |v| match v {
|
||||
OptionModelWithValue::T(v) => {
|
||||
if v.is_empty() {
|
||||
f(&None)
|
||||
} else {
|
||||
f(&Some(v.clone()))
|
||||
}
|
||||
}
|
||||
OptionModelWithValue::Option(v) => f(v),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ use super::NavDrawerInjection;
|
|||
use crate::Icon;
|
||||
use leptos::{either::Either, html, prelude::*};
|
||||
use thaw_components::CSSTransition;
|
||||
use thaw_utils::{class_list, StoredMaybeSignal};
|
||||
use thaw_utils::{class_list, StoredMaybeSignal, VecModelWithValue};
|
||||
|
||||
#[component]
|
||||
pub fn NavCategory(
|
||||
|
@ -18,10 +18,9 @@ pub fn NavCategory(
|
|||
.selected_category_value
|
||||
.with(|selected_category_value| {
|
||||
value.with(|value| match selected_category_value {
|
||||
(None, None, Some(v)) => v.contains(value),
|
||||
(None, Some(v), None) => v.as_ref() == Some(value),
|
||||
(Some(v), None, None) => v == value,
|
||||
_ => unreachable!(),
|
||||
VecModelWithValue::T(v) => v == value,
|
||||
VecModelWithValue::Option(v) => v.as_ref() == Some(value),
|
||||
VecModelWithValue::Vec(v) => v.contains(value),
|
||||
})
|
||||
})
|
||||
});
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use super::NavDrawerInjection;
|
||||
use crate::Icon;
|
||||
use leptos::{either::Either, prelude::*};
|
||||
use thaw_utils::{class_list, StoredMaybeSignal};
|
||||
use thaw_utils::{class_list, OptionModelWithValue, StoredMaybeSignal};
|
||||
|
||||
#[component]
|
||||
pub fn NavItem(
|
||||
|
@ -17,7 +17,12 @@ pub fn NavItem(
|
|||
let value = value.get_untracked();
|
||||
if nav_drawer
|
||||
.selected_value
|
||||
.with_untracked(|selected_value| selected_value != Some(&value))
|
||||
.with_untracked(|selected_value|
|
||||
match selected_value {
|
||||
OptionModelWithValue::T(v) => v != &value,
|
||||
OptionModelWithValue::Option(v) => v.as_ref() != Some(&value),
|
||||
}
|
||||
)
|
||||
{
|
||||
nav_drawer.selected_value.set(Some(value));
|
||||
}
|
||||
|
@ -39,7 +44,10 @@ pub fn NavItem(
|
|||
let selected = Memo::new(move |_| {
|
||||
nav_drawer
|
||||
.selected_value
|
||||
.with(|selected_value| value.with(|value| selected_value == Some(value)))
|
||||
.with(|selected_value| value.with(|value| match selected_value {
|
||||
OptionModelWithValue::T(v) => v == value,
|
||||
OptionModelWithValue::Option(v) => v.as_ref() == Some(&value),
|
||||
}))
|
||||
});
|
||||
|
||||
if let Some(href) = href {
|
||||
|
|
|
@ -4,7 +4,7 @@ pub use radio_group::{RadioGroup, RadioGroupRule, RadioGroupRuleTrigger};
|
|||
|
||||
use leptos::prelude::*;
|
||||
use radio_group::RadioGroupInjection;
|
||||
use thaw_utils::{class_list, mount_style};
|
||||
use thaw_utils::{class_list, mount_style, OptionModelWithValue};
|
||||
|
||||
#[component]
|
||||
pub fn Radio(
|
||||
|
@ -26,7 +26,10 @@ pub fn Radio(
|
|||
let group = group.clone();
|
||||
move |_| {
|
||||
item_value
|
||||
.with_value(|value| group.value.with(|group_value| group_value == Some(value)))
|
||||
.with_value(|value| group.value.with(|group_value| match group_value {
|
||||
OptionModelWithValue::T(v) => v == value,
|
||||
OptionModelWithValue::Option(v) => v.as_ref() == Some(value),
|
||||
}))
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ use crate::{
|
|||
use chrono::{Local, NaiveTime, Timelike};
|
||||
use leptos::{html, prelude::*};
|
||||
use thaw_components::{Binder, CSSTransition, Follower, FollowerPlacement};
|
||||
use thaw_utils::{class_list, mount_style, ArcOneCallback, ComponentRef, OptionModel};
|
||||
use thaw_utils::{class_list, mount_style, ArcOneCallback, ComponentRef, OptionModel, OptionModelWithValue};
|
||||
|
||||
#[component]
|
||||
pub fn TimePicker(
|
||||
|
@ -23,9 +23,13 @@ pub fn TimePicker(
|
|||
let show_time_text = RwSignal::new(String::new());
|
||||
let update_show_time_text = move || {
|
||||
value.with_untracked(move |time| {
|
||||
let text = time.as_ref().map_or(String::new(), |time| {
|
||||
let text = match time {
|
||||
OptionModelWithValue::T(v) => v.format(show_time_format).to_string(),
|
||||
OptionModelWithValue::Option(v) => v.map_or(String::new(), |time| {
|
||||
time.format(show_time_format).to_string()
|
||||
});
|
||||
}),
|
||||
};
|
||||
|
||||
show_time_text.set(text);
|
||||
});
|
||||
};
|
||||
|
|
|
@ -4,6 +4,6 @@ mod signal_watch;
|
|||
mod stored_maybe_signal;
|
||||
|
||||
pub use component_ref::ComponentRef;
|
||||
pub use model::{Model, OptionModel, VecModel};
|
||||
pub use model::{Model, OptionModel, VecModel, VecModelWithValue, OptionModelWithValue};
|
||||
pub use signal_watch::SignalWatch;
|
||||
pub use stored_maybe_signal::StoredMaybeSignal;
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
mod option_model;
|
||||
mod vec_model;
|
||||
|
||||
pub use option_model::OptionModel;
|
||||
pub use vec_model::VecModel;
|
||||
pub use option_model::{OptionModel, OptionModelWithValue};
|
||||
pub use vec_model::{VecModel, VecModelWithValue};
|
||||
|
||||
use leptos::reactive_graph::{
|
||||
computed::Memo,
|
||||
|
|
|
@ -48,21 +48,26 @@ impl<T: Send + Sync> OptionModel<T> {
|
|||
rw_signal.into()
|
||||
}
|
||||
|
||||
pub fn with<O>(&self, fun: impl FnOnce(Option<&T>) -> O) -> O {
|
||||
pub fn with<O>(&self, fun: impl FnOnce(OptionModelWithValue<T>) -> O) -> O {
|
||||
match self {
|
||||
Self::T(read, _, _) => read.with(|value| fun(Some(value))),
|
||||
Self::Option(read, _, _) => read.with(|value| fun(value.as_ref())),
|
||||
Self::T(read, _, _) => read.with(|value| fun(OptionModelWithValue::T(value))),
|
||||
Self::Option(read, _, _) => read.with(|value| fun(OptionModelWithValue::Option(value))),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_untracked<O>(&self, fun: impl FnOnce(Option<&T>) -> O) -> O {
|
||||
pub fn with_untracked<O>(&self, fun: impl FnOnce(OptionModelWithValue<T>) -> O) -> O {
|
||||
match self {
|
||||
Self::T(read, _, _) => read.with_untracked(|value| fun(Some(value))),
|
||||
Self::Option(read, _, _) => read.with_untracked(|value| fun(value.as_ref())),
|
||||
Self::T(read, _, _) => read.with_untracked(|value| fun(OptionModelWithValue::T(value))),
|
||||
Self::Option(read, _, _) => read.with_untracked(|value| fun(OptionModelWithValue::Option(value))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum OptionModelWithValue<'a, T> {
|
||||
T(&'a T),
|
||||
Option(&'a Option<T>),
|
||||
}
|
||||
|
||||
impl<T: Send + Sync + Clone> OptionModel<T> {
|
||||
pub fn get(&self) -> Option<T> {
|
||||
match self {
|
||||
|
|
|
@ -69,25 +69,32 @@ impl<T: Send + Sync> VecModel<T> {
|
|||
|
||||
pub fn with<O>(
|
||||
&self,
|
||||
fun: impl FnOnce((Option<&T>, Option<&Option<T>>, Option<&Vec<T>>)) -> O,
|
||||
fun: impl FnOnce(VecModelWithValue<T>) -> O,
|
||||
) -> O {
|
||||
match self {
|
||||
Self::T(read, _, _) => read.with(|value| fun((Some(value), None, None))),
|
||||
Self::Option(read, _, _) => read.with(|value| fun((None, Some(value), None))),
|
||||
Self::Vec(read, _, _) => read.with(|value| fun((None, None, Some(value)))),
|
||||
Self::T(read, _, _) => read.with(|value| fun(VecModelWithValue::T(value))),
|
||||
Self::Option(read, _, _) => read.with(|value| fun(VecModelWithValue::Option(value))),
|
||||
Self::Vec(read, _, _) => read.with(|value| fun(VecModelWithValue::Vec(value))),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_untracked<O>(
|
||||
&self,
|
||||
fun: impl FnOnce((Option<&T>, Option<&Option<T>>, Option<&Vec<T>>)) -> O,
|
||||
) -> O {
|
||||
pub fn with_untracked<O>(&self, fun: impl FnOnce(VecModelWithValue<T>) -> O) -> O {
|
||||
match self {
|
||||
Self::T(read, _, _) => read.with_untracked(|value| fun((Some(value), None, None))),
|
||||
Self::Option(read, _, _) => read.with_untracked(|value| fun((None, Some(value), None))),
|
||||
Self::Vec(read, _, _) => read.with_untracked(|value| fun((None, None, Some(value)))),
|
||||
Self::T(read, _, _) => read.with_untracked(|value| fun(VecModelWithValue::T(value))),
|
||||
Self::Option(read, _, _) => {
|
||||
read.with_untracked(|value| fun(VecModelWithValue::Option(value)))
|
||||
}
|
||||
Self::Vec(read, _, _) => {
|
||||
read.with_untracked(|value| fun(VecModelWithValue::Vec(value)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum VecModelWithValue<'a, T> {
|
||||
T(&'a T),
|
||||
Option(&'a Option<T>),
|
||||
Vec(&'a Vec<T>),
|
||||
}
|
||||
|
||||
impl<T: Send + Sync + Clone + Default> VecModel<T> {
|
||||
|
|
Loading…
Add table
Reference in a new issue