Skip to content

Commit 058ed0f

Browse files
kseamonmmalerba
authored andcommitted
feat(popover-edit): Material version of popover edit (#15873)
1 parent 98aa699 commit 058ed0f

36 files changed

+1959
-33
lines changed

.github/CODEOWNERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@
9191
/src/material-experimental/mdc-menu/** @mmalerba # Note to implementer: please repossess
9292
/src/material-experimental/mdc-radio/** @mmalerba # Note to implementer: please repossess
9393
/src/material-experimental/mdc-slide-toggle/** @mmalerba # Note to implementer: please repossess
94+
/src/material-experimental/popover-edit/** @kseamon @andrewseguin
9495

9596
# CDK experimental package
9697
/src/cdk-experimental/** @jelbourn

packages.bzl

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,18 @@ MATERIAL_SCSS_LIBS = [
7878
"//src/material/%s:%s_scss_lib" % (p, p.replace('-', '_')) for p in MATERIAL_PACKAGES
7979
]
8080

81+
MATERIAL_EXPERIMENTAL_PACKAGES = [
82+
"popover-edit",
83+
]
84+
85+
MATERIAL_EXPERIMENTAL_TARGETS = ["//src/material-experimental"] + [
86+
"//src/material-experimental/%s" % p for p in MATERIAL_EXPERIMENTAL_PACKAGES
87+
]
88+
89+
MATERIAL_EXPERIMENTAL_SCSS_LIBS = [
90+
"//src/material-experimental/%s:%s_scss_lib" % (p, p.replace('-', '_')) for p in MATERIAL_EXPERIMENTAL_PACKAGES
91+
]
92+
8193
# Each individual package uses a placeholder for the version of Angular to ensure they're
8294
# all in-sync. This map is passed to each ng_package rule to stamp out the appropriate
8395
# version for the placeholders.
@@ -114,6 +126,11 @@ ROLLUP_GLOBALS.update({
114126
"@angular/material/%s" % p: "ng.material.%s" % p for p in MATERIAL_PACKAGES
115127
})
116128

129+
# Rollup globals for material experiemental subpackages, e.g., {"@angular/material-experimental/list": "ng.materialExperimental.list"}
130+
ROLLUP_GLOBALS.update({
131+
"@angular/material-experiemntal/%s" % p: "ng.materialExperimental.%s" % p for p in MATERIAL_EXPERIMENTAL_PACKAGES
132+
})
133+
117134
# UMD bundles for Angular packages and subpackges we depend on for development and testing.
118135
ANGULAR_LIBRARY_UMDS = [
119136
"@npm//node_modules/@angular/animations:bundles/animations-browser.umd.js",

src/cdk-experimental/popover-edit/constants.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,4 @@ export const TABLE_SELECTOR = 'table, cdk-table, mat-table';
2222
export const EDIT_PANE_CLASS = 'cdk-edit-pane';
2323

2424
/** Selector for finding the edit lens pane. */
25-
export const EDIT_PANE_SELECTOR = '.' + EDIT_PANE_CLASS;
25+
export const EDIT_PANE_SELECTOR = `.${EDIT_PANE_CLASS}, .mat-edit-pane`;

src/cdk-experimental/popover-edit/edit-services.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
import {Injectable, NgZone} from '@angular/core';
1010
import {FocusTrapFactory} from '@angular/cdk/a11y';
11+
import {Directionality} from '@angular/cdk/bidi';
1112
import {Overlay} from '@angular/cdk/overlay';
1213
import {ScrollDispatcher, ViewportRuler} from '@angular/cdk/scrolling';
1314

@@ -24,6 +25,7 @@ import {PopoverEditPositionStrategyFactory} from './popover-edit-position-strate
2425
@Injectable()
2526
export class EditServices {
2627
constructor(
28+
readonly directionality: Directionality,
2729
readonly editEventDispatcher: EditEventDispatcher, readonly focusDispatcher: FocusDispatcher,
2830
readonly focusTrapFactory: FocusTrapFactory, readonly ngZone: NgZone,
2931
readonly overlay: Overlay, readonly positionFactory: PopoverEditPositionStrategyFactory,

src/cdk-experimental/popover-edit/lens-directives.ts

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
*/
88

99
import {ReplaySubject} from 'rxjs';
10-
import {Directive, ElementRef, EventEmitter, Input, OnDestroy, OnInit, Output} from '@angular/core';
10+
import {Directive, ElementRef, EventEmitter, OnDestroy, OnInit} from '@angular/core';
1111
import {EDIT_PANE_SELECTOR} from './constants';
1212
import {closest} from './polyfill';
1313
import {EditRef} from './edit-ref';
@@ -30,6 +30,12 @@ export type PopoverEditClickOutBehavior = 'close' | 'submit' | 'noop';
3030
'(keyup.escape)': 'close()',
3131
'(document:click)': 'handlePossibleClickOut($event)',
3232
},
33+
inputs: [
34+
'clickOutBehavior: cdkEditControlClickOutBehavior',
35+
'preservedFormValue: cdkEditControlPreservedFormValue',
36+
'ignoreSubmitUnlessValid: cdkEditControlIgnoreSubmitUnlessValid',
37+
],
38+
outputs: ['preservedFormValueChange: cdkEditControlPreservedFormValueChange'],
3339
providers: [EditRef],
3440
})
3541
export class CdkEditControl<FormValue> implements OnDestroy, OnInit {
@@ -39,22 +45,21 @@ export class CdkEditControl<FormValue> implements OnDestroy, OnInit {
3945
* Specifies what should happen when the user clicks outside of the edit lens.
4046
* The default behavior is to close the lens without submitting the form.
4147
*/
42-
@Input('cdkEditControlClickOutBehavior') clickOutBehavior: PopoverEditClickOutBehavior = 'close';
48+
clickOutBehavior: PopoverEditClickOutBehavior = 'close';
4349

4450
/**
4551
* A two-way binding for storing unsubmitted form state. If not provided
4652
* then form state will be discarded on close. The PeristBy directive is offered
4753
* as a convenient shortcut for these bindings.
4854
*/
49-
@Input('cdkEditControlPreservedFormValue') preservedFormValue?: FormValue;
50-
@Output('cdkEditControlPreservedFormValueChange') readonly preservedFormValueChange =
51-
new EventEmitter<FormValue>();
55+
preservedFormValue?: FormValue;
56+
readonly preservedFormValueChange = new EventEmitter<FormValue>();
5257

5358
/**
5459
* Determines whether the lens will close on form submit if the form is not in a valid
5560
* state. By default the lens will remain open.
5661
*/
57-
@Input('cdkEditControlIgnoreSubmitUnlessValid') ignoreSubmitUnlessValid = true;
62+
ignoreSubmitUnlessValid = true;
5863

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

src/cdk-experimental/popover-edit/table-directives.ts

Lines changed: 34 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import {
1313
Directive,
1414
ElementRef,
1515
EmbeddedViewRef,
16-
Input,
1716
NgZone,
1817
OnDestroy,
1918
TemplateRef,
@@ -110,34 +109,42 @@ export class CdkEditable implements AfterViewInit, OnDestroy {
110109
}
111110
}
112111

112+
const POPOVER_EDIT_HOST_BINDINGS = {
113+
'tabIndex': '0',
114+
'class': 'cdk-popover-edit-cell',
115+
'[attr.aria-haspopup]': 'true',
116+
};
117+
118+
const POPOVER_EDIT_INPUTS = [
119+
'template: cdkPopoverEdit',
120+
'context: cdkPopoverEditContext',
121+
'colspan: cdkPopoverEditColspan',
122+
];
123+
113124
/**
114125
* Attaches an ng-template to a cell and shows it when instructed to by the
115126
* EditEventDispatcher service.
116127
* Makes the cell focusable.
117128
*/
118129
@Directive({
119130
selector: '[cdkPopoverEdit]:not([cdkPopoverEditTabOut])',
120-
host: {
121-
'tabIndex': '0',
122-
'class': 'cdk-popover-edit-cell',
123-
'[attr.aria-haspopup]': 'true',
124-
}
131+
host: POPOVER_EDIT_HOST_BINDINGS,
132+
inputs: POPOVER_EDIT_INPUTS,
125133
})
126134
export class CdkPopoverEdit<C> implements AfterViewInit, OnDestroy {
127135
/** The edit lens template shown over the cell on edit. */
128-
@Input('cdkPopoverEdit') template: TemplateRef<any>|null = null;
136+
template: TemplateRef<any>|null = null;
129137

130138
/**
131139
* Implicit context to pass along to the template. Can be omitted if the template
132140
* is defined within the cell.
133141
*/
134-
@Input('cdkPopoverEditContext') context?: C;
142+
context?: C;
135143

136144
/**
137145
* Specifies that the popup should cover additional table cells before and/or after
138146
* this one.
139147
*/
140-
@Input('cdkPopoverEditColspan')
141148
get colspan(): CdkPopoverEditColspan {
142149
return this._colspan;
143150
}
@@ -184,6 +191,10 @@ export class CdkPopoverEdit<C> implements AfterViewInit, OnDestroy {
184191
this.services.editEventDispatcher.doneEditingCell(this.elementRef.nativeElement!);
185192
}
186193

194+
protected panelClass(): string {
195+
return EDIT_PANE_CLASS;
196+
}
197+
187198
private _startListeningToEditEvents(): void {
188199
this.services.editEventDispatcher.editingCell(this.elementRef.nativeElement!)
189200
.pipe(takeUntil(this.destroyed))
@@ -207,7 +218,7 @@ export class CdkPopoverEdit<C> implements AfterViewInit, OnDestroy {
207218
private _createEditOverlay(): void {
208219
this.overlayRef = this.services.overlay.create({
209220
disposeOnNavigation: true,
210-
panelClass: EDIT_PANE_CLASS,
221+
panelClass: this.panelClass(),
211222
positionStrategy: this._getPositionStrategy(),
212223
scrollStrategy: this.services.overlay.scrollStrategies.reposition(),
213224
});
@@ -276,12 +287,9 @@ export class CdkPopoverEdit<C> implements AfterViewInit, OnDestroy {
276287
* Makes the cell focusable.
277288
*/
278289
@Directive({
279-
selector: '[cdkPopoverEdit] [cdkPopoverEditTabOut]',
280-
host: {
281-
'tabIndex': '0',
282-
'class': 'cdk-popover-edit-cell',
283-
'[attr.aria-haspopup]': 'true',
284-
}
290+
selector: '[cdkPopoverEdit][cdkPopoverEditTabOut]',
291+
host: POPOVER_EDIT_HOST_BINDINGS,
292+
inputs: POPOVER_EDIT_INPUTS,
285293
})
286294
export class CdkPopoverEditTabOut<C> extends CdkPopoverEdit<C> {
287295
protected focusTrap?: FocusEscapeNotifier;
@@ -324,9 +332,6 @@ export class CdkPopoverEditTabOut<C> extends CdkPopoverEdit<C> {
324332
*/
325333
@Directive({
326334
selector: '[cdkRowHoverContent]',
327-
host: {
328-
'[attr.aria-hidden]': 'true',
329-
}
330335
})
331336
export class CdkRowHoverContent implements AfterViewInit, OnDestroy {
332337
protected readonly destroyed = new ReplaySubject<void>();
@@ -350,6 +355,14 @@ export class CdkRowHoverContent implements AfterViewInit, OnDestroy {
350355
}
351356
}
352357

358+
protected initElement(element: HTMLElement): void;
359+
protected initElement(): void {
360+
}
361+
362+
protected prepareElement(element: HTMLElement): void;
363+
protected prepareElement(): void {
364+
}
365+
353366
private _listenForHoverEvents(): void {
354367
this.services.editEventDispatcher.hoveringOnRow(this.elementRef.nativeElement!)
355368
.pipe(takeUntil(this.destroyed))
@@ -360,9 +373,11 @@ export class CdkRowHoverContent implements AfterViewInit, OnDestroy {
360373
// Not doing any positioning in CDK version. Material version
361374
// will absolutely position on right edge of cell.
362375
this.viewRef = this.viewContainerRef.createEmbeddedView(this.templateRef, {});
376+
this.initElement(this.elementRef.nativeElement!.nextSibling as HTMLElement);
363377
} else {
364378
this.viewContainerRef.insert(this.viewRef);
365379
}
380+
this.prepareElement(this.elementRef.nativeElement!.nextSibling as HTMLElement);
366381
} else if (this.viewRef) {
367382
this.viewContainerRef.detach(this.viewContainerRef.indexOf(this.viewRef));
368383
}

src/dev-app/BUILD.bazel

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package(default_visibility=["//visibility:public"])
22

33
load("@io_bazel_rules_sass//:defs.bzl", "sass_binary")
4-
load("//:packages.bzl", "MATERIAL_TARGETS", "CDK_TARGETS", "CDK_EXPERIMENTAL_TARGETS")
4+
load("//:packages.bzl", "MATERIAL_TARGETS", "CDK_TARGETS", "CDK_EXPERIMENTAL_TARGETS", "MATERIAL_EXPERIMENTAL_TARGETS", "MATERIAL_EXPERIMENTAL_SCSS_LIBS")
55
load("//tools:defaults.bzl", "ng_module")
66
load("//tools:sass_generate_binaries.bzl", "sass_generate_binaries")
77

@@ -32,7 +32,7 @@ ng_module(
3232
"//src/material-experimental/mdc-radio",
3333
"//src/material-experimental/mdc-slide-toggle",
3434
"//src/material-examples:examples",
35-
] + CDK_TARGETS + CDK_EXPERIMENTAL_TARGETS + MATERIAL_TARGETS
35+
] + CDK_TARGETS + CDK_EXPERIMENTAL_TARGETS + MATERIAL_TARGETS + MATERIAL_EXPERIMENTAL_TARGETS
3636
)
3737

3838
sass_binary(
@@ -50,5 +50,5 @@ sass_binary(
5050
"//src/material-experimental/mdc-menu:menu_scss_lib",
5151
"//src/material-experimental/mdc-radio:radio_scss_lib",
5252
"//src/material-experimental/mdc-slide-toggle:slide_toggle_scss_lib",
53-
]
53+
] + MATERIAL_EXPERIMENTAL_SCSS_LIBS
5454
)
Lines changed: 1 addition & 0 deletions
Loading

src/dev-app/system-config.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,8 @@ System.config({
149149
'dist/packages/material-experimental/mdc-slide-toggle/index.js',
150150
'@angular/material-experimental/mdc-helpers':
151151
'dist/packages/material-experimental/mdc-helpers/index.js',
152+
'@angular/material-experimental/popover-edit':
153+
'dist/packages/material-experimental/popover-edit/index.js',
152154
},
153155
packages: {
154156
// Set the default extension for the root package, because otherwise the dev-app can't

src/dev-app/theme.scss

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
@import '../material-experimental/mdc-menu/mdc-menu';
77
@import '../material-experimental/mdc-radio/mdc-radio';
88
@import '../material-experimental/mdc-slide-toggle/mdc-slide-toggle';
9+
@import '../material-experimental/popover-edit/popover-edit';
910

1011
// Plus imports for other components in your app.
1112

@@ -33,7 +34,8 @@ $candy-app-theme: mat-light-theme($candy-app-primary, $candy-app-accent);
3334
@include mat-menu-theme-mdc($candy-app-theme);
3435
@include mat-radio-theme-mdc($candy-app-theme);
3536
@include mat-slide-toggle-theme-mdc($candy-app-theme);
36-
37+
@include mat-edit-theme($candy-app-theme);
38+
@include mat-edit-typography(mat-typography-config());
3739

3840
// Define an alternate dark theme.
3941
$dark-primary: mat-palette($mat-blue-grey);
@@ -53,4 +55,5 @@ $dark-theme: mat-dark-theme($dark-primary, $dark-accent, $dark-warn);
5355
@include mat-menu-theme-mdc($dark-theme);
5456
@include mat-radio-theme-mdc($dark-theme);
5557
@include mat-slide-toggle-theme-mdc($dark-theme);
58+
@include mat-edit-theme($dark-theme);
5659
}

src/material-examples/BUILD.bazel

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package(default_visibility=["//visibility:public"])
22

3-
load("//:packages.bzl", "CDK_TARGETS", "CDK_EXPERIMENTAL_TARGETS", "MATERIAL_TARGETS", "ROLLUP_GLOBALS")
3+
load("//:packages.bzl", "CDK_TARGETS", "CDK_EXPERIMENTAL_TARGETS", "MATERIAL_TARGETS", "MATERIAL_EXPERIMENTAL_TARGETS", "ROLLUP_GLOBALS")
44
load("//tools:defaults.bzl", "ng_module", "ng_package")
55
load("//tools/highlight-files:index.bzl", "highlight_files")
66
load("//tools/package-docs-content:index.bzl", "package_docs_content")
@@ -17,7 +17,7 @@ ng_module(
1717
"@npm//@angular/forms",
1818
"@npm//moment",
1919
"//src/material-moment-adapter",
20-
] + CDK_TARGETS + CDK_EXPERIMENTAL_TARGETS + MATERIAL_TARGETS,
20+
] + CDK_TARGETS + CDK_EXPERIMENTAL_TARGETS + MATERIAL_TARGETS + MATERIAL_EXPERIMENTAL_TARGETS,
2121
# Specify the tsconfig that is also used by Gulp. We need to explicitly use this tsconfig
2222
# because in order to import Moment with TypeScript, some specific options need to be set.
2323
tsconfig = ":tsconfig-build.json",

src/material-examples/material-module.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {CdkTableModule} from '@angular/cdk/table';
77
import {CdkTreeModule} from '@angular/cdk/tree';
88
import {DragDropModule} from '@angular/cdk/drag-drop';
99
import {CdkStepperModule} from '@angular/cdk/stepper';
10+
import {MatPopoverEditModule} from '@angular/material-experimental/popover-edit';
1011
import {PortalModule} from '@angular/cdk/portal';
1112
import {
1213
MatAutocompleteModule, MatBadgeModule, MatBottomSheetModule, MatButtonModule,
@@ -46,6 +47,7 @@ import {
4647
MatListModule,
4748
MatMenuModule,
4849
MatPaginatorModule,
50+
MatPopoverEditModule,
4951
MatProgressBarModule,
5052
MatProgressSpinnerModule,
5153
MatRadioModule,
@@ -92,6 +94,7 @@ import {
9294
MatListModule,
9395
MatMenuModule,
9496
MatPaginatorModule,
97+
MatPopoverEditModule,
9598
MatProgressBarModule,
9699
MatProgressSpinnerModule,
97100
MatRadioModule,
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
.example-table {
2+
width: 100%;
3+
}
4+
5+
.example-table th {
6+
text-align: left;
7+
}
8+
9+
.example-table td,
10+
.example-table th {
11+
min-width: 300px;
12+
width: 25%;
13+
}
14+
15+
.example-input-container {
16+
display: flex;
17+
justify-content: stretch;
18+
}
19+
20+
.example-input-container mat-form-field {
21+
flex: 1;
22+
}

0 commit comments

Comments
 (0)