diff --git a/src/material-experimental/mdc-list/list-base.ts b/src/material-experimental/mdc-list/list-base.ts index c36b585db02c..baea357da20f 100644 --- a/src/material-experimental/mdc-list/list-base.ts +++ b/src/material-experimental/mdc-list/list-base.ts @@ -29,6 +29,7 @@ import { RippleTarget, setLines, } from '@angular/material-experimental/mdc-core'; +import {ANIMATION_MODULE_TYPE} from '@angular/platform-browser/animations'; import {numbers} from '@material/ripple'; import {Subscription} from 'rxjs'; import {startWith} from 'rxjs/operators'; @@ -54,12 +55,16 @@ export abstract class MatListItemBase implements AfterContentInit, OnDestroy, Ri /** Host element for the list item. */ _hostElement: HTMLElement; + /** Whether animations are disabled. */ + _noopAnimations: boolean; + @ContentChildren(MatListAvatarCssMatStyler, {descendants: false}) _avatars: QueryList; @ContentChildren(MatListIconCssMatStyler, {descendants: false}) _icons: QueryList; @Input() get disableRipple(): boolean { - return this.disabled || this._disableRipple || this._listBase.disableRipple; + return this.disabled || this._disableRipple || this._listBase.disableRipple || + this._noopAnimations; } set disableRipple(value: boolean) { this._disableRipple = coerceBooleanProperty(value); } private _disableRipple: boolean = false; @@ -90,12 +95,14 @@ export abstract class MatListItemBase implements AfterContentInit, OnDestroy, Ri constructor(public _elementRef: ElementRef, protected _ngZone: NgZone, private _listBase: MatListBase, private _platform: Platform, @Optional() @Inject(MAT_RIPPLE_GLOBAL_OPTIONS) - globalRippleOptions?: RippleGlobalOptions) { + globalRippleOptions?: RippleGlobalOptions, + @Optional() @Inject(ANIMATION_MODULE_TYPE) animationMode?: string) { // We have to clone the object, because we don't want to mutate a global value when we assign // the `animation` further down. The downside of doing this is that the ripple renderer won't // pick up dynamic changes to `disabled`, but it's not something we officially support. this.rippleConfig = {...(globalRippleOptions || {})}; this._hostElement = this._elementRef.nativeElement; + this._noopAnimations = animationMode === 'NoopAnimations'; if (!this.rippleConfig.animation) { this.rippleConfig.animation = { diff --git a/src/material-experimental/mdc-list/list-option.scss b/src/material-experimental/mdc-list/list-option.scss index c126fbbbc244..9959832daede 100644 --- a/src/material-experimental/mdc-list/list-option.scss +++ b/src/material-experimental/mdc-list/list-option.scss @@ -4,22 +4,29 @@ @use '../../cdk/a11y'; @use './list-option-trailing-avatar-compat'; -// The MDC-based list-option uses the MDC checkbox for the selection indicators. -// We need to ensure that the checkbox styles are included for the list-option. -@include mdc-checkbox.without-ripple($query: mdc-helpers.$mat-base-styles-query); - // For compatibility with the non-MDC list, we support avatars that are shown at the end // of the list option. We create a class similar to MDC's `--trailing-icon` one. @include list-option-trailing-avatar-compat.core-styles($query: mdc-helpers.$mat-base-styles-query); -// The internal checkbox is purely decorative, but because it's an `input`, the user can still -// focus it by tabbing or clicking. Furthermore, `mat-list-option` has the `option` role which -// doesn't allow a nested `input`. We use `display: none` both to remove it from the tab order -// and to prevent focus from reaching it through the screen reader's forms mode. Ideally we'd -// remove the `input` completely, but we can't because MDC uses a `:checked` selector to -// toggle the selected styles. -.mat-mdc-list-option .mdc-checkbox__native-control { - display: none; +.mat-mdc-list-option { + // The MDC-based list-option uses the MDC checkbox for the selection indicators. + // We need to ensure that the checkbox styles are not included for the list-option. + @include mdc-checkbox.without-ripple( + $query: mdc-helpers.$mat-base-styles-without-animation-query); + + &:not(._mat-animation-noopable) { + @include mdc-checkbox.without-ripple($query: animation); + } + + // The internal checkbox is purely decorative, but because it's an `input`, the user can still + // focus it by tabbing or clicking. Furthermore, `mat-list-option` has the `option` role which + // doesn't allow a nested `input`. We use `display: none` both to remove it from the tab order + // and to prevent focus from reaching it through the screen reader's forms mode. Ideally we'd + // remove the `input` completely, but we can't because MDC uses a `:checked` selector to + // toggle the selected styles. + .mdc-checkbox__native-control { + display: none; + } } @include a11y.high-contrast(active, off) { diff --git a/src/material-experimental/mdc-list/list-option.ts b/src/material-experimental/mdc-list/list-option.ts index 7c6fd85742e4..6c3c541b5a7d 100644 --- a/src/material-experimental/mdc-list/list-option.ts +++ b/src/material-experimental/mdc-list/list-option.ts @@ -32,6 +32,7 @@ import { RippleGlobalOptions, ThemePalette, } from '@angular/material-experimental/mdc-core'; +import {ANIMATION_MODULE_TYPE} from '@angular/platform-browser/animations'; import {MatListBase, MatListItemBase} from './list-base'; import {LIST_OPTION, ListOption, MatListOptionCheckboxPosition} from './list-option-types'; @@ -79,6 +80,7 @@ export interface SelectionList extends MatListBase { '[class.mdc-list-item--with-trailing-checkbox]': '_hasCheckboxAt("after")', '[class.mat-accent]': 'color !== "primary" && color !== "warn"', '[class.mat-warn]': 'color === "warn"', + '[class._mat-animation-noopable]': '_noopAnimations', '(blur)': '_handleBlur()', }, templateUrl: 'list-option.html', @@ -144,8 +146,9 @@ export class MatListOption extends MatListItemBase implements ListOption, OnInit platform: Platform, @Inject(SELECTION_LIST) public _selectionList: SelectionList, private _changeDetectorRef: ChangeDetectorRef, - @Optional() @Inject(MAT_RIPPLE_GLOBAL_OPTIONS) globalRippleOptions?: RippleGlobalOptions) { - super(element, ngZone, _selectionList, platform, globalRippleOptions); + @Optional() @Inject(MAT_RIPPLE_GLOBAL_OPTIONS) globalRippleOptions?: RippleGlobalOptions, + @Optional() @Inject(ANIMATION_MODULE_TYPE) animationMode?: string) { + super(element, ngZone, _selectionList, platform, globalRippleOptions, animationMode); // By default, we mark all options as unselected. The MDC list foundation will // automatically update the attribute based on selection. Note that we need to diff --git a/src/material-experimental/mdc-list/list.ts b/src/material-experimental/mdc-list/list.ts index 9a51b7c6355a..26161f0e847b 100644 --- a/src/material-experimental/mdc-list/list.ts +++ b/src/material-experimental/mdc-list/list.ts @@ -24,6 +24,7 @@ import { MAT_RIPPLE_GLOBAL_OPTIONS, RippleGlobalOptions, } from '@angular/material-experimental/mdc-core'; +import {ANIMATION_MODULE_TYPE} from '@angular/platform-browser/animations'; import {MatListBase, MatListItemBase} from './list-base'; @Component({ @@ -54,6 +55,7 @@ export class MatList extends MatListBase {} // the trailing meta class. Note that we also add this even if there is no projected `meta` // content. This is because there is no good way to check for remaining projected content. '[class.mdc-list-item--with-trailing-meta]': 'lines.length !== 0', + '[class._mat-animation-noopable]': '_noopAnimations', }, templateUrl: 'list-item.html', encapsulation: ViewEncapsulation.None, @@ -69,7 +71,8 @@ export class MatListItem extends MatListItemBase { ngZone: NgZone, listBase: MatListBase, platform: Platform, - @Optional() @Inject(MAT_RIPPLE_GLOBAL_OPTIONS) globalRippleOptions?: RippleGlobalOptions) { - super(element, ngZone, listBase, platform, globalRippleOptions); + @Optional() @Inject(MAT_RIPPLE_GLOBAL_OPTIONS) globalRippleOptions?: RippleGlobalOptions, + @Optional() @Inject(ANIMATION_MODULE_TYPE) animationMode?: string) { + super(element, ngZone, listBase, platform, globalRippleOptions, animationMode); } }