Skip to content

Commit 43e60c7

Browse files
committed
fix: antd issue 35170
1 parent a9f646c commit 43e60c7

File tree

3 files changed

+78
-32
lines changed

3 files changed

+78
-32
lines changed

docs/examples/controlled.tsx

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,27 @@ export default class Demo extends React.Component<
1313

1414
render() {
1515
return (
16-
<Segmented
17-
options={['iOS', 'Android', 'Web3']}
18-
value={this.state.value}
19-
onChange={(e) =>
20-
this.setState({
21-
value: e.target.value,
22-
})
23-
}
24-
/>
16+
<>
17+
<Segmented
18+
options={['iOS', 'Android', 'Web3']}
19+
value={this.state.value}
20+
onChange={(e) =>
21+
this.setState({
22+
value: e.target.value,
23+
})
24+
}
25+
/>
26+
&nbsp;&nbsp;
27+
<Segmented
28+
options={['iOS', 'Android', 'Web3']}
29+
value={this.state.value}
30+
onChange={(e) =>
31+
this.setState({
32+
value: e.target.value,
33+
})
34+
}
35+
/>
36+
</>
2537
);
2638
}
2739
}

src/index.tsx

Lines changed: 56 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ const InternalSegmentedOption: React.FC<{
104104

105105
return (
106106
<label
107-
className={classNames(`${prefixCls}-item`, className, {
107+
className={classNames(className, {
108108
[`${prefixCls}-item-disabled`]: disabled,
109109
})}
110110
>
@@ -155,34 +155,66 @@ const Segmented = React.forwardRef<HTMLDivElement, SegmentedProps>(
155155
}, [options]);
156156

157157
const [selected, setSelected] = useMergedState(segmentedOptions[0]?.value, {
158-
value,
158+
value: props.value,
159159
defaultValue,
160160
});
161161

162162
const [visualSelected, setVisualSelected] = React.useState<
163163
SegmentedRawOption | undefined
164164
>(selected);
165165

166-
const [thumbShow, setThumbShow] = React.useState(false);
166+
const latestVisualSelected = React.useRef(visualSelected);
167167

168-
const calcThumbMoveStatus = (
169-
event: React.ChangeEvent<HTMLInputElement>,
170-
) => {
171-
const toElement = event.target.closest(`.${prefixCls}-item`);
168+
React.useEffect(() => {
169+
latestVisualSelected.current = visualSelected;
170+
});
171+
172+
const [thumbShow, setThumbShow] = React.useState(false);
172173

173-
const fromElement = containerRef.current?.querySelector(
174-
`.${prefixCls}-item-selected`,
175-
);
174+
const doThumbAnimation = React.useCallback(
175+
(selectedValue: SegmentedRawOption) => {
176+
const segmentedItemIndex = segmentedOptions.findIndex(
177+
(n) => n.value === selectedValue,
178+
);
176179

177-
if (fromElement && toElement && thumbMoveStatus.current) {
178-
thumbMoveStatus.current.from = calcThumbStyle(
179-
fromElement as HTMLElement,
180+
// find target element
181+
const toElement = containerRef.current?.querySelector(
182+
`.${prefixCls}-item:nth-child(${segmentedItemIndex + 1})`,
180183
);
181-
thumbMoveStatus.current.to = calcThumbStyle(toElement as HTMLElement);
182184

183-
setThumbShow(true);
185+
if (toElement) {
186+
// find source element
187+
const fromElement = containerRef.current?.querySelector(
188+
`.${prefixCls}-item-selected`,
189+
);
190+
191+
if (fromElement && toElement && thumbMoveStatus.current) {
192+
// calculate for thumb moving animation
193+
thumbMoveStatus.current.from = calcThumbStyle(
194+
fromElement as HTMLElement,
195+
);
196+
thumbMoveStatus.current.to = calcThumbStyle(
197+
toElement as HTMLElement,
198+
);
199+
200+
// trigger css-motion starts
201+
setThumbShow(true);
202+
}
203+
}
204+
},
205+
[prefixCls, segmentedOptions],
206+
);
207+
208+
React.useEffect(() => {
209+
// Syncing `visualSelected` when `selected` changed
210+
// and do thumb animation
211+
if (
212+
(typeof selected === 'string' || typeof selected === 'number') &&
213+
selected !== latestVisualSelected.current
214+
) {
215+
doThumbAnimation(selected);
184216
}
185-
};
217+
}, [selected]);
186218

187219
const handleChange = (
188220
event: React.ChangeEvent<HTMLInputElement>,
@@ -192,8 +224,6 @@ const Segmented = React.forwardRef<HTMLDivElement, SegmentedProps>(
192224
return;
193225
}
194226

195-
calcThumbMoveStatus(event);
196-
197227
setSelected(val);
198228

199229
if (onChange) {
@@ -275,10 +305,14 @@ const Segmented = React.forwardRef<HTMLDivElement, SegmentedProps>(
275305
<InternalSegmentedOption
276306
key={segmentedOption.value}
277307
prefixCls={prefixCls}
278-
className={classNames(segmentedOption.className, {
279-
[`${prefixCls}-item-selected`]:
280-
segmentedOption.value === visualSelected,
281-
})}
308+
className={classNames(
309+
segmentedOption.className,
310+
`${prefixCls}-item`,
311+
{
312+
[`${prefixCls}-item-selected`]:
313+
segmentedOption.value === visualSelected,
314+
},
315+
)}
282316
checked={segmentedOption.value === selected}
283317
onChange={handleChange}
284318
{...segmentedOption}

tests/index.spec.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ describe('rc-segmented', () => {
163163
expect(wrapper.render()).toMatchSnapshot();
164164
expect(
165165
wrapper
166-
.find('.rc-segmented-item')
166+
.find('label.rc-segmented-item')
167167
.at(1)
168168
.hasClass('rc-segmented-item-disabled'),
169169
).toBeTruthy();

0 commit comments

Comments
 (0)