Skip to content

Commit 343dd2e

Browse files
committed
Refactor
1 parent b586f30 commit 343dd2e

10 files changed

+161
-215
lines changed

src/MultiSelectPrompt.php

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ class MultiSelectPrompt extends Prompt
1212
*/
1313
public int $highlighted = 0;
1414

15+
/**
16+
* The index of the first visible option.
17+
*/
18+
public int $firstVisible = 0;
19+
1520
/**
1621
* The options for the multi-select prompt.
1722
*
@@ -26,11 +31,6 @@ class MultiSelectPrompt extends Prompt
2631
*/
2732
public array $default;
2833

29-
/**
30-
* The view state for scrolling the options.
31-
*/
32-
public ViewState $view;
33-
3434
/**
3535
* The selected values.
3636
*
@@ -56,7 +56,6 @@ public function __construct(
5656
$this->options = $options instanceof Collection ? $options->all() : $options;
5757
$this->default = $default instanceof Collection ? $default->all() : $default;
5858
$this->values = $this->default;
59-
$this->view = new ViewState(min($this->scroll, $this->terminal()->lines() - 5), count($this->options));
6059

6160
$this->on('key', fn ($key) => match ($key) {
6261
Key::UP, Key::UP_ARROW, Key::LEFT, Key::LEFT_ARROW, Key::SHIFT_TAB, 'k', 'h' => $this->highlightPrevious(),
@@ -91,6 +90,16 @@ public function labels(): array
9190
return array_values(array_intersect_key($this->options, array_flip($this->values)));
9291
}
9392

93+
/**
94+
* The currently visible options.
95+
*
96+
* @return array<int|string, string>
97+
*/
98+
public function visible(): array
99+
{
100+
return array_slice($this->options, $this->firstVisible, $this->scroll, preserve_keys: true);
101+
}
102+
94103
/**
95104
* Check whether the value is currently highlighted.
96105
*/
@@ -117,6 +126,12 @@ public function isSelected(string $value): bool
117126
protected function highlightPrevious(): void
118127
{
119128
$this->highlighted = $this->highlighted === 0 ? count($this->options) - 1 : $this->highlighted - 1;
129+
130+
if ($this->highlighted < $this->firstVisible) {
131+
$this->firstVisible--;
132+
} elseif ($this->highlighted === count($this->options) - 1) {
133+
$this->firstVisible = count($this->options) - min($this->scroll, count($this->options));
134+
}
120135
}
121136

122137
/**
@@ -125,6 +140,12 @@ protected function highlightPrevious(): void
125140
protected function highlightNext(): void
126141
{
127142
$this->highlighted = $this->highlighted === count($this->options) - 1 ? 0 : $this->highlighted + 1;
143+
144+
if ($this->highlighted > $this->firstVisible + $this->scroll - 1) {
145+
$this->firstVisible++;
146+
} elseif ($this->highlighted === 0) {
147+
$this->firstVisible = 0;
148+
}
128149
}
129150

130151
/**

src/SearchPrompt.php

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ class SearchPrompt extends Prompt
1515
public ?int $highlighted = null;
1616

1717
/**
18-
* The view state for scrolling the matches.
18+
* The index of the first visible option.
1919
*/
20-
public ViewState $view;
20+
public int $firstVisible = 0;
2121

2222
/**
2323
* The cached matches.
@@ -39,8 +39,6 @@ public function __construct(
3939
public ?Closure $validate = null,
4040
public string $hint = ''
4141
) {
42-
$this->view = new ViewState(min($this->scroll, $this->terminal()->lines() - 7), 0);
43-
4442
$this->trackTypedValue(submit: false);
4543

4644
$this->on('key', fn ($key) => match ($key) {
@@ -96,6 +94,16 @@ public function matches(): array
9694
return $this->matches = ($this->options)($this->typedValue);
9795
}
9896

97+
/**
98+
* The currently visible matches.
99+
*
100+
* @return array<string>
101+
*/
102+
public function visible(): array
103+
{
104+
return array_slice($this->matches(), $this->firstVisible, $this->scroll, preserve_keys: true);
105+
}
106+
99107
/**
100108
* Highlight the previous entry, or wrap around to the last entry.
101109
*/
@@ -110,6 +118,12 @@ protected function highlightPrevious(): void
110118
} else {
111119
$this->highlighted = $this->highlighted - 1;
112120
}
121+
122+
if ($this->highlighted < $this->firstVisible) {
123+
$this->firstVisible--;
124+
} elseif ($this->highlighted === count($this->matches) - 1) {
125+
$this->firstVisible = count($this->matches) - min($this->scroll, count($this->matches));
126+
}
113127
}
114128

