3 const colors = require('ansi-colors');
4 const SelectPrompt = require('./select');
5 const placeholder = require('../placeholder');
7 class FormPrompt extends SelectPrompt {
9 super({ ...options, multiple: true });
11 this.initial = this.options.initial;
12 this.align = [this.options.align, 'right'].find(v => v != null);
19 if (first === true) this._index = this.index;
20 this.index = this._index;
22 this.choices.forEach(choice => choice.reset && choice.reset());
27 return !!char && this.append(char);
31 let choice = this.focused;
32 if (!choice) return this.alert();
33 let { cursor, input } = choice;
34 choice.value = choice.input = input.slice(0, cursor) + char + input.slice(cursor);
40 let choice = this.focused;
41 if (!choice || choice.cursor <= 0) return this.alert();
42 let { cursor, input } = choice;
43 choice.value = choice.input = input.slice(0, cursor - 1) + input.slice(cursor);
49 let choice = this.focused;
50 if (!choice) return this.alert();
51 let { cursor, input } = choice;
52 if (input[cursor] === void 0) return this.alert();
53 let str = `${input}`.slice(0, cursor) + `${input}`.slice(cursor + 1);
54 choice.value = choice.input = str;
59 let choice = this.focused;
60 if (!choice) return this.alert();
61 if (choice.cursor >= choice.input.length) return this.alert();
67 let choice = this.focused;
68 if (!choice) return this.alert();
69 if (choice.cursor <= 0) return this.alert();
75 return this.dispatch(ch, key);
79 return this.dispatch(ch, key);
83 let ch = this.focused;
84 if (!ch) return this.alert();
85 let { initial, input } = ch;
86 if (initial && initial.startsWith(input) && input !== initial) {
87 ch.value = ch.input = initial;
88 ch.cursor = ch.value.length;
95 let ch = this.focused;
96 if (!ch) return this.alert();
97 if (ch.cursor === 0) return super.prev();
98 ch.value = ch.input = '';
100 return this.render();
108 return !this.state.submitted ? super.format(value) : '';
116 return choice.input ? '⦿' : '⊙';
119 async choiceSeparator(choice, i) {
120 let sep = await this.resolve(choice.separator, this.state, choice, i) || ':';
121 return sep ? ' ' + this.styles.disabled(sep) : '';
124 async renderChoice(choice, i) {
125 await this.onChoice(choice, i);
127 let { state, styles } = this;
128 let { cursor, initial = '', name, hint, input = '' } = choice;
129 let { muted, submitted, primary, danger } = styles;
132 let focused = this.index === i;
133 let validate = choice.validate || (() => true);
134 let sep = await this.choiceSeparator(choice, i);
135 let msg = choice.message;
137 if (this.align === 'right') msg = msg.padStart(this.longest + 1, ' ');
138 if (this.align === 'left') msg = msg.padEnd(this.longest + 1, ' ');
140 // re-populate the form values (answers) object
141 let value = this.values[name] = (input || initial);
142 let color = input ? 'success' : 'dark';
144 if ((await validate.call(choice, value, this.state)) !== true) {
148 let style = styles[color];
149 let indicator = style(await this.indicator(choice, i)) + (choice.pad || '');
151 let indent = this.indent(choice);
152 let line = () => [indent, indicator, msg + sep, input, help].filter(Boolean).join(' ');
154 if (state.submitted) {
155 msg = colors.unstyle(msg);
156 input = submitted(input);
162 input = await choice.format.call(this, input, choice, i);
164 let color = this.styles.muted;
165 let options = { input, initial, pos: cursor, showCursor: focused, color };
166 input = placeholder(this, options);
169 if (!this.isValue(input)) {
170 input = this.styles.muted(this.symbols.ellipsis);
174 this.values[name] = await choice.result.call(this, value, choice, i);
182 input += (input ? ' ' : '') + danger(choice.error.trim());
183 } else if (choice.hint) {
184 input += (input ? ' ' : '') + muted(choice.hint.trim());
191 this.value = this.values;
192 return super.base.submit.call(this);
196 module.exports = FormPrompt;