-
Notifications
You must be signed in to change notification settings - Fork 99
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
562 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
<?php | ||
|
||
use function Laravel\Prompts\multisearch; | ||
|
||
require __DIR__.'/../vendor/autoload.php'; | ||
|
||
$models = multisearch( | ||
label: 'Which users should receive the email?', | ||
placeholder: 'Search...', | ||
returnKeys: true, | ||
options: function ($value) { | ||
if (strlen($value) === 0) { | ||
return []; | ||
} | ||
|
||
usleep(100 * 1000); | ||
|
||
$min = min(strlen($value)-1, 10); | ||
$max = max(10, 20 - strlen($value)); | ||
|
||
if ($max - $min < 0) { | ||
return []; | ||
} | ||
|
||
$data = []; | ||
|
||
foreach (range($min, $max) as $id) { | ||
$data["id-$id"] = "User $id"; | ||
} | ||
|
||
return $data; | ||
}, | ||
validate: function ($values) { | ||
if (in_array('id-1', $values)) { | ||
return 'User 1 cannot receive emails'; | ||
} | ||
}, | ||
required: true, | ||
); | ||
|
||
var_dump($models); | ||
|
||
echo str_repeat(PHP_EOL, 6); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,231 @@ | ||
<?php | ||
|
||
namespace Laravel\Prompts; | ||
|
||
use Closure; | ||
use Illuminate\Support\Collection; | ||
|
||
class MultiSearchPrompt extends Prompt | ||
{ | ||
use Concerns\Truncation; | ||
use Concerns\TypedValue; | ||
|
||
/** | ||
* The index of the highlighted option. | ||
*/ | ||
public ?int $highlighted = null; | ||
|
||
/** | ||
* The index of the first visible option. | ||
*/ | ||
public int $firstVisible = 0; | ||
|
||
/** | ||
* The cached matches. | ||
* | ||
* @var array<int|string, string>|null | ||
*/ | ||
protected ?array $matches = null; | ||
|
||
/** | ||
* The default values the multi-search prompt. | ||
* | ||
* @var array<int|string, string> | ||
*/ | ||
public array $default; | ||
|
||
/** | ||
* The selected values. | ||
* | ||
* @var array<int|string, string> | ||
*/ | ||
public array $values = []; | ||
|
||
/** | ||
* Create a new MultiSearchPrompt instance. | ||
* | ||
* @param Closure(string): array<int|string, string> $options | ||
*/ | ||
public function __construct( | ||
public string $label, | ||
public Closure $options, | ||
public bool $returnKeys = true, | ||
array|Collection $default = [], | ||
public string $placeholder = '', | ||
public int $scroll = 5, | ||
public bool|string $required = false, | ||
public ?Closure $validate = null, | ||
public string $hint = '', | ||
) { | ||
$this->default = $default instanceof Collection ? $default->all() : $default; | ||
$this->values = $this->default; | ||
|
||
$this->trackTypedValue(submit: false, allowKey: fn ($key) => $key !== Key::SPACE || $this->highlighted === null); | ||
|
||
$this->on('key', fn ($key) => match ($key) { | ||
Key::UP, Key::UP_ARROW, Key::SHIFT_TAB => $this->highlightPrevious(), | ||
Key::DOWN, Key::DOWN_ARROW, Key::TAB => $this->highlightNext(), | ||
Key::SPACE => $this->highlighted !== null ? $this->toggleHighlighted() : null, | ||
Key::ENTER => $this->submit(), | ||
Key::LEFT, Key::LEFT_ARROW, Key::RIGHT, Key::RIGHT_ARROW => $this->highlighted = null, | ||
default => $this->search(), | ||
}); | ||
} | ||
|
||
/** | ||
* Perform the search. | ||
*/ | ||
protected function search(): void | ||
{ | ||
$this->state = 'searching'; | ||
$this->highlighted = null; | ||
$this->render(); | ||
$this->matches = null; | ||
$this->firstVisible = 0; | ||
$this->state = 'active'; | ||
} | ||
|
||
/** | ||
* Get the entered value with a virtual cursor. | ||
*/ | ||
public function valueWithCursor(int $maxWidth): string | ||
{ | ||
if ($this->highlighted !== null) { | ||
return $this->typedValue === '' | ||
? $this->dim($this->truncate($this->placeholder, $maxWidth)) | ||
: $this->truncate($this->typedValue, $maxWidth); | ||
} | ||
|
||
if ($this->typedValue === '') { | ||
return $this->dim($this->addCursor($this->placeholder, 0, $maxWidth)); | ||
} | ||
|
||
return $this->addCursor($this->typedValue, $this->cursorPosition, $maxWidth); | ||
} | ||
|
||
/** | ||
* Get options that match the input. | ||
* | ||
* @return array<string> | ||
*/ | ||
public function matches(): array | ||
{ | ||
if (is_array($this->matches)) { | ||
return $this->matches; | ||
} | ||
|
||
if (strlen($this->typedValue) === 0) { | ||
return $this->matches = $this->values; | ||
} | ||
|
||
return $this->matches = ($this->options)($this->typedValue); | ||
} | ||
|
||
/** | ||
* The currently visible matches | ||
* | ||
* @return array<string> | ||
*/ | ||
public function visible(): array | ||
{ | ||
return array_slice($this->matches(), $this->firstVisible, $this->scroll, preserve_keys: true); | ||
} | ||
|
||
/** | ||
* Highlight the previous entry, or wrap around to the last entry. | ||
*/ | ||
protected function highlightPrevious(): void | ||
{ | ||
if ($this->matches === []) { | ||
$this->highlighted = null; | ||
} elseif ($this->highlighted === null) { | ||
$this->highlighted = count($this->matches) - 1; | ||
} elseif ($this->highlighted === 0) { | ||
$this->highlighted = null; | ||
} else { | ||
$this->highlighted = $this->highlighted - 1; | ||
} | ||
|
||
if ($this->highlighted < $this->firstVisible) { | ||
$this->firstVisible--; | ||
} elseif ($this->highlighted === count($this->matches) - 1) { | ||
$this->firstVisible = count($this->matches) - min($this->scroll, count($this->matches)); | ||
} | ||
} | ||
|
||
/** | ||
* Highlight the next entry, or wrap around to the first entry. | ||
*/ | ||
protected function highlightNext(): void | ||
{ | ||
if ($this->matches === []) { | ||
$this->highlighted = null; | ||
} elseif ($this->highlighted === null) { | ||
$this->highlighted = 0; | ||
} else { | ||
$this->highlighted = $this->highlighted === count($this->matches) - 1 ? null : $this->highlighted + 1; | ||
} | ||
|
||
if ($this->highlighted > $this->firstVisible + $this->scroll - 1) { | ||
$this->firstVisible++; | ||
} elseif ($this->highlighted === 0 || $this->highlighted === null) { | ||
$this->firstVisible = 0; | ||
} | ||
} | ||
|
||
/** | ||
* Toggle the highlighted entry. | ||
*/ | ||
protected function toggleHighlighted(): void | ||
{ | ||
if ($this->returnKeys) { | ||
$key = array_keys($this->matches)[$this->highlighted]; | ||
|
||
if (array_key_exists($key, $this->values)) { | ||
unset($this->values[$key]); | ||
} else { | ||
$this->values[$key] = $this->matches[$key]; | ||
} | ||
} else { | ||
$value = $this->matches[$this->highlighted]; | ||
|
||
if (in_array($value, $this->values)) { | ||
$this->values = array_filter($this->values, fn ($v) => $v !== $value); | ||
} else { | ||
$this->values[] = $value; | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Get the current search query. | ||
*/ | ||
public function searchValue(): string | ||
{ | ||
return $this->typedValue; | ||
} | ||
|
||
/** | ||
* Get the selected value. | ||
*/ | ||
public function value(): array | ||
{ | ||
if ($this->values === null) { | ||
return null; | ||
} | ||
|
||
return $this->returnKeys | ||
? array_keys($this->values) | ||
: $this->values; | ||
} | ||
|
||
/** | ||
* Get the selected labels. | ||
* | ||
* @return array<string> | ||
*/ | ||
public function labels(): array | ||
{ | ||
return array_values($this->values); | ||
} | ||
} |
Oops, something went wrong.