diff --git a/src/dev-app/button-toggle/button-toggle-demo.html b/src/dev-app/button-toggle/button-toggle-demo.html
index b2cfc8186f3e..f0619ad7895a 100644
--- a/src/dev-app/button-toggle/button-toggle-demo.html
+++ b/src/dev-app/button-toggle/button-toggle-demo.html
@@ -6,10 +6,18 @@
Disable Button Toggle Items
+
+ Hide Single Selection Indicator
+
+
+
+ Hide Multiple Selection Indicator
+
+
Exclusive Selection
-
+
format_align_left
@@ -26,7 +34,7 @@ Exclusive Selection
-
+
format_align_left
@@ -45,7 +53,7 @@ Exclusive Selection
Disabled Group
-
+
format_bold
@@ -60,7 +68,7 @@ Disabled Group
Multiple Selection
-
+
Flour
Eggs
Sugar
@@ -68,7 +76,7 @@ Multiple Selection
-
+
Flour
Eggs
Sugar
@@ -82,7 +90,7 @@ Single Toggle
Dynamic Exclusive Selection
-
+
@for (pie of pieOptions; track pie) {
{{pie}}
}
diff --git a/src/dev-app/button-toggle/button-toggle-demo.ts b/src/dev-app/button-toggle/button-toggle-demo.ts
index add6a66823a7..e7cbc1f27ee5 100644
--- a/src/dev-app/button-toggle/button-toggle-demo.ts
+++ b/src/dev-app/button-toggle/button-toggle-demo.ts
@@ -23,6 +23,8 @@ import {MatIconModule} from '@angular/material/icon';
export class ButtonToggleDemo {
isVertical = false;
isDisabled = false;
+ hideSingleSelectionIndicator = false;
+ hideMultipleSelectionIndicator = false;
favoritePie = 'Apple';
pieOptions = ['Apple', 'Cherry', 'Pecan', 'Lemon'];
}
diff --git a/src/material/button-toggle/button-toggle.html b/src/material/button-toggle/button-toggle.html
index 9a2113709715..0a1b9a06eeaf 100644
--- a/src/material/button-toggle/button-toggle.html
+++ b/src/material/button-toggle/button-toggle.html
@@ -9,6 +9,24 @@
[attr.aria-labelledby]="ariaLabelledby"
(click)="_onButtonClick()">
+
+ @if (buttonToggleGroup && checked && !buttonToggleGroup.multiple && !buttonToggleGroup.hideSingleSelectionIndicator) {
+
+ }
+
+ @if (buttonToggleGroup && checked && buttonToggleGroup.multiple && !buttonToggleGroup.hideMultipleSelectionIndicator) {
+
+ }
diff --git a/src/material/button-toggle/button-toggle.scss b/src/material/button-toggle/button-toggle.scss
index 47dcfef3c468..3cc13c4811c9 100644
--- a/src/material/button-toggle/button-toggle.scss
+++ b/src/material/button-toggle/button-toggle.scss
@@ -9,6 +9,7 @@
$standard-padding: 0 12px !default;
$legacy-padding: 0 16px !default;
+$checkmark-padding: 12px !default;
// TODO(crisbeto): these variables aren't used anymore and should be removed.
$legacy-height: 36px !default;
@@ -52,6 +53,12 @@ $_standard-tokens: (
@include token-utils.use-tokens($_standard-tokens...) {
@include token-utils.create-token-slot(border-radius, shape);
border: solid 1px var(#{token-utils.get-token-variable(divider-color)});
+
+ .mat-pseudo-checkbox {
+ --mat-minimal-pseudo-checkbox-selected-checkmark-color: var(
+ #{token-utils.get-token-variable(selected-state-text-color)}
+ );
+ }
}
&:not([class*='mat-elevation-z']) {
@@ -85,6 +92,10 @@ $_standard-tokens: (
@include token-utils.create-token-slot(font-weight, label-text-weight);
@include token-utils.create-token-slot(letter-spacing, label-text-tracking);
+ --mat-minimal-pseudo-checkbox-selected-checkmark-color: var(
+ #{token-utils.get-token-variable(selected-state-text-color)}
+ );
+
&.cdk-keyboard-focused .mat-button-toggle-focus-overlay {
@include token-utils.create-token-slot(opacity, focus-state-layer-opacity);
}
@@ -94,6 +105,14 @@ $_standard-tokens: (
.mat-icon svg {
vertical-align: top;
}
+
+ .mat-pseudo-checkbox {
+ margin-right: $checkmark-padding;
+ [dir='rtl'] & {
+ margin-right: 0;
+ margin-left: $checkmark-padding;
+ }
+ }
}
.mat-button-toggle-checked {
@@ -107,6 +126,9 @@ $_standard-tokens: (
@include token-utils.use-tokens($_legacy-tokens...) {
@include token-utils.create-token-slot(color, disabled-state-text-color);
@include token-utils.create-token-slot(background-color, disabled-state-background-color);
+ --mat-minimal-pseudo-checkbox-disabled-selected-checkmark-color: var(
+ #{token-utils.get-token-variable(disabled-state-text-color)}
+ );
&.mat-button-toggle-checked {
@include token-utils.create-token-slot(background-color,
@@ -150,6 +172,12 @@ $_standard-tokens: (
@include token-utils.create-token-slot(color, disabled-state-text-color);
@include token-utils.create-token-slot(background-color, disabled-state-background-color);
+ .mat-pseudo-checkbox {
+ --mat-minimal-pseudo-checkbox-disabled-selected-checkmark-color: var(
+ #{token-utils.get-token-variable(disabled-selected-state-text-color)}
+ );
+ }
+
&.mat-button-toggle-checked {
@include token-utils.create-token-slot(color, disabled-selected-state-text-color);
@include token-utils.create-token-slot(background-color,
diff --git a/src/material/button-toggle/button-toggle.spec.ts b/src/material/button-toggle/button-toggle.spec.ts
index 7f33cc6668f9..20265dd178fa 100644
--- a/src/material/button-toggle/button-toggle.spec.ts
+++ b/src/material/button-toggle/button-toggle.spec.ts
@@ -5,6 +5,7 @@ import {ComponentFixture, fakeAsync, flush, TestBed, tick} from '@angular/core/t
import {FormControl, FormsModule, NgModel, ReactiveFormsModule} from '@angular/forms';
import {By} from '@angular/platform-browser';
import {
+ MAT_BUTTON_TOGGLE_DEFAULT_OPTIONS,
MatButtonToggle,
MatButtonToggleChange,
MatButtonToggleGroup,
@@ -546,6 +547,13 @@ describe('MatButtonToggle without forms', () => {
expect(groupInstance.value).toBeFalsy();
expect(groupInstance.selected).toBeFalsy();
}));
+
+ it('should show checkmark indicator by default', () => {
+ buttonToggleLabelElements[0].click();
+ fixture.detectChanges();
+
+ expect(document.querySelectorAll('.mat-pseudo-checkbox').length).toBe(1);
+ });
});
describe('with initial value and change event', () => {
@@ -701,6 +709,14 @@ describe('MatButtonToggle without forms', () => {
groupInstance.value = 'not-an-array';
}).toThrowError(/Value must be an array/);
});
+
+ it('should show checkmark indicator by default', () => {
+ buttonToggleLabelElements[0].click();
+ buttonToggleLabelElements[1].click();
+ fixture.detectChanges();
+
+ expect(document.querySelectorAll('.mat-pseudo-checkbox').length).toBe(2);
+ });
});
describe('as standalone', () => {
@@ -876,6 +892,52 @@ describe('MatButtonToggle without forms', () => {
});
});
+ describe('with tokens to hide checkmark selection indicators', () => {
+ beforeEach(() => {
+ TestBed.configureTestingModule({
+ imports: [
+ MatButtonToggleModule,
+ ButtonTogglesInsideButtonToggleGroup,
+ ButtonTogglesInsideButtonToggleGroupMultiple,
+ ],
+ providers: [
+ {
+ provide: MAT_BUTTON_TOGGLE_DEFAULT_OPTIONS,
+ useValue: {
+ hideSingleSelectionIndicator: true,
+ hideMultipleSelectionIndicator: true,
+ },
+ },
+ ],
+ });
+
+ TestBed.compileComponents();
+ });
+
+ it('should hide checkmark indicator for single selection', () => {
+ const fixture = TestBed.createComponent(ButtonTogglesInsideButtonToggleGroup);
+ fixture.detectChanges();
+
+ fixture.debugElement.query(By.css('button')).nativeElement.click();
+ fixture.detectChanges();
+
+ expect(document.querySelectorAll('.mat-pseudo-checkbox').length).toBe(0);
+ });
+
+ it('should hide checkmark indicator for multiple selection', () => {
+ const fixture = TestBed.createComponent(ButtonTogglesInsideButtonToggleGroupMultiple);
+ fixture.detectChanges();
+
+ // Check all button toggles in the group
+ fixture.debugElement
+ .queryAll(By.css('button'))
+ .forEach(toggleButton => toggleButton.nativeElement.click());
+ fixture.detectChanges();
+
+ expect(document.querySelectorAll('.mat-pseudo-checkbox').length).toBe(0);
+ });
+ });
+
it('should not throw on init when toggles are repeated and there is an initial value', () => {
const fixture = TestBed.createComponent(RepeatedButtonTogglesWithPreselectedValue);
diff --git a/src/material/button-toggle/button-toggle.ts b/src/material/button-toggle/button-toggle.ts
index 34c5477a6944..ab9c8a527928 100644
--- a/src/material/button-toggle/button-toggle.ts
+++ b/src/material/button-toggle/button-toggle.ts
@@ -33,7 +33,7 @@ import {
booleanAttribute,
} from '@angular/core';
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms';
-import {MatRipple} from '@angular/material/core';
+import {MatRipple, MatPseudoCheckbox} from '@angular/material/core';
/**
* @deprecated No longer used.
@@ -54,6 +54,10 @@ export interface MatButtonToggleDefaultOptions {
* setting an appearance on a button toggle or group.
*/
appearance?: MatButtonToggleAppearance;
+ /** Whetehr icon indicators should be hidden for single-selection button toggle groups. */
+ hideSingleSelectionIndicator?: boolean;
+ /** Whether icon indicators should be hidden for multiple-selection button toggle groups. */
+ hideMultipleSelectionIndicator?: boolean;
}
/**
@@ -62,8 +66,19 @@ export interface MatButtonToggleDefaultOptions {
*/
export const MAT_BUTTON_TOGGLE_DEFAULT_OPTIONS = new InjectionToken(
'MAT_BUTTON_TOGGLE_DEFAULT_OPTIONS',
+ {
+ providedIn: 'root',
+ factory: MAT_BUTTON_TOGGLE_GROUP_DEFAULT_OPTIONS_FACTORY,
+ },
);
+export function MAT_BUTTON_TOGGLE_GROUP_DEFAULT_OPTIONS_FACTORY(): MatButtonToggleDefaultOptions {
+ return {
+ hideSingleSelectionIndicator: false,
+ hideMultipleSelectionIndicator: false,
+ };
+}
+
/**
* Injection token that can be used to reference instances of `MatButtonToggleGroup`.
* It serves as alternative token to the actual `MatButtonToggleGroup` class which
@@ -215,6 +230,28 @@ export class MatButtonToggleGroup implements ControlValueAccessor, OnInit, After
@Output() readonly change: EventEmitter =
new EventEmitter();
+ /** Whether checkmark indicator for single-selection button toggle groups is hidden. */
+ @Input({transform: booleanAttribute})
+ get hideSingleSelectionIndicator(): boolean {
+ return this._hideSingleSelectionIndicator;
+ }
+ set hideSingleSelectionIndicator(value: boolean) {
+ this._hideSingleSelectionIndicator = value;
+ this._markButtonsForCheck();
+ }
+ private _hideSingleSelectionIndicator: boolean;
+
+ /** Whether checkmark indicator for multiple-selection button toggle groups is hidden. */
+ @Input({transform: booleanAttribute})
+ get hideMultipleSelectionIndicator(): boolean {
+ return this._hideMultipleSelectionIndicator;
+ }
+ set hideMultipleSelectionIndicator(value: boolean) {
+ this._hideMultipleSelectionIndicator = value;
+ this._markButtonsForCheck();
+ }
+ private _hideMultipleSelectionIndicator: boolean;
+
constructor(
private _changeDetector: ChangeDetectorRef,
@Optional()
@@ -223,6 +260,8 @@ export class MatButtonToggleGroup implements ControlValueAccessor, OnInit, After
) {
this.appearance =
defaultOptions && defaultOptions.appearance ? defaultOptions.appearance : 'standard';
+ this.hideSingleSelectionIndicator = defaultOptions?.hideSingleSelectionIndicator ?? false;
+ this.hideMultipleSelectionIndicator = defaultOptions?.hideMultipleSelectionIndicator ?? false;
}
ngOnInit() {
@@ -401,7 +440,7 @@ export class MatButtonToggleGroup implements ControlValueAccessor, OnInit, After
'role': 'presentation',
},
standalone: true,
- imports: [MatRipple],
+ imports: [MatRipple, MatPseudoCheckbox],
})
export class MatButtonToggle implements OnInit, AfterViewInit, OnDestroy {
private _checked = false;
diff --git a/tools/public_api_guard/material/button-toggle.md b/tools/public_api_guard/material/button-toggle.md
index 2c3fa8aaa840..38d3810694fb 100644
--- a/tools/public_api_guard/material/button-toggle.md
+++ b/tools/public_api_guard/material/button-toggle.md
@@ -24,6 +24,9 @@ export const MAT_BUTTON_TOGGLE_DEFAULT_OPTIONS: InjectionToken;
+// @public (undocumented)
+export function MAT_BUTTON_TOGGLE_GROUP_DEFAULT_OPTIONS_FACTORY(): MatButtonToggleDefaultOptions;
+
// @public
export const MAT_BUTTON_TOGGLE_GROUP_VALUE_ACCESSOR: any;
@@ -84,6 +87,8 @@ export class MatButtonToggleChange {
// @public
export interface MatButtonToggleDefaultOptions {
appearance?: MatButtonToggleAppearance;
+ hideMultipleSelectionIndicator?: boolean;
+ hideSingleSelectionIndicator?: boolean;
}
// @public
@@ -96,6 +101,10 @@ export class MatButtonToggleGroup implements ControlValueAccessor, OnInit, After
get disabled(): boolean;
set disabled(value: boolean);
_emitChangeEvent(toggle: MatButtonToggle): void;
+ get hideMultipleSelectionIndicator(): boolean;
+ set hideMultipleSelectionIndicator(value: boolean);
+ get hideSingleSelectionIndicator(): boolean;
+ set hideSingleSelectionIndicator(value: boolean);
_isPrechecked(toggle: MatButtonToggle): boolean;
_isSelected(toggle: MatButtonToggle): boolean;
get multiple(): boolean;
@@ -105,6 +114,10 @@ export class MatButtonToggleGroup implements ControlValueAccessor, OnInit, After
// (undocumented)
static ngAcceptInputType_disabled: unknown;
// (undocumented)
+ static ngAcceptInputType_hideMultipleSelectionIndicator: unknown;
+ // (undocumented)
+ static ngAcceptInputType_hideSingleSelectionIndicator: unknown;
+ // (undocumented)
static ngAcceptInputType_multiple: unknown;
// (undocumented)
static ngAcceptInputType_vertical: unknown;
@@ -127,7 +140,7 @@ export class MatButtonToggleGroup implements ControlValueAccessor, OnInit, After
vertical: boolean;
writeValue(value: any): void;
// (undocumented)
- static ɵdir: i0.ɵɵDirectiveDeclaration;
+ static ɵdir: i0.ɵɵDirectiveDeclaration;
// (undocumented)
static ɵfac: i0.ɵɵFactoryDeclaration;
}