Skip to content

Commit

Permalink
fix(rangeslider): fix mouse/value attachment logic with debounce inte…
Browse files Browse the repository at this point in the history
…rval
  • Loading branch information
xavierdonnellon committed Aug 1, 2023
1 parent cd5a3fb commit 388938b
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 26 deletions.
4 changes: 2 additions & 2 deletions src/components/RangeSlider/RangeSlider.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,8 @@ export const Default: Story<DefaultProps> = ({
min={min}
max={max}
debounceInterval={debounceInterval}
onDebounceChange={newVal => {
action('onDebounceChange')(newVal);
onChange={newVal => {
action('onChange')(newVal);
setVal(Math.round(newVal));
}}
dragHandleAttachment={dragHandleAttachment}
Expand Down
65 changes: 41 additions & 24 deletions src/components/RangeSlider/RangeSlider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -311,9 +311,13 @@ export const RangeSlider = ({

const [ref, sliderBounds] = useMeasure({ polyfill: ResizeObserver });

const pixelPositions = processedValues.map(val => {
return (val.value / domain) * sliderBounds.width;
});
const pixelPositions = useMemo(
() =>
processedValues.map(val => {
return (val.value / domain) * sliderBounds.width;
}),
[processedValues, sliderBounds, domain],
);

// get the x offset and an animation setter function
const [{ dragHandleX }, springRef] = useSpring(() => ({
Expand Down Expand Up @@ -378,25 +382,29 @@ export const RangeSlider = ({
};

const bind = useDrag(
({ down, movement: [deltaX] }) => {
({ down: isDragging, movement: [deltaX] }) => {
if (readonly) return;

const delta = (deltaX / sliderBounds.width) * domain;
valueBuffer.current = clamp(delta, min, max);
setDraggedHandle(down ? 0 : -1);
setDraggedHandle(isDragging ? 0 : -1);
handleDrag(valueBuffer.current);

let animate = true;
if (prefersReducedMotion) animate = false;
if (!snapToValue) animate = springOnRelease ? !down : false;

springRef.start({
// Should handle follow value or drag gesture?
dragHandleX: snapToValue || !down ? (pixelPositions ?? [0])[0] : deltaX,

immediate: !animate,
config: { friction: 13, tension: 100 },
});
if (dragHandleAttachment === 'mouse') {
if (isDragging) {
// constantly follow mouse during drag
springRef.start({
dragHandleX: deltaX,
immediate: true,
});
} else {
// after drag release, spring to value
springRef.start({
dragHandleX: pixelPositions[0],
immediate: !springOnRelease,
});
}
}
},
{
initial: [(pixelPositions ?? [0])[0], 0],
Expand All @@ -411,23 +419,32 @@ export const RangeSlider = ({
},
);

// Snap to value on initial load and when pixelPositions changes (on click)
// Once sliderBounds are read, set initial position
useEffect(() => {
if (draggedHandle >= 0) return;
if (isInitializing.current && sliderBounds.width) {
springRef.start({
dragHandleX: pixelPositions[0],
immediate: true,
onResolve: () => {
isInitializing.current = false;
},
});
}
}, [springRef, sliderBounds, isInitializing, pixelPositions]);

if (sliderBounds.x) {
// For snap to value, listen to changes in value and always animate to value
useEffect(() => {
if (snapToValue) {
springRef.start({
dragHandleX: pixelPositions[0],

immediate: prefersReducedMotion || isInitializing.current,
immediate: prefersReducedMotion,
config: { friction: 13, tension: 100 },
onResolve: () => {
if (isInitializing) isInitializing.current = false;
},
});
}
}, [springRef, pixelPositions, draggedHandle, prefersReducedMotion, sliderBounds]);
}, [snapToValue, springRef, pixelPositions, prefersReducedMotion, sliderBounds]);

// Dispose of debounce timers
useEffect(() => {
return () => {
debouncedOnChange.cancel();
Expand Down

0 comments on commit 388938b

Please sign in to comment.