Inquirer.js: Edit Default Value on "input" Type

Created on 14 Oct 2020  路  3Comments  路  Source: SBoudrias/Inquirer.js

I came across a use-case whereby I wanted people to be able to edit their previously submitted answers. I tried utilizing the filter and transformer attributes to no avail. I think the space-bar could be used for replacing the current answer in-memory with the default value. I can submit a PR if this is desired functionality.

Most helpful comment

I created a custom prompt that can be used to override the input type and will allow you to edit the default value by pressing the spacebar. The code is the same as the input prompt of inquirer with 5 added lines to onKeypress(). It can be found below:

"use strict";

const chalk = require("chalk");
const { map, takeUntil } = require("rxjs/operators");
const Base = require("inquirer/lib/prompts/base");
const observe = require("inquirer/lib/utils/events");

class InputPrompt extends Base {
  /**
   * Start the Inquiry session
   * @param  {Function} cb      Callback when prompt is done
   * @return {this}
   */

  _run(cb) {
    this.done = cb;

    // Once user confirm (enter key)
    const events = observe(this.rl);
    const submit = events.line.pipe(map(this.filterInput.bind(this)));

    const validation = this.handleSubmitEvents(submit);
    validation.success.forEach(this.onEnd.bind(this));
    validation.error.forEach(this.onError.bind(this));

    events.keypress
      .pipe(takeUntil(validation.success))
      .forEach(this.onKeypress.bind(this));

    // Init
    this.render();

    return this;
  }

  /**
   * Render the prompt to screen
   * @return {InputPrompt} self
   */

  render(error) {
    let bottomContent = "";
    let appendContent = "";
    let message = this.getQuestion();
    const transformer = this.opt.transformer;
    const isFinal = this.status === "answered";

    if (isFinal) {
      appendContent = this.answer;
    } else {
      appendContent = this.rl.line;
    }

    if (transformer) {
      message += transformer(appendContent, this.answers, { isFinal });
    } else {
      message += isFinal ? chalk.cyan(appendContent) : appendContent;
    }

    if (error) {
      bottomContent = chalk.red(">> ") + error;
    }

    this.screen.render(message, bottomContent);
  }

  /**
   * When user press `enter` key
   */

  filterInput(input) {
    if (!input) {
      return this.opt.default == null ? "" : this.opt.default;
    }

    return input;
  }

  onEnd(state) {
    this.answer = state.value;
    this.status = "answered";

    // Re-render prompt
    this.render();

    this.screen.done();
    this.done(state.value);
  }

  onError({ value = "", isValid }) {
    this.rl.line += value;
    this.rl.cursor += value.length;
    this.render(isValid);
  }

  /**
   * When user press a key
   */

  onKeypress() {
    // If user presses the space-bar as the first key and default value exists, replace current value with default value
    if (this.rl.line === " " && this.opt.default) {
      this.rl.line = this.opt.default;
      this.rl.cursor = this.opt.default.length;
    }

    // If user press a key, just clear the default value
    if (this.opt.default) {
      this.opt.default = undefined;
    }

    // console.log("Keypress state A: ", this.rl);

    // console.log("Keypress state B: ", this);

    this.render();
  }
}

module.exports = InputPrompt;

All 3 comments

I created a custom prompt that can be used to override the input type and will allow you to edit the default value by pressing the spacebar. The code is the same as the input prompt of inquirer with 5 added lines to onKeypress(). It can be found below:

"use strict";

const chalk = require("chalk");
const { map, takeUntil } = require("rxjs/operators");
const Base = require("inquirer/lib/prompts/base");
const observe = require("inquirer/lib/utils/events");

class InputPrompt extends Base {
  /**
   * Start the Inquiry session
   * @param  {Function} cb      Callback when prompt is done
   * @return {this}
   */

  _run(cb) {
    this.done = cb;

    // Once user confirm (enter key)
    const events = observe(this.rl);
    const submit = events.line.pipe(map(this.filterInput.bind(this)));

    const validation = this.handleSubmitEvents(submit);
    validation.success.forEach(this.onEnd.bind(this));
    validation.error.forEach(this.onError.bind(this));

    events.keypress
      .pipe(takeUntil(validation.success))
      .forEach(this.onKeypress.bind(this));

    // Init
    this.render();

    return this;
  }

  /**
   * Render the prompt to screen
   * @return {InputPrompt} self
   */

  render(error) {
    let bottomContent = "";
    let appendContent = "";
    let message = this.getQuestion();
    const transformer = this.opt.transformer;
    const isFinal = this.status === "answered";

    if (isFinal) {
      appendContent = this.answer;
    } else {
      appendContent = this.rl.line;
    }

    if (transformer) {
      message += transformer(appendContent, this.answers, { isFinal });
    } else {
      message += isFinal ? chalk.cyan(appendContent) : appendContent;
    }

    if (error) {
      bottomContent = chalk.red(">> ") + error;
    }

    this.screen.render(message, bottomContent);
  }

  /**
   * When user press `enter` key
   */

  filterInput(input) {
    if (!input) {
      return this.opt.default == null ? "" : this.opt.default;
    }

    return input;
  }

  onEnd(state) {
    this.answer = state.value;
    this.status = "answered";

    // Re-render prompt
    this.render();

    this.screen.done();
    this.done(state.value);
  }

  onError({ value = "", isValid }) {
    this.rl.line += value;
    this.rl.cursor += value.length;
    this.render(isValid);
  }

  /**
   * When user press a key
   */

  onKeypress() {
    // If user presses the space-bar as the first key and default value exists, replace current value with default value
    if (this.rl.line === " " && this.opt.default) {
      this.rl.line = this.opt.default;
      this.rl.cursor = this.opt.default.length;
    }

    // If user press a key, just clear the default value
    if (this.opt.default) {
      this.opt.default = undefined;
    }

    // console.log("Keypress state A: ", this.rl);

    // console.log("Keypress state B: ", this);

    this.render();
  }
}

module.exports = InputPrompt;

looking forward to this feature

I just extended class InputPrompt, really cool solution @alex-ppg

"use strict";

const InputPrompt = require("inquirer/lib/prompts/input");

class InputPromptWithDefaultEdit extends InputPrompt {

  /**
   * When user press a key
   */
  onKeypress() {
    // If user presses the space-bar as the first key and default value exists, replace current value with default value
    if (this.rl.line === " " && this.opt.default) {
      this.rl.line = this.opt.default;
      this.rl.cursor = this.opt.default.length;
    }

    // If user press a key, just clear the default value
    if (this.opt.default) {
      this.opt.default = undefined;
    }

    this.render();
  }
}

module.exports = InputPromptWithDefaultEdit;

Was this page helpful?
0 / 5 - 0 ratings

Related issues

apierzch picture apierzch  路  4Comments

aydn picture aydn  路  5Comments

adalinesimonian picture adalinesimonian  路  6Comments

jahvi picture jahvi  路  6Comments

aguerrieri picture aguerrieri  路  6Comments