Skip to content

Commit 956825c

Browse files
authored
feat(@clack/prompts): add maxItems option to select prompt (#136)
2 parents 593f93d + e70f79b commit 956825c

File tree

2 files changed

+38
-1
lines changed

2 files changed

+38
-1
lines changed

.changeset/eight-bikes-repair.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@clack/prompts': minor
3+
---
4+
5+
add maxItems option to select prompt

packages/prompts/src/index.ts

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,7 @@ export interface SelectOptions<Options extends Option<Value>[], Value> {
178178
message: string;
179179
options: Options;
180180
initialValue?: Value;
181+
maxItems?: number;
181182
}
182183

183184
export const select = <Options extends Option<Value>[], Value>(
@@ -197,6 +198,8 @@ export const select = <Options extends Option<Value>[], Value>(
197198
return `${color.dim(S_RADIO_INACTIVE)} ${color.dim(label)}`;
198199
};
199200

201+
let slidingWindowLocation = 0;
202+
200203
return new SelectPrompt({
201204
options: opts.options,
202205
initialValue: opts.initialValue,
@@ -212,8 +215,37 @@ export const select = <Options extends Option<Value>[], Value>(
212215
'cancelled'
213216
)}\n${color.gray(S_BAR)}`;
214217
default: {
218+
// We clamp to minimum 5 because anything less doesn't make sense UX wise
219+
const maxItems = opts.maxItems === undefined ? Infinity : Math.max(opts.maxItems, 5);
220+
if (this.cursor >= slidingWindowLocation + maxItems - 3) {
221+
slidingWindowLocation = Math.max(
222+
Math.min(this.cursor - maxItems + 3, this.options.length - maxItems),
223+
0
224+
);
225+
} else if (this.cursor < slidingWindowLocation + 2) {
226+
slidingWindowLocation = Math.max(this.cursor - 2, 0);
227+
}
228+
229+
const shouldRenderTopEllipsis =
230+
maxItems < this.options.length && slidingWindowLocation > 0;
231+
const shouldRenderBottomEllipsis =
232+
maxItems < this.options.length &&
233+
slidingWindowLocation + maxItems < this.options.length;
234+
215235
return `${title}${color.cyan(S_BAR)} ${this.options
216-
.map((option, i) => opt(option, i === this.cursor ? 'active' : 'inactive'))
236+
.slice(slidingWindowLocation, slidingWindowLocation + maxItems)
237+
.map((option, i, arr) => {
238+
if (i === 0 && shouldRenderTopEllipsis) {
239+
return color.dim('...');
240+
} else if (i === arr.length - 1 && shouldRenderBottomEllipsis) {
241+
return color.dim('...');
242+
} else {
243+
return opt(
244+
option,
245+
i + slidingWindowLocation === this.cursor ? 'active' : 'inactive'
246+
);
247+
}
248+
})
217249
.join(`\n${color.cyan(S_BAR)} `)}\n${color.cyan(S_BAR_END)}\n`;
218250
}
219251
}

0 commit comments

Comments
 (0)