Skip to content

Commit 15e473f

Browse files
author
刘欢
committed
docs: update README and examples to clarify disabled handle functionality
1 parent 5303584 commit 15e473f

5 files changed

Lines changed: 57 additions & 20 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ The following APIs are shared by Slider and Range.
108108
| handle | (props) => React.ReactNode | | A handle generator which could be used to customized handle. |
109109
| included | boolean | `true` | If the value is `true`, it means a continuous value interval, otherwise, it is a independent value. |
110110
| reverse | boolean | `false` | If the value is `true`, it means the component is rendered reverse. |
111-
| disabled | boolean \| boolean[] | `false` | If `true`, handles can't be moved. Can also be an array to disable specific handles in range mode, e.g. `[true, false, true]` disables first and third handles. |
111+
| disabled | boolean \| boolean[] | `false` | If `true`, handles can't be moved. This prop can also be an array to disable specific handles in range mode, e.g. `[true, false, true]` disables first and third handles. |
112112
| keyboard | boolean | `true` | Support using keyboard to move handlers. |
113113
| dots | boolean | `false` | When the `step` value is greater than 1, you can set the `dots` to `true` if you want to render the slider with dots. |
114114
| onBeforeChange | Function | NOOP | `onBeforeChange` will be triggered when `ontouchstart` or `onmousedown` is triggered. |

