10
10
* governing permissions and limitations under the License.
11
11
*/
12
12
13
+ import { AriaLabelingProps , DOMRef , DOMRefValue , FocusableRef } from '@react-types/shared' ;
13
14
import { centerBaseline } from './CenterBaseline' ;
14
- import { ContextValue , DEFAULT_SLOT , Provider , TextContext as RACTextContext , Radio , RadioGroup , RadioGroupProps , RadioGroupStateContext , RadioProps } from 'react-aria-components' ;
15
+ import { ContextValue , DEFAULT_SLOT , Provider , TextContext as RACTextContext , Radio , RadioGroup , RadioGroupStateContext , SlotProps } from 'react-aria-components' ;
15
16
import { createContext , forwardRef , ReactNode , RefObject , useCallback , useContext , useRef } from 'react' ;
16
- import { DOMRef , DOMRefValue , FocusableRef } from '@react-types/shared' ;
17
17
import { focusRing , size , style } from '../style' with { type : 'macro' } ;
18
18
import { getAllowedOverrides , StyleProps } from './style-utils' with { type : 'macro' } ;
19
19
import { IconContext } from './Icon' ;
@@ -23,7 +23,7 @@ import {useDOMRef, useFocusableRef} from '@react-spectrum/utils';
23
23
import { useLayoutEffect } from '@react-aria/utils' ;
24
24
import { useSpectrumContextProps } from './useSpectrumContextProps' ;
25
25
26
- export interface SegmentedControlProps extends Omit < RadioGroupProps , 'isReadOnly' | 'name' | 'isRequired' | 'isInvalid' | 'validate' | 'validationBehavior' | 'children' | 'className' | 'style' | 'aria-label' | 'orientation' > , StyleProps {
26
+ export interface SegmentedControlProps extends AriaLabelingProps , StyleProps , SlotProps {
27
27
/**
28
28
* The content to display in the segmented control.
29
29
*/
@@ -32,16 +32,22 @@ export interface SegmentedControlProps extends Omit<RadioGroupProps, 'isReadOnly
32
32
* Whether the segmented control is disabled.
33
33
*/
34
34
isDisabled ?: boolean ,
35
- /**
36
- * Defines a string value that labels the current element.
37
- */
38
- 'aria-label' : string
35
+ /** The id of the currently selected item (controlled). */
36
+ selectedKey ?: string | null ,
37
+ /** The id of the initial selected item (uncontrolled). */
38
+ defaultSelectedKey ?: string ,
39
+ /** Handler that is called when the selection changes. */
40
+ onSelectionChange ?: ( id : string ) => void
39
41
}
40
- export interface SegmentedControlItemProps extends Omit < RadioProps , 'children' | 'className' | 'style' | 'onHoverStart' | 'onHoverEnd' | 'onHoverChange' > , StyleProps {
42
+ export interface SegmentedControlItemProps extends AriaLabelingProps , StyleProps {
41
43
/**
42
- * The content to display in the control item.
44
+ * The content to display in the segmented control item.
43
45
*/
44
- children : ReactNode
46
+ children : ReactNode ,
47
+ /** The id of the item, matching the value used in SegmentedControl's `selectedKey` prop. */
48
+ id : string ,
49
+ /** Whether the item is disabled or not. */
50
+ isDisabled ?: boolean
45
51
}
46
52
47
53
export const SegmentedControlContext = createContext < ContextValue < SegmentedControlProps , DOMRefValue < HTMLDivElement > > > ( null ) ;
@@ -135,8 +141,9 @@ const InternalSegmentedControlContext = createContext<InternalSegmentedControlCo
135
141
function SegmentedControl ( props : SegmentedControlProps , ref : DOMRef < HTMLDivElement > ) {
136
142
[ props , ref ] = useSpectrumContextProps ( props , ref , SegmentedControlContext ) ;
137
143
let {
138
- defaultValue,
139
- value
144
+ defaultSelectedKey,
145
+ selectedKey,
146
+ onSelectionChange
140
147
} = props ;
141
148
let domRef = useDOMRef ( ref ) ;
142
149
@@ -148,21 +155,23 @@ function SegmentedControl(props: SegmentedControlProps, ref: DOMRef<HTMLDivEleme
148
155
prevRef . current = currentSelectedRef ?. current . getBoundingClientRect ( ) ;
149
156
}
150
157
151
- if ( props . onChange ) {
152
- props . onChange ( value ) ;
158
+ if ( onSelectionChange ) {
159
+ onSelectionChange ( value ) ;
153
160
}
154
161
} ;
155
162
156
163
return (
157
164
< RadioGroup
158
165
{ ...props }
166
+ value = { selectedKey }
167
+ defaultValue = { defaultSelectedKey }
159
168
ref = { domRef }
160
169
orientation = "horizontal"
161
170
style = { props . UNSAFE_style }
162
171
onChange = { onChange }
163
172
className = { ( props . UNSAFE_className || '' ) + segmentedControl ( { size : 'M' } , props . styles ) }
164
173
aria-label = { props [ 'aria-label' ] } >
165
- < DefaultSelectionTracker defaultValue = { defaultValue } value = { value } prevRef = { prevRef } currentSelectedRef = { currentSelectedRef } >
174
+ < DefaultSelectionTracker defaultValue = { defaultSelectedKey } value = { selectedKey } prevRef = { prevRef } currentSelectedRef = { currentSelectedRef } >
166
175
{ props . children }
167
176
</ DefaultSelectionTracker >
168
177
</ RadioGroup >
@@ -197,15 +206,15 @@ function SegmentedControlItem(props: SegmentedControlItemProps, ref: FocusableRe
197
206
let divRef = useRef < HTMLDivElement > ( null ) ;
198
207
let { register, prevRef, currentSelectedRef} = useContext ( InternalSegmentedControlContext ) ;
199
208
let state = useContext ( RadioGroupStateContext ) ;
200
- let isSelected = props . value === state ?. selectedValue ;
209
+ let isSelected = props . id === state ?. selectedValue ;
201
210
// do not apply animation if a user has the prefers-reduced-motion setting
202
211
let isReduced = false ;
203
212
if ( window ?. matchMedia ) {
204
213
isReduced = window . matchMedia ( '(prefers-reduced-motion: reduce)' ) . matches ;
205
214
}
206
215
207
216
useLayoutEffect ( ( ) => {
208
- register ?.( props . value ) ;
217
+ register ?.( props . id ) ;
209
218
} , [ ] ) ;
210
219
211
220
useLayoutEffect ( ( ) => {
@@ -231,7 +240,8 @@ function SegmentedControlItem(props: SegmentedControlItemProps, ref: FocusableRe
231
240
232
241
return (
233
242
< Radio
234
- { ...props }
243
+ { ...props }
244
+ value = { props . id }
235
245
ref = { domRef }
236
246
inputRef = { inputRef }
237
247
style = { props . UNSAFE_style }
0 commit comments