Skip to content

Commit adc22ec

Browse files
author
伊北
committed
feat: use input for onChange
1 parent b55470d commit adc22ec

File tree

3 files changed

+70
-32
lines changed

3 files changed

+70
-32
lines changed

assets/index.less

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,14 @@
2727
border-radius: 2px;
2828

2929
&-item {
30-
position: relative;
3130
// box-sizing: border-box;
31+
position: relative;
3232
display: inline-block;
3333
height: 28px;
3434
padding: 4px 10px;
3535
color: fade(#000, 85%);
3636
line-height: 28px;
37+
text-align: center;
3738

3839
cursor: pointer;
3940

@@ -54,6 +55,17 @@
5455
&-label {
5556
z-index: 2;
5657
}
58+
59+
&-input {
60+
position: absolute;
61+
top: 0;
62+
left: 0;
63+
64+
width: 0;
65+
height: 0;
66+
opacity: 0;
67+
pointer-events: none;
68+
}
5769
}
5870

5971
// disabled styles

docs/examples/simple.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,16 @@ export default function App() {
66
return (
77
<div>
88
<div className="wrapper">
9-
<Segmented options={['iOS', 'Android', 'Web']} />
9+
<Segmented
10+
options={['iOS', 'Android', 'Web']}
11+
onChange={(e) => console.log(e.target.value, typeof e.target.value)}
12+
/>
1013
</div>
1114
<div className="wrapper">
12-
<Segmented options={['13333333333', '057110000', '02110086']} />
15+
<Segmented
16+
options={[13333333333, 157110000, 12110086]}
17+
onChange={(e) => console.log(e.target.value, typeof e.target.value)}
18+
/>
1319
</div>
1420
<div className="wrapper">
1521
<Segmented options={['iOS', 'Android', 'Web']} disabled />

src/index.tsx

Lines changed: 49 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,15 @@ type Option = RawOption | LabeledOption;
1818

1919
type Options = Option[];
2020

21+
type ExtendedHTMLInputElement = Omit<HTMLInputElement, 'value'> & {
22+
value: RawOption;
23+
};
24+
2125
export interface SegmentedProps extends React.HTMLProps<HTMLDivElement> {
2226
options: Options;
2327
defaultValue?: RawOption;
28+
value?: RawOption;
29+
onChange?: (e: React.ChangeEvent<ExtendedHTMLInputElement>) => void;
2430
disabled?: boolean;
2531
prefixCls?: string;
2632
direction?: 'ltr' | 'rtl';
@@ -61,7 +67,7 @@ const Segmented = React.forwardRef<HTMLDivElement, SegmentedProps>(
6167
direction,
6268
options,
6369
disabled,
64-
onClick,
70+
onChange,
6571
prefixCls: customizePrefixCls,
6672
className = '',
6773
...restProps
@@ -97,30 +103,8 @@ const Segmented = React.forwardRef<HTMLDivElement, SegmentedProps>(
97103
className,
98104
);
99105

100-
const handleItemClick = React.useCallback(
101-
(
102-
segmentedOption: LabeledOption,
103-
event: React.MouseEvent<HTMLDivElement>,
104-
) => {
105-
if (disabled || segmentedOption.disabled) {
106-
return;
107-
}
108-
109-
if (segmentedOption.value !== selected) {
110-
calcThumbMoveStyle(event);
111-
}
112-
113-
setSelected(segmentedOption.value);
114-
115-
onClick?.(event);
116-
},
117-
[selected],
118-
);
119-
120-
const calcThumbMoveStyle = (event: React.MouseEvent<HTMLDivElement>) => {
121-
const toElement = (event.target as HTMLElement).closest(
122-
`.${prefixCls}-item`,
123-
);
106+
const calcThumbMoveStyle = (event: React.ChangeEvent<HTMLInputElement>) => {
107+
const toElement = event.target.closest(`.${prefixCls}-item`);
124108

125109
const fromElement = containerRef.current?.querySelector(
126110
`.${prefixCls}-item-selected`,
@@ -136,6 +120,37 @@ const Segmented = React.forwardRef<HTMLDivElement, SegmentedProps>(
136120
}
137121
};
138122

123+
const handleChange = React.useCallback(
124+
(
125+
event: React.ChangeEvent<HTMLInputElement>,
126+
segmentedOption: LabeledOption,
127+
) => {
128+
if (disabled || segmentedOption.disabled) {
129+
return;
130+
}
131+
132+
if (segmentedOption.value !== selected) {
133+
calcThumbMoveStyle(event);
134+
}
135+
136+
setSelected(segmentedOption.value);
137+
if (onChange) {
138+
const mutationTarget = Object.create(event.target, {
139+
value: {
140+
value: segmentedOption.value,
141+
},
142+
});
143+
const mutatedEvent = Object.create(event, {
144+
target: {
145+
value: mutationTarget,
146+
},
147+
});
148+
onChange(mutatedEvent);
149+
}
150+
},
151+
[selected, disabled],
152+
);
153+
139154
// --- motion event handlers for thumb move
140155
const handleThumbEnterStart = () => {
141156
const fromStyle = thumbMoveStyles.current.from;
@@ -179,26 +194,31 @@ const Segmented = React.forwardRef<HTMLDivElement, SegmentedProps>(
179194
{({ className: motionClassName, style: motionStyle }) => {
180195
return (
181196
<div
182-
className={classNames(`${prefixCls}-thumb`, motionClassName)}
183197
style={motionStyle}
198+
className={classNames(`${prefixCls}-thumb`, motionClassName)}
184199
/>
185200
);
186201
}}
187202
</CSSMotion>
188203
{segmentedOptions.map((segmentedOption) => (
189-
<div
204+
<label
190205
key={segmentedOption.value}
191206
className={classNames(`${prefixCls}-item`, {
192207
[`${prefixCls}-item-selected`]:
193208
segmentedOption.value === visualSelected,
194209
[`${prefixCls}-item-disabled`]: !!segmentedOption.disabled,
195210
})}
196-
onClick={(e) => handleItemClick(segmentedOption, e)}
197211
>
212+
<input
213+
className={`${prefixCls}-item-input`}
214+
type="radio"
215+
checked={segmentedOption.value === selected}
216+
onChange={(e) => handleChange(e, segmentedOption)}
217+
/>
198218
<span className={`${prefixCls}-item-label`}>
199219
{segmentedOption.label}
200220
</span>
201-
</div>
221+
</label>
202222
))}
203223
</div>
204224
);

0 commit comments

Comments
 (0)