Skip to content

feat(material-expeirmental/mdc-list): add support for focus/hover states and ripples #19168

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
May 7, 2020
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
21 changes: 21 additions & 0 deletions src/material-experimental/mdc-list/_list-theme.scss
Original file line number Diff line number Diff line change
@@ -1,10 +1,31 @@
@import '@material/list/mixins.import';
@import '@material/ripple/variables.import';
@import '../mdc-helpers/mdc-helpers';

@mixin mat-mdc-list-theme($theme) {
$is-dark-theme: map-get($theme, is-dark);
$state-opacities:
if($is-dark-theme, $mdc-ripple-light-ink-opacities, $mdc-ripple-dark-ink-opacities);

@include mat-using-mdc-theme($theme) {
@include mdc-list-without-ripple($query: $mat-theme-styles-query);
}

// MDC's state styles are tied in with their ripple. Since we don't use the MDC ripple, we need to
// add the hover and focus states manually.
.mat-mdc-list-item-interactive {
&::before {
background: if($is-dark-theme, white, black);
}

&:hover::before {
opacity: map-get($state-opacities, hover);
}

&:focus::before {
opacity: map-get($state-opacities, focus);
}
}
}

@mixin mat-mdc-list-typography($config) {
Expand Down
3 changes: 3 additions & 0 deletions src/material-experimental/mdc-list/action-list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,8 @@ import {MatListBase} from './list-base';
styleUrls: ['list.css'],
encapsulation: ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush,
providers: [
{provide: MatListBase, useExisting: MatActionList},
]
})
export class MatActionList extends MatListBase {}
44 changes: 39 additions & 5 deletions src/material-experimental/mdc-list/list-base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,53 @@
* found in the LICENSE file at https://angular.io/license
*/

import {AfterContentInit, Directive, ElementRef, NgZone, OnDestroy, QueryList} from '@angular/core';
import {setLines} from '@angular/material/core';
import {Platform} from '@angular/cdk/platform';
import {
AfterContentInit,
Directive,
ElementRef,
HostBinding,
NgZone,
OnDestroy,
QueryList
} from '@angular/core';
import {RippleConfig, RippleRenderer, RippleTarget, setLines} from '@angular/material/core';
import {Subscription} from 'rxjs';
import {startWith} from 'rxjs/operators';

export class MatListBase {}
@Directive()
export class MatListBase {
// @HostBinding is used in the class as it is expected to be extended. Since @Component decorator
// metadata is not inherited by child classes, instead the host binding data is defined in a way
// that can be inherited.
// tslint:disable-next-line:no-host-decorator-in-concrete
@HostBinding('class.mdc-list--non-interactive')
_isNonInteractive: boolean;
}

@Directive()
export abstract class MatListItemBase implements AfterContentInit, OnDestroy {
export abstract class MatListItemBase implements AfterContentInit, OnDestroy, RippleTarget {
lines: QueryList<ElementRef<Element>>;

rippleConfig: RippleConfig = {};

rippleDisabled: boolean;

private _subscriptions = new Subscription();

constructor(protected _element: ElementRef, protected _ngZone: NgZone) {}
private _rippleRenderer: RippleRenderer;

constructor(protected _element: ElementRef, protected _ngZone: NgZone, listBase: MatListBase,
platform: Platform) {
const el = this._element.nativeElement;
this.rippleDisabled = listBase._isNonInteractive;
if (!listBase._isNonInteractive) {
el.classList.add('mat-mdc-list-item-interactive');
}
this._rippleRenderer =
new RippleRenderer(this, this._ngZone, el, platform);
this._rippleRenderer.setupTriggerEvents(el);
}

ngAfterContentInit() {
this._monitorLines();
Expand All @@ -44,5 +77,6 @@ export abstract class MatListItemBase implements AfterContentInit, OnDestroy {

ngOnDestroy() {
this._subscriptions.unsubscribe();
this._rippleRenderer._removeTriggerEvents();
}
}
27 changes: 27 additions & 0 deletions src/material-experimental/mdc-list/list.scss
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,21 @@
}
}

.mat-mdc-list-avatar {
// MDC's styles don't specify this, but they probably should. It gives a nicer experience when the
// image is not 1:1 aspect ratio.
// See https://github.com/material-components/material-components-web/issues/5897
object-fit: cover;
}

// MDC expects that the list items are always `<li>`, since we actually use `<button>` in some
// cases, we need to make sure it expands to fill the available width.
.mat-mdc-list-item,
.mat-mdc-list-option {
width: 100%;
box-sizing: border-box;
}

// MDC doesn't have list dividers, so we use mat-divider and style appropriately.
.mat-mdc-list-item,
.mat-mdc-list-option {
Expand All @@ -81,3 +96,15 @@
}
}
}

