@topcli/prompts 1.8.1 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -1
- package/dist/index.cjs +82 -30
- package/dist/index.d.cts +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +82 -30
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -86,6 +86,10 @@ select(message: string, options: SelectOptions): Promise<string>
|
|
|
86
86
|
Scrollable select depending `maxVisible` (default `8`).
|
|
87
87
|
Use `ignoreValues` to skip result render & clear lines after a selected one.
|
|
88
88
|
|
|
89
|
+
Use `autocomplete` to allow filtered choices. This can be useful for a large list of choices.
|
|
90
|
+
|
|
91
|
+
Use `caseSensitive` to make autocomplete filters case sensitive. Default `false`
|
|
92
|
+
|
|
89
93
|
### `multiselect()`
|
|
90
94
|
|
|
91
95
|
```ts
|
|
@@ -97,6 +101,8 @@ Use `preSelectedChoices` to pre-select choices.
|
|
|
97
101
|
|
|
98
102
|
Use `validators` to handle user input.
|
|
99
103
|
|
|
104
|
+
Use `showHint: false` to disable hint (this option is truthy by default).
|
|
105
|
+
|
|
100
106
|
**Example**
|
|
101
107
|
|
|
102
108
|
```js
|
|
@@ -106,7 +112,7 @@ const os = await multiselect('Choose OS', {
|
|
|
106
112
|
});
|
|
107
113
|
```
|
|
108
114
|
|
|
109
|
-
Use `autocomplete` to allow filtered choices. This can be
|
|
115
|
+
Use `autocomplete` to allow filtered choices. This can be useful for a large list of choices.
|
|
110
116
|
|
|
111
117
|
Use `caseSensitive` to make autocomplete filters case sensitive. Default `false`
|
|
112
118
|
|
|
@@ -172,6 +178,8 @@ export interface SelectOptions extends SharedOptions {
|
|
|
172
178
|
choices: (Choice | string)[];
|
|
173
179
|
maxVisible?: number;
|
|
174
180
|
ignoreValues?: (string | number | boolean)[];
|
|
181
|
+
autocomplete?: boolean;
|
|
182
|
+
caseSensitive?: boolean;
|
|
175
183
|
}
|
|
176
184
|
|
|
177
185
|
export interface MultiselectOptions extends SharedOptions {
|
|
@@ -181,6 +189,7 @@ export interface MultiselectOptions extends SharedOptions {
|
|
|
181
189
|
validators?: Validator[];
|
|
182
190
|
autocomplete?: boolean;
|
|
183
191
|
caseSensitive?: boolean;
|
|
192
|
+
showHint?: boolean;
|
|
184
193
|
}
|
|
185
194
|
|
|
186
195
|
export interface ConfirmOptions extends SharedOptions {
|
|
@@ -202,6 +211,7 @@ export interface ConfirmOptions extends SharedOptions {
|
|
|
202
211
|
<td align="center" valign="top" width="14.28%"><a href="http://sofiand.github.io/portfolio-client/"><img src="https://avatars.githubusercontent.com/u/39944043?v=4?s=100" width="100px;" alt="Yefis"/><br /><sub><b>Yefis</b></sub></a><br /><a href="https://github.com/TopCli/prompts/commits?author=SofianD" title="Code">💻</a> <a href="https://github.com/TopCli/prompts/commits?author=SofianD" title="Documentation">📖</a></td>
|
|
203
212
|
<td align="center" valign="top" width="14.28%"><a href="http://justie.dev"><img src="https://avatars.githubusercontent.com/u/7118300?v=4?s=100" width="100px;" alt="Ben"/><br /><sub><b>Ben</b></sub></a><br /><a href="https://github.com/TopCli/prompts/commits?author=JUSTIVE" title="Documentation">📖</a> <a href="#maintenance-JUSTIVE" title="Maintenance">🚧</a></td>
|
|
204
213
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ncukondo"><img src="https://avatars.githubusercontent.com/u/17022138?v=4?s=100" width="100px;" alt="Takeshi Kondo"/><br /><sub><b>Takeshi Kondo</b></sub></a><br /><a href="#maintenance-ncukondo" title="Maintenance">🚧</a></td>
|
|
214
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/FredGuiou"><img src="https://avatars.githubusercontent.com/u/99122562?v=4?s=100" width="100px;" alt="FredGuiou"/><br /><sub><b>FredGuiou</b></sub></a><br /><a href="https://github.com/TopCli/prompts/commits?author=FredGuiou" title="Code">💻</a> <a href="https://github.com/TopCli/prompts/commits?author=FredGuiou" title="Tests">⚠️</a> <a href="https://github.com/TopCli/prompts/commits?author=FredGuiou" title="Documentation">📖</a></td>
|
|
205
215
|
</tr>
|
|
206
216
|
</tbody>
|
|
207
217
|
</table>
|
package/dist/index.cjs
CHANGED
|
@@ -379,10 +379,10 @@ var ConfirmPrompt = class extends AbstractPrompt {
|
|
|
379
379
|
return `${query} ${this.#getHint()}`;
|
|
380
380
|
}
|
|
381
381
|
#onQuestionAnswer() {
|
|
382
|
-
|
|
382
|
+
this.clearLastLine();
|
|
383
383
|
this.stdout.moveCursor(
|
|
384
384
|
-this.stdout.columns,
|
|
385
|
-
-
|
|
385
|
+
-Math.floor((0, import_wcwidth2.default)(stripAnsi(this.#getQuestionQuery())) / this.stdout.columns)
|
|
386
386
|
);
|
|
387
387
|
this.stdout.clearScreenDown();
|
|
388
388
|
this.write(`${this.selectedValue ? SYMBOLS.Tick : SYMBOLS.Cross} ${import_kleur3.default.bold(this.message)}${import_node_os3.EOL}`);
|
|
@@ -413,18 +413,47 @@ var ConfirmPrompt = class extends AbstractPrompt {
|
|
|
413
413
|
var import_node_os4 = require("os");
|
|
414
414
|
var import_kleur4 = __toESM(require("kleur"), 1);
|
|
415
415
|
var import_wcwidth3 = __toESM(require("@topcli/wcwidth"), 1);
|
|
416
|
+
var kRequiredChoiceProperties = ["label", "value"];
|
|
416
417
|
var SelectPrompt = class extends AbstractPrompt {
|
|
417
418
|
#boundExitEvent = () => void 0;
|
|
418
419
|
#boundKeyPressEvent = () => void 0;
|
|
419
420
|
activeIndex = 0;
|
|
420
|
-
selectedIndexes = [];
|
|
421
421
|
questionMessage;
|
|
422
|
-
|
|
422
|
+
autocompleteValue = "";
|
|
423
423
|
options;
|
|
424
424
|
lastRender;
|
|
425
425
|
get choices() {
|
|
426
426
|
return this.options.choices;
|
|
427
427
|
}
|
|
428
|
+
get filteredChoices() {
|
|
429
|
+
if (!(this.options.autocomplete && this.autocompleteValue.length > 0)) {
|
|
430
|
+
return this.choices;
|
|
431
|
+
}
|
|
432
|
+
const isCaseSensitive = this.options.caseSensitive;
|
|
433
|
+
const autocompleteValue = isCaseSensitive ? this.autocompleteValue : this.autocompleteValue.toLowerCase();
|
|
434
|
+
return this.choices.filter((choice) => this.#filterChoice(choice, autocompleteValue, isCaseSensitive));
|
|
435
|
+
}
|
|
436
|
+
#filterChoice(choice, autocompleteValue, isCaseSensitive = false) {
|
|
437
|
+
const choiceValue = typeof choice === "string" ? isCaseSensitive ? choice : choice.toLowerCase() : isCaseSensitive ? choice.label : choice.label.toLowerCase();
|
|
438
|
+
if (autocompleteValue.includes(" ")) {
|
|
439
|
+
return this.#filterMultipleWords(choiceValue, autocompleteValue, isCaseSensitive);
|
|
440
|
+
}
|
|
441
|
+
return choiceValue.includes(autocompleteValue);
|
|
442
|
+
}
|
|
443
|
+
#filterMultipleWords(choiceValue, autocompleteValue, isCaseSensitive) {
|
|
444
|
+
return autocompleteValue.split(" ").every((word) => {
|
|
445
|
+
const wordValue = isCaseSensitive ? word : word.toLowerCase();
|
|
446
|
+
return choiceValue.includes(wordValue) || choiceValue.includes(autocompleteValue);
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
get longestChoice() {
|
|
450
|
+
return Math.max(...this.filteredChoices.map((choice) => {
|
|
451
|
+
if (typeof choice === "string") {
|
|
452
|
+
return choice.length;
|
|
453
|
+
}
|
|
454
|
+
return choice.label.length;
|
|
455
|
+
}));
|
|
456
|
+
}
|
|
428
457
|
constructor(message, options) {
|
|
429
458
|
const {
|
|
430
459
|
stdin = process.stdin,
|
|
@@ -441,22 +470,20 @@ var SelectPrompt = class extends AbstractPrompt {
|
|
|
441
470
|
this.destroy();
|
|
442
471
|
throw new TypeError("Missing required param: choices");
|
|
443
472
|
}
|
|
444
|
-
|
|
473
|
+
for (const choice of choices) {
|
|
445
474
|
if (typeof choice === "string") {
|
|
446
|
-
|
|
475
|
+
continue;
|
|
447
476
|
}
|
|
448
|
-
const
|
|
449
|
-
for (const prop of kRequiredChoiceProperties2) {
|
|
477
|
+
for (const prop of kRequiredChoiceProperties) {
|
|
450
478
|
if (!choice[prop]) {
|
|
451
479
|
this.destroy();
|
|
452
480
|
throw new TypeError(`Missing ${prop} for choice ${JSON.stringify(choice)}`);
|
|
453
481
|
}
|
|
454
482
|
}
|
|
455
|
-
|
|
456
|
-
}));
|
|
483
|
+
}
|
|
457
484
|
}
|
|
458
485
|
#getFormattedChoice(choiceIndex) {
|
|
459
|
-
const choice = this.
|
|
486
|
+
const choice = this.filteredChoices[choiceIndex];
|
|
460
487
|
if (typeof choice === "string") {
|
|
461
488
|
return { value: choice, label: choice };
|
|
462
489
|
}
|
|
@@ -464,21 +491,24 @@ var SelectPrompt = class extends AbstractPrompt {
|
|
|
464
491
|
}
|
|
465
492
|
#getVisibleChoices() {
|
|
466
493
|
const maxVisible = this.options.maxVisible || 8;
|
|
467
|
-
let startIndex = Math.min(this.
|
|
494
|
+
let startIndex = Math.min(this.filteredChoices.length - maxVisible, this.activeIndex - Math.floor(maxVisible / 2));
|
|
468
495
|
if (startIndex < 0) {
|
|
469
496
|
startIndex = 0;
|
|
470
497
|
}
|
|
471
|
-
const endIndex = Math.min(startIndex + maxVisible, this.
|
|
498
|
+
const endIndex = Math.min(startIndex + maxVisible, this.filteredChoices.length);
|
|
472
499
|
return { startIndex, endIndex };
|
|
473
500
|
}
|
|
474
501
|
#showChoices() {
|
|
475
502
|
const { startIndex, endIndex } = this.#getVisibleChoices();
|
|
476
503
|
this.lastRender = { startIndex, endIndex };
|
|
504
|
+
if (this.options.autocomplete) {
|
|
505
|
+
this.write(`${SYMBOLS.Pointer} ${this.autocompleteValue}${import_node_os4.EOL}`);
|
|
506
|
+
}
|
|
477
507
|
for (let choiceIndex = startIndex; choiceIndex < endIndex; choiceIndex++) {
|
|
478
508
|
const choice = this.#getFormattedChoice(choiceIndex);
|
|
479
509
|
const isChoiceSelected = choiceIndex === this.activeIndex;
|
|
480
510
|
const showPreviousChoicesArrow = startIndex > 0 && choiceIndex === startIndex;
|
|
481
|
-
const showNextChoicesArrow = endIndex < this.
|
|
511
|
+
const showNextChoicesArrow = endIndex < this.filteredChoices.length && choiceIndex === endIndex - 1;
|
|
482
512
|
let prefixArrow = " ";
|
|
483
513
|
if (showPreviousChoicesArrow) {
|
|
484
514
|
prefixArrow = SYMBOLS.Previous;
|
|
@@ -487,16 +517,17 @@ var SelectPrompt = class extends AbstractPrompt {
|
|
|
487
517
|
}
|
|
488
518
|
const prefix = `${prefixArrow}${isChoiceSelected ? `${SYMBOLS.Pointer} ` : " "}`;
|
|
489
519
|
const formattedLabel = choice.label.padEnd(
|
|
490
|
-
this.
|
|
520
|
+
this.longestChoice < 10 ? this.longestChoice : 0
|
|
491
521
|
);
|
|
492
522
|
const formattedDescription = choice.description ? ` - ${choice.description}` : "";
|
|
493
523
|
const color = isChoiceSelected ? import_kleur4.default.white().bold : import_kleur4.default.gray;
|
|
494
|
-
const str =
|
|
524
|
+
const str = `${prefix}${color(`${formattedLabel}${formattedDescription}`)}${import_node_os4.EOL}`;
|
|
495
525
|
this.write(str);
|
|
496
526
|
}
|
|
497
527
|
}
|
|
498
528
|
#showAnsweredQuestion(choice) {
|
|
499
|
-
const
|
|
529
|
+
const symbolPrefix = choice === "" ? SYMBOLS.Cross : SYMBOLS.Tick;
|
|
530
|
+
const prefix = `${symbolPrefix} ${import_kleur4.default.bold(this.message)} ${SYMBOLS.Pointer}`;
|
|
500
531
|
const formattedChoice = import_kleur4.default.yellow(typeof choice === "string" ? choice : choice.label);
|
|
501
532
|
this.write(`${prefix} ${formattedChoice}${import_node_os4.EOL}`);
|
|
502
533
|
}
|
|
@@ -509,17 +540,18 @@ var SelectPrompt = class extends AbstractPrompt {
|
|
|
509
540
|
#onKeypress(...args) {
|
|
510
541
|
const [resolve, render, , key] = args;
|
|
511
542
|
if (key.name === "up") {
|
|
512
|
-
this.activeIndex = this.activeIndex === 0 ? this.
|
|
543
|
+
this.activeIndex = this.activeIndex === 0 ? this.filteredChoices.length - 1 : this.activeIndex - 1;
|
|
513
544
|
render();
|
|
514
545
|
} else if (key.name === "down") {
|
|
515
|
-
this.activeIndex = this.activeIndex === this.
|
|
546
|
+
this.activeIndex = this.activeIndex === this.filteredChoices.length - 1 ? 0 : this.activeIndex + 1;
|
|
516
547
|
render();
|
|
517
548
|
} else if (key.name === "return") {
|
|
549
|
+
const choice = this.filteredChoices[this.activeIndex] || "";
|
|
550
|
+
const label = typeof choice === "string" ? choice : choice.label;
|
|
551
|
+
const value = typeof choice === "string" ? choice : choice.value;
|
|
518
552
|
render({ clearRender: true });
|
|
519
|
-
const currentChoice = this.choices[this.activeIndex];
|
|
520
|
-
const value = typeof currentChoice === "string" ? currentChoice : currentChoice.value;
|
|
521
553
|
if (!this.options.ignoreValues?.includes(value)) {
|
|
522
|
-
this.#showAnsweredQuestion(
|
|
554
|
+
this.#showAnsweredQuestion(label);
|
|
523
555
|
}
|
|
524
556
|
this.write(SYMBOLS.ShowCursor);
|
|
525
557
|
this.destroy();
|
|
@@ -527,6 +559,14 @@ var SelectPrompt = class extends AbstractPrompt {
|
|
|
527
559
|
process.off("exit", this.#boundExitEvent);
|
|
528
560
|
resolve(value);
|
|
529
561
|
} else {
|
|
562
|
+
if (!key.ctrl && this.options.autocomplete) {
|
|
563
|
+
this.activeIndex = 0;
|
|
564
|
+
if (key.name === "backspace" && this.autocompleteValue.length > 0) {
|
|
565
|
+
this.autocompleteValue = this.autocompleteValue.slice(0, -1);
|
|
566
|
+
} else if (key.name !== "backspace") {
|
|
567
|
+
this.autocompleteValue += key.sequence;
|
|
568
|
+
}
|
|
569
|
+
}
|
|
530
570
|
render();
|
|
531
571
|
}
|
|
532
572
|
}
|
|
@@ -550,6 +590,15 @@ var SelectPrompt = class extends AbstractPrompt {
|
|
|
550
590
|
this.clearLastLine();
|
|
551
591
|
linesToClear--;
|
|
552
592
|
}
|
|
593
|
+
if (this.options.autocomplete) {
|
|
594
|
+
let linesToClear2 = Math.ceil(
|
|
595
|
+
(0, import_wcwidth3.default)(`${SYMBOLS.Pointer} ${this.autocompleteValue}`) / this.stdout.columns
|
|
596
|
+
);
|
|
597
|
+
while (linesToClear2 > 0) {
|
|
598
|
+
this.clearLastLine();
|
|
599
|
+
linesToClear2--;
|
|
600
|
+
}
|
|
601
|
+
}
|
|
553
602
|
}
|
|
554
603
|
if (clearRender) {
|
|
555
604
|
const questionLineCount = Math.ceil(
|
|
@@ -579,11 +628,12 @@ var SelectPrompt = class extends AbstractPrompt {
|
|
|
579
628
|
var import_node_os5 = require("os");
|
|
580
629
|
var import_kleur5 = __toESM(require("kleur"), 1);
|
|
581
630
|
var import_wcwidth4 = __toESM(require("@topcli/wcwidth"), 1);
|
|
582
|
-
var
|
|
631
|
+
var kRequiredChoiceProperties2 = ["label", "value"];
|
|
583
632
|
var MultiselectPrompt = class extends AbstractPrompt {
|
|
584
633
|
#boundExitEvent = () => void 0;
|
|
585
634
|
#boundKeyPressEvent = () => void 0;
|
|
586
635
|
#validators;
|
|
636
|
+
#showHint;
|
|
587
637
|
activeIndex = 0;
|
|
588
638
|
selectedIndexes = [];
|
|
589
639
|
questionMessage;
|
|
@@ -628,7 +678,8 @@ var MultiselectPrompt = class extends AbstractPrompt {
|
|
|
628
678
|
stdout = process.stdout,
|
|
629
679
|
choices,
|
|
630
680
|
preSelectedChoices,
|
|
631
|
-
validators = []
|
|
681
|
+
validators = [],
|
|
682
|
+
showHint = true
|
|
632
683
|
} = options ?? {};
|
|
633
684
|
super(message, stdin, stdout);
|
|
634
685
|
if (!options) {
|
|
@@ -641,11 +692,12 @@ var MultiselectPrompt = class extends AbstractPrompt {
|
|
|
641
692
|
throw new TypeError("Missing required param: choices");
|
|
642
693
|
}
|
|
643
694
|
this.#validators = validators;
|
|
695
|
+
this.#showHint = showHint;
|
|
644
696
|
for (const choice of choices) {
|
|
645
697
|
if (typeof choice === "string") {
|
|
646
698
|
continue;
|
|
647
699
|
}
|
|
648
|
-
for (const prop of
|
|
700
|
+
for (const prop of kRequiredChoiceProperties2) {
|
|
649
701
|
if (!choice[prop]) {
|
|
650
702
|
this.destroy();
|
|
651
703
|
throw new TypeError(`Missing ${prop} for choice ${JSON.stringify(choice)}`);
|
|
@@ -835,14 +887,14 @@ var MultiselectPrompt = class extends AbstractPrompt {
|
|
|
835
887
|
});
|
|
836
888
|
}
|
|
837
889
|
#showQuestion(error = null) {
|
|
838
|
-
let hint = import_kleur5.default.gray(
|
|
890
|
+
let hint = this.#showHint ? import_kleur5.default.gray(
|
|
839
891
|
// eslint-disable-next-line max-len
|
|
840
|
-
`(Press ${import_kleur5.default.bold("<Ctrl+A>")} to toggle all, ${import_kleur5.default.bold("<
|
|
841
|
-
);
|
|
892
|
+
`(Press ${import_kleur5.default.bold("<Ctrl+A>")} to toggle all, ${import_kleur5.default.bold("<Left/Right>")} to toggle, ${import_kleur5.default.bold("<Return>")} to submit)`
|
|
893
|
+
) : "";
|
|
842
894
|
if (error) {
|
|
843
|
-
hint +=
|
|
895
|
+
hint += `${hint.length > 0 ? " " : ""}${import_kleur5.default.red().bold(`[${error}]`)}`;
|
|
844
896
|
}
|
|
845
|
-
this.questionMessage = `${SYMBOLS.QuestionMark} ${import_kleur5.default.bold(this.message)} ${hint}`;
|
|
897
|
+
this.questionMessage = `${SYMBOLS.QuestionMark} ${import_kleur5.default.bold(this.message)}${hint.length > 0 ? ` ${hint}` : ""}`;
|
|
846
898
|
this.write(`${this.questionMessage}${import_node_os5.EOL}`);
|
|
847
899
|
}
|
|
848
900
|
};
|
package/dist/index.d.cts
CHANGED
|
@@ -61,12 +61,15 @@ interface MultiselectOptions extends SharedOptions {
|
|
|
61
61
|
validators?: PromptValidator[];
|
|
62
62
|
autocomplete?: boolean;
|
|
63
63
|
caseSensitive?: boolean;
|
|
64
|
+
showHint?: boolean;
|
|
64
65
|
}
|
|
65
66
|
|
|
66
67
|
interface SelectOptions extends SharedOptions {
|
|
67
68
|
choices: (Choice | string)[];
|
|
68
69
|
maxVisible?: number;
|
|
69
70
|
ignoreValues?: (string | number | boolean)[];
|
|
71
|
+
autocomplete?: boolean;
|
|
72
|
+
caseSensitive?: boolean;
|
|
70
73
|
}
|
|
71
74
|
|
|
72
75
|
declare function question(message: string, options?: QuestionOptions): Promise<string>;
|
package/dist/index.d.ts
CHANGED
|
@@ -61,12 +61,15 @@ interface MultiselectOptions extends SharedOptions {
|
|
|
61
61
|
validators?: PromptValidator[];
|
|
62
62
|
autocomplete?: boolean;
|
|
63
63
|
caseSensitive?: boolean;
|
|
64
|
+
showHint?: boolean;
|
|
64
65
|
}
|
|
65
66
|
|
|
66
67
|
interface SelectOptions extends SharedOptions {
|
|
67
68
|
choices: (Choice | string)[];
|
|
68
69
|
maxVisible?: number;
|
|
69
70
|
ignoreValues?: (string | number | boolean)[];
|
|
71
|
+
autocomplete?: boolean;
|
|
72
|
+
caseSensitive?: boolean;
|
|
70
73
|
}
|
|
71
74
|
|
|
72
75
|
declare function question(message: string, options?: QuestionOptions): Promise<string>;
|
package/dist/index.js
CHANGED
|
@@ -339,10 +339,10 @@ var ConfirmPrompt = class extends AbstractPrompt {
|
|
|
339
339
|
return `${query} ${this.#getHint()}`;
|
|
340
340
|
}
|
|
341
341
|
#onQuestionAnswer() {
|
|
342
|
-
|
|
342
|
+
this.clearLastLine();
|
|
343
343
|
this.stdout.moveCursor(
|
|
344
344
|
-this.stdout.columns,
|
|
345
|
-
-
|
|
345
|
+
-Math.floor(wcwidth2(stripAnsi(this.#getQuestionQuery())) / this.stdout.columns)
|
|
346
346
|
);
|
|
347
347
|
this.stdout.clearScreenDown();
|
|
348
348
|
this.write(`${this.selectedValue ? SYMBOLS.Tick : SYMBOLS.Cross} ${kleur3.bold(this.message)}${EOL3}`);
|
|
@@ -373,18 +373,47 @@ var ConfirmPrompt = class extends AbstractPrompt {
|
|
|
373
373
|
import { EOL as EOL4 } from "os";
|
|
374
374
|
import kleur4 from "kleur";
|
|
375
375
|
import wcwidth3 from "@topcli/wcwidth";
|
|
376
|
+
var kRequiredChoiceProperties = ["label", "value"];
|
|
376
377
|
var SelectPrompt = class extends AbstractPrompt {
|
|
377
378
|
#boundExitEvent = () => void 0;
|
|
378
379
|
#boundKeyPressEvent = () => void 0;
|
|
379
380
|
activeIndex = 0;
|
|
380
|
-
selectedIndexes = [];
|
|
381
381
|
questionMessage;
|
|
382
|
-
|
|
382
|
+
autocompleteValue = "";
|
|
383
383
|
options;
|
|
384
384
|
lastRender;
|
|
385
385
|
get choices() {
|
|
386
386
|
return this.options.choices;
|
|
387
387
|
}
|
|
388
|
+
get filteredChoices() {
|
|
389
|
+
if (!(this.options.autocomplete && this.autocompleteValue.length > 0)) {
|
|
390
|
+
return this.choices;
|
|
391
|
+
}
|
|
392
|
+
const isCaseSensitive = this.options.caseSensitive;
|
|
393
|
+
const autocompleteValue = isCaseSensitive ? this.autocompleteValue : this.autocompleteValue.toLowerCase();
|
|
394
|
+
return this.choices.filter((choice) => this.#filterChoice(choice, autocompleteValue, isCaseSensitive));
|
|
395
|
+
}
|
|
396
|
+
#filterChoice(choice, autocompleteValue, isCaseSensitive = false) {
|
|
397
|
+
const choiceValue = typeof choice === "string" ? isCaseSensitive ? choice : choice.toLowerCase() : isCaseSensitive ? choice.label : choice.label.toLowerCase();
|
|
398
|
+
if (autocompleteValue.includes(" ")) {
|
|
399
|
+
return this.#filterMultipleWords(choiceValue, autocompleteValue, isCaseSensitive);
|
|
400
|
+
}
|
|
401
|
+
return choiceValue.includes(autocompleteValue);
|
|
402
|
+
}
|
|
403
|
+
#filterMultipleWords(choiceValue, autocompleteValue, isCaseSensitive) {
|
|
404
|
+
return autocompleteValue.split(" ").every((word) => {
|
|
405
|
+
const wordValue = isCaseSensitive ? word : word.toLowerCase();
|
|
406
|
+
return choiceValue.includes(wordValue) || choiceValue.includes(autocompleteValue);
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
get longestChoice() {
|
|
410
|
+
return Math.max(...this.filteredChoices.map((choice) => {
|
|
411
|
+
if (typeof choice === "string") {
|
|
412
|
+
return choice.length;
|
|
413
|
+
}
|
|
414
|
+
return choice.label.length;
|
|
415
|
+
}));
|
|
416
|
+
}
|
|
388
417
|
constructor(message, options) {
|
|
389
418
|
const {
|
|
390
419
|
stdin = process.stdin,
|
|
@@ -401,22 +430,20 @@ var SelectPrompt = class extends AbstractPrompt {
|
|
|
401
430
|
this.destroy();
|
|
402
431
|
throw new TypeError("Missing required param: choices");
|
|
403
432
|
}
|
|
404
|
-
|
|
433
|
+
for (const choice of choices) {
|
|
405
434
|
if (typeof choice === "string") {
|
|
406
|
-
|
|
435
|
+
continue;
|
|
407
436
|
}
|
|
408
|
-
const
|
|
409
|
-
for (const prop of kRequiredChoiceProperties2) {
|
|
437
|
+
for (const prop of kRequiredChoiceProperties) {
|
|
410
438
|
if (!choice[prop]) {
|
|
411
439
|
this.destroy();
|
|
412
440
|
throw new TypeError(`Missing ${prop} for choice ${JSON.stringify(choice)}`);
|
|
413
441
|
}
|
|
414
442
|
}
|
|
415
|
-
|
|
416
|
-
}));
|
|
443
|
+
}
|
|
417
444
|
}
|
|
418
445
|
#getFormattedChoice(choiceIndex) {
|
|
419
|
-
const choice = this.
|
|
446
|
+
const choice = this.filteredChoices[choiceIndex];
|
|
420
447
|
if (typeof choice === "string") {
|
|
421
448
|
return { value: choice, label: choice };
|
|
422
449
|
}
|
|
@@ -424,21 +451,24 @@ var SelectPrompt = class extends AbstractPrompt {
|
|
|
424
451
|
}
|
|
425
452
|
#getVisibleChoices() {
|
|
426
453
|
const maxVisible = this.options.maxVisible || 8;
|
|
427
|
-
let startIndex = Math.min(this.
|
|
454
|
+
let startIndex = Math.min(this.filteredChoices.length - maxVisible, this.activeIndex - Math.floor(maxVisible / 2));
|
|
428
455
|
if (startIndex < 0) {
|
|
429
456
|
startIndex = 0;
|
|
430
457
|
}
|
|
431
|
-
const endIndex = Math.min(startIndex + maxVisible, this.
|
|
458
|
+
const endIndex = Math.min(startIndex + maxVisible, this.filteredChoices.length);
|
|
432
459
|
return { startIndex, endIndex };
|
|
433
460
|
}
|
|
434
461
|
#showChoices() {
|
|
435
462
|
const { startIndex, endIndex } = this.#getVisibleChoices();
|
|
436
463
|
this.lastRender = { startIndex, endIndex };
|
|
464
|
+
if (this.options.autocomplete) {
|
|
465
|
+
this.write(`${SYMBOLS.Pointer} ${this.autocompleteValue}${EOL4}`);
|
|
466
|
+
}
|
|
437
467
|
for (let choiceIndex = startIndex; choiceIndex < endIndex; choiceIndex++) {
|
|
438
468
|
const choice = this.#getFormattedChoice(choiceIndex);
|
|
439
469
|
const isChoiceSelected = choiceIndex === this.activeIndex;
|
|
440
470
|
const showPreviousChoicesArrow = startIndex > 0 && choiceIndex === startIndex;
|
|
441
|
-
const showNextChoicesArrow = endIndex < this.
|
|
471
|
+
const showNextChoicesArrow = endIndex < this.filteredChoices.length && choiceIndex === endIndex - 1;
|
|
442
472
|
let prefixArrow = " ";
|
|
443
473
|
if (showPreviousChoicesArrow) {
|
|
444
474
|
prefixArrow = SYMBOLS.Previous;
|
|
@@ -447,16 +477,17 @@ var SelectPrompt = class extends AbstractPrompt {
|
|
|
447
477
|
}
|
|
448
478
|
const prefix = `${prefixArrow}${isChoiceSelected ? `${SYMBOLS.Pointer} ` : " "}`;
|
|
449
479
|
const formattedLabel = choice.label.padEnd(
|
|
450
|
-
this.
|
|
480
|
+
this.longestChoice < 10 ? this.longestChoice : 0
|
|
451
481
|
);
|
|
452
482
|
const formattedDescription = choice.description ? ` - ${choice.description}` : "";
|
|
453
483
|
const color = isChoiceSelected ? kleur4.white().bold : kleur4.gray;
|
|
454
|
-
const str =
|
|
484
|
+
const str = `${prefix}${color(`${formattedLabel}${formattedDescription}`)}${EOL4}`;
|
|
455
485
|
this.write(str);
|
|
456
486
|
}
|
|
457
487
|
}
|
|
458
488
|
#showAnsweredQuestion(choice) {
|
|
459
|
-
const
|
|
489
|
+
const symbolPrefix = choice === "" ? SYMBOLS.Cross : SYMBOLS.Tick;
|
|
490
|
+
const prefix = `${symbolPrefix} ${kleur4.bold(this.message)} ${SYMBOLS.Pointer}`;
|
|
460
491
|
const formattedChoice = kleur4.yellow(typeof choice === "string" ? choice : choice.label);
|
|
461
492
|
this.write(`${prefix} ${formattedChoice}${EOL4}`);
|
|
462
493
|
}
|
|
@@ -469,17 +500,18 @@ var SelectPrompt = class extends AbstractPrompt {
|
|
|
469
500
|
#onKeypress(...args) {
|
|
470
501
|
const [resolve, render, , key] = args;
|
|
471
502
|
if (key.name === "up") {
|
|
472
|
-
this.activeIndex = this.activeIndex === 0 ? this.
|
|
503
|
+
this.activeIndex = this.activeIndex === 0 ? this.filteredChoices.length - 1 : this.activeIndex - 1;
|
|
473
504
|
render();
|
|
474
505
|
} else if (key.name === "down") {
|
|
475
|
-
this.activeIndex = this.activeIndex === this.
|
|
506
|
+
this.activeIndex = this.activeIndex === this.filteredChoices.length - 1 ? 0 : this.activeIndex + 1;
|
|
476
507
|
render();
|
|
477
508
|
} else if (key.name === "return") {
|
|
509
|
+
const choice = this.filteredChoices[this.activeIndex] || "";
|
|
510
|
+
const label = typeof choice === "string" ? choice : choice.label;
|
|
511
|
+
const value = typeof choice === "string" ? choice : choice.value;
|
|
478
512
|
render({ clearRender: true });
|
|
479
|
-
const currentChoice = this.choices[this.activeIndex];
|
|
480
|
-
const value = typeof currentChoice === "string" ? currentChoice : currentChoice.value;
|
|
481
513
|
if (!this.options.ignoreValues?.includes(value)) {
|
|
482
|
-
this.#showAnsweredQuestion(
|
|
514
|
+
this.#showAnsweredQuestion(label);
|
|
483
515
|
}
|
|
484
516
|
this.write(SYMBOLS.ShowCursor);
|
|
485
517
|
this.destroy();
|
|
@@ -487,6 +519,14 @@ var SelectPrompt = class extends AbstractPrompt {
|
|
|
487
519
|
process.off("exit", this.#boundExitEvent);
|
|
488
520
|
resolve(value);
|
|
489
521
|
} else {
|
|
522
|
+
if (!key.ctrl && this.options.autocomplete) {
|
|
523
|
+
this.activeIndex = 0;
|
|
524
|
+
if (key.name === "backspace" && this.autocompleteValue.length > 0) {
|
|
525
|
+
this.autocompleteValue = this.autocompleteValue.slice(0, -1);
|
|
526
|
+
} else if (key.name !== "backspace") {
|
|
527
|
+
this.autocompleteValue += key.sequence;
|
|
528
|
+
}
|
|
529
|
+
}
|
|
490
530
|
render();
|
|
491
531
|
}
|
|
492
532
|
}
|
|
@@ -510,6 +550,15 @@ var SelectPrompt = class extends AbstractPrompt {
|
|
|
510
550
|
this.clearLastLine();
|
|
511
551
|
linesToClear--;
|
|
512
552
|
}
|
|
553
|
+
if (this.options.autocomplete) {
|
|
554
|
+
let linesToClear2 = Math.ceil(
|
|
555
|
+
wcwidth3(`${SYMBOLS.Pointer} ${this.autocompleteValue}`) / this.stdout.columns
|
|
556
|
+
);
|
|
557
|
+
while (linesToClear2 > 0) {
|
|
558
|
+
this.clearLastLine();
|
|
559
|
+
linesToClear2--;
|
|
560
|
+
}
|
|
561
|
+
}
|
|
513
562
|
}
|
|
514
563
|
if (clearRender) {
|
|
515
564
|
const questionLineCount = Math.ceil(
|
|
@@ -539,11 +588,12 @@ var SelectPrompt = class extends AbstractPrompt {
|
|
|
539
588
|
import { EOL as EOL5 } from "os";
|
|
540
589
|
import kleur5 from "kleur";
|
|
541
590
|
import wcwidth4 from "@topcli/wcwidth";
|
|
542
|
-
var
|
|
591
|
+
var kRequiredChoiceProperties2 = ["label", "value"];
|
|
543
592
|
var MultiselectPrompt = class extends AbstractPrompt {
|
|
544
593
|
#boundExitEvent = () => void 0;
|
|
545
594
|
#boundKeyPressEvent = () => void 0;
|
|
546
595
|
#validators;
|
|
596
|
+
#showHint;
|
|
547
597
|
activeIndex = 0;
|
|
548
598
|
selectedIndexes = [];
|
|
549
599
|
questionMessage;
|
|
@@ -588,7 +638,8 @@ var MultiselectPrompt = class extends AbstractPrompt {
|
|
|
588
638
|
stdout = process.stdout,
|
|
589
639
|
choices,
|
|
590
640
|
preSelectedChoices,
|
|
591
|
-
validators = []
|
|
641
|
+
validators = [],
|
|
642
|
+
showHint = true
|
|
592
643
|
} = options ?? {};
|
|
593
644
|
super(message, stdin, stdout);
|
|
594
645
|
if (!options) {
|
|
@@ -601,11 +652,12 @@ var MultiselectPrompt = class extends AbstractPrompt {
|
|
|
601
652
|
throw new TypeError("Missing required param: choices");
|
|
602
653
|
}
|
|
603
654
|
this.#validators = validators;
|
|
655
|
+
this.#showHint = showHint;
|
|
604
656
|
for (const choice of choices) {
|
|
605
657
|
if (typeof choice === "string") {
|
|
606
658
|
continue;
|
|
607
659
|
}
|
|
608
|
-
for (const prop of
|
|
660
|
+
for (const prop of kRequiredChoiceProperties2) {
|
|
609
661
|
if (!choice[prop]) {
|
|
610
662
|
this.destroy();
|
|
611
663
|
throw new TypeError(`Missing ${prop} for choice ${JSON.stringify(choice)}`);
|
|
@@ -795,14 +847,14 @@ var MultiselectPrompt = class extends AbstractPrompt {
|
|
|
795
847
|
});
|
|
796
848
|
}
|
|
797
849
|
#showQuestion(error = null) {
|
|
798
|
-
let hint = kleur5.gray(
|
|
850
|
+
let hint = this.#showHint ? kleur5.gray(
|
|
799
851
|
// eslint-disable-next-line max-len
|
|
800
|
-
`(Press ${kleur5.bold("<Ctrl+A>")} to toggle all, ${kleur5.bold("<
|
|
801
|
-
);
|
|
852
|
+
`(Press ${kleur5.bold("<Ctrl+A>")} to toggle all, ${kleur5.bold("<Left/Right>")} to toggle, ${kleur5.bold("<Return>")} to submit)`
|
|
853
|
+
) : "";
|
|
802
854
|
if (error) {
|
|
803
|
-
hint +=
|
|
855
|
+
hint += `${hint.length > 0 ? " " : ""}${kleur5.red().bold(`[${error}]`)}`;
|
|
804
856
|
}
|
|
805
|
-
this.questionMessage = `${SYMBOLS.QuestionMark} ${kleur5.bold(this.message)} ${hint}`;
|
|
857
|
+
this.questionMessage = `${SYMBOLS.QuestionMark} ${kleur5.bold(this.message)}${hint.length > 0 ? ` ${hint}` : ""}`;
|
|
806
858
|
this.write(`${this.questionMessage}${EOL5}`);
|
|
807
859
|
}
|
|
808
860
|
};
|