diff --git a/src/lib/list/index.ts b/src/lib/list/index.ts index 78100824d63f..0afe8ac61e30 100644 --- a/src/lib/list/index.ts +++ b/src/lib/list/index.ts @@ -1,5 +1,5 @@ import {NgModule, ModuleWithProviders} from '@angular/core'; -import {MdLineModule, CompatibilityModule} from '../core'; +import {MdLineModule, MdRippleModule, CompatibilityModule} from '../core'; import { MdList, MdListItem, @@ -10,11 +10,12 @@ import { MdNavListCssMatStyler, MdDividerCssMatStyler, MdListSubheaderCssMatStyler, + MdNavListTokenSetter, } from './list'; @NgModule({ - imports: [MdLineModule, CompatibilityModule], + imports: [MdLineModule, MdRippleModule, CompatibilityModule], exports: [ MdList, MdListItem, @@ -26,7 +27,8 @@ import { MdListCssMatStyler, MdNavListCssMatStyler, MdDividerCssMatStyler, - MdListSubheaderCssMatStyler + MdListSubheaderCssMatStyler, + MdNavListTokenSetter, ], declarations: [ MdList, @@ -37,7 +39,8 @@ import { MdListCssMatStyler, MdNavListCssMatStyler, MdDividerCssMatStyler, - MdListSubheaderCssMatStyler + MdListSubheaderCssMatStyler, + MdNavListTokenSetter, ], }) export class MdListModule { diff --git a/src/lib/list/list-item.html b/src/lib/list/list-item.html index 323ec3e1e004..c603ad62a727 100644 --- a/src/lib/list/list-item.html +++ b/src/lib/list/list-item.html @@ -1,4 +1,5 @@ -
+
diff --git a/src/lib/list/list.scss b/src/lib/list/list.scss index 0b7f34621ead..3751684ec2a0 100644 --- a/src/lib/list/list.scss +++ b/src/lib/list/list.scss @@ -44,6 +44,7 @@ $mat-dense-three-line-height: 76px; font-size: $font-size; height: $base-height; padding: 0 $mat-list-side-padding; + position: relative; } &.mat-list-item-avatar .mat-list-item-content { diff --git a/src/lib/list/list.spec.ts b/src/lib/list/list.spec.ts index db1937370472..b7a46ec95cdb 100644 --- a/src/lib/list/list.spec.ts +++ b/src/lib/list/list.spec.ts @@ -1,5 +1,5 @@ import {async, TestBed} from '@angular/core/testing'; -import {Component} from '@angular/core'; +import {Component, QueryList, ViewChildren} from '@angular/core'; import {By} from '@angular/platform-browser'; import {MdListItem, MdListModule} from './index'; @@ -19,6 +19,7 @@ describe('MdList', () => { ListWithDynamicNumberOfLines, ListWithMultipleItems, ListWithManyLines, + NavListWithOneAnchorItem, ], }); @@ -114,6 +115,29 @@ describe('MdList', () => { expect(list.nativeElement.getAttribute('role')).toBe('list'); expect(listItem.nativeElement.getAttribute('role')).toBe('listitem'); }); + + it('should not show ripples for non-nav lists', () => { + let fixture = TestBed.createComponent(ListWithOneAnchorItem); + fixture.detectChanges(); + + const items: QueryList = fixture.debugElement.componentInstance.listItems; + expect(items.length).toBeGreaterThan(0); + items.forEach(item => expect(item.isRippleEnabled()).toBe(false)); + }); + + it('should maybe show ripples for nav lists', () => { + let fixture = TestBed.createComponent(NavListWithOneAnchorItem); + fixture.detectChanges(); + + const items: QueryList = fixture.debugElement.componentInstance.listItems; + expect(items.length).toBeGreaterThan(0); + // Ripples should be enabled by default, and can be disabled with a binding. + items.forEach(item => expect(item.isRippleEnabled()).toBe(true)); + + fixture.debugElement.componentInstance.disableRipple = true; + fixture.detectChanges(); + items.forEach(item => expect(item.isRippleEnabled()).toBe(false)); + }); }); @@ -132,7 +156,22 @@ class BaseTestList { Paprika `}) -class ListWithOneAnchorItem extends BaseTestList { } +class ListWithOneAnchorItem extends BaseTestList { + // This needs to be declared directly on the class; if declared on the BaseTestList superclass, + // it doesn't get populated. + @ViewChildren(MdListItem) listItems: QueryList; +} + +@Component({template: ` + + + Paprika + + `}) +class NavListWithOneAnchorItem extends BaseTestList { + @ViewChildren(MdListItem) listItems: QueryList; + disableRipple: boolean = false; +} @Component({template: ` diff --git a/src/lib/list/list.ts b/src/lib/list/list.ts index f5a79b7f0778..5a7df8fe1d76 100644 --- a/src/lib/list/list.ts +++ b/src/lib/list/list.ts @@ -6,6 +6,10 @@ import { QueryList, Directive, ElementRef, + Inject, + Input, + OpaqueToken, + Optional, Renderer, AfterContentInit, } from '@angular/core'; @@ -16,6 +20,15 @@ import {MdLine, MdLineSetter} from '../core'; }) export class MdListDivider {} +/** + * Token used to inject the list type into child MdListItem components so they can know whether + * they're in a nav list (and thus should use an MdRipple). + */ +export const LIST_TYPE_TOKEN = new OpaqueToken('list_type'); + +const NORMAL_LIST_TYPE = 'normal_list_type'; +const NAV_LIST_TYPE = 'nav_list_type'; + @Component({ moduleId: module.id, selector: 'md-list, mat-list, md-nav-list, mat-nav-list', @@ -23,6 +36,7 @@ export class MdListDivider {} 'role': 'list'}, template: '', styleUrls: ['list.css'], + providers: [{ provide: LIST_TYPE_TOKEN, useValue: NORMAL_LIST_TYPE }], encapsulation: ViewEncapsulation.None }) export class MdList {} @@ -51,6 +65,15 @@ export class MdListCssMatStyler {} }) export class MdNavListCssMatStyler {} +/** + * Directive to set the ListType token to NAV_LIST_TYPE. + */ +@Directive({ + selector: 'md-nav-list, mat-nav-list', + providers: [{ provide: LIST_TYPE_TOKEN, useValue: NAV_LIST_TYPE }], +}) +export class MdNavListTokenSetter {} + /** * Directive whose purpose is to add the mat- CSS styling to this selector. * @docs-private @@ -112,6 +135,11 @@ export class MdListSubheaderCssMatStyler {} encapsulation: ViewEncapsulation.None }) export class MdListItem implements AfterContentInit { + /** + * Whether the ripple effect on click should be disabled. This applies only to list items that + * are children of an md-nav-list; md-list items never have ripples. + */ + @Input() disableRipple: boolean = false; _hasFocus: boolean = false; private _lineSetter: MdLineSetter; @@ -124,12 +152,18 @@ export class MdListItem implements AfterContentInit { this._element.nativeElement, 'mat-list-item-avatar', avatar != null); } - constructor(private _renderer: Renderer, private _element: ElementRef) {} + constructor(private _renderer: Renderer, private _element: ElementRef, + @Optional() @Inject(LIST_TYPE_TOKEN) private _listType: string) {} ngAfterContentInit() { this._lineSetter = new MdLineSetter(this._lines, this._renderer, this._element); } + /** Whether this list item should show a ripple effect when clicked. */ + isRippleEnabled() { + return !this.disableRipple && (this._listType === NAV_LIST_TYPE); + } + _handleFocus() { this._hasFocus = true; }