6
6
* found in the LICENSE file at https://angular.dev/license
7
7
*/
8
8
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
+ ] ) ;
11
22
12
23
/**
13
24
* 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
20
26
*/
21
27
@Component ( {
22
28
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]
26
32
` ,
27
33
templateUrl : 'button.html' ,
28
34
styleUrls : [ 'button.css' , 'button-high-contrast.css' ] ,
@@ -31,18 +37,87 @@ import {MAT_BUTTON_HOST, MatButtonBase} from './button-base';
31
37
encapsulation : ViewEncapsulation . None ,
32
38
changeDetection : ChangeDetectionStrategy . OnPush ,
33
39
} )
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
+ }
35
115
36
116
// tslint:disable:variable-name
37
117
/**
38
118
* Material Design button component for anchor elements. Anchor elements are used to provide
39
119
* 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
46
121
*/
47
122
export const MatAnchor = MatButton ;
48
123
export type MatAnchor = MatButton ;
0 commit comments