diff --git a/src/material/list/list.scss b/src/material/list/list.scss index b38b5f7d1e9e..fce0ce3f8405 100644 --- a/src/material/list/list.scss +++ b/src/material/list/list.scss @@ -278,8 +278,8 @@ mat-action-list { font: inherit; outline: inherit; -webkit-tap-highlight-color: transparent; - text-align: left; + [dir='rtl'] & { text-align: right; } @@ -300,6 +300,10 @@ mat-action-list { outline: none; } +.mat-list-item-disabled { + pointer-events: none; +} + @include cdk-high-contrast { .mat-selection-list:focus { outline-style: dotted; diff --git a/src/material/list/list.spec.ts b/src/material/list/list.spec.ts index 208bdec247d8..e8ae421da7ca 100644 --- a/src/material/list/list.spec.ts +++ b/src/material/list/list.spec.ts @@ -16,7 +16,7 @@ describe('MatList', () => { ListWithOneAnchorItem, ListWithOneItem, ListWithTwoLineItem, ListWithThreeLineItem, ListWithAvatar, ListWithItemWithCssClass, ListWithDynamicNumberOfLines, ListWithMultipleItems, ListWithManyLines, NavListWithOneAnchorItem, ActionListWithoutType, - ActionListWithType, ListWithIndirectDescendantLines + ActionListWithType, ListWithIndirectDescendantLines, ListWithDisabledItems, ], }); @@ -275,8 +275,40 @@ describe('MatList', () => { expect(listItems[0].nativeElement.className).toContain('mat-2-line'); expect(listItems[1].nativeElement.className).toContain('mat-2-line'); }); -}); + it('should be able to disable a single list item', () => { + const fixture = TestBed.createComponent(ListWithDisabledItems); + const listItems: HTMLElement[] = + Array.from(fixture.nativeElement.querySelectorAll('mat-list-item')); + fixture.detectChanges(); + + expect(listItems.map(item => { + return item.classList.contains('mat-list-item-disabled'); + })).toEqual([false, false, false]); + + fixture.componentInstance.firstItemDisabled = true; + fixture.detectChanges(); + + expect(listItems.map(item => { + return item.classList.contains('mat-list-item-disabled'); + })).toEqual([true, false, false]); + }); + + it('should be able to disable the entire list', () => { + const fixture = TestBed.createComponent(ListWithDisabledItems); + const listItems: HTMLElement[] = + Array.from(fixture.nativeElement.querySelectorAll('mat-list-item')); + fixture.detectChanges(); + + expect(listItems.every(item => item.classList.contains('mat-list-item-disabled'))).toBe(false); + + fixture.componentInstance.listDisabled = true; + fixture.detectChanges(); + + expect(listItems.every(item => item.classList.contains('mat-list-item-disabled'))).toBe(true); + }); + +}); class BaseTestList { items: any[] = [ @@ -425,3 +457,15 @@ class ListWithMultipleItems extends BaseTestList { } }) class ListWithIndirectDescendantLines extends BaseTestList { } + + +@Component({template: ` + + One + Two + Three + `}) +class ListWithDisabledItems { + firstItemDisabled = false; + listDisabled = false; +} diff --git a/src/material/list/list.ts b/src/material/list/list.ts index 2be160ab501c..f51d41b4d295 100644 --- a/src/material/list/list.ts +++ b/src/material/list/list.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {BooleanInput} from '@angular/cdk/coercion'; +import {coerceBooleanProperty, BooleanInput} from '@angular/cdk/coercion'; import { AfterContentInit, ChangeDetectionStrategy, @@ -21,13 +21,17 @@ import { OnChanges, OnDestroy, ChangeDetectorRef, + Input, } from '@angular/core'; import { + CanDisable, + CanDisableCtor, CanDisableRipple, CanDisableRippleCtor, MatLine, setLines, mixinDisableRipple, + mixinDisabled, } from '@angular/material/core'; import {Subject} from 'rxjs'; import {takeUntil} from 'rxjs/operators'; @@ -35,8 +39,8 @@ import {takeUntil} from 'rxjs/operators'; // Boilerplate for applying mixins to MatList. /** @docs-private */ class MatListBase {} -const _MatListMixinBase: CanDisableRippleCtor & typeof MatListBase = - mixinDisableRipple(MatListBase); +const _MatListMixinBase: CanDisableRippleCtor & CanDisableCtor & typeof MatListBase = + mixinDisabled(mixinDisableRipple(MatListBase)); // Boilerplate for applying mixins to MatListItem. /** @docs-private */ @@ -53,12 +57,12 @@ const _MatListItemMixinBase: CanDisableRippleCtor & typeof MatListItemBase = }, templateUrl: 'list.html', styleUrls: ['list.css'], - inputs: ['disableRipple'], + inputs: ['disableRipple', 'disabled'], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, }) -export class MatNavList extends _MatListMixinBase implements CanDisableRipple, OnChanges, - OnDestroy { +export class MatNavList extends _MatListMixinBase implements CanDisable, CanDisableRipple, + OnChanges, OnDestroy { /** Emits when the state of the list changes. */ _stateChanges = new Subject(); @@ -71,6 +75,7 @@ export class MatNavList extends _MatListMixinBase implements CanDisableRipple, O } static ngAcceptInputType_disableRipple: BooleanInput; + static ngAcceptInputType_disabled: BooleanInput; } @Component({ @@ -81,11 +86,12 @@ export class MatNavList extends _MatListMixinBase implements CanDisableRipple, O 'class': 'mat-list mat-list-base' }, styleUrls: ['list.css'], - inputs: ['disableRipple'], + inputs: ['disableRipple', 'disabled'], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, }) -export class MatList extends _MatListMixinBase implements CanDisableRipple, OnChanges, OnDestroy { +export class MatList extends _MatListMixinBase implements CanDisable, CanDisableRipple, OnChanges, + OnDestroy { /** Emits when the state of the list changes. */ _stateChanges = new Subject(); @@ -120,6 +126,7 @@ export class MatList extends _MatListMixinBase implements CanDisableRipple, OnCh } static ngAcceptInputType_disableRipple: BooleanInput; + static ngAcceptInputType_disabled: BooleanInput; } /** @@ -158,6 +165,7 @@ export class MatListSubheaderCssMatStyler {} exportAs: 'matListItem', host: { 'class': 'mat-list-item', + '[class.mat-list-item-disabled]': 'disabled', // @breaking-change 8.0.0 Remove `mat-list-item-avatar` in favor of `mat-list-item-with-avatar`. '[class.mat-list-item-avatar]': '_avatar || _icon', '[class.mat-list-item-with-avatar]': '_avatar || _icon', @@ -202,6 +210,14 @@ export class MatListItem extends _MatListItemMixinBase implements AfterContentIn } } + /** Whether the option is disabled. */ + @Input() + get disabled() { return this._disabled || !!(this._list && this._list.disabled); } + set disabled(value: boolean) { + this._disabled = coerceBooleanProperty(value); + } + private _disabled = false; + ngAfterContentInit() { setLines(this._lines, this._element); } @@ -223,4 +239,5 @@ export class MatListItem extends _MatListItemMixinBase implements AfterContentIn } static ngAcceptInputType_disableRipple: BooleanInput; + static ngAcceptInputType_disabled: BooleanInput; } diff --git a/tools/public_api_guard/material/list.d.ts b/tools/public_api_guard/material/list.d.ts index 2ad5258a63fe..519bf239fd23 100644 --- a/tools/public_api_guard/material/list.d.ts +++ b/tools/public_api_guard/material/list.d.ts @@ -1,13 +1,14 @@ export declare const MAT_SELECTION_LIST_VALUE_ACCESSOR: any; -export declare class MatList extends _MatListMixinBase implements CanDisableRipple, OnChanges, OnDestroy { +export declare class MatList extends _MatListMixinBase implements CanDisable, CanDisableRipple, OnChanges, OnDestroy { _stateChanges: Subject; constructor(_elementRef: ElementRef); _getListType(): 'list' | 'action-list' | null; ngOnChanges(): void; ngOnDestroy(): void; static ngAcceptInputType_disableRipple: BooleanInput; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + static ngAcceptInputType_disabled: BooleanInput; + static ɵcmp: i0.ɵɵComponentDefWithMeta; static ɵfac: i0.ɵɵFactoryDef; } @@ -25,13 +26,16 @@ export declare class MatListItem extends _MatListItemMixinBase implements AfterC _avatar: MatListAvatarCssMatStyler; _icon: MatListIconCssMatStyler; _lines: QueryList; + get disabled(): boolean; + set disabled(value: boolean); constructor(_element: ElementRef, _changeDetectorRef: ChangeDetectorRef, navList?: MatNavList, list?: MatList); _getHostElement(): HTMLElement; _isRippleDisabled(): boolean; ngAfterContentInit(): void; ngOnDestroy(): void; static ngAcceptInputType_disableRipple: BooleanInput; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + static ngAcceptInputType_disabled: BooleanInput; + static ɵcmp: i0.ɵɵComponentDefWithMeta; static ɵfac: i0.ɵɵFactoryDef; } @@ -82,12 +86,13 @@ export declare class MatListSubheaderCssMatStyler { static ɵfac: i0.ɵɵFactoryDef; } -export declare class MatNavList extends _MatListMixinBase implements CanDisableRipple, OnChanges, OnDestroy { +export declare class MatNavList extends _MatListMixinBase implements CanDisable, CanDisableRipple, OnChanges, OnDestroy { _stateChanges: Subject; ngOnChanges(): void; ngOnDestroy(): void; static ngAcceptInputType_disableRipple: BooleanInput; - static ɵcmp: i0.ɵɵComponentDefWithMeta; + static ngAcceptInputType_disabled: BooleanInput; + static ɵcmp: i0.ɵɵComponentDefWithMeta; static ɵfac: i0.ɵɵFactoryDef; }