Skip to content

Commit df50eed

Browse files
committed
feat: add selectableGroups option to group multi-select
Adds a `selectableGroups` boolean flag which can be used to disallow selecting groups. This basically just skips over them when using up/down, but still renders when they are "selected" (by selecting all the children individually).
1 parent d8a10da commit df50eed

File tree

2 files changed

+18
-3
lines changed

2 files changed

+18
-3
lines changed

packages/core/src/prompts/group-multiselect.ts

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@ interface GroupMultiSelectOptions<T extends { value: any }>
66
initialValues?: T['value'][];
77
required?: boolean;
88
cursorAt?: T['value'];
9+
selectableGroups?: boolean;
910
}
1011
export default class GroupMultiSelectPrompt<T extends { value: any }> extends Prompt {
1112
options: (T & { group: string | boolean })[];
1213
cursor = 0;
14+
#selectableGroups: boolean;
1315

1416
getGroupItems(group: string): T[] {
1517
return this.options.filter((o) => o.group === group);
@@ -44,26 +46,37 @@ export default class GroupMultiSelectPrompt<T extends { value: any }> extends Pr
4446
constructor(opts: GroupMultiSelectOptions<T>) {
4547
super(opts, false);
4648
const { options } = opts;
49+
this.#selectableGroups = opts.selectableGroups !== false;
4750
this.options = Object.entries(options).flatMap(([key, option]) => [
4851
{ value: key, group: true, label: key },
4952
...option.map((opt) => ({ ...opt, group: key })),
5053
]) as any;
5154
this.value = [...(opts.initialValues ?? [])];
5255
this.cursor = Math.max(
5356
this.options.findIndex(({ value }) => value === opts.cursorAt),
54-
0
57+
this.#selectableGroups ? 0 : 1
5558
);
5659

5760
this.on('cursor', (key) => {
5861
switch (key) {
5962
case 'left':
60-
case 'up':
63+
case 'up': {
6164
this.cursor = this.cursor === 0 ? this.options.length - 1 : this.cursor - 1;
65+
const currentIsGroup = this.options[this.cursor]?.group === true;
66+
if (!this.#selectableGroups && currentIsGroup) {
67+
this.cursor = this.cursor === 0 ? this.options.length - 1 : this.cursor - 1;
68+
}
6269
break;
70+
}
6371
case 'down':
64-
case 'right':
72+
case 'right': {
6573
this.cursor = this.cursor === this.options.length - 1 ? 0 : this.cursor + 1;
74+
const currentIsGroup = this.options[this.cursor]?.group === true;
75+
if (!this.#selectableGroups && currentIsGroup) {
76+
this.cursor = this.cursor === this.options.length - 1 ? 0 : this.cursor + 1;
77+
}
6678
break;
79+
}
6780
case 'space':
6881
this.toggleValue();
6982
break;

packages/prompts/src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -462,6 +462,7 @@ export interface GroupMultiSelectOptions<Value> {
462462
initialValues?: Value[];
463463
required?: boolean;
464464
cursorAt?: Value;
465+
selectableGroups?: boolean;
465466
}
466467
export const groupMultiselect = <Value>(opts: GroupMultiSelectOptions<Value>) => {
467468
const opt = (
@@ -516,6 +517,7 @@ export const groupMultiselect = <Value>(opts: GroupMultiSelectOptions<Value>) =>
516517
initialValues: opts.initialValues,
517518
required: opts.required ?? true,
518519
cursorAt: opts.cursorAt,
520+
selectableGroups: opts.selectableGroups,
519521
validate(selected: Value[]) {
520522
if (this.required && selected.length === 0)
521523
return `Please select at least one option.\n${color.reset(

0 commit comments

Comments
 (0)