UNPKG

@angular/cdk

Version:

Angular Material Component Development Kit

1,457 lines (1,444 loc) 180 kB
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