Skip to content

Commit cf5819c

Browse files
刘欢zombieJ
andcommitted
refactor: optimize disabled handle implementation based on review feedback
- Handle.tsx: rename handleDisabled to mergedDisabled, remove globalDisabled - Slider.tsx: use ?? operator for fallback to global disabled state - Slider.tsx: simplify boundary calculation with functional style - useOffset.ts: treat disabled handles as fixed anchors with candidates pattern - useOffset.ts: handle dead-lock case when minBound > maxBound Co-Authored-By: zombieJ <zombieJ@users.noreply.github.com>
1 parent 7fae517 commit cf5819c

3 files changed

Lines changed: 55 additions & 52 deletions

File tree

src/Handles/Handle.tsx

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,6 @@ const Handle = React.forwardRef<HTMLDivElement, HandleProps>((props, ref) => {
5555
min,
5656
max,
5757
direction,
58-
disabled: globalDisabled,
5958
keyboard,
6059
range,
6160
tabIndex,
@@ -68,13 +67,13 @@ const Handle = React.forwardRef<HTMLDivElement, HandleProps>((props, ref) => {
6867
isHandleDisabled,
6968
} = React.useContext(SliderContext);
7069

71-
const handleDisabled = globalDisabled || isHandleDisabled(valueIndex);
70+
const mergedDisabled = isHandleDisabled(valueIndex);
7271

7372
const handlePrefixCls = `${prefixCls}-handle`;
7473

7574
// ============================ Events ============================
7675
const onInternalStartMove = (e: React.MouseEvent | React.TouchEvent) => {
77-
if (handleDisabled) {
76+
if (mergedDisabled) {
7877
e.stopPropagation();
7978
return;
8079
}
@@ -91,7 +90,7 @@ const Handle = React.forwardRef<HTMLDivElement, HandleProps>((props, ref) => {
9190

9291
// =========================== Keyboard ===========================
9392
const onKeyDown: React.KeyboardEventHandler<HTMLDivElement> = (e) => {
94-
if (!handleDisabled && keyboard) {
93+
if (!mergedDisabled && keyboard) {
9594
let offset: number | 'min' | 'max' = null;
9695

9796
// Change the value
@@ -166,12 +165,12 @@ const Handle = React.forwardRef<HTMLDivElement, HandleProps>((props, ref) => {
166165

167166
if (valueIndex !== null) {
168167
divProps = {
169-
tabIndex: handleDisabled ? null : getIndex(tabIndex, valueIndex),
168+
tabIndex: mergedDisabled ? null : getIndex(tabIndex, valueIndex),
170169
role: 'slider',
171170
'aria-valuemin': min,
172171
'aria-valuemax': max,
173172
'aria-valuenow': value,
174-
'aria-disabled': handleDisabled,
173+
'aria-disabled': mergedDisabled,
175174
'aria-label': getIndex(ariaLabelForHandle, valueIndex),
176175
'aria-labelledby': getIndex(ariaLabelledByForHandle, valueIndex),
177176
'aria-required': getIndex(ariaRequired, valueIndex),
@@ -195,7 +194,7 @@ const Handle = React.forwardRef<HTMLDivElement, HandleProps>((props, ref) => {
195194
[`${handlePrefixCls}-${valueIndex + 1}`]: valueIndex !== null && range,
196195
[`${handlePrefixCls}-dragging`]: dragging,
197196
[`${handlePrefixCls}-dragging-delete`]: draggingDelete,
198-
[`${handlePrefixCls}-disabled`]: handleDisabled,
197+
[`${handlePrefixCls}-disabled`]: mergedDisabled,
199198
},
200199
classNames.handle,
201200
)}

src/Slider.tsx

Lines changed: 28 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -205,9 +205,10 @@ const Slider = React.forwardRef<SliderRef, SliderProps<number | number[]>>((prop
205205
if (typeof rawDisabled === 'boolean') {
206206
return rawDisabled;
207207
}
208-
return rawDisabled[index] || false;
208+
// Return individual disabled state if defined, otherwise fallback to global disabled
209+
return rawDisabled[index] ?? disabled;
209210
},
210-
[rawDisabled],
211+
[rawDisabled, disabled],
211212
);
212213

213214
const direction = React.useMemo<Direction>(() => {
@@ -415,47 +416,36 @@ const Slider = React.forwardRef<SliderRef, SliderProps<number | number[]>>((prop
415416
cloneNextValues.splice(valueBeforeIndex + 1, 0, newValue);
416417
focusIndex = valueBeforeIndex + 1;
417418
} else {
419+
// Find nearest enabled handle if current is disabled
418420
if (isHandleDisabled(valueIndex)) {
419-
let nearestIndex = -1;
420-
let nearestDist = mergedMax - mergedMin;
421-
422-
rawValues.forEach((val, index) => {
423-
if (!isHandleDisabled(index)) {
424-
const dist = Math.abs(newValue - val);
425-
if (dist < nearestDist) {
426-
nearestDist = dist;
427-
nearestIndex = index;
428-
}
429-
}
430-
});
431-
432-
// If all handles are disabled, do nothing
433-
if (nearestIndex === -1) {
434-
return;
435-
}
436-
437-
valueIndex = nearestIndex;
438-
}
421+
const enabledIndices = rawValues
422+
.map((_, i) => i)
423+
.filter((i) => !isHandleDisabled(i));
439424

440-
// Apply boundary constraints from disabled handles
441-
let minBound = mergedMin;
442-
let maxBound = mergedMax;
425+
if (enabledIndices.length === 0) return;
443426

444-
for (let i = valueIndex - 1; i >= 0; i -= 1) {
445-
if (isHandleDisabled(i)) {
446-
const pushDistance = typeof mergedPush === 'number' ? mergedPush : 0;
447-
minBound = rawValues[i] + pushDistance;
448-
break;
449-
}
450-
}
451-
for (let i = valueIndex + 1; i < rawValues.length; i += 1) {
452-
if (isHandleDisabled(i)) {
453-
const pushDistance = typeof mergedPush === 'number' ? mergedPush : 0;
454-
maxBound = rawValues[i] - pushDistance;
455-
break;
456-
}
427+
valueIndex = enabledIndices.reduce((nearest, i) =>
428+
Math.abs(newValue - rawValues[i]) < Math.abs(newValue - rawValues[nearest]) ? i : nearest,
429+
);
457430
}
458431

432+
// Calculate boundaries from disabled handles (treat as fixed anchors)
433+
const pushDist = typeof mergedPush === 'number' ? mergedPush : 0;
434+
const minBound = Math.max(
435+
mergedMin,
436+
...rawValues
437+
.slice(0, valueIndex)
438+
.map((v, i) => (isHandleDisabled(i) ? v + pushDist : mergedMin))
439+
.filter((v) => v > mergedMin),
440+
);
441+
const maxBound = Math.min(
442+
mergedMax,
443+
...rawValues
444+
.slice(valueIndex + 1)
445+
.map((v, i) => (isHandleDisabled(i + valueIndex + 1) ? v - pushDist : mergedMax))
446+
.filter((v) => v < mergedMax),
447+
);
448+
459449
cloneNextValues[valueIndex] = Math.max(minBound, Math.min(maxBound, newValue));
460450
focusIndex = valueIndex;
461451
}

src/hooks/useOffset.ts

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -190,24 +190,32 @@ export default function useOffset(
190190
return (pushable === null && dist === 0) || (typeof pushable === 'number' && dist < pushable);
191191
};
192192

193-
// Get the minimum boundary for a handle considering disabled handles
193+
// Get the minimum boundary for a handle considering disabled handles as fixed anchors
194194
const getHandleMinBound = (values: number[], handleIndex: number): number => {
195+
const gap = typeof pushable === 'number' ? pushable : 0;
196+
// Collect min and all left-side disabled handle positions as candidates
197+
const candidates = [min];
195198
for (let i = handleIndex - 1; i >= 0; i -= 1) {
196199
if (isHandleDisabled(i)) {
197-
return values[i] + (typeof pushable === 'number' ? pushable : 0);
200+
candidates.push(values[i] + gap);
201+
break; // Only need the nearest disabled handle
198202
}
199203
}
200-
return min;
204+
return Math.max(...candidates);
201205
};
202206

203-
// Get the maximum boundary for a handle considering disabled handles
207+
// Get the maximum boundary for a handle considering disabled handles as fixed anchors
204208
const getHandleMaxBound = (values: number[], handleIndex: number): number => {
209+
const gap = typeof pushable === 'number' ? pushable : 0;
210+
// Collect max and all right-side disabled handle positions as candidates
211+
const candidates = [max];
205212
for (let i = handleIndex + 1; i < values.length; i += 1) {
206213
if (isHandleDisabled(i)) {
207-
return values[i] - (typeof pushable === 'number' ? pushable : 0);
214+
candidates.push(values[i] - gap);
215+
break; // Only need the nearest disabled handle
208216
}
209217
}
210-
return max;
218+
return Math.min(...candidates);
211219
};
212220

213221
// Values
@@ -222,7 +230,13 @@ export default function useOffset(
222230
nextValues[valueIndex] = nextValue;
223231

224232
// Apply disabled handle boundaries
225-
nextValues[valueIndex] = Math.max(minBound, Math.min(maxBound, nextValues[valueIndex]));
233+
// If bounds conflict (min > max), the handle is locked between two disabled handles
234+
// In this case, keep the original value
235+
if (minBound <= maxBound) {
236+
nextValues[valueIndex] = Math.max(minBound, Math.min(maxBound, nextValues[valueIndex]));
237+
} else {
238+
nextValues[valueIndex] = originValue;
239+
}
226240

227241
if (allowCross === false) {
228242
// >>>>> Allow Cross

0 commit comments

Comments
 (0)