手写inquirer中的list

188 阅读1分钟

image.png

const EventEmitter = require("events");
// mute-stream 让输入输出处于静默状态
const MuteStream = require("mute-stream");
const readline = require("readline");
const ansiEscapes = require("ansi-escapes");
//完成事件的监听
const { fromEvent } = require("rxjs");
const option = {
  type: "list",
  name: "name",
  messege: "select your name: ",
  choices: [
    {
      value: "sam",
      name: "sam",
    },
    {
      value: "zhangsan",
      name: "zhangsan",
    },
    {
      value: "rainbow",
      name: "rainbow",
    },
  ],
};

function Prompt(option) {
  return new Promise((resolve, reject) => {
    try {
      const list = new List(option);
      list.render(); //render方法把列表渲染出来
      list.on("exit", function (answers) {
        resolve(answers);
      });
    } catch (e) {
      reject(e);
    }
  });
}

// 效果同EventEmitter.call(this);
class List extends EventEmitter {
  constructor(option) {
    super();
    const { name, messege, choices } = option;
    this.name = name;
    this.messege = messege;
    this.choices = choices;
    this.intput = process.stdin;
    // 封装输出流,静默输出
    const ms = new MuteStream();
    ms.pipe(process.stdout);
    this.output = ms;
    this.rl = readline.createInterface({
      input: this.intput,
      output: this.output,
    });
    this.selected = 0;
    this.height = 0;
    this.keypress = fromEvent(this.rl.input, "keypress").forEach(
      this.onkeypress
    );
    this.haveSelected = false; //是否已经选择完毕
  }

  onkeypress = (keymap) => {
    const key = keymap[1];
    switch (key.name) {
      case "down":
        this.selected++;
        if (this.selected > this.choices.length - 1) {
          this.selected = 0;
        }
        this.render();
        break;
      case "up":
        this.selected--;
        if (this.selected < 0) {
          this.selected = this.choices.length - 1;
        }
        this.render();
        break;
      case "return":
        this.haveSelected = true;
        this.render();
        this.close();
        this.emit("exit", this.choices[this.selected]);
        break;
      default:
        break;
    }
  };

  render() {
    //列表渲染
    this.output.unmute(); //让用户不能再输出内容了
    this.clean();
    this.output.write(this.getContent());
    this.output.mute(); //让用户不能再输出内容了
  }

  // 获取列白渲染内容
  getContent() {
    let title =
      "\x1B[32m?\x1B[39m \x1B[1m" +
      this.messege +
      "\x1B[22m\x1B[0m\x1B[0m\x1B[2m(Use arrow keys)\x1B[22m \n";
    if (!this.haveSelected) {
      // 判断是否为最后一个元素,如果是,则不加上换行符\n
      this.choices.forEach((choice, index) => {
        if (index === this.selected) {
          if (index === this.choices.length - 1) {
            title += "\x1B[36m> " + choice.name + "\x1B[39m";
          } else {
            title += "\x1B[36m> " + choice.name + "\x1B[39m \n";
          }
        } else {
          if (index === this.choices.length - 1) {
            title += "  " + choice.name;
          } else {
            title += "  " + choice.name + "\n";
          }
        }
      });
    } else {
      //输出结束后的逻辑
      const name = this.choices[this.selected].name;
      title =
        "\x1B[32m?\x1B[39m \x1B[1m" +
        this.messege +
        "\x1B[22m\x1B[0m\x1B[36m" +
        name +
        "\x1B[39m\x1B[0m \n";
    }
    this.height = this.choices.length + 1;
    return title;
  }

  // 清屏
  clean() {
    const emptyLines = ansiEscapes.eraseLines(this.height);
    this.output.write(emptyLines);
  }
  close() {
    this.output.unmute();
    this.rl.output.end();
    this.rl.pause();
    this.rl.close();
  }
}

//实现该方法
Prompt(option).then((answer) => {
  console.log("answer~~", answer);
});