123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196 |
- 'use strict';
-
- const colors = require('ansi-colors');
- const SelectPrompt = require('./select');
- const placeholder = require('../placeholder');
-
- class FormPrompt extends SelectPrompt {
- constructor(options) {
- super({ ...options, multiple: true });
- this.type = 'form';
- this.initial = this.options.initial;
- this.align = [this.options.align, 'right'].find(v => v != null);
- this.emptyError = '';
- this.values = {};
- }
-
- async reset(first) {
- await super.reset();
- if (first === true) this._index = this.index;
- this.index = this._index;
- this.values = {};
- this.choices.forEach(choice => choice.reset && choice.reset());
- return this.render();
- }
-
- dispatch(char) {
- return !!char && this.append(char);
- }
-
- append(char) {
- let choice = this.focused;
- if (!choice) return this.alert();
- let { cursor, input } = choice;
- choice.value = choice.input = input.slice(0, cursor) + char + input.slice(cursor);
- choice.cursor++;
- return this.render();
- }
-
- delete() {
- let choice = this.focused;
- if (!choice || choice.cursor <= 0) return this.alert();
- let { cursor, input } = choice;
- choice.value = choice.input = input.slice(0, cursor - 1) + input.slice(cursor);
- choice.cursor--;
- return this.render();
- }
-
- deleteForward() {
- let choice = this.focused;
- if (!choice) return this.alert();
- let { cursor, input } = choice;
- if (input[cursor] === void 0) return this.alert();
- let str = `${input}`.slice(0, cursor) + `${input}`.slice(cursor + 1);
- choice.value = choice.input = str;
- return this.render();
- }
-
- right() {
- let choice = this.focused;
- if (!choice) return this.alert();
- if (choice.cursor >= choice.input.length) return this.alert();
- choice.cursor++;
- return this.render();
- }
-
- left() {
- let choice = this.focused;
- if (!choice) return this.alert();
- if (choice.cursor <= 0) return this.alert();
- choice.cursor--;
- return this.render();
- }
-
- space(ch, key) {
- return this.dispatch(ch, key);
- }
-
- number(ch, key) {
- return this.dispatch(ch, key);
- }
-
- next() {
- let ch = this.focused;
- if (!ch) return this.alert();
- let { initial, input } = ch;
- if (initial && initial.startsWith(input) && input !== initial) {
- ch.value = ch.input = initial;
- ch.cursor = ch.value.length;
- return this.render();
- }
- return super.next();
- }
-
- prev() {
- let ch = this.focused;
- if (!ch) return this.alert();
- if (ch.cursor === 0) return super.prev();
- ch.value = ch.input = '';
- ch.cursor = 0;
- return this.render();
- }
-
- separator() {
- return '';
- }
-
- format(value) {
- return !this.state.submitted ? super.format(value) : '';
- }
-
- pointer() {
- return '';
- }
-
- indicator(choice) {
- return choice.input ? '⦿' : '⊙';
- }
-
- async choiceSeparator(choice, i) {
- let sep = await this.resolve(choice.separator, this.state, choice, i) || ':';
- return sep ? ' ' + this.styles.disabled(sep) : '';
- }
-
- async renderChoice(choice, i) {
- await this.onChoice(choice, i);
-
- let { state, styles } = this;
- let { cursor, initial = '', name, hint, input = '' } = choice;
- let { muted, submitted, primary, danger } = styles;
-
- let help = hint;
- let focused = this.index === i;
- let validate = choice.validate || (() => true);
- let sep = await this.choiceSeparator(choice, i);
- let msg = choice.message;
-
- if (this.align === 'right') msg = msg.padStart(this.longest + 1, ' ');
- if (this.align === 'left') msg = msg.padEnd(this.longest + 1, ' ');
-
- // re-populate the form values (answers) object
- let value = this.values[name] = (input || initial);
- let color = input ? 'success' : 'dark';
-
- if ((await validate.call(choice, value, this.state)) !== true) {
- color = 'danger';
- }
-
- let style = styles[color];
- let indicator = style(await this.indicator(choice, i)) + (choice.pad || '');
-
- let indent = this.indent(choice);
- let line = () => [indent, indicator, msg + sep, input, help].filter(Boolean).join(' ');
-
- if (state.submitted) {
- msg = colors.unstyle(msg);
- input = submitted(input);
- help = '';
- return line();
- }
-
- if (choice.format) {
- input = await choice.format.call(this, input, choice, i);
- } else {
- let color = this.styles.muted;
- let options = { input, initial, pos: cursor, showCursor: focused, color };
- input = placeholder(this, options);
- }
-
- if (!this.isValue(input)) {
- input = this.styles.muted(this.symbols.ellipsis);
- }
-
- if (choice.result) {
- this.values[name] = await choice.result.call(this, value, choice, i);
- }
-
- if (focused) {
- msg = primary(msg);
- }
-
- if (choice.error) {
- input += (input ? ' ' : '') + danger(choice.error.trim());
- } else if (choice.hint) {
- input += (input ? ' ' : '') + muted(choice.hint.trim());
- }
-
- return line();
- }
-
- async submit() {
- this.value = this.values;
- return super.base.submit.call(this);
- }
- }
-
- module.exports = FormPrompt;
|