11const color = require ( 'clorox' ) ;
22const Prompt = require ( './prompt' ) ;
33const { cursor, erase } = require ( 'sisteransi' ) ;
4- const { style : stl , clear } = require ( '../util' ) ;
4+ const { style, clear } = require ( '../util' ) ;
55
66const isNumber = / [ 0 - 9 ] / ;
7+ const isValidChar = / \. | - / ;
78const isDef = any => any !== undefined ;
8- const isNull = any => any === null ;
9+ const round = ( number , precision ) => {
10+ let factor = Math . pow ( 10 , precision ) ;
11+ return Math . round ( number * factor ) / factor ;
12+ }
913
14+ /**
15+ * NumberPrompt Base Element
16+ * @param {Object } opts Options
17+ * @param {String } opts.message Message
18+ * @param {String } [opts.style='default'] Render style
19+ * @param {Number } [opts.initial] Default value
20+ * @param {Number } [opts.max=+Infinity] Max value
21+ * @param {Number } [opts.min=-Infinity] Min value
22+ * @param {Boolean } [opts.float=false] Parse input as floats
23+ * @param {Number } [opts.round=2] Round floats to x decimals
24+ * @param {Number } [opts.increment=1] Number to increment by when using arrow-keys
25+ */
1026class NumberPrompt extends Prompt {
11- constructor ( { message, initial = '' , min, max, style = 'default' } ) {
12- super ( ) ;
13-
14- this . msg = message ;
15- this . transform = stl . render ( style ) ;
16-
17- this . min = isDef ( min ) ? min : - Infinity ;
18- this . max = isDef ( max ) ? max : Infinity ;
19- this . value = initial ;
20-
27+ constructor ( opts = { } ) {
28+ super ( opts ) ;
29+ this . transform = style . render ( opts . style ) ;
30+ this . msg = opts . message ;
31+ this . initial = isDef ( opts . initial ) ? opts . initial : '' ;
32+ this . float = ! ! opts . float ;
33+ this . round = opts . round || 2 ;
34+ this . inc = opts . increment || 1 ;
35+ this . min = isDef ( opts . min ) ? opts . min : - Infinity ;
36+ this . max = isDef ( opts . max ) ? opts . max : Infinity ;
37+ this . value = ''
2138 this . typed = '' ;
2239 this . lastHit = 0 ;
40+ this . render ( ) ;
41+ }
2342
24- this . initialValue = this . value ;
43+ set value ( v ) {
44+ if ( ! v && v !== 0 ) {
45+ this . placeholder = true ;
46+ this . rendered = color . gray ( this . transform . render ( `${ this . initial } ` ) ) ;
47+ } else {
48+ this . placeholder = false ;
49+ this . rendered = this . transform . render ( `${ round ( v , this . round ) } ` ) ;
50+ }
51+ this . _value = round ( v , this . round ) ;
52+ this . fire ( ) ;
53+ }
2554
26- this . render ( ) ;
55+ get value ( ) {
56+ return this . _value ;
57+ }
58+
59+ parse ( x ) {
60+ return this . float ? parseFloat ( x ) : parseInt ( x ) ;
61+ }
62+
63+ valid ( c ) {
64+ return c === '-' || c === '.' && this . float || isNumber . test ( c )
2765 }
2866
2967 reset ( ) {
3068 this . typed = '' ;
31- this . value = this . initialValue ;
69+ this . value = '' ;
3270 this . fire ( ) ;
3371 this . render ( ) ;
3472 }
3573
3674 abort ( ) {
75+ this . value = this . value || this . initial ;
3776 this . done = this . aborted = true ;
3877 this . fire ( ) ;
3978 this . render ( ) ;
@@ -42,6 +81,7 @@ class NumberPrompt extends Prompt {
4281 }
4382
4483 submit ( ) {
84+ this . value = this . value || this . initial ;
4585 this . done = true ;
4686 this . aborted = false ;
4787 this . fire ( ) ;
@@ -53,54 +93,60 @@ class NumberPrompt extends Prompt {
5393 up ( ) {
5494 this . typed = '' ;
5595 if ( this . value >= this . max ) return this . bell ( ) ;
56- this . value ++ ;
96+ this . value += this . inc ;
5797 this . fire ( ) ;
5898 this . render ( ) ;
5999 }
60100
61101 down ( ) {
62102 this . typed = '' ;
63103 if ( this . value <= this . min ) return this . bell ( ) ;
64- this . value -- ;
104+ this . value -= this . inc ;
65105 this . fire ( ) ;
66106 this . render ( ) ;
67107 }
68108
69109 delete ( ) {
70110 let val = this . value . toString ( ) ;
71111 if ( val . length === 0 ) return this . bell ( ) ;
72- this . value = parseInt ( ( val = val . slice ( 0 , - 1 ) ) ) || '' ;
112+ this . value = this . parse ( ( val = val . slice ( 0 , - 1 ) ) ) || '' ;
113+ this . fire ( ) ;
114+ this . render ( ) ;
115+ }
116+
117+ next ( ) {
118+ this . value = this . initial ;
73119 this . fire ( ) ;
74120 this . render ( ) ;
75121 }
76122
77123 _ ( c , key ) {
78- if ( ! isNumber . test ( c ) ) return this . bell ( ) ;
124+ if ( ! this . valid ( c ) ) return this . bell ( ) ;
79125
80126 const now = Date . now ( ) ;
81127 if ( now - this . lastHit > 1000 ) this . typed = '' ; // 1s elapsed
82128 this . typed += c ;
83129 this . lastHit = now ;
84130
85- this . value = Math . min ( parseInt ( this . typed ) , this . max ) ;
131+ if ( c === '.' ) return this . fire ( ) ;
132+
133+ this . value = Math . min ( this . parse ( this . typed ) , this . max ) ;
86134 if ( this . value > this . max ) this . value = this . max ;
87135 if ( this . value < this . min ) this . value = this . min ;
88136 this . fire ( ) ;
89137 this . render ( ) ;
90138 }
91139
92140 render ( ) {
93- let value = this . transform . render ( this . value !== null ? this . value : '' ) ;
94- if ( ! this . done ) value = color . cyan . underline ( value ) ;
95-
141+ let underline = ! this . done || ( ! this . done && ! this . placeholder ) ;
96142 this . out . write (
97143 erase . line +
98144 cursor . to ( 0 ) +
99145 [
100- stl . symbol ( this . done , this . aborted ) ,
146+ style . symbol ( this . done , this . aborted ) ,
101147 color . bold ( this . msg ) ,
102- stl . delimiter ( this . done ) ,
103- value
148+ style . delimiter ( this . done ) ,
149+ underline ? color . cyan . underline ( this . rendered ) : this . rendered
104150 ] . join ( ' ' )
105151 ) ;
106152 }
0 commit comments