Skip to content
Merged
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
69 changes: 24 additions & 45 deletions src/material/button/button-base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,30 +7,20 @@
*/

import {FocusMonitor, FocusOrigin} from '@angular/cdk/a11y';
import {coerceBooleanProperty} from '@angular/cdk/coercion';
import {Platform} from '@angular/cdk/platform';
import {
AfterViewInit,
booleanAttribute,
Directive,
ElementRef,
inject,
Input,
NgZone,
numberAttribute,
OnDestroy,
OnInit,
} from '@angular/core';
import {
CanColor,
CanDisable,
CanDisableRipple,
MatRipple,
mixinColor,
mixinDisabled,
mixinDisableRipple,
MatRippleLoader,
} from '@angular/material/core';

/** Inputs common to all buttons. */
export const MAT_BUTTON_INPUTS = ['disabled', 'disableRipple', 'color'];
import {MatRipple, MatRippleLoader} from '@angular/material/core';

/** Shared host configuration for all buttons */
export const MAT_BUTTON_HOST = {
Expand All @@ -43,6 +33,7 @@ export const MAT_BUTTON_HOST = {
// Add a class that applies to all buttons. This makes it easier to target if somebody
// wants to target all Material buttons.
'[class.mat-mdc-button-base]': 'true',
'[class]': 'color ? "mat-" + color : ""',
};

/** List of classes to add to buttons instances based on host attribute selector. */
Expand Down Expand Up @@ -77,24 +68,9 @@ const HOST_SELECTOR_MDC_CLASS_PAIR: {selector: string; mdcClasses: string[]}[] =
},
];

// Boilerplate for applying mixins to MatButton.
/** @docs-private */
export const _MatButtonMixin = mixinColor(
mixinDisabled(
mixinDisableRipple(
class {
constructor(public _elementRef: ElementRef) {}
},
),
),
);