115129
/**
@@ -124,6 +138,12 @@ protected function highlightNext(): void
124138
} else {
125139
$this->highlighted = $this->highlighted === count($this->matches) - 1 ? null : $this->highlighted + 1;
126140
}
141+
142+
if ($this->highlighted > $this->firstVisible + $this->scroll - 1) {
143+
$this->firstVisible++;
144+
} elseif ($this->highlighted === 0 || $this->highlighted === null) {
145+
$this->firstVisible = 0;
146+
}
127147
}
128148

129149
/**

src/SelectPrompt.php

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,18 @@ class SelectPrompt extends Prompt
1212
*/
1313
public int $highlighted = 0;
1414

15+
/**
16+
* The index of the first visible option.
17+
*/
18+
public int $firstVisible = 0;
19+
1520
/**
1621
* The options for the select prompt.
1722
*
1823
* @var array<int|string, string>
1924
*/
2025
public array $options;
2126

22-
/**
23-
* The view state for scrolling the options.
24-
*/
25-
public ViewState $view;
26-
2727
/**
2828
* Create a new SelectPrompt instance.
2929
*
@@ -38,7 +38,6 @@ public function __construct(
3838
public string $hint = ''
3939
) {
4040
$this->options = $options instanceof Collection ? $options->all() : $options;
41-
$this->view = new ViewState(min($this->scroll, $this->terminal()->lines() - 5), count($this->options));
4241

4342
if ($this->default) {
4443
if (array_is_list($this->options)) {
@@ -80,12 +79,28 @@ public function label(): ?string
8079
}
8180
}
8281

82+
/**
83+
* The currently visible options.
84+
*
85+
* @return array<int|string, string>
86+
*/
87+
public function visible(): array
88+
{
89+
return array_slice($this->options, $this->firstVisible, $this->scroll, preserve_keys: true);
90+
}
91+
8392
/**
8493
* Highlight the previous entry, or wrap around to the last entry.
8594
*/
8695
protected function highlightPrevious(): void
8796
{
8897
$this->highlighted = $this->highlighted === 0 ? count($this->options) - 1 : $this->highlighted - 1;
98+
99+
if ($this->highlighted < $this->firstVisible) {
100+
$this->firstVisible--;
101+
} elseif ($this->highlighted === count($this->options) - 1) {
102+
$this->firstVisible = count($this->options) - min($this->scroll, count($this->options));
103+
}
89104
}
90105

91106
/**
@@ -94,5 +109,11 @@ protected function highlightPrevious(): void
94109
protected function highlightNext(): void
95110
{
96111
$this->highlighted = $this->highlighted === count($this->options) - 1 ? 0 : $this->highlighted + 1;
112+
113+
if ($this->highlighted > $this->firstVisible + $this->scroll - 1) {
114+
$this->firstVisible++;
115+
} elseif ($this->highlighted === 0) {
116+
$this->firstVisible = 0;
117+
}
97118
}
98119
}

src/SuggestPrompt.php

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,18 @@ class SuggestPrompt extends Prompt
1515
*/
1616
public ?int $highlighted = null;
1717

18+
/**
19+
* The index of the first visible option.
20+
*/
21+
public int $firstVisible = 0;
22+
1823
/**
1924
* The options for the suggest prompt.
2025
*
2126
* @var array<string>|Closure(string): array<string>
2227
*/
2328
public array|Closure $options;
2429

25-
/**
26-
* The view state for scrolling the options.
27-
*/
28-
public ViewState $view;
29-
3030
/**
3131
* The cache of matches.
3232
*
@@ -50,7 +50,6 @@ public function __construct(
5050
public string $hint = ''
5151
) {
5252
$this->options = $options instanceof Collection ? $options->all() : $options;
53-
$this->view = new ViewState(min($this->scroll, $this->terminal()->lines() - 7), 0);
5453

5554
$this->on('key', fn ($key) => match ($key) {
5655
Key::UP, Key::UP_ARROW, Key::SHIFT_TAB => $this->highlightPrevious(),
@@ -104,6 +103,16 @@ public function matches(): array
104103
}));
105104
}
106105

106+
/**
107+
* The current visible matches.
108+
*
109+
* @return array<string>
110+
*/
111+
public function visible(): array
112+
{
113+
return array_slice($this->matches(), $this->firstVisible, $this->scroll, preserve_keys: true);
114+
}
115+
107116
/**
108117
* Highlight the previous entry, or wrap around to the last entry.
109118
*/
@@ -118,6 +127,12 @@ protected function highlightPrevious(): void
118127
} else {
119128
$this->highlighted = $this->highlighted - 1;
120129
}
130+
131+
if ($this->highlighted < $this->firstVisible) {
132+
$this->firstVisible--;
133+
} elseif ($this->highlighted === count($this->matches()) - 1) {
134+
$this->firstVisible = count($this->matches()) - min($this->scroll, count($this->matches()));
135+
}
121136
}
122137

