@@ -7,9 +7,9 @@ import {debounce, isNumeric, isEmpty} from "./Utils";
77/**
88 * Default constants
99 */
10+ const defaultSpeed = 2.5 ;
11+ const defaultAccelerationDelay = 1000 ;
1012const defaultColor = "#3E525F" ;
11- const defaultLongPressDelay = 750 ;
12- const defaultLongPressSpeed = 5 ;
1313const defaultTypingTime = 500 ;
1414
1515/**
@@ -27,6 +27,7 @@ class InputSpinner extends Component {
2727 // Timers
2828 this . increaseTimer = null ;
2929 this . decreaseTimer = null ;
30+ this . holdTime = null ;
3031
3132 let spinnerStep = this . parseNum ( this . props . step ) ;
3233 if ( ! this . typeDecimal ( ) && spinnerStep < 1 ) {
@@ -151,23 +152,31 @@ class InputSpinner extends Component {
151152 this . decreaseTimer = null ;
152153 }
153154
155+ /**
156+ * Clear on change timers
157+ * @private
158+ */
159+ _clearOnChangeTimers ( ) {
160+ this . _clearMaxTimer ( ) ;
161+ this . _clearMinTimer ( ) ;
162+ }
163+
154164 /**
155165 * Clear all timers
156166 * @private
157167 */
158- clearTimers ( ) {
168+ _clearTimers ( ) {
169+ this . _clearOnChangeTimers ( ) ;
159170 this . _clearIncreaseTimer ( ) ;
160171 this . _clearDecreaseTimer ( ) ;
161- this . _clearMaxTimer ( ) ;
162- this . _clearMinTimer ( ) ;
163172 }
164173
165174 /**
166175 * On value change
167176 * @param value
168177 */
169178 async onChange ( value ) {
170- this . clearTimers ( ) ;
179+ this . _clearOnChangeTimers ( ) ;
171180
172181 let num = value ;
173182 let parsedNum = value ;
@@ -188,23 +197,34 @@ class InputSpinner extends Component {
188197 num = parsedNum = this . parseNum ( String ( num ) . replace ( / ^ 0 + / , "" ) ) || 0 ;
189198 if ( ! this . minReached ( num ) ) {
190199 if ( this . maxReached ( num ) ) {
191- parsedNum = this . state . max ;
192- if ( ! isEmpty ( value ) ) {
193- this . maxTimer = this . _debounceSetMax ( ) ;
194- }
195- if ( this . props . onMax ) {
196- this . props . onMax ( this . state . max ) ;
200+ if ( this . maxReached ( num ) ) {
201+ parsedNum = this . state . max ;
202+ if ( ! isEmpty ( value ) ) {
203+ this . maxTimer = this . _debounceSetMax ( ) ;
204+ }
205+ if ( this . props . onMax ) {
206+ this . props . onMax ( this . state . max ) ;
207+ }
197208 }
198209 }
199210 } else {
200- parsedNum = this . state . min ;
201- if ( ! isEmpty ( value ) ) {
211+ if ( ! isEmpty ( value ) && ! this . isEmptied ( ) ) {
212+ parsedNum = this . state . min ;
202213 this . minTimer = this . _debounceSetMin ( ) ;
214+ } else if ( this . isEmptied ( ) ) {
215+ parsedNum = null ;
216+ } else {
217+ parsedNum = this . state . min ;
203218 }
204219 if ( this . props . onMin ) {
205- this . props . onMin ( this . state . min ) ;
220+ this . props . onMin ( parsedNum ) ;
206221 }
207222 }
223+
224+ if ( isEmpty ( value ) && this . isEmptied ( ) ) {
225+ parsedNum = value ;
226+ }
227+
208228 if ( current_value !== num && this . props . onChange ) {
209229 const res = await this . props . onChange ( parsedNum ) ;
210230 if ( ! isEmpty ( value ) ) {
@@ -222,6 +242,15 @@ class InputSpinner extends Component {
222242 }
223243 }
224244
245+ /**
246+ * On buttons press out
247+ * @param e
248+ */
249+ onPressOut ( e ) {
250+ this . _clearTimers ( ) ;
251+ this . _resetHoldTime ( ) ;
252+ }
253+
225254 /**
226255 * On Button Press
227256 * @param buttonDirection
@@ -342,8 +371,6 @@ class InputSpinner extends Component {
342371 return "" ;
343372 } else {
344373 return String ( this . state . min ) ;
345- } else {
346- return this . props . placeholder ;
347374 }
348375 }
349376
@@ -374,11 +401,56 @@ class InputSpinner extends Component {
374401 }
375402
376403 /**
377- * Get time to wait before increase/decrease on long press
404+ * Update holding time
405+ * @private
406+ */
407+ _startHoldTime ( ) {
408+ this . holdTime = new Date ( ) . getTime ( ) ;
409+ }
410+
411+ /**
412+ * Get the holding time
413+ * @private
414+ */
415+ _getHoldTime ( ) {
416+ if ( isEmpty ( this . holdTime ) ) {
417+ return 0 ;
418+ }
419+ let now = new Date ( ) . getTime ( ) ;
420+ return now - this . holdTime ;
421+ }
422+
423+ /**
424+ * Reset holding time
425+ * @private
426+ */
427+ _resetHoldTime ( ) {
428+ this . holdTime = null ;
429+ }
430+
431+ /**
432+ * Find the interval between changing values after a button has been held for a certain amount of time
378433 * @returns {number }
434+ * @author Tom Hardern <https://gist.github.com/taeh98/f709451457400818094d802cd33694d5>
435+ * @private
379436 */
380- getLongPressWaitingTime ( ) {
381- return 1000 / ( this . withinRange ( this . props . onLongPressSpeed , 1 , 10 ) * 2 ) ;
437+ _getHoldChangeInterval ( ) {
438+ const MIN_HOLD_CHANGE_INTERVAL = 10 ; // the minimum time interval between increases or decreases in value when holding a button
439+ const HOLD_CHANGE_ACCELERATION_FACTOR = this . props . acceleration ; // a factor that controls how fast the speed of the change in value will accelerate while a button is held
440+ const HOLD_CHANGE_ACCELERATION_DELAY = this . props . accelerationDelay ; // how long to wait after a button is being held before the acceleration of it speed going through values starts
441+ const TIME_HOLDING_BUTTON_FOR_MIN_CHANGE_INTERVAL =
442+ ( Math . exp ( MIN_HOLD_CHANGE_INTERVAL / - 1 ) +
443+ HOLD_CHANGE_ACCELERATION_DELAY ) /
444+ HOLD_CHANGE_ACCELERATION_FACTOR ;
445+ const HOLD_TIME = this . _getHoldTime ( ) ; // time on hold in ms
446+
447+ return HOLD_TIME >= TIME_HOLDING_BUTTON_FOR_MIN_CHANGE_INTERVAL
448+ ? MIN_HOLD_CHANGE_INTERVAL // if the button has been held for long enough, return the minimum interval
449+ : - 1 *
450+ Math . log (
451+ HOLD_CHANGE_ACCELERATION_FACTOR * HOLD_TIME -
452+ HOLD_CHANGE_ACCELERATION_DELAY ,
453+ ) ; // otherwise calculate with a logarithm => an exponential increase in speed
382454 }
383455
384456 /**
@@ -401,9 +473,10 @@ class InputSpinner extends Component {
401473 }
402474 }
403475
404- let wait = this . getLongPressWaitingTime ( ) ;
476+ let wait = this . _getHoldChangeInterval ( ) ;
405477 if ( this . increaseTimer === null ) {
406- wait = this . props . onLongPressDelay ;
478+ this . _startHoldTime ( ) ;
479+ wait = this . props . accelerationDelay ;
407480 } else {
408481 if ( this . props . onLongPress ) {
409482 await this . props . onLongPress ( num ) ;
@@ -434,9 +507,10 @@ class InputSpinner extends Component {
434507 }
435508 }
436509
437- let wait = this . getLongPressWaitingTime ( ) ;
510+ let wait = this . _getHoldChangeInterval ( ) ;
438511 if ( this . decreaseTimer === null ) {
439- wait = this . props . onLongPressDelay ;
512+ this . _startHoldTime ( ) ;
513+ wait = this . props . accelerationDelay ;
440514 } else {
441515 if ( this . props . onLongPress ) {
442516 await this . props . onLongPress ( num ) ;
@@ -892,7 +966,7 @@ class InputSpinner extends Component {
892966 disabled = { this . _isDisabledButtonLeft ( ) }
893967 style = { buttonStyle }
894968 onPressIn = { this . decrease . bind ( this ) }
895- onPressOut = { this . clearTimers . bind ( this ) }
969+ onPressOut = { this . onPressOut . bind ( this ) }
896970 { ...this . props . leftButtonProps } >
897971 { this . _renderLeftButtonElement ( ) }
898972 </ TouchableHighlight >
@@ -928,7 +1002,7 @@ class InputSpinner extends Component {
9281002 disabled = { this . _isDisabledButtonRight ( ) }
9291003 style = { buttonStyle }
9301004 onPressIn = { this . increase . bind ( this ) }
931- onPressOut = { this . clearTimers . bind ( this ) }
1005+ onPressOut = { this . onPressOut . bind ( this ) }
9321006 { ...this . props . rightButtonProps } >
9331007 { this . _renderRightButtonElement ( ) }
9341008 </ TouchableHighlight >
0 commit comments