diff --git a/thaw/src/combobox/combobox.css b/thaw/src/combobox/combobox.css index 1864f56..fbea8bf 100644 --- a/thaw/src/combobox/combobox.css +++ b/thaw/src/combobox/combobox.css @@ -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); +} diff --git a/thaw/src/combobox/combobox.rs b/thaw/src/combobox/combobox.rs index 6dd9811..fe2264a 100644 --- a/thaw/src/combobox/combobox.rs +++ b/thaw/src/combobox/combobox.rs @@ -10,8 +10,11 @@ pub fn Combobox( #[prop(optional, into)] class: MaybeProp, #[prop(optional, into)] value: Model, #[prop(optional, into)] selected_options: VecModel, + #[prop(optional, into)] disabled: MaybeSignal, + #[prop(optional, into)] placeholder: MaybeProp, /// 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::::new(); let listbox_ref = NodeRef::::new(); let is_show_listbox = RwSignal::new(false); - let options = StoredValue::new(HashMap::::new()); + let options = StoredValue::new(HashMap::)>::new()); let clear_icon_ref = NodeRef::::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! {
, selected_options: VecModel, - options: StoredValue>, + options: StoredValue)>>, is_show_listbox: RwSignal, 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)) { self.options .update_value(|options| options.insert(id, value)); } diff --git a/thaw/src/combobox/combobox_option.rs b/thaw/src/combobox/combobox_option.rs index 923ed04..2c19016 100644 --- a/thaw/src/combobox/combobox_option.rs +++ b/thaw/src/combobox/combobox_option.rs @@ -7,10 +7,16 @@ use thaw_utils::class_list; #[component] pub fn ComboboxOption( #[prop(optional, into)] class: MaybeProp, + /// Sets an option to the disabled state. Disabled options cannot be selected, + /// but are still keyboard navigable. + #[prop(optional, into)] + disabled: MaybeSignal, /// Defines a unique identifier for the option. Defaults to `text` if not provided. - #[prop(optional, into)] value: Option, + #[prop(optional, into)] + value: Option, /// 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, ) -> 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! {
); view! { - - + + } @@ -18,7 +18,7 @@ let selected_options = RwSignal::new(vec![]); view! { - + } @@ -31,7 +31,7 @@ let selected_options = RwSignal::new(vec![]); view! { - + } @@ -49,21 +49,28 @@ view! { { land.into_iter().map(|option| view!{ - - {option} - + }).collect_view() } - + { water.into_iter().map(|option| view!{ - - {option} - + }).collect_view() } - + } ``` + +### Disabled + +```rust demo +view! { + + + + +} +``` \ No newline at end of file