@ng-matero/extensions 12.9.2 → 12.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/bundles/mtxCore.umd.js.map +1 -1
  2. package/bundles/mtxDatetimepicker.umd.js +5 -0
  3. package/bundles/mtxDatetimepicker.umd.js.map +1 -1
  4. package/bundles/mtxGrid.umd.js +24 -21
  5. package/bundles/mtxGrid.umd.js.map +1 -1
  6. package/bundles/mtxPopover.umd.js +997 -803
  7. package/bundles/mtxPopover.umd.js.map +1 -1
  8. package/bundles/mtxSelect.umd.js +237 -156
  9. package/bundles/mtxSelect.umd.js.map +1 -1
  10. package/core/datetime/datetime-formats.d.ts +2 -0
  11. package/esm2015/core/datetime/datetime-formats.js +1 -1
  12. package/esm2015/datetimepicker/datetimepicker-input.js +6 -1
  13. package/esm2015/grid/cell.component.js +7 -4
  14. package/esm2015/grid/column-menu.component.js +10 -14
  15. package/esm2015/grid/grid.component.js +10 -6
  16. package/esm2015/popover/popover-animations.js +10 -13
  17. package/esm2015/popover/popover-content.js +99 -0
  18. package/esm2015/popover/popover-interfaces.js +1 -1
  19. package/esm2015/popover/popover-module.js +7 -5
  20. package/esm2015/popover/popover-target.js +3 -3
  21. package/esm2015/popover/popover-trigger.js +210 -172
  22. package/esm2015/popover/popover-types.js +1 -1
  23. package/esm2015/popover/popover.js +173 -125
  24. package/esm2015/popover/public-api.js +2 -1
  25. package/esm2015/select/option.component.js +4 -6
  26. package/esm2015/select/select.component.js +111 -31
  27. package/fesm2015/mtxCore.js.map +1 -1
  28. package/fesm2015/mtxDatetimepicker.js +5 -0
  29. package/fesm2015/mtxDatetimepicker.js.map +1 -1
  30. package/fesm2015/mtxGrid.js +24 -21
  31. package/fesm2015/mtxGrid.js.map +1 -1
  32. package/fesm2015/mtxPopover.js +526 -351
  33. package/fesm2015/mtxPopover.js.map +1 -1
  34. package/fesm2015/mtxSelect.js +169 -92
  35. package/fesm2015/mtxSelect.js.map +1 -1
  36. package/grid/cell.component.d.ts +4 -2
  37. package/grid/column-menu.component.d.ts +3 -5
  38. package/grid/grid.component.d.ts +6 -4
  39. package/grid/mtxGrid.metadata.json +1 -1
  40. package/package.json +1 -1
  41. package/popover/mtxPopover.metadata.json +1 -1
  42. package/popover/popover-animations.d.ts +1 -1
  43. package/popover/popover-content.d.ts +38 -0
  44. package/popover/popover-interfaces.d.ts +37 -31
  45. package/popover/popover-target.d.ts +2 -2
  46. package/popover/popover-trigger.d.ts +65 -60
  47. package/popover/popover-types.d.ts +6 -1
  48. package/popover/popover.d.ts +97 -69
  49. package/popover/popover.scss +2 -0
  50. package/popover/public-api.d.ts +1 -0
  51. package/select/mtxSelect.metadata.json +1 -1
  52. package/select/option.component.d.ts +7 -6
  53. package/select/select.component.d.ts +59 -20
@@ -1,14 +1,133 @@
1
- import { EventEmitter, Component, ChangeDetectionStrategy, ViewEncapsulation, Optional, ElementRef, NgZone, HostBinding, Input, Output, ViewChild, TemplateRef, Directive, ViewContainerRef, ChangeDetectorRef, HostListener, NgModule } from '@angular/core';
2
- import { CommonModule } from '@angular/common';
3
- import { OverlayConfig, Overlay, OverlayModule } from '@angular/cdk/overlay';
4
- import { isFakeMousedownFromScreenReader, A11yModule } from '@angular/cdk/a11y';
1
+ import { InjectionToken, Directive, TemplateRef, ComponentFactoryResolver, ApplicationRef, Injector, ViewContainerRef, Inject, ChangeDetectorRef, EventEmitter, Component, ChangeDetectionStrategy, ViewEncapsulation, ElementRef, NgZone, Input, Output, ViewChild, ContentChild, Optional, NgModule } from '@angular/core';
2
+ import { DOCUMENT, CommonModule } from '@angular/common';
3
+ import { Overlay, OverlayConfig, OverlayModule } from '@angular/cdk/overlay';
4
+ import { isFakeMousedownFromScreenReader, FocusMonitor, A11yModule } from '@angular/cdk/a11y';
5
5
  import { coerceBooleanProperty } from '@angular/cdk/coercion';
6
- import { ESCAPE } from '@angular/cdk/keycodes';
7
- import { Directionality } from '@angular/cdk/bidi';
6
+ import { ESCAPE, hasModifierKey, ENTER, SPACE } from '@angular/cdk/keycodes';
7
+ import { Subject, Subscription, of, merge } from 'rxjs';
8
8
  import { trigger, state, style, transition, animate } from '@angular/animations';
9
- import { TemplatePortal } from '@angular/cdk/portal';
10
- import { Subject } from 'rxjs';
11
- import { takeUntil } from 'rxjs/operators';
9
+ import { TemplatePortal, DomPortalOutlet } from '@angular/cdk/portal';
10
+ import { Directionality } from '@angular/cdk/bidi';
11
+ import { filter, take, takeUntil } from 'rxjs/operators';
12
+
13
+ /**
14
+ * Below are all the animations for the mtx-popover component.
15
+ * Animation duration and timing values are based on AngularJS Material.
16
+ */
17
+ /**
18
+ * This animation controls the popover panel's entry and exit from the page.
19
+ *
20
+ * When the popover panel is added to the DOM, it scales in and fades in its border.
21
+ *
22
+ * When the popover panel is removed from the DOM, it simply fades out after a brief
23
+ * delay to display the ripple.
24
+ */
25
+ const transformPopover = trigger('transformPopover', [
26
+ state('void', style({
27
+ opacity: 0,
28
+ transform: 'scale(0.8)',
29
+ })),
30
+ transition('void => enter', animate('120ms cubic-bezier(0, 0, 0.2, 1)', style({
31
+ opacity: 1,
32
+ transform: 'scale(1)',
33
+ }))),
34
+ transition('* => void', animate('100ms 25ms linear', style({ opacity: 0 }))),
35
+ ]);
36
+
37
+ /**
38
+ * Injection token that can be used to reference instances of `MtxPopoverContent`. It serves
39
+ * as alternative token to the actual `MtxPopoverContent` class which could cause unnecessary
40
+ * retention of the class and its directive metadata.
41
+ */
42
+ const MTX_POPOVER_CONTENT = new InjectionToken('MtxPopoverContent');
43
+ class _MtxPopoverContentBase {
44
+ constructor(_template, _componentFactoryResolver, _appRef, _injector, _viewContainerRef, _document, _changeDetectorRef) {
45
+ this._template = _template;
46
+ this._componentFactoryResolver = _componentFactoryResolver;
47
+ this._appRef = _appRef;
48
+ this._injector = _injector;
49
+ this._viewContainerRef = _viewContainerRef;
50
+ this._document = _document;
51
+ this._changeDetectorRef = _changeDetectorRef;
52
+ /** Emits when the popover content has been attached. */
53
+ this._attached = new Subject();
54
+ }
55
+ /**
56
+ * Attaches the content with a particular context.
57
+ * @docs-private
58
+ */
59
+ attach(context = {}) {
60
+ if (!this._portal) {
61
+ this._portal = new TemplatePortal(this._template, this._viewContainerRef);
62
+ }
63
+ this.detach();
64
+ if (!this._outlet) {
65
+ this._outlet = new DomPortalOutlet(this._document.createElement('div'), this._componentFactoryResolver, this._appRef, this._injector);
66
+ }
67
+ const element = this._template.elementRef.nativeElement;
68
+ // Because we support opening the same popover from different triggers (which in turn have their
69
+ // own `OverlayRef` panel), we have to re-insert the host element every time, otherwise we
70
+ // risk it staying attached to a pane that's no longer in the DOM.
71
+ element.parentNode.insertBefore(this._outlet.outletElement, element);
72
+ // When `MtxPopoverContent` is used in an `OnPush` component, the insertion of the popover
73
+ // content via `createEmbeddedView` does not cause the content to be seen as "dirty"
74
+ // by Angular. This causes the `@ContentChildren` for popover items within the popover to
75
+ // not be updated by Angular. By explicitly marking for check here, we tell Angular that
76
+ // it needs to check for new popover items and update the `@ContentChild` in `MtxPopover`.
77
+ // @breaking-change 9.0.0 Make change detector ref required
78
+ if (this._changeDetectorRef) {
79
+ this._changeDetectorRef.markForCheck();
80
+ }
81
+ this._portal.attach(this._outlet, context);
82
+ this._attached.next();
83
+ }
84
+ /**
85
+ * Detaches the content.
86
+ * @docs-private
87
+ */
88
+ detach() {
89
+ if (this._portal.isAttached) {
90
+ this._portal.detach();
91
+ }
92
+ }
93
+ ngOnDestroy() {
94
+ if (this._outlet) {
95
+ this._outlet.dispose();
96
+ }
97
+ }
98
+ }
99
+ /** @type {!Array<{type: !Function, args: (undefined|!Array<?>)}>} */
100
+ _MtxPopoverContentBase.decorators = [
101
+ { type: Directive }
102
+ ];
103
+ /**
104
+ * @type {function(): !Array<(null|{
105
+ * type: ?,
106
+ * decorators: (undefined|!Array<{type: !Function, args: (undefined|!Array<?>)}>),
107
+ * })>}
108
+ * @nocollapse
109
+ */
110
+ _MtxPopoverContentBase.ctorParameters = () => [
111
+ { type: TemplateRef },
112
+ { type: ComponentFactoryResolver },
113
+ { type: ApplicationRef },
114
+ { type: Injector },
115
+ { type: ViewContainerRef },
116
+ { type: undefined, decorators: [{ type: Inject, args: [DOCUMENT,] }] },
117
+ { type: ChangeDetectorRef }
118
+ ];
119
+ /**
120
+ * Popover content that will be rendered lazily once the popover is opened.
121
+ */
122
+ class MtxPopoverContent extends _MtxPopoverContentBase {
123
+ }
124
+ /** @type {!Array<{type: !Function, args: (undefined|!Array<?>)}>} */
125
+ MtxPopoverContent.decorators = [
126
+ { type: Directive, args: [{
127
+ selector: 'ng-template[mtxPopoverContent]',
128
+ providers: [{ provide: MTX_POPOVER_CONTENT, useExisting: MtxPopoverContent }],
129
+ },] }
130
+ ];
12
131
 
