TanStack/ranger

Support moving the segment.

mrybinski opened this issue · 2 comments

Excellent library! Really easy to use. I starred it and will watch closely.
I can see you're working on the new version, I didn't check it out yet, I just used the latest from npm. So if you already want to add that feature, great!
It would be awesome if you can support segment moving. I forked your lib locally and prototyped the moving (but only assuming there are 2 handles). As you will see, I forked the master, not the beta, that's why I didn't create PR.

My prototype assumes there are 2 handles, but it can be easily extended, to support moving segments between any values (and only moving the ones that have handles on each end).
Let me know if that is something you would like to support. If so, I can spend some free time working on the beta version PR, unless you want to work on this yourself.
For now, I will use my fork of your lib, and include your license message.
Here's the video, and below code that I used.

Screen.Recording.2023-01-11.at.11.44.47.mov

In short, I added this event on segment press:

const handleSegmentPress = React.useCallback(
		(e) => {
			const clientX = e.type === 'touchmove' ? e.changedTouches[0].clientX : e.clientX;

			const onDrag = e => handleSegmentDrag(e, clientX);
			const handleRelease = e => {
				const {
					tempValues,
					values,
					onChange = () => {},
				} = getLatest();

				document.removeEventListener('mousemove', onDrag);
				document.removeEventListener('touchmove', onDrag);
				document.removeEventListener('mouseup', handleRelease);
				document.removeEventListener('touchend', handleRelease);
				const sortedValues = sortNumList(tempValues || values);
				onChange(sortedValues);
				onDrag(sortedValues);
				setTempValues();
			};

			document.addEventListener('mousemove', onDrag);
			document.addEventListener('touchmove', onDrag);
			document.addEventListener('mouseup', handleRelease);
			document.addEventListener('touchend', handleRelease);
		},
		[getLatest, handleSegmentDrag]
	);

and this is handleSegmentDrag:

const handleSegmentDrag = React.useCallback(
		(e, initialX) => {
			const clientX = e.type === 'touchmove' ? e.changedTouches[0].clientX : e.clientX;
			const initial = getValueForClientX(initialX);
			const newValue = getValueForClientX(clientX);
			const diff = newValue - initial;
			if (diff) {
				let actualDiff = 0;
				if (diff > 0) {
					const last = values[values.length - 1];
					const newRoundedLastValue = roundToStep(last + diff);

					actualDiff = newRoundedLastValue - last;
					
				} else {
					const first = values[0];
					const newRoundedFirstValue = roundToStep(first + diff);
					actualDiff = newRoundedFirstValue - first;
				}

				if (actualDiff) {
					setTempValues(values.map(v => v += actualDiff));
				}
			}
			
		},
		[getValueForClientX, roundToStep, values]
	);

Thanks. Once migration is done for sure we can consider it!