Skip to content

Commit

Permalink
Allow selecting all options in a multiselect (#147)
Browse files Browse the repository at this point in the history
* Allow selecting all options in `multiselect`

* Add test

* Update type hint

* Make it possible to pass custom label to select all option

* Back out of this approach

* Multiselect: Ctrl+A should select all options

* Multisearch: Ctrl+A should select all options

Although... Ctrl+A should only select all options when the user is in the context of the options, not when they're in the context of searching.

* Add back Home & End keybindings

* This change wasn't needed.

* Formatting

* Check if current matches are selected and only filter out current matches

* Fix "unselect all" behaviour when options are a list

Co-authored-by: Jess Archer <[email protected]>

---------

Co-authored-by: Jess Archer <[email protected]>
  • Loading branch information
duncanmcclean and jessarcher authored May 27, 2024
1 parent 5fac84e commit 9bc4df7
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 4 deletions.
27 changes: 25 additions & 2 deletions src/MultiSearchPrompt.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,11 @@ public function __construct(
$this->on('key', fn ($key) => match ($key) {
Key::UP, Key::UP_ARROW, Key::SHIFT_TAB => $this->highlightPrevious(count($this->matches), true),
Key::DOWN, Key::DOWN_ARROW, Key::TAB => $this->highlightNext(count($this->matches), true),
Key::oneOf([Key::HOME, Key::CTRL_A], $key) => $this->highlighted !== null ? $this->highlight(0) : null,
Key::oneOf([Key::END, Key::CTRL_E], $key) => $this->highlighted !== null ? $this->highlight(count($this->matches()) - 1) : null,
Key::oneOf(Key::HOME, $key) => $this->highlighted !== null ? $this->highlight(0) : null,
Key::oneOf(Key::END, $key) => $this->highlighted !== null ? $this->highlight(count($this->matches()) - 1) : null,
Key::SPACE => $this->highlighted !== null ? $this->toggleHighlighted() : null,
Key::CTRL_A => $this->highlighted !== null ? $this->toggleAll() : null,
Key::CTRL_E => null,
Key::ENTER => $this->submit(),
Key::LEFT, Key::LEFT_ARROW, Key::RIGHT, Key::RIGHT_ARROW => $this->highlighted = null,
default => $this->search(),
Expand Down Expand Up @@ -132,6 +134,27 @@ public function visible(): array
return array_slice($this->matches(), $this->firstVisible, $this->scroll, preserve_keys: true);
}

/**
* Toggle all options.
*/
protected function toggleAll(): void
{
$allMatchesSelected = collect($this->matches)->every(fn ($label, $key) => $this->isList()
? array_key_exists($label, $this->values)
: array_key_exists($key, $this->values));

if ($allMatchesSelected) {
$this->values = array_filter($this->values, fn ($value) => $this->isList()
? ! in_array($value, $this->matches)
: ! array_key_exists(array_search($value, $this->matches), $this->matches)
);
} else {
$this->values = $this->isList()
? array_merge($this->values, array_combine(array_values($this->matches), array_values($this->matches)))
: array_merge($this->values, array_combine(array_keys($this->matches), array_values($this->matches)));
}
}

/**
* Toggle the highlighted entry.
*/
Expand Down
19 changes: 17 additions & 2 deletions src/MultiSelectPrompt.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,10 @@ public function __construct(
$this->on('key', fn ($key) => match ($key) {
Key::UP, Key::UP_ARROW, Key::LEFT, Key::LEFT_ARROW, Key::SHIFT_TAB, Key::CTRL_P, Key::CTRL_B, 'k', 'h' => $this->highlightPrevious(count($this->options)),
Key::DOWN, Key::DOWN_ARROW, Key::RIGHT, Key::RIGHT_ARROW, Key::TAB, Key::CTRL_N, Key::CTRL_F, 'j', 'l' => $this->highlightNext(count($this->options)),
Key::oneOf([Key::HOME, Key::CTRL_A], $key) => $this->highlight(0),
Key::oneOf([Key::END, Key::CTRL_E], $key) => $this->highlight(count($this->options) - 1),
Key::oneOf(Key::HOME, $key) => $this->highlight(0),
Key::oneOf(Key::END, $key) => $this->highlight(count($this->options) - 1),
Key::SPACE => $this->toggleHighlighted(),
Key::CTRL_A => $this->toggleAll(),
Key::ENTER => $this->submit(),
default => null,
});
Expand Down Expand Up @@ -115,6 +116,20 @@ public function isSelected(string $value): bool
return in_array($value, $this->values);
}

/**
* Toggle all options.
*/
protected function toggleAll(): void
{
if (count($this->values) === count($this->options)) {
$this->values = [];
} else {
$this->values = array_is_list($this->options)
? array_values($this->options)
: array_keys($this->options);
}
}

/**
* Toggle the highlighted entry.
*/
Expand Down
28 changes: 28 additions & 0 deletions tests/Feature/MultiSearchPromptTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -333,3 +333,31 @@

Prompt::validateUsing(fn () => null);
});

it('supports selecting all options', function () {
Prompt::fake([Key::DOWN, Key::CTRL_A, Key::ENTER]);

$result = multisearch(
label: 'What are your favorite colors?',
options: fn () => [
'red' => 'Red',
'green' => 'Green',
'blue' => 'Blue',
],
);

expect($result)->toBe(['red', 'green', 'blue']);

Prompt::fake([Key::DOWN, Key::CTRL_A, Key::CTRL_A, Key::ENTER]);

$result = multisearch(
label: 'What are your favorite colors?',
options: fn () => [
'red' => 'Red',
'green' => 'Green',
'blue' => 'Blue',
],
);

expect($result)->toBe([]);
});
28 changes: 28 additions & 0 deletions tests/Feature/MultiSelectPromptTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,34 @@
expect($result)->toBe(['blue', 'red']);
});

it('supports selecting all options', function () {
Prompt::fake([Key::CTRL_A, Key::ENTER]);

$result = multiselect(
label: 'What are your favorite colors?',
options: [
'red' => 'Red',
'green' => 'Green',
'blue' => 'Blue',
]
);

expect($result)->toBe(['red', 'green', 'blue']);

Prompt::fake([Key::CTRL_A, Key::CTRL_A, Key::ENTER]);

$result = multiselect(
label: 'What are your favorite colors?',
options: [
'red' => 'Red',
'green' => 'Green',
'blue' => 'Blue',
]
);

expect($result)->toBe([]);
});

it('returns an empty array when non-interactive', function () {
Prompt::interactive(false);

Expand Down

0 comments on commit 9bc4df7

Please sign in to comment.