From 6a25079ab3367c37507205cde9c2fdb7b243ab21 Mon Sep 17 00:00:00 2001 From: Karl Seamon Date: Thu, 2 Jan 2020 17:29:02 -0500 Subject: [PATCH] feat(popover-edit): Adds support for using mat-selection-list for select-like interactions. Includes minor behavior and style updates to PopoverEdit and an updated demo showing off the new interaction. --- .../popover-edit/lens-directives.ts | 23 ++--- .../popover-edit/BUILD.bazel | 2 + .../popover-edit/index.ts | 4 + .../popover-edit-mat-table-example.css | 2 +- .../popover-edit-mat-table-example.html | 76 ++++++++++++++++ .../popover-edit-mat-table-example.ts | 90 ++++++++++++++----- src/dev-app/popover-edit/popover-edit-demo.ts | 18 ++-- .../popover-edit/_popover-edit.scss | 21 ++++- .../popover-edit/lens-directives.ts | 7 +- 9 files changed, 195 insertions(+), 48 deletions(-) diff --git a/src/cdk-experimental/popover-edit/lens-directives.ts b/src/cdk-experimental/popover-edit/lens-directives.ts index c591bb5a6177..7f1865af4302 100644 --- a/src/cdk-experimental/popover-edit/lens-directives.ts +++ b/src/cdk-experimental/popover-edit/lens-directives.ts @@ -175,24 +175,27 @@ export class CdkEditRevert { } /** Closes the lens on click. */ -@Directive({ - selector: 'button[cdkEditClose]', - host: { - 'type': 'button', // Prevents accidental form submits. - } -}) +@Directive({selector: '[cdkEditClose]'}) export class CdkEditClose { - /** Type of the button. Defaults to `button` to avoid accident form submits. */ - @Input() type: string = 'button'; - constructor( - protected readonly editRef: EditRef) {} + protected readonly elementRef: ElementRef, + protected readonly editRef: EditRef) { + + 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. diff --git a/src/components-examples/material-experimental/popover-edit/BUILD.bazel b/src/components-examples/material-experimental/popover-edit/BUILD.bazel index 67731374733f..b54879aba6d0 100644 --- a/src/components-examples/material-experimental/popover-edit/BUILD.bazel +++ b/src/components-examples/material-experimental/popover-edit/BUILD.bazel @@ -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", ], ) diff --git a/src/components-examples/material-experimental/popover-edit/index.ts b/src/components-examples/material-experimental/popover-edit/index.ts index b827ab09cad4..2911c1f66223 100644 --- a/src/components-examples/material-experimental/popover-edit/index.ts +++ b/src/components-examples/material-experimental/popover-edit/index.ts @@ -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 { @@ -33,9 +35,11 @@ const EXAMPLES = [ @NgModule({ imports: [ + CommonModule, MatButtonModule, MatIconModule, MatInputModule, + MatListModule, MatPopoverEditModule, MatSnackBarModule, MatTableModule, diff --git a/src/components-examples/material-experimental/popover-edit/popover-edit-mat-table/popover-edit-mat-table-example.css b/src/components-examples/material-experimental/popover-edit/popover-edit-mat-table/popover-edit-mat-table-example.css index eba60b11b33b..22e16bd83228 100644 --- a/src/components-examples/material-experimental/popover-edit/popover-edit-mat-table/popover-edit-mat-table-example.css +++ b/src/components-examples/material-experimental/popover-edit/popover-edit-mat-table/popover-edit-mat-table-example.css @@ -8,5 +8,5 @@ .example-table td, .example-table th { - width: 25%; + width: 16%; } diff --git a/src/components-examples/material-experimental/popover-edit/popover-edit-mat-table/popover-edit-mat-table-example.html b/src/components-examples/material-experimental/popover-edit/popover-edit-mat-table/popover-edit-mat-table-example.html index 9a92edc7b24b..19172c9d45e2 100644 --- a/src/components-examples/material-experimental/popover-edit/popover-edit-mat-table/popover-edit-mat-table-example.html +++ b/src/components-examples/material-experimental/popover-edit/popover-edit-mat-table/popover-edit-mat-table-example.html @@ -71,6 +71,43 @@

Name

+ + + Type + + {{element.type}} + + + +
+
+
+ + + {{type}} + + +
+
+
+
+ + + + + +
+ Weight @@ -92,4 +129,43 @@

Name

+ + + + Fantasy Counterparts + + {{element.fantasyCounterparts.join(', ')}} + + + +
+
+
+ + + {{fantasyElement}} + + +
+
+ + +
+
+
+
+ + + + + +
diff --git a/src/components-examples/material-experimental/popover-edit/popover-edit-mat-table/popover-edit-mat-table-example.ts b/src/components-examples/material-experimental/popover-edit/popover-edit-mat-table/popover-edit-mat-table-example.ts index 4d1edc82ecc6..a3072170ef01 100644 --- a/src/components-examples/material-experimental/popover-edit/popover-edit-mat-table/popover-edit-mat-table-example.ts +++ b/src/components-examples/material-experimental/popover-edit/popover-edit-mat-table/popover-edit-mat-table-example.ts @@ -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 */ @@ -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(); readonly weightValues = new FormValueContainer(); + readonly typeValues = new FormValueContainer(); + readonly fantasyValues = new FormValueContainer(); constructor(private readonly _snackBar: MatSnackBar) {} @@ -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}); } diff --git a/src/dev-app/popover-edit/popover-edit-demo.ts b/src/dev-app/popover-edit/popover-edit-demo.ts index 44d7a2bef9cb..0b4dd0adb229 100644 --- a/src/dev-app/popover-edit/popover-edit-demo.ts +++ b/src/dev-app/popover-edit/popover-edit-demo.ts @@ -10,25 +10,25 @@ import {Component} from '@angular/core'; @Component({ template: ` -

CDK popover-edit with cdk-table

+

CDK popover-edit with cdk-table

-

CDK popover-edit with cdk-table flex

+

CDK popover-edit with cdk-table flex

-

CDK popover-edit with vanilla table

+

CDK popover-edit with vanilla table

-

CDK popover-edit with vanilla table and tab out

+

CDK popover-edit with vanilla table and tab out

-

CDK popover-edit with vanilla table

+

CDK popover-edit with vanilla table

-

Material popover-edit with mat-table and cell span

+

Material popover-edit with mat-table and cell span

-

Material popover-edit with mat-table

+

Material popover-edit with mat-table

-

Material popover-edit with mat-table flex

+

Material popover-edit with mat-table flex

-

Material popover-edit with mat

+

Material popover-edit with mat-table and tab out

`, }) diff --git a/src/material-experimental/popover-edit/_popover-edit.scss b/src/material-experimental/popover-edit/_popover-edit.scss index 55f204dd6702..da8cfebf029e 100644 --- a/src/material-experimental/popover-edit/_popover-edit.scss +++ b/src/material-experimental/popover-edit/_popover-edit.scss @@ -99,7 +99,8 @@ margin: 0; } - [mat-edit-content] { + [mat-edit-content], + [mat-edit-fill] { display: block; mat-form-field { @@ -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] { @@ -119,6 +134,10 @@ flex-wrap: wrap; justify-content: flex-end; margin: 8px -16px -8px; + + [mat-edit-fill] + & { + margin-top: 16px; + } } } diff --git a/src/material-experimental/popover-edit/lens-directives.ts b/src/material-experimental/popover-edit/lens-directives.ts index b15e09dbde5d..2f6c07dad613 100644 --- a/src/material-experimental/popover-edit/lens-directives.ts +++ b/src/material-experimental/popover-edit/lens-directives.ts @@ -48,11 +48,6 @@ export class MatEditRevert extends CdkEditRevert { } /** Closes the lens on click. */ -@Directive({ - selector: 'button[matEditClose]', - host: { - 'type': 'button', // Prevents accidental form submits. - } -}) +@Directive({selector: '[matEditClose]'}) export class MatEditClose extends CdkEditClose { }