source: trip-planner-front/node_modules/@angular/cdk/fesm2015/overlay.js@ 571e0df

Last change on this file since 571e0df was e29cc2e, checked in by Ema <ema_spirova@…>, 3 years ago

primeNG components

  • Property mode set to 100644
File size: 134.3 KB
RevLine 
[6a3a178]1import * as i1 from '@angular/cdk/scrolling';
2import { ScrollDispatcher, ViewportRuler, ScrollingModule } from '@angular/cdk/scrolling';
3export { CdkScrollable, ScrollDispatcher, ViewportRuler } from '@angular/cdk/scrolling';
4import * as i1$1 from '@angular/common';
5import { DOCUMENT, Location } from '@angular/common';
6import * as i0 from '@angular/core';
7import { Injectable, NgZone, Inject, Optional, ElementRef, ApplicationRef, ComponentFactoryResolver, Injector, InjectionToken, Directive, EventEmitter, TemplateRef, ViewContainerRef, Input, Output, NgModule } from '@angular/core';
8import { coerceCssPixelValue, coerceArray, coerceBooleanProperty } from '@angular/cdk/coercion';
9import * as i2 from '@angular/cdk/platform';
10import { supportsScrollBehavior, _getEventTarget, Platform, _isTestEnvironment } from '@angular/cdk/platform';
11import { Directionality, BidiModule } from '@angular/cdk/bidi';
12import { DomPortalOutlet, TemplatePortal, PortalModule } from '@angular/cdk/portal';
13import { Subject, Subscription, merge } from 'rxjs';
14import { take, takeUntil, takeWhile } from 'rxjs/operators';
15import { ESCAPE, hasModifierKey } from '@angular/cdk/keycodes';
16
17/**
18 * @license
19 * Copyright Google LLC All Rights Reserved.
20 *
21 * Use of this source code is governed by an MIT-style license that can be
22 * found in the LICENSE file at https://angular.io/license
23 */
24const scrollBehaviorSupported = supportsScrollBehavior();
25/**
26 * Strategy that will prevent the user from scrolling while the overlay is visible.
27 */
28class BlockScrollStrategy {
29 constructor(_viewportRuler, document) {
30 this._viewportRuler = _viewportRuler;
31 this._previousHTMLStyles = { top: '', left: '' };
32 this._isEnabled = false;
33 this._document = document;
34 }
35 /** Attaches this scroll strategy to an overlay. */
36 attach() { }
37 /** Blocks page-level scroll while the attached overlay is open. */
38 enable() {
39 if (this._canBeEnabled()) {
40 const root = this._document.documentElement;
41 this._previousScrollPosition = this._viewportRuler.getViewportScrollPosition();
42 // Cache the previous inline styles in case the user had set them.
43 this._previousHTMLStyles.left = root.style.left || '';
44 this._previousHTMLStyles.top = root.style.top || '';
45 // Note: we're using the `html` node, instead of the `body`, because the `body` may
46 // have the user agent margin, whereas the `html` is guaranteed not to have one.
47 root.style.left = coerceCssPixelValue(-this._previousScrollPosition.left);
48 root.style.top = coerceCssPixelValue(-this._previousScrollPosition.top);
49 root.classList.add('cdk-global-scrollblock');
50 this._isEnabled = true;
51 }
52 }
53 /** Unblocks page-level scroll while the attached overlay is open. */
54 disable() {
55 if (this._isEnabled) {
56 const html = this._document.documentElement;
57 const body = this._document.body;
58 const htmlStyle = html.style;
59 const bodyStyle = body.style;
60 const previousHtmlScrollBehavior = htmlStyle.scrollBehavior || '';
61 const previousBodyScrollBehavior = bodyStyle.scrollBehavior || '';
62 this._isEnabled = false;
63 htmlStyle.left = this._previousHTMLStyles.left;
64 htmlStyle.top = this._previousHTMLStyles.top;
65 html.classList.remove('cdk-global-scrollblock');
66 // Disable user-defined smooth scrolling temporarily while we restore the scroll position.
67 // See https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-behavior
68 // Note that we don't mutate the property if the browser doesn't support `scroll-behavior`,
69 // because it can throw off feature detections in `supportsScrollBehavior` which
70 // checks for `'scrollBehavior' in documentElement.style`.
71 if (scrollBehaviorSupported) {
72 htmlStyle.scrollBehavior = bodyStyle.scrollBehavior = 'auto';
73 }
74 window.scroll(this._previousScrollPosition.left, this._previousScrollPosition.top);
75 if (scrollBehaviorSupported) {
76 htmlStyle.scrollBehavior = previousHtmlScrollBehavior;
77 bodyStyle.scrollBehavior = previousBodyScrollBehavior;
78 }
79 }
80 }
81 _canBeEnabled() {
82 // Since the scroll strategies can't be singletons, we have to use a global CSS class
83 // (`cdk-global-scrollblock`) to make sure that we don't try to disable global
84 // scrolling multiple times.
85 const html = this._document.documentElement;
86 if (html.classList.contains('cdk-global-scrollblock') || this._isEnabled) {
87 return false;
88 }
89 const body = this._document.body;
90 const viewport = this._viewportRuler.getViewportSize();
91 return body.scrollHeight > viewport.height || body.scrollWidth > viewport.width;
92 }
93}
94
95/**
96 * @license
97 * Copyright Google LLC All Rights Reserved.
98 *
99 * Use of this source code is governed by an MIT-style license that can be
100 * found in the LICENSE file at https://angular.io/license
101 */
102/**
103 * Returns an error to be thrown when attempting to attach an already-attached scroll strategy.
104 */
105function getMatScrollStrategyAlreadyAttachedError() {
106 return Error(`Scroll strategy has already been attached.`);
107}
108
109/**
110 * Strategy that will close the overlay as soon as the user starts scrolling.
111 */
112class CloseScrollStrategy {
113 constructor(_scrollDispatcher, _ngZone, _viewportRuler, _config) {
114 this._scrollDispatcher = _scrollDispatcher;
115 this._ngZone = _ngZone;
116 this._viewportRuler = _viewportRuler;
117 this._config = _config;
118 this._scrollSubscription = null;
119 /** Detaches the overlay ref and disables the scroll strategy. */
120 this._detach = () => {
121 this.disable();
122 if (this._overlayRef.hasAttached()) {
123 this._ngZone.run(() => this._overlayRef.detach());
124 }
125 };
126 }
127 /** Attaches this scroll strategy to an overlay. */
128 attach(overlayRef) {
129 if (this._overlayRef && (typeof ngDevMode === 'undefined' || ngDevMode)) {
130 throw getMatScrollStrategyAlreadyAttachedError();
131 }
132 this._overlayRef = overlayRef;
133 }
134 /** Enables the closing of the attached overlay on scroll. */
135 enable() {
136 if (this._scrollSubscription) {
137 return;
138 }
139 const stream = this._scrollDispatcher.scrolled(0);
140 if (this._config && this._config.threshold && this._config.threshold > 1) {
141 this._initialScrollPosition = this._viewportRuler.getViewportScrollPosition().top;
142 this._scrollSubscription = stream.subscribe(() => {
143 const scrollPosition = this._viewportRuler.getViewportScrollPosition().top;
144 if (Math.abs(scrollPosition - this._initialScrollPosition) > this._config.threshold) {
145 this._detach();
146 }
147 else {
148 this._overlayRef.updatePosition();
149 }
150 });
151 }
152 else {
153 this._scrollSubscription = stream.subscribe(this._detach);
154 }
155 }
156 /** Disables the closing the attached overlay on scroll. */
157 disable() {
158 if (this._scrollSubscription) {
159 this._scrollSubscription.unsubscribe();
160 this._scrollSubscription = null;
161 }
162 }
163 detach() {
164 this.disable();
165 this._overlayRef = null;
166 }
167}
168
169/**
170 * @license
171 * Copyright Google LLC All Rights Reserved.
172 *
173 * Use of this source code is governed by an MIT-style license that can be
174 * found in the LICENSE file at https://angular.io/license
175 */
176/** Scroll strategy that doesn't do anything. */
177class NoopScrollStrategy {
178 /** Does nothing, as this scroll strategy is a no-op. */
179 enable() { }
180 /** Does nothing, as this scroll strategy is a no-op. */
181 disable() { }
182 /** Does nothing, as this scroll strategy is a no-op. */
183 attach() { }
184}
185
186/**
187 * @license
188 * Copyright Google LLC All Rights Reserved.
189 *
190 * Use of this source code is governed by an MIT-style license that can be
191 * found in the LICENSE file at https://angular.io/license
192 */
193// TODO(jelbourn): move this to live with the rest of the scrolling code
194// TODO(jelbourn): someday replace this with IntersectionObservers
195/**
196 * Gets whether an element is scrolled outside of view by any of its parent scrolling containers.
197 * @param element Dimensions of the element (from getBoundingClientRect)
198 * @param scrollContainers Dimensions of element's scrolling containers (from getBoundingClientRect)
199 * @returns Whether the element is scrolled out of view
200 * @docs-private
201 */
202function isElementScrolledOutsideView(element, scrollContainers) {
203 return scrollContainers.some(containerBounds => {
204 const outsideAbove = element.bottom < containerBounds.top;
205 const outsideBelow = element.top > containerBounds.bottom;
206 const outsideLeft = element.right < containerBounds.left;
207 const outsideRight = element.left > containerBounds.right;
208 return outsideAbove || outsideBelow || outsideLeft || outsideRight;
209 });
210}
211/**
212 * Gets whether an element is clipped by any of its scrolling containers.
213 * @param element Dimensions of the element (from getBoundingClientRect)
214 * @param scrollContainers Dimensions of element's scrolling containers (from getBoundingClientRect)
215 * @returns Whether the element is clipped
216 * @docs-private
217 */
218function isElementClippedByScrolling(element, scrollContainers) {
219 return scrollContainers.some(scrollContainerRect => {
220 const clippedAbove = element.top < scrollContainerRect.top;
221 const clippedBelow = element.bottom > scrollContainerRect.bottom;
222 const clippedLeft = element.left < scrollContainerRect.left;
223 const clippedRight = element.right > scrollContainerRect.right;
224 return clippedAbove || clippedBelow || clippedLeft || clippedRight;
225 });
226}
227
228/**
229 * @license
230 * Copyright Google LLC All Rights Reserved.
231 *
232 * Use of this source code is governed by an MIT-style license that can be
233 * found in the LICENSE file at https://angular.io/license
234 */
235/**
236 * Strategy that will update the element position as the user is scrolling.
237 */
238class RepositionScrollStrategy {
239 constructor(_scrollDispatcher, _viewportRuler, _ngZone, _config) {
240 this._scrollDispatcher = _scrollDispatcher;
241 this._viewportRuler = _viewportRuler;
242 this._ngZone = _ngZone;
243 this._config = _config;
244 this._scrollSubscription = null;
245 }
246 /** Attaches this scroll strategy to an overlay. */
247 attach(overlayRef) {
248 if (this._overlayRef && (typeof ngDevMode === 'undefined' || ngDevMode)) {
249 throw getMatScrollStrategyAlreadyAttachedError();
250 }
251 this._overlayRef = overlayRef;
252 }
253 /** Enables repositioning of the attached overlay on scroll. */
254 enable() {
255 if (!this._scrollSubscription) {
256 const throttle = this._config ? this._config.scrollThrottle : 0;
257 this._scrollSubscription = this._scrollDispatcher.scrolled(throttle).subscribe(() => {
258 this._overlayRef.updatePosition();
259 // TODO(crisbeto): make `close` on by default once all components can handle it.
260 if (this._config && this._config.autoClose) {
261 const overlayRect = this._overlayRef.overlayElement.getBoundingClientRect();
262 const { width, height } = this._viewportRuler.getViewportSize();
263 // TODO(crisbeto): include all ancestor scroll containers here once
264 // we have a way of exposing the trigger element to the scroll strategy.
265 const parentRects = [{ width, height, bottom: height, right: width, top: 0, left: 0 }];
266 if (isElementScrolledOutsideView(overlayRect, parentRects)) {
267 this.disable();
268 this._ngZone.run(() => this._overlayRef.detach());
269 }
270 }
271 });
272 }
273 }
274 /** Disables repositioning of the attached overlay on scroll. */
275 disable() {
276 if (this._scrollSubscription) {
277 this._scrollSubscription.unsubscribe();
278 this._scrollSubscription = null;
279 }
280 }
281 detach() {
282 this.disable();
283 this._overlayRef = null;
284 }
285}
286
287/**
288 * @license
289 * Copyright Google LLC All Rights Reserved.
290 *
291 * Use of this source code is governed by an MIT-style license that can be
292 * found in the LICENSE file at https://angular.io/license
293 */
294/**
295 * Options for how an overlay will handle scrolling.
296 *
297 * Users can provide a custom value for `ScrollStrategyOptions` to replace the default
298 * behaviors. This class primarily acts as a factory for ScrollStrategy instances.
299 */
300class ScrollStrategyOptions {
301 constructor(_scrollDispatcher, _viewportRuler, _ngZone, document) {
302 this._scrollDispatcher = _scrollDispatcher;
303 this._viewportRuler = _viewportRuler;
304 this._ngZone = _ngZone;
305 /** Do nothing on scroll. */
306 this.noop = () => new NoopScrollStrategy();
307 /**
308 * Close the overlay as soon as the user scrolls.
309 * @param config Configuration to be used inside the scroll strategy.
310 */
311 this.close = (config) => new CloseScrollStrategy(this._scrollDispatcher, this._ngZone, this._viewportRuler, config);
312 /** Block scrolling. */
313 this.block = () => new BlockScrollStrategy(this._viewportRuler, this._document);
314 /**
315 * Update the overlay's position on scroll.
316 * @param config Configuration to be used inside the scroll strategy.
317 * Allows debouncing the reposition calls.
318 */
319 this.reposition = (config) => new RepositionScrollStrategy(this._scrollDispatcher, this._viewportRuler, this._ngZone, config);
320 this._document = document;
321 }
322}
323ScrollStrategyOptions.ɵprov = i0.ɵɵdefineInjectable({ factory: function ScrollStrategyOptions_Factory() { return new ScrollStrategyOptions(i0.ɵɵinject(i1.ScrollDispatcher), i0.ɵɵinject(i1.ViewportRuler), i0.ɵɵinject(i0.NgZone), i0.ɵɵinject(i1$1.DOCUMENT)); }, token: ScrollStrategyOptions, providedIn: "root" });
324ScrollStrategyOptions.decorators = [
325 { type: Injectable, args: [{ providedIn: 'root' },] }
326];
327ScrollStrategyOptions.ctorParameters = () => [
328 { type: ScrollDispatcher },
329 { type: ViewportRuler },
330 { type: NgZone },
331 { type: undefined, decorators: [{ type: Inject, args: [DOCUMENT,] }] }
332];
333
334/**
335 * @license
336 * Copyright Google LLC All Rights Reserved.
337 *
338 * Use of this source code is governed by an MIT-style license that can be
339 * found in the LICENSE file at https://angular.io/license
340 */
341
342/**
343 * @license
344 * Copyright Google LLC All Rights Reserved.
345 *
346 * Use of this source code is governed by an MIT-style license that can be
347 * found in the LICENSE file at https://angular.io/license
348 */
349/** Initial configuration used when creating an overlay. */
350class OverlayConfig {
351 constructor(config) {
352 /** Strategy to be used when handling scroll events while the overlay is open. */
353 this.scrollStrategy = new NoopScrollStrategy();
354 /** Custom class to add to the overlay pane. */
355 this.panelClass = '';
356 /** Whether the overlay has a backdrop. */
357 this.hasBackdrop = false;
358 /** Custom class to add to the backdrop */
359 this.backdropClass = 'cdk-overlay-dark-backdrop';
360 /**
361 * Whether the overlay should be disposed of when the user goes backwards/forwards in history.
362 * Note that this usually doesn't include clicking on links (unless the user is using
363 * the `HashLocationStrategy`).
364 */
365 this.disposeOnNavigation = false;
366 if (config) {
367 // Use `Iterable` instead of `Array` because TypeScript, as of 3.6.3,
368 // loses the array generic type in the `for of`. But we *also* have to use `Array` because
369 // typescript won't iterate over an `Iterable` unless you compile with `--downlevelIteration`
370 const configKeys = Object.keys(config);
371 for (const key of configKeys) {
372 if (config[key] !== undefined) {
373 // TypeScript, as of version 3.5, sees the left-hand-side of this expression
374 // as "I don't know *which* key this is, so the only valid value is the intersection
375 // of all the posible values." In this case, that happens to be `undefined`. TypeScript
376 // is not smart enough to see that the right-hand-side is actually an access of the same
377 // exact type with the same exact key, meaning that the value type must be identical.
378 // So we use `any` to work around this.
379 this[key] = config[key];
380 }
381 }
382 }
383 }
384}
385
386/**
387 * @license
388 * Copyright Google LLC All Rights Reserved.
389 *
390 * Use of this source code is governed by an MIT-style license that can be
391 * found in the LICENSE file at https://angular.io/license
392 */
393/** The points of the origin element and the overlay element to connect. */
394class ConnectionPositionPair {
395 constructor(origin, overlay,
396 /** Offset along the X axis. */
397 offsetX,
398 /** Offset along the Y axis. */
399 offsetY,
400 /** Class(es) to be applied to the panel while this position is active. */
401 panelClass) {
402 this.offsetX = offsetX;
403 this.offsetY = offsetY;
404 this.panelClass = panelClass;
405 this.originX = origin.originX;
406 this.originY = origin.originY;
407 this.overlayX = overlay.overlayX;
408 this.overlayY = overlay.overlayY;
409 }
410}
411/**
412 * Set of properties regarding the position of the origin and overlay relative to the viewport
413 * with respect to the containing Scrollable elements.
414 *
415 * The overlay and origin are clipped if any part of their bounding client rectangle exceeds the
416 * bounds of any one of the strategy's Scrollable's bounding client rectangle.
417 *
418 * The overlay and origin are outside view if there is no overlap between their bounding client
419 * rectangle and any one of the strategy's Scrollable's bounding client rectangle.
420 *
421 * ----------- -----------
422 * | outside | | clipped |
423 * | view | --------------------------
424 * | | | | | |
425 * ---------- | ----------- |
426 * -------------------------- | |
427 * | | | Scrollable |
428 * | | | |
429 * | | --------------------------
430 * | Scrollable |
431 * | |
432 * --------------------------
433 *
434 * @docs-private
435 */
436class ScrollingVisibility {
437}
438/** The change event emitted by the strategy when a fallback position is used. */
439class ConnectedOverlayPositionChange {
440 constructor(
441 /** The position used as a result of this change. */
442 connectionPair,
443 /** @docs-private */
444 scrollableViewProperties) {
445 this.connectionPair = connectionPair;
446 this.scrollableViewProperties = scrollableViewProperties;
447 }
448}
449ConnectedOverlayPositionChange.ctorParameters = () => [
450 { type: ConnectionPositionPair },
451 { type: ScrollingVisibility, decorators: [{ type: Optional }] }
452];
453/**
454 * Validates whether a vertical position property matches the expected values.
455 * @param property Name of the property being validated.
456 * @param value Value of the property being validated.
457 * @docs-private
458 */
459function validateVerticalPosition(property, value) {
460 if (value !== 'top' && value !== 'bottom' && value !== 'center') {
461 throw Error(`ConnectedPosition: Invalid ${property} "${value}". ` +
462 `Expected "top", "bottom" or "center".`);
463 }
464}
465/**
466 * Validates whether a horizontal position property matches the expected values.
467 * @param property Name of the property being validated.
468 * @param value Value of the property being validated.
469 * @docs-private
470 */
471function validateHorizontalPosition(property, value) {
472 if (value !== 'start' && value !== 'end' && value !== 'center') {
473 throw Error(`ConnectedPosition: Invalid ${property} "${value}". ` +
474 `Expected "start", "end" or "center".`);
475 }
476}
477
478/**
479 * @license
480 * Copyright Google LLC All Rights Reserved.
481 *
482 * Use of this source code is governed by an MIT-style license that can be
483 * found in the LICENSE file at https://angular.io/license
484 */
485/**
486 * Service for dispatching events that land on the body to appropriate overlay ref,
487 * if any. It maintains a list of attached overlays to determine best suited overlay based
488 * on event target and order of overlay opens.
489 */
490class BaseOverlayDispatcher {
491 constructor(document) {
492 /** Currently attached overlays in the order they were attached. */
493 this._attachedOverlays = [];
494 this._document = document;
495 }
496 ngOnDestroy() {
497 this.detach();
498 }
499 /** Add a new overlay to the list of attached overlay refs. */
500 add(overlayRef) {
501 // Ensure that we don't get the same overlay multiple times.
502 this.remove(overlayRef);
503 this._attachedOverlays.push(overlayRef);
504 }
505 /** Remove an overlay from the list of attached overlay refs. */
506 remove(overlayRef) {
507 const index = this._attachedOverlays.indexOf(overlayRef);
508 if (index > -1) {
509 this._attachedOverlays.splice(index, 1);
510 }
511 // Remove the global listener once there are no more overlays.
512 if (this._attachedOverlays.length === 0) {
513 this.detach();
514 }
515 }
516}
517BaseOverlayDispatcher.ɵprov = i0.ɵɵdefineInjectable({ factory: function BaseOverlayDispatcher_Factory() { return new BaseOverlayDispatcher(i0.ɵɵinject(i1$1.DOCUMENT)); }, token: BaseOverlayDispatcher, providedIn: "root" });
518BaseOverlayDispatcher.decorators = [
519 { type: Injectable, args: [{ providedIn: 'root' },] }
520];
521BaseOverlayDispatcher.ctorParameters = () => [
522 { type: undefined, decorators: [{ type: Inject, args: [DOCUMENT,] }] }
523];
524
525/**
526 * @license
527 * Copyright Google LLC All Rights Reserved.
528 *
529 * Use of this source code is governed by an MIT-style license that can be
530 * found in the LICENSE file at https://angular.io/license
531 */
532/**
533 * Service for dispatching keyboard events that land on the body to appropriate overlay ref,
534 * if any. It maintains a list of attached overlays to determine best suited overlay based
535 * on event target and order of overlay opens.
536 */
537class OverlayKeyboardDispatcher extends BaseOverlayDispatcher {
538 constructor(document) {
539 super(document);
540 /** Keyboard event listener that will be attached to the body. */
541 this._keydownListener = (event) => {
542 const overlays = this._attachedOverlays;
543 for (let i = overlays.length - 1; i > -1; i--) {
544 // Dispatch the keydown event to the top overlay which has subscribers to its keydown events.
545 // We want to target the most recent overlay, rather than trying to match where the event came
546 // from, because some components might open an overlay, but keep focus on a trigger element
547 // (e.g. for select and autocomplete). We skip overlays without keydown event subscriptions,
548 // because we don't want overlays that don't handle keyboard events to block the ones below
549 // them that do.
550 if (overlays[i]._keydownEvents.observers.length > 0) {
551 overlays[i]._keydownEvents.next(event);
552 break;
553 }
554 }
555 };
556 }
557 /** Add a new overlay to the list of attached overlay refs. */
558 add(overlayRef) {
559 super.add(overlayRef);
560 // Lazily start dispatcher once first overlay is added
561 if (!this._isAttached) {
562 this._document.body.addEventListener('keydown', this._keydownListener);
563 this._isAttached = true;
564 }
565 }
566 /** Detaches the global keyboard event listener. */
567 detach() {
568 if (this._isAttached) {
569 this._document.body.removeEventListener('keydown', this._keydownListener);
570 this._isAttached = false;
571 }
572 }
573}
574OverlayKeyboardDispatcher.ɵprov = i0.ɵɵdefineInjectable({ factory: function OverlayKeyboardDispatcher_Factory() { return new OverlayKeyboardDispatcher(i0.ɵɵinject(i1$1.DOCUMENT)); }, token: OverlayKeyboardDispatcher, providedIn: "root" });
575OverlayKeyboardDispatcher.decorators = [
576 { type: Injectable, args: [{ providedIn: 'root' },] }
577];
578OverlayKeyboardDispatcher.ctorParameters = () => [
579 { type: undefined, decorators: [{ type: Inject, args: [DOCUMENT,] }] }
580];
581
582/**
583 * @license
584 * Copyright Google LLC All Rights Reserved.
585 *
586 * Use of this source code is governed by an MIT-style license that can be
587 * found in the LICENSE file at https://angular.io/license
588 */
589/**
590 * Service for dispatching mouse click events that land on the body to appropriate overlay ref,
591 * if any. It maintains a list of attached overlays to determine best suited overlay based
592 * on event target and order of overlay opens.
593 */
594class OverlayOutsideClickDispatcher extends BaseOverlayDispatcher {
595 constructor(document, _platform) {
596 super(document);
597 this._platform = _platform;
598 this._cursorStyleIsSet = false;
[e29cc2e]599 /** Store pointerdown event target to track origin of click. */
600 this._pointerDownListener = (event) => {
601 this._pointerDownEventTarget = _getEventTarget(event);
602 };
[6a3a178]603 /** Click event listener that will be attached to the body propagate phase. */
604 this._clickListener = (event) => {
605 const target = _getEventTarget(event);
[e29cc2e]606 // In case of a click event, we want to check the origin of the click
607 // (e.g. in case where a user starts a click inside the overlay and
608 // releases the click outside of it).
609 // This is done by using the event target of the preceding pointerdown event.
610 // Every click event caused by a pointer device has a preceding pointerdown
611 // event, unless the click was programmatically triggered (e.g. in a unit test).
612 const origin = event.type === 'click' && this._pointerDownEventTarget
613 ? this._pointerDownEventTarget : target;
614 // Reset the stored pointerdown event target, to avoid having it interfere
615 // in subsequent events.
616 this._pointerDownEventTarget = null;
[6a3a178]617 // We copy the array because the original may be modified asynchronously if the
618 // outsidePointerEvents listener decides to detach overlays resulting in index errors inside
619 // the for loop.
620 const overlays = this._attachedOverlays.slice();
621 // Dispatch the mouse event to the top overlay which has subscribers to its mouse events.
622 // We want to target all overlays for which the click could be considered as outside click.
623 // As soon as we reach an overlay for which the click is not outside click we break off
624 // the loop.
625 for (let i = overlays.length - 1; i > -1; i--) {
626 const overlayRef = overlays[i];
627 if (overlayRef._outsidePointerEvents.observers.length < 1 || !overlayRef.hasAttached()) {
628 continue;
629 }
630 // If it's a click inside the overlay, just break - we should do nothing
[e29cc2e]631 // If it's an outside click (both origin and target of the click) dispatch the mouse event,
632 // and proceed with the next overlay
633 if (overlayRef.overlayElement.contains(target) ||
634 overlayRef.overlayElement.contains(origin)) {
[6a3a178]635 break;
636 }
637 overlayRef._outsidePointerEvents.next(event);
638 }
639 };
640 }
641 /** Add a new overlay to the list of attached overlay refs. */
642 add(overlayRef) {
643 super.add(overlayRef);
644 // Safari on iOS does not generate click events for non-interactive
645 // elements. However, we want to receive a click for any element outside
646 // the overlay. We can force a "clickable" state by setting
647 // `cursor: pointer` on the document body. See:
648 // https://developer.mozilla.org/en-US/docs/Web/API/Element/click_event#Safari_Mobile
649 // https://developer.apple.com/library/archive/documentation/AppleApplications/Reference/SafariWebContent/HandlingEvents/HandlingEvents.html
650 if (!this._isAttached) {
651 const body = this._document.body;
[e29cc2e]652 body.addEventListener('pointerdown', this._pointerDownListener, true);
[6a3a178]653 body.addEventListener('click', this._clickListener, true);
654 body.addEventListener('auxclick', this._clickListener, true);
655 body.addEventListener('contextmenu', this._clickListener, true);
656 // click event is not fired on iOS. To make element "clickable" we are
657 // setting the cursor to pointer
658 if (this._platform.IOS && !this._cursorStyleIsSet) {
659 this._cursorOriginalValue = body.style.cursor;
660 body.style.cursor = 'pointer';
661 this._cursorStyleIsSet = true;
662 }
663 this._isAttached = true;
664 }
665 }
666 /** Detaches the global keyboard event listener. */
667 detach() {
668 if (this._isAttached) {
669 const body = this._document.body;
[e29cc2e]670 body.removeEventListener('pointerdown', this._pointerDownListener, true);
[6a3a178]671 body.removeEventListener('click', this._clickListener, true);
672 body.removeEventListener('auxclick', this._clickListener, true);
673 body.removeEventListener('contextmenu', this._clickListener, true);
674 if (this._platform.IOS && this._cursorStyleIsSet) {
675 body.style.cursor = this._cursorOriginalValue;
676 this._cursorStyleIsSet = false;
677 }
678 this._isAttached = false;
679 }
680 }
681}
682OverlayOutsideClickDispatcher.ɵprov = i0.ɵɵdefineInjectable({ factory: function OverlayOutsideClickDispatcher_Factory() { return new OverlayOutsideClickDispatcher(i0.ɵɵinject(i1$1.DOCUMENT), i0.ɵɵinject(i2.Platform)); }, token: OverlayOutsideClickDispatcher, providedIn: "root" });
683OverlayOutsideClickDispatcher.decorators = [
684 { type: Injectable, args: [{ providedIn: 'root' },] }
685];
686OverlayOutsideClickDispatcher.ctorParameters = () => [
687 { type: undefined, decorators: [{ type: Inject, args: [DOCUMENT,] }] },
688 { type: Platform }
689];
690
691/**
692 * @license
693 * Copyright Google LLC All Rights Reserved.
694 *
695 * Use of this source code is governed by an MIT-style license that can be
696 * found in the LICENSE file at https://angular.io/license
697 */
698/** Container inside which all overlays will render. */
699class OverlayContainer {
700 constructor(document, _platform) {
701 this._platform = _platform;
702 this._document = document;
703 }
704 ngOnDestroy() {
705 const container = this._containerElement;
706 if (container && container.parentNode) {
707 container.parentNode.removeChild(container);
708 }
709 }
710 /**
711 * This method returns the overlay container element. It will lazily
712 * create the element the first time it is called to facilitate using
713 * the container in non-browser environments.
714 * @returns the container element
715 */
716 getContainerElement() {
717 if (!this._containerElement) {
718 this._createContainer();
719 }
720 return this._containerElement;
721 }
722 /**
723 * Create the overlay container element, which is simply a div
724 * with the 'cdk-overlay-container' class on the document body.
725 */
726 _createContainer() {
727 const containerClass = 'cdk-overlay-container';
728 // TODO(crisbeto): remove the testing check once we have an overlay testing
729 // module or Angular starts tearing down the testing `NgModule`. See:
730 // https://github.com/angular/angular/issues/18831
731 if (this._platform.isBrowser || _isTestEnvironment()) {
732 const oppositePlatformContainers = this._document.querySelectorAll(`.${containerClass}[platform="server"], ` +
733 `.${containerClass}[platform="test"]`);
734 // Remove any old containers from the opposite platform.
735 // This can happen when transitioning from the server to the client.
736 for (let i = 0; i < oppositePlatformContainers.length; i++) {
737 oppositePlatformContainers[i].parentNode.removeChild(oppositePlatformContainers[i]);
738 }
739 }
740 const container = this._document.createElement('div');
741 container.classList.add(containerClass);
742 // A long time ago we kept adding new overlay containers whenever a new app was instantiated,
743 // but at some point we added logic which clears the duplicate ones in order to avoid leaks.
744 // The new logic was a little too aggressive since it was breaking some legitimate use cases.
745 // To mitigate the problem we made it so that only containers from a different platform are
746 // cleared, but the side-effect was that people started depending on the overly-aggressive
747 // logic to clean up their tests for them. Until we can introduce an overlay-specific testing
748 // module which does the cleanup, we try to detect that we're in a test environment and we
749 // always clear the container. See #17006.
750 // TODO(crisbeto): remove the test environment check once we have an overlay testing module.
751 if (_isTestEnvironment()) {
752 container.setAttribute('platform', 'test');
753 }
754 else if (!this._platform.isBrowser) {
755 container.setAttribute('platform', 'server');
756 }
757 this._document.body.appendChild(container);
758 this._containerElement = container;
759 }
760}
761OverlayContainer.ɵprov = i0.ɵɵdefineInjectable({ factory: function OverlayContainer_Factory() { return new OverlayContainer(i0.ɵɵinject(i1$1.DOCUMENT), i0.ɵɵinject(i2.Platform)); }, token: OverlayContainer, providedIn: "root" });
762OverlayContainer.decorators = [
763 { type: Injectable, args: [{ providedIn: 'root' },] }
764];
765OverlayContainer.ctorParameters = () => [
766 { type: undefined, decorators: [{ type: Inject, args: [DOCUMENT,] }] },
767 { type: Platform }
768];
769
770/**
771 * @license
772 * Copyright Google LLC All Rights Reserved.
773 *
774 * Use of this source code is governed by an MIT-style license that can be
775 * found in the LICENSE file at https://angular.io/license
776 */
777/**
778 * Reference to an overlay that has been created with the Overlay service.
779 * Used to manipulate or dispose of said overlay.
780 */
781class OverlayRef {
782 constructor(_portalOutlet, _host, _pane, _config, _ngZone, _keyboardDispatcher, _document, _location, _outsideClickDispatcher) {
783 this._portalOutlet = _portalOutlet;
784 this._host = _host;
785 this._pane = _pane;
786 this._config = _config;
787 this._ngZone = _ngZone;
788 this._keyboardDispatcher = _keyboardDispatcher;
789 this._document = _document;
790 this._location = _location;
791 this._outsideClickDispatcher = _outsideClickDispatcher;
792 this._backdropElement = null;
793 this._backdropClick = new Subject();
794 this._attachments = new Subject();
795 this._detachments = new Subject();
796 this._locationChanges = Subscription.EMPTY;
797 this._backdropClickHandler = (event) => this._backdropClick.next(event);
798 /** Stream of keydown events dispatched to this overlay. */
799 this._keydownEvents = new Subject();
800 /** Stream of mouse outside events dispatched to this overlay. */
801 this._outsidePointerEvents = new Subject();
802 if (_config.scrollStrategy) {
803 this._scrollStrategy = _config.scrollStrategy;
804 this._scrollStrategy.attach(this);
805 }
806 this._positionStrategy = _config.positionStrategy;
807 }
808 /** The overlay's HTML element */
809 get overlayElement() {
810 return this._pane;
811 }
812 /** The overlay's backdrop HTML element. */
813 get backdropElement() {
814 return this._backdropElement;
815 }
816 /**
817 * Wrapper around the panel element. Can be used for advanced
818 * positioning where a wrapper with specific styling is
819 * required around the overlay pane.
820 */
821 get hostElement() {
822 return this._host;
823 }
824 /**
825 * Attaches content, given via a Portal, to the overlay.
826 * If the overlay is configured to have a backdrop, it will be created.
827 *
828 * @param portal Portal instance to which to attach the overlay.
829 * @returns The portal attachment result.
830 */
831 attach(portal) {
832 let attachResult = this._portalOutlet.attach(portal);
833 // Update the pane element with the given configuration.
834 if (!this._host.parentElement && this._previousHostParent) {
835 this._previousHostParent.appendChild(this._host);
836 }
837 if (this._positionStrategy) {
838 this._positionStrategy.attach(this);
839 }
840 this._updateStackingOrder();
841 this._updateElementSize();
842 this._updateElementDirection();
843 if (this._scrollStrategy) {
844 this._scrollStrategy.enable();
845 }
846 // Update the position once the zone is stable so that the overlay will be fully rendered
847 // before attempting to position it, as the position may depend on the size of the rendered
848 // content.
849 this._ngZone.onStable
850 .pipe(take(1))
851 .subscribe(() => {
852 // The overlay could've been detached before the zone has stabilized.
853 if (this.hasAttached()) {
854 this.updatePosition();
855 }
856 });
857 // Enable pointer events for the overlay pane element.
858 this._togglePointerEvents(true);
859 if (this._config.hasBackdrop) {
860 this._attachBackdrop();
861 }
862 if (this._config.panelClass) {
863 this._toggleClasses(this._pane, this._config.panelClass, true);
864 }
865 // Only emit the `attachments` event once all other setup is done.
866 this._attachments.next();
867 // Track this overlay by the keyboard dispatcher
868 this._keyboardDispatcher.add(this);
869 if (this._config.disposeOnNavigation) {
870 this._locationChanges = this._location.subscribe(() => this.dispose());
871 }
872 this._outsideClickDispatcher.add(this);
873 return attachResult;
874 }
875 /**
876 * Detaches an overlay from a portal.
877 * @returns The portal detachment result.
878 */
879 detach() {
880 if (!this.hasAttached()) {
881 return;
882 }
883 this.detachBackdrop();
884 // When the overlay is detached, the pane element should disable pointer events.
885 // This is necessary because otherwise the pane element will cover the page and disable
886 // pointer events therefore. Depends on the position strategy and the applied pane boundaries.
887 this._togglePointerEvents(false);
888 if (this._positionStrategy && this._positionStrategy.detach) {
889 this._positionStrategy.detach();
890 }
891 if (this._scrollStrategy) {
892 this._scrollStrategy.disable();
893 }
894 const detachmentResult = this._portalOutlet.detach();
895 // Only emit after everything is detached.
896 this._detachments.next();
897 // Remove this overlay from keyboard dispatcher tracking.
898 this._keyboardDispatcher.remove(this);
899 // Keeping the host element in the DOM can cause scroll jank, because it still gets
900 // rendered, even though it's transparent and unclickable which is why we remove it.
901 this._detachContentWhenStable();
902 this._locationChanges.unsubscribe();
903 this._outsideClickDispatcher.remove(this);
904 return detachmentResult;
905 }
906 /** Cleans up the overlay from the DOM. */
907 dispose() {
908 const isAttached = this.hasAttached();
909 if (this._positionStrategy) {
910 this._positionStrategy.dispose();
911 }
912 this._disposeScrollStrategy();
913 this._disposeBackdrop(this._backdropElement);
914 this._locationChanges.unsubscribe();
915 this._keyboardDispatcher.remove(this);
916 this._portalOutlet.dispose();
917 this._attachments.complete();
918 this._backdropClick.complete();
919 this._keydownEvents.complete();
920 this._outsidePointerEvents.complete();
921 this._outsideClickDispatcher.remove(this);
922 if (this._host && this._host.parentNode) {
923 this._host.parentNode.removeChild(this._host);
924 this._host = null;
925 }
926 this._previousHostParent = this._pane = null;
927 if (isAttached) {
928 this._detachments.next();
929 }
930 this._detachments.complete();
931 }
932 /** Whether the overlay has attached content. */
933 hasAttached() {
934 return this._portalOutlet.hasAttached();
935 }
936 /** Gets an observable that emits when the backdrop has been clicked. */
937 backdropClick() {
938 return this._backdropClick;
939 }
940 /** Gets an observable that emits when the overlay has been attached. */
941 attachments() {
942 return this._attachments;
943 }
944 /** Gets an observable that emits when the overlay has been detached. */
945 detachments() {
946 return this._detachments;
947 }
948 /** Gets an observable of keydown events targeted to this overlay. */
949 keydownEvents() {
950 return this._keydownEvents;
951 }
952 /** Gets an observable of pointer events targeted outside this overlay. */
953 outsidePointerEvents() {
954 return this._outsidePointerEvents;
955 }
956 /** Gets the current overlay configuration, which is immutable. */
957 getConfig() {
958 return this._config;
959 }
960 /** Updates the position of the overlay based on the position strategy. */
961 updatePosition() {
962 if (this._positionStrategy) {
963 this._positionStrategy.apply();
964 }
965 }
966 /** Switches to a new position strategy and updates the overlay position. */
967 updatePositionStrategy(strategy) {
968 if (strategy === this._positionStrategy) {
969 return;
970 }
971 if (this._positionStrategy) {
972 this._positionStrategy.dispose();
973 }
974 this._positionStrategy = strategy;
975 if (this.hasAttached()) {
976 strategy.attach(this);
977 this.updatePosition();
978 }
979 }
980 /** Update the size properties of the overlay. */
981 updateSize(sizeConfig) {
982 this._config = Object.assign(Object.assign({}, this._config), sizeConfig);
983 this._updateElementSize();
984 }
985 /** Sets the LTR/RTL direction for the overlay. */
986 setDirection(dir) {
987 this._config = Object.assign(Object.assign({}, this._config), { direction: dir });
988 this._updateElementDirection();
989 }
990 /** Add a CSS class or an array of classes to the overlay pane. */
991 addPanelClass(classes) {
992 if (this._pane) {
993 this._toggleClasses(this._pane, classes, true);
994 }
995 }
996 /** Remove a CSS class or an array of classes from the overlay pane. */
997 removePanelClass(classes) {
998 if (this._pane) {
999 this._toggleClasses(this._pane, classes, false);
1000 }
1001 }
1002 /**
1003 * Returns the layout direction of the overlay panel.
1004 */
1005 getDirection() {
1006 const direction = this._config.direction;
1007 if (!direction) {
1008 return 'ltr';
1009 }
1010 return typeof direction === 'string' ? direction : direction.value;
1011 }
1012 /** Switches to a new scroll strategy. */
1013 updateScrollStrategy(strategy) {
1014 if (strategy === this._scrollStrategy) {
1015 return;
1016 }
1017 this._disposeScrollStrategy();
1018 this._scrollStrategy = strategy;
1019 if (this.hasAttached()) {
1020 strategy.attach(this);
1021 strategy.enable();
1022 }
1023 }
1024 /** Updates the text direction of the overlay panel. */
1025 _updateElementDirection() {
1026 this._host.setAttribute('dir', this.getDirection());
1027 }
1028 /** Updates the size of the overlay element based on the overlay config. */
1029 _updateElementSize() {
1030 if (!this._pane) {
1031 return;
1032 }
1033 const style = this._pane.style;
1034 style.width = coerceCssPixelValue(this._config.width);
1035 style.height = coerceCssPixelValue(this._config.height);
1036 style.minWidth = coerceCssPixelValue(this._config.minWidth);
1037 style.minHeight = coerceCssPixelValue(this._config.minHeight);
1038 style.maxWidth = coerceCssPixelValue(this._config.maxWidth);
1039 style.maxHeight = coerceCssPixelValue(this._config.maxHeight);
1040 }
1041 /** Toggles the pointer events for the overlay pane element. */
1042 _togglePointerEvents(enablePointer) {
1043 this._pane.style.pointerEvents = enablePointer ? '' : 'none';
1044 }
1045 /** Attaches a backdrop for this overlay. */
1046 _attachBackdrop() {
1047 const showingClass = 'cdk-overlay-backdrop-showing';
1048 this._backdropElement = this._document.createElement('div');
1049 this._backdropElement.classList.add('cdk-overlay-backdrop');
1050 if (this._config.backdropClass) {
1051 this._toggleClasses(this._backdropElement, this._config.backdropClass, true);
1052 }
1053 // Insert the backdrop before the pane in the DOM order,
1054 // in order to handle stacked overlays properly.
1055 this._host.parentElement.insertBefore(this._backdropElement, this._host);
1056 // Forward backdrop clicks such that the consumer of the overlay can perform whatever
1057 // action desired when such a click occurs (usually closing the overlay).
1058 this._backdropElement.addEventListener('click', this._backdropClickHandler);
1059 // Add class to fade-in the backdrop after one frame.
1060 if (typeof requestAnimationFrame !== 'undefined') {
1061 this._ngZone.runOutsideAngular(() => {
1062 requestAnimationFrame(() => {
1063 if (this._backdropElement) {
1064 this._backdropElement.classList.add(showingClass);
1065 }
1066 });
1067 });
1068 }
1069 else {
1070 this._backdropElement.classList.add(showingClass);
1071 }
1072 }
1073 /**
1074 * Updates the stacking order of the element, moving it to the top if necessary.
1075 * This is required in cases where one overlay was detached, while another one,
1076 * that should be behind it, was destroyed. The next time both of them are opened,
1077 * the stacking will be wrong, because the detached element's pane will still be
1078 * in its original DOM position.
1079 */
1080 _updateStackingOrder() {
1081 if (this._host.nextSibling) {
1082 this._host.parentNode.appendChild(this._host);
1083 }
1084 }
1085 /** Detaches the backdrop (if any) associated with the overlay. */
1086 detachBackdrop() {
1087 const backdropToDetach = this._backdropElement;
1088 if (!backdropToDetach) {
1089 return;
1090 }
1091 let timeoutId;
1092 const finishDetach = () => {
1093 // It may not be attached to anything in certain cases (e.g. unit tests).
1094 if (backdropToDetach) {
1095 backdropToDetach.removeEventListener('click', this._backdropClickHandler);
1096 backdropToDetach.removeEventListener('transitionend', finishDetach);
1097 this._disposeBackdrop(backdropToDetach);
1098 }
1099 if (this._config.backdropClass) {
1100 this._toggleClasses(backdropToDetach, this._config.backdropClass, false);
1101 }
1102 clearTimeout(timeoutId);
1103 };
1104 backdropToDetach.classList.remove('cdk-overlay-backdrop-showing');
1105 this._ngZone.runOutsideAngular(() => {
1106 backdropToDetach.addEventListener('transitionend', finishDetach);
1107 });
1108 // If the backdrop doesn't have a transition, the `transitionend` event won't fire.
1109 // In this case we make it unclickable and we try to remove it after a delay.
1110 backdropToDetach.style.pointerEvents = 'none';
1111 // Run this outside the Angular zone because there's nothing that Angular cares about.
1112 // If it were to run inside the Angular zone, every test that used Overlay would have to be
1113 // either async or fakeAsync.
1114 timeoutId = this._ngZone.runOutsideAngular(() => setTimeout(finishDetach, 500));
1115 }
1116 /** Toggles a single CSS class or an array of classes on an element. */
1117 _toggleClasses(element, cssClasses, isAdd) {
1118 const classList = element.classList;
1119 coerceArray(cssClasses).forEach(cssClass => {
1120 // We can't do a spread here, because IE doesn't support setting multiple classes.
1121 // Also trying to add an empty string to a DOMTokenList will throw.
1122 if (cssClass) {
1123 isAdd ? classList.add(cssClass) : classList.remove(cssClass);
1124 }
1125 });
1126 }
1127 /** Detaches the overlay content next time the zone stabilizes. */
1128 _detachContentWhenStable() {
1129 // Normally we wouldn't have to explicitly run this outside the `NgZone`, however
1130 // if the consumer is using `zone-patch-rxjs`, the `Subscription.unsubscribe` call will
1131 // be patched to run inside the zone, which will throw us into an infinite loop.
1132 this._ngZone.runOutsideAngular(() => {
1133 // We can't remove the host here immediately, because the overlay pane's content
1134 // might still be animating. This stream helps us avoid interrupting the animation
1135 // by waiting for the pane to become empty.
1136 const subscription = this._ngZone.onStable
1137 .pipe(takeUntil(merge(this._attachments, this._detachments)))
1138 .subscribe(() => {
1139 // Needs a couple of checks for the pane and host, because
1140 // they may have been removed by the time the zone stabilizes.
1141 if (!this._pane || !this._host || this._pane.children.length === 0) {
1142 if (this._pane && this._config.panelClass) {
1143 this._toggleClasses(this._pane, this._config.panelClass, false);
1144 }
1145 if (this._host && this._host.parentElement) {
1146 this._previousHostParent = this._host.parentElement;
1147 this._previousHostParent.removeChild(this._host);
1148 }
1149 subscription.unsubscribe();
1150 }
1151 });
1152 });
1153 }
1154 /** Disposes of a scroll strategy. */
1155 _disposeScrollStrategy() {
1156 const scrollStrategy = this._scrollStrategy;
1157 if (scrollStrategy) {
1158 scrollStrategy.disable();
1159 if (scrollStrategy.detach) {
1160 scrollStrategy.detach();
1161 }
1162 }
1163 }
1164 /** Removes a backdrop element from the DOM. */
1165 _disposeBackdrop(backdrop) {
1166 if (backdrop) {
1167 if (backdrop.parentNode) {
1168 backdrop.parentNode.removeChild(backdrop);
1169 }
1170 // It is possible that a new portal has been attached to this overlay since we started
1171 // removing the backdrop. If that is the case, only clear the backdrop reference if it
1172 // is still the same instance that we started to remove.
1173 if (this._backdropElement === backdrop) {
1174 this._backdropElement = null;
1175 }
1176 }
1177 }
1178}
1179
1180/**
1181 * @license
1182 * Copyright Google LLC All Rights Reserved.
1183 *
1184 * Use of this source code is governed by an MIT-style license that can be
1185 * found in the LICENSE file at https://angular.io/license
1186 */
1187// TODO: refactor clipping detection into a separate thing (part of scrolling module)
1188// TODO: doesn't handle both flexible width and height when it has to scroll along both axis.
1189/** Class to be added to the overlay bounding box. */
1190const boundingBoxClass = 'cdk-overlay-connected-position-bounding-box';
1191/** Regex used to split a string on its CSS units. */
1192const cssUnitPattern = /([A-Za-z%]+)$/;
1193/**
1194 * A strategy for positioning overlays. Using this strategy, an overlay is given an
1195 * implicit position relative some origin element. The relative position is defined in terms of
1196 * a point on the origin element that is connected to a point on the overlay element. For example,
1197 * a basic dropdown is connecting the bottom-left corner of the origin to the top-left corner
1198 * of the overlay.
1199 */
1200class FlexibleConnectedPositionStrategy {
1201 constructor(connectedTo, _viewportRuler, _document, _platform, _overlayContainer) {
1202 this._viewportRuler = _viewportRuler;
1203 this._document = _document;
1204 this._platform = _platform;
1205 this._overlayContainer = _overlayContainer;
1206 /** Last size used for the bounding box. Used to avoid resizing the overlay after open. */
1207 this._lastBoundingBoxSize = { width: 0, height: 0 };
1208 /** Whether the overlay was pushed in a previous positioning. */
1209 this._isPushed = false;
1210 /** Whether the overlay can be pushed on-screen on the initial open. */
1211 this._canPush = true;
1212 /** Whether the overlay can grow via flexible width/height after the initial open. */
1213 this._growAfterOpen = false;
1214 /** Whether the overlay's width and height can be constrained to fit within the viewport. */
1215 this._hasFlexibleDimensions = true;
1216 /** Whether the overlay position is locked. */
1217 this._positionLocked = false;
1218 /** Amount of space that must be maintained between the overlay and the edge of the viewport. */
1219 this._viewportMargin = 0;
1220 /** The Scrollable containers used to check scrollable view properties on position change. */
1221 this._scrollables = [];
1222 /** Ordered list of preferred positions, from most to least desirable. */
1223 this._preferredPositions = [];
1224 /** Subject that emits whenever the position changes. */
1225 this._positionChanges = new Subject();
1226 /** Subscription to viewport size changes. */
1227 this._resizeSubscription = Subscription.EMPTY;
1228 /** Default offset for the overlay along the x axis. */
1229 this._offsetX = 0;
1230 /** Default offset for the overlay along the y axis. */
1231 this._offsetY = 0;
1232 /** Keeps track of the CSS classes that the position strategy has applied on the overlay panel. */
1233 this._appliedPanelClasses = [];
1234 /** Observable sequence of position changes. */
1235 this.positionChanges = this._positionChanges;
1236 this.setOrigin(connectedTo);
1237 }
1238 /** Ordered list of preferred positions, from most to least desirable. */
1239 get positions() {
1240 return this._preferredPositions;
1241 }
1242 /** Attaches this position strategy to an overlay. */
1243 attach(overlayRef) {
1244 if (this._overlayRef && overlayRef !== this._overlayRef &&
1245 (typeof ngDevMode === 'undefined' || ngDevMode)) {
1246 throw Error('This position strategy is already attached to an overlay');
1247 }
1248 this._validatePositions();
1249 overlayRef.hostElement.classList.add(boundingBoxClass);
1250 this._overlayRef = overlayRef;
1251 this._boundingBox = overlayRef.hostElement;
1252 this._pane = overlayRef.overlayElement;
1253 this._isDisposed = false;
1254 this._isInitialRender = true;
1255 this._lastPosition = null;
1256 this._resizeSubscription.unsubscribe();
1257 this._resizeSubscription = this._viewportRuler.change().subscribe(() => {
1258 // When the window is resized, we want to trigger the next reposition as if it
1259 // was an initial render, in order for the strategy to pick a new optimal position,
1260 // otherwise position locking will cause it to stay at the old one.
1261 this._isInitialRender = true;
1262 this.apply();
1263 });
1264 }
1265 /**
1266 * Updates the position of the overlay element, using whichever preferred position relative
1267 * to the origin best fits on-screen.
1268 *
1269 * The selection of a position goes as follows:
1270 * - If any positions fit completely within the viewport as-is,
1271 * choose the first position that does so.
1272 * - If flexible dimensions are enabled and at least one satifies the given minimum width/height,
1273 * choose the position with the greatest available size modified by the positions' weight.
1274 * - If pushing is enabled, take the position that went off-screen the least and push it
1275 * on-screen.
1276 * - If none of the previous criteria were met, use the position that goes off-screen the least.
1277 * @docs-private
1278 */
1279 apply() {
1280 // We shouldn't do anything if the strategy was disposed or we're on the server.
1281 if (this._isDisposed || !this._platform.isBrowser) {
1282 return;
1283 }
1284 // If the position has been applied already (e.g. when the overlay was opened) and the
1285 // consumer opted into locking in the position, re-use the old position, in order to
1286 // prevent the overlay from jumping around.
1287 if (!this._isInitialRender && this._positionLocked && this._lastPosition) {
1288 this.reapplyLastPosition();
1289 return;
1290 }
1291 this._clearPanelClasses();
1292 this._resetOverlayElementStyles();
1293 this._resetBoundingBoxStyles();
1294 // We need the bounding rects for the origin and the overlay to determine how to position
1295 // the overlay relative to the origin.
1296 // We use the viewport rect to determine whether a position would go off-screen.
1297 this._viewportRect = this._getNarrowedViewportRect();
1298 this._originRect = this._getOriginRect();
1299 this._overlayRect = this._pane.getBoundingClientRect();
1300 const originRect = this._originRect;
1301 const overlayRect = this._overlayRect;
1302 const viewportRect = this._viewportRect;
1303 // Positions where the overlay will fit with flexible dimensions.
1304 const flexibleFits = [];
1305 // Fallback if none of the preferred positions fit within the viewport.
1306 let fallback;
1307 // Go through each of the preferred positions looking for a good fit.
1308 // If a good fit is found, it will be applied immediately.
1309 for (let pos of this._preferredPositions) {
1310 // Get the exact (x, y) coordinate for the point-of-origin on the origin element.
1311 let originPoint = this._getOriginPoint(originRect, pos);
1312 // From that point-of-origin, get the exact (x, y) coordinate for the top-left corner of the
1313 // overlay in this position. We use the top-left corner for calculations and later translate
1314 // this into an appropriate (top, left, bottom, right) style.
1315 let overlayPoint = this._getOverlayPoint(originPoint, overlayRect, pos);
1316 // Calculate how well the overlay would fit into the viewport with this point.
1317 let overlayFit = this._getOverlayFit(overlayPoint, overlayRect, viewportRect, pos);
1318 // If the overlay, without any further work, fits into the viewport, use this position.
1319 if (overlayFit.isCompletelyWithinViewport) {
1320 this._isPushed = false;
1321 this._applyPosition(pos, originPoint);
1322 return;
1323 }
1324 // If the overlay has flexible dimensions, we can use this position
1325 // so long as there's enough space for the minimum dimensions.
1326 if (this._canFitWithFlexibleDimensions(overlayFit, overlayPoint, viewportRect)) {
1327 // Save positions where the overlay will fit with flexible dimensions. We will use these
1328 // if none of the positions fit *without* flexible dimensions.
1329 flexibleFits.push({
1330 position: pos,
1331 origin: originPoint,
1332 overlayRect,
1333 boundingBoxRect: this._calculateBoundingBoxRect(originPoint, pos)
1334 });
1335 continue;
1336 }
1337 // If the current preferred position does not fit on the screen, remember the position
1338 // if it has more visible area on-screen than we've seen and move onto the next preferred
1339 // position.
1340 if (!fallback || fallback.overlayFit.visibleArea < overlayFit.visibleArea) {
1341 fallback = { overlayFit, overlayPoint, originPoint, position: pos, overlayRect };
1342 }
1343 }
1344 // If there are any positions where the overlay would fit with flexible dimensions, choose the
1345 // one that has the greatest area available modified by the position's weight
1346 if (flexibleFits.length) {
1347 let bestFit = null;
1348 let bestScore = -1;
1349 for (const fit of flexibleFits) {
1350 const score = fit.boundingBoxRect.width * fit.boundingBoxRect.height * (fit.position.weight || 1);
1351 if (score > bestScore) {
1352 bestScore = score;
1353 bestFit = fit;
1354 }
1355 }
1356 this._isPushed = false;
1357 this._applyPosition(bestFit.position, bestFit.origin);
1358 return;
1359 }
1360 // When none of the preferred positions fit within the viewport, take the position
1361 // that went off-screen the least and attempt to push it on-screen.
1362 if (this._canPush) {
1363 // TODO(jelbourn): after pushing, the opening "direction" of the overlay might not make sense.
1364 this._isPushed = true;
1365 this._applyPosition(fallback.position, fallback.originPoint);
1366 return;
1367 }
1368 // All options for getting the overlay within the viewport have been exhausted, so go with the
1369 // position that went off-screen the least.
1370 this._applyPosition(fallback.position, fallback.originPoint);
1371 }
1372 detach() {
1373 this._clearPanelClasses();
1374 this._lastPosition = null;
1375 this._previousPushAmount = null;
1376 this._resizeSubscription.unsubscribe();
1377 }
1378 /** Cleanup after the element gets destroyed. */
1379 dispose() {
1380 if (this._isDisposed) {
1381 return;
1382 }
1383 // We can't use `_resetBoundingBoxStyles` here, because it resets
1384 // some properties to zero, rather than removing them.
1385 if (this._boundingBox) {
1386 extendStyles(this._boundingBox.style, {
1387 top: '',
1388 left: '',
1389 right: '',
1390 bottom: '',
1391 height: '',
1392 width: '',
1393 alignItems: '',
1394 justifyContent: '',
1395 });
1396 }
1397 if (this._pane) {
1398 this._resetOverlayElementStyles();
1399 }
1400 if (this._overlayRef) {
1401 this._overlayRef.hostElement.classList.remove(boundingBoxClass);
1402 }
1403 this.detach();
1404 this._positionChanges.complete();
1405 this._overlayRef = this._boundingBox = null;
1406 this._isDisposed = true;
1407 }
1408 /**
1409 * This re-aligns the overlay element with the trigger in its last calculated position,
1410 * even if a position higher in the "preferred positions" list would now fit. This
1411 * allows one to re-align the panel without changing the orientation of the panel.
1412 */
1413 reapplyLastPosition() {
1414 if (!this._isDisposed && (!this._platform || this._platform.isBrowser)) {
1415 this._originRect = this._getOriginRect();
1416 this._overlayRect = this._pane.getBoundingClientRect();
1417 this._viewportRect = this._getNarrowedViewportRect();
1418 const lastPosition = this._lastPosition || this._preferredPositions[0];
1419 const originPoint = this._getOriginPoint(this._originRect, lastPosition);
1420 this._applyPosition(lastPosition, originPoint);
1421 }
1422 }
1423 /**
1424 * Sets the list of Scrollable containers that host the origin element so that
1425 * on reposition we can evaluate if it or the overlay has been clipped or outside view. Every
1426 * Scrollable must be an ancestor element of the strategy's origin element.
1427 */
1428 withScrollableContainers(scrollables) {
1429 this._scrollables = scrollables;
1430 return this;
1431 }
1432 /**
1433 * Adds new preferred positions.
1434 * @param positions List of positions options for this overlay.
1435 */
1436 withPositions(positions) {
1437 this._preferredPositions = positions;
1438 // If the last calculated position object isn't part of the positions anymore, clear
1439 // it in order to avoid it being picked up if the consumer tries to re-apply.
1440 if (positions.indexOf(this._lastPosition) === -1) {
1441 this._lastPosition = null;
1442 }
1443 this._validatePositions();
1444 return this;
1445 }
1446 /**
1447 * Sets a minimum distance the overlay may be positioned to the edge of the viewport.
1448 * @param margin Required margin between the overlay and the viewport edge in pixels.
1449 */
1450 withViewportMargin(margin) {
1451 this._viewportMargin = margin;
1452 return this;
1453 }
1454 /** Sets whether the overlay's width and height can be constrained to fit within the viewport. */
1455 withFlexibleDimensions(flexibleDimensions = true) {
1456 this._hasFlexibleDimensions = flexibleDimensions;
1457 return this;
1458 }
1459 /** Sets whether the overlay can grow after the initial open via flexible width/height. */
1460 withGrowAfterOpen(growAfterOpen = true) {
1461 this._growAfterOpen = growAfterOpen;
1462 return this;
1463 }
1464 /** Sets whether the overlay can be pushed on-screen if none of the provided positions fit. */
1465 withPush(canPush = true) {
1466 this._canPush = canPush;
1467 return this;
1468 }
1469 /**
1470 * Sets whether the overlay's position should be locked in after it is positioned
1471 * initially. When an overlay is locked in, it won't attempt to reposition itself
1472 * when the position is re-applied (e.g. when the user scrolls away).
1473 * @param isLocked Whether the overlay should locked in.
1474 */
1475 withLockedPosition(isLocked = true) {
1476 this._positionLocked = isLocked;
1477 return this;
1478 }
1479 /**
1480 * Sets the origin, relative to which to position the overlay.
1481 * Using an element origin is useful for building components that need to be positioned
1482 * relatively to a trigger (e.g. dropdown menus or tooltips), whereas using a point can be
1483 * used for cases like contextual menus which open relative to the user's pointer.
1484 * @param origin Reference to the new origin.
1485 */
1486 setOrigin(origin) {
1487 this._origin = origin;
1488 return this;
1489 }
1490 /**
1491 * Sets the default offset for the overlay's connection point on the x-axis.
1492 * @param offset New offset in the X axis.
1493 */
1494 withDefaultOffsetX(offset) {
1495 this._offsetX = offset;
1496 return this;
1497 }
1498 /**
1499 * Sets the default offset for the overlay's connection point on the y-axis.
1500 * @param offset New offset in the Y axis.
1501 */
1502 withDefaultOffsetY(offset) {
1503 this._offsetY = offset;
1504 return this;
1505 }
1506 /**
1507 * Configures that the position strategy should set a `transform-origin` on some elements
1508 * inside the overlay, depending on the current position that is being applied. This is
1509 * useful for the cases where the origin of an animation can change depending on the
1510 * alignment of the overlay.
1511 * @param selector CSS selector that will be used to find the target
1512 * elements onto which to set the transform origin.
1513 */
1514 withTransformOriginOn(selector) {
1515 this._transformOriginSelector = selector;
1516 return this;
1517 }
1518 /**
1519 * Gets the (x, y) coordinate of a connection point on the origin based on a relative position.
1520 */
1521 _getOriginPoint(originRect, pos) {
1522 let x;
1523 if (pos.originX == 'center') {
1524 // Note: when centering we should always use the `left`
1525 // offset, otherwise the position will be wrong in RTL.
1526 x = originRect.left + (originRect.width / 2);
1527 }
1528 else {
1529 const startX = this._isRtl() ? originRect.right : originRect.left;
1530 const endX = this._isRtl() ? originRect.left : originRect.right;
1531 x = pos.originX == 'start' ? startX : endX;
1532 }
1533 let y;
1534 if (pos.originY == 'center') {
1535 y = originRect.top + (originRect.height / 2);
1536 }
1537 else {
1538 y = pos.originY == 'top' ? originRect.top : originRect.bottom;
1539 }
1540 return { x, y };
1541 }
1542 /**
1543 * Gets the (x, y) coordinate of the top-left corner of the overlay given a given position and
1544 * origin point to which the overlay should be connected.
1545 */
1546 _getOverlayPoint(originPoint, overlayRect, pos) {
1547 // Calculate the (overlayStartX, overlayStartY), the start of the
1548 // potential overlay position relative to the origin point.
1549 let overlayStartX;
1550 if (pos.overlayX == 'center') {
1551 overlayStartX = -overlayRect.width / 2;
1552 }
1553 else if (pos.overlayX === 'start') {
1554 overlayStartX = this._isRtl() ? -overlayRect.width : 0;
1555 }
1556 else {
1557 overlayStartX = this._isRtl() ? 0 : -overlayRect.width;
1558 }
1559 let overlayStartY;
1560 if (pos.overlayY == 'center') {
1561 overlayStartY = -overlayRect.height / 2;
1562 }
1563 else {
1564 overlayStartY = pos.overlayY == 'top' ? 0 : -overlayRect.height;
1565 }
1566 // The (x, y) coordinates of the overlay.
1567 return {
1568 x: originPoint.x + overlayStartX,
1569 y: originPoint.y + overlayStartY,
1570 };
1571 }
1572 /** Gets how well an overlay at the given point will fit within the viewport. */
1573 _getOverlayFit(point, rawOverlayRect, viewport, position) {
1574 // Round the overlay rect when comparing against the
1575 // viewport, because the viewport is always rounded.
1576 const overlay = getRoundedBoundingClientRect(rawOverlayRect);
1577 let { x, y } = point;
1578 let offsetX = this._getOffset(position, 'x');
1579 let offsetY = this._getOffset(position, 'y');
1580 // Account for the offsets since they could push the overlay out of the viewport.
1581 if (offsetX) {
1582 x += offsetX;
1583 }
1584 if (offsetY) {
1585 y += offsetY;
1586 }
1587 // How much the overlay would overflow at this position, on each side.
1588 let leftOverflow = 0 - x;
1589 let rightOverflow = (x + overlay.width) - viewport.width;
1590 let topOverflow = 0 - y;
1591 let bottomOverflow = (y + overlay.height) - viewport.height;
1592 // Visible parts of the element on each axis.
1593 let visibleWidth = this._subtractOverflows(overlay.width, leftOverflow, rightOverflow);
1594 let visibleHeight = this._subtractOverflows(overlay.height, topOverflow, bottomOverflow);
1595 let visibleArea = visibleWidth * visibleHeight;
1596 return {
1597 visibleArea,
1598 isCompletelyWithinViewport: (overlay.width * overlay.height) === visibleArea,
1599 fitsInViewportVertically: visibleHeight === overlay.height,
1600 fitsInViewportHorizontally: visibleWidth == overlay.width,
1601 };
1602 }
1603 /**
1604 * Whether the overlay can fit within the viewport when it may resize either its width or height.
1605 * @param fit How well the overlay fits in the viewport at some position.
1606 * @param point The (x, y) coordinates of the overlat at some position.
1607 * @param viewport The geometry of the viewport.
1608 */
1609 _canFitWithFlexibleDimensions(fit, point, viewport) {
1610 if (this._hasFlexibleDimensions) {
1611 const availableHeight = viewport.bottom - point.y;
1612 const availableWidth = viewport.right - point.x;
1613 const minHeight = getPixelValue(this._overlayRef.getConfig().minHeight);
1614 const minWidth = getPixelValue(this._overlayRef.getConfig().minWidth);
1615 const verticalFit = fit.fitsInViewportVertically ||
1616 (minHeight != null && minHeight <= availableHeight);
1617 const horizontalFit = fit.fitsInViewportHorizontally ||
1618 (minWidth != null && minWidth <= availableWidth);
1619 return verticalFit && horizontalFit;
1620 }
1621 return false;
1622 }
1623 /**
1624 * Gets the point at which the overlay can be "pushed" on-screen. If the overlay is larger than
1625 * the viewport, the top-left corner will be pushed on-screen (with overflow occuring on the
1626 * right and bottom).
1627 *
1628 * @param start Starting point from which the overlay is pushed.
1629 * @param overlay Dimensions of the overlay.
1630 * @param scrollPosition Current viewport scroll position.
1631 * @returns The point at which to position the overlay after pushing. This is effectively a new
1632 * originPoint.
1633 */
1634 _pushOverlayOnScreen(start, rawOverlayRect, scrollPosition) {
1635 // If the position is locked and we've pushed the overlay already, reuse the previous push
1636 // amount, rather than pushing it again. If we were to continue pushing, the element would
1637 // remain in the viewport, which goes against the expectations when position locking is enabled.
1638 if (this._previousPushAmount && this._positionLocked) {
1639 return {
1640 x: start.x + this._previousPushAmount.x,
1641 y: start.y + this._previousPushAmount.y
1642 };
1643 }
1644 // Round the overlay rect when comparing against the
1645 // viewport, because the viewport is always rounded.
1646 const overlay = getRoundedBoundingClientRect(rawOverlayRect);
1647 const viewport = this._viewportRect;
1648 // Determine how much the overlay goes outside the viewport on each
1649 // side, which we'll use to decide which direction to push it.
1650 const overflowRight = Math.max(start.x + overlay.width - viewport.width, 0);
1651 const overflowBottom = Math.max(start.y + overlay.height - viewport.height, 0);
1652 const overflowTop = Math.max(viewport.top - scrollPosition.top - start.y, 0);
1653 const overflowLeft = Math.max(viewport.left - scrollPosition.left - start.x, 0);
1654 // Amount by which to push the overlay in each axis such that it remains on-screen.
1655 let pushX = 0;
1656 let pushY = 0;
1657 // If the overlay fits completely within the bounds of the viewport, push it from whichever
1658 // direction is goes off-screen. Otherwise, push the top-left corner such that its in the
1659 // viewport and allow for the trailing end of the overlay to go out of bounds.
1660 if (overlay.width <= viewport.width) {
1661 pushX = overflowLeft || -overflowRight;
1662 }
1663 else {
1664 pushX = start.x < this._viewportMargin ? (viewport.left - scrollPosition.left) - start.x : 0;
1665 }
1666 if (overlay.height <= viewport.height) {
1667 pushY = overflowTop || -overflowBottom;
1668 }
1669 else {
1670 pushY = start.y < this._viewportMargin ? (viewport.top - scrollPosition.top) - start.y : 0;
1671 }
1672 this._previousPushAmount = { x: pushX, y: pushY };
1673 return {
1674 x: start.x + pushX,
1675 y: start.y + pushY,
1676 };
1677 }
1678 /**
1679 * Applies a computed position to the overlay and emits a position change.
1680 * @param position The position preference
1681 * @param originPoint The point on the origin element where the overlay is connected.
1682 */
1683 _applyPosition(position, originPoint) {
1684 this._setTransformOrigin(position);
1685 this._setOverlayElementStyles(originPoint, position);
1686 this._setBoundingBoxStyles(originPoint, position);
1687 if (position.panelClass) {
1688 this._addPanelClasses(position.panelClass);
1689 }
1690 // Save the last connected position in case the position needs to be re-calculated.
1691 this._lastPosition = position;
1692 // Notify that the position has been changed along with its change properties.
1693 // We only emit if we've got any subscriptions, because the scroll visibility
1694 // calculcations can be somewhat expensive.
1695 if (this._positionChanges.observers.length) {
1696 const scrollableViewProperties = this._getScrollVisibility();
1697 const changeEvent = new ConnectedOverlayPositionChange(position, scrollableViewProperties);
1698 this._positionChanges.next(changeEvent);
1699 }
1700 this._isInitialRender = false;
1701 }
1702 /** Sets the transform origin based on the configured selector and the passed-in position. */
1703 _setTransformOrigin(position) {
1704 if (!this._transformOriginSelector) {
1705 return;
1706 }
1707 const elements = this._boundingBox.querySelectorAll(this._transformOriginSelector);
1708 let xOrigin;
1709 let yOrigin = position.overlayY;
1710 if (position.overlayX === 'center') {
1711 xOrigin = 'center';
1712 }
1713 else if (this._isRtl()) {
1714 xOrigin = position.overlayX === 'start' ? 'right' : 'left';
1715 }
1716 else {
1717 xOrigin = position.overlayX === 'start' ? 'left' : 'right';
1718 }
1719 for (let i = 0; i < elements.length; i++) {
1720 elements[i].style.transformOrigin = `${xOrigin} ${yOrigin}`;
1721 }
1722 }
1723 /**
1724 * Gets the position and size of the overlay's sizing container.
1725 *
1726 * This method does no measuring and applies no styles so that we can cheaply compute the
1727 * bounds for all positions and choose the best fit based on these results.
1728 */
1729 _calculateBoundingBoxRect(origin, position) {
1730 const viewport = this._viewportRect;
1731 const isRtl = this._isRtl();
1732 let height, top, bottom;
1733 if (position.overlayY === 'top') {
1734 // Overlay is opening "downward" and thus is bound by the bottom viewport edge.
1735 top = origin.y;
1736 height = viewport.height - top + this._viewportMargin;
1737 }
1738 else if (position.overlayY === 'bottom') {
1739 // Overlay is opening "upward" and thus is bound by the top viewport edge. We need to add
1740 // the viewport margin back in, because the viewport rect is narrowed down to remove the
1741 // margin, whereas the `origin` position is calculated based on its `ClientRect`.
1742 bottom = viewport.height - origin.y + this._viewportMargin * 2;
1743 height = viewport.height - bottom + this._viewportMargin;
1744 }
1745 else {
1746 // If neither top nor bottom, it means that the overlay is vertically centered on the
1747 // origin point. Note that we want the position relative to the viewport, rather than
1748 // the page, which is why we don't use something like `viewport.bottom - origin.y` and
1749 // `origin.y - viewport.top`.
1750 const smallestDistanceToViewportEdge = Math.min(viewport.bottom - origin.y + viewport.top, origin.y);
1751 const previousHeight = this._lastBoundingBoxSize.height;
1752 height = smallestDistanceToViewportEdge * 2;
1753 top = origin.y - smallestDistanceToViewportEdge;
1754 if (height > previousHeight && !this._isInitialRender && !this._growAfterOpen) {
1755 top = origin.y - (previousHeight / 2);
1756 }
1757 }
1758 // The overlay is opening 'right-ward' (the content flows to the right).
1759 const isBoundedByRightViewportEdge = (position.overlayX === 'start' && !isRtl) ||
1760 (position.overlayX === 'end' && isRtl);
1761 // The overlay is opening 'left-ward' (the content flows to the left).
1762 const isBoundedByLeftViewportEdge = (position.overlayX === 'end' && !isRtl) ||
1763 (position.overlayX === 'start' && isRtl);
1764 let width, left, right;
1765 if (isBoundedByLeftViewportEdge) {
1766 right = viewport.width - origin.x + this._viewportMargin;
1767 width = origin.x - this._viewportMargin;
1768 }
1769 else if (isBoundedByRightViewportEdge) {
1770 left = origin.x;
1771 width = viewport.right - origin.x;
1772 }
1773 else {
1774 // If neither start nor end, it means that the overlay is horizontally centered on the
1775 // origin point. Note that we want the position relative to the viewport, rather than
1776 // the page, which is why we don't use something like `viewport.right - origin.x` and
1777 // `origin.x - viewport.left`.
1778 const smallestDistanceToViewportEdge = Math.min(viewport.right - origin.x + viewport.left, origin.x);
1779 const previousWidth = this._lastBoundingBoxSize.width;
1780 width = smallestDistanceToViewportEdge * 2;
1781 left = origin.x - smallestDistanceToViewportEdge;
1782 if (width > previousWidth && !this._isInitialRender && !this._growAfterOpen) {
1783 left = origin.x - (previousWidth / 2);
1784 }
1785 }
1786 return { top: top, left: left, bottom: bottom, right: right, width, height };
1787 }
1788 /**
1789 * Sets the position and size of the overlay's sizing wrapper. The wrapper is positioned on the
1790 * origin's connection point and stetches to the bounds of the viewport.
1791 *
1792 * @param origin The point on the origin element where the overlay is connected.
1793 * @param position The position preference
1794 */
1795 _setBoundingBoxStyles(origin, position) {
1796 const boundingBoxRect = this._calculateBoundingBoxRect(origin, position);
1797 // It's weird if the overlay *grows* while scrolling, so we take the last size into account
1798 // when applying a new size.
1799 if (!this._isInitialRender && !this._growAfterOpen) {
1800 boundingBoxRect.height = Math.min(boundingBoxRect.height, this._lastBoundingBoxSize.height);
1801 boundingBoxRect.width = Math.min(boundingBoxRect.width, this._lastBoundingBoxSize.width);
1802 }
1803 const styles = {};
1804 if (this._hasExactPosition()) {
1805 styles.top = styles.left = '0';
1806 styles.bottom = styles.right = styles.maxHeight = styles.maxWidth = '';
1807 styles.width = styles.height = '100%';
1808 }
1809 else {
1810 const maxHeight = this._overlayRef.getConfig().maxHeight;
1811 const maxWidth = this._overlayRef.getConfig().maxWidth;
1812 styles.height = coerceCssPixelValue(boundingBoxRect.height);
1813 styles.top = coerceCssPixelValue(boundingBoxRect.top);
1814 styles.bottom = coerceCssPixelValue(boundingBoxRect.bottom);
1815 styles.width = coerceCssPixelValue(boundingBoxRect.width);
1816 styles.left = coerceCssPixelValue(boundingBoxRect.left);
1817 styles.right = coerceCssPixelValue(boundingBoxRect.right);
1818 // Push the pane content towards the proper direction.
1819 if (position.overlayX === 'center') {
1820 styles.alignItems = 'center';
1821 }
1822 else {
1823 styles.alignItems = position.overlayX === 'end' ? 'flex-end' : 'flex-start';
1824 }
1825 if (position.overlayY === 'center') {
1826 styles.justifyContent = 'center';
1827 }
1828 else {
1829 styles.justifyContent = position.overlayY === 'bottom' ? 'flex-end' : 'flex-start';
1830 }
1831 if (maxHeight) {
1832 styles.maxHeight = coerceCssPixelValue(maxHeight);
1833 }
1834 if (maxWidth) {
1835 styles.maxWidth = coerceCssPixelValue(maxWidth);
1836 }
1837 }
1838 this._lastBoundingBoxSize = boundingBoxRect;
1839 extendStyles(this._boundingBox.style, styles);
1840 }
1841 /** Resets the styles for the bounding box so that a new positioning can be computed. */
1842 _resetBoundingBoxStyles() {
1843 extendStyles(this._boundingBox.style, {
1844 top: '0',
1845 left: '0',
1846 right: '0',
1847 bottom: '0',
1848 height: '',
1849 width: '',
1850 alignItems: '',
1851 justifyContent: '',
1852 });
1853 }
1854 /** Resets the styles for the overlay pane so that a new positioning can be computed. */
1855 _resetOverlayElementStyles() {
1856 extendStyles(this._pane.style, {
1857 top: '',
1858 left: '',
1859 bottom: '',
1860 right: '',
1861 position: '',
1862 transform: '',
1863 });
1864 }
1865 /** Sets positioning styles to the overlay element. */
1866 _setOverlayElementStyles(originPoint, position) {
1867 const styles = {};
1868 const hasExactPosition = this._hasExactPosition();
1869 const hasFlexibleDimensions = this._hasFlexibleDimensions;
1870 const config = this._overlayRef.getConfig();
1871 if (hasExactPosition) {
1872 const scrollPosition = this._viewportRuler.getViewportScrollPosition();
1873 extendStyles(styles, this._getExactOverlayY(position, originPoint, scrollPosition));
1874 extendStyles(styles, this._getExactOverlayX(position, originPoint, scrollPosition));
1875 }
1876 else {
1877 styles.position = 'static';
1878 }
1879 // Use a transform to apply the offsets. We do this because the `center` positions rely on
1880 // being in the normal flex flow and setting a `top` / `left` at all will completely throw
1881 // off the position. We also can't use margins, because they won't have an effect in some
1882 // cases where the element doesn't have anything to "push off of". Finally, this works
1883 // better both with flexible and non-flexible positioning.
1884 let transformString = '';
1885 let offsetX = this._getOffset(position, 'x');
1886 let offsetY = this._getOffset(position, 'y');
1887 if (offsetX) {
1888 transformString += `translateX(${offsetX}px) `;
1889 }
1890 if (offsetY) {
1891 transformString += `translateY(${offsetY}px)`;
1892 }
1893 styles.transform = transformString.trim();
1894 // If a maxWidth or maxHeight is specified on the overlay, we remove them. We do this because
1895 // we need these values to both be set to "100%" for the automatic flexible sizing to work.
1896 // The maxHeight and maxWidth are set on the boundingBox in order to enforce the constraint.
1897 // Note that this doesn't apply when we have an exact position, in which case we do want to
1898 // apply them because they'll be cleared from the bounding box.
1899 if (config.maxHeight) {
1900 if (hasExactPosition) {
1901 styles.maxHeight = coerceCssPixelValue(config.maxHeight);
1902 }
1903 else if (hasFlexibleDimensions) {
1904 styles.maxHeight = '';
1905 }
1906 }
1907 if (config.maxWidth) {
1908 if (hasExactPosition) {
1909 styles.maxWidth = coerceCssPixelValue(config.maxWidth);
1910 }
1911 else if (hasFlexibleDimensions) {
1912 styles.maxWidth = '';
1913 }
1914 }
1915 extendStyles(this._pane.style, styles);
1916 }
1917 /** Gets the exact top/bottom for the overlay when not using flexible sizing or when pushing. */
1918 _getExactOverlayY(position, originPoint, scrollPosition) {
1919 // Reset any existing styles. This is necessary in case the
1920 // preferred position has changed since the last `apply`.
1921 let styles = { top: '', bottom: '' };
1922 let overlayPoint = this._getOverlayPoint(originPoint, this._overlayRect, position);
1923 if (this._isPushed) {
1924 overlayPoint = this._pushOverlayOnScreen(overlayPoint, this._overlayRect, scrollPosition);
1925 }
1926 let virtualKeyboardOffset = this._overlayContainer.getContainerElement().getBoundingClientRect().top;
1927 // Normally this would be zero, however when the overlay is attached to an input (e.g. in an
1928 // autocomplete), mobile browsers will shift everything in order to put the input in the middle
1929 // of the screen and to make space for the virtual keyboard. We need to account for this offset,
1930 // otherwise our positioning will be thrown off.
1931 overlayPoint.y -= virtualKeyboardOffset;
1932 // We want to set either `top` or `bottom` based on whether the overlay wants to appear
1933 // above or below the origin and the direction in which the element will expand.
1934 if (position.overlayY === 'bottom') {
1935 // When using `bottom`, we adjust the y position such that it is the distance
1936 // from the bottom of the viewport rather than the top.
1937 const documentHeight = this._document.documentElement.clientHeight;
1938 styles.bottom = `${documentHeight - (overlayPoint.y + this._overlayRect.height)}px`;
1939 }
1940 else {
1941 styles.top = coerceCssPixelValue(overlayPoint.y);
1942 }
1943 return styles;
1944 }
1945 /** Gets the exact left/right for the overlay when not using flexible sizing or when pushing. */
1946 _getExactOverlayX(position, originPoint, scrollPosition) {
1947 // Reset any existing styles. This is necessary in case the preferred position has
1948 // changed since the last `apply`.
1949 let styles = { left: '', right: '' };
1950 let overlayPoint = this._getOverlayPoint(originPoint, this._overlayRect, position);
1951 if (this._isPushed) {
1952 overlayPoint = this._pushOverlayOnScreen(overlayPoint, this._overlayRect, scrollPosition);
1953 }
1954 // We want to set either `left` or `right` based on whether the overlay wants to appear "before"
1955 // or "after" the origin, which determines the direction in which the element will expand.
1956 // For the horizontal axis, the meaning of "before" and "after" change based on whether the
1957 // page is in RTL or LTR.
1958 let horizontalStyleProperty;
1959 if (this._isRtl()) {
1960 horizontalStyleProperty = position.overlayX === 'end' ? 'left' : 'right';
1961 }
1962 else {
1963 horizontalStyleProperty = position.overlayX === 'end' ? 'right' : 'left';
1964 }
1965 // When we're setting `right`, we adjust the x position such that it is the distance
1966 // from the right edge of the viewport rather than the left edge.
1967 if (horizontalStyleProperty === 'right') {
1968 const documentWidth = this._document.documentElement.clientWidth;
1969 styles.right = `${documentWidth - (overlayPoint.x + this._overlayRect.width)}px`;
1970 }
1971 else {
1972 styles.left = coerceCssPixelValue(overlayPoint.x);
1973 }
1974 return styles;
1975 }
1976 /**
1977 * Gets the view properties of the trigger and overlay, including whether they are clipped
1978 * or completely outside the view of any of the strategy's scrollables.
1979 */
1980 _getScrollVisibility() {
1981 // Note: needs fresh rects since the position could've changed.
1982 const originBounds = this._getOriginRect();
1983 const overlayBounds = this._pane.getBoundingClientRect();
1984 // TODO(jelbourn): instead of needing all of the client rects for these scrolling containers
1985 // every time, we should be able to use the scrollTop of the containers if the size of those
1986 // containers hasn't changed.
1987 const scrollContainerBounds = this._scrollables.map(scrollable => {
1988 return scrollable.getElementRef().nativeElement.getBoundingClientRect();
1989 });
1990 return {
1991 isOriginClipped: isElementClippedByScrolling(originBounds, scrollContainerBounds),
1992 isOriginOutsideView: isElementScrolledOutsideView(originBounds, scrollContainerBounds),
1993 isOverlayClipped: isElementClippedByScrolling(overlayBounds, scrollContainerBounds),
1994 isOverlayOutsideView: isElementScrolledOutsideView(overlayBounds, scrollContainerBounds),
1995 };
1996 }
1997 /** Subtracts the amount that an element is overflowing on an axis from its length. */
1998 _subtractOverflows(length, ...overflows) {
1999 return overflows.reduce((currentValue, currentOverflow) => {
2000 return currentValue - Math.max(currentOverflow, 0);
2001 }, length);
2002 }
2003 /** Narrows the given viewport rect by the current _viewportMargin. */
2004 _getNarrowedViewportRect() {
2005 // We recalculate the viewport rect here ourselves, rather than using the ViewportRuler,
2006 // because we want to use the `clientWidth` and `clientHeight` as the base. The difference
2007 // being that the client properties don't include the scrollbar, as opposed to `innerWidth`
2008 // and `innerHeight` that do. This is necessary, because the overlay container uses
2009 // 100% `width` and `height` which don't include the scrollbar either.
2010 const width = this._document.documentElement.clientWidth;
2011 const height = this._document.documentElement.clientHeight;
2012 const scrollPosition = this._viewportRuler.getViewportScrollPosition();
2013 return {
2014 top: scrollPosition.top + this._viewportMargin,
2015 left: scrollPosition.left + this._viewportMargin,
2016 right: scrollPosition.left + width - this._viewportMargin,
2017 bottom: scrollPosition.top + height - this._viewportMargin,
2018 width: width - (2 * this._viewportMargin),
2019 height: height - (2 * this._viewportMargin),
2020 };
2021 }
2022 /** Whether the we're dealing with an RTL context */
2023 _isRtl() {
2024 return this._overlayRef.getDirection() === 'rtl';
2025 }
2026 /** Determines whether the overlay uses exact or flexible positioning. */
2027 _hasExactPosition() {
2028 return !this._hasFlexibleDimensions || this._isPushed;
2029 }
2030 /** Retrieves the offset of a position along the x or y axis. */
2031 _getOffset(position, axis) {
2032 if (axis === 'x') {
2033 // We don't do something like `position['offset' + axis]` in
2034 // order to avoid breking minifiers that rename properties.
2035 return position.offsetX == null ? this._offsetX : position.offsetX;
2036 }
2037 return position.offsetY == null ? this._offsetY : position.offsetY;
2038 }
2039 /** Validates that the current position match the expected values. */
2040 _validatePositions() {
2041 if (typeof ngDevMode === 'undefined' || ngDevMode) {
2042 if (!this._preferredPositions.length) {
2043 throw Error('FlexibleConnectedPositionStrategy: At least one position is required.');
2044 }
2045 // TODO(crisbeto): remove these once Angular's template type
2046 // checking is advanced enough to catch these cases.
2047 this._preferredPositions.forEach(pair => {
2048 validateHorizontalPosition('originX', pair.originX);
2049 validateVerticalPosition('originY', pair.originY);
2050 validateHorizontalPosition('overlayX', pair.overlayX);
2051 validateVerticalPosition('overlayY', pair.overlayY);
2052 });
2053 }
2054 }
2055 /** Adds a single CSS class or an array of classes on the overlay panel. */
2056 _addPanelClasses(cssClasses) {
2057 if (this._pane) {
2058 coerceArray(cssClasses).forEach(cssClass => {
2059 if (cssClass !== '' && this._appliedPanelClasses.indexOf(cssClass) === -1) {
2060 this._appliedPanelClasses.push(cssClass);
2061 this._pane.classList.add(cssClass);
2062 }
2063 });
2064 }
2065 }
2066 /** Clears the classes that the position strategy has applied from the overlay panel. */
2067 _clearPanelClasses() {
2068 if (this._pane) {
2069 this._appliedPanelClasses.forEach(cssClass => {
2070 this._pane.classList.remove(cssClass);
2071 });
2072 this._appliedPanelClasses = [];
2073 }
2074 }
2075 /** Returns the ClientRect of the current origin. */
2076 _getOriginRect() {
2077 const origin = this._origin;
2078 if (origin instanceof ElementRef) {
2079 return origin.nativeElement.getBoundingClientRect();
2080 }
2081 // Check for Element so SVG elements are also supported.
2082 if (origin instanceof Element) {
2083 return origin.getBoundingClientRect();
2084 }
2085 const width = origin.width || 0;
2086 const height = origin.height || 0;
2087 // If the origin is a point, return a client rect as if it was a 0x0 element at the point.
2088 return {
2089 top: origin.y,
2090 bottom: origin.y + height,
2091 left: origin.x,
2092 right: origin.x + width,
2093 height,
2094 width
2095 };
2096 }
2097}
2098/** Shallow-extends a stylesheet object with another stylesheet object. */
2099function extendStyles(destination, source) {
2100 for (let key in source) {
2101 if (source.hasOwnProperty(key)) {
2102 destination[key] = source[key];
2103 }
2104 }
2105 return destination;
2106}
2107/**
2108 * Extracts the pixel value as a number from a value, if it's a number
2109 * or a CSS pixel string (e.g. `1337px`). Otherwise returns null.
2110 */
2111function getPixelValue(input) {
2112 if (typeof input !== 'number' && input != null) {
2113 const [value, units] = input.split(cssUnitPattern);
2114 return (!units || units === 'px') ? parseFloat(value) : null;
2115 }
2116 return input || null;
2117}
2118/**
2119 * Gets a version of an element's bounding `ClientRect` where all the values are rounded down to
2120 * the nearest pixel. This allows us to account for the cases where there may be sub-pixel
2121 * deviations in the `ClientRect` returned by the browser (e.g. when zoomed in with a percentage
2122 * size, see #21350).
2123 */
2124function getRoundedBoundingClientRect(clientRect) {
2125 return {
2126 top: Math.floor(clientRect.top),
2127 right: Math.floor(clientRect.right),
2128 bottom: Math.floor(clientRect.bottom),
2129 left: Math.floor(clientRect.left),
2130 width: Math.floor(clientRect.width),
2131 height: Math.floor(clientRect.height)
2132 };
2133}
2134
2135/**
2136 * @license
2137 * Copyright Google LLC All Rights Reserved.
2138 *
2139 * Use of this source code is governed by an MIT-style license that can be
2140 * found in the LICENSE file at https://angular.io/license
2141 */
2142/**
2143 * A strategy for positioning overlays. Using this strategy, an overlay is given an
2144 * implicit position relative to some origin element. The relative position is defined in terms of
2145 * a point on the origin element that is connected to a point on the overlay element. For example,
2146 * a basic dropdown is connecting the bottom-left corner of the origin to the top-left corner
2147 * of the overlay.
2148 * @deprecated Use `FlexibleConnectedPositionStrategy` instead.
2149 * @breaking-change 8.0.0
2150 */
2151class ConnectedPositionStrategy {
2152 constructor(originPos, overlayPos, connectedTo, viewportRuler, document, platform, overlayContainer) {
2153 /** Ordered list of preferred positions, from most to least desirable. */
2154 this._preferredPositions = [];
2155 // Since the `ConnectedPositionStrategy` is deprecated and we don't want to maintain
2156 // the extra logic, we create an instance of the positioning strategy that has some
2157 // defaults that make it behave as the old position strategy and to which we'll
2158 // proxy all of the API calls.
2159 this._positionStrategy = new FlexibleConnectedPositionStrategy(connectedTo, viewportRuler, document, platform, overlayContainer)
2160 .withFlexibleDimensions(false)
2161 .withPush(false)
2162 .withViewportMargin(0);
2163 this.withFallbackPosition(originPos, overlayPos);
2164 this.onPositionChange = this._positionStrategy.positionChanges;
2165 }
2166 /** Ordered list of preferred positions, from most to least desirable. */
2167 get positions() {
2168 return this._preferredPositions;
2169 }
2170 /** Attach this position strategy to an overlay. */
2171 attach(overlayRef) {
2172 this._overlayRef = overlayRef;
2173 this._positionStrategy.attach(overlayRef);
2174 if (this._direction) {
2175 overlayRef.setDirection(this._direction);
2176 this._direction = null;
2177 }
2178 }
2179 /** Disposes all resources used by the position strategy. */
2180 dispose() {
2181 this._positionStrategy.dispose();
2182 }
2183 /** @docs-private */
2184 detach() {
2185 this._positionStrategy.detach();
2186 }
2187 /**
2188 * Updates the position of the overlay element, using whichever preferred position relative
2189 * to the origin fits on-screen.
2190 * @docs-private
2191 */
2192 apply() {
2193 this._positionStrategy.apply();
2194 }
2195 /**
2196 * Re-positions the overlay element with the trigger in its last calculated position,
2197 * even if a position higher in the "preferred positions" list would now fit. This
2198 * allows one to re-align the panel without changing the orientation of the panel.
2199 */
2200 recalculateLastPosition() {
2201 this._positionStrategy.reapplyLastPosition();
2202 }
2203 /**
2204 * Sets the list of Scrollable containers that host the origin element so that
2205 * on reposition we can evaluate if it or the overlay has been clipped or outside view. Every
2206 * Scrollable must be an ancestor element of the strategy's origin element.
2207 */
2208 withScrollableContainers(scrollables) {
2209 this._positionStrategy.withScrollableContainers(scrollables);
2210 }
2211 /**
2212 * Adds a new preferred fallback position.
2213 * @param originPos
2214 * @param overlayPos
2215 */
2216 withFallbackPosition(originPos, overlayPos, offsetX, offsetY) {
2217 const position = new ConnectionPositionPair(originPos, overlayPos, offsetX, offsetY);
2218 this._preferredPositions.push(position);
2219 this._positionStrategy.withPositions(this._preferredPositions);
2220 return this;
2221 }
2222 /**
2223 * Sets the layout direction so the overlay's position can be adjusted to match.
2224 * @param dir New layout direction.
2225 */
2226 withDirection(dir) {
2227 // Since the direction might be declared before the strategy is attached,
2228 // we save the value in a temporary property and we'll transfer it to the
2229 // overlay ref on attachment.
2230 if (this._overlayRef) {
2231 this._overlayRef.setDirection(dir);
2232 }
2233 else {
2234 this._direction = dir;
2235 }
2236 return this;
2237 }
2238 /**
2239 * Sets an offset for the overlay's connection point on the x-axis
2240 * @param offset New offset in the X axis.
2241 */
2242 withOffsetX(offset) {
2243 this._positionStrategy.withDefaultOffsetX(offset);
2244 return this;
2245 }
2246 /**
2247 * Sets an offset for the overlay's connection point on the y-axis
2248 * @param offset New offset in the Y axis.
2249 */
2250 withOffsetY(offset) {
2251 this._positionStrategy.withDefaultOffsetY(offset);
2252 return this;
2253 }
2254 /**
2255 * Sets whether the overlay's position should be locked in after it is positioned
2256 * initially. When an overlay is locked in, it won't attempt to reposition itself
2257 * when the position is re-applied (e.g. when the user scrolls away).
2258 * @param isLocked Whether the overlay should locked in.
2259 */
2260 withLockedPosition(isLocked) {
2261 this._positionStrategy.withLockedPosition(isLocked);
2262 return this;
2263 }
2264 /**
2265 * Overwrites the current set of positions with an array of new ones.
2266 * @param positions Position pairs to be set on the strategy.
2267 */
2268 withPositions(positions) {
2269 this._preferredPositions = positions.slice();
2270 this._positionStrategy.withPositions(this._preferredPositions);
2271 return this;
2272 }
2273 /**
2274 * Sets the origin element, relative to which to position the overlay.
2275 * @param origin Reference to the new origin element.
2276 */
2277 setOrigin(origin) {
2278 this._positionStrategy.setOrigin(origin);
2279 return this;
2280 }
2281}
2282
2283/**
2284 * @license
2285 * Copyright Google LLC All Rights Reserved.
2286 *
2287 * Use of this source code is governed by an MIT-style license that can be
2288 * found in the LICENSE file at https://angular.io/license
2289 */
2290/** Class to be added to the overlay pane wrapper. */
2291const wrapperClass = 'cdk-global-overlay-wrapper';
2292/**
2293 * A strategy for positioning overlays. Using this strategy, an overlay is given an
2294 * explicit position relative to the browser's viewport. We use flexbox, instead of
2295 * transforms, in order to avoid issues with subpixel rendering which can cause the
2296 * element to become blurry.
2297 */
2298class GlobalPositionStrategy {
2299 constructor() {
2300 this._cssPosition = 'static';
2301 this._topOffset = '';
2302 this._bottomOffset = '';
2303 this._leftOffset = '';
2304 this._rightOffset = '';
2305 this._alignItems = '';
2306 this._justifyContent = '';
2307 this._width = '';
2308 this._height = '';
2309 }
2310 attach(overlayRef) {
2311 const config = overlayRef.getConfig();
2312 this._overlayRef = overlayRef;
2313 if (this._width && !config.width) {
2314 overlayRef.updateSize({ width: this._width });
2315 }
2316 if (this._height && !config.height) {
2317 overlayRef.updateSize({ height: this._height });
2318 }
2319 overlayRef.hostElement.classList.add(wrapperClass);
2320 this._isDisposed = false;
2321 }
2322 /**
2323 * Sets the top position of the overlay. Clears any previously set vertical position.
2324 * @param value New top offset.
2325 */
2326 top(value = '') {
2327 this._bottomOffset = '';
2328 this._topOffset = value;
2329 this._alignItems = 'flex-start';
2330 return this;
2331 }
2332 /**
2333 * Sets the left position of the overlay. Clears any previously set horizontal position.
2334 * @param value New left offset.
2335 */
2336 left(value = '') {
2337 this._rightOffset = '';
2338 this._leftOffset = value;
2339 this._justifyContent = 'flex-start';
2340 return this;
2341 }
2342 /**
2343 * Sets the bottom position of the overlay. Clears any previously set vertical position.
2344 * @param value New bottom offset.
2345 */
2346 bottom(value = '') {
2347 this._topOffset = '';
2348 this._bottomOffset = value;
2349 this._alignItems = 'flex-end';
2350 return this;
2351 }
2352 /**
2353 * Sets the right position of the overlay. Clears any previously set horizontal position.
2354 * @param value New right offset.
2355 */
2356 right(value = '') {
2357 this._leftOffset = '';
2358 this._rightOffset = value;
2359 this._justifyContent = 'flex-end';
2360 return this;
2361 }
2362 /**
2363 * Sets the overlay width and clears any previously set width.
2364 * @param value New width for the overlay
2365 * @deprecated Pass the `width` through the `OverlayConfig`.
2366 * @breaking-change 8.0.0
2367 */
2368 width(value = '') {
2369 if (this._overlayRef) {
2370 this._overlayRef.updateSize({ width: value });
2371 }
2372 else {
2373 this._width = value;
2374 }
2375 return this;
2376 }
2377 /**
2378 * Sets the overlay height and clears any previously set height.
2379 * @param value New height for the overlay
2380 * @deprecated Pass the `height` through the `OverlayConfig`.
2381 * @breaking-change 8.0.0
2382 */
2383 height(value = '') {
2384 if (this._overlayRef) {
2385 this._overlayRef.updateSize({ height: value });
2386 }
2387 else {
2388 this._height = value;
2389 }
2390 return this;
2391 }
2392 /**
2393 * Centers the overlay horizontally with an optional offset.
2394 * Clears any previously set horizontal position.
2395 *
2396 * @param offset Overlay offset from the horizontal center.
2397 */
2398 centerHorizontally(offset = '') {
2399 this.left(offset);
2400 this._justifyContent = 'center';
2401 return this;
2402 }
2403 /**
2404 * Centers the overlay vertically with an optional offset.
2405 * Clears any previously set vertical position.
2406 *
2407 * @param offset Overlay offset from the vertical center.
2408 */
2409 centerVertically(offset = '') {
2410 this.top(offset);
2411 this._alignItems = 'center';
2412 return this;
2413 }
2414 /**
2415 * Apply the position to the element.
2416 * @docs-private
2417 */
2418 apply() {
2419 // Since the overlay ref applies the strategy asynchronously, it could
2420 // have been disposed before it ends up being applied. If that is the
2421 // case, we shouldn't do anything.
2422 if (!this._overlayRef || !this._overlayRef.hasAttached()) {
2423 return;
2424 }
2425 const styles = this._overlayRef.overlayElement.style;
2426 const parentStyles = this._overlayRef.hostElement.style;
2427 const config = this._overlayRef.getConfig();
2428 const { width, height, maxWidth, maxHeight } = config;
2429 const shouldBeFlushHorizontally = (width === '100%' || width === '100vw') &&
2430 (!maxWidth || maxWidth === '100%' || maxWidth === '100vw');
2431 const shouldBeFlushVertically = (height === '100%' || height === '100vh') &&
2432 (!maxHeight || maxHeight === '100%' || maxHeight === '100vh');
2433 styles.position = this._cssPosition;
2434 styles.marginLeft = shouldBeFlushHorizontally ? '0' : this._leftOffset;
2435 styles.marginTop = shouldBeFlushVertically ? '0' : this._topOffset;
2436 styles.marginBottom = this._bottomOffset;
2437 styles.marginRight = this._rightOffset;
2438 if (shouldBeFlushHorizontally) {
2439 parentStyles.justifyContent = 'flex-start';
2440 }
2441 else if (this._justifyContent === 'center') {
2442 parentStyles.justifyContent = 'center';
2443 }
2444 else if (this._overlayRef.getConfig().direction === 'rtl') {
2445 // In RTL the browser will invert `flex-start` and `flex-end` automatically, but we
2446 // don't want that because our positioning is explicitly `left` and `right`, hence
2447 // why we do another inversion to ensure that the overlay stays in the same position.
2448 // TODO: reconsider this if we add `start` and `end` methods.
2449 if (this._justifyContent === 'flex-start') {
2450 parentStyles.justifyContent = 'flex-end';
2451 }
2452 else if (this._justifyContent === 'flex-end') {
2453 parentStyles.justifyContent = 'flex-start';
2454 }
2455 }
2456 else {
2457 parentStyles.justifyContent = this._justifyContent;
2458 }
2459 parentStyles.alignItems = shouldBeFlushVertically ? 'flex-start' : this._alignItems;
2460 }
2461 /**
2462 * Cleans up the DOM changes from the position strategy.
2463 * @docs-private
2464 */
2465 dispose() {
2466 if (this._isDisposed || !this._overlayRef) {
2467 return;
2468 }
2469 const styles = this._overlayRef.overlayElement.style;
2470 const parent = this._overlayRef.hostElement;
2471 const parentStyles = parent.style;
2472 parent.classList.remove(wrapperClass);
2473 parentStyles.justifyContent = parentStyles.alignItems = styles.marginTop =
2474 styles.marginBottom = styles.marginLeft = styles.marginRight = styles.position = '';
2475 this._overlayRef = null;
2476 this._isDisposed = true;
2477 }
2478}
2479
2480/**
2481 * @license
2482 * Copyright Google LLC All Rights Reserved.
2483 *
2484 * Use of this source code is governed by an MIT-style license that can be
2485 * found in the LICENSE file at https://angular.io/license
2486 */
2487/** Builder for overlay position strategy. */
2488class OverlayPositionBuilder {
2489 constructor(_viewportRuler, _document, _platform, _overlayContainer) {
2490 this._viewportRuler = _viewportRuler;
2491 this._document = _document;
2492 this._platform = _platform;
2493 this._overlayContainer = _overlayContainer;
2494 }
2495 /**
2496 * Creates a global position strategy.
2497 */
2498 global() {
2499 return new GlobalPositionStrategy();
2500 }
2501 /**
2502 * Creates a relative position strategy.
2503 * @param elementRef
2504 * @param originPos
2505 * @param overlayPos
2506 * @deprecated Use `flexibleConnectedTo` instead.
2507 * @breaking-change 8.0.0
2508 */
2509 connectedTo(elementRef, originPos, overlayPos) {
2510 return new ConnectedPositionStrategy(originPos, overlayPos, elementRef, this._viewportRuler, this._document, this._platform, this._overlayContainer);
2511 }
2512 /**
2513 * Creates a flexible position strategy.
2514 * @param origin Origin relative to which to position the overlay.
2515 */
2516 flexibleConnectedTo(origin) {
2517 return new FlexibleConnectedPositionStrategy(origin, this._viewportRuler, this._document, this._platform, this._overlayContainer);
2518 }
2519}
2520OverlayPositionBuilder.ɵprov = i0.ɵɵdefineInjectable({ factory: function OverlayPositionBuilder_Factory() { return new OverlayPositionBuilder(i0.ɵɵinject(i1.ViewportRuler), i0.ɵɵinject(i1$1.DOCUMENT), i0.ɵɵinject(i2.Platform), i0.ɵɵinject(OverlayContainer)); }, token: OverlayPositionBuilder, providedIn: "root" });
2521OverlayPositionBuilder.decorators = [
2522 { type: Injectable, args: [{ providedIn: 'root' },] }
2523];
2524OverlayPositionBuilder.ctorParameters = () => [
2525 { type: ViewportRuler },
2526 { type: undefined, decorators: [{ type: Inject, args: [DOCUMENT,] }] },
2527 { type: Platform },
2528 { type: OverlayContainer }
2529];
2530
2531/**
2532 * @license
2533 * Copyright Google LLC All Rights Reserved.
2534 *
2535 * Use of this source code is governed by an MIT-style license that can be
2536 * found in the LICENSE file at https://angular.io/license
2537 */
2538/** Next overlay unique ID. */
2539let nextUniqueId = 0;
2540// Note that Overlay is *not* scoped to the app root because of the ComponentFactoryResolver
2541// which needs to be different depending on where OverlayModule is imported.
2542/**
2543 * Service to create Overlays. Overlays are dynamically added pieces of floating UI, meant to be
2544 * used as a low-level building block for other components. Dialogs, tooltips, menus,
2545 * selects, etc. can all be built using overlays. The service should primarily be used by authors
2546 * of re-usable components rather than developers building end-user applications.
2547 *
2548 * An overlay *is* a PortalOutlet, so any kind of Portal can be loaded into one.
2549 */
2550class Overlay {
2551 constructor(
2552 /** Scrolling strategies that can be used when creating an overlay. */
2553 scrollStrategies, _overlayContainer, _componentFactoryResolver, _positionBuilder, _keyboardDispatcher, _injector, _ngZone, _document, _directionality, _location, _outsideClickDispatcher) {
2554 this.scrollStrategies = scrollStrategies;
2555 this._overlayContainer = _overlayContainer;
2556 this._componentFactoryResolver = _componentFactoryResolver;
2557 this._positionBuilder = _positionBuilder;
2558 this._keyboardDispatcher = _keyboardDispatcher;
2559 this._injector = _injector;
2560 this._ngZone = _ngZone;
2561 this._document = _document;
2562 this._directionality = _directionality;
2563 this._location = _location;
2564 this._outsideClickDispatcher = _outsideClickDispatcher;
2565 }
2566 /**
2567 * Creates an overlay.
2568 * @param config Configuration applied to the overlay.
2569 * @returns Reference to the created overlay.
2570 */
2571 create(config) {
2572 const host = this._createHostElement();
2573 const pane = this._createPaneElement(host);
2574 const portalOutlet = this._createPortalOutlet(pane);
2575 const overlayConfig = new OverlayConfig(config);
2576 overlayConfig.direction = overlayConfig.direction || this._directionality.value;
2577 return new OverlayRef(portalOutlet, host, pane, overlayConfig, this._ngZone, this._keyboardDispatcher, this._document, this._location, this._outsideClickDispatcher);
2578 }
2579 /**
2580 * Gets a position builder that can be used, via fluent API,
2581 * to construct and configure a position strategy.
2582 * @returns An overlay position builder.
2583 */
2584 position() {
2585 return this._positionBuilder;
2586 }
2587 /**
2588 * Creates the DOM element for an overlay and appends it to the overlay container.
2589 * @returns Newly-created pane element
2590 */
2591 _createPaneElement(host) {
2592 const pane = this._document.createElement('div');
2593 pane.id = `cdk-overlay-${nextUniqueId++}`;
2594 pane.classList.add('cdk-overlay-pane');
2595 host.appendChild(pane);
2596 return pane;
2597 }
2598 /**
2599 * Creates the host element that wraps around an overlay
2600 * and can be used for advanced positioning.
2601 * @returns Newly-create host element.
2602 */
2603 _createHostElement() {
2604 const host = this._document.createElement('div');
2605 this._overlayContainer.getContainerElement().appendChild(host);
2606 return host;
2607 }
2608 /**
2609 * Create a DomPortalOutlet into which the overlay content can be loaded.
2610 * @param pane The DOM element to turn into a portal outlet.
2611 * @returns A portal outlet for the given DOM element.
2612 */
2613 _createPortalOutlet(pane) {
2614 // We have to resolve the ApplicationRef later in order to allow people
2615 // to use overlay-based providers during app initialization.
2616 if (!this._appRef) {
2617 this._appRef = this._injector.get(ApplicationRef);
2618 }
2619 return new DomPortalOutlet(pane, this._componentFactoryResolver, this._appRef, this._injector, this._document);
2620 }
2621}
2622Overlay.decorators = [
2623 { type: Injectable }
2624];
2625Overlay.ctorParameters = () => [
2626 { type: ScrollStrategyOptions },
2627 { type: OverlayContainer },
2628 { type: ComponentFactoryResolver },
2629 { type: OverlayPositionBuilder },
2630 { type: OverlayKeyboardDispatcher },
2631 { type: Injector },
2632 { type: NgZone },
2633 { type: undefined, decorators: [{ type: Inject, args: [DOCUMENT,] }] },
2634 { type: Directionality },
2635 { type: Location },
2636 { type: OverlayOutsideClickDispatcher }
2637];
2638
2639/**
2640 * @license
2641 * Copyright Google LLC All Rights Reserved.
2642 *
2643 * Use of this source code is governed by an MIT-style license that can be
2644 * found in the LICENSE file at https://angular.io/license
2645 */
2646/** Default set of positions for the overlay. Follows the behavior of a dropdown. */
2647const defaultPositionList = [
2648 {
2649 originX: 'start',
2650 originY: 'bottom',
2651 overlayX: 'start',
2652 overlayY: 'top'
2653 },
2654 {
2655 originX: 'start',
2656 originY: 'top',
2657 overlayX: 'start',
2658 overlayY: 'bottom'
2659 },
2660 {
2661 originX: 'end',
2662 originY: 'top',
2663 overlayX: 'end',
2664 overlayY: 'bottom'
2665 },
2666 {
2667 originX: 'end',
2668 originY: 'bottom',
2669 overlayX: 'end',
2670 overlayY: 'top'
2671 }
2672];
2673/** Injection token that determines the scroll handling while the connected overlay is open. */
2674const CDK_CONNECTED_OVERLAY_SCROLL_STRATEGY = new InjectionToken('cdk-connected-overlay-scroll-strategy');
2675/**
2676 * Directive applied to an element to make it usable as an origin for an Overlay using a
2677 * ConnectedPositionStrategy.
2678 */
2679class CdkOverlayOrigin {
2680 constructor(
2681 /** Reference to the element on which the directive is applied. */
2682 elementRef) {
2683 this.elementRef = elementRef;
2684 }
2685}
2686CdkOverlayOrigin.decorators = [
2687 { type: Directive, args: [{
2688 selector: '[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]',
2689 exportAs: 'cdkOverlayOrigin',
2690 },] }
2691];
2692CdkOverlayOrigin.ctorParameters = () => [
2693 { type: ElementRef }
2694];
2695/**
2696 * Directive to facilitate declarative creation of an
2697 * Overlay using a FlexibleConnectedPositionStrategy.
2698 */
2699class CdkConnectedOverlay {
2700 // TODO(jelbourn): inputs for size, scroll behavior, animation, etc.
2701 constructor(_overlay, templateRef, viewContainerRef, scrollStrategyFactory, _dir) {
2702 this._overlay = _overlay;
2703 this._dir = _dir;
2704 this._hasBackdrop = false;
2705 this._lockPosition = false;
2706 this._growAfterOpen = false;
2707 this._flexibleDimensions = false;
2708 this._push = false;
2709 this._backdropSubscription = Subscription.EMPTY;
2710 this._attachSubscription = Subscription.EMPTY;
2711 this._detachSubscription = Subscription.EMPTY;
2712 this._positionSubscription = Subscription.EMPTY;
2713 /** Margin between the overlay and the viewport edges. */
2714 this.viewportMargin = 0;
2715 /** Whether the overlay is open. */
2716 this.open = false;
2717 /** Whether the overlay can be closed by user interaction. */
2718 this.disableClose = false;
2719 /** Event emitted when the backdrop is clicked. */
2720 this.backdropClick = new EventEmitter();
2721 /** Event emitted when the position has changed. */
2722 this.positionChange = new EventEmitter();
2723 /** Event emitted when the overlay has been attached. */
2724 this.attach = new EventEmitter();
2725 /** Event emitted when the overlay has been detached. */
2726 this.detach = new EventEmitter();
2727 /** Emits when there are keyboard events that are targeted at the overlay. */
2728 this.overlayKeydown = new EventEmitter();
2729 /** Emits when there are mouse outside click events that are targeted at the overlay. */
2730 this.overlayOutsideClick = new EventEmitter();
2731 this._templatePortal = new TemplatePortal(templateRef, viewContainerRef);
2732 this._scrollStrategyFactory = scrollStrategyFactory;
2733 this.scrollStrategy = this._scrollStrategyFactory();
2734 }
2735 /** The offset in pixels for the overlay connection point on the x-axis */
2736 get offsetX() { return this._offsetX; }
2737 set offsetX(offsetX) {
2738 this._offsetX = offsetX;
2739 if (this._position) {
2740 this._updatePositionStrategy(this._position);
2741 }
2742 }
2743 /** The offset in pixels for the overlay connection point on the y-axis */
2744 get offsetY() { return this._offsetY; }
2745 set offsetY(offsetY) {
2746 this._offsetY = offsetY;
2747 if (this._position) {
2748 this._updatePositionStrategy(this._position);
2749 }
2750 }
2751 /** Whether or not the overlay should attach a backdrop. */
2752 get hasBackdrop() { return this._hasBackdrop; }
2753 set hasBackdrop(value) { this._hasBackdrop = coerceBooleanProperty(value); }
2754 /** Whether or not the overlay should be locked when scrolling. */
2755 get lockPosition() { return this._lockPosition; }
2756 set lockPosition(value) { this._lockPosition = coerceBooleanProperty(value); }
2757 /** Whether the overlay's width and height can be constrained to fit within the viewport. */
2758 get flexibleDimensions() { return this._flexibleDimensions; }
2759 set flexibleDimensions(value) {
2760 this._flexibleDimensions = coerceBooleanProperty(value);
2761 }
2762 /** Whether the overlay can grow after the initial open when flexible positioning is turned on. */
2763 get growAfterOpen() { return this._growAfterOpen; }
2764 set growAfterOpen(value) { this._growAfterOpen = coerceBooleanProperty(value); }
2765 /** Whether the overlay can be pushed on-screen if none of the provided positions fit. */
2766 get push() { return this._push; }
2767 set push(value) { this._push = coerceBooleanProperty(value); }
2768 /** The associated overlay reference. */
2769 get overlayRef() {
2770 return this._overlayRef;
2771 }
2772 /** The element's layout direction. */
2773 get dir() {
2774 return this._dir ? this._dir.value : 'ltr';
2775 }
2776 ngOnDestroy() {
2777 this._attachSubscription.unsubscribe();
2778 this._detachSubscription.unsubscribe();
2779 this._backdropSubscription.unsubscribe();
2780 this._positionSubscription.unsubscribe();
2781 if (this._overlayRef) {
2782 this._overlayRef.dispose();
2783 }
2784 }
2785 ngOnChanges(changes) {
2786 if (this._position) {
2787 this._updatePositionStrategy(this._position);
2788 this._overlayRef.updateSize({
2789 width: this.width,
2790 minWidth: this.minWidth,
2791 height: this.height,
2792 minHeight: this.minHeight,
2793 });
2794 if (changes['origin'] && this.open) {
2795 this._position.apply();
2796 }
2797 }
2798 if (changes['open']) {
2799 this.open ? this._attachOverlay() : this._detachOverlay();
2800 }
2801 }
2802 /** Creates an overlay */
2803 _createOverlay() {
2804 if (!this.positions || !this.positions.length) {
2805 this.positions = defaultPositionList;
2806 }
2807 const overlayRef = this._overlayRef = this._overlay.create(this._buildConfig());
2808 this._attachSubscription = overlayRef.attachments().subscribe(() => this.attach.emit());
2809 this._detachSubscription = overlayRef.detachments().subscribe(() => this.detach.emit());
2810 overlayRef.keydownEvents().subscribe((event) => {
2811 this.overlayKeydown.next(event);
2812 if (event.keyCode === ESCAPE && !this.disableClose && !hasModifierKey(event)) {
2813 event.preventDefault();
2814 this._detachOverlay();
2815 }
2816 });
2817 this._overlayRef.outsidePointerEvents().subscribe((event) => {
2818 this.overlayOutsideClick.next(event);
2819 });
2820 }
2821 /** Builds the overlay config based on the directive's inputs */
2822 _buildConfig() {
2823 const positionStrategy = this._position =
2824 this.positionStrategy || this._createPositionStrategy();
2825 const overlayConfig = new OverlayConfig({
2826 direction: this._dir,
2827 positionStrategy,
2828 scrollStrategy: this.scrollStrategy,
2829 hasBackdrop: this.hasBackdrop
2830 });
2831 if (this.width || this.width === 0) {
2832 overlayConfig.width = this.width;
2833 }
2834 if (this.height || this.height === 0) {
2835 overlayConfig.height = this.height;
2836 }
2837 if (this.minWidth || this.minWidth === 0) {
2838 overlayConfig.minWidth = this.minWidth;
2839 }
2840 if (this.minHeight || this.minHeight === 0) {
2841 overlayConfig.minHeight = this.minHeight;
2842 }
2843 if (this.backdropClass) {
2844 overlayConfig.backdropClass = this.backdropClass;
2845 }
2846 if (this.panelClass) {
2847 overlayConfig.panelClass = this.panelClass;
2848 }
2849 return overlayConfig;
2850 }
2851 /** Updates the state of a position strategy, based on the values of the directive inputs. */
2852 _updatePositionStrategy(positionStrategy) {
2853 const positions = this.positions.map(currentPosition => ({
2854 originX: currentPosition.originX,
2855 originY: currentPosition.originY,
2856 overlayX: currentPosition.overlayX,
2857 overlayY: currentPosition.overlayY,
2858 offsetX: currentPosition.offsetX || this.offsetX,
2859 offsetY: currentPosition.offsetY || this.offsetY,
2860 panelClass: currentPosition.panelClass || undefined,
2861 }));
2862 return positionStrategy
2863 .setOrigin(this.origin.elementRef)
2864 .withPositions(positions)
2865 .withFlexibleDimensions(this.flexibleDimensions)
2866 .withPush(this.push)
2867 .withGrowAfterOpen(this.growAfterOpen)
2868 .withViewportMargin(this.viewportMargin)
2869 .withLockedPosition(this.lockPosition)
2870 .withTransformOriginOn(this.transformOriginSelector);
2871 }
2872 /** Returns the position strategy of the overlay to be set on the overlay config */
2873 _createPositionStrategy() {
2874 const strategy = this._overlay.position().flexibleConnectedTo(this.origin.elementRef);
2875 this._updatePositionStrategy(strategy);
2876 return strategy;
2877 }
2878 /** Attaches the overlay and subscribes to backdrop clicks if backdrop exists */
2879 _attachOverlay() {
2880 if (!this._overlayRef) {
2881 this._createOverlay();
2882 }
2883 else {
2884 // Update the overlay size, in case the directive's inputs have changed
2885 this._overlayRef.getConfig().hasBackdrop = this.hasBackdrop;
2886 }
2887 if (!this._overlayRef.hasAttached()) {
2888 this._overlayRef.attach(this._templatePortal);
2889 }
2890 if (this.hasBackdrop) {
2891 this._backdropSubscription = this._overlayRef.backdropClick().subscribe(event => {
2892 this.backdropClick.emit(event);
2893 });
2894 }
2895 else {
2896 this._backdropSubscription.unsubscribe();
2897 }
2898 this._positionSubscription.unsubscribe();
2899 // Only subscribe to `positionChanges` if requested, because putting
2900 // together all the information for it can be expensive.
2901 if (this.positionChange.observers.length > 0) {
2902 this._positionSubscription = this._position.positionChanges
2903 .pipe(takeWhile(() => this.positionChange.observers.length > 0))
2904 .subscribe(position => {
2905 this.positionChange.emit(position);
2906 if (this.positionChange.observers.length === 0) {
2907 this._positionSubscription.unsubscribe();
2908 }
2909 });
2910 }
2911 }
2912 /** Detaches the overlay and unsubscribes to backdrop clicks if backdrop exists */
2913 _detachOverlay() {
2914 if (this._overlayRef) {
2915 this._overlayRef.detach();
2916 }
2917 this._backdropSubscription.unsubscribe();
2918 this._positionSubscription.unsubscribe();
2919 }
2920}
2921CdkConnectedOverlay.decorators = [
2922 { type: Directive, args: [{
2923 selector: '[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]',
2924 exportAs: 'cdkConnectedOverlay'
2925 },] }
2926];
2927CdkConnectedOverlay.ctorParameters = () => [
2928 { type: Overlay },
2929 { type: TemplateRef },
2930 { type: ViewContainerRef },
2931 { type: undefined, decorators: [{ type: Inject, args: [CDK_CONNECTED_OVERLAY_SCROLL_STRATEGY,] }] },
2932 { type: Directionality, decorators: [{ type: Optional }] }
2933];
2934CdkConnectedOverlay.propDecorators = {
2935 origin: [{ type: Input, args: ['cdkConnectedOverlayOrigin',] }],
2936 positions: [{ type: Input, args: ['cdkConnectedOverlayPositions',] }],
2937 positionStrategy: [{ type: Input, args: ['cdkConnectedOverlayPositionStrategy',] }],
2938 offsetX: [{ type: Input, args: ['cdkConnectedOverlayOffsetX',] }],
2939 offsetY: [{ type: Input, args: ['cdkConnectedOverlayOffsetY',] }],
2940 width: [{ type: Input, args: ['cdkConnectedOverlayWidth',] }],
2941 height: [{ type: Input, args: ['cdkConnectedOverlayHeight',] }],
2942 minWidth: [{ type: Input, args: ['cdkConnectedOverlayMinWidth',] }],
2943 minHeight: [{ type: Input, args: ['cdkConnectedOverlayMinHeight',] }],
2944 backdropClass: [{ type: Input, args: ['cdkConnectedOverlayBackdropClass',] }],
2945 panelClass: [{ type: Input, args: ['cdkConnectedOverlayPanelClass',] }],
2946 viewportMargin: [{ type: Input, args: ['cdkConnectedOverlayViewportMargin',] }],
2947 scrollStrategy: [{ type: Input, args: ['cdkConnectedOverlayScrollStrategy',] }],
2948 open: [{ type: Input, args: ['cdkConnectedOverlayOpen',] }],
2949 disableClose: [{ type: Input, args: ['cdkConnectedOverlayDisableClose',] }],
2950 transformOriginSelector: [{ type: Input, args: ['cdkConnectedOverlayTransformOriginOn',] }],
2951 hasBackdrop: [{ type: Input, args: ['cdkConnectedOverlayHasBackdrop',] }],
2952 lockPosition: [{ type: Input, args: ['cdkConnectedOverlayLockPosition',] }],
2953 flexibleDimensions: [{ type: Input, args: ['cdkConnectedOverlayFlexibleDimensions',] }],
2954 growAfterOpen: [{ type: Input, args: ['cdkConnectedOverlayGrowAfterOpen',] }],
2955 push: [{ type: Input, args: ['cdkConnectedOverlayPush',] }],
2956 backdropClick: [{ type: Output }],
2957 positionChange: [{ type: Output }],
2958 attach: [{ type: Output }],
2959 detach: [{ type: Output }],
2960 overlayKeydown: [{ type: Output }],
2961 overlayOutsideClick: [{ type: Output }]
2962};
2963/** @docs-private */
2964function CDK_CONNECTED_OVERLAY_SCROLL_STRATEGY_PROVIDER_FACTORY(overlay) {
2965 return () => overlay.scrollStrategies.reposition();
2966}
2967/** @docs-private */
2968const CDK_CONNECTED_OVERLAY_SCROLL_STRATEGY_PROVIDER = {
2969 provide: CDK_CONNECTED_OVERLAY_SCROLL_STRATEGY,
2970 deps: [Overlay],
2971 useFactory: CDK_CONNECTED_OVERLAY_SCROLL_STRATEGY_PROVIDER_FACTORY,
2972};
2973
2974/**
2975 * @license
2976 * Copyright Google LLC All Rights Reserved.
2977 *
2978 * Use of this source code is governed by an MIT-style license that can be
2979 * found in the LICENSE file at https://angular.io/license
2980 */
2981class OverlayModule {
2982}
2983OverlayModule.decorators = [
2984 { type: NgModule, args: [{
2985 imports: [BidiModule, PortalModule, ScrollingModule],
2986 exports: [CdkConnectedOverlay, CdkOverlayOrigin, ScrollingModule],
2987 declarations: [CdkConnectedOverlay, CdkOverlayOrigin],
2988 providers: [
2989 Overlay,
2990 CDK_CONNECTED_OVERLAY_SCROLL_STRATEGY_PROVIDER,
2991 ],
2992 },] }
2993];
2994
2995/**
2996 * @license
2997 * Copyright Google LLC All Rights Reserved.
2998 *
2999 * Use of this source code is governed by an MIT-style license that can be
3000 * found in the LICENSE file at https://angular.io/license
3001 */
3002
3003/**
3004 * @license
3005 * Copyright Google LLC All Rights Reserved.
3006 *
3007 * Use of this source code is governed by an MIT-style license that can be
3008 * found in the LICENSE file at https://angular.io/license
3009 */
3010/**
3011 * Alternative to OverlayContainer that supports correct displaying of overlay elements in
3012 * Fullscreen mode
3013 * https://developer.mozilla.org/en-US/docs/Web/API/Element/requestFullScreen
3014 *
3015 * Should be provided in the root component.
3016 */
3017class FullscreenOverlayContainer extends OverlayContainer {
3018 constructor(_document, platform) {
3019 super(_document, platform);
3020 }
3021 ngOnDestroy() {
3022 super.ngOnDestroy();
3023 if (this._fullScreenEventName && this._fullScreenListener) {
3024 this._document.removeEventListener(this._fullScreenEventName, this._fullScreenListener);
3025 }
3026 }
3027 _createContainer() {
3028 super._createContainer();
3029 this._adjustParentForFullscreenChange();
3030 this._addFullscreenChangeListener(() => this._adjustParentForFullscreenChange());
3031 }
3032 _adjustParentForFullscreenChange() {
3033 if (!this._containerElement) {
3034 return;
3035 }
3036 const fullscreenElement = this.getFullscreenElement();
3037 const parent = fullscreenElement || this._document.body;
3038 parent.appendChild(this._containerElement);
3039 }
3040 _addFullscreenChangeListener(fn) {
3041 const eventName = this._getEventName();
3042 if (eventName) {
3043 if (this._fullScreenListener) {
3044 this._document.removeEventListener(eventName, this._fullScreenListener);
3045 }
3046 this._document.addEventListener(eventName, fn);
3047 this._fullScreenListener = fn;
3048 }
3049 }
3050 _getEventName() {
3051 if (!this._fullScreenEventName) {
3052 const _document = this._document;
3053 if (_document.fullscreenEnabled) {
3054 this._fullScreenEventName = 'fullscreenchange';
3055 }
3056 else if (_document.webkitFullscreenEnabled) {
3057 this._fullScreenEventName = 'webkitfullscreenchange';
3058 }
3059 else if (_document.mozFullScreenEnabled) {
3060 this._fullScreenEventName = 'mozfullscreenchange';
3061 }
3062 else if (_document.msFullscreenEnabled) {
3063 this._fullScreenEventName = 'MSFullscreenChange';
3064 }
3065 }
3066 return this._fullScreenEventName;
3067 }
3068 /**
3069 * When the page is put into fullscreen mode, a specific element is specified.
3070 * Only that element and its children are visible when in fullscreen mode.
3071 */
3072 getFullscreenElement() {
3073 const _document = this._document;
3074 return _document.fullscreenElement ||
3075 _document.webkitFullscreenElement ||
3076 _document.mozFullScreenElement ||
3077 _document.msFullscreenElement ||
3078 null;
3079 }
3080}
3081FullscreenOverlayContainer.ɵprov = i0.ɵɵdefineInjectable({ factory: function FullscreenOverlayContainer_Factory() { return new FullscreenOverlayContainer(i0.ɵɵinject(i1$1.DOCUMENT), i0.ɵɵinject(i2.Platform)); }, token: FullscreenOverlayContainer, providedIn: "root" });
3082FullscreenOverlayContainer.decorators = [
3083 { type: Injectable, args: [{ providedIn: 'root' },] }
3084];
3085FullscreenOverlayContainer.ctorParameters = () => [
3086 { type: undefined, decorators: [{ type: Inject, args: [DOCUMENT,] }] },
3087 { type: Platform }
3088];
3089
3090/**
3091 * @license
3092 * Copyright Google LLC All Rights Reserved.
3093 *
3094 * Use of this source code is governed by an MIT-style license that can be
3095 * found in the LICENSE file at https://angular.io/license
3096 */
3097
3098/**
3099 * Generated bundle index. Do not edit.
3100 */
3101
3102export { BlockScrollStrategy, CdkConnectedOverlay, CdkOverlayOrigin, CloseScrollStrategy, ConnectedOverlayPositionChange, ConnectedPositionStrategy, ConnectionPositionPair, FlexibleConnectedPositionStrategy, FullscreenOverlayContainer, GlobalPositionStrategy, NoopScrollStrategy, Overlay, OverlayConfig, OverlayContainer, OverlayKeyboardDispatcher, OverlayModule, OverlayOutsideClickDispatcher, OverlayPositionBuilder, OverlayRef, RepositionScrollStrategy, ScrollStrategyOptions, ScrollingVisibility, validateHorizontalPosition, validateVerticalPosition, CDK_CONNECTED_OVERLAY_SCROLL_STRATEGY as ɵangular_material_src_cdk_overlay_overlay_a, CDK_CONNECTED_OVERLAY_SCROLL_STRATEGY_PROVIDER_FACTORY as ɵangular_material_src_cdk_overlay_overlay_b, CDK_CONNECTED_OVERLAY_SCROLL_STRATEGY_PROVIDER as ɵangular_material_src_cdk_overlay_overlay_c, BaseOverlayDispatcher as ɵangular_material_src_cdk_overlay_overlay_d };
3103//# sourceMappingURL=overlay.js.map
Note: See TracBrowser for help on using the repository browser.