@@ -19,12 +19,17 @@ import {
1919 model ,
2020 signal ,
2121 WritableSignal ,
22- OnDestroy ,
2322} from '@angular/core' ;
24- import { RadioButtonPattern , RadioGroupPattern } from '../ui-patterns' ;
23+ import {
24+ RadioButtonPattern ,
25+ RadioGroupInputs ,
26+ RadioGroupPattern ,
27+ ToolbarRadioGroupInputs ,
28+ ToolbarRadioGroupPattern ,
29+ } from '../ui-patterns' ;
2530import { Directionality } from '@angular/cdk/bidi' ;
2631import { _IdGenerator } from '@angular/cdk/a11y' ;
27- import { CdkToolbar } from '.. /toolbar' ;
32+ import { CdkToolbarWidgetGroup } from '@angular/cdk-experimental /toolbar' ;
2833
2934// TODO: Move mapSignal to it's own file so it can be reused across components.
3035
@@ -91,23 +96,24 @@ export function mapSignal<T, V>(
9196 '(pointerdown)' : 'pattern.onPointerdown($event)' ,
9297 '(focusin)' : 'onFocus()' ,
9398 } ,
99+ hostDirectives : [ CdkToolbarWidgetGroup ] ,
94100} )
95101export class CdkRadioGroup < V > {
96102 /** A reference to the radio group element. */
97103 private readonly _elementRef = inject ( ElementRef ) ;
98104
105+ /** A reference to the CdkToolbarWidgetGroup, if the radio group is in a toolbar. */
106+ private readonly _cdkToolbarWidgetGroup = inject ( CdkToolbarWidgetGroup ) ;
107+
108+ /** Whether the radio group is inside of a CdkToolbar. */
109+ private readonly _hasToolbar = computed ( ( ) => ! ! this . _cdkToolbarWidgetGroup . toolbar ( ) ) ;
110+
99111 /** The CdkRadioButtons nested inside of the CdkRadioGroup. */
100112 private readonly _cdkRadioButtons = contentChildren ( CdkRadioButton , { descendants : true } ) ;
101113
102114 /** A signal wrapper for directionality. */
103115 protected textDirection = inject ( Directionality ) . valueSignal ;
104116
105- /** A signal wrapper for toolbar. */
106- toolbar = inject ( CdkToolbar , { optional : true } ) ;
107-
108- /** Toolbar pattern if applicable */
109- private readonly _toolbarPattern = computed ( ( ) => this . toolbar ?. pattern ) ;
110-
111117 /** The RadioButton UIPatterns of the child CdkRadioButtons. */
112118 protected items = computed ( ( ) => this . _cdkRadioButtons ( ) . map ( radio => radio . pattern ) ) ;
113119
@@ -136,22 +142,37 @@ export class CdkRadioGroup<V> {
136142 } ) ;
137143
138144 /** The RadioGroup UIPattern. */
139- pattern : RadioGroupPattern < V > = new RadioGroupPattern < V > ( {
140- ...this ,
141- items : this . items ,
142- value : this . _value ,
143- activeItem : signal ( undefined ) ,
144- textDirection : this . textDirection ,
145- toolbar : this . _toolbarPattern ,
146- element : ( ) => this . _elementRef . nativeElement ,
147- focusMode : this . _toolbarPattern ( ) ?. inputs . focusMode ?? this . focusMode ,
148- skipDisabled : this . _toolbarPattern ( ) ?. inputs . skipDisabled ?? this . skipDisabled ,
149- } ) ;
145+ readonly pattern : RadioGroupPattern < V > ;
150146
151147 /** Whether the radio group has received focus yet. */
152148 private _hasFocused = signal ( false ) ;
153149
154150 constructor ( ) {
151+ const inputs : RadioGroupInputs < V > | ToolbarRadioGroupInputs < V > = {
152+ ...this ,
153+ items : this . items ,
154+ value : this . _value ,
155+ activeItem : signal ( undefined ) ,
156+ textDirection : this . textDirection ,
157+ element : ( ) => this . _elementRef . nativeElement ,
158+ getItem : e => {
159+ if ( ! ( e . target instanceof HTMLElement ) ) {
160+ return undefined ;
161+ }
162+ const element = e . target . closest ( '[role="radio"]' ) ;
163+ return this . items ( ) . find ( i => i . element ( ) === element ) ;
164+ } ,
165+ toolbar : this . _cdkToolbarWidgetGroup . toolbar ,
166+ } ;
167+
168+ this . pattern = this . _hasToolbar ( )
169+ ? new ToolbarRadioGroupPattern ( inputs as ToolbarRadioGroupInputs < V > )
170+ : new RadioGroupPattern ( inputs as RadioGroupInputs < V > ) ;
171+
172+ if ( this . _hasToolbar ( ) ) {
173+ this . _cdkToolbarWidgetGroup . controls . set ( this . pattern as ToolbarRadioGroupPattern < V > ) ;
174+ }
175+
155176 afterRenderEffect ( ( ) => {
156177 if ( typeof ngDevMode === 'undefined' || ngDevMode ) {
157178 const violations = this . pattern . validate ( ) ;
@@ -162,35 +183,21 @@ export class CdkRadioGroup<V> {
162183 } ) ;
163184
164185 afterRenderEffect ( ( ) => {
165- if ( ! this . _hasFocused ( ) && ! this . toolbar ) {
186+ if ( ! this . _hasFocused ( ) && ! this . _hasToolbar ( ) ) {
166187 this . pattern . setDefaultState ( ) ;
167188 }
168189 } ) ;
169190
170- // TODO: Refactor to be handled within list behavior
171191 afterRenderEffect ( ( ) => {
172- if ( this . toolbar ) {
173- const radioButtons = this . _cdkRadioButtons ( ) ;
174- // If the group is disabled and the toolbar is set to skip disabled items,
175- // the radio buttons should not be part of the toolbar's navigation.
176- if ( this . disabled ( ) && this . toolbar . skipDisabled ( ) ) {
177- radioButtons . forEach ( radio => this . toolbar ! . unregister ( radio ) ) ;
178- } else {
179- radioButtons . forEach ( radio => this . toolbar ! . register ( radio ) ) ;
180- }
192+ if ( this . _hasToolbar ( ) ) {
193+ this . _cdkToolbarWidgetGroup . disabled . set ( this . disabled ( ) ) ;
181194 }
182195 } ) ;
183196 }
184197
185198 onFocus ( ) {
186199 this . _hasFocused . set ( true ) ;
187200 }
188-
189- toolbarButtonUnregister ( radio : CdkRadioButton < V > ) {
190- if ( this . toolbar ) {
191- this . toolbar . unregister ( radio ) ;
192- }
193- }
194201}
195202
196203/** A selectable radio button in a CdkRadioGroup. */
@@ -207,7 +214,7 @@ export class CdkRadioGroup<V> {
207214 '[id]' : 'pattern.id()' ,
208215 } ,
209216} )
210- export class CdkRadioButton < V > implements OnDestroy {
217+ export class CdkRadioButton < V > {
211218 /** A reference to the radio button element. */
212219 private readonly _elementRef = inject ( ElementRef ) ;
213220
@@ -218,13 +225,13 @@ export class CdkRadioButton<V> implements OnDestroy {
218225 private readonly _generatedId = inject ( _IdGenerator ) . getId ( 'cdk-radio-button-' ) ;
219226
220227 /** A unique identifier for the radio button. */
221- protected id = computed ( ( ) => this . _generatedId ) ;
228+ readonly id = computed ( ( ) => this . _generatedId ) ;
222229
223230 /** The value associated with the radio button. */
224231 readonly value = input . required < V > ( ) ;
225232
226233 /** The parent RadioGroup UIPattern. */
227- protected group = computed ( ( ) => this . _cdkRadioGroup . pattern ) ;
234+ readonly group = computed ( ( ) => this . _cdkRadioGroup . pattern ) ;
228235
229236 /** A reference to the radio button element to be focused on navigation. */
230237 element = computed ( ( ) => this . _elementRef . nativeElement ) ;
@@ -240,10 +247,4 @@ export class CdkRadioButton<V> implements OnDestroy {
240247 group : this . group ,
241248 element : this . element ,
242249 } ) ;
243-
244- ngOnDestroy ( ) {
245- if ( this . _cdkRadioGroup . toolbar ) {
246- this . _cdkRadioGroup . toolbarButtonUnregister ( this ) ;
247- }
248- }
249250}
0 commit comments