mirror of
https://github.com/adoyle0/thaw.git
synced 2025-01-22 22:09:22 -05:00
feat: ComboboxOption adds disabled prop
This commit is contained in:
parent
25d0b81c07
commit
1c243922bd
4 changed files with 97 additions and 23 deletions
|
@ -78,6 +78,11 @@
|
|||
outline-style: none;
|
||||
}
|
||||
|
||||
.thaw-combobox__input::placeholder {
|
||||
color: var(--colorNeutralForeground4);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.thaw-combobox__clear-icon,
|
||||
.thaw-combobox__expand-icon {
|
||||
display: block;
|
||||
|
@ -88,6 +93,28 @@
|
|||
font-size: 20px;
|
||||
}
|
||||
|
||||
.thaw-combobox.thaw-combobox--disabled {
|
||||
border-color: var(--colorNeutralStrokeDisabled);
|
||||
border-bottom-color: var(--colorNeutralStrokeDisabled);
|
||||
background-color: var(--colorTransparentBackground);
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.thaw-combobox--disabled > .thaw-combobox__input {
|
||||
background-color: var(--colorTransparentBackground);
|
||||
color: var(--colorNeutralForegroundDisabled);
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.thaw-combobox--disabled > .thaw-combobox__input::placeholder {
|
||||
color: var(--colorNeutralForegroundDisabled);
|
||||
}
|
||||
|
||||
.thaw-combobox--disabled > .thaw-combobox__clear-icon,
|
||||
.thaw-combobox--disabled > .thaw-combobox__expand-icon {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.thaw-combobox__listbox {
|
||||
row-gap: var(--spacingHorizontalXXS);
|
||||
display: flex;
|
||||
|
@ -181,3 +208,13 @@
|
|||
color: var(--colorNeutralForegroundInverted);
|
||||
background-color: var(--colorCompoundBrandBackground);
|
||||
}
|
||||
|
||||
.thaw-combobox-option--disabled {
|
||||
color: var(--colorNeutralForegroundDisabled);
|
||||
}
|
||||
|
||||
.thaw-combobox-option--disabled:active,
|
||||
.thaw-combobox-option--disabled:hover {
|
||||
background-color: var(--colorTransparentBackground);
|
||||
color: var(--colorNeutralForegroundDisabled);
|
||||
}
|
||||
|
|
|
@ -10,8 +10,11 @@ pub fn Combobox(
|
|||
#[prop(optional, into)] class: MaybeProp<String>,
|
||||
#[prop(optional, into)] value: Model<String>,
|
||||
#[prop(optional, into)] selected_options: VecModel<String>,
|
||||
#[prop(optional, into)] disabled: MaybeSignal<bool>,
|
||||
#[prop(optional, into)] placeholder: MaybeProp<String>,
|
||||
/// If set, the combobox will show an icon to clear the current value.
|
||||
#[prop(optional)] clearable: bool,
|
||||
#[prop(optional)]
|
||||
clearable: bool,
|
||||
children: Children,
|
||||
) -> impl IntoView {
|
||||
mount_style("combobox", include_str!("./combobox.css"));
|
||||
|
@ -19,7 +22,7 @@ pub fn Combobox(
|
|||
let input_ref = NodeRef::<html::Input>::new();
|
||||
let listbox_ref = NodeRef::<html::Div>::new();
|
||||
let is_show_listbox = RwSignal::new(false);
|
||||
let options = StoredValue::new(HashMap::<String, (String, String)>::new());
|
||||
let options = StoredValue::new(HashMap::<String, (String, String, MaybeSignal<bool>)>::new());
|
||||
|
||||
let clear_icon_ref = NodeRef::<html::Span>::new();
|
||||
let is_show_clear_icon = Memo::new(move |_| {
|
||||
|
@ -40,6 +43,9 @@ pub fn Combobox(
|
|||
return;
|
||||
};
|
||||
let handler = add_event_listener(clear_icon_el.into(), ev::click, move |e| {
|
||||
if disabled.get_untracked() {
|
||||
return;
|
||||
}
|
||||
e.stop_propagation();
|
||||
selected_options.set(vec![]);
|
||||
});
|
||||
|
@ -138,7 +144,10 @@ pub fn Combobox(
|
|||
&active_descendant_controller,
|
||||
move |option| {
|
||||
combobox_injection.options.with_value(|options| {
|
||||
if let Some((value, text)) = options.get(&option.id()) {
|
||||
if let Some((value, text, disabled)) = options.get(&option.id()) {
|
||||
if disabled.get_untracked() {
|
||||
return;
|
||||
}
|
||||
combobox_injection.select_option(value, text);
|
||||
}
|
||||
});
|
||||
|
@ -149,7 +158,11 @@ pub fn Combobox(
|
|||
view! {
|
||||
<Binder target_ref=trigger_ref>
|
||||
<div
|
||||
class=class_list!["thaw-combobox", class]
|
||||
class=class_list![
|
||||
"thaw-combobox",
|
||||
("thaw-combobox--disabled", move || disabled.get()),
|
||||
class
|
||||
]
|
||||
node_ref=trigger_ref
|
||||
>
|
||||
<input
|
||||
|
@ -160,6 +173,8 @@ pub fn Combobox(
|
|||
prop:value=move || {
|
||||
value.get()
|
||||
}
|
||||
placeholder=move || placeholder.get()
|
||||
disabled=move || disabled.get()
|
||||
node_ref=input_ref
|
||||
on:input=on_input
|
||||
on:blur=on_blur
|
||||
|
@ -187,12 +202,16 @@ pub fn Combobox(
|
|||
}
|
||||
}
|
||||
<span
|
||||
aria-expanded="true"
|
||||
aria-disabled=move || if disabled.get() { "true" } else { "" }
|
||||
aria-expanded=move || is_show_listbox.get().to_string()
|
||||
role="button"
|
||||
aria-label="Open"
|
||||
class="thaw-combobox__expand-icon"
|
||||
style=move || is_show_clear_icon.get().then(|| "display: none").unwrap_or_default()
|
||||
on:click=move |_| {
|
||||
if disabled.get_untracked() {
|
||||
return;
|
||||
}
|
||||
is_show_listbox.update(|show| *show = !*show);
|
||||
if let Some(el) = input_ref.get_untracked() {
|
||||
let _ = el.focus();
|
||||
|
@ -225,7 +244,7 @@ pub fn Combobox(
|
|||
pub(crate) struct ComboboxInjection {
|
||||
value: Model<String>,
|
||||
selected_options: VecModel<String>,
|
||||
options: StoredValue<HashMap<String, (String, String)>>,
|
||||
options: StoredValue<HashMap<String, (String, String, MaybeSignal<bool>)>>,
|
||||
is_show_listbox: RwSignal<bool>,
|
||||
pub multiselect: bool,
|
||||
}
|
||||
|
@ -235,7 +254,7 @@ impl ComboboxInjection {
|
|||
expect_context()
|
||||
}
|
||||
|
||||
pub fn insert_option(&self, id: String, value: (String, String)) {
|
||||
pub fn insert_option(&self, id: String, value: (String, String, MaybeSignal<bool>)) {
|
||||
self.options
|
||||
.update_value(|options| options.insert(id, value));
|
||||
}
|
||||
|
|
|
@ -7,10 +7,16 @@ use thaw_utils::class_list;
|
|||
#[component]
|
||||
pub fn ComboboxOption(
|
||||
#[prop(optional, into)] class: MaybeProp<String>,
|
||||
/// Sets an option to the disabled state. Disabled options cannot be selected,
|
||||
/// but are still keyboard navigable.
|
||||
#[prop(optional, into)]
|
||||
disabled: MaybeSignal<bool>,
|
||||
/// Defines a unique identifier for the option. Defaults to `text` if not provided.
|
||||
#[prop(optional, into)] value: Option<String>,
|
||||
#[prop(optional, into)]
|
||||
value: Option<String>,
|
||||
/// An optional override the string value of the Option's display text, defaulting to the Option's child content.
|
||||
#[prop(into)] text: String,
|
||||
#[prop(into)]
|
||||
text: String,
|
||||
#[prop(optional)] children: Option<Children>,
|
||||
) -> impl IntoView {
|
||||
let combobox = ComboboxInjection::expect_context();
|
||||
|
@ -21,6 +27,9 @@ pub fn ComboboxOption(
|
|||
let id = uuid::Uuid::new_v4().to_string();
|
||||
|
||||
let on_click = move |_| {
|
||||
if disabled.get_untracked() {
|
||||
return;
|
||||
}
|
||||
text.with_value(|text| {
|
||||
value.with_value(|value| {
|
||||
combobox.select_option(value, text);
|
||||
|
@ -29,7 +38,7 @@ pub fn ComboboxOption(
|
|||
};
|
||||
|
||||
{
|
||||
combobox.insert_option(id.clone(), (value.get_value(), text.get_value()));
|
||||
combobox.insert_option(id.clone(), (value.get_value(), text.get_value(), disabled));
|
||||
let id = id.clone();
|
||||
listbox.trigger();
|
||||
on_cleanup(move || {
|
||||
|
@ -41,11 +50,13 @@ pub fn ComboboxOption(
|
|||
view! {
|
||||
<div
|
||||
role="option"
|
||||
aria-selected=move || if is_selected.get() { "true" } else { "false" }
|
||||
aria-disabled=move || if disabled.get() { "true" } else { "" }
|
||||
aria-selected=move || is_selected.get().to_string()
|
||||
id=id
|
||||
class=class_list![
|
||||
"thaw-combobox-option",
|
||||
("thaw-combobox-option--selected", move || is_selected.get()),
|
||||
("thaw-combobox-option--disabled", move || disabled.get()),
|
||||
class
|
||||
]
|
||||
on:click=on_click
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
let selected_options = RwSignal::new(None::<String>);
|
||||
|
||||
view! {
|
||||
<Combobox selected_options>
|
||||
<ComboboxOption value="cat" text="Car" />
|
||||
<Combobox selected_options placeholder="Select an animal">
|
||||
<ComboboxOption value="cat" text="Cat" disabled=true/>
|
||||
<ComboboxOption value="dog" text="Dog" />
|
||||
</Combobox>
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ let selected_options = RwSignal::new(vec![]);
|
|||
|
||||
view! {
|
||||
<Combobox selected_options clearable=true>
|
||||
<ComboboxOption value="cat" text="Car" />
|
||||
<ComboboxOption value="cat" text="Cat" disabled=true/>
|
||||
<ComboboxOption value="dog" text="Dog" />
|
||||
</Combobox>
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ let selected_options = RwSignal::new(vec![]);
|
|||
|
||||
view! {
|
||||
<Combobox selected_options>
|
||||
<ComboboxOption value="cat" text="Car" />
|
||||
<ComboboxOption value="cat" text="Cat" disabled=true/>
|
||||
<ComboboxOption value="dog" text="Dog" />
|
||||
</Combobox>
|
||||
}
|
||||
|
@ -49,21 +49,28 @@ view! {
|
|||
<ComboboxOptionGroup label="Land">
|
||||
{
|
||||
land.into_iter().map(|option| view!{
|
||||
<ComboboxOption value={option.clone()}>
|
||||
{option}
|
||||
</ComboboxOption>
|
||||
<ComboboxOption text={option} />
|
||||
}).collect_view()
|
||||
}
|
||||
</ComboboxOptionGroup>
|
||||
<OptionGroup label="Sea">
|
||||
<ComboboxOptionGroup label="Sea">
|
||||
{
|
||||
water.into_iter().map(|option| view!{
|
||||
<ComboboxOption value={option.clone()}>
|
||||
{option}
|
||||
</ComboboxOption>
|
||||
<ComboboxOption text={option} />
|
||||
}).collect_view()
|
||||
}
|
||||
</OptionGroup>
|
||||
</ComboboxOptionGroup>
|
||||
</Combobox>
|
||||
}
|
||||
```
|
||||
|
||||
### Disabled
|
||||
|
||||
```rust demo
|
||||
view! {
|
||||
<Combobox disabled=true>
|
||||
<ComboboxOption value="cat" text="Car" />
|
||||
<ComboboxOption value="dog" text="Dog" />
|
||||
</Combobox>
|
||||
}
|
||||
```
|
Loading…
Add table
Reference in a new issue