// MDC's state styles are included with their ripple which we don't use. Instead we add the focus
// and hover styles ourselves using this pseudo-element
.mat-mdc-list-item-interactive::before {
content: '';
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
opacity: 0;
}
12 changes: 9 additions & 3 deletions src/material-experimental/mdc-list/list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/

import {Platform} from '@angular/cdk/platform';
import {
ChangeDetectionStrategy,
Component,
Expand Down Expand Up @@ -61,8 +62,13 @@ export class MatListSubheaderCssMatStyler {}
styleUrls: ['list.css'],
encapsulation: ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush,
providers: [
{provide: MatListBase, useExisting: MatList},
]
})
export class MatList extends MatListBase {}
export class MatList extends MatListBase {
_isNonInteractive = true;
}

@Component({
selector: 'mat-list-item, a[mat-list-item], button[mat-list-item]',
Expand All @@ -78,7 +84,7 @@ export class MatListItem extends MatListItemBase {
@ContentChildren(MatLine, {read: ElementRef, descendants: true}) lines:
QueryList<ElementRef<Element>>;

constructor(element: ElementRef, ngZone: NgZone) {
super(element, ngZone);
constructor(element: ElementRef, ngZone: NgZone, listBase: MatListBase, platform: Platform) {
super(element, ngZone, listBase, platform);
}
}
7 changes: 5 additions & 2 deletions src/material-experimental/mdc-list/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
*/

import {NgModule} from '@angular/core';
import {MatLineModule} from '@angular/material/core';
import {MatLineModule, MatRippleModule} from '@angular/material/core';
import {MatDividerModule} from '@angular/material/divider';
import {MatActionList} from './action-list';
import {
Expand All @@ -21,7 +21,10 @@ import {MatNavList} from './nav-list';
import {MatListOption, MatSelectionList} from './selection-list';

@NgModule({
imports: [MatLineModule],
imports: [
MatLineModule,
MatRippleModule,
],
exports: [
MatList,
MatActionList,
Expand Down
3 changes: 2 additions & 1 deletion src/material-experimental/mdc-list/nav-list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,12 @@ import {MatListBase} from './list-base';
encapsulation: ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush,
providers: [
{provide: MatListBase, useExisting: MatNavList},
/**
* @deprecated Provider for `MatList` will be removed, use `MatNavList` instead.
* @breaking-change 11.0.0
*/
{provide: MatList, useExisting: MatNavList}
{provide: MatList, useExisting: MatNavList},
]
})
export class MatNavList extends MatListBase {}
12 changes: 8 additions & 4 deletions src/material-experimental/mdc-list/selection-list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/

import {Platform} from '@angular/cdk/platform';
import {
ChangeDetectionStrategy,
Component,
Expand Down Expand Up @@ -44,8 +45,11 @@ export class MatSelectionListChange {
templateUrl: 'selection-list.html',
styleUrls: ['list.css'],
encapsulation: ViewEncapsulation.None,
providers: [MAT_SELECTION_LIST_VALUE_ACCESSOR],
changeDetection: ChangeDetectionStrategy.OnPush
providers: [
MAT_SELECTION_LIST_VALUE_ACCESSOR,
{provide: MatListBase, useExisting: MatSelectionList}
],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class MatSelectionList extends MatListBase {}

Expand All @@ -63,7 +67,7 @@ export class MatListOption extends MatListItemBase {
@ContentChildren(MatLine, {read: ElementRef, descendants: true}) lines:
QueryList<ElementRef<Element>>;

constructor(element: ElementRef, ngZone: NgZone) {
super(element, ngZone);
constructor(element: ElementRef, ngZone: NgZone, listBase: MatListBase, platform: Platform) {
super(element, ngZone, listBase, platform);
}
}