@@ -19,12 +19,17 @@ import {
19
19
model ,
20
20
signal ,
21
21
WritableSignal ,
22
- OnDestroy ,
23
22
} 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' ;
25
30
import { Directionality } from '@angular/cdk/bidi' ;
26
31
import { _IdGenerator } from '@angular/cdk/a11y' ;
27
- import { CdkToolbar } from '.. /toolbar' ;
32
+ import { CdkToolbarWidgetGroup } from '@angular/cdk-experimental /toolbar' ;
28
33
29
34
// TODO: Move mapSignal to it's own file so it can be reused across components.
30
35
@@ -91,23 +96,24 @@ export function mapSignal<T, V>(
91
96
'(pointerdown)' : 'pattern.onPointerdown($event)' ,
92
97
'(focusin)' : 'onFocus()' ,
93
98
} ,
99
+ hostDirectives : [ CdkToolbarWidgetGroup ] ,
94
100
} )
95
101
export class CdkRadioGroup < V > {
96
102
/** A reference to the radio group element. */
97
103
private readonly _elementRef = inject ( ElementRef ) ;
98
104
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
+
99
111
/** The CdkRadioButtons nested inside of the CdkRadioGroup. */
100
112
private readonly _cdkRadioButtons = contentChildren ( CdkRadioButton , { descendants : true } ) ;
101
113
102
114
/** A signal wrapper for directionality. */
103
115
protected textDirection = inject ( Directionality ) . valueSignal ;
104
116
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
-
111
117
/** The RadioButton UIPatterns of the child CdkRadioButtons. */
112
118
protected items = computed ( ( ) => this . _cdkRadioButtons ( ) . map ( radio => radio . pattern ) ) ;
113
119
@@ -136,22 +142,37 @@ export class CdkRadioGroup<V> {
136
142
} ) ;
137
143
138
144
/** 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 > ;
150
146
151
147
/** Whether the radio group has received focus yet. */
152
148
private _hasFocused = signal ( false ) ;
153
149
154
150
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
+
155
176
afterRenderEffect ( ( ) => {
156
177
if ( typeof ngDevMode === 'undefined' || ngDevMode ) {
157
178
const violations = this . pattern . validate ( ) ;
@@ -162,35 +183,21 @@ export class CdkRadioGroup<V> {
162
183
} ) ;
163
184
164
185
afterRenderEffect ( ( ) => {
165
- if ( ! this . _hasFocused ( ) && ! this . toolbar ) {
186
+ if ( ! this . _hasFocused ( ) && ! this . _hasToolbar ( ) ) {
166
187
this . pattern . setDefaultState ( ) ;
167
188
}
168
189
} ) ;
169
190
170
- // TODO: Refactor to be handled within list behavior
171
191
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 ( ) ) ;
181
194
}
182
195
} ) ;
183
196
}
184
197
185
198
onFocus ( ) {
186
199
this . _hasFocused . set ( true ) ;
187
200
}
188
-
189
- toolbarButtonUnregister ( radio : CdkRadioButton < V > ) {
190
- if ( this . toolbar ) {
191
- this . toolbar . unregister ( radio ) ;
192
- }
193
- }
194
201
}
195
202
196
203
/** A selectable radio button in a CdkRadioGroup. */
@@ -207,7 +214,7 @@ export class CdkRadioGroup<V> {
207
214
'[id]' : 'pattern.id()' ,
208
215
} ,
209
216
} )
210
- export class CdkRadioButton < V > implements OnDestroy {
217
+ export class CdkRadioButton < V > {
211
218
/** A reference to the radio button element. */
212
219
private readonly _elementRef = inject ( ElementRef ) ;
213
220
@@ -218,13 +225,13 @@ export class CdkRadioButton<V> implements OnDestroy {
218
225
private readonly _generatedId = inject ( _IdGenerator ) . getId ( 'cdk-radio-button-' ) ;
219
226
220
227
/** A unique identifier for the radio button. */
221
- protected id = computed ( ( ) => this . _generatedId ) ;
228
+ readonly id = computed ( ( ) => this . _generatedId ) ;
222
229
223
230
/** The value associated with the radio button. */
224
231
readonly value = input . required < V > ( ) ;
225
232
226
233
/** The parent RadioGroup UIPattern. */
227
- protected group = computed ( ( ) => this . _cdkRadioGroup . pattern ) ;
234
+ readonly group = computed ( ( ) => this . _cdkRadioGroup . pattern ) ;
228
235
229
236
/** A reference to the radio button element to be focused on navigation. */
230
237
element = computed ( ( ) => this . _elementRef . nativeElement ) ;
@@ -240,10 +247,4 @@ export class CdkRadioButton<V> implements OnDestroy {
240
247
group : this . group ,
241
248
element : this . element ,
242
249
} ) ;
243
-
244
- ngOnDestroy ( ) {
245
- if ( this . _cdkRadioGroup . toolbar ) {
246
- this . _cdkRadioGroup . toolbarButtonUnregister ( this ) ;
247
- }
248
- }
249
250
}
0 commit comments