/** Base class for all buttons. */
@Directive()
export class MatButtonBase
extends _MatButtonMixin
implements CanDisable, CanColor, CanDisableRipple, AfterViewInit, OnDestroy
{
export class MatButtonBase implements AfterViewInit, OnDestroy {
private readonly _focusMonitor = inject(FocusMonitor);

/**
Expand All @@ -118,41 +94,41 @@ export class MatButtonBase
this._rippleLoader?.attachRipple(this._elementRef.nativeElement, v);
}

// We override `disableRipple` and `disabled` so we can hook into
// their setters and update the ripple disabled state accordingly.
/** Theme color palette of the button */
@Input() color?: string | null;

/** Whether the ripple effect is disabled or not. */
override get disableRipple(): boolean {
@Input({transform: booleanAttribute})
get disableRipple(): boolean {
return this._disableRipple;
}
override set disableRipple(value: any) {
this._disableRipple = coerceBooleanProperty(value);
set disableRipple(value: any) {
this._disableRipple = value;
this._updateRippleDisabled();
}
private _disableRipple: boolean = false;

override get disabled(): boolean {
@Input({transform: booleanAttribute})
get disabled(): boolean {
return this._disabled;
}
override set disabled(value: any) {
this._disabled = coerceBooleanProperty(value);
set disabled(value: any) {
this._disabled = value;
this._updateRippleDisabled();
}
private _disabled: boolean = false;

constructor(
elementRef: ElementRef,
public _elementRef: ElementRef,
public _platform: Platform,
public _ngZone: NgZone,
public _animationMode?: string,
) {
super(elementRef);

this._rippleLoader?.configureRipple(this._elementRef.nativeElement, {
className: 'mat-mdc-button-ripple',
});

const classList = (elementRef.nativeElement as HTMLElement).classList;
const classList = (_elementRef.nativeElement as HTMLElement).classList;

// For each of the variant selectors that is present in the button's host
// attributes, add the correct corresponding MDC classes.
Expand Down Expand Up @@ -195,9 +171,6 @@ export class MatButtonBase
}
}

/** Shared inputs by buttons using the `<a>` tag */
export const MAT_ANCHOR_INPUTS = ['disabled', 'disableRipple', 'color', 'tabIndex'];

/** Shared host configuration for buttons using the `<a>` tag. */
export const MAT_ANCHOR_HOST = {
'[attr.disabled]': 'disabled || null',
Expand All @@ -215,13 +188,19 @@ export const MAT_ANCHOR_HOST = {
// Add a class that applies to all buttons. This makes it easier to target if somebody
// wants to target all Material buttons.
'[class.mat-mdc-button-base]': 'true',
'[class]': 'color ? "mat-" + color : ""',
};

/**
* Anchor button base.
*/
@Directive()
export class MatAnchorBase extends MatButtonBase implements OnInit, OnDestroy {
@Input({
transform: (value: unknown) => {
return value == null ? undefined : numberAttribute(value);
},
})
tabIndex: number;

constructor(elementRef: ElementRef, platform: Platform, ngZone: NgZone, animationMode?: string) {
Expand Down
11 changes: 1 addition & 10 deletions src/material/button/button.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,7 @@ import {
} from '@angular/core';
import {ANIMATION_MODULE_TYPE} from '@angular/platform-browser/animations';

import {
MAT_ANCHOR_HOST,
MAT_ANCHOR_INPUTS,
MAT_BUTTON_HOST,
MAT_BUTTON_INPUTS,
MatAnchorBase,
MatButtonBase,
} from './button-base';
import {MAT_ANCHOR_HOST, MAT_BUTTON_HOST, MatAnchorBase, MatButtonBase} from './button-base';

/**
* Material Design button component. Users interact with a button to perform an action.
Expand All @@ -43,7 +36,6 @@ import {
`,
templateUrl: 'button.html',
styleUrls: ['button.css', 'button-high-contrast.css'],
inputs: MAT_BUTTON_INPUTS,
host: MAT_BUTTON_HOST,
exportAs: 'matButton',
encapsulation: ViewEncapsulation.None,
Expand Down Expand Up @@ -74,7 +66,6 @@ export class MatButton extends MatButtonBase {
selector: `a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button]`,
exportAs: 'matButton, matAnchor',
host: MAT_ANCHOR_HOST,
inputs: MAT_ANCHOR_INPUTS,
templateUrl: 'button.html',
styleUrls: ['button.css', 'button-high-contrast.css'],
encapsulation: ViewEncapsulation.None,
Expand Down
41 changes: 9 additions & 32 deletions src/material/button/fab.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,17 @@ import {
ElementRef,
Inject,
InjectionToken,
Input,
NgZone,
Optional,
ViewEncapsulation,
booleanAttribute,
} from '@angular/core';
import {ANIMATION_MODULE_TYPE} from '@angular/platform-browser/animations';

import {MatAnchor} from './button';
import {
MAT_ANCHOR_HOST,
MAT_ANCHOR_INPUTS,
MAT_BUTTON_HOST,
MAT_BUTTON_INPUTS,
MatButtonBase,
} from './button-base';
import {MAT_ANCHOR_HOST, MAT_BUTTON_HOST, MatButtonBase} from './button-base';
import {ThemePalette} from '@angular/material/core';
import {BooleanInput, coerceBooleanProperty} from '@angular/cdk/coercion';

/** Default FAB options that can be overridden. */
export interface MatFabDefaultOptions {
Expand All @@ -55,8 +50,6 @@ export function MAT_FAB_DEFAULT_OPTIONS_FACTORY(): MatFabDefaultOptions {
// Default FAB configuration.
const defaults = MAT_FAB_DEFAULT_OPTIONS_FACTORY();

let buttonInputs = [...MAT_ANCHOR_INPUTS, 'extended'];

/**
* Material Design floating action button (FAB) component. These buttons represent the primary
* or most common action for users to interact with.
Expand All @@ -68,7 +61,6 @@ let buttonInputs = [...MAT_ANCHOR_INPUTS, 'extended'];
selector: `button[mat-fab]`,
templateUrl: 'button.html',
styleUrls: ['fab.css'],
inputs: buttonInputs,
host: {
...MAT_BUTTON_HOST,
'[class.mdc-fab--extended]': 'extended',
Expand All @@ -81,13 +73,7 @@ let buttonInputs = [...MAT_ANCHOR_INPUTS, 'extended'];
export class MatFabButton extends MatButtonBase {
override _isFab = true;

get extended(): boolean {
return this._extended;
}
set extended(value: BooleanInput) {
this._extended = coerceBooleanProperty(value);
}
private _extended: boolean;
@Input({transform: booleanAttribute}) extended: boolean;

constructor(
elementRef: ElementRef,
Expand All @@ -98,7 +84,7 @@ export class MatFabButton extends MatButtonBase {
) {
super(elementRef, platform, ngZone, animationMode);
this._options = this._options || defaults;
this.color = this.defaultColor = this._options!.color || defaults.color;
this.color = this._options!.color || defaults.color;
}
}

Expand All @@ -111,7 +97,6 @@ export class MatFabButton extends MatButtonBase {
selector: `button[mat-mini-fab]`,
templateUrl: 'button.html',
styleUrls: ['fab.css'],
inputs: MAT_BUTTON_INPUTS,
host: MAT_BUTTON_HOST,
exportAs: 'matButton',
encapsulation: ViewEncapsulation.None,
Expand All @@ -129,7 +114,7 @@ export class MatMiniFabButton extends MatButtonBase {
) {
super(elementRef, platform, ngZone, animationMode);
this._options = this._options || defaults;
this.color = this.defaultColor = this._options!.color || defaults.color;
this.color = this._options!.color || defaults.color;
}
}

Expand All @@ -144,7 +129,6 @@ export class MatMiniFabButton extends MatButtonBase {
selector: `a[mat-fab]`,
templateUrl: 'button.html',
styleUrls: ['fab.css'],
inputs: buttonInputs,
host: {
...MAT_ANCHOR_HOST,
'[class.mdc-fab--extended]': 'extended',
Expand All @@ -157,13 +141,7 @@ export class MatMiniFabButton extends MatButtonBase {
export class MatFabAnchor extends MatAnchor {
override _isFab = true;

get extended(): boolean {
return this._extended;
}
set extended(value: BooleanInput) {
this._extended = coerceBooleanProperty(value);
}
private _extended: boolean;
@Input({transform: booleanAttribute}) extended: boolean;

constructor(
elementRef: ElementRef,
Expand All @@ -174,7 +152,7 @@ export class MatFabAnchor extends MatAnchor {
) {
super(elementRef, platform, ngZone, animationMode);
this._options = this._options || defaults;
this.color = this.defaultColor = this._options!.color || defaults.color;
this.color = this._options!.color || defaults.color;
}
}

Expand All @@ -187,7 +165,6 @@ export class MatFabAnchor extends MatAnchor {
selector: `a[mat-mini-fab]`,
templateUrl: 'button.html',
styleUrls: ['fab.css'],
inputs: MAT_ANCHOR_INPUTS,
host: MAT_ANCHOR_HOST,
exportAs: 'matButton, matAnchor',
encapsulation: ViewEncapsulation.None,
Expand All @@ -205,6 +182,6 @@ export class MatMiniFabAnchor extends MatAnchor {
) {
super(elementRef, platform, ngZone, animationMode);
this._options = this._options || defaults;
this.color = this.defaultColor = this._options!.color || defaults.color;
this.color = this._options!.color || defaults.color;
}
}
12 changes: 1 addition & 11 deletions src/material/button/icon-button.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,7 @@ import {
ViewEncapsulation,
} from '@angular/core';
import {ANIMATION_MODULE_TYPE} from '@angular/platform-browser/animations';

import {
MAT_ANCHOR_HOST,
MAT_ANCHOR_INPUTS,
MAT_BUTTON_HOST,
MAT_BUTTON_INPUTS,
MatAnchorBase,
MatButtonBase,
} from './button-base';
import {MAT_ANCHOR_HOST, MAT_BUTTON_HOST, MatAnchorBase, MatButtonBase} from './button-base';

/**
* Material Design icon button component. This type of button displays a single interactive icon for
Expand All @@ -36,7 +28,6 @@ import {
selector: `button[mat-icon-button]`,
templateUrl: 'icon-button.html',
styleUrls: ['icon-button.css', 'button-high-contrast.css'],
inputs: MAT_BUTTON_INPUTS,
host: MAT_BUTTON_HOST,
exportAs: 'matButton',
encapsulation: ViewEncapsulation.None,
Expand Down Expand Up @@ -64,7 +55,6 @@ export class MatIconButton extends MatButtonBase {
selector: `a[mat-icon-button]`,
templateUrl: 'button.html',
styleUrls: ['icon-button.css', 'button-high-contrast.css'],
inputs: MAT_ANCHOR_INPUTS,
host: MAT_ANCHOR_HOST,
exportAs: 'matButton, matAnchor',
encapsulation: ViewEncapsulation.None,
Expand Down
2 changes: 1 addition & 1 deletion src/material/button/testing/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ ts_library(
exclude = ["**/*.spec.ts"],
),
deps = [
"//src/cdk/coercion",
"//src/cdk/testing",
"@npm//@angular/core",
],
)

Expand Down
4 changes: 2 additions & 2 deletions src/material/button/testing/button-harness.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@
* found in the LICENSE file at https://angular.io/license
*/

import {booleanAttribute} from '@angular/core';
import {
ComponentHarnessConstructor,
ContentContainerComponentHarness,
HarnessPredicate,
} from '@angular/cdk/testing';
import {coerceBooleanProperty} from '@angular/cdk/coercion';
import {ButtonHarnessFilters, ButtonVariant} from './button-harness-filters';

/** Harness for interacting with a MDC-based mat-button in tests. */
Expand Down Expand Up @@ -61,7 +61,7 @@ export class MatButtonHarness extends ContentContainerComponentHarness {
/** Gets a boolean promise indicating if the button is disabled. */
async isDisabled(): Promise<boolean> {
const disabled = (await this.host()).getAttribute('disabled');
return coerceBooleanProperty(await disabled);
return booleanAttribute(await disabled);
}

/** Gets a promise for the button's label text. */
Expand Down
Loading