--- /dev/null
+'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;