Skip to content

Commit 5c11ddf

Browse files
authored
[Slider] Fix overlapping slider thumbs stuck at min or max (#1732)
1 parent 557e0db commit 5c11ddf

File tree

2 files changed

+22
-22
lines changed

2 files changed

+22
-22
lines changed

packages/react/src/slider/root/useSliderRoot.ts

+22-20
Original file line numberDiff line numberDiff line change
@@ -37,23 +37,23 @@ function areValuesEqual(
3737
return false;
3838
}
3939

40-
function findClosest(values: readonly number[], currentValue: number) {
41-
const { index: closestIndex } =
42-
values.reduce<{ distance: number; index: number } | null>(
43-
(acc, value: number, index: number) => {
44-
const distance = Math.abs(currentValue - value);
45-
46-
if (acc === null || distance < acc.distance || distance === acc.distance) {
47-
return {
48-
distance,
49-
index,
50-
};
51-
}
40+
function getClosestThumbIndex(values: readonly number[], currentValue: number, max: number) {
41+
let closestIndex;
42+
let minDistance;
43+
for (let i = 0; i < values.length; i += 1) {
44+
const distance = Math.abs(currentValue - values[i]);
45+
if (
46+
minDistance === undefined ||
47+
// when the value is at max, the lowest index thumb has to be dragged
48+
// first or it will block higher index thumbs from moving
49+
// otherwise consider higher index thumbs to be closest when their values are identical
50+
(values[i] === max ? distance < minDistance : distance <= minDistance)
51+
) {
52+
closestIndex = i;
53+
minDistance = distance;
54+
}
55+
}
5256

53-
return acc;
54-
},
55-
null,
56-
) ?? {};
5757
return closestIndex;
5858
}
5959

@@ -74,12 +74,14 @@ export function focusThumb(
7474
return;
7575
}
7676

77-
const doc = ownerDocument(sliderRef.current);
77+
const activeEl = activeElement(ownerDocument(sliderRef.current));
7878

7979
if (
80-
!sliderRef.current.contains(doc.activeElement) ||
81-
Number(doc?.activeElement?.getAttribute(SliderThumbDataAttributes.index)) !== thumbIndex
80+
activeEl == null ||
81+
!sliderRef.current.contains(activeEl) ||
82+
Number(activeEl.getAttribute(SliderThumbDataAttributes.index)) !== thumbIndex
8283
) {
84+
// TODO: possibly simplify with thumbRefs as it already exists
8385
(
8486
sliderRef.current.querySelector(
8587
`[type="range"][${SliderThumbDataAttributes.index}="${thumbIndex}"]`,
@@ -371,7 +373,7 @@ export function useSliderRoot(parameters: useSliderRoot.Parameters): useSliderRo
371373
}
372374

373375
if (shouldCaptureThumbIndex) {
374-
closestThumbIndexRef.current = findClosest(values, newValue) ?? 0;
376+
closestThumbIndexRef.current = getClosestThumbIndex(values, newValue, max) ?? 0;
375377
}
376378

377379
const closestThumbIndex = closestThumbIndexRef.current ?? 0;

packages/react/src/slider/thumb/useSliderThumb.ts

-2
Original file line numberDiff line numberDiff line change
@@ -129,8 +129,6 @@ export function useSliderThumb(parameters: useSliderThumb.Parameters): useSlider
129129
}[orientation]]: `${percent}%`,
130130
[isVertical ? 'left' : 'top']: '50%',
131131
transform: `translate(${(isVertical || !isRtl ? -1 : 1) * 50}%, ${(isVertical ? 1 : -1) * 50}%)`,
132-
// So the non active thumb doesn't show its label on hover.
133-
pointerEvents: activeIndex !== -1 && activeIndex !== index ? 'none' : undefined,
134132
zIndex: activeIndex === index ? 1 : undefined,
135133
} satisfies React.CSSProperties;
136134
}, [activeIndex, isRtl, orientation, percent, index]);

0 commit comments

Comments
 (0)