Inquirer.js icon indicating copy to clipboard operation
Inquirer.js copied to clipboard

Edit Default Value on "input" Type

Open alex-ppg opened this issue 5 years ago • 3 comments

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.

alex-ppg avatar Oct 14 '20 17:10 alex-ppg

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;

alex-ppg avatar Oct 16 '20 11:10 alex-ppg

looking forward to this feature

henrycjchen avatar Dec 16 '20 08:12 henrycjchen

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;

thibautLedz avatar Dec 22 '20 15:12 thibautLedz

@SBoudrias tagging in case you missed the issue, looks like very useful functionality which can be added with only a few lines.

alex-ppg avatar Jan 10 '23 10:01 alex-ppg

I'm not against adding this; but I think it needs some refinement on the UX and how it'd be controlled by the user. I'm not too sure about the current hotkeys proposed.

Would it work if we only auto-fill the value, and let the user delete the input with backspace? (or cmd+backspace)

If someone wants to make a PR, I'll review and make a release. I'd ask though to please implement in both the new (@inquirer/input) and old code (inquirer) as to not create more work to be done to eventually release the rewrite.

SBoudrias avatar Jan 10 '23 21:01 SBoudrias

@SBoudrias Any updates here?

remisture avatar Apr 09 '23 18:04 remisture

I'll close this one in favour of the more recent #1215 where I think we have an agreement on a suggested UX for this feature.

SBoudrias avatar May 19 '23 15:05 SBoudrias