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.

139 lines
3.8 KiB

4 years ago
  1. 'use strict';
  2. const ArrayPrompt = require('../types/array');
  3. const utils = require('../utils');
  4. class SelectPrompt extends ArrayPrompt {
  5. constructor(options) {
  6. super(options);
  7. this.emptyError = this.options.emptyError || 'No items were selected';
  8. }
  9. async dispatch(s, key) {
  10. if (this.multiple) {
  11. return this[key.name] ? await this[key.name](s, key) : await super.dispatch(s, key);
  12. }
  13. this.alert();
  14. }
  15. separator() {
  16. if (this.options.separator) return super.separator();
  17. let sep = this.styles.muted(this.symbols.ellipsis);
  18. return this.state.submitted ? super.separator() : sep;
  19. }
  20. pointer(choice, i) {
  21. return (!this.multiple || this.options.pointer) ? super.pointer(choice, i) : '';
  22. }
  23. indicator(choice, i) {
  24. return this.multiple ? super.indicator(choice, i) : '';
  25. }
  26. choiceMessage(choice, i) {
  27. let message = this.resolve(choice.message, this.state, choice, i);
  28. if (choice.role === 'heading' && !utils.hasColor(message)) {
  29. message = this.styles.strong(message);
  30. }
  31. return this.resolve(message, this.state, choice, i);
  32. }
  33. choiceSeparator() {
  34. return ':';
  35. }
  36. async renderChoice(choice, i) {
  37. await this.onChoice(choice, i);
  38. let focused = this.index === i;
  39. let pointer = await this.pointer(choice, i);
  40. let check = await this.indicator(choice, i) + (choice.pad || '');
  41. let hint = await this.resolve(choice.hint, this.state, choice, i);
  42. if (hint && !utils.hasColor(hint)) {
  43. hint = this.styles.muted(hint);
  44. }
  45. let ind = this.indent(choice);
  46. let msg = await this.choiceMessage(choice, i);
  47. let line = () => [this.margin[3], ind + pointer + check, msg, this.margin[1], hint].filter(Boolean).join(' ');
  48. if (choice.role === 'heading') {
  49. return line();
  50. }
  51. if (choice.disabled) {
  52. if (!utils.hasColor(msg)) {
  53. msg = this.styles.disabled(msg);
  54. }
  55. return line();
  56. }
  57. if (focused) {
  58. msg = this.styles.em(msg);
  59. }
  60. return line();
  61. }
  62. async renderChoices() {
  63. if (this.state.loading === 'choices') {
  64. return this.styles.warning('Loading choices');
  65. }
  66. if (this.state.submitted) return '';
  67. let choices = this.visible.map(async(ch, i) => await this.renderChoice(ch, i));
  68. let visible = await Promise.all(choices);
  69. if (!visible.length) visible.push(this.styles.danger('No matching choices'));
  70. let result = this.margin[0] + visible.join('\n');
  71. let header;
  72. if (this.options.choicesHeader) {
  73. header = await this.resolve(this.options.choicesHeader, this.state);
  74. }
  75. return [header, result].filter(Boolean).join('\n');
  76. }
  77. format() {
  78. if (!this.state.submitted || this.state.cancelled) return '';
  79. if (Array.isArray(this.selected)) {
  80. return this.selected.map(choice => this.styles.primary(choice.name)).join(', ');
  81. }
  82. return this.styles.primary(this.selected.name);
  83. }
  84. async render() {
  85. let { submitted, size } = this.state;
  86. let prompt = '';
  87. let header = await this.header();
  88. let prefix = await this.prefix();
  89. let separator = await this.separator();
  90. let message = await this.message();
  91. if (this.options.promptLine !== false) {
  92. prompt = [prefix, message, separator, ''].join(' ');
  93. this.state.prompt = prompt;
  94. }
  95. let output = await this.format();
  96. let help = (await this.error()) || (await this.hint());
  97. let body = await this.renderChoices();
  98. let footer = await this.footer();
  99. if (output) prompt += output;
  100. if (help && !prompt.includes(help)) prompt += ' ' + help;
  101. if (submitted && !output && !body.trim() && this.multiple && this.emptyError != null) {
  102. prompt += this.styles.danger(this.emptyError);
  103. }
  104. this.clear(size);
  105. this.write([header, prompt, body, footer].filter(Boolean).join('\n'));
  106. this.write(this.margin[2]);
  107. this.restore();
  108. }
  109. }
  110. module.exports = SelectPrompt;