source: frontend/node_modules/prompts/lib/elements/autocompleteMultiselect.js

Last change on this file was 9af201e, checked in by MBK <marija.karapandzova@…>, 12 days ago

Fix frontend appearance

  • Property mode set to 100644
File size: 5.2 KB
RevLine 
[9af201e]1'use strict';
2
3const color = require('kleur');
4const { cursor } = require('sisteransi');
5const MultiselectPrompt = require('./multiselect');
6const { clear, style, figures } = require('../util');
7/**
8 * MultiselectPrompt Base Element
9 * @param {Object} opts Options
10 * @param {String} opts.message Message
11 * @param {Array} opts.choices Array of choice objects
12 * @param {String} [opts.hint] Hint to display
13 * @param {String} [opts.warn] Hint shown for disabled choices
14 * @param {Number} [opts.max] Max choices
15 * @param {Number} [opts.cursor=0] Cursor start position
16 * @param {Stream} [opts.stdin] The Readable stream to listen to
17 * @param {Stream} [opts.stdout] The Writable stream to write readline data to
18 */
19class AutocompleteMultiselectPrompt extends MultiselectPrompt {
20 constructor(opts={}) {
21 opts.overrideRender = true;
22 super(opts);
23 this.inputValue = '';
24 this.clear = clear('', this.out.columns);
25 this.filteredOptions = this.value;
26 this.render();
27 }
28
29 last() {
30 this.cursor = this.filteredOptions.length - 1;
31 this.render();
32 }
33 next() {
34 this.cursor = (this.cursor + 1) % this.filteredOptions.length;
35 this.render();
36 }
37
38 up() {
39 if (this.cursor === 0) {
40 this.cursor = this.filteredOptions.length - 1;
41 } else {
42 this.cursor--;
43 }
44 this.render();
45 }
46
47 down() {
48 if (this.cursor === this.filteredOptions.length - 1) {
49 this.cursor = 0;
50 } else {
51 this.cursor++;
52 }
53 this.render();
54 }
55
56 left() {
57 this.filteredOptions[this.cursor].selected = false;
58 this.render();
59 }
60
61 right() {
62 if (this.value.filter(e => e.selected).length >= this.maxChoices) return this.bell();
63 this.filteredOptions[this.cursor].selected = true;
64 this.render();
65 }
66
67 delete() {
68 if (this.inputValue.length) {
69 this.inputValue = this.inputValue.substr(0, this.inputValue.length - 1);
70 this.updateFilteredOptions();
71 }
72 }
73
74 updateFilteredOptions() {
75 const currentHighlight = this.filteredOptions[this.cursor];
76 this.filteredOptions = this.value
77 .filter(v => {
78 if (this.inputValue) {
79 if (typeof v.title === 'string') {
80 if (v.title.toLowerCase().includes(this.inputValue.toLowerCase())) {
81 return true;
82 }
83 }
84 if (typeof v.value === 'string') {
85 if (v.value.toLowerCase().includes(this.inputValue.toLowerCase())) {
86 return true;
87 }
88 }
89 return false;
90 }
91 return true;
92 });
93 const newHighlightIndex = this.filteredOptions.findIndex(v => v === currentHighlight)
94 this.cursor = newHighlightIndex < 0 ? 0 : newHighlightIndex;
95 this.render();
96 }
97
98 handleSpaceToggle() {
99 const v = this.filteredOptions[this.cursor];
100
101 if (v.selected) {
102 v.selected = false;
103 this.render();
104 } else if (v.disabled || this.value.filter(e => e.selected).length >= this.maxChoices) {
105 return this.bell();
106 } else {
107 v.selected = true;
108 this.render();
109 }
110 }
111
112 handleInputChange(c) {
113 this.inputValue = this.inputValue + c;
114 this.updateFilteredOptions();
115 }
116
117 _(c, key) {
118 if (c === ' ') {
119 this.handleSpaceToggle();
120 } else {
121 this.handleInputChange(c);
122 }
123 }
124
125 renderInstructions() {
126 if (this.instructions === undefined || this.instructions) {
127 if (typeof this.instructions === 'string') {
128 return this.instructions;
129 }
130 return `
131Instructions:
132 ${figures.arrowUp}/${figures.arrowDown}: Highlight option
133 ${figures.arrowLeft}/${figures.arrowRight}/[space]: Toggle selection
134 [a,b,c]/delete: Filter choices
135 enter/return: Complete answer
136`;
137 }
138 return '';
139 }
140
141 renderCurrentInput() {
142 return `
143Filtered results for: ${this.inputValue ? this.inputValue : color.gray('Enter something to filter')}\n`;
144 }
145
146 renderOption(cursor, v, i) {
147 let title;
148 if (v.disabled) title = cursor === i ? color.gray().underline(v.title) : color.strikethrough().gray(v.title);
149 else title = cursor === i ? color.cyan().underline(v.title) : v.title;
150 return (v.selected ? color.green(figures.radioOn) : figures.radioOff) + ' ' + title
151 }
152
153 renderDoneOrInstructions() {
154 if (this.done) {
155 return this.value
156 .filter(e => e.selected)
157 .map(v => v.title)
158 .join(', ');
159 }
160
161 const output = [color.gray(this.hint), this.renderInstructions(), this.renderCurrentInput()];
162
163 if (this.filteredOptions.length && this.filteredOptions[this.cursor].disabled) {
164 output.push(color.yellow(this.warn));
165 }
166 return output.join(' ');
167 }
168
169 render() {
170 if (this.closed) return;
171 if (this.firstRender) this.out.write(cursor.hide);
172 super.render();
173
174 // print prompt
175
176 let prompt = [
177 style.symbol(this.done, this.aborted),
178 color.bold(this.msg),
179 style.delimiter(false),
180 this.renderDoneOrInstructions()
181 ].join(' ');
182
183 if (this.showMinError) {
184 prompt += color.red(`You must select a minimum of ${this.minSelected} choices.`);
185 this.showMinError = false;
186 }
187 prompt += this.renderOptions(this.filteredOptions);
188
189 this.out.write(this.clear + prompt);
190 this.clear = clear(prompt, this.out.columns);
191 }
192}
193
194module.exports = AutocompleteMultiselectPrompt;
Note: See TracBrowser for help on using the repository browser.