Skip to content

Commit a0b2dd5

Browse files
committed
feat: add disabled items support in multiselect
Sometimes it's nice to show a user a list of options, some of which the user cannot interact with. e.g. A list of linux users on this machine to import to a new machine, I would like to show root but without allowing the user to deselect it. Current implementation limitations: - No theme support - there's no indication that an item is disabled until the user attempts to interact with it. Would adding such visual indication be a good idea from a compatibility perspective? Are themes all built-in or possibly user defined? - Cannot dynamically add items with a different disabled state. This would require modifying the signature of e.g. `item_checked` which would be a breaking change. I could also add a new method but I wasn't sure if it's worth it. - No tests - No documentation Do you think this is a desired feature? If so, I can work on improving the limitations. Should this maybe be a separate widget altogether?
1 parent 6244c77 commit a0b2dd5

File tree

1 file changed

+35
-10
lines changed

1 file changed

+35
-10
lines changed

src/prompts/multi_select.rs

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ use crate::{
3333
#[derive(Clone)]
3434
pub struct MultiSelect<'a> {
3535
defaults: Vec<bool>,
36+
disabled: Vec<bool>,
3637
items: Vec<String>,
3738
prompt: Option<String>,
3839
report: bool,
@@ -63,15 +64,24 @@ impl MultiSelect<'_> {
6364
self
6465
}
6566

66-
/// Sets a defaults for the menu.
67-
pub fn defaults(mut self, val: &[bool]) -> Self {
68-
self.defaults = val
69-
.to_vec()
67+
fn get_bool_array(&self, val: &[bool]) -> Vec<bool> {
68+
val.to_vec()
7069
.iter()
7170
.copied()
7271
.chain(repeat(false))
7372
.take(self.items.len())
74-
.collect();
73+
.collect()
74+
}
75+
76+
/// Sets a defaults for the menu.
77+
pub fn defaults(mut self, val: &[bool]) -> Self {
78+
self.defaults = self.get_bool_array(val);
79+
self
80+
}
81+
82+
/// Sets disabled items for the menu.
83+
pub fn disabled(mut self, val: &[bool]) -> Self {
84+
self.disabled = self.get_bool_array(val);
7585
self
7686
}
7787

@@ -97,6 +107,8 @@ impl MultiSelect<'_> {
97107
pub fn item_checked<T: ToString>(mut self, item: T, checked: bool) -> Self {
98108
self.items.push(item.to_string());
99109
self.defaults.push(checked);
110+
// TODO: Add support for adding disabled items
111+
self.disabled.push(false);
100112
self
101113
}
102114

@@ -118,6 +130,8 @@ impl MultiSelect<'_> {
118130
for (item, checked) in items.into_iter() {
119131
self.items.push(item.to_string());
120132
self.defaults.push(checked);
133+
// TODO: Add support for adding disabled items
134+
self.disabled.push(false);
121135
}
122136
self
123137
}
@@ -229,6 +243,7 @@ impl MultiSelect<'_> {
229243
}
230244

231245
let mut checked: Vec<bool> = self.defaults.clone();
246+
let disabled: Vec<bool> = self.disabled.clone();
232247

233248
term.hide_cursor()?;
234249

@@ -277,13 +292,22 @@ impl MultiSelect<'_> {
277292
}
278293
}
279294
Key::Char(' ') => {
280-
checked[sel] = !checked[sel];
295+
if !disabled[sel] {
296+
checked[sel] = !checked[sel];
297+
}
281298
}
282299
Key::Char('a') => {
283-
if checked.iter().all(|&item_checked| item_checked) {
284-
checked.fill(false);
285-
} else {
286-
checked.fill(true);
300+
let all_checked = checked.iter().all(|&item_checked| item_checked);
301+
302+
// If all already checked, uncheck all
303+
let new_checked = !all_checked;
304+
305+
for (idx, (_checked_value, disabled_value)) in
306+
checked.clone().iter().zip(disabled.iter()).enumerate()
307+
{
308+
if !disabled_value {
309+
checked[idx] = new_checked;
310+
}
287311
}
288312
}
289313
Key::Escape | Key::Char('q') => {
@@ -367,6 +391,7 @@ impl<'a> MultiSelect<'a> {
367391
Self {
368392
items: vec![],
369393
defaults: vec![],
394+
disabled: vec![],
370395
clear: true,
371396
prompt: None,
372397
report: true,

0 commit comments

Comments
 (0)