Skip to content

Commit 7b60ace

Browse files
刘欢claude
andcommitted
fix: apply disabled handle boundaries when clicking slider
Ensure that when clicking the slider to move a handle, the new value is constrained by the positions of neighboring disabled handles to prevent crossing. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent a7db356 commit 7b60ace

File tree

2 files changed

+56
-1
lines changed

2 files changed

+56
-1
lines changed

src/Slider.tsx

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,7 @@ const Slider = React.forwardRef<SliderRef, SliderProps<number | number[]>>((prop
407407
cloneNextValues.splice(valueBeforeIndex + 1, 0, newValue);
408408
focusIndex = valueBeforeIndex + 1;
409409
} else {
410+
// Find nearest non-disabled handle if current is disabled
410411
if (isHandleDisabled(valueIndex)) {
411412
let nearestIndex = -1;
412413
let nearestDist = mergedMax - mergedMin;
@@ -428,7 +429,21 @@ const Slider = React.forwardRef<SliderRef, SliderProps<number | number[]>>((prop
428429

429430
valueIndex = nearestIndex;
430431
}
431-
cloneNextValues[valueIndex] = newValue;
432+
433+
// Apply disabled handle boundaries to prevent crossing
434+
let minBound = mergedMin;
435+
let maxBound = mergedMax;
436+
rawValues.forEach((val, idx) => {
437+
if (isHandleDisabled(idx)) {
438+
if (idx < valueIndex) {
439+
minBound = Math.max(minBound, val);
440+
} else if (idx > valueIndex) {
441+
maxBound = Math.min(maxBound, val);
442+
}
443+
}
444+
});
445+
446+
cloneNextValues[valueIndex] = Math.max(minBound, Math.min(maxBound, newValue));
432447
focusIndex = valueIndex;
433448
}
434449

tests/Range.test.tsx

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -981,5 +981,45 @@ describe('Range', () => {
981981

982982
expect(onChange).not.toHaveBeenCalled();
983983
});
984+
985+
it('click should respect disabled handle boundaries', () => {
986+
const onChange = jest.fn();
987+
const { container } = render(
988+
<Slider range defaultValue={[20, 50, 80]} disabled={[false, true, false]} onChange={onChange} />,
989+
);
990+
991+
// Click at position 60 (between disabled handle at 50 and enabled handle at 80)
992+
// Nearest enabled handle is 80, it should move but not cross disabled handle at 50
993+
const rail = container.querySelector('.rc-slider-rail');
994+
const mouseDown = createEvent.mouseDown(rail);
995+
Object.defineProperties(mouseDown, {
996+
clientX: { get: () => 60 },
997+
clientY: { get: () => 60 },
998+
});
999+
fireEvent(rail, mouseDown);
1000+
1001+
// Handle at 80 moves to 60, which is valid (60 > 50)
1002+
expect(onChange).toHaveBeenCalledWith([20, 50, 60]);
1003+
});
1004+
1005+
it('click near disabled handle should move correct enabled handle', () => {
1006+
const onChange = jest.fn();
1007+
const { container } = render(
1008+
<Slider range defaultValue={[20, 50, 80]} disabled={[false, true, false]} onChange={onChange} />,
1009+
);
1010+
1011+
// Click at position 30 (near disabled handle at 50)
1012+
// Nearest enabled handle is 20, it should move to 30
1013+
const rail = container.querySelector('.rc-slider-rail');
1014+
const mouseDown = createEvent.mouseDown(rail);
1015+
Object.defineProperties(mouseDown, {
1016+
clientX: { get: () => 30 },
1017+
clientY: { get: () => 30 },
1018+
});
1019+
fireEvent(rail, mouseDown);
1020+
1021+
// Handle at 20 moves to 30 (30 < 50, valid, not crossing disabled handle)
1022+
expect(onChange).toHaveBeenCalledWith([30, 50, 80]);
1023+
});
9841024
});
9851025
});

0 commit comments

Comments
 (0)