Skip to content

Commit df21506

Browse files
committed
feat: use single cursor function
1 parent 2aa0c03 commit df21506

File tree

3 files changed

+36
-40
lines changed

3 files changed

+36
-40
lines changed

packages/core/src/prompts/multi-select.ts

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,42 @@
1-
import { findNextCursor, findPrevCursor } from '../utils/cursor.js';
1+
import { findCursor } from '../utils/cursor.js';
22
import Prompt, { type PromptOptions } from './prompt.js';
33

4-
interface MultiSelectOptions<T extends { value: any; disabled?: boolean }>
4+
interface OptionLike {
5+
value: any;
6+
disabled?: boolean;
7+
}
8+
9+
interface MultiSelectOptions<T extends OptionLike>
510
extends PromptOptions<T['value'][], MultiSelectPrompt<T>> {
611
options: T[];
712
initialValues?: T['value'][];
813
required?: boolean;
914
cursorAt?: T['value'];
1015
}
11-
export default class MultiSelectPrompt<T extends { value: any; disabled?: boolean }> extends Prompt<
12-
T['value'][]
13-
> {
16+
export default class MultiSelectPrompt<T extends OptionLike> extends Prompt<T['value'][]> {
1417
options: T[];
1518
cursor = 0;
16-
#enabledOptions: T[] = [];
1719

1820
private get _value(): T['value'] {
1921
return this.options[this.cursor].value;
2022
}
2123

24+
private get _enabledOptions(): T[] {
25+
return this.options.filter((option) => option.disabled !== true);
26+
}
27+
2228
private toggleAll() {
23-
const allSelected = this.value !== undefined && this.value.length === this.#enabledOptions.length;
24-
this.value = allSelected ? [] : this.#enabledOptions.map((v) => v.value);
29+
const enabledOptions = this._enabledOptions;
30+
const allSelected = this.value !== undefined && this.value.length === enabledOptions.length;
31+
this.value = allSelected ? [] : enabledOptions.map((v) => v.value);
2532
}
2633

2734
private toggleInvert() {
2835
const value = this.value;
2936
if (!value) {
3037
return;
3138
}
32-
const notSelected = this.#enabledOptions.filter((v) => !value.includes(v.value));
39+
const notSelected = this._enabledOptions.filter((v) => !value.includes(v.value));
3340
this.value = notSelected.map((v) => v.value);
3441
}
3542

@@ -47,17 +54,12 @@ export default class MultiSelectPrompt<T extends { value: any; disabled?: boolea
4754
super(opts, false);
4855

4956
this.options = opts.options;
50-
this.#enabledOptions = this.options.filter((option) => !option.disabled);
51-
if (this.#enabledOptions.length === 0) return;
5257
this.value = [...(opts.initialValues ?? [])];
5358
const cursor = Math.max(
5459
this.options.findIndex(({ value }) => value === opts.cursorAt),
5560
0
5661
);
57-
this.cursor = this.options[cursor].disabled ? findNextCursor<T>(
58-
cursor,
59-
this.options
60-
) : cursor;
62+
this.cursor = this.options[cursor].disabled ? findCursor<T>(cursor, 1, this.options) : cursor;
6163
this.on('key', (char) => {
6264
if (char === 'a') {
6365
this.toggleAll();
@@ -71,11 +73,11 @@ export default class MultiSelectPrompt<T extends { value: any; disabled?: boolea
7173
switch (key) {
7274
case 'left':
7375
case 'up':
74-
this.cursor = findPrevCursor<T>(this.cursor, this.options);
76+
this.cursor = findCursor<T>(this.cursor, -1, this.options);
7577
break;
7678
case 'down':
7779
case 'right':
78-
this.cursor = findNextCursor<T>(this.cursor, this.options);
80+
this.cursor = findCursor<T>(this.cursor, 1, this.options);
7981
break;
8082
case 'space':
8183
this.toggleValue();

packages/core/src/prompts/select.ts

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { findNextCursor, findPrevCursor } from '../utils/cursor.js';
1+
import { findCursor } from '../utils/cursor.js';
22
import Prompt, { type PromptOptions } from './prompt.js';
33

44
interface SelectOptions<T extends { value: any; disabled?: boolean }>
@@ -11,7 +11,6 @@ export default class SelectPrompt<T extends { value: any; disabled?: boolean }>
1111
> {
1212
options: T[];
1313
cursor = 0;
14-
#enabledOptions: T[] = [];
1514

1615
private get _selectedValue() {
1716
return this.options[this.cursor];
@@ -25,23 +24,21 @@ export default class SelectPrompt<T extends { value: any; disabled?: boolean }>
2524
super(opts, false);
2625

2726
this.options = opts.options;
28-
this.#enabledOptions = this.options.filter((option) => !option.disabled);
29-
if (this.#enabledOptions.length === 0) return;
3027

3128
const initialCursor = this.options.findIndex(({ value }) => value === opts.initialValue);
32-
const cursor = initialCursor === -1 ? 0 : initialCursor
33-
this.cursor = this.options[cursor].disabled ? findNextCursor<T>(cursor, this.options) : cursor;
29+
const cursor = initialCursor === -1 ? 0 : initialCursor;
30+
this.cursor = this.options[cursor].disabled ? findCursor<T>(cursor, 1, this.options) : cursor;
3431
this.changeValue();
3532

3633
this.on('cursor', (key) => {
3734
switch (key) {
3835
case 'left':
3936
case 'up':
40-
this.cursor = findPrevCursor<T>(this.cursor, this.options);
37+
this.cursor = findCursor<T>(this.cursor, -1, this.options);
4138
break;
4239
case 'down':
4340
case 'right':
44-
this.cursor = findNextCursor<T>(this.cursor, this.options);
41+
this.cursor = findCursor<T>(this.cursor, 1, this.options);
4542
break;
4643
}
4744
this.changeValue();

packages/core/src/utils/cursor.ts

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,14 @@
1-
export function findPrevCursor<T extends { disabled?: boolean }>(cursor: number, options: T[]) {
2-
const prevCursor = cursor === 0 ? options.length - 1 : cursor - 1;
3-
const prevOption = options[prevCursor];
4-
if (prevOption.disabled) {
5-
return findPrevCursor(prevCursor, options);
1+
export function findCursor<T extends { disabled?: boolean }>(
2+
cursor: number,
3+
delta: number,
4+
options: T[]
5+
) {
6+
const newCursor = cursor + delta;
7+
const maxCursor = Math.max(options.length - 1, 0);
8+
const clampedCursor = newCursor < 0 ? maxCursor : newCursor > maxCursor ? 0 : newCursor;
9+
const newOption = options[clampedCursor];
10+
if (newOption.disabled) {
11+
return findCursor(clampedCursor, delta + (delta < 0 ? -1 : 1), options);
612
}
7-
return prevCursor;
8-
}
9-
10-
export function findNextCursor<T extends { disabled?: boolean }>(cursor: number, options: T[]) {
11-
const nextCursor = cursor === options.length - 1 ? 0 : cursor + 1;
12-
const nextOption = options[nextCursor];
13-
if (nextOption.disabled) {
14-
return findNextCursor(nextCursor, options);
15-
}
16-
return nextCursor;
13+
return clampedCursor;
1714
}

0 commit comments

Comments
 (0)