Skip to content

Commit c865136

Browse files
authored
feat(mdc-list): add list-option template (#19327)
* feat(material-expeirmental/mdc-list): add support for focus/hover states and ripples * add state styles * add demo and stub implementation for mdc-based selection-list * reduce the number of ng-template's needed in list-item and list-option * address comments
1 parent f41d0f7 commit c865136

File tree

9 files changed

+159
-17
lines changed

9 files changed

+159
-17
lines changed

src/dev-app/mdc-list/mdc-list-demo.html

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,68 @@ <h2>Action list</h2>
116116
<div>
117117
<h2>Selection list</h2>
118118

119-
TODO: Implement MDC-based selection list.
119+
<mat-selection-list #groceries [ngModel]="selectedOptions"
120+
(ngModelChange)="onSelectedOptionsChange($event)"
121+
(change)="changeEventCount = changeEventCount + 1"
122+
[disabled]="selectionListDisabled"
123+
[disableRipple]="selectionListRippleDisabled"
124+
color="primary">
125+
<div mat-subheader>Groceries</div>
126+
127+
<mat-list-option value="bananas" checkboxPosition="before">Bananas</mat-list-option>
128+
<mat-list-option selected value="oranges">Oranges</mat-list-option>
129+
<mat-list-option value="apples" color="accent">Apples</mat-list-option>
130+
<mat-list-option value="strawberries" color="warn">Strawberries</mat-list-option>
131+
</mat-selection-list>
132+
133+
<mat-selection-list [disableRipple]="selectionListRippleDisabled">
134+
<div mat-subheader>Dogs</div>
135+
136+
<mat-list-option checkboxPosition="before">
137+
<img matListAvatar src="https://material.angular.io/assets/img/examples/shiba1.jpg">
138+
<span matLine>Shiba Inu</span>
139+
</mat-list-option>
140+
141+
<mat-list-option checkboxPosition="after">
142+
<img matListAvatar src="https://material.angular.io/assets/img/examples/shiba2.jpg">
143+
<span matLine>Other Shiba Inu</span>
144+
</mat-list-option>
145+
</mat-selection-list>
146+
147+
<p>Selected: {{selectedOptions | json}}</p>
148+
<p>Change Event Count {{changeEventCount}}</p>
149+
<p>Model Change Event Count {{modelChangeEventCount}}</p>
150+
<p>
151+
<mat-checkbox [(ngModel)]="selectionListDisabled">
152+
Disable Selection List
153+
</mat-checkbox>
154+
</p>
155+
<p>
156+
<mat-checkbox [(ngModel)]="selectionListRippleDisabled">
157+
Disable Selection List ripples
158+
</mat-checkbox>
159+
</p>
160+
<p>
161+
<button mat-raised-button (click)="groceries.selectAll()">Select all</button>
162+
<button mat-raised-button (click)="groceries.deselectAll()">Deselect all</button>
163+
</p>
164+
</div>
165+
166+
<div>
167+
<h2>Single Selection list</h2>
168+
169+
<mat-selection-list #favorite
170+
[(ngModel)]="favoriteOptions"
171+
[multiple]="false"
172+
color="primary">
173+
<div mat-subheader>Favorite Grocery</div>
174+
175+
<mat-list-option value="bananas">Bananas</mat-list-option>
176+
<mat-list-option selected value="oranges">Oranges</mat-list-option>
177+
<mat-list-option value="apples">Apples</mat-list-option>
178+
<mat-list-option value="strawberries" color="warn">Strawberries</mat-list-option>
179+
</mat-selection-list>
180+
181+
<p>Selected: {{favoriteOptions | json}}</p>
120182
</div>
121183
</div>

src/material-experimental/mdc-list/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ ng_module(
2222
assets = [":list_scss"] + glob(["**/*.html"]),
2323
module_name = "@angular/material-experimental/mdc-list",
2424
deps = [
25+
"//src/cdk/collections",
2526
"//src/material/divider",
2627
"@npm//@angular/core",
2728
"@npm//@angular/forms",

src/material-experimental/mdc-list/list-base.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ export abstract class MatListItemBase implements AfterContentInit, OnDestroy, Ri
4646

4747
rippleConfig: RippleConfig = {};
4848

49+
// TODO(mmalerba): Add @Input for disabling ripple.
4950
rippleDisabled: boolean;
5051

5152
private _subscriptions = new Subscription();
Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,25 @@
1-
TODO: Implement.
1+
<!-- Save icons and unclassified content to be placed later. -->
2+
<ng-template #icons>
3+
<ng-content select="[mat-list-avatar],[matListAvatar],[mat-list-icon],[matListIcon]"></ng-content>
4+
</ng-template>
5+
<ng-template #unsortedContent>
6+
<ng-content></ng-content>
7+
</ng-template>
8+
9+
<!-- Prefix -->
10+
<span class="mdc-list-item__graphic" *ngIf="checkboxPosition !== 'after' else icons">
11+
<mat-pseudo-checkbox></mat-pseudo-checkbox>
12+
</span>
13+
<!-- Text -->
14+
<span class="mdc-list-item__text">
15+
<ng-content *ngIf="lines.length else unsortedContent" select="[mat-line],[matLine]"></ng-content>
16+
</span>
17+
<!-- Suffix -->
18+
<span class="mdc-list-item__meta">
19+
<span class="mdc-list-item__graphic" *ngIf="checkboxPosition === 'after' else icons">
20+
<mat-pseudo-checkbox></mat-pseudo-checkbox>
21+
</span>
22+
<ng-container *ngIf="lines.length" [ngTemplateOutlet]="unsortedContent"></ng-container>
23+
</span>
24+
<!-- Divider -->
25+
<ng-content select="mat-divider"></ng-content>

src/material-experimental/mdc-list/list.scss

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,3 +108,11 @@
108108
right: 0;
109109
opacity: 0;
110110
}
111+
112+
// Normally the `.mdc-list-item__meta` class would be applied to the icon or checkbox directly.
113+
// However, we need to group multiple potential `ng-content` blocks inside the meta section, so we
114+
// add them as children instead. These styles ensure that they are properly aligned.
115+
.mat-mdc-list-option .mdc-list-item__meta .mdc-list-item__graphic {
116+
margin-right: 0;
117+
vertical-align: middle;
118+
}

src/material-experimental/mdc-list/module.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
import {CommonModule} from '@angular/common';
1010
import {NgModule} from '@angular/core';
11-
import {MatLineModule, MatRippleModule} from '@angular/material/core';
11+
import {MatLineModule, MatPseudoCheckboxModule, MatRippleModule} from '@angular/material/core';
1212
import {MatDividerModule} from '@angular/material/divider';
1313
import {MatActionList} from './action-list';
1414
import {
@@ -26,6 +26,7 @@ import {MatListOption, MatSelectionList} from './selection-list';
2626
CommonModule,
2727
MatLineModule,
2828
MatRippleModule,
29+
MatPseudoCheckboxModule,
2930
],
3031
exports: [
3132
MatList,

src/material-experimental/mdc-list/selection-list.html

Lines changed: 0 additions & 1 deletion
This file was deleted.

src/material-experimental/mdc-list/selection-list.ts

Lines changed: 58 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,24 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9+
import {BooleanInput} from '@angular/cdk/coercion';
10+
import {SelectionModel} from '@angular/cdk/collections';
911
import {Platform} from '@angular/cdk/platform';
1012
import {
1113
ChangeDetectionStrategy,
1214
Component,
1315
ContentChildren,
1416
ElementRef,
17+
EventEmitter,
1518
forwardRef,
19+
Input,
1620
NgZone,
21+
Output,
1722
QueryList,
1823
ViewEncapsulation
1924
} from '@angular/core';
20-
import {NG_VALUE_ACCESSOR} from '@angular/forms';
21-
import {MatLine} from '@angular/material/core';
25+
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms';
26+
import {MatLine, ThemePalette} from '@angular/material/core';
2227
import {MatListBase, MatListItemBase} from './list-base';
2328

2429
const MAT_SELECTION_LIST_VALUE_ACCESSOR: any = {
@@ -40,9 +45,10 @@ export class MatSelectionListChange {
4045
selector: 'mat-selection-list',
4146
exportAs: 'matSelectionList',
4247
host: {
43-
'class': 'mat-mdc-selection-list mat-mdc-list-base'
48+
'class': 'mat-mdc-selection-list mat-mdc-list-base mdc-list',
49+
'role': 'listbox',
4450
},
45-
templateUrl: 'selection-list.html',
51+
template: '<ng-content></ng-content>',
4652
styleUrls: ['list.css'],
4753
encapsulation: ViewEncapsulation.None,
4854
providers: [
@@ -51,23 +57,68 @@ export class MatSelectionListChange {
5157
],
5258
changeDetection: ChangeDetectionStrategy.OnPush,
5359
})
54-
export class MatSelectionList extends MatListBase {}
60+
export class MatSelectionList extends MatListBase implements ControlValueAccessor {
61+
// TODO: Implement these inputs.
62+
@Input() disableRipple: boolean;
63+
@Input() tabIndex: number;
64+
@Input() color: ThemePalette;
65+
@Input() compareWith: (o1: any, o2: any) => boolean;
66+
@Input() disabled: boolean;
67+
@Input() multiple: boolean;
68+
69+
// TODO: Implement these inputs.
70+
@Output() readonly selectionChange = new EventEmitter<MatSelectionListChange>();
71+
72+
@ContentChildren(forwardRef(() => MatListOption), {descendants: true}) options:
73+
QueryList<MatListOption>;
74+
75+
// TODO: Implement these properties.
76+
selectedOptions: SelectionModel<MatListOption>;
77+
78+
// TODO: Implement these methods.
79+
focus(options?: FocusOptions) {}
80+
selectAll() {}
81+
deselectAll() {}
82+
registerOnChange(fn: any) {}
83+
registerOnTouched(fn: any) {}
84+
writeValue(obj: any) {}
85+
}
5586

5687
@Component({
5788
selector: 'mat-list-option',
5889
exportAs: 'matListOption',
5990
host: {
60-
'class': 'mat-mdc-list-item mat-mdc-list-option',
91+
'class': 'mat-mdc-list-item mat-mdc-list-option mdc-list-item',
92+
'role': 'option',
93+
'tabindex': '-1',
6194
},
6295
templateUrl: 'list-option.html',
6396
encapsulation: ViewEncapsulation.None,
6497
changeDetection: ChangeDetectionStrategy.OnPush,
6598
})
6699
export class MatListOption extends MatListItemBase {
100+
static ngAcceptInputType_disabled: BooleanInput;
101+
static ngAcceptInputType_selected: BooleanInput;
102+
static ngAcceptInputType_disableRipple: BooleanInput;
103+
67104
@ContentChildren(MatLine, {read: ElementRef, descendants: true}) lines:
68105
QueryList<ElementRef<Element>>;
69106

70-
constructor(element: ElementRef, ngZone: NgZone, listBase: MatListBase, platform: Platform) {
107+
// TODO: Implement these inputs.
108+
@Input() disableRipple: boolean;
109+
@Input() checkboxPosition: 'before' | 'after' = 'before';
110+
@Input() color: ThemePalette;
111+
@Input() value: any;
112+
@Input() disabled: boolean;
113+
@Input() selected: boolean;
114+
115+
constructor(element: ElementRef, ngZone: NgZone, listBase: MatListBase, platform: Platform,
116+
public selectionList: MatSelectionList) {
71117
super(element, ngZone, listBase, platform);
72118
}
119+
120+
// TODO: Implement these methods.
121+
getLabel() { return ''; }
122+
focus() {}
123+
toggle() {}
73124
}

src/material/list/selection-list.ts

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import {
3232
forwardRef,
3333
Inject,
3434
Input,
35+
isDevMode,
3536
OnChanges,
3637
OnDestroy,
3738
OnInit,
@@ -40,7 +41,6 @@ import {
4041
SimpleChanges,
4142
ViewChild,
4243
ViewEncapsulation,
43-
isDevMode,
4444
} from '@angular/core';
4545
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms';
4646
import {
@@ -51,19 +51,14 @@ import {
5151
setLines,
5252
ThemePalette,
5353
} from '@angular/material/core';
54-
5554
import {Subject} from 'rxjs';
5655
import {startWith, takeUntil} from 'rxjs/operators';
57-
5856
import {MatListAvatarCssMatStyler, MatListIconCssMatStyler} from './list';
5957

60-
61-
/** @docs-private */
6258
class MatSelectionListBase {}
6359
const _MatSelectionListMixinBase: CanDisableRippleCtor & typeof MatSelectionListBase =
6460
mixinDisableRipple(MatSelectionListBase);
6561

66-
/** @docs-private */
6762
class MatListOptionBase {}
6863
const _MatListOptionMixinBase: CanDisableRippleCtor & typeof MatListOptionBase =
6964
mixinDisableRipple(MatListOptionBase);

0 commit comments

Comments
 (0)