Skip to content

Commit 942b218

Browse files
刘欢claude
andcommitted
fix: properly detect global disabled when all handles are disabled
Use value/defaultValue props to determine expected length instead of rawValues which may not be available during first render. This ensures disabled=[true,true,true] correctly applies global disabled styling. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent b456606 commit 942b218

File tree

2 files changed

+200
-2
lines changed

2 files changed

+200
-2
lines changed

docs/examples/disabled-handle.tsx

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,162 @@ const style: React.CSSProperties = {
88
margin: 50,
99
};
1010

11+
// Basic editable with disabled handles
12+
const EditableWithDisabled = () => {
13+
const [value, setValue] = useState<number[]>([0, 30, 60, 100]);
14+
const [disabled, setDisabled] = useState<boolean[]>([true, false, false, true]);
15+
16+
return (
17+
<div>
18+
<Slider
19+
range={{
20+
editable: true,
21+
minCount: 2,
22+
maxCount: 5,
23+
}}
24+
value={value}
25+
onChange={(v) => setValue(v as number[])}
26+
disabled={disabled}
27+
/>
28+
<div style={{ marginTop: 16 }}>
29+
{value.map((val, index) => (
30+
<label key={index} style={{ marginRight: 16 }}>
31+
<input
32+
type="checkbox"
33+
checked={disabled[index]}
34+
onChange={() => {
35+
const newDisabled = [...disabled];
36+
newDisabled[index] = !newDisabled[index];
37+
setDisabled(newDisabled);
38+
}}
39+
/>
40+
Handle {index + 1} ({val}) {disabled[index] ? 'Disabled' : 'Enabled'}
41+
</label>
42+
))}
43+
</div>
44+
<p style={{ marginTop: 8, color: '#999', fontSize: 12 }}>
45+
Try: Click track to add handle • Drag handle to edge to delete • Toggle checkboxes to disable handles
46+
</p>
47+
</div>
48+
);
49+
};
50+
51+
// Test: Delete middle disabled handle
52+
const DeleteDisabledHandle = () => {
53+
const [value, setValue] = useState<number[]>([10, 30, 50, 70, 90]);
54+
// Middle handle (index 2, value 50) is disabled
55+
const [disabled, setDisabled] = useState<boolean[]>([false, false, true, false, false]);
56+
57+
const handleAdd = () => {
58+
// Add a new handle - what happens to disabled array?
59+
setValue([...value, 50]);
60+
// In controlled mode, user needs to also update disabled
61+
setDisabled([...disabled, false]);
62+
};
63+
64+
const handleRemove = () => {
65+
// Remove last handle - what happens to disabled array?
66+
setValue(value.slice(0, -1));
67+
setDisabled(disabled.slice(0, -1));
68+
};
69+
70+
return (
71+
<div>
72+
<div style={{ marginBottom: 16 }}>
73+
<button onClick={handleAdd} style={{ marginRight: 8 }}>Add Handle</button>
74+
<button onClick={handleRemove}>Remove Last</button>
75+
</div>
76+
<Slider
77+
range={{
78+
editable: true,
79+
minCount: 2,
80+
maxCount: 6,
81+
}}
82+
value={value}
83+
onChange={(v) => setValue(v as number[])}
84+
disabled={disabled}
85+
/>
86+
<p style={{ marginTop: 8, color: '#999', fontSize: 12 }}>
87+
Value: [{value.join(', ')}] <br />
88+
Disabled: [{disabled.join(', ')}] <br />
89+
Try adding/removing handles and observe disabled array alignment
90+
</p>
91+
</div>
92+
);
93+
};
94+
95+
// Test: draggableTrack with disabled handles
96+
const DraggableTrackWithDisabled = () => {
97+
const [value, setValue] = useState<number[]>([20, 40, 60, 80]);
98+
99+
return (
100+
<div>
101+
<Slider
102+
range={{
103+
editable: true,
104+
draggableTrack: true,
105+
minCount: 2,
106+
maxCount: 4,
107+
}}
108+
value={value}
109+
onChange={(v) => setValue(v as number[])}
110+
disabled={[true, false, false, true]}
111+
/>
112+
<p style={{ marginTop: 8, color: '#999', fontSize: 12 }}>
113+
First and last handles are disabled (boundaries).
114+
Drag track to move middle handles together.
115+
Click track to add/delete handles.
116+
</p>
117+
</div>
118+
);
119+
};
120+
121+
// Test: All handles disabled scenario
122+
const AllDisabled = () => {
123+
const [value, setValue] = useState<number[]>([20, 50, 80]);
124+
125+
return (
126+
<div>
127+
<Slider
128+
range={{
129+
editable: true,
130+
minCount: 2,
131+
maxCount: 4,
132+
}}
133+
value={value}
134+
onChange={(v) => setValue(v as number[])}
135+
disabled={[true, true, true]}
136+
/>
137+
<p style={{ marginTop: 8, color: '#999', fontSize: 12 }}>
138+
All handles disabled: Slider should still work for adding new handles via track click
139+
</p>
140+
</div>
141+
);
142+
};
143+
144+
// Test: Min count boundary
145+
const MinCountBoundary = () => {
146+
const [value, setValue] = useState<number[]>([20, 80]);
147+
148+
return (
149+
<div>
150+
<Slider
151+
range={{
152+
editable: true,
153+
minCount: 2, // Cannot delete below 2 handles
154+
maxCount: 4,
155+
}}
156+
value={value}
157+
onChange={(v) => setValue(v as number[])}
158+
disabled={[false, false]}
159+
/>
160+
<p style={{ marginTop: 8, color: '#999', fontSize: 12 }}>
161+
minCount=2: Cannot delete below 2 handles. Try dragging first handle to the left edge.
162+
</p>
163+
</div>
164+
);
165+
};
166+
11167
const BasicDisabledHandle = () => {
12168
const [value, setValue] = useState<number[]>([0, 30, 60, 100]);
13169
const [disabled, setDisabled] = useState([true, false, false, true]);
@@ -62,5 +218,33 @@ export default () => (
62218
<h3>Disabled Handle as Boundary</h3>
63219
<DisabledHandleAsBoundary />
64220
</div>
221+
<div>
222+
<div style={style}>
223+
<h3>Editable + Disabled Array</h3>
224+
<p>Toggle checkboxes to enable/disable handles in editable mode</p>
225+
<EditableWithDisabled />
226+
</div>
227+
228+
<div style={style}>
229+
<h3>Controlled Add/Remove with Disabled</h3>
230+
<p>Test disabled array sync when value length changes</p>
231+
<DeleteDisabledHandle />
232+
</div>
233+
234+
<div style={style}>
235+
<h3>DraggableTrack + Disabled Boundaries</h3>
236+
<DraggableTrackWithDisabled />
237+
</div>
238+
239+
<div style={style}>
240+
<h3>All Handles Disabled</h3>
241+
<AllDisabled />
242+
</div>
243+
244+
<div style={style}>
245+
<h3>Min Count Boundary</h3>
246+
<MinCountBoundary />
247+
</div>
248+
</div>
65249
</div>
66250
);

src/Slider.tsx

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -193,11 +193,25 @@ const Slider = React.forwardRef<SliderRef, SliderProps<number | number[]>>((prop
193193
return rawDisabled;
194194
}
195195
// rawValues might not be defined yet at this point
196+
// But if user provides an array of all true values, we should treat it as globally disabled
196197
if (!rawDisabled?.length) {
197198
return false;
198199
}
199-
return rawDisabled.length === rawValues?.length && rawDisabled.every(Boolean);
200-
}, [rawDisabled, rawValues]);
200+
// Get expected value length from value/defaultValue props
201+
const valueFromProps = value !== undefined ? value : defaultValue;
202+
const expectedLength = Array.isArray(valueFromProps)
203+
? valueFromProps.length
204+
: valueFromProps !== undefined
205+
? 1
206+
: 0;
207+
208+
// If rawDisabled length matches the initial value length and all are true
209+
if (rawDisabled.length === expectedLength && rawDisabled.length > 0) {
210+
return rawDisabled.every(Boolean);
211+
}
212+
213+
return false;
214+
}, [rawDisabled, value, defaultValue]);
201215

202216
const isHandleDisabled = React.useCallback(
203217
(index: number) => {

0 commit comments

Comments
 (0)