
How to change numeric input by dragging in React?
The above GIF is one of the interactions that I love aboutFigma . To change the position of an item, I do not have to use a keyboard at all. Just a mouse is enough.
So in this article we are going to create a numeric input field whose value can be changed by dragging on its labelfrom scratch. We needed this forGraftini because we think a visual toolshould not require keyboard for most interactions.
If you are eager to see the code and try it out for yourself, then jump to CodeSandbox athttps://codesandbox.io/s/drag-number-input-z2rnj.
Let us first create a simple input
We can start by creating an input which reads from and writes the values to a state. If you have already created forms this should be simple enough to understand.
functionInput(){const[value,setValue]=useState(0);constonInputChange=useCallback((ev)=>setValue(parseInt(ev.target.value,10)),[]);return(<inputvalue={value}onChange={onInputChange}style={{padding:8,}}/>);}
We are creating a state which stores the current value of the input. This state will be updated when the input changes via the keyboard.
Now we need a label that fits right in with the input
We need the label to be an anchor on which the mouse interactions can be added. The input itself cannot be the anchor because then it will ruin how input fields normally are expected to behave. The code that captures that idea can look like:
<divstyle={{display:"flex",border:"1px solid #CCC",alignItems:"center",borderRadius:4,fontFamily:"sans-serif",width:300,}}><spanstyle={{padding:8,color:"gray",cursor:"ew-resize",userSelect:"none",}}>Count</span><inputvalue={value}onChange={onInputChange}style={{flex:1,padding:8,border:"none",outline:"none",}}/></div>
The above code is just visual cosmetics. You can make it to look however you see fit. Now the input should look something like:
Adding mouse interactions on the label
We will extract the label into its own component to make it easier to write & understand the code. Then we will add three mouse interactions in it. One on the label itself and two on the document. Why? We will discuss it along side the code.
functionDragLabel({value,setValue}){// We are creating a snapshot of the values when the drag starts// because the [value] will itself change & we need the original// [value] to calculate during a drag.const[snapshot,setSnapshot]=useState(value);// This captures the starting position of the drag and is used to// calculate the diff in positions of the cursor.const[startVal,setStartVal]=useState(0);// Start the drag to change operation when the mouse button is down.constonStart=useCallback((event)=>{setStartVal(event.clientX);setSnapshot(value);},[value]);// We use document events to update and end the drag operation// because the mouse may not be present over the label during// the operation..useEffect(()=>{// Only change the value if the drag was actually started.constonUpdate=(event)=>{if(startVal){setValue(event.clientX-snapshot);}};// Stop the drag operation now.constonEnd=()=>{setStartVal(0);};document.addEventListener("mousemove",onUpdate);document.addEventListener("mouseup",onEnd);return()=>{document.removeEventListener("mousemove",onUpdate);document.removeEventListener("mouseup",onEnd);};},[startVal,setValue,snapshot]);return(<spanonMouseDown={onStart}style={{padding:8,color:"gray",cursor:"ew-resize",userSelect:"none",}}>Count</span>);}
Now try running it up and voila 🎉🎊 you have your own drag to change numeric input.
It looks awesome doesn't it? Though the cursor during the operation does not look good. This could be something that you can fix. 😋
The full code is at CodeSandbox for you to try it outhttps://codesandbox.io/s/drag-number-input-z2rnj?file=/src/App.js.
Fork it & make improvements to it. Till then ✌️.
Top comments(3)

Change:
if (startVal) { setValue(event.clientX - snapshot);}
to:
if (startVal) { setValue(snapshot + event.clientX - startVal);}
to prevent zeroing out the input on next drag
- LocationVictoria BC
- WorkIntermediate Full Stack Engineer at Certn
- Joined
Very nice start but you'll have to do some math if you want a default value other than 0!
For further actions, you may consider blocking this person and/orreporting abuse