Skip to content

feat(cdk-experimental/toolbar): toolbar and widget #31610

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 13 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .ng-dev/commit-message.mts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export const commitMessage: CommitMessageConfig = {
'cdk-experimental/selection',
'cdk-experimental/table-scroll-container',
'cdk-experimental/tabs',
'cdk-experimental/toolbar',
'cdk-experimental/tree',
'cdk-experimental/ui-patterns',
'cdk/a11y',
Expand Down
30 changes: 30 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,33 @@
<a name="20.2.0-next.3"></a>
# 20.2.0-next.3 "metal-monkey" (2025-08-06)
### material
| Commit | Type | Description |
| -- | -- | -- |
| [845a6910a6](https://github.com/angular/components/commit/845a6910a60a652dd7d171ee026ee8a8887a2459) | fix | **autocomplete:** default to transparent backdrop ([#31647](https://github.com/angular/components/pull/31647)) |
| [11ad09ff3e](https://github.com/angular/components/commit/11ad09ff3e67d1825e2c6ce3d211d5192cf8e354) | fix | **button-toggle:** skip keyboard navigation when modifier key is pressed ([#31651](https://github.com/angular/components/pull/31651)) |
| [4bf8ebf5f3](https://github.com/angular/components/commit/4bf8ebf5f3b874ae2f512dc684506f017f022f69) | fix | **chips:** focus not moved on destroy ([#31653](https://github.com/angular/components/pull/31653)) |
| [b2c7abfbad](https://github.com/angular/components/commit/b2c7abfbadb5d12900c4bf9705b871183d4b2af7) | fix | **core:** add key validation to m2-theme ([#31631](https://github.com/angular/components/pull/31631)) |
| [839aa3e375](https://github.com/angular/components/commit/839aa3e37521e7b8739a47ea7a4b54845cf62731) | fix | **core:** fix m2 system color values ([#31632](https://github.com/angular/components/pull/31632)) |
| [7674e5872c](https://github.com/angular/components/commit/7674e5872c627998bd093994e8ef3f94427417c8) | fix | **core:** special-case icon button color token ([#31625](https://github.com/angular/components/pull/31625)) |
| [96117bceda](https://github.com/angular/components/commit/96117bcedad66f21257e043f83e90a77dc56deef) | fix | **form-field:** resolve memory leak ([#31643](https://github.com/angular/components/pull/31643)) |
### cdk-experimental
| Commit | Type | Description |
| -- | -- | -- |
| [228aaf1fa3](https://github.com/angular/components/commit/228aaf1fa395e805d7b581b9d02102d65f0a1562) | feat | **ui-patterns:** create List behavior ([#31601](https://github.com/angular/components/pull/31601)) |

<!-- CHANGELOG SPLIT MARKER -->

<a name="20.1.5"></a>
# 20.1.5 "plastic-car" (2025-08-06)
### material
| Commit | Type | Description |
| -- | -- | -- |
| [dbdcc7dcb7](https://github.com/angular/components/commit/dbdcc7dcb77a1f86446e8ced2359717d3af00e1f) | fix | **autocomplete:** default to transparent backdrop ([#31647](https://github.com/angular/components/pull/31647)) |
| [ae9e8d2f84](https://github.com/angular/components/commit/ae9e8d2f846f605dc77154e7f3d7df75cc22ae06) | fix | **chips:** focus not moved on destroy ([#31653](https://github.com/angular/components/pull/31653)) |
| [24ae377723](https://github.com/angular/components/commit/24ae3777233f63da35ba9106bf554d6dba20bb88) | fix | **form-field:** resolve memory leak ([#31643](https://github.com/angular/components/pull/31643)) |

<!-- CHANGELOG SPLIT MARKER -->

<a name="20.2.0-next.2"></a>
# 20.2.0-next.2 "archerite-asparagus" (2025-07-30)
### cdk
Expand Down
2 changes: 1 addition & 1 deletion goldens/material/chips/index.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ export class MatChip implements OnInit, AfterViewInit, AfterContentInit, DoCheck
protected _allTrailingIcons: QueryList<MatChipTrailingIcon>;
_animationsDisabled: boolean;
ariaDescription: string | null;
_ariaDescriptionId: string;
ariaLabel: string | null;
protected basicChipAttrName: string;
// (undocumented)
Expand All @@ -84,6 +83,7 @@ export class MatChip implements OnInit, AfterViewInit, AfterContentInit, DoCheck
_handlePrimaryActionInteraction(): void;
// (undocumented)
_hasFocus(): boolean;
_hasInteractiveActions(): boolean;
_hasTrailingIcon(): boolean;
highlighted: boolean;
id: string;
Expand Down
6 changes: 6 additions & 0 deletions goldens/material/stepper/index.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,12 @@ export class MatStepHeader extends CdkStepHeader implements AfterViewInit, OnDes
// (undocumented)
_getDefaultTextForState(state: StepState): string;
_getHostElement(): HTMLElement;
// (undocumented)
protected _hasEmptyLabel(): boolean;
// (undocumented)
protected _hasErrorLabel(): boolean;
// (undocumented)
protected _hasOptionalLabel(): boolean;
iconOverrides: {
[key: string]: TemplateRef<MatStepperIconContext>;
};
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
"ci-docs-monitor-test": "node --no-warnings=ExperimentalWarning --loader ts-node/esm/transpile-only scripts/docs-deploy/monitoring/ci-test.mts",
"prepare": "husky"
},
"version": "20.2.0-next.2",
"version": "20.2.0-next.3",
"dependencies": {
"@angular-devkit/core": "catalog:",
"@angular-devkit/schematics": "catalog:",
Expand Down
1 change: 1 addition & 0 deletions src/cdk-experimental/config.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ CDK_EXPERIMENTAL_ENTRYPOINTS = [
"scrolling",
"selection",
"tabs",
"toolbar",
"tree",
"table-scroll-container",
"ui-patterns",
Expand Down
1 change: 1 addition & 0 deletions src/cdk-experimental/radio-group/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ ng_project(
),
deps = [
"//:node_modules/@angular/core",
"//src/cdk-experimental/toolbar",
"//src/cdk-experimental/ui-patterns",
"//src/cdk/a11y",
"//src/cdk/bidi",
Expand Down
41 changes: 38 additions & 3 deletions src/cdk-experimental/radio-group/radio-group.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@ import {
model,
signal,
WritableSignal,
OnDestroy,
} from '@angular/core';
import {RadioButtonPattern, RadioGroupPattern} from '../ui-patterns';
import {Directionality} from '@angular/cdk/bidi';
import {_IdGenerator} from '@angular/cdk/a11y';
import {CdkToolbar} from '../toolbar';

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

Expand Down Expand Up @@ -97,6 +99,12 @@ export class CdkRadioGroup<V> {
/** A signal wrapper for directionality. */
protected textDirection = inject(Directionality).valueSignal;

/** A signal wrapper for toolbar. */
toolbar = inject(CdkToolbar, {optional: true});

/** Toolbar pattern if applicable */
private readonly _toolbarPattern = computed(() => this.toolbar?.pattern);

/** The RadioButton UIPatterns of the child CdkRadioButtons. */
protected items = computed(() => this._cdkRadioButtons().map(radio => radio.pattern));

Expand Down Expand Up @@ -131,6 +139,8 @@ export class CdkRadioGroup<V> {
value: this._value,
activeItem: signal(undefined),
textDirection: this.textDirection,
toolbar: this._toolbarPattern,
focusMode: this._toolbarPattern()?.inputs.focusMode ?? this.focusMode,
});

/** Whether the radio group has received focus yet. */
Expand All @@ -147,15 +157,34 @@ export class CdkRadioGroup<V> {
});

afterRenderEffect(() => {
if (!this._hasFocused()) {
if (!this._hasFocused() && !this.toolbar) {
this.pattern.setDefaultState();
}
});

afterRenderEffect(() => {
if (this.toolbar) {
const radioButtons = this._cdkRadioButtons();
// If the group is disabled and the toolbar is set to skip disabled items,
// the radio buttons should not be part of the toolbar's navigation.
if (this.disabled() && this.toolbar.skipDisabled()) {
radioButtons.forEach(radio => this.toolbar!.deregister(radio));
} else {
radioButtons.forEach(radio => this.toolbar!.register(radio));
}
}
});
}

onFocus() {
this._hasFocused.set(true);
}

toolbarButtonDeregister(radio: CdkRadioButton<V>) {
if (this.toolbar) {
this.toolbar.deregister(radio);
}
}
}

/** A selectable radio button in a CdkRadioGroup. */
Expand All @@ -172,7 +201,7 @@ export class CdkRadioGroup<V> {
'[id]': 'pattern.id()',
},
})
export class CdkRadioButton<V> {
export class CdkRadioButton<V> implements OnDestroy {
/** A reference to the radio button element. */
private readonly _elementRef = inject(ElementRef);

Expand All @@ -192,7 +221,7 @@ export class CdkRadioButton<V> {
protected group = computed(() => this._cdkRadioGroup.pattern);

/** A reference to the radio button element to be focused on navigation. */
protected element = computed(() => this._elementRef.nativeElement);
element = computed(() => this._elementRef.nativeElement);

/** Whether the radio button is disabled. */
disabled = input(false, {transform: booleanAttribute});
Expand All @@ -205,4 +234,10 @@ export class CdkRadioButton<V> {
group: this.group,
element: this.element,
});

ngOnDestroy() {
if (this._cdkRadioGroup.toolbar) {
this._cdkRadioGroup.toolbarButtonDeregister(this);
}
}
}
17 changes: 17 additions & 0 deletions src/cdk-experimental/toolbar/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
load("//tools:defaults.bzl", "ng_project")

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

ng_project(
name = "toolbar",
srcs = glob(
["**/*.ts"],
exclude = ["**/*.spec.ts"],
),
deps = [
"//:node_modules/@angular/core",
"//src/cdk-experimental/ui-patterns",
"//src/cdk/a11y",
"//src/cdk/bidi",
],
)
9 changes: 9 additions & 0 deletions src/cdk-experimental/toolbar/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/

export * from './public-api';
9 changes: 9 additions & 0 deletions src/cdk-experimental/toolbar/public-api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/

export {CdkToolbar, CdkToolbarWidget} from './toolbar';
Loading