1010 * governing permissions and limitations under the License.
1111 */
1212
13+ import { AriaLabelingProps , DOMRef , DOMRefValue , FocusableRef } from '@react-types/shared' ;
1314import { 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' ;
1516import { createContext , forwardRef , ReactNode , RefObject , useCallback , useContext , useRef } from 'react' ;
16- import { DOMRef , DOMRefValue , FocusableRef } from '@react-types/shared' ;
1717import { focusRing , size , style } from '../style' with { type : 'macro' } ;
1818import { getAllowedOverrides , StyleProps } from './style-utils' with { type : 'macro' } ;
1919import { IconContext } from './Icon' ;
@@ -23,7 +23,7 @@ import {useDOMRef, useFocusableRef} from '@react-spectrum/utils';
2323import { useLayoutEffect } from '@react-aria/utils' ;
2424import { useSpectrumContextProps } from './useSpectrumContextProps' ;
2525
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 {
2727 /**
2828 * The content to display in the segmented control.
2929 */
@@ -32,16 +32,22 @@ export interface SegmentedControlProps extends Omit<RadioGroupProps, 'isReadOnly
3232 * Whether the segmented control is disabled.
3333 */
3434 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
3941}
40- export interface SegmentedControlItemProps extends Omit < RadioProps , 'children' | 'className' | 'style' | 'onHoverStart' | 'onHoverEnd' | 'onHoverChange' > , StyleProps {
42+ export interface SegmentedControlItemProps extends AriaLabelingProps , StyleProps {
4143 /**
42- * The content to display in the control item.
44+ * The content to display in the segmented control item.
4345 */
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
4551}
4652
4753export const SegmentedControlContext = createContext < ContextValue < SegmentedControlProps , DOMRefValue < HTMLDivElement > > > ( null ) ;
@@ -135,8 +141,9 @@ const InternalSegmentedControlContext = createContext<InternalSegmentedControlCo
135141function SegmentedControl ( props : SegmentedControlProps , ref : DOMRef < HTMLDivElement > ) {
136142 [ props , ref ] = useSpectrumContextProps ( props , ref , SegmentedControlContext ) ;
137143 let {
138- defaultValue,
139- value
144+ defaultSelectedKey,
145+ selectedKey,
146+ onSelectionChange
140147 } = props ;
141148 let domRef = useDOMRef ( ref ) ;
142149
@@ -148,21 +155,23 @@ function SegmentedControl(props: SegmentedControlProps, ref: DOMRef<HTMLDivEleme
148155 prevRef . current = currentSelectedRef ?. current . getBoundingClientRect ( ) ;
149156 }
150157
151- if ( props . onChange ) {
152- props . onChange ( value ) ;
158+ if ( onSelectionChange ) {
159+ onSelectionChange ( value ) ;
153160 }
154161 } ;
155162
156163 return (
157164 < RadioGroup
158165 { ...props }
166+ value = { selectedKey }
167+ defaultValue = { defaultSelectedKey }
159168 ref = { domRef }
160169 orientation = "horizontal"
161170 style = { props . UNSAFE_style }
162171 onChange = { onChange }
163172 className = { ( props . UNSAFE_className || '' ) + segmentedControl ( { size : 'M' } , props . styles ) }
164173 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 } >
166175 { props . children }
167176 </ DefaultSelectionTracker >
168177 </ RadioGroup >
@@ -197,15 +206,15 @@ function SegmentedControlItem(props: SegmentedControlItemProps, ref: FocusableRe
197206 let divRef = useRef < HTMLDivElement > ( null ) ;
198207 let { register, prevRef, currentSelectedRef} = useContext ( InternalSegmentedControlContext ) ;
199208 let state = useContext ( RadioGroupStateContext ) ;
200- let isSelected = props . value === state ?. selectedValue ;
209+ let isSelected = props . id === state ?. selectedValue ;
201210 // do not apply animation if a user has the prefers-reduced-motion setting
202211 let isReduced = false ;
203212 if ( window ?. matchMedia ) {
204213 isReduced = window . matchMedia ( '(prefers-reduced-motion: reduce)' ) . matches ;
205214 }
206215
207216 useLayoutEffect ( ( ) => {
208- register ?.( props . value ) ;
217+ register ?.( props . id ) ;
209218 } , [ ] ) ;
210219
211220 useLayoutEffect ( ( ) => {
@@ -231,7 +240,8 @@ function SegmentedControlItem(props: SegmentedControlItemProps, ref: FocusableRe
231240
232241 return (
233242 < Radio
234- { ...props }
243+ { ...props }
244+ value = { props . id }
235245 ref = { domRef }
236246 inputRef = { inputRef }
237247 style = { props . UNSAFE_style }
0 commit comments