123138
/**
@@ -132,6 +147,12 @@ protected function highlightNext(): void
132147
} else {
133148
$this->highlighted = $this->highlighted === count($this->matches()) - 1 ? null : $this->highlighted + 1;
134149
}
150+
151+
if ($this->highlighted > $this->firstVisible + $this->scroll - 1) {
152+
$this->firstVisible++;
153+
} elseif ($this->highlighted === 0 || $this->highlighted === null) {
154+
$this->firstVisible = 0;
155+
}
135156
}
136157

137158
/**

src/Themes/Default/Concerns/DrawsScrollbars.php

Lines changed: 20 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -3,84 +3,53 @@
33
namespace Laravel\Prompts\Themes\Default\Concerns;
44

55
use Illuminate\Support\Collection;
6-
use Laravel\Prompts\ViewState;
76

87
trait DrawsScrollbars
98
{
109
/**
11-
* Scroll the given lines.
10+
* Render a scrollbar beside the visible items.
1211
*
13-
* @param \Illuminate\Support\Collection<int, string> $lines
14-
* @param \Laravel\Prompts\ViewState $view
12+
* @param \Illuminate\Support\Collection<int, string> $visible
1513
* @return \Illuminate\Support\Collection<int, string>
1614
*/
17-
protected function scroll(Collection $lines, ?int $focused, ViewState $view, int $height, int $width, string $color = 'cyan'): Collection
15+
protected function scrollbar(Collection $visible, int $firstVisible, int $height, int $total, int $width, string $color = 'cyan'): Collection
1816
{
19-
if ($lines->count() <= $height) {
20-
return $lines;
17+
if ($height >= $total) {
18+
return $visible;
2119
}
2220

23-
$visible = $this->visible($lines, $focused, $view, $height);
21+
$scrollPosition = $this->scrollPosition($firstVisible, $height, $total);
2422

2523
return $visible
24+
->values()
2625
->map(fn ($line) => $this->pad($line, $width))
27-
->map(fn ($line, $index) => match (true) {
28-
$index === $this->scrollPosition($visible, $view, $height, $lines->count()) => preg_replace('/.$/', $this->{$color}(''), $line),
26+
->map(fn ($line, $index) => match ($index) {
27+
$scrollPosition => preg_replace('/.$/', $this->{$color}(''), $line),
2928
default => preg_replace('/.$/', $this->gray(''), $line),
3029
});
3130
}
3231

3332
/**
34-
* Get a scrolled version of the items and update the view state.
35-
*
36-
* @param \Illuminate\Support\Collection<int, string> $lines
37-
* @param \Laravel\Prompts\ViewState $view
38-
* @return \Illuminate\Support\Collection<int, string>
33+
* Return the position where the scrollbar "handle" should be rendered.
3934
*/
40-
protected function visible(Collection $lines, ?int $focused, ViewState $view, int $height): Collection
35+
protected function scrollPosition(int $firstVisible, int $height, int $total): int
4136
{
42-
if ($lines->count() <= $height) {
43-
return $lines;
44-
}
45-
46-
if ($focused === null) {
47-
$view->resetStart();
48-
return $lines->slice(0, $height);
49-
}
50-
51-
if ($focused < $view->first()) {
52-
$view->update($focused);
53-
return $lines->slice($view->first(), count($view->items));
54-
}
55-
56-
if ($focused > $view->last()) {
57-
$view->update($focused - $height + 1);
58-
return $lines->slice($view->first(), count($view->items));
37+
if ($firstVisible === 0) {
38+
return 0;
5939
}
6040

61-
return $lines->slice($view->first(), count($view->items));
62-
}
41+
$maxPosition = $total - $height;
6342

64-
/**
65-
* Scroll the given lines.
66-
*
67-
* @param \Illuminate\Support\Collection<int, string> $visible
68-
*/
69-
protected function scrollPosition(Collection $visible, ViewState $view, int $height, int $total): int
70-
{
71-
if ($view->last() < $height) {
72-
return 0;
43+
if ($firstVisible === $maxPosition) {
44+
return $height - 1;
7345
}
7446

75-
if ($view->last() === $total - 1) {
76-
return $total - 1;
47+
if ($height <= 2) {
48+
return -1;
7749
}
7850

79-
$percent = ($view->last() + 1 - $height) / ($total - $height);
80-
81-
$keys = $visible->slice(1, -1)->keys();
82-
$position = (int) ceil($percent * count($keys) - 1);
51+
$percent = $firstVisible / $maxPosition;
8352

84-
return $keys[$position] ?? 0;
53+
return (int) round($percent * ($height - 3)) + 1;
8554
}
8655
}

0 commit comments

Comments
 (0)