Skip to content

Commit

Permalink
feat(prompt): progress indicator for filter and validate with optiona… (
Browse files Browse the repository at this point in the history
#1002)

Co-authored-by: Kim Brandwijk <kim.brandwijk@gmail.com>
Co-authored-by: Simon Boudrias <admin@simonboudrias.com>
  • Loading branch information
3 people committed May 21, 2021
1 parent 0053e3f commit 6717092
Show file tree
Hide file tree
Showing 5 changed files with 305 additions and 51 deletions.
43 changes: 43 additions & 0 deletions packages/inquirer/examples/filter-validate-progress.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/**
* Filter and validate progress example
*/

'use strict';
const inquirer = require('..');

/* eslint-disable no-promise-executor-return */

const questions = [
{
type: 'input',
name: 'first_question',
message: 'Question with filtering and validating text',
validate: async () => {
await new Promise((r) => setTimeout(r, 3000));
return true;
},
filter: async (answer) => {
await new Promise((r) => setTimeout(r, 3000));
return `filtered${answer}`;
},
filteringText: 'Filtering your answer...',
validatingText: 'Validating what you wrote...',
},
{
type: 'input',
name: 'second_question',
message: 'Question without filtering and validating text',
validate: async () => {
await new Promise((r) => setTimeout(r, 3000));
return true;
},
filter: async (answer) => {
await new Promise((r) => setTimeout(r, 3000));
return `filtered${answer}`;
},
},
];

inquirer.prompt(questions).then((answers) => {
console.log(JSON.stringify(answers, null, ' '));
});
29 changes: 21 additions & 8 deletions packages/inquirer/lib/prompts/base.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ class Prompt {
// Set defaults prompt options
this.opt = _.defaults(_.clone(question), {
validate: () => true,
validatingText: '',
filter: (val) => val,
filteringText: '',
when: () => true,
suffix: '',
prefix: chalk.green('?'),
Expand Down Expand Up @@ -96,16 +98,19 @@ class Prompt {
const validate = runAsync(this.opt.validate);
const asyncFilter = runAsync(this.opt.filter);
const validation = submit.pipe(
flatMap((value) =>
asyncFilter(value, self.answers).then(
(filteredValue) =>
validate(filteredValue, self.answers).then(
flatMap((value) => {
this.startSpinner(value, this.opt.filteringText);
return asyncFilter(value, self.answers).then(
(filteredValue) => {
this.startSpinner(filteredValue, this.opt.validatingText);
return validate(filteredValue, self.answers).then(
(isValid) => ({ isValid, value: filteredValue }),
(err) => ({ isValid: err, value: filteredValue })
),
);
},
(err) => ({ isValid: err })
)
),
);
}),
share()
);

Expand All @@ -124,11 +129,19 @@ class Prompt {
};
}

startSpinner(value, bottomContent) {
// If the question will spin, cut off the prefix (for layout purposes)
const content = bottomContent
? this.getQuestion() + value
: this.getQuestion().slice(this.opt.prefix.length + 1) + value;

this.screen.renderWithSpinner(content, bottomContent);
}

/**
* Generate the prompt question string
* @return {String} prompt question string
*/

getQuestion() {
let message =
this.opt.prefix +
Expand Down
32 changes: 31 additions & 1 deletion packages/inquirer/lib/utils/screen-manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const util = require('./readline');
const cliWidth = require('cli-width');
const stripAnsi = require('strip-ansi');
const stringWidth = require('string-width');
const ora = require('ora');

function height(content) {
return content.split('\n').length;
Expand All @@ -25,7 +26,36 @@ class ScreenManager {
this.rl = rl;
}

render(content, bottomContent) {
renderWithSpinner(content, bottomContent) {
if (this.spinnerId) {
clearInterval(this.spinnerId);
}

let spinner;
let contentFunc;
let bottomContentFunc;

if (bottomContent) {
spinner = ora(bottomContent);
contentFunc = () => content;
bottomContentFunc = () => spinner.frame();
} else {
spinner = ora(content);
contentFunc = () => spinner.frame();
bottomContentFunc = () => '';
}

this.spinnerId = setInterval(
() => this.render(contentFunc(), bottomContentFunc(), true),
spinner.interval
);
}

render(content, bottomContent, spinning = false) {
if (this.spinnerId && !spinning) {
clearInterval(this.spinnerId);
}

this.rl.output.unmute();
this.clean(this.extraLinesUnderPrompt);

Expand Down
Loading

0 comments on commit 6717092

Please sign in to comment.