UI for Zipcoin Blue

base.js 3.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. /**
  2. * Base prompt implementation
  3. * Should be extended by prompt types.
  4. */
  5. var _ = require('lodash');
  6. var chalk = require('chalk');
  7. var runAsync = require('run-async');
  8. var Choices = require('../objects/choices');
  9. var ScreenManager = require('../utils/screen-manager');
  10. var Prompt = module.exports = function (question, rl, answers) {
  11. // Setup instance defaults property
  12. _.assign(this, {
  13. answers: answers,
  14. status: 'pending'
  15. });
  16. // Set defaults prompt options
  17. this.opt = _.defaults(_.clone(question), {
  18. validate: function () {
  19. return true;
  20. },
  21. filter: function (val) {
  22. return val;
  23. },
  24. when: function () {
  25. return true;
  26. },
  27. suffix: '',
  28. prefix: chalk.green('?')
  29. });
  30. // Check to make sure prompt requirements are there
  31. if (!this.opt.message) {
  32. this.throwParamError('message');
  33. }
  34. if (!this.opt.name) {
  35. this.throwParamError('name');
  36. }
  37. // Normalize choices
  38. if (Array.isArray(this.opt.choices)) {
  39. this.opt.choices = new Choices(this.opt.choices, answers);
  40. }
  41. this.rl = rl;
  42. this.screen = new ScreenManager(this.rl);
  43. };
  44. /**
  45. * Start the Inquiry session and manage output value filtering
  46. * @return {Promise}
  47. */
  48. Prompt.prototype.run = function () {
  49. return new Promise(function (resolve) {
  50. this._run(function (value) {
  51. resolve(value);
  52. });
  53. }.bind(this));
  54. };
  55. // default noop (this one should be overwritten in prompts)
  56. Prompt.prototype._run = function (cb) {
  57. cb();
  58. };
  59. /**
  60. * Throw an error telling a required parameter is missing
  61. * @param {String} name Name of the missing param
  62. * @return {Throw Error}
  63. */
  64. Prompt.prototype.throwParamError = function (name) {
  65. throw new Error('You must provide a `' + name + '` parameter');
  66. };
  67. /**
  68. * Called when the UI closes. Override to do any specific cleanup necessary
  69. */
  70. Prompt.prototype.close = function () {
  71. this.screen.releaseCursor();
  72. };
  73. /**
  74. * Run the provided validation method each time a submit event occur.
  75. * @param {Rx.Observable} submit - submit event flow
  76. * @return {Object} Object containing two observables: `success` and `error`
  77. */
  78. Prompt.prototype.handleSubmitEvents = function (submit) {
  79. var self = this;
  80. var validate = runAsync(this.opt.validate);
  81. var filter = runAsync(this.opt.filter);
  82. var validation = submit.flatMap(function (value) {
  83. return filter(value, self.answers).then(function (filteredValue) {
  84. return validate(filteredValue, self.answers).then(function (isValid) {
  85. return {isValid: isValid, value: filteredValue};
  86. }, function (err) {
  87. return {isValid: err};
  88. });
  89. }, function (err) {
  90. return {isValid: err};
  91. });
  92. }).share();
  93. var success = validation
  94. .filter(function (state) {
  95. return state.isValid === true;
  96. })
  97. .take(1);
  98. var error = validation
  99. .filter(function (state) {
  100. return state.isValid !== true;
  101. })
  102. .takeUntil(success);
  103. return {
  104. success: success,
  105. error: error
  106. };
  107. };
  108. /**
  109. * Generate the prompt question string
  110. * @return {String} prompt question string
  111. */
  112. Prompt.prototype.getQuestion = function () {
  113. var message = this.opt.prefix + ' ' + chalk.bold(this.opt.message) + this.opt.suffix + chalk.reset(' ');
  114. // Append the default if available, and if question isn't answered
  115. if (this.opt.default != null && this.status !== 'answered') {
  116. message += chalk.dim('(' + this.opt.default + ') ');
  117. }
  118. return message;
  119. };