refactor: checkbox

This commit is contained in:
luoxiao 2024-05-23 16:05:06 +08:00
parent ae4a69fd15
commit 4aee77908f
5 changed files with 57 additions and 61 deletions

View file

@ -4,7 +4,7 @@
let checked = RwSignal::new(false); let checked = RwSignal::new(false);
view! { view! {
<Checkbox checked>"Click"</Checkbox> <Checkbox checked label="Click"/>
<Checkbox /> <Checkbox />
} }
``` ```
@ -18,9 +18,9 @@ let value = RwSignal::new(HashSet::new());
view! { view! {
<CheckboxGroup value> <CheckboxGroup value>
<CheckboxItem label="apple" value="a"/> <Checkbox label="apple" value="a"/>
<CheckboxItem label="b" value="b"/> <Checkbox label="b" value="b"/>
<CheckboxItem label="c" value="c"/> <Checkbox label="c" value="c"/>
</CheckboxGroup> </CheckboxGroup>
<div style="margin-top: 1rem">"value: " {move || format!("{:?}", value.get())}</div> <div style="margin-top: 1rem">"value: " {move || format!("{:?}", value.get())}</div>
} }

View file

@ -19,8 +19,10 @@ pub fn CheckboxGroup(
#[derive(Clone)] #[derive(Clone)]
pub(crate) struct CheckboxGroupInjection(pub Model<HashSet<String>>); pub(crate) struct CheckboxGroupInjection(pub Model<HashSet<String>>);
impl Copy for CheckboxGroupInjection {}
impl CheckboxGroupInjection { impl CheckboxGroupInjection {
pub fn use_() -> Self { pub fn use_() -> Option<Self> {
expect_context() use_context()
} }
} }

View file

@ -1,44 +0,0 @@
use super::{checkbox_group::CheckboxGroupInjection, Checkbox};
use leptos::*;
use thaw_utils::OptionalProp;
#[component]
pub fn CheckboxItem(
#[prop(optional, into)] label: Option<String>,
#[prop(optional, into)] class: OptionalProp<MaybeSignal<String>>,
#[prop(into)] value: String,
) -> impl IntoView {
let group_value = CheckboxGroupInjection::use_().0;
let checked = RwSignal::new(false);
let item_value = StoredValue::new(value);
let is_checked = Memo::new(move |_| {
group_value.with(|group_value| item_value.with_value(|value| group_value.contains(value)))
});
Effect::new(move |_| {
checked.track();
if is_checked.get_untracked() {
group_value.update(move |group_value| {
item_value.with_value(|value| {
group_value.remove(value);
});
});
} else if checked.get_untracked() {
group_value.update(move |group_value| {
group_value.insert(item_value.get_value());
});
}
});
if let Some(label) = label {
view! {
<Checkbox class checked=(is_checked, checked.write_only())>
{label}
</Checkbox>
}
} else {
view! { <Checkbox class checked=(is_checked, checked.write_only())/> }
}
}

View file

@ -1,33 +1,64 @@
mod checkbox_group; mod checkbox_group;
mod checkbox_item;
pub use checkbox_group::CheckboxGroup; pub use checkbox_group::CheckboxGroup;
pub use checkbox_item::CheckboxItem;
use checkbox_group::CheckboxGroupInjection;
use leptos::*; use leptos::*;
use thaw_components::*;
use thaw_utils::{class_list, mount_style, Model, OptionalProp}; use thaw_utils::{class_list, mount_style, Model, OptionalProp};
#[component] #[component]
pub fn Checkbox( pub fn Checkbox(
#[prop(optional, into)] checked: Model<bool>, #[prop(optional, into)] checked: Model<bool>,
#[prop(optional, into)] class: OptionalProp<MaybeSignal<String>>, #[prop(optional, into)] class: OptionalProp<MaybeSignal<String>>,
#[prop(optional)] children: Option<Children>, #[prop(optional, into)] value: Option<String>,
#[prop(optional, into)] label: MaybeProp<String>,
) -> impl IntoView { ) -> impl IntoView {
mount_style("checkbox", include_str!("./checkbox.css")); mount_style("checkbox", include_str!("./checkbox.css"));
let id = uuid::Uuid::new_v4().to_string(); let id = uuid::Uuid::new_v4().to_string();
let input_ref = NodeRef::<html::Input>::new(); let input_ref = NodeRef::<html::Input>::new();
let group = CheckboxGroupInjection::use_();
let item_value = StoredValue::new(value);
let group_checked = Memo::new(move |_| {
let Some(group) = group.as_ref() else {
return None;
};
group.0.with(|group_value| {
item_value.with_value(|value| {
let Some(value) = value else {
return None;
};
Some(group_value.contains(value))
})
})
});
let on_change = move |_| { let on_change = move |_| {
let input = input_ref.get_untracked().unwrap(); let input = input_ref.get_untracked().unwrap();
checked.set(input.checked()) if group_checked.get_untracked().is_some() {
if input.checked() {
group.as_ref().unwrap().0.update(move |group_value| {
group_value.insert(item_value.get_value().unwrap());
});
} else {
group.as_ref().unwrap().0.update(move |group_value| {
item_value.with_value(|value| {
group_value.remove(value.as_ref().unwrap());
});
});
}
} else {
checked.set(input.checked())
}
}; };
let checked = move || group_checked.get().unwrap_or_else(|| checked.get());
view! { view! {
<span <span
class=class_list![ class=class_list![
"thaw-checkbox", ("thaw-checkbox--checked", move || checked.get()), class.map(| c | "thaw-checkbox", ("thaw-checkbox--checked", checked), class.map(| c |
move || c.get()) move || c.get())
] ]
> >
@ -35,13 +66,13 @@ pub fn Checkbox(
class="thaw-checkbox__input" class="thaw-checkbox__input"
type="checkbox" type="checkbox"
id=id.clone() id=id.clone()
checked=checked.get_untracked() checked=checked
ref=input_ref ref=input_ref
on:change=on_change on:change=on_change
/> />
<div aria-hidden="true" class="thaw-checkbox__indicator"> <div aria-hidden="true" class="thaw-checkbox__indicator">
{ {
move || if checked.get() { move || if checked() {
view! { view! {
<svg fill="currentColor" aria-hidden="true" width="12" height="12" viewBox="0 0 12 12" style="display: inline;line-height: 0"> <svg fill="currentColor" aria-hidden="true" width="12" height="12" viewBox="0 0 12 12" style="display: inline;line-height: 0">
<path d="M9.76 3.2c.3.29.32.76.04 1.06l-4.25 4.5a.75.75 0 0 1-1.08.02L2.22 6.53a.75.75 0 0 1 1.06-1.06l1.7 1.7L8.7 3.24a.75.75 0 0 1 1.06-.04Z" fill="currentColor"></path> <path d="M9.76 3.2c.3.29.32.76.04 1.06l-4.25 4.5a.75.75 0 0 1-1.08.02L2.22 6.53a.75.75 0 0 1 1.06-1.06l1.7 1.7L8.7 3.24a.75.75 0 0 1 1.06-.04Z" fill="currentColor"></path>
@ -52,9 +83,15 @@ pub fn Checkbox(
} }
} }
</div> </div>
<OptionComp value=children let:children> {
<label class="thaw-checkbox__label" for=id>{children()}</label> move || if let Some(label) = label.get() {
</OptionComp> view! {
<label class="thaw-checkbox__label" for=id.clone()>{label}</label>
}.into()
} else {
None
}
}
</span> </span>
} }
} }

View file

@ -43,6 +43,7 @@ pub fn Radio(
class="thaw-radio__input" class="thaw-radio__input"
type="radio" type="radio"
id=id.clone() id=id.clone()
value=item_value.get_value()
prop:checked=move || checked.get() prop:checked=move || checked.get()
on:change=on_change on:change=on_change
/> />