From ecbf399a9b23d85b47f25779dce9b05a5836cc6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=B3=E5=BB=B7=E5=AE=89?= <73953029+nrps9909@users.noreply.github.com> Date: Thu, 27 Aug 2026 23:40:45 +0800 Subject: [PATCH 1/3] fix: preserve decimal pushable gaps --- src/hooks/useOffset.ts | 25 +++++++++++++++++++------ tests/Range.test.tsx | 22 ++++++++++++++++++++++ 2 files changed, 41 insertions(+), 6 deletions(-) diff --git a/src/hooks/useOffset.ts b/src/hooks/useOffset.ts index 75a729268..86e1b4027 100644 --- a/src/hooks/useOffset.ts +++ b/src/hooks/useOffset.ts @@ -266,8 +266,21 @@ 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; + + if (pushable === null) { + return dist === 0; + } + + if (typeof pushable === 'number') { + // 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(pushable)); + return dist < pushable - tolerance; + } + + return false; }; // Values @@ -322,7 +335,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 +355,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 +376,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 +396,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, () => { From 6c9b24bcb5e670df04e35e2591576945ebacf37c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=B3=E5=BB=B7=E5=AE=89?= <73953029+nrps9909@users.noreply.github.com> Date: Thu, 27 Aug 2026 23:49:14 +0800 Subject: [PATCH 2/3] test: keep push tolerance coverage focused --- src/hooks/useOffset.ts | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/src/hooks/useOffset.ts b/src/hooks/useOffset.ts index 86e1b4027..ada83b564 100644 --- a/src/hooks/useOffset.ts +++ b/src/hooks/useOffset.ts @@ -268,19 +268,16 @@ export default function useOffset( const needPush = (startValue: number, endValue: number) => { const dist = endValue - startValue; - - if (pushable === null) { - return dist === 0; - } - - if (typeof pushable === 'number') { - // 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(pushable)); - return dist < pushable - tolerance; - } - - return false; + // Aligned decimal values can subtract to just below the configured gap. + const tolerance = + typeof pushable === 'number' + ? Number.EPSILON * Math.max(Math.abs(startValue), Math.abs(endValue), Math.abs(pushable)) + : 0; + + return ( + (pushable === null && dist === 0) || + (typeof pushable === 'number' && dist < pushable - tolerance) + ); }; // Values From e6bf453a93ebadd4ee3ee867dba612b2be15594e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=B3=E5=BB=B7=E5=AE=89?= <73953029+nrps9909@users.noreply.github.com> Date: Thu, 27 Aug 2026 23:52:24 +0800 Subject: [PATCH 3/3] test: keep decimal pushable patch fully covered --- src/hooks/useOffset.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/hooks/useOffset.ts b/src/hooks/useOffset.ts index ada83b564..c67d78e8a 100644 --- a/src/hooks/useOffset.ts +++ b/src/hooks/useOffset.ts @@ -270,9 +270,8 @@ export default function useOffset( const dist = endValue - startValue; // Aligned decimal values can subtract to just below the configured gap. const tolerance = - typeof pushable === 'number' - ? Number.EPSILON * Math.max(Math.abs(startValue), Math.abs(endValue), Math.abs(pushable)) - : 0; + Number.EPSILON * + Math.max(Math.abs(startValue), Math.abs(endValue), Math.abs(Number(pushable))); return ( (pushable === null && dist === 0) ||