66 * found in the LICENSE file at https://angular.dev/license
77 */
88
9- import { ChangeDetectionStrategy , Component , ViewEncapsulation } from '@angular/core' ;
10- import { MAT_BUTTON_HOST , MatButtonBase } from './button-base' ;
9+ import { ChangeDetectionStrategy , Component , Input , ViewEncapsulation } from '@angular/core' ;
10+ import { MAT_BUTTON_HOST , MatButtonAppearance , MatButtonBase } from './button-base' ;
11+
12+ /**
13+ * Classes that need to be set for each appearance of the button.
14+ * Note that we use a `Map` here to avoid issues with property renaming.
15+ */
16+ const APPEARANCE_CLASSES : Map < MatButtonAppearance , readonly string [ ] > = new Map ( [
17+ [ 'text' , [ 'mat-mdc-button' ] ] ,
18+ [ 'filled' , [ 'mdc-button--unelevated' , 'mat-mdc-unelevated-button' ] ] ,
19+ [ 'elevated' , [ 'mdc-button--raised' , 'mat-mdc-raised-button' ] ] ,
20+ [ 'outlined' , [ 'mdc-button--outlined' , 'mat-mdc-outlined-button' ] ] ,
21+ ] ) ;
1122
1223/**
1324 * Material Design button component. Users interact with a button to perform an action.
14- * See https://material.io/components/buttons
15- *
16- * The `MatButton` class applies to native button elements and captures the appearances for
17- * "text button", "outlined button", and "contained button" per the Material Design
18- * specification. `MatButton` additionally captures an additional "flat" appearance, which matches
19- * "contained" but without elevation.
25+ * See https://m3.material.io/components/buttons/overview
2026 */
2127@Component ( {
2228 selector : `
23- button[mat-button ], button[mat-raised- button], button[mat-flat -button],
24- button[mat-stroked -button], a [mat-button], a[mat-raised- button], a[mat-flat -button],
25- a[mat-stroked-button]
29+ button[matButton ], a[matButton], button[mat-button], button[mat-raised -button],
30+ button[mat-flat -button], button [mat-stroked- button], a[mat-button], a[mat-raised -button],
31+ a[mat-flat-button], a[mat- stroked-button]
2632 ` ,
2733 templateUrl : 'button.html' ,
2834 styleUrls : [ 'button.css' , 'button-high-contrast.css' ] ,
@@ -31,18 +37,87 @@ import {MAT_BUTTON_HOST, MatButtonBase} from './button-base';
3137 encapsulation : ViewEncapsulation . None ,
3238 changeDetection : ChangeDetectionStrategy . OnPush ,
3339} )
34- export class MatButton extends MatButtonBase { }
40+ export class MatButton extends MatButtonBase {
41+ /** Appearance of the button. */
42+ @Input ( 'matButton' )
43+ get appearance ( ) : MatButtonAppearance | null {
44+ return this . _appearance ;
45+ }
46+ set appearance ( value : MatButtonAppearance | '' ) {
47+ // Allow empty string so users can do `<button matButton></button>`
48+ // without having to write out `="text"` every time.
49+ this . setAppearance ( value || this . _config ?. defaultAppearance || 'text' ) ;
50+ }
51+ private _appearance : MatButtonAppearance | null = null ;
52+
53+ constructor ( ...args : unknown [ ] ) ;
54+
55+ constructor ( ) {
56+ super ( ) ;
57+ const element = this . _elementRef . nativeElement ;
58+ const inferredAppearance = _inferAppearance ( element ) ;
59+
60+ // This class is common across all the appearances so we add it ahead of time.
61+ element . classList . add ( 'mdc-button' ) ;
62+
63+ // Only set the appearance if we managed to infer it from the static attributes, rather than
64+ // doing something like `setAppearance(inferredAppearance || 'text')`, because doing so can
65+ // cause the fallback appearance's classes to be set and then immediately replaced when
66+ // the input value is assigned.
67+ if ( inferredAppearance ) {
68+ this . setAppearance ( inferredAppearance ) ;
69+ }
70+ }
71+
72+ /** Programmatically sets the appearance of the button. */
73+ setAppearance ( appearance : MatButtonAppearance ) : void {
74+ if ( appearance === this . _appearance ) {
75+ return ;
76+ }
77+
78+ const classList = this . _elementRef . nativeElement . classList ;
79+ const previousClasses = this . _appearance ? APPEARANCE_CLASSES . get ( this . _appearance ) : null ;
80+ const newClasses = APPEARANCE_CLASSES . get ( appearance ) ! ;
81+
82+ if ( ( typeof ngDevMode === 'undefined' || ngDevMode ) && ! newClasses ) {
83+ throw new Error ( `Unsupported MatButton appearance "${ appearance } "` ) ;
84+ }
85+
86+ if ( previousClasses ) {
87+ classList . remove ( ...previousClasses ) ;
88+ }
89+
90+ classList . add ( ...newClasses ) ;
91+ this . _appearance = appearance ;
92+ }
93+ }
94+
95+ /** Infers the button's appearance from its static attributes. */
96+ function _inferAppearance ( button : HTMLElement ) : MatButtonAppearance | null {
97+ if ( button . hasAttribute ( 'mat-raised-button' ) ) {
98+ return 'elevated' ;
99+ }
100+
101+ if ( button . hasAttribute ( 'mat-stroked-button' ) ) {
102+ return 'outlined' ;
103+ }
104+
105+ if ( button . hasAttribute ( 'mat-flat-button' ) ) {
106+ return 'filled' ;
107+ }
108+
109+ if ( button . hasAttribute ( 'mat-button' ) ) {
110+ return 'text' ;
111+ }
112+
113+ return null ;
114+ }
35115
36116// tslint:disable:variable-name
37117/**
38118 * Material Design button component for anchor elements. Anchor elements are used to provide
39119 * links for the user to navigate across different routes or pages.
40- * See https://material.io/components/buttons
41- *
42- * The `MatAnchor` class applies to native anchor elements and captures the appearances for
43- * "text button", "outlined button", and "contained button" per the Material Design
44- * specification. `MatAnchor` additionally captures an additional "flat" appearance, which matches
45- * "contained" but without elevation.
120+ * See https://m3.material.io/components/buttons/overview
46121 */
47122export const MatAnchor = MatButton ;
48123export type MatAnchor = MatButton ;
0 commit comments