Skip to content
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

feat(popover-edit): Adds support for select-like interactions. #18194

Merged
merged 1 commit into from
Jan 22, 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
23 changes: 13 additions & 10 deletions src/cdk-experimental/popover-edit/lens-directives.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,24 +175,27 @@ export class CdkEditRevert<FormValue> {
}

/** Closes the lens on click. */
@Directive({
selector: 'button[cdkEditClose]',
host: {
'type': 'button', // Prevents accidental form submits.
}
})
@Directive({selector: '[cdkEditClose]'})
export class CdkEditClose<FormValue> {
/** Type of the button. Defaults to `button` to avoid accident form submits. */
@Input() type: string = 'button';

constructor(
protected readonly editRef: EditRef<FormValue>) {}
protected readonly elementRef: ElementRef<HTMLElement>,
protected readonly editRef: EditRef<FormValue>) {

const nativeElement = elementRef.nativeElement;

// Prevent accidental form submits.
if (nativeElement.nodeName === 'BUTTON' && !nativeElement.getAttribute('type')) {
nativeElement.setAttribute('type', 'button');
}
}

// In Ivy the `host` metadata will be merged, whereas in ViewEngine it is overridden. In order
// to avoid double event listeners, we need to use `HostListener`. Once Ivy is the default, we
// can move this back into `host`.
// tslint:disable:no-host-decorator-in-concrete
@HostListener('click')
@HostListener('keyup.enter')
@HostListener('keyup.space')
closeEdit(): void {
// Note that we use `click` here, rather than a keyboard event, because some screen readers
// will emit a fake click event instead of an enter keyboard event on buttons.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@ ng_module(
"//src/material/button",
"//src/material/icon",
"//src/material/input",
"//src/material/list",
"//src/material/snack-bar",
"//src/material/table",
"@npm//@angular/common",
"@npm//@angular/forms",
],
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {FormsModule} from '@angular/forms';
import {MatPopoverEditModule} from '@angular/material-experimental/popover-edit';
import {MatButtonModule} from '@angular/material/button';
import {MatIconModule} from '@angular/material/icon';
import {MatInputModule} from '@angular/material/input';
import {MatListModule} from '@angular/material/list';
import {MatSnackBarModule} from '@angular/material/snack-bar';
import {MatTableModule} from '@angular/material/table';
import {
Expand Down Expand Up @@ -33,9 +35,11 @@ const EXAMPLES = [

@NgModule({
imports: [
CommonModule,
MatButtonModule,
MatIconModule,
MatInputModule,
MatListModule,
MatPopoverEditModule,
MatSnackBarModule,
MatTableModule,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@

.example-table td,
.example-table th {
width: 25%;
width: 16%;
}
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,43 @@ <h2 mat-edit-title>Name</h2>
</td>
</ng-container>

<!-- Type Column -->
<ng-container matColumnDef="type">
<th mat-header-cell *matHeaderCellDef> Type </th>
<td mat-cell *matCellDef="let element"
[matPopoverEdit]="typeEdit">
{{element.type}}

<!-- This edit is defined in the cell and can implicitly access element -->
<ng-template #typeEdit>
<div>
<form #f="ngForm"
matEditLens
matEditClose
(ngSubmit)="onSubmitType(element, f)"
[(matEditLensPreservedFormValue)]="typeValues.for(element).value">
<div mat-edit-fill>
<mat-selection-list [multiple]="false"
name="type"
[ngModel]="[element.type]"
(selectionChange)="f.ngSubmit.emit()"
aria-label="Element type">
<mat-list-option *ngFor="let type of TYPES"
[value]="type">
{{type}}
</mat-list-option>
</mat-selection-list>
</div>
</form>
</div>
</ng-template>

<span *matRowHoverContent>
<button mat-icon-button matEditOpen><mat-icon>arrow_drop_down</mat-icon></button>
</span>
</td>
</ng-container>

<!-- Weight Column -->
<ng-container matColumnDef="weight">
<th mat-header-cell *matHeaderCellDef> Weight </th>
Expand All @@ -92,4 +129,43 @@ <h2 mat-edit-title>Name</h2>

<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>

<!-- Fantasy Counterparts Column -->
<ng-container matColumnDef="fantasyCounterpart">
<th mat-header-cell *matHeaderCellDef> Fantasy Counterparts </th>
<td mat-cell *matCellDef="let element"
[matPopoverEdit]="fantasyCounterpartEdit">
{{element.fantasyCounterparts.join(', ')}}

<!-- This edit is defined in the cell and can implicitly access element -->
<ng-template #fantasyCounterpartEdit>
<div>
<form #f="ngForm"
matEditLens
(ngSubmit)="onSubmitFantasyCounterparts(element, f)"
[(matEditLensPreservedFormValue)]="fantasyValues.for(element).value">
<div mat-edit-fill>
<mat-selection-list [ngModel]="element.fantasyCounterparts"
name="fantasyCounterparts"
aria-label="Fantasy Element Counterparts">
<mat-list-option *ngFor="let fantasyElement of FANTASY_ELEMENTS"
[value]="fantasyElement"
checkboxPosition="before">
{{fantasyElement}}
</mat-list-option>
</mat-selection-list>
</div>
<div mat-edit-actions>
<button mat-button type="submit">Confirm</button>
<button mat-button matEditRevert>Revert</button>
</div>
</form>
</div>
</ng-template>

<span *matRowHoverContent>
<button mat-icon-button matEditOpen><mat-icon>arrow_drop_down</mat-icon></button>
</span>
</td>
</ng-container>
</table>
Original file line number Diff line number Diff line change
Expand Up @@ -5,36 +5,66 @@ import {NgForm} from '@angular/forms';
import {MatSnackBar} from '@angular/material/snack-bar';
import {BehaviorSubject, Observable} from 'rxjs';

export type ElementType = 'Metal' | 'Semimetal' | 'Nonmetal';

export type FantasyElement = 'Earth' | 'Water' | 'Wind' | 'Fire' | 'Light' | 'Dark';

export interface PeriodicElement {
name: string;
type: ElementType;
position: number;
weight: number;
symbol: string;
fantasyCounterparts: FantasyElement[];
}

const ELEMENT_DATA: PeriodicElement[] = [
{position: 1, name: 'Hydrogen', weight: 1.0079, symbol: 'H'},
{position: 2, name: 'Helium', weight: 4.0026, symbol: 'He'},
{position: 3, name: 'Lithium', weight: 6.941, symbol: 'Li'},
{position: 4, name: 'Beryllium', weight: 9.0122, symbol: 'Be'},
{position: 5, name: 'Boron', weight: 10.811, symbol: 'B'},
{position: 6, name: 'Carbon', weight: 12.0107, symbol: 'C'},
{position: 7, name: 'Nitrogen', weight: 14.0067, symbol: 'N'},
{position: 8, name: 'Oxygen', weight: 15.9994, symbol: 'O'},
{position: 9, name: 'Fluorine', weight: 18.9984, symbol: 'F'},
{position: 10, name: 'Neon', weight: 20.1797, symbol: 'Ne'},
{position: 11, name: 'Sodium', weight: 22.9897, symbol: 'Na'},
{position: 12, name: 'Magnesium', weight: 24.305, symbol: 'Mg'},
{position: 13, name: 'Aluminum', weight: 26.9815, symbol: 'Al'},
{position: 14, name: 'Silicon', weight: 28.0855, symbol: 'Si'},
{position: 15, name: 'Phosphorus', weight: 30.9738, symbol: 'P'},
{position: 16, name: 'Sulfur', weight: 32.065, symbol: 'S'},
{position: 17, name: 'Chlorine', weight: 35.453, symbol: 'Cl'},
{position: 18, name: 'Argon', weight: 39.948, symbol: 'Ar'},
{position: 19, name: 'Potassium', weight: 39.0983, symbol: 'K'},
{position: 20, name: 'Calcium', weight: 40.078, symbol: 'Ca'},
{position: 1, name: 'Hydrogen', type: 'Nonmetal', weight: 1.0079, symbol: 'H',
fantasyCounterparts: ['Fire', 'Wind', 'Light']},
{position: 2, name: 'Helium', type: 'Nonmetal', weight: 4.0026, symbol: 'He',
fantasyCounterparts: ['Wind', 'Light']},
{position: 3, name: 'Lithium', type: 'Metal', weight: 6.941, symbol: 'Li',
fantasyCounterparts: []},
{position: 4, name: 'Beryllium', type: 'Metal', weight: 9.0122, symbol: 'Be',
fantasyCounterparts: []},
{position: 5, name: 'Boron', type: 'Semimetal', weight: 10.811, symbol: 'B',
fantasyCounterparts: []},
{position: 6, name: 'Carbon', type: 'Nonmetal', weight: 12.0107, symbol: 'C',
fantasyCounterparts: ['Earth', 'Dark']},
{position: 7, name: 'Nitrogen', type: 'Nonmetal', weight: 14.0067, symbol: 'N',
fantasyCounterparts: ['Wind']},
{position: 8, name: 'Oxygen', type: 'Nonmetal', weight: 15.9994, symbol: 'O',
fantasyCounterparts: ['Fire', 'Water', 'Wind']},
{position: 9, name: 'Fluorine', type: 'Nonmetal', weight: 18.9984, symbol: 'F',
fantasyCounterparts: []},
{position: 10, name: 'Neon', type: 'Nonmetal', weight: 20.1797, symbol: 'Ne',
fantasyCounterparts: ['Light']},
{position: 11, name: 'Sodium', type: 'Metal', weight: 22.9897, symbol: 'Na',
fantasyCounterparts: ['Earth', 'Water']},
{position: 12, name: 'Magnesium', type: 'Metal', weight: 24.305, symbol: 'Mg',
fantasyCounterparts: []},
{position: 13, name: 'Aluminum', type: 'Metal', weight: 26.9815, symbol: 'Al',
fantasyCounterparts: []},
{position: 14, name: 'Silicon', type: 'Semimetal', weight: 28.0855, symbol: 'Si',
fantasyCounterparts: []},
{position: 15, name: 'Phosphorus', type: 'Nonmetal', weight: 30.9738, symbol: 'P',
fantasyCounterparts: []},
{position: 16, name: 'Sulfur', type: 'Nonmetal', weight: 32.065, symbol: 'S',
fantasyCounterparts: []},
{position: 17, name: 'Chlorine', type: 'Nonmetal', weight: 35.453, symbol: 'Cl',
fantasyCounterparts: []},
{position: 18, name: 'Argon', type: 'Nonmetal', weight: 39.948, symbol: 'Ar',
fantasyCounterparts: []},
{position: 19, name: 'Potassium', type: 'Metal', weight: 39.0983, symbol: 'K',
fantasyCounterparts: []},
{position: 20, name: 'Calcium', type: 'Metal', weight: 40.078, symbol: 'Ca',
fantasyCounterparts: []},
];

const TYPES: readonly ElementType[] = ['Metal', 'Semimetal', 'Nonmetal'];
const FANTASY_ELEMENTS: readonly FantasyElement[] =
['Earth', 'Water', 'Wind', 'Fire', 'Light', 'Dark'];

/**
* @title Material Popover Edit on a Material data-table
*/
Expand All @@ -44,11 +74,17 @@ const ELEMENT_DATA: PeriodicElement[] = [
templateUrl: 'popover-edit-mat-table-example.html',
})
export class PopoverEditMatTableExample {
displayedColumns: string[] = ['position', 'name', 'weight', 'symbol'];
displayedColumns: string[] =
['position', 'name', 'type', 'weight', 'symbol', 'fantasyCounterpart'];
dataSource = new ExampleDataSource();

readonly TYPES = TYPES;
readonly FANTASY_ELEMENTS = FANTASY_ELEMENTS;

readonly nameValues = new FormValueContainer<PeriodicElement, any>();
readonly weightValues = new FormValueContainer<PeriodicElement, any>();
readonly typeValues = new FormValueContainer<PeriodicElement, any>();
readonly fantasyValues = new FormValueContainer<PeriodicElement, any>();

constructor(private readonly _snackBar: MatSnackBar) {}

Expand All @@ -64,6 +100,18 @@ export class PopoverEditMatTableExample {
element.weight = f.value.weight;
}

onSubmitType(element: PeriodicElement, f: NgForm) {
if (!f.valid) { return; }

element.type = f.value.type[0];
}

onSubmitFantasyCounterparts(element: PeriodicElement, f: NgForm) {
if (!f.valid) { return; }

element.fantasyCounterparts = f.value.fantasyCounterparts;
}

goodJob(element: PeriodicElement) {
this._snackBar.open(`Way to go, ${element.name}!`, undefined, {duration: 2000});
}
Expand Down
18 changes: 9 additions & 9 deletions src/dev-app/popover-edit/popover-edit-demo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,25 +10,25 @@ import {Component} from '@angular/core';

@Component({
template: `
<h3>CDK popover-edit with cdk-table</h3>
<h3 id="cdk-popover-edit">CDK popover-edit with cdk-table</h3>
<cdk-popover-edit-cdk-table-example></cdk-popover-edit-cdk-table-example>
<h3>CDK popover-edit with cdk-table flex</h3>
<h3 id="cdk-popover-edit-flex">CDK popover-edit with cdk-table flex</h3>
<cdk-popover-edit-cdk-table-flex-example></cdk-popover-edit-cdk-table-flex-example>
<h3>CDK popover-edit with vanilla table</h3>
<h3 id="cdk-popover-edit-vanilla-span">CDK popover-edit with vanilla table</h3>
<cdk-popover-edit-cell-span-vanilla-table-example>
</cdk-popover-edit-cell-span-vanilla-table-example>
<h3>CDK popover-edit with vanilla table and tab out</h3>
<h3 id="cdk-popover-edit-vanilla-tabout">CDK popover-edit with vanilla table and tab out</h3>
<cdk-popover-edit-tab-out-vanilla-table-example>
</cdk-popover-edit-tab-out-vanilla-table-example>
<h3>CDK popover-edit with vanilla table</h3>
<h3 id="cdk-popover-edit-vanilla">CDK popover-edit with vanilla table</h3>
<cdk-popover-edit-vanilla-table-example></cdk-popover-edit-vanilla-table-example>
<h3>Material popover-edit with mat-table and cell span</h3>
<h3 id="mat-popover-edit-span">Material popover-edit with mat-table and cell span</h3>
<popover-edit-cell-span-mat-table-example></popover-edit-cell-span-mat-table-example>
<h3>Material popover-edit with mat-table</h3>
<h3 id="mat-popover-edit">Material popover-edit with mat-table</h3>
<popover-edit-mat-table-example></popover-edit-mat-table-example>
<h3>Material popover-edit with mat-table flex</h3>
<h3 id="mat-popover-edit-flex">Material popover-edit with mat-table flex</h3>
<popover-edit-mat-table-flex-example></popover-edit-mat-table-flex-example>
<h3>Material popover-edit with mat</h3>
<h3 id="mat-popover-edit-tabout">Material popover-edit with mat-table and tab out</h3>
<popover-edit-tab-out-mat-table-example></popover-edit-tab-out-mat-table-example>
`,
})
Expand Down
21 changes: 20 additions & 1 deletion src/material-experimental/popover-edit/_popover-edit.scss
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,8 @@
margin: 0;
}

[mat-edit-content] {
[mat-edit-content],
[mat-edit-fill] {
display: block;

mat-form-field {
Expand All @@ -111,6 +112,20 @@
padding-top: 0;
}
}

// Make mat-selection-lists inside of the look more like mat-select popups.
mat-selection-list {
max-height: 256px; // Same as mat-select.
overflow-y: auto;
}
}

[mat-edit-fill] {
margin: -16px -24px;

mat-selection-list:first-child {
padding-top: 0;
}
}

[mat-edit-actions] {
Expand All @@ -119,6 +134,10 @@
flex-wrap: wrap;
justify-content: flex-end;
margin: 8px -16px -8px;

[mat-edit-fill] + & {
margin-top: 16px;
}
}
}

Expand Down
7 changes: 1 addition & 6 deletions src/material-experimental/popover-edit/lens-directives.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,6 @@ export class MatEditRevert<FormValue> extends CdkEditRevert<FormValue> {
}

/** Closes the lens on click. */
@Directive({
selector: 'button[matEditClose]',
host: {
'type': 'button', // Prevents accidental form submits.
}
})
@Directive({selector: '[matEditClose]'})
export class MatEditClose<FormValue> extends CdkEditClose<FormValue> {
}