diff --git a/src/hooks/useOffset.ts b/src/hooks/useOffset.ts index 75a729268..c67d78e8a 100644 --- a/src/hooks/useOffset.ts +++ b/src/hooks/useOffset.ts @@ -266,8 +266,17 @@ export default function useOffset( }; }; - const needPush = (dist: number) => { - return (pushable === null && dist === 0) || (typeof pushable === 'number' && dist < pushable); + const needPush = (startValue: number, endValue: number) => { + const dist = endValue - startValue; + // Aligned decimal values can subtract to just below the configured gap. + const tolerance = + Number.EPSILON * + Math.max(Math.abs(startValue), Math.abs(endValue), Math.abs(Number(pushable))); + + return ( + (pushable === null && dist === 0) || + (typeof pushable === 'number' && dist < pushable - tolerance) + ); }; // Values @@ -322,7 +331,7 @@ export default function useOffset( break; } let changed = true; - while (needPush(nextValues[i] - nextValues[i - 1]) && changed) { + while (needPush(nextValues[i - 1], nextValues[i]) && changed) { ({ value: nextValues[i], changed } = offsetChangedValue(nextValues, 1, i)); } const [, itemMaxBound] = getDisabledBoundaryValues( @@ -342,7 +351,7 @@ export default function useOffset( break; } let changed = true; - while (needPush(nextValues[i] - nextValues[i - 1]) && changed) { + while (needPush(nextValues[i - 1], nextValues[i]) && changed) { ({ value: nextValues[i - 1], changed } = offsetChangedValue(nextValues, -1, i - 1)); } const [itemMinBound] = getDisabledBoundaryValues( @@ -363,7 +372,7 @@ export default function useOffset( continue; } let changed = true; - while (needPush(nextValues[i] - nextValues[i - 1]) && changed) { + while (needPush(nextValues[i - 1], nextValues[i]) && changed) { ({ value: nextValues[i - 1], changed } = offsetChangedValue(nextValues, -1, i - 1)); } const [itemMinBound] = getDisabledBoundaryValues( @@ -383,7 +392,7 @@ export default function useOffset( continue; } let changed = true; - while (needPush(nextValues[i + 1] - nextValues[i]) && changed) { + while (needPush(nextValues[i], nextValues[i + 1]) && changed) { ({ value: nextValues[i + 1], changed } = offsetChangedValue(nextValues, 1, i + 1)); } const [, itemMaxBound] = getDisabledBoundaryValues( diff --git a/tests/Range.test.tsx b/tests/Range.test.tsx index dd6ad0044..a8445938c 100644 --- a/tests/Range.test.tsx +++ b/tests/Range.test.tsx @@ -328,6 +328,28 @@ describe('Range', () => { expect(onChange).toHaveBeenCalledWith([0, 90, 100]); }); + it('pushes decimal handles in both directions', () => { + const onChange = jest.fn(); + const { container } = render( + , + ); + doMouseMove(container, 50, 60, 'rc-slider-handle', 0); + fireEvent.mouseUp(document); + expect(onChange).toHaveBeenLastCalledWith([0.6, 0.7, 0.8]); + + doMouseMove(container, 80, 70, 'rc-slider-handle', 2); + fireEvent.mouseUp(document); + expect(onChange).toHaveBeenLastCalledWith([0.5, 0.6, 0.7]); + }); + describe('should render correctly when allowCross', () => { function testLTR(name, func) { it(name, () => {