@@ -20,17 +20,21 @@ export default class RangeStepInput extends React.Component {
2020 this . onMouseDown = this . onMouseDown . bind ( this ) ;
2121 this . onMouseUp = this . onMouseUp . bind ( this ) ;
2222 this . onMouseMove = this . onMouseMove . bind ( this ) ;
23+
24+ this . domRef = React . createRef ( ) ;
2325 }
2426 render ( ) {
2527 return < input
2628 type = "range"
29+ ref = { this . domRef }
2730 className = { this . props . className }
2831 min = { this . props . min }
2932 max = { this . props . max }
3033 step = { this . props . step }
3134 value = { this . props . value }
3235 name = { this . props . name }
3336 id = { this . props . id }
37+ disabled = { this . props . disabled }
3438 onChange = { this . props . onChange }
3539 onMouseDown = { this . onMouseDown }
3640 onMouseUp = { this . onMouseUp }
@@ -40,12 +44,25 @@ export default class RangeStepInput extends React.Component {
4044 }
4145 onMouseDown ( ) {
4246 this . setState ( { isMouseDown : true } ) ;
47+
48+ if ( this . props . hold ) {
49+ const oldVal = this . props . value ;
50+ const self = this ;
51+ // Add some initial delay on the click-hold functionality.
52+ setTimeout ( function ( ) {
53+ self . holdLoop = self . makeHoldLoop ( oldVal ) ;
54+ } , 600 ) ;
55+ }
4356 }
4457 onMouseUp ( ) {
4558 this . setState ( {
4659 isMouseDown : false ,
4760 isDragging : false
4861 } ) ;
62+
63+ if ( this . holdLoop ) {
64+ clearInterval ( this . holdLoop ) ;
65+ }
4966 }
5067 onMouseMove ( ) {
5168 if ( this . state . isMouseDown ) {
@@ -67,7 +84,45 @@ export default class RangeStepInput extends React.Component {
6784 oldVal + step : oldVal - step ;
6885 }
6986 }
70- }
87+ makeHoldLoop ( oldVal ) {
88+ const self = this ;
89+
90+ return setInterval ( function ( ) {
91+ if ( ! self . state . isMouseDown ) {
92+ // The user isn't holding the cursor anymore: clean up
93+ // and cancel.
94+ if ( self . holdLoop ) {
95+ clearInterval ( self . holdLoop ) ;
96+ }
97+ return false ;
98+ }
99+
100+ const input = self . domRef . current ;
101+
102+ let newVal = self . props . value ;
103+ if ( oldVal > newVal ) {
104+ newVal -= self . props . step ;
105+ } else if ( oldVal < newVal ) {
106+ newVal += self . props . step ;
107+ } else {
108+ // The user is just holding the cursor at the current
109+ // value, so don't do anything.
110+ return false ;
111+ }
112+
113+ // Directly setting input.value will cause the new value
114+ // to not be recognized, because of React.
115+ // https://stackoverflow.com/a/46012210/173630
116+ const nativeInputValueSetter = Object . getOwnPropertyDescriptor (
117+ window . HTMLInputElement . prototype , 'value' ) . set ;
118+ nativeInputValueSetter . call ( input , newVal ) ;
119+
120+ // Trigger an onChange event.
121+ const e = new Event ( 'change' , { bubbles : true } ) ;
122+ return input . dispatchEvent ( e ) ;
123+ } , 100 ) ;
124+ }
125+ } ;
71126
72127RangeStepInput . propTypes = {
73128 value : PropTypes . number . isRequired ,
@@ -77,5 +132,14 @@ RangeStepInput.propTypes = {
77132 min : PropTypes . number ,
78133 max : PropTypes . number ,
79134 id : PropTypes . string ,
80- name : PropTypes . string
135+ name : PropTypes . string ,
136+ disabled : PropTypes . bool ,
137+
138+ // Determines whether the slider changes value when the cursor is
139+ // held on it.
140+ hold : PropTypes . bool
141+ } ;
142+
143+ RangeStepInput . defaultProps = {
144+ hold : true
81145} ;
0 commit comments