@angular/cdk
Version:
Angular Material Component Development Kit
1,457 lines (1,444 loc) • 180 kB
JavaScript
import { ScrollDispatcher, ViewportRuler, ScrollingModule } from '@angular/cdk/scrolling';
export { CdkScrollable, ScrollDispatcher, ViewportRuler } from '@angular/cdk/scrolling';
import { DOCUMENT, Location } from '@angular/common';
import { Injectable, NgZone, Inject, ɵɵdefineInjectable, ɵɵinject, Optional, SkipSelf, ElementRef, ApplicationRef, ComponentFactoryResolver, Injector, InjectionToken, Directive, EventEmitter, TemplateRef, ViewContainerRef, Input, Output, NgModule } from '@angular/core';
import { coerceCssPixelValue, coerceArray, coerceBooleanProperty } from '@angular/cdk/coercion';
import { Directionality, BidiModule } from '@angular/cdk/bidi';
import { DomPortalOutlet, TemplatePortal, PortalModule } from '@angular/cdk/portal';
import { Platform } from '@angular/cdk/platform';
import { Subject, Subscription, Observable, merge } from 'rxjs';
import { take, takeUntil } from 'rxjs/operators';
import { ESCAPE, hasModifierKey } from '@angular/cdk/keycodes';
/**
* @fileoverview added by tsickle
* Generated from: src/cdk/overlay/scroll/block-scroll-strategy.ts
* @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
/**
* Strategy that will prevent the user from scrolling while the overlay is visible.
*/
class BlockScrollStrategy {
/**
* @param {?} _viewportRuler
* @param {?} document
*/
constructor(_viewportRuler, document) {
this._viewportRuler = _viewportRuler;
this._previousHTMLStyles = { top: '', left: '' };
this._isEnabled = false;
this._document = document;
}
/**
* Attaches this scroll strategy to an overlay.
* @return {?}
*/
attach() { }
/**
* Blocks page-level scroll while the attached overlay is open.
* @return {?}
*/
enable() {
if (this._canBeEnabled()) {
/** @type {?} */
const root = (/** @type {?} */ (this._document.documentElement));
this._previousScrollPosition = this._viewportRuler.getViewportScrollPosition();
// Cache the previous inline styles in case the user had set them.
this._previousHTMLStyles.left = root.style.left || '';
this._previousHTMLStyles.top = root.style.top || '';
// Note: we're using the `html` node, instead of the `body`, because the `body` may
// have the user agent margin, whereas the `html` is guaranteed not to have one.
root.style.left = coerceCssPixelValue(-this._previousScrollPosition.left);
root.style.top = coerceCssPixelValue(-this._previousScrollPosition.top);
root.classList.add('cdk-global-scrollblock');
this._isEnabled = true;
}
}
/**
* Unblocks page-level scroll while the attached overlay is open.
* @return {?}
*/
disable() {
if (this._isEnabled) {
/** @type {?} */
const html = (/** @type {?} */ (this._document.documentElement));
/** @type {?} */
const body = (/** @type {?} */ (this._document.body));
/** @type {?} */
const htmlStyle = (/** @type {?} */ (html.style));
/** @type {?} */
const bodyStyle = (/** @type {?} */ (body.style));
/** @type {?} */
const previousHtmlScrollBehavior = htmlStyle.scrollBehavior || '';
/** @type {?} */
const previousBodyScrollBehavior = bodyStyle.scrollBehavior || '';
this._isEnabled = false;
htmlStyle.left = this._previousHTMLStyles.left;
htmlStyle.top = this._previousHTMLStyles.top;
html.classList.remove('cdk-global-scrollblock');
// Disable user-defined smooth scrolling temporarily while we restore the scroll position.
// See https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-behavior
htmlStyle.scrollBehavior = bodyStyle.scrollBehavior = 'auto';
window.scroll(this._previousScrollPosition.left, this._previousScrollPosition.top);
htmlStyle.scrollBehavior = previousHtmlScrollBehavior;
bodyStyle.scrollBehavior = previousBodyScrollBehavior;
}
}
/**
* @private
* @return {?}
*/
_canBeEnabled() {
// Since the scroll strategies can't be singletons, we have to use a global CSS class
// (`cdk-global-scrollblock`) to make sure that we don't try to disable global
// scrolling multiple times.
/** @type {?} */
const html = (/** @type {?} */ (this._document.documentElement));
if (html.classList.contains('cdk-global-scrollblock') || this._isEnabled) {
return false;
}
/** @type {?} */
const body = this._document.body;
/** @type {?} */
const viewport = this._viewportRuler.getViewportSize();
return body.scrollHeight > viewport.height || body.scrollWidth > viewport.width;
}
}
if (false) {
/**
* @type {?}
* @private
*/
BlockScrollStrategy.prototype._previousHTMLStyles;
/**
* @type {?}
* @private
*/
BlockScrollStrategy.prototype._previousScrollPosition;
/**
* @type {?}
* @private
*/
BlockScrollStrategy.prototype._isEnabled;
/**
* @type {?}
* @private
*/
BlockScrollStrategy.prototype._document;
/**
* @type {?}
* @private
*/
BlockScrollStrategy.prototype._viewportRuler;
}
/**
* @fileoverview added by tsickle
* Generated from: src/cdk/overlay/scroll/scroll-strategy.ts
* @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
/**
* Describes a strategy that will be used by an overlay to handle scroll events while it is open.
* @record
*/
function ScrollStrategy() { }
if (false) {
/**
* Enable this scroll strategy (called when the attached overlay is attached to a portal).
* @type {?}
*/
ScrollStrategy.prototype.enable;
/**
* Disable this scroll strategy (called when the attached overlay is detached from a portal).
* @type {?}
*/
ScrollStrategy.prototype.disable;
/**
* Attaches this `ScrollStrategy` to an overlay.
* @type {?}
*/
ScrollStrategy.prototype.attach;
/**
* Detaches the scroll strategy from the current overlay.
* @type {?|undefined}
*/
ScrollStrategy.prototype.detach;
}
/**
* Returns an error to be thrown when attempting to attach an already-attached scroll strategy.
* @return {?}
*/
function getMatScrollStrategyAlreadyAttachedError() {
return Error(`Scroll strategy has already been attached.`);
}
/**
* @fileoverview added by tsickle
* Generated from: src/cdk/overlay/scroll/close-scroll-strategy.ts
* @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
/**
* Config options for the CloseScrollStrategy.
* @record
*/
function CloseScrollStrategyConfig() { }
if (false) {
/**
* Amount of pixels the user has to scroll before the overlay is closed.
* @type {?|undefined}
*/
CloseScrollStrategyConfig.prototype.threshold;
}
/**
* Strategy that will close the overlay as soon as the user starts scrolling.
*/
class CloseScrollStrategy {
/**
* @param {?} _scrollDispatcher
* @param {?} _ngZone
* @param {?} _viewportRuler
* @param {?=} _config
*/
constructor(_scrollDispatcher, _ngZone, _viewportRuler, _config) {
this._scrollDispatcher = _scrollDispatcher;
this._ngZone = _ngZone;
this._viewportRuler = _viewportRuler;
this._config = _config;
this._scrollSubscription = null;
/**
* Detaches the overlay ref and disables the scroll strategy.
*/
this._detach = (/**
* @return {?}
*/
() => {
this.disable();
if (this._overlayRef.hasAttached()) {
this._ngZone.run((/**
* @return {?}
*/
() => this._overlayRef.detach()));
}
});
}
/**
* Attaches this scroll strategy to an overlay.
* @param {?} overlayRef
* @return {?}
*/
attach(overlayRef) {
if (this._overlayRef) {
throw getMatScrollStrategyAlreadyAttachedError();
}
this._overlayRef = overlayRef;
}
/**
* Enables the closing of the attached overlay on scroll.
* @return {?}
*/
enable() {
if (this._scrollSubscription) {
return;
}
/** @type {?} */
const stream = this._scrollDispatcher.scrolled(0);
if (this._config && this._config.threshold && this._config.threshold > 1) {
this._initialScrollPosition = this._viewportRuler.getViewportScrollPosition().top;
this._scrollSubscription = stream.subscribe((/**
* @return {?}
*/
() => {
/** @type {?} */
const scrollPosition = this._viewportRuler.getViewportScrollPosition().top;
if (Math.abs(scrollPosition - this._initialScrollPosition) > (/** @type {?} */ ((/** @type {?} */ (this._config)).threshold))) {
this._detach();
}
else {
this._overlayRef.updatePosition();
}
}));
}
else {
this._scrollSubscription = stream.subscribe(this._detach);
}
}
/**
* Disables the closing the attached overlay on scroll.
* @return {?}
*/
disable() {
if (this._scrollSubscription) {
this._scrollSubscription.unsubscribe();
this._scrollSubscription = null;
}
}
/**
* @return {?}
*/
detach() {
this.disable();
this._overlayRef = (/** @type {?} */ (null));
}
}
if (false) {
/**
* @type {?}
* @private
*/
CloseScrollStrategy.prototype._scrollSubscription;
/**
* @type {?}
* @private
*/
CloseScrollStrategy.prototype._overlayRef;
/**
* @type {?}
* @private
*/
CloseScrollStrategy.prototype._initialScrollPosition;
/**
* Detaches the overlay ref and disables the scroll strategy.
* @type {?}
* @private
*/
CloseScrollStrategy.prototype._detach;
/**
* @type {?}
* @private
*/
CloseScrollStrategy.prototype._scrollDispatcher;
/**
* @type {?}
* @private
*/
CloseScrollStrategy.prototype._ngZone;
/**
* @type {?}
* @private
*/
CloseScrollStrategy.prototype._viewportRuler;
/**
* @type {?}
* @private
*/
CloseScrollStrategy.prototype._config;
}
/**
* @fileoverview added by tsickle
* Generated from: src/cdk/overlay/scroll/noop-scroll-strategy.ts
* @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
/**
* Scroll strategy that doesn't do anything.
*/
class NoopScrollStrategy {
/**
* Does nothing, as this scroll strategy is a no-op.
* @return {?}
*/
enable() { }
/**
* Does nothing, as this scroll strategy is a no-op.
* @return {?}
*/
disable() { }
/**
* Does nothing, as this scroll strategy is a no-op.
* @return {?}
*/
attach() { }
}
/**
* @fileoverview added by tsickle
* Generated from: src/cdk/overlay/position/scroll-clip.ts
* @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
// TODO(jelbourn): move this to live with the rest of the scrolling code
// TODO(jelbourn): someday replace this with IntersectionObservers
/**
* Gets whether an element is scrolled outside of view by any of its parent scrolling containers.
* \@docs-private
* @param {?} element Dimensions of the element (from getBoundingClientRect)
* @param {?} scrollContainers Dimensions of element's scrolling containers (from getBoundingClientRect)
* @return {?} Whether the element is scrolled out of view
*/
function isElementScrolledOutsideView(element, scrollContainers) {
return scrollContainers.some((/**
* @param {?} containerBounds
* @return {?}
*/
containerBounds => {
/** @type {?} */
const outsideAbove = element.bottom < containerBounds.top;
/** @type {?} */
const outsideBelow = element.top > containerBounds.bottom;
/** @type {?} */
const outsideLeft = element.right < containerBounds.left;
/** @type {?} */
const outsideRight = element.left > containerBounds.right;
return outsideAbove || outsideBelow || outsideLeft || outsideRight;
}));
}
/**
* Gets whether an element is clipped by any of its scrolling containers.
* \@docs-private
* @param {?} element Dimensions of the element (from getBoundingClientRect)
* @param {?} scrollContainers Dimensions of element's scrolling containers (from getBoundingClientRect)
* @return {?} Whether the element is clipped
*/
function isElementClippedByScrolling(element, scrollContainers) {
return scrollContainers.some((/**
* @param {?} scrollContainerRect
* @return {?}
*/
scrollContainerRect => {
/** @type {?} */
const clippedAbove = element.top < scrollContainerRect.top;
/** @type {?} */
const clippedBelow = element.bottom > scrollContainerRect.bottom;
/** @type {?} */
const clippedLeft = element.left < scrollContainerRect.left;
/** @type {?} */
const clippedRight = element.right > scrollContainerRect.right;
return clippedAbove || clippedBelow || clippedLeft || clippedRight;
}));
}
/**
* @fileoverview added by tsickle
* Generated from: src/cdk/overlay/scroll/reposition-scroll-strategy.ts
* @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
/**
* Config options for the RepositionScrollStrategy.
* @record
*/
function RepositionScrollStrategyConfig() { }
if (false) {
/**
* Time in milliseconds to throttle the scroll events.
* @type {?|undefined}
*/
RepositionScrollStrategyConfig.prototype.scrollThrottle;
/**
* Whether to close the overlay once the user has scrolled away completely.
* @type {?|undefined}
*/
RepositionScrollStrategyConfig.prototype.autoClose;
}
/**
* Strategy that will update the element position as the user is scrolling.
*/
class RepositionScrollStrategy {
/**
* @param {?} _scrollDispatcher
* @param {?} _viewportRuler
* @param {?} _ngZone
* @param {?=} _config
*/
constructor(_scrollDispatcher, _viewportRuler, _ngZone, _config) {
this._scrollDispatcher = _scrollDispatcher;
this._viewportRuler = _viewportRuler;
this._ngZone = _ngZone;
this._config = _config;
this._scrollSubscription = null;
}
/**
* Attaches this scroll strategy to an overlay.
* @param {?} overlayRef
* @return {?}
*/
attach(overlayRef) {
if (this._overlayRef) {
throw getMatScrollStrategyAlreadyAttachedError();
}
this._overlayRef = overlayRef;
}
/**
* Enables repositioning of the attached overlay on scroll.
* @return {?}
*/
enable() {
if (!this._scrollSubscription) {
/** @type {?} */
const throttle = this._config ? this._config.scrollThrottle : 0;
this._scrollSubscription = this._scrollDispatcher.scrolled(throttle).subscribe((/**
* @return {?}
*/
() => {
this._overlayRef.updatePosition();
// TODO(crisbeto): make `close` on by default once all components can handle it.
if (this._config && this._config.autoClose) {
/** @type {?} */
const overlayRect = this._overlayRef.overlayElement.getBoundingClientRect();
const { width, height } = this._viewportRuler.getViewportSize();
// TODO(crisbeto): include all ancestor scroll containers here once
// we have a way of exposing the trigger element to the scroll strategy.
/** @type {?} */
const parentRects = [{ width, height, bottom: height, right: width, top: 0, left: 0 }];
if (isElementScrolledOutsideView(overlayRect, parentRects)) {
this.disable();
this._ngZone.run((/**
* @return {?}
*/
() => this._overlayRef.detach()));
}
}
}));
}
}
/**
* Disables repositioning of the attached overlay on scroll.
* @return {?}
*/
disable() {
if (this._scrollSubscription) {
this._scrollSubscription.unsubscribe();
this._scrollSubscription = null;
}
}
/**
* @return {?}
*/
detach() {
this.disable();
this._overlayRef = (/** @type {?} */ (null));
}
}
if (false) {
/**
* @type {?}
* @private
*/
RepositionScrollStrategy.prototype._scrollSubscription;
/**
* @type {?}
* @private
*/
RepositionScrollStrategy.prototype._overlayRef;
/**
* @type {?}
* @private
*/
RepositionScrollStrategy.prototype._scrollDispatcher;
/**
* @type {?}
* @private
*/
RepositionScrollStrategy.prototype._viewportRuler;
/**
* @type {?}
* @private
*/
RepositionScrollStrategy.prototype._ngZone;
/**
* @type {?}
* @private
*/
RepositionScrollStrategy.prototype._config;
}
/**
* @fileoverview added by tsickle
* Generated from: src/cdk/overlay/scroll/scroll-strategy-options.ts
* @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
/**
* Options for how an overlay will handle scrolling.
*
* Users can provide a custom value for `ScrollStrategyOptions` to replace the default
* behaviors. This class primarily acts as a factory for ScrollStrategy instances.
*/
class ScrollStrategyOptions {
/**
* @param {?} _scrollDispatcher
* @param {?} _viewportRuler
* @param {?} _ngZone
* @param {?} document
*/
constructor(_scrollDispatcher, _viewportRuler, _ngZone, document) {
this._scrollDispatcher = _scrollDispatcher;
this._viewportRuler = _viewportRuler;
this._ngZone = _ngZone;
/**
* Do nothing on scroll.
*/
this.noop = (/**
* @return {?}
*/
() => new NoopScrollStrategy());
/**
* Close the overlay as soon as the user scrolls.
* @param config Configuration to be used inside the scroll strategy.
*/
this.close = (/**
* @param {?=} config
* @return {?}
*/
(config) => new CloseScrollStrategy(this._scrollDispatcher, this._ngZone, this._viewportRuler, config));
/**
* Block scrolling.
*/
this.block = (/**
* @return {?}
*/
() => new BlockScrollStrategy(this._viewportRuler, this._document));
/**
* Update the overlay's position on scroll.
* @param config Configuration to be used inside the scroll strategy.
* Allows debouncing the reposition calls.
*/
this.reposition = (/**
* @param {?=} config
* @return {?}
*/
(config) => new RepositionScrollStrategy(this._scrollDispatcher, this._viewportRuler, this._ngZone, config));
this._document = document;
}
}
ScrollStrategyOptions.decorators = [
{ type: Injectable, args: [{ providedIn: 'root' },] }
];
/** @nocollapse */
ScrollStrategyOptions.ctorParameters = () => [
{ type: ScrollDispatcher },
{ type: ViewportRuler },
{ type: NgZone },
{ type: undefined, decorators: [{ type: Inject, args: [DOCUMENT,] }] }
];
/** @nocollapse */ ScrollStrategyOptions.ɵprov = ɵɵdefineInjectable({ factory: function ScrollStrategyOptions_Factory() { return new ScrollStrategyOptions(ɵɵinject(ScrollDispatcher), ɵɵinject(ViewportRuler), ɵɵinject(NgZone), ɵɵinject(DOCUMENT)); }, token: ScrollStrategyOptions, providedIn: "root" });
if (false) {
/**
* @type {?}
* @private
*/
ScrollStrategyOptions.prototype._document;
/**
* Do nothing on scroll.
* @type {?}
*/
ScrollStrategyOptions.prototype.noop;
/**
* Close the overlay as soon as the user scrolls.
* \@param config Configuration to be used inside the scroll strategy.
* @type {?}
*/
ScrollStrategyOptions.prototype.close;
/**
* Block scrolling.
* @type {?}
*/
ScrollStrategyOptions.prototype.block;
/**
* Update the overlay's position on scroll.
* \@param config Configuration to be used inside the scroll strategy.
* Allows debouncing the reposition calls.
* @type {?}
*/
ScrollStrategyOptions.prototype.reposition;
/**
* @type {?}
* @private
*/
ScrollStrategyOptions.prototype._scrollDispatcher;
/**
* @type {?}
* @private
*/
ScrollStrategyOptions.prototype._viewportRuler;
/**
* @type {?}
* @private
*/
ScrollStrategyOptions.prototype._ngZone;
}
/**
* @fileoverview added by tsickle
* Generated from: src/cdk/overlay/scroll/index.ts
* @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
/**
* @fileoverview added by tsickle
* Generated from: src/cdk/overlay/overlay-config.ts
* @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
/**
* Initial configuration used when creating an overlay.
*/
class OverlayConfig {
/**
* @param {?=} config
*/
constructor(config) {
/**
* Strategy to be used when handling scroll events while the overlay is open.
*/
this.scrollStrategy = new NoopScrollStrategy();
/**
* Custom class to add to the overlay pane.
*/
this.panelClass = '';
/**
* Whether the overlay has a backdrop.
*/
this.hasBackdrop = false;
/**
* Custom class to add to the backdrop
*/
this.backdropClass = 'cdk-overlay-dark-backdrop';
/**
* Whether the overlay should be disposed of when the user goes backwards/forwards in history.
* Note that this usually doesn't include clicking on links (unless the user is using
* the `HashLocationStrategy`).
*/
this.disposeOnNavigation = false;
if (config) {
// Use `Iterable` instead of `Array` because TypeScript, as of 3.6.3,
// loses the array generic type in the `for of`. But we *also* have to use `Array` because
// typescript won't iterate over an `Iterable` unless you compile with `--downlevelIteration`
/** @type {?} */
const configKeys = (/** @type {?} */ (Object.keys(config)));
for (const key of configKeys) {
if (config[key] !== undefined) {
// TypeScript, as of version 3.5, sees the left-hand-side of this expression
// as "I don't know *which* key this is, so the only valid value is the intersection
// of all the posible values." In this case, that happens to be `undefined`. TypeScript
// is not smart enough to see that the right-hand-side is actually an access of the same
// exact type with the same exact key, meaning that the value type must be identical.
// So we use `any` to work around this.
this[key] = (/** @type {?} */ (config[key]));
}
}
}
}
}
if (false) {
/**
* Strategy with which to position the overlay.
* @type {?}
*/
OverlayConfig.prototype.positionStrategy;
/**
* Strategy to be used when handling scroll events while the overlay is open.
* @type {?}
*/
OverlayConfig.prototype.scrollStrategy;
/**
* Custom class to add to the overlay pane.
* @type {?}
*/
OverlayConfig.prototype.panelClass;
/**
* Whether the overlay has a backdrop.
* @type {?}
*/
OverlayConfig.prototype.hasBackdrop;
/**
* Custom class to add to the backdrop
* @type {?}
*/
OverlayConfig.prototype.backdropClass;
/**
* The width of the overlay panel. If a number is provided, pixel units are assumed.
* @type {?}
*/
OverlayConfig.prototype.width;
/**
* The height of the overlay panel. If a number is provided, pixel units are assumed.
* @type {?}
*/
OverlayConfig.prototype.height;
/**
* The min-width of the overlay panel. If a number is provided, pixel units are assumed.
* @type {?}
*/
OverlayConfig.prototype.minWidth;
/**
* The min-height of the overlay panel. If a number is provided, pixel units are assumed.
* @type {?}
*/
OverlayConfig.prototype.minHeight;
/**
* The max-width of the overlay panel. If a number is provided, pixel units are assumed.
* @type {?}
*/
OverlayConfig.prototype.maxWidth;
/**
* The max-height of the overlay panel. If a number is provided, pixel units are assumed.
* @type {?}
*/
OverlayConfig.prototype.maxHeight;
/**
* Direction of the text in the overlay panel. If a `Directionality` instance
* is passed in, the overlay will handle changes to its value automatically.
* @type {?}
*/
OverlayConfig.prototype.direction;
/**
* Whether the overlay should be disposed of when the user goes backwards/forwards in history.
* Note that this usually doesn't include clicking on links (unless the user is using
* the `HashLocationStrategy`).
* @type {?}
*/
OverlayConfig.prototype.disposeOnNavigation;
}
/**
* @fileoverview added by tsickle
* Generated from: src/cdk/overlay/position/connected-position.ts
* @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
/**
* A connection point on the origin element.
* @record
*/
function OriginConnectionPosition() { }
if (false) {
/** @type {?} */
OriginConnectionPosition.prototype.originX;
/** @type {?} */
OriginConnectionPosition.prototype.originY;
}
/**
* A connection point on the overlay element.
* @record
*/
function OverlayConnectionPosition() { }
if (false) {
/** @type {?} */
OverlayConnectionPosition.prototype.overlayX;
/** @type {?} */
OverlayConnectionPosition.prototype.overlayY;
}
/**
* The points of the origin element and the overlay element to connect.
*/
class ConnectionPositionPair {
/**
* @param {?} origin
* @param {?} overlay
* @param {?=} offsetX
* @param {?=} offsetY
* @param {?=} panelClass
*/
constructor(origin, overlay, offsetX, offsetY, panelClass) {
this.offsetX = offsetX;
this.offsetY = offsetY;
this.panelClass = panelClass;
this.originX = origin.originX;
this.originY = origin.originY;
this.overlayX = overlay.overlayX;
this.overlayY = overlay.overlayY;
}
}
if (false) {
/**
* X-axis attachment point for connected overlay origin. Can be 'start', 'end', or 'center'.
* @type {?}
*/
ConnectionPositionPair.prototype.originX;
/**
* Y-axis attachment point for connected overlay origin. Can be 'top', 'bottom', or 'center'.
* @type {?}
*/
ConnectionPositionPair.prototype.originY;
/**
* X-axis attachment point for connected overlay. Can be 'start', 'end', or 'center'.
* @type {?}
*/
ConnectionPositionPair.prototype.overlayX;
/**
* Y-axis attachment point for connected overlay. Can be 'top', 'bottom', or 'center'.
* @type {?}
*/
ConnectionPositionPair.prototype.overlayY;
/**
* Offset along the X axis.
* @type {?}
*/
ConnectionPositionPair.prototype.offsetX;
/**
* Offset along the Y axis.
* @type {?}
*/
ConnectionPositionPair.prototype.offsetY;
/**
* Class(es) to be applied to the panel while this position is active.
* @type {?}
*/
ConnectionPositionPair.prototype.panelClass;
}
/**
* Set of properties regarding the position of the origin and overlay relative to the viewport
* with respect to the containing Scrollable elements.
*
* The overlay and origin are clipped if any part of their bounding client rectangle exceeds the
* bounds of any one of the strategy's Scrollable's bounding client rectangle.
*
* The overlay and origin are outside view if there is no overlap between their bounding client
* rectangle and any one of the strategy's Scrollable's bounding client rectangle.
*
* ----------- -----------
* | outside | | clipped |
* | view | --------------------------
* | | | | | |
* ---------- | ----------- |
* -------------------------- | |
* | | | Scrollable |
* | | | |
* | | --------------------------
* | Scrollable |
* | |
* --------------------------
*
* \@docs-private
*/
class ScrollingVisibility {
}
if (false) {
/** @type {?} */
ScrollingVisibility.prototype.isOriginClipped;
/** @type {?} */
ScrollingVisibility.prototype.isOriginOutsideView;
/** @type {?} */
ScrollingVisibility.prototype.isOverlayClipped;
/** @type {?} */
ScrollingVisibility.prototype.isOverlayOutsideView;
}
/**
* The change event emitted by the strategy when a fallback position is used.
*/
class ConnectedOverlayPositionChange {
/**
* @param {?} connectionPair
* @param {?} scrollableViewProperties
*/
constructor(connectionPair, scrollableViewProperties) {
this.connectionPair = connectionPair;
this.scrollableViewProperties = scrollableViewProperties;
}
}
/** @nocollapse */
ConnectedOverlayPositionChange.ctorParameters = () => [
{ type: ConnectionPositionPair },
{ type: ScrollingVisibility, decorators: [{ type: Optional }] }
];
if (false) {
/**
* The position used as a result of this change.
* @type {?}
*/
ConnectedOverlayPositionChange.prototype.connectionPair;
/**
* \@docs-private
* @type {?}
*/
ConnectedOverlayPositionChange.prototype.scrollableViewProperties;
}
/**
* Validates whether a vertical position property matches the expected values.
* \@docs-private
* @param {?} property Name of the property being validated.
* @param {?} value Value of the property being validated.
* @return {?}
*/
function validateVerticalPosition(property, value) {
if (value !== 'top' && value !== 'bottom' && value !== 'center') {
throw Error(`ConnectedPosition: Invalid ${property} "${value}". ` +
`Expected "top", "bottom" or "center".`);
}
}
/**
* Validates whether a horizontal position property matches the expected values.
* \@docs-private
* @param {?} property Name of the property being validated.
* @param {?} value Value of the property being validated.
* @return {?}
*/
function validateHorizontalPosition(property, value) {
if (value !== 'start' && value !== 'end' && value !== 'center') {
throw Error(`ConnectedPosition: Invalid ${property} "${value}". ` +
`Expected "start", "end" or "center".`);
}
}
/**
* @fileoverview added by tsickle
* Generated from: src/cdk/overlay/keyboard/overlay-keyboard-dispatcher.ts
* @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
/**
* Service for dispatching keyboard events that land on the body to appropriate overlay ref,
* if any. It maintains a list of attached overlays to determine best suited overlay based
* on event target and order of overlay opens.
*/
class OverlayKeyboardDispatcher {
/**
* @param {?} document
*/
constructor(document) {
/**
* Currently attached overlays in the order they were attached.
*/
this._attachedOverlays = [];
/**
* Keyboard event listener that will be attached to the body.
*/
this._keydownListener = (/**
* @param {?} event
* @return {?}
*/
(event) => {
/** @type {?} */
const overlays = this._attachedOverlays;
for (let i = overlays.length - 1; i > -1; i--) {
// Dispatch the keydown event to the top overlay which has subscribers to its keydown events.
// We want to target the most recent overlay, rather than trying to match where the event came
// from, because some components might open an overlay, but keep focus on a trigger element
// (e.g. for select and autocomplete). We skip overlays without keydown event subscriptions,
// because we don't want overlays that don't handle keyboard events to block the ones below
// them that do.
if (overlays[i]._keydownEventSubscriptions > 0) {
overlays[i]._keydownEvents.next(event);
break;
}
}
});
this._document = document;
}
/**
* @return {?}
*/
ngOnDestroy() {
this._detach();
}
/**
* Add a new overlay to the list of attached overlay refs.
* @param {?} overlayRef
* @return {?}
*/
add(overlayRef) {
// Ensure that we don't get the same overlay multiple times.
this.remove(overlayRef);
// Lazily start dispatcher once first overlay is added
if (!this._isAttached) {
this._document.body.addEventListener('keydown', this._keydownListener);
this._isAttached = true;
}
this._attachedOverlays.push(overlayRef);
}
/**
* Remove an overlay from the list of attached overlay refs.
* @param {?} overlayRef
* @return {?}
*/
remove(overlayRef) {
/** @type {?} */
const index = this._attachedOverlays.indexOf(overlayRef);
if (index > -1) {
this._attachedOverlays.splice(index, 1);
}
// Remove the global listener once there are no more overlays.
if (this._attachedOverlays.length === 0) {
this._detach();
}
}
/**
* Detaches the global keyboard event listener.
* @private
* @return {?}
*/
_detach() {
if (this._isAttached) {
this._document.body.removeEventListener('keydown', this._keydownListener);
this._isAttached = false;
}
}
}
OverlayKeyboardDispatcher.decorators = [
{ type: Injectable, args: [{ providedIn: 'root' },] }
];
/** @nocollapse */
OverlayKeyboardDispatcher.ctorParameters = () => [
{ type: undefined, decorators: [{ type: Inject, args: [DOCUMENT,] }] }
];
/** @nocollapse */ OverlayKeyboardDispatcher.ɵprov = ɵɵdefineInjectable({ factory: function OverlayKeyboardDispatcher_Factory() { return new OverlayKeyboardDispatcher(ɵɵinject(DOCUMENT)); }, token: OverlayKeyboardDispatcher, providedIn: "root" });
if (false) {
/**
* Currently attached overlays in the order they were attached.
* @type {?}
*/
OverlayKeyboardDispatcher.prototype._attachedOverlays;
/**
* @type {?}
* @private
*/
OverlayKeyboardDispatcher.prototype._document;
/**
* @type {?}
* @private
*/
OverlayKeyboardDispatcher.prototype._isAttached;
/**
* Keyboard event listener that will be attached to the body.
* @type {?}
* @private
*/
OverlayKeyboardDispatcher.prototype._keydownListener;
}
/**
* \@docs-private \@deprecated \@breaking-change 8.0.0
* @param {?} dispatcher
* @param {?} _document
* @return {?}
*/
function OVERLAY_KEYBOARD_DISPATCHER_PROVIDER_FACTORY(dispatcher, _document) {
return dispatcher || new OverlayKeyboardDispatcher(_document);
}
/**
* \@docs-private \@deprecated \@breaking-change 8.0.0
* @type {?}
*/
const OVERLAY_KEYBOARD_DISPATCHER_PROVIDER = {
// If there is already an OverlayKeyboardDispatcher available, use that.
// Otherwise, provide a new one.
provide: OverlayKeyboardDispatcher,
deps: [
[new Optional(), new SkipSelf(), OverlayKeyboardDispatcher],
(/** @type {?} */ (
// Coerce to `InjectionToken` so that the `deps` match the "shape"
// of the type expected by Angular
DOCUMENT))
],
useFactory: OVERLAY_KEYBOARD_DISPATCHER_PROVIDER_FACTORY
};
/**
* @fileoverview added by tsickle
* Generated from: src/cdk/overlay/overlay-container.ts
* @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
/**
* Whether we're in a testing environment.
* TODO(crisbeto): remove this once we have an overlay testing module.
* @type {?}
*/
const isTestEnvironment = typeof window !== 'undefined' && !!window &&
!!(((/** @type {?} */ (window))).__karma__ || ((/** @type {?} */ (window))).jasmine);
/**
* Container inside which all overlays will render.
*/
class OverlayContainer {
/**
* @param {?} document
* @param {?=} _platform
*/
constructor(document, _platform) {
this._platform = _platform;
this._document = document;
}
/**
* @return {?}
*/
ngOnDestroy() {
/** @type {?} */
const container = this._containerElement;
if (container && container.parentNode) {
container.parentNode.removeChild(container);
}
}
/**
* This method returns the overlay container element. It will lazily
* create the element the first time it is called to facilitate using
* the container in non-browser environments.
* @return {?} the container element
*/
getContainerElement() {
if (!this._containerElement) {
this._createContainer();
}
return this._containerElement;
}
/**
* Create the overlay container element, which is simply a div
* with the 'cdk-overlay-container' class on the document body.
* @protected
* @return {?}
*/
_createContainer() {
// @breaking-change 10.0.0 Remove null check for `_platform`.
/** @type {?} */
const isBrowser = this._platform ? this._platform.isBrowser : typeof window !== 'undefined';
/** @type {?} */
const containerClass = 'cdk-overlay-container';
if (isBrowser || isTestEnvironment) {
/** @type {?} */
const oppositePlatformContainers = this._document.querySelectorAll(`.${containerClass}[platform="server"], ` +
`.${containerClass}[platform="test"]`);
// Remove any old containers from the opposite platform.
// This can happen when transitioning from the server to the client.
for (let i = 0; i < oppositePlatformContainers.length; i++) {
(/** @type {?} */ (oppositePlatformContainers[i].parentNode)).removeChild(oppositePlatformContainers[i]);
}
}
/** @type {?} */
const container = this._document.createElement('div');
container.classList.add(containerClass);
// A long time ago we kept adding new overlay containers whenever a new app was instantiated,
// but at some point we added logic which clears the duplicate ones in order to avoid leaks.
// The new logic was a little too aggressive since it was breaking some legitimate use cases.
// To mitigate the problem we made it so that only containers from a different platform are
// cleared, but the side-effect was that people started depending on the overly-aggressive
// logic to clean up their tests for them. Until we can introduce an overlay-specific testing
// module which does the cleanup, we try to detect that we're in a test environment and we
// always clear the container. See #17006.
// TODO(crisbeto): remove the test environment check once we have an overlay testing module.
if (isTestEnvironment) {
container.setAttribute('platform', 'test');
}
else if (!isBrowser) {
container.setAttribute('platform', 'server');
}
this._document.body.appendChild(container);
this._containerElement = container;
}
}
OverlayContainer.decorators = [
{ type: Injectable, args: [{ providedIn: 'root' },] }
];
/** @nocollapse */
OverlayContainer.ctorParameters = () => [
{ type: undefined, decorators: [{ type: Inject, args: [DOCUMENT,] }] },
{ type: Platform }
];
/** @nocollapse */ OverlayContainer.ɵprov = ɵɵdefineInjectable({ factory: function OverlayContainer_Factory() { return new OverlayContainer(ɵɵinject(DOCUMENT), ɵɵinject(Platform)); }, token: OverlayContainer, providedIn: "root" });
if (false) {
/**
* @type {?}
* @protected
*/
OverlayContainer.prototype._containerElement;
/**
* @type {?}
* @protected
*/
OverlayContainer.prototype._document;
/**
* @deprecated `platform` parameter to become required.
* \@breaking-change 10.0.0
* @type {?}
* @protected
*/
OverlayContainer.prototype._platform;
}
/**
* \@docs-private \@deprecated \@breaking-change 8.0.0
* @param {?} parentContainer
* @param {?} _document
* @return {?}
*/
function OVERLAY_CONTAINER_PROVIDER_FACTORY(parentContainer, _document) {
return parentContainer || new OverlayContainer(_document);
}
/**
* \@docs-private \@deprecated \@breaking-change 8.0.0
* @type {?}
*/
const OVERLAY_CONTAINER_PROVIDER = {
// If there is already an OverlayContainer available, use that. Otherwise, provide a new one.
provide: OverlayContainer,
deps: [
[new Optional(), new SkipSelf(), OverlayContainer],
(/** @type {?} */ (DOCUMENT))
],
useFactory: OVERLAY_CONTAINER_PROVIDER_FACTORY
};
/**
* @fileoverview added by tsickle
* Generated from: src/cdk/overlay/overlay-ref.ts
* @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
/**
* Reference to an overlay that has been created with the Overlay service.
* Used to manipulate or dispose of said overlay.
*/
class OverlayRef {
/**
* @param {?} _portalOutlet
* @param {?} _host
* @param {?} _pane
* @param {?} _config
* @param {?} _ngZone
* @param {?} _keyboardDispatcher
* @param {?} _document
* @param {?=} _location
*/
constructor(_portalOutlet, _host, _pane, _config, _ngZone, _keyboardDispatcher, _document, _location) {
this._portalOutlet = _portalOutlet;
this._host = _host;
this._pane = _pane;
this._config = _config;
this._ngZone = _ngZone;
this._keyboardDispatcher = _keyboardDispatcher;
this._document = _document;
this._location = _location;
this._backdropElement = null;
this._backdropClick = new Subject();
this._attachments = new Subject();
this._detachments = new Subject();
this._locationChanges = Subscription.EMPTY;
this._backdropClickHandler = (/**
* @param {?} event
* @return {?}
*/
(event) => this._backdropClick.next(event));
this._keydownEventsObservable = new Observable((/**
* @param {?} observer
* @return {?}
*/
(observer) => {
/** @type {?} */
const subscription = this._keydownEvents.subscribe(observer);
this._keydownEventSubscriptions++;
return (/**
* @return {?}
*/
() => {
subscription.unsubscribe();
this._keydownEventSubscriptions--;
});
}));
/**
* Stream of keydown events dispatched to this overlay.
*/
this._keydownEvents = new Subject();
/**
* Amount of subscriptions to the keydown events.
*/
this._keydownEventSubscriptions = 0;
if (_config.scrollStrategy) {
this._scrollStrategy = _config.scrollStrategy;
this._scrollStrategy.attach(this);
}
this._positionStrategy = _config.positionStrategy;
}
/**
* The overlay's HTML element
* @return {?}
*/
get overlayElement() {
return this._pane;
}
/**
* The overlay's backdrop HTML element.
* @return {?}
*/
get backdropElement() {
return this._backdropElement;
}
/**
* Wrapper around the panel element. Can be used for advanced
* positioning where a wrapper with specific styling is
* required around the overlay pane.
* @return {?}
*/
get hostElement() {
return this._host;
}
/**
* Attaches content, given via a Portal, to the overlay.
* If the overlay is configured to have a backdrop, it will be created.
*
* @param {?} portal Portal instance to which to attach the overlay.
* @return {?} The portal attachment result.
*/
attach(portal) {
/** @type {?} */
let attachResult = this._portalOutlet.attach(portal);
if (this._positionStrategy) {
this._positionStrategy.attach(this);
}
// Update the pane element with the given configuration.
if (!this._host.parentElement && this._previousHostParent) {
this._previousHostParent.appendChild(this._host);
}
this._updateStackingOrder();
this._updateElementSize();
this._updateElementDirection();
if (this._scrollStrategy) {
this._scrollStrategy.enable();
}
// Update the position once the zone is stable so that the overlay will be fully rendered
// before attempting to position it, as the position may depend on the size of the rendered
// content.
this._ngZone.onStable
.asObservable()
.pipe(take(1))
.subscribe((/**
* @return {?}
*/
() => {
// The overlay could've been detached before the zone has stabilized.
if (this.hasAttached()) {
this.updatePosition();
}
}));
// Enable pointer events for the overlay pane element.
this._togglePointerEvents(true);
if (this._config.hasBackdrop) {
this._attachBackdrop();
}
if (this._config.panelClass) {
this._toggleClasses(this._pane, this._config.panelClass, true);
}
// Only emit the `attachments` event once all other setup is done.
this._attachments.next();
// Track this overlay by the keyboard dispatcher
this._keyboardDispatcher.add(this);
// @breaking-change 8.0.0 remove the null check for `_location`
// once the constructor parameter is made required.
if (this._config.disposeOnNavigation && this._location) {
this._locationChanges = this._location.subscribe((/**
* @return {?}
*/
() => this.dispose()));
}
return attachResult;
}
/**
* Detaches an overlay from a portal.
* @return {?} The portal detachment result.
*/
detach() {
if (!this.hasAttached()) {
return;
}
this.detachBackdrop();
// When the overlay is detached, the pane element should disable pointer events.
// This is necessary because otherwise the pane element will cover the page and disable
// pointer events therefore. Depends on the position strategy and the applied pane boundaries.
this._togglePointerEvents(false);
if (this._positionStrategy && this._positionStrategy.detach) {
this._positionStrategy.detach();
}
if (this._scrollStrategy) {
this._scrollStrategy.disable();
}
/** @type {?} */
const detachmentResult = this._portalOutlet.detach();
// Only emit after everything is detached.
this._detachments.next();
// Rem