Ohm-Management - Projektarbeit B-ME
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

expand.js 6.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. 'use strict';
  2. /**
  3. * `rawlist` type prompt
  4. */
  5. var _ = require('lodash');
  6. var chalk = require('chalk');
  7. var { map, takeUntil } = require('rxjs/operators');
  8. var Base = require('./base');
  9. var Separator = require('../objects/separator');
  10. var observe = require('../utils/events');
  11. var Paginator = require('../utils/paginator');
  12. class ExpandPrompt extends Base {
  13. constructor(questions, rl, answers) {
  14. super(questions, rl, answers);
  15. if (!this.opt.choices) {
  16. this.throwParamError('choices');
  17. }
  18. this.validateChoices(this.opt.choices);
  19. // Add the default `help` (/expand) option
  20. this.opt.choices.push({
  21. key: 'h',
  22. name: 'Help, list all options',
  23. value: 'help'
  24. });
  25. this.opt.validate = choice => {
  26. if (choice == null) {
  27. return 'Please enter a valid command';
  28. }
  29. return choice !== 'help';
  30. };
  31. // Setup the default string (capitalize the default key)
  32. this.opt.default = this.generateChoicesString(this.opt.choices, this.opt.default);
  33. this.paginator = new Paginator(this.screen);
  34. }
  35. /**
  36. * Start the Inquiry session
  37. * @param {Function} cb Callback when prompt is done
  38. * @return {this}
  39. */
  40. _run(cb) {
  41. this.done = cb;
  42. // Save user answer and update prompt to show selected option.
  43. var events = observe(this.rl);
  44. var validation = this.handleSubmitEvents(
  45. events.line.pipe(map(this.getCurrentValue.bind(this)))
  46. );
  47. validation.success.forEach(this.onSubmit.bind(this));
  48. validation.error.forEach(this.onError.bind(this));
  49. this.keypressObs = events.keypress
  50. .pipe(takeUntil(validation.success))
  51. .forEach(this.onKeypress.bind(this));
  52. // Init the prompt
  53. this.render();
  54. return this;
  55. }
  56. /**
  57. * Render the prompt to screen
  58. * @return {ExpandPrompt} self
  59. */
  60. render(error, hint) {
  61. var message = this.getQuestion();
  62. var bottomContent = '';
  63. if (this.status === 'answered') {
  64. message += chalk.cyan(this.answer);
  65. } else if (this.status === 'expanded') {
  66. var choicesStr = renderChoices(this.opt.choices, this.selectedKey);
  67. message += this.paginator.paginate(choicesStr, this.selectedKey, this.opt.pageSize);
  68. message += '\n Answer: ';
  69. }
  70. message += this.rl.line;
  71. if (error) {
  72. bottomContent = chalk.red('>> ') + error;
  73. }
  74. if (hint) {
  75. bottomContent = chalk.cyan('>> ') + hint;
  76. }
  77. this.screen.render(message, bottomContent);
  78. }
  79. getCurrentValue(input) {
  80. if (!input) {
  81. input = this.rawDefault;
  82. }
  83. var selected = this.opt.choices.where({ key: input.toLowerCase().trim() })[0];
  84. if (!selected) {
  85. return null;
  86. }
  87. return selected.value;
  88. }
  89. /**
  90. * Generate the prompt choices string
  91. * @return {String} Choices string
  92. */
  93. getChoices() {
  94. var output = '';
  95. this.opt.choices.forEach(choice => {
  96. output += '\n ';
  97. if (choice.type === 'separator') {
  98. output += ' ' + choice;
  99. return;
  100. }
  101. var choiceStr = choice.key + ') ' + choice.name;
  102. if (this.selectedKey === choice.key) {
  103. choiceStr = chalk.cyan(choiceStr);
  104. }
  105. output += choiceStr;
  106. });
  107. return output;
  108. }
  109. onError(state) {
  110. if (state.value === 'help') {
  111. this.selectedKey = '';
  112. this.status = 'expanded';
  113. this.render();
  114. return;
  115. }
  116. this.render(state.isValid);
  117. }
  118. /**
  119. * When user press `enter` key
  120. */
  121. onSubmit(state) {
  122. this.status = 'answered';
  123. var choice = this.opt.choices.where({ value: state.value })[0];
  124. this.answer = choice.short || choice.name;
  125. // Re-render prompt
  126. this.render();
  127. this.screen.done();
  128. this.done(state.value);
  129. }
  130. /**
  131. * When user press a key
  132. */
  133. onKeypress() {
  134. this.selectedKey = this.rl.line.toLowerCase();
  135. var selected = this.opt.choices.where({ key: this.selectedKey })[0];
  136. if (this.status === 'expanded') {
  137. this.render();
  138. } else {
  139. this.render(null, selected ? selected.name : null);
  140. }
  141. }
  142. /**
  143. * Validate the choices
  144. * @param {Array} choices
  145. */
  146. validateChoices(choices) {
  147. var formatError;
  148. var errors = [];
  149. var keymap = {};
  150. choices.filter(Separator.exclude).forEach(choice => {
  151. if (!choice.key || choice.key.length !== 1) {
  152. formatError = true;
  153. }
  154. if (keymap[choice.key]) {
  155. errors.push(choice.key);
  156. }
  157. keymap[choice.key] = true;
  158. choice.key = String(choice.key).toLowerCase();
  159. });
  160. if (formatError) {
  161. throw new Error(
  162. 'Format error: `key` param must be a single letter and is required.'
  163. );
  164. }
  165. if (keymap.h) {
  166. throw new Error(
  167. 'Reserved key error: `key` param cannot be `h` - this value is reserved.'
  168. );
  169. }
  170. if (errors.length) {
  171. throw new Error(
  172. 'Duplicate key error: `key` param must be unique. Duplicates: ' +
  173. _.uniq(errors).join(', ')
  174. );
  175. }
  176. }
  177. /**
  178. * Generate a string out of the choices keys
  179. * @param {Array} choices
  180. * @param {Number|String} default - the choice index or name to capitalize
  181. * @return {String} The rendered choices key string
  182. */
  183. generateChoicesString(choices, defaultChoice) {
  184. var defIndex = choices.realLength - 1;
  185. if (_.isNumber(defaultChoice) && this.opt.choices.getChoice(defaultChoice)) {
  186. defIndex = defaultChoice;
  187. } else if (_.isString(defaultChoice)) {
  188. let index = _.findIndex(
  189. choices.realChoices,
  190. ({ value }) => value === defaultChoice
  191. );
  192. defIndex = index === -1 ? defIndex : index;
  193. }
  194. var defStr = this.opt.choices.pluck('key');
  195. this.rawDefault = defStr[defIndex];
  196. defStr[defIndex] = String(defStr[defIndex]).toUpperCase();
  197. return defStr.join('');
  198. }
  199. }
  200. /**
  201. * Function for rendering checkbox choices
  202. * @param {String} pointer Selected key
  203. * @return {String} Rendered content
  204. */
  205. function renderChoices(choices, pointer) {
  206. var output = '';
  207. choices.forEach(choice => {
  208. output += '\n ';
  209. if (choice.type === 'separator') {
  210. output += ' ' + choice;
  211. return;
  212. }
  213. var choiceStr = choice.key + ') ' + choice.name;
  214. if (pointer === choice.key) {
  215. choiceStr = chalk.cyan(choiceStr);
  216. }
  217. output += choiceStr;
  218. });
  219. return output;
  220. }
  221. module.exports = ExpandPrompt;