Skip to content

Commit

Permalink
feat(@clack/prompts): add maxItems option to select prompt (#136)
Browse files Browse the repository at this point in the history
  • Loading branch information
cpreston321 authored Aug 9, 2023
2 parents 593f93d + e70f79b commit 956825c
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 1 deletion.
5 changes: 5 additions & 0 deletions .changeset/eight-bikes-repair.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@clack/prompts': minor
---

add maxItems option to select prompt
34 changes: 33 additions & 1 deletion packages/prompts/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ export interface SelectOptions<Options extends Option<Value>[], Value> {
message: string;
options: Options;
initialValue?: Value;
maxItems?: number;
}

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

let slidingWindowLocation = 0;

return new SelectPrompt({
options: opts.options,
initialValue: opts.initialValue,
Expand All @@ -212,8 +215,37 @@ export const select = <Options extends Option<Value>[], Value>(
'cancelled'
)}\n${color.gray(S_BAR)}`;
default: {
// We clamp to minimum 5 because anything less doesn't make sense UX wise
const maxItems = opts.maxItems === undefined ? Infinity : Math.max(opts.maxItems, 5);
if (this.cursor >= slidingWindowLocation + maxItems - 3) {
slidingWindowLocation = Math.max(
Math.min(this.cursor - maxItems + 3, this.options.length - maxItems),
0
);
} else if (this.cursor < slidingWindowLocation + 2) {
slidingWindowLocation = Math.max(this.cursor - 2, 0);
}

const shouldRenderTopEllipsis =
maxItems < this.options.length && slidingWindowLocation > 0;
const shouldRenderBottomEllipsis =
maxItems < this.options.length &&
slidingWindowLocation + maxItems < this.options.length;

return `${title}${color.cyan(S_BAR)} ${this.options
.map((option, i) => opt(option, i === this.cursor ? 'active' : 'inactive'))
.slice(slidingWindowLocation, slidingWindowLocation + maxItems)
.map((option, i, arr) => {
if (i === 0 && shouldRenderTopEllipsis) {
return color.dim('...');
} else if (i === arr.length - 1 && shouldRenderBottomEllipsis) {
return color.dim('...');
} else {
return opt(
option,
i + slidingWindowLocation === this.cursor ? 'active' : 'inactive'
);
}
})
.join(`\n${color.cyan(S_BAR)} `)}\n${color.cyan(S_BAR_END)}\n`;
}
}
Expand Down

0 comments on commit 956825c

Please sign in to comment.