prompts
Advanced tools
Comparing version 2.2.1 to 2.3.0
@@ -18,3 +18,5 @@ 'use strict'; | ||
clear = _require2.clear, | ||
wrap = _require2.wrap; | ||
figures = _require2.figures, | ||
wrap = _require2.wrap, | ||
entriesToDisplay = _require2.entriesToDisplay; | ||
@@ -58,4 +60,3 @@ const getVal = (arr, i) => arr[i] && (arr[i].value || arr[i].title || arr[i]); | ||
this.fallback = opts.fallback || this.initial; | ||
this.suggestions = [[]]; | ||
this.page = 0; | ||
this.suggestions = []; | ||
this.input = ''; | ||
@@ -89,3 +90,3 @@ this.limit = opts.limit || 10; | ||
this.select = i; | ||
if (this.suggestions[this.page].length > 0) this.value = getVal(this.suggestions[this.page], i);else this.value = this.fallback.value; | ||
if (this.suggestions.length > 0) this.value = getVal(this.suggestions, i);else this.value = this.fallback.value; | ||
this.fire(); | ||
@@ -106,15 +107,4 @@ } | ||
description: s.description | ||
})).reduce((arr, sug) => { | ||
if (arr[arr.length - 1].length < _this.limit) arr[arr.length - 1].push(sug);else arr.push([sug]); | ||
return arr; | ||
}, [[]]); | ||
})); | ||
_this.completing = false; | ||
if (!_this.suggestions[_this.page]) _this.page = 0; // if (!this.suggestions.length && this.fallback) { | ||
// const index = getIndex(this.choices, this.fallback); | ||
// this.suggestions = [[]]; | ||
// if (index !== undefined) | ||
// this.suggestions[0].push({ title: getTitle(this.choices, index), value: getVal(this.choices, index) }); | ||
// this.isFallback = true; | ||
// } | ||
const l = Math.max(suggestions.length - 1, 0); | ||
@@ -155,3 +145,2 @@ | ||
_(c, key) { | ||
// TODO on ctrl+# go to page # | ||
let s1 = this.input.slice(0, this.cursor); | ||
@@ -190,3 +179,3 @@ let s2 = this.input.slice(this.cursor); | ||
last() { | ||
this.moveSelect(this.suggestions[this.page].length - 1); | ||
this.moveSelect(this.suggestions.length - 1); | ||
this.render(); | ||
@@ -202,3 +191,3 @@ } | ||
down() { | ||
if (this.select >= this.suggestions[this.page].length - 1) return this.bell(); | ||
if (this.select >= this.suggestions.length - 1) return this.bell(); | ||
this.moveSelect(this.select + 1); | ||
@@ -209,4 +198,3 @@ this.render(); | ||
next() { | ||
if (this.select === this.suggestions[this.page].length - 1) { | ||
this.page = (this.page + 1) % this.suggestions.length; | ||
if (this.select === this.suggestions.length - 1) { | ||
this.moveSelect(0); | ||
@@ -219,5 +207,3 @@ } else this.moveSelect(this.select + 1); | ||
nextPage() { | ||
if (this.page >= this.suggestions.length - 1) return this.bell(); | ||
this.page++; | ||
this.moveSelect(0); | ||
this.moveSelect(Math.min(this.select + this.limit, this.suggestions.length - 1)); | ||
this.render(); | ||
@@ -227,5 +213,3 @@ } | ||
prevPage() { | ||
if (this.page <= 0) return this.bell(); | ||
this.page--; | ||
this.moveSelect(0); | ||
this.moveSelect(Math.max(this.select - this.limit, 0)); | ||
this.render(); | ||
@@ -246,21 +230,20 @@ } | ||
renderOption(v, hovered) { | ||
let desc, | ||
title = v.title; | ||
renderOption(v, hovered, isStart, isEnd) { | ||
let desc; | ||
let prefix = isStart ? figures.arrowUp : isEnd ? figures.arrowDown : ' '; | ||
let title = hovered ? color.cyan().underline(v.title) : v.title; | ||
prefix = (hovered ? color.cyan(figures.pointer) + ' ' : ' ') + prefix; | ||
if (hovered) { | ||
title = color.cyan(v.title); | ||
if (v.description) { | ||
desc = ` - ${v.description}`; | ||
if (v.description) { | ||
desc = ` - ${v.description}`; | ||
if (title.length + desc.length >= this.out.columns || v.description.split(/\r?\n/).length > 1) { | ||
desc = '\n' + wrap(v.description, { | ||
width: this.out.columns | ||
}); | ||
} | ||
if (prefix.length + title.length + desc.length >= this.out.columns || v.description.split(/\r?\n/).length > 1) { | ||
desc = '\n' + wrap(v.description, { | ||
margin: 3, | ||
width: this.out.columns | ||
}); | ||
} | ||
} | ||
return title + color.gray(desc || ''); | ||
return prefix + ' ' + title + color.gray(desc || ''); | ||
} | ||
@@ -270,13 +253,14 @@ | ||
if (this.closed) return; | ||
if (!this.firstRender) this.out.write(clear(this.outputText)); | ||
if (this.firstRender) this.out.write(cursor.hide);else this.out.write(clear(this.outputText)); | ||
super.render(); | ||
this.outputText = [color.bold(style.symbol(this.done, this.aborted)), color.bold(this.msg), style.delimiter(this.completing), this.done && this.suggestions[this.page][this.select] ? this.suggestions[this.page][this.select].title : this.rendered = this.transform.render(this.input)].join(' '); | ||
let _entriesToDisplay = entriesToDisplay(this.select, this.choices.length, this.limit), | ||
startIndex = _entriesToDisplay.startIndex, | ||
endIndex = _entriesToDisplay.endIndex; | ||
this.outputText = [color.bold(style.symbol(this.done, this.aborted)), color.bold(this.msg), style.delimiter(this.completing), this.done && this.suggestions[this.select] ? this.suggestions[this.select].title : this.rendered = this.transform.render(this.input)].join(' '); | ||
if (!this.done) { | ||
const suggestions = this.suggestions[this.page].map((item, i) => this.renderOption(item, this.select === i)).join('\n'); | ||
const suggestions = this.suggestions.slice(startIndex, endIndex).map((item, i) => this.renderOption(item, this.select === i + startIndex, i === 0 && startIndex > 0, i + startIndex === endIndex - 1 && endIndex < this.choices.length)).join('\n'); | ||
this.outputText += `\n` + (suggestions || color.gray(this.fallback.title)); | ||
if (this.suggestions[this.page].length > 1) { | ||
this.outputText += color.blue(`\nPage ${this.page + 1}/${this.suggestions.length}`); | ||
} | ||
} | ||
@@ -283,0 +267,0 @@ |
@@ -140,3 +140,8 @@ 'use strict'; | ||
renderInstructions() { | ||
return ` | ||
if (this.instructions === undefined || this.instructions) { | ||
if (typeof this.instructions === 'string') { | ||
return this.instructions; | ||
} | ||
return ` | ||
Instructions: | ||
@@ -147,3 +152,6 @@ ${figures.arrowUp}/${figures.arrowDown}: Highlight option | ||
enter/return: Complete answer | ||
`; | ||
`; | ||
} | ||
return ''; | ||
} | ||
@@ -150,0 +158,0 @@ |
@@ -15,3 +15,3 @@ 'use strict'; | ||
wrap = _require2.wrap, | ||
strip = _require2.strip; | ||
entriesToDisplay = _require2.entriesToDisplay; | ||
/** | ||
@@ -26,2 +26,3 @@ * MultiselectPrompt Base Element | ||
* @param {Number} [opts.cursor=0] Cursor start position | ||
* @param {Number} [opts.optionsPerPage=10] Max options to display at once | ||
* @param {Stream} [opts.stdin] The Readable stream to listen to | ||
@@ -44,2 +45,3 @@ * @param {Stream} [opts.stdout] The Writable stream to write readline data to | ||
this.instructions = opts.instructions; | ||
this.optionsPerPage = opts.optionsPerPage || 10; | ||
this.value = opts.choices.map((ch, idx) => { | ||
@@ -53,3 +55,3 @@ if (typeof ch === 'string') ch = { | ||
description: ch && ch.description, | ||
value: ch && (ch.value || idx), | ||
value: ch && (ch.value === undefined ? idx : ch.value), | ||
selected: ch && ch.selected, | ||
@@ -193,4 +195,4 @@ disabled: ch && ch.disabled | ||
renderOption(cursor, v, i) { | ||
const prefix = (v.selected ? color.green(figures.radioOn) : figures.radioOff) + ' '; | ||
renderOption(cursor, v, i, arrowIndicator) { | ||
const prefix = (v.selected ? color.green(figures.radioOn) : figures.radioOff) + ' ' + arrowIndicator + ' '; | ||
let title, desc; | ||
@@ -220,28 +222,26 @@ | ||
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. | ||
if (options.length === 0) { | ||
return color.red('No matches for this query.'); | ||
} | ||
let scopedOptions = styledOptions; | ||
let hint = ''; | ||
let _entriesToDisplay = entriesToDisplay(this.cursor, options.length, this.optionsPerPage), | ||
startIndex = _entriesToDisplay.startIndex, | ||
endIndex = _entriesToDisplay.endIndex; | ||
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; | ||
let prefix, | ||
styledOptions = []; | ||
if (startIndex < 0) { | ||
startIndex = 0; | ||
endIndex = numOfOptionsToRender; | ||
} else if (endIndex > options.length) { | ||
endIndex = options.length; | ||
startIndex = endIndex - numOfOptionsToRender; | ||
for (let i = startIndex; i < endIndex; i++) { | ||
if (i === startIndex && startIndex > 0) { | ||
prefix = figures.arrowUp; | ||
} else if (i === endIndex - 1 && endIndex < options.length) { | ||
prefix = figures.arrowDown; | ||
} else { | ||
prefix = ' '; | ||
} | ||
scopedOptions = styledOptions.slice(startIndex, endIndex); | ||
hint = color.dim('(Move up and down to reveal more choices)'); | ||
styledOptions.push(this.renderOption(this.cursor, options[i], i, prefix)); | ||
} | ||
return '\n' + scopedOptions.join('\n') + '\n' + hint; | ||
return '\n' + styledOptions.join('\n'); | ||
} // shared with autocomleteMultiselect | ||
@@ -260,4 +260,3 @@ | ||
if (this.done) { | ||
const selected = this.value.filter(e => e.selected).map(v => v.title).join(', '); | ||
return selected; | ||
return this.value.filter(e => e.selected).map(v => v.title).join(', '); | ||
} | ||
@@ -264,0 +263,0 @@ |
@@ -162,2 +162,7 @@ "use strict"; | ||
this.typed = ``; | ||
if (this.value === '') { | ||
this.value = this.min - this.inc; | ||
} | ||
if (this.value >= this.max) return this.bell(); | ||
@@ -172,2 +177,7 @@ this.value += this.inc; | ||
this.typed = ``; | ||
if (this.value === '') { | ||
this.value = this.min + this.inc; | ||
} | ||
if (this.value <= this.min) return this.bell(); | ||
@@ -184,2 +194,7 @@ this.value -= this.inc; | ||
this.value = this.parse(val = val.slice(0, -1)) || ``; | ||
if (this.value !== '' && this.value < this.min) { | ||
this.value = this.min; | ||
} | ||
this.color = `cyan`; | ||
@@ -186,0 +201,0 @@ this.fire(); |
@@ -26,4 +26,4 @@ 'use strict'; | ||
this.firstRender = true; | ||
this.in = opts.in || process.stdin; | ||
this.out = opts.out || process.stdout; | ||
this.in = opts.stdin || process.stdin; | ||
this.out = opts.stdout || process.stdout; | ||
@@ -30,0 +30,0 @@ this.onRender = (opts.onRender || (() => void 0)).bind(this); |
@@ -11,3 +11,4 @@ 'use strict'; | ||
figures = _require.figures, | ||
wrap = _require.wrap; | ||
wrap = _require.wrap, | ||
entriesToDisplay = _require.entriesToDisplay; | ||
@@ -25,2 +26,3 @@ const _require2 = require('sisteransi'), | ||
* @param {Stream} [opts.stdout] The Writable stream to write readline data to | ||
* @param {Number} [opts.optionsPerPage=10] Max options to display at once | ||
*/ | ||
@@ -43,3 +45,3 @@ | ||
title: ch && (ch.title || ch.value || ch), | ||
value: ch && (ch.value || idx), | ||
value: ch && (ch.value === undefined ? idx : ch.value), | ||
description: ch && ch.description, | ||
@@ -50,2 +52,3 @@ selected: ch && ch.selected, | ||
}); | ||
this.optionsPerPage = opts.optionsPerPage || 10; | ||
this.value = (this.choices[this.cursor] || {}).value; | ||
@@ -125,18 +128,34 @@ this.clear = clear(''); | ||
if (this.firstRender) this.out.write(cursor.hide);else this.out.write(clear(this.outputText)); | ||
super.render(); // Print prompt | ||
super.render(); | ||
let _entriesToDisplay = entriesToDisplay(this.cursor, this.choices.length, this.optionsPerPage), | ||
startIndex = _entriesToDisplay.startIndex, | ||
endIndex = _entriesToDisplay.endIndex; // Print prompt | ||
this.outputText = [style.symbol(this.done, this.aborted), color.bold(this.msg), style.delimiter(false), this.done ? this.selection.title : this.selection.disabled ? color.yellow(this.warn) : color.gray(this.hint)].join(' '); // Print choices | ||
if (!this.done) { | ||
this.outputText += '\n' + this.choices.map((v, i) => { | ||
this.outputText += '\n'; | ||
for (let i = startIndex; i < endIndex; i++) { | ||
let title, | ||
prefix, | ||
desc = ''; | ||
desc = '', | ||
v = this.choices[i]; // Determine whether to display "more choices" indicators | ||
if (i === startIndex && startIndex > 0) { | ||
prefix = figures.arrowUp; | ||
} else if (i === endIndex - 1 && endIndex < this.choices.length) { | ||
prefix = figures.arrowDown; | ||
} else { | ||
prefix = ' '; | ||
} | ||
if (v.disabled) { | ||
title = this.cursor === i ? color.gray().underline(v.title) : color.strikethrough().gray(v.title); | ||
prefix = this.cursor === i ? color.bold().gray(figures.pointer) + ' ' : ' '; | ||
prefix = (this.cursor === i ? color.bold().gray(figures.pointer) + ' ' : ' ') + prefix; | ||
} else { | ||
title = this.cursor === i ? color.cyan().underline(v.title) : v.title; | ||
prefix = this.cursor === i ? color.cyan(figures.pointer) + ' ' : ' '; | ||
prefix = (this.cursor === i ? color.cyan(figures.pointer) + ' ' : ' ') + prefix; | ||
@@ -155,4 +174,4 @@ if (v.description && this.cursor === i) { | ||
return `${prefix} ${title}${color.gray(desc)}`; | ||
}).join('\n'); | ||
this.outputText += `${prefix} ${title}${color.gray(desc)}\n`; | ||
} | ||
} | ||
@@ -159,0 +178,0 @@ |
@@ -15,3 +15,3 @@ 'use strict'; | ||
const passOn = ['suggest', 'format', 'onState', 'validate', 'onRender']; | ||
const passOn = ['suggest', 'format', 'onState', 'validate', 'onRender', 'type']; | ||
@@ -40,3 +40,3 @@ const noop = () => {}; | ||
questions = [].concat(questions); | ||
let answer, question, quit, name, type; | ||
let answer, question, quit, name, type, lastPrompt; | ||
@@ -70,9 +70,18 @@ const getFormattedAnswer = | ||
// if property is a function, invoke it unless it's a special function | ||
// evaluate type first and skip if type is a falsy value | ||
if (typeof type === 'function') { | ||
type = yield type(answer, _objectSpread({}, answers), question); | ||
question['type'] = type; | ||
} | ||
if (!type) continue; // if property is a function, invoke it unless it's a special function | ||
for (let key in question) { | ||
if (passOn.includes(key)) continue; | ||
let value = question[key]; | ||
question[key] = typeof value === 'function' ? yield value(answer, _objectSpread({}, answers), question) : value; | ||
question[key] = typeof value === 'function' ? yield value(answer, _objectSpread({}, answers), lastPrompt) : value; | ||
} | ||
lastPrompt = question; | ||
if (typeof question.message !== 'string') { | ||
@@ -86,4 +95,2 @@ throw new Error('prompt message is required'); | ||
type = _question2.type; | ||
// skip if type is a falsy value | ||
if (!type) continue; | ||
@@ -90,0 +97,0 @@ if (prompts[type] === void 0) { |
@@ -10,3 +10,4 @@ 'use strict'; | ||
lines: require('./lines'), | ||
wrap: require('./wrap') | ||
wrap: require('./wrap'), | ||
entriesToDisplay: require('./entriesToDisplay') | ||
}; |
@@ -6,3 +6,3 @@ 'use strict'; | ||
const { erase, cursor } = require('sisteransi'); | ||
const { style, clear, wrap } = require('../util'); | ||
const { style, clear, figures, wrap, entriesToDisplay } = require('../util'); | ||
@@ -43,4 +43,3 @@ const getVal = (arr, i) => arr[i] && (arr[i].value || arr[i].title || arr[i]); | ||
this.fallback = opts.fallback || this.initial; | ||
this.suggestions = [[]]; | ||
this.page = 0; | ||
this.suggestions = []; | ||
this.input = ''; | ||
@@ -73,4 +72,4 @@ this.limit = opts.limit || 10; | ||
this.select = i; | ||
if (this.suggestions[this.page].length > 0) | ||
this.value = getVal(this.suggestions[this.page], i); | ||
if (this.suggestions.length > 0) | ||
this.value = getVal(this.suggestions, i); | ||
else this.value = this.fallback.value; | ||
@@ -86,21 +85,4 @@ this.fire(); | ||
this.suggestions = suggestions | ||
.map((s, i, arr) => ({ title: getTitle(arr, i), value: getVal(arr, i), description: s.description })) | ||
.reduce((arr, sug) => { | ||
if (arr[arr.length - 1].length < this.limit) | ||
arr[arr.length - 1].push(sug); | ||
else arr.push([sug]); | ||
return arr; | ||
}, [[]]); | ||
.map((s, i, arr) => ({ title: getTitle(arr, i), value: getVal(arr, i), description: s.description })); | ||
this.completing = false; | ||
if (!this.suggestions[this.page]) | ||
this.page = 0; | ||
// if (!this.suggestions.length && this.fallback) { | ||
// const index = getIndex(this.choices, this.fallback); | ||
// this.suggestions = [[]]; | ||
// if (index !== undefined) | ||
// this.suggestions[0].push({ title: getTitle(this.choices, index), value: getVal(this.choices, index) }); | ||
// this.isFallback = true; | ||
// } | ||
const l = Math.max(suggestions.length - 1, 0); | ||
@@ -138,3 +120,3 @@ this.moveSelect(Math.min(l, this.select)); | ||
_(c, key) { // TODO on ctrl+# go to page # | ||
_(c, key) { | ||
let s1 = this.input.slice(0, this.cursor); | ||
@@ -159,8 +141,8 @@ let s2 = this.input.slice(this.cursor); | ||
deleteForward() { | ||
if(this.cursor*this.scale >= this.rendered.length) return this.bell(); | ||
let s1 = this.input.slice(0, this.cursor); | ||
let s2 = this.input.slice(this.cursor+1); | ||
this.input = `${s1}${s2}`; | ||
this.complete(this.render); | ||
this.render(); | ||
if(this.cursor*this.scale >= this.rendered.length) return this.bell(); | ||
let s1 = this.input.slice(0, this.cursor); | ||
let s2 = this.input.slice(this.cursor+1); | ||
this.input = `${s1}${s2}`; | ||
this.complete(this.render); | ||
this.render(); | ||
} | ||
@@ -174,3 +156,3 @@ | ||
last() { | ||
this.moveSelect(this.suggestions[this.page].length - 1); | ||
this.moveSelect(this.suggestions.length - 1); | ||
this.render(); | ||
@@ -186,3 +168,3 @@ } | ||
down() { | ||
if (this.select >= this.suggestions[this.page].length - 1) return this.bell(); | ||
if (this.select >= this.suggestions.length - 1) return this.bell(); | ||
this.moveSelect(this.select + 1); | ||
@@ -193,4 +175,3 @@ this.render(); | ||
next() { | ||
if (this.select === this.suggestions[this.page].length - 1) { | ||
this.page = (this.page + 1) % this.suggestions.length; | ||
if (this.select === this.suggestions.length - 1) { | ||
this.moveSelect(0); | ||
@@ -202,6 +183,3 @@ } else this.moveSelect(this.select + 1); | ||
nextPage() { | ||
if (this.page >= this.suggestions.length - 1) | ||
return this.bell(); | ||
this.page++; | ||
this.moveSelect(0); | ||
this.moveSelect(Math.min(this.select + this.limit, this.suggestions.length - 1)); | ||
this.render(); | ||
@@ -211,6 +189,3 @@ } | ||
prevPage() { | ||
if (this.page <= 0) | ||
return this.bell(); | ||
this.page--; | ||
this.moveSelect(0); | ||
this.moveSelect(Math.max(this.select - this.limit, 0)); | ||
this.render(); | ||
@@ -231,15 +206,15 @@ } | ||
renderOption(v, hovered) { | ||
let desc, title = v.title; | ||
if (hovered) { | ||
title = color.cyan(v.title); | ||
if (v.description) { | ||
desc = ` - ${v.description}`; | ||
if (title.length + desc.length >= this.out.columns | ||
|| v.description.split(/\r?\n/).length > 1) { | ||
desc = '\n' + wrap(v.description, { width: this.out.columns }) | ||
} | ||
renderOption(v, hovered, isStart, isEnd) { | ||
let desc; | ||
let prefix = isStart ? figures.arrowUp : isEnd ? figures.arrowDown : ' '; | ||
let title = hovered ? color.cyan().underline(v.title) : v.title; | ||
prefix = (hovered ? color.cyan(figures.pointer) + ' ' : ' ') + prefix; | ||
if (v.description) { | ||
desc = ` - ${v.description}`; | ||
if (prefix.length + title.length + desc.length >= this.out.columns | ||
|| v.description.split(/\r?\n/).length > 1) { | ||
desc = '\n' + wrap(v.description, { margin: 3, width: this.out.columns }) | ||
} | ||
} | ||
return title + color.gray(desc || ''); | ||
return prefix + ' ' + title + color.gray(desc || ''); | ||
} | ||
@@ -249,5 +224,8 @@ | ||
if (this.closed) return; | ||
if (!this.firstRender) this.out.write(clear(this.outputText)); | ||
if (this.firstRender) this.out.write(cursor.hide); | ||
else this.out.write(clear(this.outputText)); | ||
super.render(); | ||
let { startIndex, endIndex } = entriesToDisplay(this.select, this.choices.length, this.limit); | ||
this.outputText = [ | ||
@@ -257,16 +235,16 @@ color.bold(style.symbol(this.done, this.aborted)), | ||
style.delimiter(this.completing), | ||
this.done && this.suggestions[this.page][this.select] | ||
? this.suggestions[this.page][this.select].title | ||
: this.rendered = this.transform.render(this.input) | ||
this.done && this.suggestions[this.select] | ||
? this.suggestions[this.select].title | ||
: this.rendered = this.transform.render(this.input) | ||
].join(' '); | ||
if (!this.done) { | ||
const suggestions = this.suggestions[this.page] | ||
.map((item, i) => this.renderOption(item, this.select === i)) | ||
const suggestions = this.suggestions | ||
.slice(startIndex, endIndex) | ||
.map((item, i) => this.renderOption(item, | ||
this.select === i + startIndex, | ||
i === 0 && startIndex > 0, | ||
i + startIndex === endIndex - 1 && endIndex < this.choices.length)) | ||
.join('\n'); | ||
this.outputText += `\n` + (suggestions || color.gray(this.fallback.title)); | ||
if (this.suggestions[this.page].length > 1) { | ||
this.outputText += color.blue(`\nPage ${this.page+1}/${this.suggestions.length}`); | ||
} | ||
} | ||
@@ -273,0 +251,0 @@ |
@@ -126,3 +126,7 @@ 'use strict'; | ||
renderInstructions() { | ||
return ` | ||
if (this.instructions === undefined || this.instructions) { | ||
if (typeof this.instructions === 'string') { | ||
return this.instructions; | ||
} | ||
return ` | ||
Instructions: | ||
@@ -133,3 +137,5 @@ ${figures.arrowUp}/${figures.arrowDown}: Highlight option | ||
enter/return: Complete answer | ||
` | ||
`; | ||
} | ||
return ''; | ||
} | ||
@@ -136,0 +142,0 @@ |
@@ -6,3 +6,3 @@ 'use strict'; | ||
const Prompt = require('./prompt'); | ||
const { clear, figures, style, wrap, strip } = require('../util'); | ||
const { clear, figures, style, wrap, entriesToDisplay } = require('../util'); | ||
@@ -18,2 +18,3 @@ /** | ||
* @param {Number} [opts.cursor=0] Cursor start position | ||
* @param {Number} [opts.optionsPerPage=10] Max options to display at once | ||
* @param {Stream} [opts.stdin] The Readable stream to listen to | ||
@@ -34,2 +35,3 @@ * @param {Stream} [opts.stdout] The Writable stream to write readline data to | ||
this.instructions = opts.instructions; | ||
this.optionsPerPage = opts.optionsPerPage || 10; | ||
this.value = opts.choices.map((ch, idx) => { | ||
@@ -41,3 +43,3 @@ if (typeof ch === 'string') | ||
description: ch && ch.description, | ||
value: ch && (ch.value || idx), | ||
value: ch && (ch.value === undefined ? idx : ch.value), | ||
selected: ch && ch.selected, | ||
@@ -179,7 +181,7 @@ disabled: ch && ch.disabled | ||
renderOption(cursor, v, i) { | ||
const prefix = (v.selected ? color.green(figures.radioOn) : figures.radioOff) + ' '; | ||
renderOption(cursor, v, i, arrowIndicator) { | ||
const prefix = (v.selected ? color.green(figures.radioOn) : figures.radioOff) + ' ' + arrowIndicator + ' '; | ||
let title, desc; | ||
if(v.disabled) { | ||
if (v.disabled) { | ||
title = cursor === i ? color.gray().underline(v.title) : color.strikethrough().gray(v.title); | ||
@@ -202,24 +204,21 @@ } else { | ||
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. | ||
if (options.length === 0) { | ||
return color.red('No matches for this query.'); | ||
} | ||
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; | ||
let { startIndex, endIndex } = entriesToDisplay(this.cursor, options.length, this.optionsPerPage); | ||
let prefix, styledOptions = []; | ||
for (let i = startIndex; i < endIndex; i++) { | ||
if (i === startIndex && startIndex > 0) { | ||
prefix = figures.arrowUp; | ||
} else if (i === endIndex - 1 && endIndex < options.length) { | ||
prefix = figures.arrowDown; | ||
} else { | ||
prefix = ' '; | ||
} | ||
scopedOptions = styledOptions.slice(startIndex, endIndex); | ||
hint = color.dim('(Move up and down to reveal more choices)'); | ||
styledOptions.push(this.renderOption(this.cursor, options[i], i, prefix)); | ||
} | ||
return '\n' + scopedOptions.join('\n') + '\n' + hint; | ||
return '\n' + styledOptions.join('\n'); | ||
} | ||
@@ -237,7 +236,6 @@ | ||
if (this.done) { | ||
const selected = this.value | ||
return this.value | ||
.filter(e => e.selected) | ||
.map(v => v.title) | ||
.join(', '); | ||
return selected; | ||
} | ||
@@ -244,0 +242,0 @@ |
@@ -122,2 +122,5 @@ const color = require('kleur'); | ||
this.typed = ``; | ||
if(this.value === '') { | ||
this.value = this.min - this.inc; | ||
} | ||
if (this.value >= this.max) return this.bell(); | ||
@@ -132,2 +135,5 @@ this.value += this.inc; | ||
this.typed = ``; | ||
if(this.value === '') { | ||
this.value = this.min + this.inc; | ||
} | ||
if (this.value <= this.min) return this.bell(); | ||
@@ -144,2 +150,5 @@ this.value -= this.inc; | ||
this.value = this.parse((val = val.slice(0, -1))) || ``; | ||
if (this.value !== '' && this.value < this.min) { | ||
this.value = this.min; | ||
} | ||
this.color = `cyan`; | ||
@@ -146,0 +155,0 @@ this.fire(); |
@@ -19,4 +19,4 @@ 'use strict'; | ||
this.firstRender = true; | ||
this.in = opts.in || process.stdin; | ||
this.out = opts.out || process.stdout; | ||
this.in = opts.stdin || process.stdin; | ||
this.out = opts.stdout || process.stdout; | ||
this.onRender = (opts.onRender || (() => void 0)).bind(this); | ||
@@ -23,0 +23,0 @@ const rl = readline.createInterface(this.in); |
@@ -5,3 +5,3 @@ 'use strict'; | ||
const Prompt = require('./prompt'); | ||
const { style, clear, figures, wrap } = require('../util'); | ||
const { style, clear, figures, wrap, entriesToDisplay } = require('../util'); | ||
const { cursor } = require('sisteransi'); | ||
@@ -18,2 +18,3 @@ | ||
* @param {Stream} [opts.stdout] The Writable stream to write readline data to | ||
* @param {Number} [opts.optionsPerPage=10] Max options to display at once | ||
*/ | ||
@@ -32,3 +33,3 @@ class SelectPrompt extends Prompt { | ||
title: ch && (ch.title || ch.value || ch), | ||
value: ch && (ch.value || idx), | ||
value: ch && (ch.value === undefined ? idx : ch.value), | ||
description: ch && ch.description, | ||
@@ -39,2 +40,3 @@ selected: ch && ch.selected, | ||
}); | ||
this.optionsPerPage = opts.optionsPerPage || 10; | ||
this.value = (this.choices[this.cursor] || {}).value; | ||
@@ -118,2 +120,4 @@ this.clear = clear(''); | ||
let { startIndex, endIndex } = entriesToDisplay(this.cursor, this.choices.length, this.optionsPerPage); | ||
// Print prompt | ||
@@ -130,23 +134,32 @@ this.outputText = [ | ||
if (!this.done) { | ||
this.outputText += '\n' + | ||
this.choices | ||
.map((v, i) => { | ||
let title, prefix, desc = ''; | ||
if (v.disabled) { | ||
title = this.cursor === i ? color.gray().underline(v.title) : color.strikethrough().gray(v.title); | ||
prefix = this.cursor === i ? color.bold().gray(figures.pointer) + ' ' : ' '; | ||
} else { | ||
title = this.cursor === i ? color.cyan().underline(v.title) : v.title; | ||
prefix = this.cursor === i ? color.cyan(figures.pointer) + ' ' : ' '; | ||
if (v.description && this.cursor === i) { | ||
desc = ` - ${v.description}`; | ||
if (prefix.length + title.length + desc.length >= this.out.columns | ||
|| v.description.split(/\r?\n/).length > 1) { | ||
desc = '\n' + wrap(v.description, { margin: 3, width: this.out.columns }); | ||
} | ||
} | ||
} | ||
return `${prefix} ${title}${color.gray(desc)}`; | ||
}) | ||
.join('\n'); | ||
this.outputText += '\n'; | ||
for (let i = startIndex; i < endIndex; i++) { | ||
let title, prefix, desc = '', v = this.choices[i]; | ||
// Determine whether to display "more choices" indicators | ||
if (i === startIndex && startIndex > 0) { | ||
prefix = figures.arrowUp; | ||
} else if (i === endIndex - 1 && endIndex < this.choices.length) { | ||
prefix = figures.arrowDown; | ||
} else { | ||
prefix = ' '; | ||
} | ||
if (v.disabled) { | ||
title = this.cursor === i ? color.gray().underline(v.title) : color.strikethrough().gray(v.title); | ||
prefix = (this.cursor === i ? color.bold().gray(figures.pointer) + ' ' : ' ') + prefix; | ||
} else { | ||
title = this.cursor === i ? color.cyan().underline(v.title) : v.title; | ||
prefix = (this.cursor === i ? color.cyan(figures.pointer) + ' ' : ' ') + prefix; | ||
if (v.description && this.cursor === i) { | ||
desc = ` - ${v.description}`; | ||
if (prefix.length + title.length + desc.length >= this.out.columns | ||
|| v.description.split(/\r?\n/).length > 1) { | ||
desc = '\n' + wrap(v.description, { margin: 3, width: this.out.columns }); | ||
} | ||
} | ||
} | ||
this.outputText += `${prefix} ${title}${color.gray(desc)}\n`; | ||
} | ||
} | ||
@@ -153,0 +166,0 @@ |
@@ -5,3 +5,3 @@ 'use strict'; | ||
const passOn = ['suggest', 'format', 'onState', 'validate', 'onRender']; | ||
const passOn = ['suggest', 'format', 'onState', 'validate', 'onRender', 'type']; | ||
const noop = () => {}; | ||
@@ -20,3 +20,3 @@ | ||
questions = [].concat(questions); | ||
let answer, question, quit, name, type; | ||
let answer, question, quit, name, type, lastPrompt; | ||
@@ -33,2 +33,9 @@ const getFormattedAnswer = async (question, answer, skipValidation = false) => { | ||
// evaluate type first and skip if type is a falsy value | ||
if (typeof type === 'function') { | ||
type = await type(answer, { ...answers }, question) | ||
question['type'] = type | ||
} | ||
if (!type) continue; | ||
// if property is a function, invoke it unless it's a special function | ||
@@ -38,5 +45,7 @@ for (let key in question) { | ||
let value = question[key]; | ||
question[key] = typeof value === 'function' ? await value(answer, { ...answers }, question) : value; | ||
question[key] = typeof value === 'function' ? await value(answer, { ...answers }, lastPrompt) : value; | ||
} | ||
lastPrompt = question; | ||
if (typeof question.message !== 'string') { | ||
@@ -49,5 +58,2 @@ throw new Error('prompt message is required'); | ||
// skip if type is a falsy value | ||
if (!type) continue; | ||
if (prompts[type] === void 0) { | ||
@@ -54,0 +60,0 @@ throw new Error(`prompt type (${type}) is not defined`); |
@@ -10,3 +10,4 @@ 'use strict'; | ||
lines: require('./lines'), | ||
wrap: require('./wrap') | ||
wrap: require('./wrap'), | ||
entriesToDisplay: require('./entriesToDisplay') | ||
}; |
{ | ||
"name": "prompts", | ||
"version": "2.2.1", | ||
"version": "2.3.0", | ||
"description": "Lightweight, beautiful and user-friendly prompts", | ||
@@ -5,0 +5,0 @@ "license": "MIT", |
@@ -36,3 +36,3 @@ <p align="center"> | ||
* **Testable**: provides a way to submit answers programmatically. | ||
* **Unified**: consistent experience across all prompts. | ||
* **Unified**: consistent experience across all [prompts](#-types). | ||
@@ -82,3 +82,3 @@ | ||
Prompt with a single prompt object. Returns object with the response. | ||
Prompt with a single prompt object. Returns an object with the response. | ||
@@ -101,3 +101,3 @@ ```js | ||
Prompt with a list of prompt objects. Returns object with response. | ||
Prompt with a list of prompt objects. Returns an object with the responses. | ||
Make sure to give each prompt a unique `name` property to prevent overwriting values. | ||
@@ -197,3 +197,3 @@ | ||
const questions = [{ ... }]; | ||
const onSubmit = (prompt, response) => console.log(`Thanks I got ${response} from ${prompt.name}`); | ||
const onSubmit = (prompt, answer) => console.log(`Thanks I got ${answer} from ${prompt.name}`); | ||
const response = await prompts(questions, { onSubmit }); | ||
@@ -422,3 +422,3 @@ })(); | ||
The function signature is `(state)` where `state` is an object with a snapshot of the current state. | ||
The state object have two properties `value` and `aborted`. E.g `{ value: 'This is ', aborted: false }` | ||
The state object has two properties `value` and `aborted`. E.g `{ value: 'This is ', aborted: false }` | ||
@@ -431,2 +431,17 @@ | ||
* [text](#textmessage-initial-style) | ||
* [password](#passwordmessage-initial) | ||
* [invisible](#invisiblemessage-initial) | ||
* [number](#numbermessage-initial-max-min-style) | ||
* [confirm](#confirmmessage-initial) | ||
* [list](#listmessage-initial) | ||
* [toggle](#togglemessage-initial-active-inactive) | ||
* [select](#selectmessage-choices-initial-hint-warn) | ||
* [multiselect](#multiselectmessage-choices-initial-max-hint-warn) | ||
* [autocompleteMultiselect](#multiselectmessage-choices-initial-max-hint-warn) | ||
* [autocomplete](#autocompletemessage-choices-initial-suggest-limit-style) | ||
* [date](#datemessage-initial-warn) | ||
*** | ||
### text(message, [initial], [style]) | ||
@@ -457,5 +472,8 @@ > Text prompt for free text input. | ||
| onRender | `function` | On render callback. Keyword `this` refers to the current prompt | | ||
| onState | `function` | On state change callback. Function signature is an `object` with two propetires: `value` and `aborted` | | ||
| onState | `function` | On state change callback. Function signature is an `object` with two properties: `value` and `aborted` | | ||
**↑ back to:** [Prompt types](#-types) | ||
*** | ||
### password(message, [initial]) | ||
@@ -485,5 +503,8 @@ > Password prompt with masked input. | ||
| onRender | `function` | On render callback. Keyword `this` refers to the current prompt | | ||
| onState | `function` | On state change callback. Function signature is an `object` with two propetires: `value` and `aborted` | | ||
| onState | `function` | On state change callback. Function signature is an `object` with two properties: `value` and `aborted` | | ||
**↑ back to:** [Prompt types](#-types) | ||
*** | ||
### invisible(message, [initial]) | ||
@@ -514,5 +535,8 @@ > Prompts user for invisible text input. | ||
| onRender | `function` | On render callback. Keyword `this` refers to the current prompt | | ||
| onState | `function` | On state change callback. Function signature is an `object` with two propetires: `value` and `aborted` | | ||
| onState | `function` | On state change callback. Function signature is an `object` with two properties: `value` and `aborted` | | ||
**↑ back to:** [Prompt types](#-types) | ||
*** | ||
### number(message, initial, [max], [min], [style]) | ||
@@ -552,4 +576,8 @@ > Prompts user for number input. | ||
| onRender | `function` | On render callback. Keyword `this` refers to the current prompt | | ||
| onState | `function` | On state change callback. Function signature is an `object` with two propetires: `value` and `aborted` | | ||
| onState | `function` | On state change callback. Function signature is an `object` with two properties: `value` and `aborted` | | ||
**↑ back to:** [Prompt types](#-types) | ||
*** | ||
### confirm(message, [initial]) | ||
@@ -582,2 +610,6 @@ > Classic yes/no prompt. | ||
**↑ back to:** [Prompt types](#-types) | ||
*** | ||
### list(message, [initial]) | ||
@@ -609,5 +641,8 @@ > List prompt that return an array. | ||
| onRender | `function` | On render callback. Keyword `this` refers to the current prompt | | ||
| onState | `function` | On state change callback. Function signature is an `object` with two propetires: `value` and `aborted` | | ||
| onState | `function` | On state change callback. Function signature is an `object` with two properties: `value` and `aborted` | | ||
**↑ back to:** [Prompt types](#-types) | ||
*** | ||
### toggle(message, [initial], [active], [inactive]) | ||
@@ -641,4 +676,8 @@ > Interactive toggle/switch prompt. | ||
| onRender | `function` | On render callback. Keyword `this` refers to the current prompt | | ||
| onState | `function` | On state change callback. Function signature is an `object` with two propetires: `value` and `aborted` | | ||
| onState | `function` | On state change callback. Function signature is an `object` with two properties: `value` and `aborted` | | ||
**↑ back to:** [Prompt types](#-types) | ||
*** | ||
### select(message, choices, [initial], [hint], [warn]) | ||
@@ -678,2 +717,6 @@ > Interactive select prompt. | ||
**↑ back to:** [Prompt types](#-types) | ||
*** | ||
### multiselect(message, choices, [initial], [max], [hint], [warn]) | ||
@@ -712,2 +755,3 @@ ### autocompleteMultiselect(same) | ||
| choices | `Array` | Array of strings or choices objects `[{ title, value, disabled }, ...]`. The choice's index in the array will be used as its value if it is not specified. | | ||
| optionsPerPage | `number` | Number of options displayed per page (default: 10) | | ||
| min | `number` | Min select - will display error | | ||
@@ -723,3 +767,6 @@ | max | `number` | Max select | | ||
**↑ back to:** [Prompt types](#-types) | ||
*** | ||
### autocomplete(message, choices, [initial], [suggest], [limit], [style]) | ||
@@ -764,3 +811,3 @@ > Interactive auto complete prompt. | ||
| onRender | `function` | On render callback. Keyword `this` refers to the current prompt | | ||
| onState | `function` | On state change callback. Function signature is an `object` with two propetires: `value` and `aborted` | | ||
| onState | `function` | On state change callback. Function signature is an `object` with two properties: `value` and `aborted` | | ||
@@ -773,3 +820,6 @@ Example on what a `suggest` function might look like: | ||
**↑ back to:** [Prompt types](#-types) | ||
*** | ||
### date(message, [initial], [warn]) | ||
@@ -802,3 +852,3 @@ > Interactive date prompt. | ||
| onRender | `function` | On render callback. Keyword `this` refers to the current prompt | | ||
| onState | `function` | On state change callback. Function signature is an `object` with two propetires: `value` and `aborted` | | ||
| onState | `function` | On state change callback. Function signature is an `object` with two properties: `value` and `aborted` | | ||
@@ -831,3 +881,6 @@ Default locales: | ||
**↑ back to:** [Prompt types](#-types) | ||
*** | ||
## ❯ Credit | ||
@@ -834,0 +887,0 @@ Many of the prompts are based on the work of [derhuerst](https://github.com/derhuerst). |
179014
68
4453
873