You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
setup-go/node_modules/prompts/lib/elements/multiselect.js

239 lines
6.0 KiB
JavaScript

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.

'use strict';
const color = require('kleur');
const { cursor } = require('sisteransi');
const Prompt = require('./prompt');
const { clear, figures, style } = require('../util');
/**
* MultiselectPrompt Base Element
* @param {Object} opts Options
* @param {String} opts.message Message
* @param {Array} opts.choices Array of choice objects
* @param {String} [opts.hint] Hint to display
* @param {String} [opts.warn] Hint shown for disabled choices
* @param {Number} [opts.max] Max choices
* @param {Number} [opts.cursor=0] Cursor start position
* @param {Stream} [opts.stdin] The Readable stream to listen to
* @param {Stream} [opts.stdout] The Writable stream to write readline data to
*/
class MultiselectPrompt extends Prompt {
constructor(opts={}) {
super(opts);
this.msg = opts.message;
this.cursor = opts.cursor || 0;
this.scrollIndex = opts.cursor || 0;
this.hint = opts.hint || '';
this.warn = opts.warn || '- This option is disabled -';
this.minSelected = opts.min;
this.showMinError = false;
this.maxChoices = opts.max;
this.value = opts.choices.map((ch, idx) => {
if (typeof ch === 'string')
ch = {title: ch, value: idx};
return {
title: ch && (ch.title || ch.value || ch),
value: ch && (ch.value || idx),
selected: ch && ch.selected,
disabled: ch && ch.disabled
};
});
this.clear = clear('');
if (!opts.overrideRender) {
this.render();
}
}
reset() {
this.value.map(v => !v.selected);
this.cursor = 0;
this.fire();
this.render();
}
selected() {
return this.value.filter(v => v.selected);
}
abort() {
this.done = this.aborted = true;
this.fire();
this.render();
this.out.write('\n');
this.close();
}
submit() {
const selected = this.value
.filter(e => e.selected);
if (this.minSelected && selected.length < this.minSelected) {
this.showMinError = true;
this.render();
} else {
this.done = true;
this.aborted = false;
this.fire();
this.render();
this.out.write('\n');
this.close();
}
}
first() {
this.cursor = 0;
this.render();
}
last() {
this.cursor = this.value.length - 1;
this.render();
}
next() {
this.cursor = (this.cursor + 1) % this.value.length;
this.render();
}
up() {
if (this.cursor === 0) {
this.cursor = this.value.length - 1;
} else {
this.cursor--;
}
this.render();
}
down() {
if (this.cursor === this.value.length - 1) {
this.cursor = 0;
} else {
this.cursor++;
}
this.render();
}
left() {
this.value[this.cursor].selected = false;
this.render();
}
right() {
if (this.value.filter(e => e.selected).length >= this.maxChoices) return this.bell();
this.value[this.cursor].selected = true;
this.render();
}
handleSpaceToggle() {
const v = this.value[this.cursor];
if (v.selected) {
v.selected = false;
this.render();
} else if (v.disabled || this.value.filter(e => e.selected).length >= this.maxChoices) {
return this.bell();
} else {
v.selected = true;
this.render();
}
}
_(c, key) {
if (c === ' ') {
this.handleSpaceToggle();
} else {
return this.bell();
}
}
renderInstructions() {
return `
Instructions:
${figures.arrowUp}/${figures.arrowDown}: Highlight option
${figures.arrowLeft}/${figures.arrowRight}/[space]: Toggle selection
enter/return: Complete answer
`
}
renderOption(cursor, v, i) {
let title;
if (v.disabled) title = cursor === i ? color.gray().underline(v.title) : color.strikethrough().gray(v.title);
else title = cursor === i ? color.cyan().underline(v.title) : v.title;
return (v.selected ? color.green(figures.radioOn) : figures.radioOff) + ' ' + title
}
// shared with autocompleteMultiselect
paginateOptions(options) {
const c = this.cursor;
let styledOptions = options.map((v, i) => this.renderOption(c, v, i));
const numOfOptionsToRender = 10; // if needed, can add an option to change this.
let scopedOptions = styledOptions;
let hint = '';
if (styledOptions.length === 0) {
return color.red('No matches for this query.');
} else if (styledOptions.length > numOfOptionsToRender) {
let startIndex = c - (numOfOptionsToRender / 2);
let endIndex = c + (numOfOptionsToRender / 2);
if (startIndex < 0) {
startIndex = 0;
endIndex = numOfOptionsToRender;
} else if (endIndex > options.length) {
endIndex = options.length;
startIndex = endIndex - numOfOptionsToRender;
}
scopedOptions = styledOptions.slice(startIndex, endIndex);
hint = color.dim('(Move up and down to reveal more choices)');
}
return '\n' + scopedOptions.join('\n') + '\n' + hint;
}
// shared with autocomleteMultiselect
renderOptions(options) {
if (!this.done) {
return this.paginateOptions(options);
}
return '';
}
renderDoneOrInstructions() {
if (this.done) {
const selected = this.value
.filter(e => e.selected)
.map(v => v.title)
.join(', ');
return selected;
}
const output = [color.gray(this.hint), this.renderInstructions()];
if (this.value[this.cursor].disabled) {
output.push(color.yellow(this.warn));
}
return output.join(' ');
}
render() {
if (this.closed) return;
if (this.firstRender) this.out.write(cursor.hide);
super.render();
// print prompt
let prompt = [
style.symbol(this.done, this.aborted),
color.bold(this.msg),
style.delimiter(false),
this.renderDoneOrInstructions()
].join(' ');
if (this.showMinError) {
prompt += color.red(`You must select a minimum of ${this.minSelected} choices.`);
this.showMinError = false;
}
prompt += this.renderOptions(this.value);
this.out.write(this.clear + prompt);
this.clear = clear(prompt);
}
}
module.exports = MultiselectPrompt;