Skip to content

Commit 42b0dd0

Browse files
committed
refactor(cdk-experimental/ui-patterns): add toolbar widget group to decouple toolbar and radio group
1 parent c21dfa3 commit 42b0dd0

File tree

15 files changed

+1400
-571
lines changed

15 files changed

+1400
-571
lines changed

src/cdk-experimental/radio-group/radio-group.ts

Lines changed: 20 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,11 @@ import {
1919
model,
2020
signal,
2121
WritableSignal,
22-
OnDestroy,
2322
} from '@angular/core';
2423
import {RadioButtonPattern, RadioGroupPattern} from '../ui-patterns';
2524
import {Directionality} from '@angular/cdk/bidi';
2625
import {_IdGenerator} from '@angular/cdk/a11y';
27-
import {CdkToolbar} from '../toolbar';
26+
import {CdkToolbarWidgetGroup} from '@angular/cdk-experimental/toolbar';
2827

2928
// TODO: Move mapSignal to it's own file so it can be reused across components.
3029

@@ -91,23 +90,24 @@ export function mapSignal<T, V>(
9190
'(pointerdown)': 'pattern.onPointerdown($event)',
9291
'(focusin)': 'onFocus()',
9392
},
93+
hostDirectives: [CdkToolbarWidgetGroup],
9494
})
9595
export class CdkRadioGroup<V> {
9696
/** A reference to the radio group element. */
9797
private readonly _elementRef = inject(ElementRef);
9898

99+
/** A reference to the CdkToolbarWidgetGroup, if the radio group is in a toolbar. */
100+
private readonly _cdkToolbarWidgetGroup = inject(CdkToolbarWidgetGroup);
101+
102+
/** Whether the radio group is inside of a CdkToolbar. */
103+
private readonly _hasToolbar = computed(() => !!this._cdkToolbarWidgetGroup.toolbar());
104+
99105
/** The CdkRadioButtons nested inside of the CdkRadioGroup. */
100106
private readonly _cdkRadioButtons = contentChildren(CdkRadioButton, {descendants: true});
101107

102108
/** A signal wrapper for directionality. */
103109
protected textDirection = inject(Directionality).valueSignal;
104110

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-
111111
/** The RadioButton UIPatterns of the child CdkRadioButtons. */
112112
protected items = computed(() => this._cdkRadioButtons().map(radio => radio.pattern));
113113

@@ -136,16 +136,14 @@ export class CdkRadioGroup<V> {
136136
});
137137

138138
/** The RadioGroup UIPattern. */
139-
pattern: RadioGroupPattern<V> = new RadioGroupPattern<V>({
139+
readonly pattern: RadioGroupPattern<V> = new RadioGroupPattern<V>({
140140
...this,
141141
items: this.items,
142142
value: this._value,
143143
activeItem: signal(undefined),
144144
textDirection: this.textDirection,
145-
toolbar: this._toolbarPattern,
146145
element: () => this._elementRef.nativeElement,
147-
focusMode: this._toolbarPattern()?.inputs.focusMode ?? this.focusMode,
148-
skipDisabled: this._toolbarPattern()?.inputs.skipDisabled ?? this.skipDisabled,
146+
toolbar: this._cdkToolbarWidgetGroup.toolbar,
149147
});
150148

151149
/** Whether the radio group has received focus yet. */
@@ -162,35 +160,25 @@ export class CdkRadioGroup<V> {
162160
});
163161

164162
afterRenderEffect(() => {
165-
if (!this._hasFocused() && !this.toolbar) {
163+
if (!this._hasFocused() && !this._hasToolbar()) {
166164
this.pattern.setDefaultState();
167165
}
168166
});
169167

170-
// TODO: Refactor to be handled within list behavior
171168
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-
}
169+
if (this._hasToolbar()) {
170+
this._cdkToolbarWidgetGroup.disabled.set(this.disabled());
181171
}
182172
});
173+
174+
if (this._hasToolbar()) {
175+
this._cdkToolbarWidgetGroup.actions.set(this.pattern.toolbarWidgetGroupActions);
176+
}
183177
}
184178

185179
onFocus() {
186180
this._hasFocused.set(true);
187181
}
188-
189-
toolbarButtonUnregister(radio: CdkRadioButton<V>) {
190-
if (this.toolbar) {
191-
this.toolbar.unregister(radio);
192-
}
193-
}
194182
}
195183

196184
/** A selectable radio button in a CdkRadioGroup. */
@@ -207,7 +195,7 @@ export class CdkRadioGroup<V> {
207195
'[id]': 'pattern.id()',
208196
},
209197
})
210-
export class CdkRadioButton<V> implements OnDestroy {
198+
export class CdkRadioButton<V> {
211199
/** A reference to the radio button element. */
212200
private readonly _elementRef = inject(ElementRef);
213201

@@ -218,13 +206,13 @@ export class CdkRadioButton<V> implements OnDestroy {
218206
private readonly _generatedId = inject(_IdGenerator).getId('cdk-radio-button-');
219207

220208
/** A unique identifier for the radio button. */
221-
protected id = computed(() => this._generatedId);
209+
readonly id = computed(() => this._generatedId);
222210

223211
/** The value associated with the radio button. */
224212
readonly value = input.required<V>();
225213

226214
/** The parent RadioGroup UIPattern. */
227-
protected group = computed(() => this._cdkRadioGroup.pattern);
215+
readonly group = computed(() => this._cdkRadioGroup.pattern);
228216

229217
/** A reference to the radio button element to be focused on navigation. */
230218
element = computed(() => this._elementRef.nativeElement);
@@ -240,10 +228,4 @@ export class CdkRadioButton<V> implements OnDestroy {
240228
group: this.group,
241229
element: this.element,
242230
});
243-
244-
ngOnDestroy() {
245-
if (this._cdkRadioGroup.toolbar) {
246-
this._cdkRadioGroup.toolbarButtonUnregister(this);
247-
}
248-
}
249231
}

src/cdk-experimental/toolbar/BUILD.bazel

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
load("//tools:defaults.bzl", "ng_project")
1+
load("//tools:defaults.bzl", "ng_project", "ng_web_test_suite", "ts_project")
22

33
package(default_visibility = ["//visibility:public"])
44

@@ -15,3 +15,22 @@ ng_project(
1515
"//src/cdk/bidi",
1616
],
1717
)
18+
19+
ts_project(
20+
name = "unit_test_sources",
21+
testonly = True,
22+
srcs = [
23+
"toolbar.spec.ts",
24+
],
25+
deps = [
26+
":toolbar",
27+
"//:node_modules/@angular/core",
28+
"//:node_modules/@angular/platform-browser",
29+
"//src/cdk/testing/private",
30+
],
31+
)
32+
33+
ng_web_test_suite(
34+
name = "unit_tests",
35+
deps = [":unit_test_sources"],
36+
)

src/cdk-experimental/toolbar/public-api.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,4 @@
66
* found in the LICENSE file at https://angular.dev/license
77
*/
88

9-
export {CdkToolbar, CdkToolbarWidget} from './toolbar';
9+
export {CdkToolbar, CdkToolbarWidget, CdkToolbarWidgetGroup} from './toolbar';

0 commit comments

Comments
 (0)