number.js 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. "use strict";
  2. function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
  3. function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
  4. const color = require('kleur');
  5. const Prompt = require('./prompt');
  6. const _require = require('sisteransi'),
  7. cursor = _require.cursor,
  8. erase = _require.erase;
  9. const _require2 = require('../util'),
  10. style = _require2.style,
  11. figures = _require2.figures,
  12. clear = _require2.clear,
  13. lines = _require2.lines;
  14. const isNumber = /[0-9]/;
  15. const isDef = any => any !== undefined;
  16. const round = (number, precision) => {
  17. let factor = Math.pow(10, precision);
  18. return Math.round(number * factor) / factor;
  19. };
  20. /**
  21. * NumberPrompt Base Element
  22. * @param {Object} opts Options
  23. * @param {String} opts.message Message
  24. * @param {String} [opts.style='default'] Render style
  25. * @param {Number} [opts.initial] Default value
  26. * @param {Number} [opts.max=+Infinity] Max value
  27. * @param {Number} [opts.min=-Infinity] Min value
  28. * @param {Boolean} [opts.float=false] Parse input as floats
  29. * @param {Number} [opts.round=2] Round floats to x decimals
  30. * @param {Number} [opts.increment=1] Number to increment by when using arrow-keys
  31. * @param {Function} [opts.validate] Validate function
  32. * @param {Stream} [opts.stdin] The Readable stream to listen to
  33. * @param {Stream} [opts.stdout] The Writable stream to write readline data to
  34. * @param {String} [opts.error] The invalid error label
  35. */
  36. class NumberPrompt extends Prompt {
  37. constructor(opts = {}) {
  38. super(opts);
  39. this.transform = style.render(opts.style);
  40. this.msg = opts.message;
  41. this.initial = isDef(opts.initial) ? opts.initial : '';
  42. this.float = !!opts.float;
  43. this.round = opts.round || 2;
  44. this.inc = opts.increment || 1;
  45. this.min = isDef(opts.min) ? opts.min : -Infinity;
  46. this.max = isDef(opts.max) ? opts.max : Infinity;
  47. this.errorMsg = opts.error || `Please Enter A Valid Value`;
  48. this.validator = opts.validate || (() => true);
  49. this.color = `cyan`;
  50. this.value = ``;
  51. this.typed = ``;
  52. this.lastHit = 0;
  53. this.render();
  54. }
  55. set value(v) {
  56. if (!v && v !== 0) {
  57. this.placeholder = true;
  58. this.rendered = color.gray(this.transform.render(`${this.initial}`));
  59. this._value = ``;
  60. } else {
  61. this.placeholder = false;
  62. this.rendered = this.transform.render(`${round(v, this.round)}`);
  63. this._value = round(v, this.round);
  64. }
  65. this.fire();
  66. }
  67. get value() {
  68. return this._value;
  69. }
  70. parse(x) {
  71. return this.float ? parseFloat(x) : parseInt(x);
  72. }
  73. valid(c) {
  74. return c === `-` || c === `.` && this.float || isNumber.test(c);
  75. }
  76. reset() {
  77. this.typed = ``;
  78. this.value = ``;
  79. this.fire();
  80. this.render();
  81. }
  82. exit() {
  83. this.abort();
  84. }
  85. abort() {
  86. let x = this.value;
  87. this.value = x !== `` ? x : this.initial;
  88. this.done = this.aborted = true;
  89. this.error = false;
  90. this.fire();
  91. this.render();
  92. this.out.write(`\n`);
  93. this.close();
  94. }
  95. validate() {
  96. var _this = this;
  97. return _asyncToGenerator(function* () {
  98. let valid = yield _this.validator(_this.value);
  99. if (typeof valid === `string`) {
  100. _this.errorMsg = valid;
  101. valid = false;
  102. }
  103. _this.error = !valid;
  104. })();
  105. }
  106. submit() {
  107. var _this2 = this;
  108. return _asyncToGenerator(function* () {
  109. yield _this2.validate();
  110. if (_this2.error) {
  111. _this2.color = `red`;
  112. _this2.fire();
  113. _this2.render();
  114. return;
  115. }
  116. let x = _this2.value;
  117. _this2.value = x !== `` ? x : _this2.initial;
  118. _this2.done = true;
  119. _this2.aborted = false;
  120. _this2.error = false;
  121. _this2.fire();
  122. _this2.render();
  123. _this2.out.write(`\n`);
  124. _this2.close();
  125. })();
  126. }
  127. up() {
  128. this.typed = ``;
  129. if (this.value === '') {
  130. this.value = this.min - this.inc;
  131. }
  132. if (this.value >= this.max) return this.bell();
  133. this.value += this.inc;
  134. this.color = `cyan`;
  135. this.fire();
  136. this.render();
  137. }
  138. down() {
  139. this.typed = ``;
  140. if (this.value === '') {
  141. this.value = this.min + this.inc;
  142. }
  143. if (this.value <= this.min) return this.bell();
  144. this.value -= this.inc;
  145. this.color = `cyan`;
  146. this.fire();
  147. this.render();
  148. }
  149. delete() {
  150. let val = this.value.toString();
  151. if (val.length === 0) return this.bell();
  152. this.value = this.parse(val = val.slice(0, -1)) || ``;
  153. if (this.value !== '' && this.value < this.min) {
  154. this.value = this.min;
  155. }
  156. this.color = `cyan`;
  157. this.fire();
  158. this.render();
  159. }
  160. next() {
  161. this.value = this.initial;
  162. this.fire();
  163. this.render();
  164. }
  165. _(c, key) {
  166. if (!this.valid(c)) return this.bell();
  167. const now = Date.now();
  168. if (now - this.lastHit > 1000) this.typed = ``; // 1s elapsed
  169. this.typed += c;
  170. this.lastHit = now;
  171. this.color = `cyan`;
  172. if (c === `.`) return this.fire();
  173. this.value = Math.min(this.parse(this.typed), this.max);
  174. if (this.value > this.max) this.value = this.max;
  175. if (this.value < this.min) this.value = this.min;
  176. this.fire();
  177. this.render();
  178. }
  179. render() {
  180. if (this.closed) return;
  181. if (!this.firstRender) {
  182. if (this.outputError) this.out.write(cursor.down(lines(this.outputError, this.out.columns) - 1) + clear(this.outputError, this.out.columns));
  183. this.out.write(clear(this.outputText, this.out.columns));
  184. }
  185. super.render();
  186. this.outputError = ''; // Print prompt
  187. this.outputText = [style.symbol(this.done, this.aborted), color.bold(this.msg), style.delimiter(this.done), !this.done || !this.done && !this.placeholder ? color[this.color]().underline(this.rendered) : this.rendered].join(` `); // Print error
  188. if (this.error) {
  189. this.outputError += this.errorMsg.split(`\n`).reduce((a, l, i) => a + `\n${i ? ` ` : figures.pointerSmall} ${color.red().italic(l)}`, ``);
  190. }
  191. this.out.write(erase.line + cursor.to(0) + this.outputText + cursor.save + this.outputError + cursor.restore);
  192. }
  193. }
  194. module.exports = NumberPrompt;