docs/examples/disabled-handle.tsx

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -49,17 +49,16 @@ const EditableWithDisabled = () => {
4949
</div>
5050
);
5151
};
52-
52+
const defaultValue = [0, 30, 60, 100];
5353
const BasicDisabledHandle = () => {
54-
const [value, setValue] = useState<number[]>([0, 30, 60, 100]);
5554
const [disabled, setDisabled] = useState([true]);
5655

5756
return (
5857
<div>
59-
<Slider range={{ draggableTrack: true }} value={value} onChange={(v) => setValue(v as number[])} disabled={disabled} />
58+
<Slider range={{ draggableTrack: true }} defaultValue={defaultValue} disabled={disabled} />
6059
Slider disabled {JSON.stringify(disabled)}
6160
<div style={{ marginTop: 16 }}>
62-
{value.map((val, index) => (
61+
{defaultValue.map((_, index) => (
6362
<label key={index} style={{ marginRight: 16 }}>
6463
<input
6564
type="checkbox"
@@ -70,7 +69,7 @@ const BasicDisabledHandle = () => {
7069
setDisabled(newDisabled);
7170
}}
7271
/>
73-
Handle {index + 1} ({val}) {disabled[index] ? 'Disabled' : 'Enabled'}
72+
Handle {index + 1} {disabled[index] ? 'Disabled' : 'Enabled'}
7473
</label>
7574
))}
7675
</div>
@@ -111,9 +110,6 @@ export default () => (
111110
<div>
112111
single handle disabled
113112
<SingleSlider />
114-
</div>
115-
<div>
116-
117113
</div>
118114
<div style={style}>
119115
<h3>Disabled Handle + Draggable Track</h3>

src/Slider.tsx

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -189,18 +189,19 @@ const Slider = React.forwardRef<SliderRef, SliderProps<number | number[]>>((prop
189189

190190
const handlesRef = React.useRef<HandlesRef>(null);
191191
const containerRef = React.useRef<HTMLDivElement>(null);
192+
const [mergedValue, setValue] = useControlledState(defaultValue, value);
192193

193194
// ============================ Disabled ============================
194195
const disabled = React.useMemo(() => {
195196
if (typeof rawDisabled === 'boolean') {
196197
return rawDisabled;
197198
}
198-
if (Array.isArray(value)) {
199-
return value.every((_, index) => rawDisabled[index]);
199+
if (Array.isArray(rawDisabled)) {
200+
const values = Array.isArray(mergedValue) ? mergedValue : [mergedValue];
201+
return values.every((_, index) => rawDisabled[index]);
200202
}
201-
202203
return false;
203-
}, [rawDisabled, value]);
204+
}, [rawDisabled, mergedValue]);
204205

205206
const isHandleDisabled = React.useCallback(
206207
(index: number) => {
@@ -275,7 +276,6 @@ const Slider = React.forwardRef<SliderRef, SliderProps<number | number[]>>((prop
275276
);
276277

277278
// ============================ Values ============================
278-
const [mergedValue, setValue] = useControlledState(defaultValue, value);
279279

280280
const rawValues = React.useMemo(() => {
281281
const valueList =
@@ -330,8 +330,9 @@ const Slider = React.forwardRef<SliderRef, SliderProps<number | number[]>>((prop
330330
const newDisabled = [...rawDisabled];
331331

332332
if (cloneNextValues.length > rawValues.length) {
333-
const index = cloneNextValues.findIndex((item) => !rawValues.includes(item));
334-
newDisabled.splice(index, 0, false);
333+
const index = cloneNextValues.findIndex((item, i) => item !== rawValues[i]);
334+
const insertIndex = index === -1 ? rawValues.length : index;
335+
newDisabled.splice(insertIndex, 0, false);
335336
} else if (cloneNextValues.length < rawValues.length) {
336337
const index = rawValues.findIndex((item) => !cloneNextValues.includes(item));
337338
newDisabled.splice(index, 1);

src/hooks/useOffset.ts

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -244,33 +244,45 @@ export default function useOffset(
244244
// =============== Push ==================
245245

246246
// >>>>>> Basic push
247-
// End values
247+
// End values (skip disabled handles)
248248
for (let i = valueIndex + 1; i < nextValues.length; i += 1) {
249+
if (isHandleDisabled?.(i)) {
250+
break; // Stop pushing when hitting a disabled handle
251+
}
249252
let changed = true;
250253
while (needPush(nextValues[i] - nextValues[i - 1]) && changed) {
251254
({ value: nextValues[i], changed } = offsetChangedValue(nextValues, 1, i));
252255
}
253256
}
254257

255-
// Start values
258+
// Start values (skip disabled handles)
256259
for (let i = valueIndex; i > 0; i -= 1) {
260+
if (isHandleDisabled?.(i - 1)) {
261+
break; // Stop pushing when hitting a disabled handle
262+
}
257263
let changed = true;
258264
while (needPush(nextValues[i] - nextValues[i - 1]) && changed) {
259265
({ value: nextValues[i - 1], changed } = offsetChangedValue(nextValues, -1, i - 1));
260266
}
261267
}
262268

263269
// >>>>> Revert back to safe push range
264-
// End to Start
270+
// End to Start (skip disabled handles)
265271
for (let i = nextValues.length - 1; i > 0; i -= 1) {
272+
if (isHandleDisabled?.(i) || isHandleDisabled?.(i - 1)) {
273+
continue; // Skip if either handle is disabled
274+
}
266275
let changed = true;
267276
while (needPush(nextValues[i] - nextValues[i - 1]) && changed) {
268277
({ value: nextValues[i - 1], changed } = offsetChangedValue(nextValues, -1, i - 1));
269278
}
270279
}
271280

272-
// Start to End
281+
// Start to End (skip disabled handles)
273282
for (let i = 0; i < nextValues.length - 1; i += 1) {
283+
if (isHandleDisabled?.(i) || isHandleDisabled?.(i + 1)) {
284+
continue; // Skip if either handle is disabled
285+
}
274286
let changed = true;
275287
while (needPush(nextValues[i + 1] - nextValues[i]) && changed) {
276288
({ value: nextValues[i + 1], changed } = offsetChangedValue(nextValues, 1, i + 1));

tests/Range.test.tsx

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1064,5 +1064,33 @@ describe('Range', () => {
10641064
expect(onChange).not.toHaveBeenCalled();
10651065
expect(onDisabledChange).not.toHaveBeenCalled();
10661066
});
1067+
1068+
it('click to move cannot cross disabled handle boundary', () => {
1069+
const onChange = jest.fn();
1070+
const { container } = render(
1071+
<Slider range value={[20, 50, 80]} disabled={[true, false, false]} onChange={onChange} />,
1072+
);
1073+
1074+
// Click at position 10, which is left of disabled handle at 20
1075+
// Nearest enabled handle is 50, but it cannot cross below 20
1076+
doMouseDown(container, 10, 'rc-slider', true);
1077+
1078+
// Should move handle to 20 (boundary), not 10
1079+
expect(onChange).toHaveBeenCalledWith([20, 20, 80]);
1080+
});
1081+
1082+
it('click to move cannot cross disabled handle on right side', () => {
1083+
const onChange = jest.fn();
1084+
const { container } = render(
1085+
<Slider range value={[20, 50, 80]} disabled={[false, false, true]} onChange={onChange} />,
1086+
);
1087+
1088+
// Click at position 90, which is right of disabled handle at 80
1089+
// Nearest enabled handle is 50, but it cannot cross above 80
1090+
doMouseDown(container, 90, 'rc-slider', true);
1091+
1092+
// Should move handle to 80 (boundary), not 90
1093+
expect(onChange).toHaveBeenCalledWith([20, 80, 80]);
1094+
});
10671095
});
10681096
});

0 commit comments

Comments
 (0)