13
132
  /**
14
133
  * Throws an exception for the case when popover trigger doesn't have a valid mtx-popover instance
@@ -37,190 +156,183 @@ function throwMtxPopoverInvalidPositionEnd() {
37
156
  Example: <mtx-popover [position]="['below', 'after']" #popover="mtxPopover"></mtx-popover>`);
38
157
  }
39
158
 
40
- /**
41
- * Below are all the animations for the md-popover component.
42
- * Animation duration and timing values are based on AngularJS Material.
43
- */
44
- /**
45
- * This animation controls the popover panel's entry and exit from the page.
46
- *
47
- * When the popover panel is added to the DOM, it scales in and fades in its border.
48
- *
49
- * When the popover panel is removed from the DOM, it simply fades out after a brief
50
- * delay to display the ripple.
51
- */
52
- const transformPopover = trigger('transformPopover', [
53
- state('enter', style({
54
- opacity: 1,
55
- transform: `scale(1)`,
56
- })),
57
- transition('void => *', [
58
- style({
59
- opacity: 0,
60
- transform: `scale(0)`,
61
- }),
62
- animate(`200ms cubic-bezier(0.25, 0.8, 0.25, 1)`),
63
- ]),
64
- transition('* => void', [animate('50ms 100ms linear', style({ opacity: 0 }))]),
65
- ]);
66
-
159
+ /** Injection token to be used to override the default options for `mtx-popover`. */
160
+ const MTX_POPOVER_DEFAULT_OPTIONS = new InjectionToken('mtx-popover-default-options', {
161
+ providedIn: 'root',
162
+ factory: MTX_POPOVER_DEFAULT_OPTIONS_FACTORY,
163
+ });
164
+ /** @docs-private */
165
+ function MTX_POPOVER_DEFAULT_OPTIONS_FACTORY() {
166
+ return {
167
+ backdropClass: 'cdk-overlay-transparent-backdrop',
168
+ };
169
+ }
170
+ let popoverPanelUid = 0;
67
171
  class MtxPopover {
68
- constructor(_dir, _elementRef, zone) {
69
- this._dir = _dir;
172
+ constructor(_elementRef, _ngZone, _defaultOptions) {
173
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
70
174
  this._elementRef = _elementRef;
71
- this.zone = zone;
72
- this.role = 'dialog';
73
- /** Settings for popover, view setters and getters for more detail */
74
- this._position = ['below', 'after'];
75
- this._triggerEvent = 'hover';
76
- this._scrollStrategy = 'reposition';
77
- this._enterDelay = 100;
78
- this._leaveDelay = 100;
79
- this._panelOffsetX = 0;
80
- this._panelOffsetY = 0;
81
- this._closeOnPanelClick = false;
82
- this._closeOnBackdropClick = true;
83
- this._disableAnimation = false;
84
- this._focusTrapEnabled = true;
85
- this._focusTrapAutoCaptureEnabled = true;
86
- this._arrowOffsetX = 20;
87
- this._arrowOffsetY = 20;
88
- this._arrowWidth = 16;
89
- this._arrowHeight = 16;
90
- /** Config object to be passed into the popover's ngClass */
175
+ this._ngZone = _ngZone;
176
+ this._defaultOptions = _defaultOptions;
177
+ this._triggerEvent = (_a = this._defaultOptions.triggerEvent) !== null && _a !== void 0 ? _a : 'hover';
178
+ this._enterDelay = (_b = this._defaultOptions.enterDelay) !== null && _b !== void 0 ? _b : 100;
179
+ this._leaveDelay = (_c = this._defaultOptions.leaveDelay) !== null && _c !== void 0 ? _c : 100;
180
+ this._position = (_d = this._defaultOptions.position) !== null && _d !== void 0 ? _d : ['below', 'after'];
181
+ this._panelOffsetX = (_e = this._defaultOptions.xOffset) !== null && _e !== void 0 ? _e : 0;
182
+ this._panelOffsetY = (_f = this._defaultOptions.yOffset) !== null && _f !== void 0 ? _f : 0;
183
+ this._arrowWidth = (_g = this._defaultOptions.arrowWidth) !== null && _g !== void 0 ? _g : 16;
184
+ this._arrowHeight = (_h = this._defaultOptions.arrowHeight) !== null && _h !== void 0 ? _h : 16;
185
+ this._arrowOffsetX = (_j = this._defaultOptions.arrowOffsetX) !== null && _j !== void 0 ? _j : 20;
186
+ this._arrowOffsetY = (_k = this._defaultOptions.arrowOffsetY) !== null && _k !== void 0 ? _k : 20;
187
+ this._closeOnPanelClick = (_l = this._defaultOptions.closeOnPanelClick) !== null && _l !== void 0 ? _l : false;
188
+ this._closeOnBackdropClick = (_m = this._defaultOptions.closeOnBackdropClick) !== null && _m !== void 0 ? _m : true;
189
+ this._focusTrapEnabled = (_o = this._defaultOptions.focusTrapEnabled) !== null && _o !== void 0 ? _o : false;
190
+ this._focusTrapAutoCaptureEnabled = (_p = this._defaultOptions.focusTrapAutoCaptureEnabled) !== null && _p !== void 0 ? _p : false;
191
+ this._hasBackdrop = this._defaultOptions.hasBackdrop;
192
+ this._elevation = (_q = this._defaultOptions.elevation) !== null && _q !== void 0 ? _q : 8;
193
+ this._elevationPrefix = 'mat-elevation-z';
194
+ /** Config object to be passed into the popover's ngClass. */
91
195
  this._classList = {};
92
- /** Whether popover's `targetElement` is defined */
93
- this.containerPositioning = false;
196
+ /** Current state of the panel animation. */
197
+ this._panelAnimationState = 'void';
198
+ /** Emits whenever an animation on the popover completes. */
199
+ this._animationDone = new Subject();
200
+ /** Whether the popover is animating. */
201
+ this._isAnimating = false;
94
202
  /** Closing disabled on popover */
95
203
  this.closeDisabled = false;
96
- /** Emits the current animation state whenever it changes. */
97
- this._onAnimationStateChange = new EventEmitter();
204
+ /** Config object to be passed into the popover's arrow ngStyle */
205
+ this.arrowStyles = null;
206
+ /** Class or list of classes to be added to the overlay panel. */
207
+ this.overlayPanelClass = this._defaultOptions.overlayPanelClass || '';
208
+ /** Class to be added to the backdrop element. */
209
+ this.backdropClass = this._defaultOptions.backdropClass;
98
210
  /** Event emitted when the popover is closed. */
99
211
  this.closed = new EventEmitter();
100
- this.setPositionClasses();
101
- }
102
- /** Position of the popover. */
103
- get position() {
104
- return this._position;
105
- }
106
- set position(value) {
107
- if (!['before', 'after', 'above', 'below'].includes(value[0])) {
108
- throwMtxPopoverInvalidPositionStart();
109
- }
110
- if (!['before', 'after', 'above', 'below', 'center'].includes(value[1])) {
111
- throwMtxPopoverInvalidPositionEnd();
112
- }
113
- this._position = value;
114
- this.setPositionClasses();
212
+ this.panelId = `mtx-popover-panel-${popoverPanelUid++}`;
115
213
  }
116
- /** Popover trigger event */
214
+ /** Popover's trigger event. */
117
215
  get triggerEvent() {
118
216
  return this._triggerEvent;
119
217
  }
120
218
  set triggerEvent(value) {
121
219
  this._triggerEvent = value;
122
220
  }
123
- /** Popover scroll strategy */
124
- get scrollStrategy() {
125
- return this._scrollStrategy;
126
- }
127
- set scrollStrategy(value) {
128
- this._scrollStrategy = value;
129
- }
130
- /** Popover enter delay */
221
+ /** Popover's enter delay. */
131
222
  get enterDelay() {
132
223
  return this._enterDelay;
133
224
  }
134
225
  set enterDelay(value) {
135
226
  this._enterDelay = value;
136
227
  }
137
- /** Popover leave delay */
228
+ /** Popover's leave delay. */
138
229
  get leaveDelay() {
139
230
  return this._leaveDelay;
140
231
  }
141
232
  set leaveDelay(value) {
142
233
  this._leaveDelay = value;
143
234
  }
144
- /** Popover target offset x */
235
+ /** Popover's position. */
236
+ get position() {
237
+ return this._position;
238
+ }
239
+ set position(value) {
240
+ if (!['before', 'after', 'above', 'below'].includes(value[0])) {
241
+ throwMtxPopoverInvalidPositionStart();
242
+ }
243
+ if (!['before', 'after', 'above', 'below', 'center'].includes(value[1])) {
244
+ throwMtxPopoverInvalidPositionEnd();
245
+ }
246
+ this._position = value;
247
+ this.setPositionClasses();
248
+ }
249
+ /** Popover-panel's X offset. */
145
250
  get xOffset() {
146
251
  return this._panelOffsetX;
147
252
  }
148
253
  set xOffset(value) {
149
254
  this._panelOffsetX = value;
150
255
  }
151
- /** Popover target offset y */
256
+ /** Popover-panel's Y offset. */
152
257
  get yOffset() {
153
258
  return this._panelOffsetY;
154
259
  }
155
260
  set yOffset(value) {
156
261
  this._panelOffsetY = value;
157
262
  }
158
- /** Popover arrow offset x */
159
- get arrowOffsetX() {
160
- return this._arrowOffsetX;
161
- }
162
- set arrowOffsetX(value) {
163
- this._arrowOffsetX = value;
164
- }
165
- /** Popover arrow offset y */
166
- get arrowOffsetY() {
167
- return this._arrowOffsetY;
168
- }
169
- set arrowOffsetY(value) {
170
- this._arrowOffsetY = value;
171
- }
172
- /** Popover arrow width */
263
+ /** Popover-arrow's width. */
173
264
  get arrowWidth() {
174
265
  return this._arrowWidth;
175
266
  }
176
267
  set arrowWidth(value) {
177
268
  this._arrowWidth = value;
178
269
  }
179
- /** Popover arrow height */
270
+ /** Popover-arrow's height. */
180
271
  get arrowHeight() {
181
272
  return this._arrowHeight;
182
273
  }
183
274
  set arrowHeight(value) {
184
275
  this._arrowHeight = value;
185
276
  }
186
- /** Popover close on container click */
277
+ /** Popover-arrow's X offset. */
278
+ get arrowOffsetX() {
279
+ return this._arrowOffsetX;
280
+ }
281
+ set arrowOffsetX(value) {
282
+ this._arrowOffsetX = value;
283
+ }
284
+ /** Popover-arrow's Y offset. */
285
+ get arrowOffsetY() {
286
+ return this._arrowOffsetY;
287
+ }
288
+ set arrowOffsetY(value) {
289
+ this._arrowOffsetY = value;
290
+ }
291
+ /** Whether popover can be closed when click the popover-panel. */
187
292
  get closeOnPanelClick() {
188
293
  return this._closeOnPanelClick;
189
294
  }
190
295
  set closeOnPanelClick(value) {
191
296
  this._closeOnPanelClick = coerceBooleanProperty(value);
192
297
  }
193
- /** Popover close on backdrop click */
298
+ /** Whether popover can be closed when click the backdrop. */
194
299
  get closeOnBackdropClick() {
195
300
  return this._closeOnBackdropClick;
196
301
  }
197
302
  set closeOnBackdropClick(value) {
198
303
  this._closeOnBackdropClick = coerceBooleanProperty(value);
199
304
  }
200
- /** Disable animations of popover and all child elements */
201
- get disableAnimation() {
202
- return this._disableAnimation;
203
- }
204
- set disableAnimation(value) {
205
- this._disableAnimation = coerceBooleanProperty(value);
206
- }
207
- /** Popover focus trap using cdkTrapFocus */
305
+ /** Whether enable focus trap using `cdkTrapFocus`. */
208
306
  get focusTrapEnabled() {
209
307
  return this._focusTrapEnabled;
210
308
  }
211
309
  set focusTrapEnabled(value) {
212
310
  this._focusTrapEnabled = coerceBooleanProperty(value);
213
311
  }
214
- /** Popover focus trap auto capture using cdkTrapFocusAutoCapture */
312
+ /** Whether enable focus trap auto capture using `cdkTrapFocusAutoCapture`. */
215
313
  get focusTrapAutoCaptureEnabled() {
216
314
  return this._focusTrapAutoCaptureEnabled;
217
315
  }
218
316
  set focusTrapAutoCaptureEnabled(value) {
219
317
  this._focusTrapAutoCaptureEnabled = coerceBooleanProperty(value);
220
318
  }
319
+ /** Whether the popover has a backdrop. It will always be false if the trigger event is hover. */
320
+ get hasBackdrop() {
321
+ return this._hasBackdrop;
322
+ }
323
+ set hasBackdrop(value) {
324
+ this._hasBackdrop = coerceBooleanProperty(value);
325
+ }
326
+ /** Popover-panel's elevation (0~24). */
327
+ get elevation() {
328
+ return Math.max(0, Math.min(Math.round(this._elevation), 24));
329
+ }
330
+ set elevation(value) {
331
+ this._elevation = value;
332
+ }
221
333
  /**
222
334
  * This method takes classes set on the host md-popover element and applies them on the
223
- * popover template that displays in the overlay container. Otherwise, it's difficult
335
+ * popover template that displays in the overlay container. Otherwise, it's difficult
224
336
  * to style the containing popover from outside the component.
225
337
  * @param classes list of class names
226
338
  */
@@ -236,7 +348,7 @@ class MtxPopover {
236
348
  }
237
349
  /**
238
350
  * This method takes classes set on the host md-popover element and applies them on the
239
- * popover template that displays in the overlay container. Otherwise, it's difficult
351
+ * popover template that displays in the overlay container. Otherwise, it's difficult
240
352
  * to style the containing popover from outside the component.
241
353
  * @deprecated Use `panelClass` instead.
242
354
  * @breaking-change 8.0.0
@@ -247,47 +359,46 @@ class MtxPopover {
247
359
  set classList(classes) {
248
360
  this.panelClass = classes;
249
361
  }
362
+ ngOnInit() {
363
+ this.setPositionClasses();
364
+ }
250
365
  ngOnDestroy() {
251
- this._emitCloseEvent();
252
366
  this.closed.complete();
253
367
  }
254
368
  /** Handle a keyboard event from the popover, delegating to the appropriate action. */
255
369
  _handleKeydown(event) {
256
- switch (event.keyCode) {
370
+ const keyCode = event.keyCode;
371
+ switch (keyCode) {
257
372
  case ESCAPE:
258
- this._emitCloseEvent();
259
- return;
373
+ if (!hasModifierKey(event)) {
374
+ event.preventDefault();
375
+ this.closed.emit('keydown');
376
+ }
377
+ break;
260
378
  }
261
379
  }
262
- /**
263
- * This emits a close event to which the trigger is subscribed. When emitted, the
264
- * trigger will close the popover.
265
- */
266
- _emitCloseEvent() {
267
- this.closed.emit();
268
- }
269
- /** Close popover on click if closeOnPanelClick is true */
270
- onClick() {
380
+ /** Close popover on click if `closeOnPanelClick` is true. */
381
+ _handleClick() {
271
382
  if (this.closeOnPanelClick) {
272
- this._emitCloseEvent();
383
+ this.closed.emit('click');
273
384
  }
274
385
  }
275
- /** Disables close of popover when leaving trigger element and mouse over the popover */
276
- onMouseOver() {
386
+ /** Disables close of popover when leaving trigger element and mouse over the popover. */
387
+ _handleMouseOver() {
277
388
  if (this.triggerEvent === 'hover') {
278
389
  this.closeDisabled = true;
279
390
  }
280
391
  }
281
- /** Enables close of popover when mouse leaving popover element */
282
- onMouseLeave() {
392
+ /** Enables close of popover when mouse leaving popover element. */
393
+ _handleMouseLeave() {
283
394
  if (this.triggerEvent === 'hover') {
284
395
  setTimeout(() => {
285
396
  this.closeDisabled = false;
286
- this._emitCloseEvent();
397
+ this.closed.emit();
287
398
  }, this.leaveDelay);
288
399
  }
289
400
  }
290
- /** Sets the current styles for the popover to allow for dynamically changing settings */
401
+ /** Sets the current styles for the popover to allow for dynamically changing settings. */
291
402
  setCurrentStyles(pos = this.position) {
292
403
  const left = pos[1] === 'after'
293
404
  ? `${this.arrowOffsetX - this.arrowWidth / 2}px`
@@ -301,16 +412,13 @@ class MtxPopover {
301
412
  ? `calc(50% - ${this.arrowHeight / 2}px)`
302
413
  : '';
303
414
  const top = pos[1] === 'below' ? `${this.arrowOffsetY - this.arrowHeight / 2}px` : '';
304
- this.popoverArrowStyles =
415
+ this.arrowStyles =
305
416
  pos[0] === 'above' || pos[0] === 'below'
306
417
  ? {
307
- left: this._dir.value === 'ltr' ? left : right,
308
- right: this._dir.value === 'ltr' ? right : left,
418
+ left: this.direction === 'ltr' ? left : right,
419
+ right: this.direction === 'ltr' ? right : left,
309
420
  }
310
- : {
311
- top,
312
- bottom,
313
- };
421
+ : { top, bottom };
314
422
  }
315
423
  /**
316
424
  * It's necessary to set position-based classes to ensure the popover panel animation
@@ -330,17 +438,44 @@ class MtxPopover {
330
438
  this._classList['mtx-popover-below-center'] = pos[0] === 'below' && pos[1] === 'center';
331
439
  this._classList['mtx-popover-below-after'] = pos[0] === 'below' && pos[1] === 'after';
332
440
  }
441
+ /** Sets the popover-panel's elevation. */
442
+ setElevation() {
443
+ const newElevation = `${this._elevationPrefix}${this.elevation}`;
444
+ if (this._previousElevation) {
445
+ this._classList[this._previousElevation] = false;
446
+ }
447
+ this._classList[newElevation] = true;
448
+ this._previousElevation = newElevation;
449
+ }
450
+ /** Starts the enter animation. */
451
+ _startAnimation() {
452
+ // @breaking-change 8.0.0 Combine with _resetAnimation.
453
+ this._panelAnimationState = 'enter';
454
+ }
455
+ /** Resets the panel animation to its initial state. */
456
+ _resetAnimation() {
457
+ // @breaking-change 8.0.0 Combine with _startAnimation.
458
+ this._panelAnimationState = 'void';
459
+ }
460
+ /** Callback that is invoked when the panel animation completes. */
461
+ _onAnimationDone(event) {
462
+ this._animationDone.next(event);
463
+ this._isAnimating = false;
464
+ }
465
+ _onAnimationStart(event) {
466
+ this._isAnimating = true;
467
+ }
333
468
  }
334
469
  /** @type {!Array<{type: !Function, args: (undefined|!Array<?>)}>} */
335
470
  MtxPopover.decorators = [
336
471
  { type: Component, args: [{
337
472
  selector: 'mtx-popover',
338
- template: "<ng-template>\r\n <div class=\"mtx-popover-panel mat-elevation-z8\" role=\"dialog\"\r\n [ngClass]=\"_classList\"\r\n [ngStyle]=\"popoverPanelStyles\"\r\n (keydown)=\"_handleKeydown($event)\"\r\n (click)=\"onClick()\"\r\n (mouseover)=\"onMouseOver()\"\r\n (mouseleave)=\"onMouseLeave()\"\r\n [@.disabled]=\"disableAnimation\"\r\n [@transformPopover]=\"'enter'\">\r\n <div class=\"mtx-popover-direction-arrow\" [ngStyle]=\"popoverArrowStyles\"></div>\r\n <div class=\"mtx-popover-content\"\r\n [ngStyle]=\"popoverContentStyles\"\r\n [cdkTrapFocus]=\"focusTrapEnabled\"\r\n [cdkTrapFocusAutoCapture]=\"focusTrapAutoCaptureEnabled\">\r\n <ng-content></ng-content>\r\n </div>\r\n </div>\r\n</ng-template>\r\n",
473
+ template: "<ng-template>\r\n <div class=\"mtx-popover-panel\"\r\n [id]=\"panelId\"\r\n [ngClass]=\"_classList\"\r\n (keydown)=\"_handleKeydown($event)\"\r\n (click)=\"_handleClick()\"\r\n (mouseover)=\"_handleMouseOver()\"\r\n (mouseleave)=\"_handleMouseLeave()\"\r\n [@transformPopover]=\"_panelAnimationState\"\r\n (@transformPopover.start)=\"_onAnimationStart($event)\"\r\n (@transformPopover.done)=\"_onAnimationDone($event)\"\r\n tabindex=\"-1\"\r\n role=\"dialog\"\r\n [attr.aria-label]=\"ariaLabel || null\"\r\n [attr.aria-labelledby]=\"ariaLabelledby || null\"\r\n [attr.aria-describedby]=\"ariaDescribedby || null\"\r\n [cdkTrapFocus]=\"focusTrapEnabled\"\r\n [cdkTrapFocusAutoCapture]=\"focusTrapAutoCaptureEnabled\">\r\n <div class=\"mtx-popover-content\">\r\n <ng-content></ng-content>\r\n </div>\r\n <div class=\"mtx-popover-direction-arrow\" [ngStyle]=\"arrowStyles\"></div>\r\n </div>\r\n</ng-template>\r\n",
339
474
  changeDetection: ChangeDetectionStrategy.OnPush,
340
475
  encapsulation: ViewEncapsulation.None,
341
476
  animations: [transformPopover],
342
477
  exportAs: 'mtxPopover',
343
- styles: [".mtx-popover-panel{max-height:calc(100vh - 48px);padding:8px;border-radius:4px;font-size:16px}.mtx-popover-panel[class*=mtx-popover-below]{margin-top:calc(.5em + 2px)}.mtx-popover-panel[class*=mtx-popover-above]{margin-bottom:calc(.5em + 2px)}.mtx-popover-panel[class*=mtx-popover-before]{margin-right:calc(.5em + 2px)}[dir=rtl] .mtx-popover-panel[class*=mtx-popover-before]{margin-right:auto;margin-left:calc(.5em + 2px)}.mtx-popover-panel[class*=mtx-popover-after]{margin-left:calc(.5em + 2px)}[dir=rtl] .mtx-popover-panel[class*=mtx-popover-after]{margin-left:auto;margin-right:calc(.5em + 2px)}.mtx-popover-direction-arrow{position:absolute}.mtx-popover-direction-arrow:before,.mtx-popover-direction-arrow:after{position:absolute;display:inline-block;content:\"\";border-width:.5em;border-style:solid}.mtx-popover-direction-arrow:after{border-width:calc(.5em - 1px)}[class*=mtx-popover-below] .mtx-popover-direction-arrow,[class*=mtx-popover-above] .mtx-popover-direction-arrow{width:1em}[class*=mtx-popover-below] .mtx-popover-direction-arrow:after,[class*=mtx-popover-above] .mtx-popover-direction-arrow:after{left:1px}[dir=rtl] [class*=mtx-popover-below] .mtx-popover-direction-arrow:after,[dir=rtl] [class*=mtx-popover-above] .mtx-popover-direction-arrow:after{right:1px;left:auto}[class*=mtx-popover-below] .mtx-popover-direction-arrow{top:0}[class*=mtx-popover-below] .mtx-popover-direction-arrow:before,[class*=mtx-popover-below] .mtx-popover-direction-arrow:after{bottom:0;border-top-width:0}[class*=mtx-popover-above] .mtx-popover-direction-arrow{bottom:0}[class*=mtx-popover-above] .mtx-popover-direction-arrow:before,[class*=mtx-popover-above] .mtx-popover-direction-arrow:after{top:0;border-bottom-width:0}[class*=mtx-popover-before] .mtx-popover-direction-arrow,[class*=mtx-popover-after] .mtx-popover-direction-arrow{height:1em}[class*=mtx-popover-before] .mtx-popover-direction-arrow:after,[class*=mtx-popover-after] .mtx-popover-direction-arrow:after{top:1px}[class*=mtx-popover-before] .mtx-popover-direction-arrow{right:0}[class*=mtx-popover-before] .mtx-popover-direction-arrow:before,[class*=mtx-popover-before] .mtx-popover-direction-arrow:after{left:0;border-right-width:0}[dir=rtl] [class*=mtx-popover-before] .mtx-popover-direction-arrow{right:auto;left:0}[dir=rtl] [class*=mtx-popover-before] .mtx-popover-direction-arrow:before,[dir=rtl] [class*=mtx-popover-before] .mtx-popover-direction-arrow:after{left:auto;right:0;border-left-width:0}[dir=rtl] [class*=mtx-popover-before] .mtx-popover-direction-arrow:before{border-right-width:.5em}[dir=rtl] [class*=mtx-popover-before] .mtx-popover-direction-arrow:after{border-right-width:calc(.5em - 1px)}[class*=mtx-popover-after] .mtx-popover-direction-arrow{left:0}[class*=mtx-popover-after] .mtx-popover-direction-arrow:before,[class*=mtx-popover-after] .mtx-popover-direction-arrow:after{right:0;border-left-width:0}[dir=rtl] [class*=mtx-popover-after] .mtx-popover-direction-arrow{left:auto;right:0}[dir=rtl] [class*=mtx-popover-after] .mtx-popover-direction-arrow:before,[dir=rtl] [class*=mtx-popover-after] .mtx-popover-direction-arrow:after{right:auto;left:0;border-right-width:0}[dir=rtl] [class*=mtx-popover-after] .mtx-popover-direction-arrow:before{border-left-width:.5em}[dir=rtl] [class*=mtx-popover-after] .mtx-popover-direction-arrow:after{border-left-width:calc(.5em - 1px)}\n"]
478
+ styles: [".mtx-popover-panel{position:relative;max-height:calc(100vh - 48px);padding:8px;border-radius:4px;font-size:16px;outline:0}.mtx-popover-panel[class*=mtx-popover-below]{margin-top:calc(.5em + 2px)}.mtx-popover-panel[class*=mtx-popover-above]{margin-bottom:calc(.5em + 2px)}.mtx-popover-panel[class*=mtx-popover-before]{margin-right:calc(.5em + 2px)}[dir=rtl] .mtx-popover-panel[class*=mtx-popover-before]{margin-right:auto;margin-left:calc(.5em + 2px)}.mtx-popover-panel[class*=mtx-popover-after]{margin-left:calc(.5em + 2px)}[dir=rtl] .mtx-popover-panel[class*=mtx-popover-after]{margin-left:auto;margin-right:calc(.5em + 2px)}.mtx-popover-direction-arrow{position:absolute}.mtx-popover-direction-arrow:before,.mtx-popover-direction-arrow:after{position:absolute;display:inline-block;content:\"\";border-width:.5em;border-style:solid}.mtx-popover-direction-arrow:after{border-width:calc(.5em - 1px)}[class*=mtx-popover-below] .mtx-popover-direction-arrow,[class*=mtx-popover-above] .mtx-popover-direction-arrow{width:1em}[class*=mtx-popover-below] .mtx-popover-direction-arrow:after,[class*=mtx-popover-above] .mtx-popover-direction-arrow:after{left:1px}[dir=rtl] [class*=mtx-popover-below] .mtx-popover-direction-arrow:after,[dir=rtl] [class*=mtx-popover-above] .mtx-popover-direction-arrow:after{right:1px;left:auto}[class*=mtx-popover-below] .mtx-popover-direction-arrow{top:0}[class*=mtx-popover-below] .mtx-popover-direction-arrow:before,[class*=mtx-popover-below] .mtx-popover-direction-arrow:after{bottom:0;border-top-width:0}[class*=mtx-popover-above] .mtx-popover-direction-arrow{bottom:0}[class*=mtx-popover-above] .mtx-popover-direction-arrow:before,[class*=mtx-popover-above] .mtx-popover-direction-arrow:after{top:0;border-bottom-width:0}[class*=mtx-popover-before] .mtx-popover-direction-arrow,[class*=mtx-popover-after] .mtx-popover-direction-arrow{height:1em}[class*=mtx-popover-before] .mtx-popover-direction-arrow:after,[class*=mtx-popover-after] .mtx-popover-direction-arrow:after{top:1px}[class*=mtx-popover-before] .mtx-popover-direction-arrow{right:0}[class*=mtx-popover-before] .mtx-popover-direction-arrow:before,[class*=mtx-popover-before] .mtx-popover-direction-arrow:after{left:0;border-right-width:0}[dir=rtl] [class*=mtx-popover-before] .mtx-popover-direction-arrow{right:auto;left:0}[dir=rtl] [class*=mtx-popover-before] .mtx-popover-direction-arrow:before,[dir=rtl] [class*=mtx-popover-before] .mtx-popover-direction-arrow:after{left:auto;right:0;border-left-width:0}[dir=rtl] [class*=mtx-popover-before] .mtx-popover-direction-arrow:before{border-right-width:.5em}[dir=rtl] [class*=mtx-popover-before] .mtx-popover-direction-arrow:after{border-right-width:calc(.5em - 1px)}[class*=mtx-popover-after] .mtx-popover-direction-arrow{left:0}[class*=mtx-popover-after] .mtx-popover-direction-arrow:before,[class*=mtx-popover-after] .mtx-popover-direction-arrow:after{right:0;border-left-width:0}[dir=rtl] [class*=mtx-popover-after] .mtx-popover-direction-arrow{left:auto;right:0}[dir=rtl] [class*=mtx-popover-after] .mtx-popover-direction-arrow:before,[dir=rtl] [class*=mtx-popover-after] .mtx-popover-direction-arrow:after{right:auto;left:0;border-right-width:0}[dir=rtl] [class*=mtx-popover-after] .mtx-popover-direction-arrow:before{border-left-width:.5em}[dir=rtl] [class*=mtx-popover-after] .mtx-popover-direction-arrow:after{border-left-width:calc(.5em - 1px)}\n"]
344
479
  },] }
345
480
  ];
346
481
  /**
@@ -351,68 +486,129 @@ MtxPopover.decorators = [
351
486
  * @nocollapse
352
487
  */
353
488
  MtxPopover.ctorParameters = () => [
354
- { type: Directionality, decorators: [{ type: Optional }] },
355
489
  { type: ElementRef },
356
- { type: NgZone }
490
+ { type: NgZone },
491
+ { type: undefined, decorators: [{ type: Inject, args: [MTX_POPOVER_DEFAULT_OPTIONS,] }] }
357
492
  ];
358
493
  /** @type {!Object<string, !Array<{type: !Function, args: (undefined|!Array<?>)}>>} */
359
494
  MtxPopover.propDecorators = {
360
- role: [{ type: HostBinding, args: ['attr.role',] }],
361
- position: [{ type: Input }],
495
+ backdropClass: [{ type: Input }],
496
+ ariaLabel: [{ type: Input, args: ['aria-label',] }],
497
+ ariaLabelledby: [{ type: Input, args: ['aria-labelledby',] }],
498
+ ariaDescribedby: [{ type: Input, args: ['aria-describedby',] }],
362
499
  triggerEvent: [{ type: Input }],
363
- scrollStrategy: [{ type: Input }],
364
500
  enterDelay: [{ type: Input }],
365
501
  leaveDelay: [{ type: Input }],
502
+ position: [{ type: Input }],
366
503
  xOffset: [{ type: Input }],
367
504
  yOffset: [{ type: Input }],
368
- arrowOffsetX: [{ type: Input }],
369
- arrowOffsetY: [{ type: Input }],
370
505
  arrowWidth: [{ type: Input }],
371
506
  arrowHeight: [{ type: Input }],
507
+ arrowOffsetX: [{ type: Input }],
508
+ arrowOffsetY: [{ type: Input }],
372
509
  closeOnPanelClick: [{ type: Input }],
373
510
  closeOnBackdropClick: [{ type: Input }],
374
- disableAnimation: [{ type: Input }],
375
511
  focusTrapEnabled: [{ type: Input }],
376
512
  focusTrapAutoCaptureEnabled: [{ type: Input }],
513
+ hasBackdrop: [{ type: Input }],
514
+ elevation: [{ type: Input }],
377
515
  panelClass: [{ type: Input, args: ['class',] }],
378
516
  classList: [{ type: Input }],
379
517
  closed: [{ type: Output }],
380
- templateRef: [{ type: ViewChild, args: [TemplateRef,] }]
518
+ templateRef: [{ type: ViewChild, args: [TemplateRef,] }],
519
+ lazyContent: [{ type: ContentChild, args: [MTX_POPOVER_CONTENT,] }]
381
520
  };
382
521
 
522
+ class MtxPopoverTarget {
523
+ constructor(elementRef) {
524
+ this.elementRef = elementRef;
525
+ }
526
+ }
527
+ /** @type {!Array<{type: !Function, args: (undefined|!Array<?>)}>} */
528
+ MtxPopoverTarget.decorators = [
529
+ { type: Directive, args: [{
530
+ selector: 'mtx-popover-target, [mtxPopoverTarget]',
531
+ exportAs: 'mtxPopoverTarget',
532
+ },] }
533
+ ];
534
+ /**
535
+ * @type {function(): !Array<(null|{
536
+ * type: ?,
537
+ * decorators: (undefined|!Array<{type: !Function, args: (undefined|!Array<?>)}>),
538
+ * })>}
539
+ * @nocollapse
540
+ */
541
+ MtxPopoverTarget.ctorParameters = () => [
542
+ { type: ElementRef }
543
+ ];
544
+
545
+ /** Injection token that determines the scroll handling while the popover is open. */
546
+ const MTX_POPOVER_SCROLL_STRATEGY = new InjectionToken('mtx-popover-scroll-strategy');
547
+ /** @docs-private */
548
+ function MTX_POPOVER_SCROLL_STRATEGY_FACTORY(overlay) {
549
+ return () => overlay.scrollStrategies.reposition();
550
+ }
551
+ /** @docs-private */
552
+ const MTX_POPOVER_SCROLL_STRATEGY_FACTORY_PROVIDER = {
553
+ provide: MTX_POPOVER_SCROLL_STRATEGY,
554
+ deps: [Overlay],
555
+ useFactory: MTX_POPOVER_SCROLL_STRATEGY_FACTORY,
556
+ };
383
557
  /**
384
- * This directive is intended to be used in conjunction with an mtx-popover tag. It is
558
+ * This directive is intended to be used in conjunction with an `mtx-popover` tag. It is
385
559
  * responsible for toggling the display of the provided popover instance.
386
560
  */
387
561
  class MtxPopoverTrigger {
388
- constructor(_overlay, _elementRef, _viewContainerRef, _dir, _changeDetectorRef) {
562
+ constructor(_overlay, _elementRef, _viewContainerRef, scrollStrategy, _dir, _changeDetectorRef, _focusMonitor) {
389
563
  this._overlay = _overlay;
390
564
  this._elementRef = _elementRef;
391
565
  this._viewContainerRef = _viewContainerRef;
392
566
  this._dir = _dir;
393
567
  this._changeDetectorRef = _changeDetectorRef;
394
- this.ariaHaspopup = true;
395
- this.popoverOpened$ = new Subject();
396
- this.popoverClosed$ = new Subject();
568
+ this._focusMonitor = _focusMonitor;
397
569
  this._overlayRef = null;
398
570
  this._popoverOpen = false;
399
571
  this._halt = false;
400
- // tracking input type is necessary so it's possible to only auto-focus
572
+ this._positionSubscription = Subscription.EMPTY;
573
+ this._popoverCloseSubscription = Subscription.EMPTY;
574
+ this._closingActionsSubscription = Subscription.EMPTY;
575
+ // Tracking input type is necessary so it's possible to only auto-focus
401
576
  // the first item of the list when the popover is opened via the keyboard
402
- this._openedByMouse = false;
403
- this._onDestroy = new Subject();
577
+ this._openedBy = undefined;
404
578
  /** Event emitted when the associated popover is opened. */
405
579
  this.popoverOpened = new EventEmitter();
406
580
  /** Event emitted when the associated popover is closed. */
407
581
  this.popoverClosed = new EventEmitter();
582
+ this._scrollStrategy = scrollStrategy;
408
583
  }
409
- ngAfterViewInit() {
584
+ /** References the popover instance that the trigger is associated with. */
585
+ get popover() {
586
+ return this._popover;
587
+ }
588
+ set popover(popover) {
589
+ if (popover === this._popover) {
590
+ return;
591
+ }
592
+ this._popover = popover;
593
+ this._popoverCloseSubscription.unsubscribe();
594
+ if (popover) {
595
+ this._popoverCloseSubscription = popover.closed.subscribe((reason) => {
596
+ this._destroyPopover();
597
+ });
598
+ }
599
+ }
600
+ ngAfterContentInit() {
410
601
  this._checkPopover();
411
602
  this._setCurrentConfig();
412
- this.popover.closed.subscribe(() => this.closePopover());
413
603
  }
414
604
  ngOnDestroy() {
415
- this.destroyPopover();
605
+ if (this._overlayRef) {
606
+ this._overlayRef.dispose();
607
+ this._overlayRef = null;
608
+ }
609
+ this._positionSubscription.unsubscribe();
610
+ this._popoverCloseSubscription.unsubscribe();
611
+ this._closingActionsSubscription.unsubscribe();
416
612
  }
417
613
  _setCurrentConfig() {
418
614
  if (this.triggerEvent) {
@@ -424,12 +620,18 @@ class MtxPopoverTrigger {
424
620
  get popoverOpen() {
425
621
  return this._popoverOpen;
426
622
  }
427
- onClick(event) {
623
+ /** The text direction of the containing app. */
624
+ get dir() {
625
+ return this._dir && this._dir.value === 'rtl' ? 'rtl' : 'ltr';
626
+ }
627
+ /** Handles mouse click on the trigger. */
628
+ _handleClick(event) {
428
629
  if (this.popover.triggerEvent === 'click') {
429
630
  this.togglePopover();
430
631
  }
431
632
  }
432
- onMouseEnter(event) {
633
+ /** Handles mouse enter on the trigger. */
634
+ _handleMouseEnter(event) {
433
635
  this._halt = false;
434
636
  if (this.popover.triggerEvent === 'hover') {
435
637
  this._mouseoverTimer = setTimeout(() => {
@@ -437,7 +639,8 @@ class MtxPopoverTrigger {
437
639
  }, this.popover.enterDelay);
438
640
  }
439
641
  }
440
- onMouseLeave(event) {
642
+ /** Handles mouse leave on the trigger. */
643
+ _handleMouseLeave(event) {
441
644
  if (this.popover.triggerEvent === 'hover') {
442
645
  if (this._mouseoverTimer) {
443
646
  clearTimeout(this._mouseoverTimer);
@@ -455,117 +658,120 @@ class MtxPopoverTrigger {
455
658
  }
456
659
  }
457
660
  }
661
+ /** Handles mouse presses on the trigger. */
662
+ _handleMousedown(event) {
663
+ if (!isFakeMousedownFromScreenReader(event)) {
664
+ // Since right or middle button clicks won't trigger the `click` event,
665
+ // we shouldn't consider the popover as opened by mouse in those cases.
666
+ this._openedBy = event.button === 0 ? 'mouse' : undefined;
667
+ }
668
+ }
669
+ /** Handles key presses on the trigger. */
670
+ _handleKeydown(event) {
671
+ const keyCode = event.keyCode;
672
+ // Pressing enter on the trigger will trigger the click handler later.
673
+ if (keyCode === ENTER || keyCode === SPACE) {
674
+ this._openedBy = 'keyboard';
675
+ }
676
+ }
458
677
  /** Toggles the popover between the open and closed states. */
459
678
  togglePopover() {
460
679
  return this._popoverOpen ? this.closePopover() : this.openPopover();
461
680
  }
462
681
  /** Opens the popover. */
463
682
  openPopover() {
464
- if (!this._popoverOpen && !this._halt) {
465
- this._createOverlay().attach(this._portal);
466
- this._subscribeToBackdrop();
467
- this._subscribeToDetachments();
468
- this._initPopover();
683
+ var _a;
684
+ if (this._popoverOpen || this._halt) {
685
+ return;
686
+ }
687
+ this._checkPopover();
688
+ const overlayRef = this._createOverlay();
689
+ const overlayConfig = overlayRef.getConfig();
690
+ this._setPosition(overlayConfig.positionStrategy);
691
+ if (this.popover.triggerEvent === 'click') {
692
+ overlayConfig.hasBackdrop = (_a = this.popover.hasBackdrop) !== null && _a !== void 0 ? _a : true;
693
+ }
694
+ overlayRef.attach(this._getPortal());
695
+ if (this.popover.lazyContent) {
696
+ this.popover.lazyContent.attach(this.popoverData);
697
+ }
698
+ this._closingActionsSubscription = this._popoverClosingActions().subscribe(() => this.closePopover());
699
+ this._initPopover();
700
+ if (this.popover instanceof MtxPopover) {
701
+ this.popover._startAnimation();
469
702
  }
470
703
  }
471
704
  /** Closes the popover. */
472
705
  closePopover() {
473
- if (this._overlayRef) {
474
- this._overlayRef.detach();
475
- this._resetPopover();
706
+ this.popover.closed.emit();
707
+ }
708
+ /**
709
+ * Focuses the popover trigger.
710
+ * @param origin Source of the popover trigger's focus.
711
+ */
712
+ focus(origin, options) {
713
+ if (this._focusMonitor && origin) {
714
+ this._focusMonitor.focusVia(this._elementRef, origin, options);
715
+ }
716
+ else {
717
+ this._elementRef.nativeElement.focus(options);
476
718
  }
477
- this.destroyPopover();
478
719
  }
479
720
  /** Removes the popover from the DOM. */
480
- destroyPopover() {
721
+ _destroyPopover(reason) {
722
+ if (!this._overlayRef || !this.popoverOpen) {
723
+ return;
724
+ }
725
+ // Clear the timeout for hover event.
481
726
  if (this._mouseoverTimer) {
482
727
  clearTimeout(this._mouseoverTimer);
483
728
  this._mouseoverTimer = null;
484
729
  }
485
- if (this._overlayRef) {
486
- this._overlayRef.dispose();
487
- this._overlayRef = null;
488
- this._cleanUpSubscriptions();
489
- }
490
- this._onDestroy.next();
491
- this._onDestroy.complete();
492
- }
493
- /** Focuses the popover trigger. */
494
- focus() {
495
- this._elementRef.nativeElement.focus();
496
- }
497
- /** The text direction of the containing app. */
498
- get dir() {
499
- return this._dir && this._dir.value === 'rtl' ? 'rtl' : 'ltr';
500
- }
501
- /**
502
- * This method ensures that the popover closes when the overlay backdrop is clicked.
503
- * We do not use first() here because doing so would not catch clicks from within
504
- * the popover, and it would fail to unsubscribe properly. Instead, we unsubscribe
505
- * explicitly when the popover is closed or destroyed.
506
- */
507
- _subscribeToBackdrop() {
508
- if (this._overlayRef) {
509
- /** Only subscribe to backdrop if trigger event is click */
510
- if (this.triggerEvent === 'click' && this.popover.closeOnBackdropClick === true) {
511
- this._overlayRef
512
- .backdropClick()
513
- .pipe(takeUntil(this.popoverClosed$), takeUntil(this._onDestroy))
514
- .subscribe(() => {
515
- this.popover._emitCloseEvent();
730
+ const popover = this.popover;
731
+ this._closingActionsSubscription.unsubscribe();
732
+ this._overlayRef.detach();
733
+ this._openedBy = undefined;
734
+ if (popover instanceof MtxPopover) {
735
+ popover._resetAnimation();
736
+ if (popover.lazyContent) {
737
+ // Wait for the exit animation to finish before detaching the content.
738
+ popover._animationDone
739
+ .pipe(filter(event => event.toState === 'void'), take(1),
740
+ // Interrupt if the content got re-attached.
741
+ takeUntil(popover.lazyContent._attached))
742
+ .subscribe({
743
+ next: () => popover.lazyContent.detach(),
744
+ // No matter whether the content got re-attached, reset the popover.
745
+ complete: () => this._setIsPopoverOpen(false),
516
746
  });
517
747
  }
748
+ else {
749
+ this._setIsPopoverOpen(false);
750
+ }
518
751
  }
519
- }
520
- _subscribeToDetachments() {
521
- if (this._overlayRef) {
522
- this._overlayRef
523
- .detachments()
524
- .pipe(takeUntil(this.popoverClosed$), takeUntil(this._onDestroy))
525
- .subscribe(() => {
526
- this._setPopoverClosed();
527
- });
752
+ else {
753
+ this._setIsPopoverOpen(false);
754
+ if (popover.lazyContent) {
755
+ popover.lazyContent.detach();
756
+ }
528
757
  }
529
758
  }
530
759
  /**
531
- * This method sets the popover state to open and focuses the first item if
532
- * the popover was opened via the keyboard.
760
+ * This method sets the popover state to open.
533
761
  */
534
762
  _initPopover() {
535
- this._setPopoverOpened();
763
+ this.popover.direction = this.dir;
764
+ this.popover.setElevation();
765
+ this._setIsPopoverOpen(true);
536
766
  }
537
- /**
538
- * This method resets the popover when it's closed, most importantly restoring
539
- * focus to the popover trigger if the popover was opened via the keyboard.
540
- */
541
- _resetPopover() {
542
- this._setPopoverClosed();
543
- // Focus only needs to be reset to the host element if the popover was opened
544
- // by the keyboard and manually shifted to the first popover item.
545
- if (!this._openedByMouse) {
546
- this.focus();
547
- }
548
- this._openedByMouse = false;
549
- }
550
- /** set state rather than toggle to support triggers sharing a popover */
551
- _setPopoverOpened() {
552
- if (!this._popoverOpen) {
553
- this._popoverOpen = true;
554
- this.popoverOpened$.next();
555
- this.popoverOpened.emit();
556
- }
557
- }
558
- /** set state rather than toggle to support triggers sharing a popover */
559
- _setPopoverClosed() {
560
- if (this._popoverOpen) {
561
- this._popoverOpen = false;
562
- this.popoverClosed$.next();
563
- this.popoverClosed.emit();
564
- }
767
+ // set state rather than toggle to support triggers sharing a popover
768
+ _setIsPopoverOpen(isOpen) {
769
+ this._popoverOpen = isOpen;
770
+ this._popoverOpen ? this.popoverOpened.emit() : this.popoverClosed.emit();
565
771
  }
566
772
  /**
567
- * This method checks that a valid instance of MdPopover has been passed into
568
- * mdPopoverTriggerFor. If not, an exception is thrown.
773
+ * This method checks that a valid instance of MdPopover has been passed into
774
+ * `mtxPopoverTriggerFor`. If not, an exception is thrown.
569
775
  */
570
776
  _checkPopover() {
571
777
  if (!this.popover) {
@@ -573,16 +779,20 @@ class MtxPopoverTrigger {
573
779
  }
574
780
  }
575
781
  /**
576
- * This method creates the overlay from the provided popover's template and saves its
577
- * OverlayRef so that it can be attached to the DOM when openPopover is called.
782
+ * This method creates the overlay from the provided popover's template and saves its
783
+ * OverlayRef so that it can be attached to the DOM when openPopover is called.
578
784
  */
579
785
  _createOverlay() {
580
786
  if (!this._overlayRef) {
581
- this._portal = new TemplatePortal(this.popover.templateRef, this._viewContainerRef);
582
787
  const config = this._getOverlayConfig();
583
788
  this._subscribeToPositions(config.positionStrategy);
584
789
  this._overlayRef = this._overlay.create(config);
585
790
  }
791
+ else {
792
+ const overlayConfig = this._overlayRef.getConfig();
793
+ const positionStrategy = overlayConfig.positionStrategy;
794
+ positionStrategy.setOrigin(this._getTargetElement());
795
+ }
586
796
  return this._overlayRef;
587
797
  }
588
798
  /**
@@ -590,32 +800,24 @@ class MtxPopoverTrigger {
590
800
  * @returns OverlayConfig
591
801
  */
592
802
  _getOverlayConfig() {
593
- const overlayState = new OverlayConfig();
594
- overlayState.positionStrategy = this._getPosition();
595
- /** Display overlay backdrop if trigger event is click */
596
- if (this.triggerEvent === 'click') {
597
- overlayState.hasBackdrop = true;
598
- overlayState.backdropClass = 'cdk-overlay-transparent-backdrop';
599
- }
600
- overlayState.direction = this._dir;
601
- overlayState.scrollStrategy = this._getOverlayScrollStrategy(this.popover.scrollStrategy);
602
- return overlayState;
803
+ return new OverlayConfig({
804
+ positionStrategy: this._overlay
805
+ .position()
806
+ .flexibleConnectedTo(this._getTargetElement())
807
+ .withLockedPosition()
808
+ .withGrowAfterOpen()
809
+ .withTransformOriginOn('.mtx-popover-panel'),
810
+ backdropClass: this.popover.backdropClass || 'cdk-overlay-transparent-backdrop',
811
+ panelClass: this.popover.overlayPanelClass,
812
+ scrollStrategy: this._scrollStrategy(),
813
+ direction: this._dir,
814
+ });
603
815
  }
604
- /**
605
- * This method returns the scroll strategy used by the cdk/overlay.
606
- */
607
- _getOverlayScrollStrategy(strategy) {
608
- switch (strategy) {
609
- case 'noop':
610
- return this._overlay.scrollStrategies.noop();
611
- case 'close':
612
- return this._overlay.scrollStrategies.close();
613
- case 'block':
614
- return this._overlay.scrollStrategies.block();
615
- case 'reposition':
616
- default:
617
- return this._overlay.scrollStrategies.reposition();
816
+ _getTargetElement() {
817
+ if (this.targetElement) {
818
+ return this.targetElement.elementRef;
618
819
  }
820
+ return this._elementRef;
619
821
  }
620
822
  /**
621
823
  * Listens to changes in the position of the overlay and sets the correct classes
@@ -639,18 +841,16 @@ class MtxPopoverTrigger {
639
841
  : [posX, posY];
640
842
  // required for ChangeDetectionStrategy.OnPush
641
843
  this._changeDetectorRef.markForCheck();
642
- this.popover.zone.run(() => {
643
- this.popover.setCurrentStyles(pos);
644
- this.popover.setPositionClasses(pos);
645
- });
844
+ this.popover.setCurrentStyles(pos);
845
+ this.popover.setPositionClasses(pos);
646
846
  });
647
847
  }
648
848
  /**
649
- * This method builds the position strategy for the overlay, so the popover is properly connected
650
- * to the trigger.
651
- * @returns ConnectedPositionStrategy
849
+ * Sets the appropriate positions on a position strategy
850
+ * so the overlay connects with the trigger correctly.
851
+ * @param positionStrategy Strategy whose position to update.
652
852
  */
653
- _getPosition() {
853
+ _setPosition(positionStrategy) {
654
854
  const [originX, origin2ndX, origin3rdX] = this.popover.position[0] === 'before' || this.popover.position[1] === 'after'
655
855
  ? ['start', 'center', 'end']
656
856
  : this.popover.position[0] === 'after' || this.popover.position[1] === 'before'
@@ -679,16 +879,6 @@ class MtxPopoverTrigger {
679
879
  const offsetY = this.popover.yOffset && !isNaN(Number(this.popover.yOffset))
680
880
  ? Number(this.popover.yOffset)
681
881
  : 0;
682
- /**
683
- * For overriding position element, when `mtxPopoverTargetAt` has a valid element reference.
684
- * Useful for sticking popover to parent element and offsetting arrow to trigger element.
685
- * If undefined defaults to the trigger element reference.
686
- */
687
- let element = this._elementRef;
688
- if (typeof this.targetElement !== 'undefined') {
689
- this.popover.containerPositioning = true;
690
- element = this.targetElement._elementRef;
691
- }
692
882
  let positions = [{ originX, originY, overlayX, overlayY }];
693
883
  if (this.popover.position[0] === 'above' || this.popover.position[0] === 'below') {
694
884
  positions = [
@@ -746,36 +936,45 @@ class MtxPopoverTrigger {
746
936
  },
747
937
  ];
748
938
  }
749
- return this._overlay
750
- .position()
751
- .flexibleConnectedTo(element)
752
- .withLockedPosition()
939
+ positionStrategy
753
940
  .withPositions(positions)
754
941
  .withDefaultOffsetX(offsetX)
755
942
  .withDefaultOffsetY(offsetY);
756
943
  }
757
- _cleanUpSubscriptions() {
758
- if (this._backdropSubscription) {
759
- this._backdropSubscription.unsubscribe();
760
- }
761
- if (this._positionSubscription) {
762
- this._positionSubscription.unsubscribe();
763
- }
764
- if (this._detachmentsSubscription) {
765
- this._detachmentsSubscription.unsubscribe();
766
- }
767
- }
768
- _handleMousedown(event) {
769
- if (event && !isFakeMousedownFromScreenReader(event)) {
770
- this._openedByMouse = true;
944
+ /** Returns a stream that emits whenever an action that should close the popover occurs. */
945
+ _popoverClosingActions() {
946
+ const backdrop = this.popover.triggerEvent === 'click' && this.popover.closeOnBackdropClick === true
947
+ ? this._overlayRef.backdropClick()
948
+ : of();
949
+ const detachments = this._overlayRef.detachments();
950
+ return merge(backdrop, detachments);
951
+ }
952
+ /** Gets the portal that should be attached to the overlay. */
953
+ _getPortal() {
954
+ // Note that we can avoid this check by keeping the portal on the popover panel.
955
+ // While it would be cleaner, we'd have to introduce another required method on
956
+ // `MtxPopoverPanel`, making it harder to consume.
957
+ if (!this._portal || this._portal.templateRef !== this.popover.templateRef) {
958
+ this._portal = new TemplatePortal(this.popover.templateRef, this._viewContainerRef);
771
959
  }
960
+ return this._portal;
772
961
  }
773
962
  }
774
963
  /** @type {!Array<{type: !Function, args: (undefined|!Array<?>)}>} */
775
964
  MtxPopoverTrigger.decorators = [
776
965
  { type: Directive, args: [{
777
- selector: '[mtxPopoverTriggerFor]',
966
+ selector: '[mtx-popover-trigger-for], [mtxPopoverTriggerFor]',
778
967
  exportAs: 'mtxPopoverTrigger',
968
+ host: {
969
+ 'aria-haspopup': 'true',
970
+ '[attr.aria-expanded]': 'popoverOpen || null',
971
+ '[attr.aria-controls]': 'popoverOpen ? popover.panelId : null',
972
+ '(click)': '_handleClick($event)',
973
+ '(mouseenter)': '_handleMouseEnter($event)',
974
+ '(mouseleave)': '_handleMouseLeave($event)',
975
+ '(mousedown)': '_handleMousedown($event)',
976
+ '(keydown)': '_handleKeydown($event)',
977
+ },
779
978
  },] }
780
979
  ];
781
980
  /**
@@ -789,54 +988,30 @@ MtxPopoverTrigger.ctorParameters = () => [
789
988
  { type: Overlay },
790
989
  { type: ElementRef },
791
990
  { type: ViewContainerRef },
991
+ { type: undefined, decorators: [{ type: Inject, args: [MTX_POPOVER_SCROLL_STRATEGY,] }] },
792
992
  { type: Directionality, decorators: [{ type: Optional }] },
793
- { type: ChangeDetectorRef }
993
+ { type: ChangeDetectorRef },
994
+ { type: FocusMonitor }
794
995
  ];
795
996
  /** @type {!Object<string, !Array<{type: !Function, args: (undefined|!Array<?>)}>>} */
796
997
  MtxPopoverTrigger.propDecorators = {
797
- ariaHaspopup: [{ type: HostBinding, args: ['attr.aria-haspopup',] }],
798
998
  popover: [{ type: Input, args: ['mtxPopoverTriggerFor',] }],
999
+ popoverData: [{ type: Input, args: ['mtxPopoverTriggerData',] }],
799
1000
  targetElement: [{ type: Input, args: ['mtxPopoverTargetAt',] }],
800
1001
  triggerEvent: [{ type: Input, args: ['mtxPopoverTriggerOn',] }],
801
1002
  popoverOpened: [{ type: Output }],
802
- popoverClosed: [{ type: Output }],
803
- onClick: [{ type: HostListener, args: ['click', ['$event'],] }],
804
- onMouseEnter: [{ type: HostListener, args: ['mouseenter', ['$event'],] }],
805
- onMouseLeave: [{ type: HostListener, args: ['mouseleave', ['$event'],] }],
806
- _handleMousedown: [{ type: HostListener, args: ['mousedown', ['$event'],] }]
1003
+ popoverClosed: [{ type: Output }]
807
1004
  };
808
1005
 
809
- class MtxPopoverTarget {
810
- constructor(_elementRef) {
811
- this._elementRef = _elementRef;
812
- }
813
- }
814
- /** @type {!Array<{type: !Function, args: (undefined|!Array<?>)}>} */
815
- MtxPopoverTarget.decorators = [
816
- { type: Directive, args: [{
817
- selector: 'mtx-popover-target, [mtxPopoverTarget]',
818
- exportAs: 'mtxPopoverTarget',
819
- },] }
820
- ];
821
- /**
822
- * @type {function(): !Array<(null|{
823
- * type: ?,
824
- * decorators: (undefined|!Array<{type: !Function, args: (undefined|!Array<?>)}>),
825
- * })>}
826
- * @nocollapse
827
- */
828
- MtxPopoverTarget.ctorParameters = () => [
829
- { type: ElementRef }
830
- ];
831
-
832
1006
  class MtxPopoverModule {
833
1007
  }
834
1008
  /** @type {!Array<{type: !Function, args: (undefined|!Array<?>)}>} */
835
1009
  MtxPopoverModule.decorators = [
836
1010
  { type: NgModule, args: [{
837
- imports: [OverlayModule, CommonModule, A11yModule],
838
- exports: [MtxPopover, MtxPopoverTrigger, MtxPopoverTarget],
839
- declarations: [MtxPopover, MtxPopoverTrigger, MtxPopoverTarget],
1011
+ imports: [CommonModule, OverlayModule, A11yModule],
1012
+ exports: [MtxPopover, MtxPopoverTrigger, MtxPopoverTarget, MtxPopoverContent],
1013
+ declarations: [MtxPopover, MtxPopoverTrigger, MtxPopoverTarget, MtxPopoverContent],
1014
+ providers: [MTX_POPOVER_SCROLL_STRATEGY_FACTORY_PROVIDER],
840
1015
  },] }
841
1016
  ];
842
1017
 
@@ -844,5 +1019,5 @@ MtxPopoverModule.decorators = [
844
1019
  * Generated bundle index. Do not edit.
845
1020
  */
846
1021
 
847
- export { MtxPopover, MtxPopoverModule, MtxPopoverTarget, MtxPopoverTrigger, transformPopover };
1022
+ export { MTX_POPOVER_CONTENT, MTX_POPOVER_DEFAULT_OPTIONS, MTX_POPOVER_DEFAULT_OPTIONS_FACTORY, MTX_POPOVER_SCROLL_STRATEGY, MTX_POPOVER_SCROLL_STRATEGY_FACTORY, MTX_POPOVER_SCROLL_STRATEGY_FACTORY_PROVIDER, MtxPopover, MtxPopoverContent, MtxPopoverModule, MtxPopoverTarget, MtxPopoverTrigger, _MtxPopoverContentBase, transformPopover };
848
1023
  //# sourceMappingURL=mtxPopover.js.map