UNPKG

@angular/cdk

Version:

Angular Material Component Development Kit

1,472 lines (1,456 loc) 90.4 kB
import * as i0 from '@angular/core'; import { DOCUMENT, NgZone, inject, Injector, Injectable, RendererFactory2, Component, ChangeDetectionStrategy, ViewEncapsulation, afterNextRender, ElementRef, InjectionToken, ApplicationRef, Renderer2, ANIMATION_MODULE_TYPE, EnvironmentInjector, Directive, EventEmitter, TemplateRef, ViewContainerRef, booleanAttribute, Input, Output, NgModule } from '@angular/core'; import { Location } from '@angular/common'; import { Platform } from './_platform-chunk.mjs'; import { _getEventTarget } from './_shadow-dom-chunk.mjs'; import { _isTestEnvironment } from './_test-environment-chunk.mjs'; import { _CdkPrivateStyleLoader } from './_style-loader-chunk.mjs'; import { Subject, Subscription } from 'rxjs'; import { coerceCssPixelValue } from './_css-pixel-value-chunk.mjs'; import { coerceArray } from './_array-chunk.mjs'; import { ViewportRuler, ScrollDispatcher, ScrollingModule } from './scrolling.mjs'; import { DomPortalOutlet, TemplatePortal, PortalModule } from './portal.mjs'; import { supportsScrollBehavior } from './_scrolling-chunk.mjs'; import { filter, takeWhile } from 'rxjs/operators'; import { _IdGenerator } from './_id-generator-chunk.mjs'; import { Directionality } from './_directionality-chunk.mjs'; import { ESCAPE } from './_keycodes-chunk.mjs'; import { hasModifierKey } from './keycodes.mjs'; import { BidiModule } from './bidi.mjs'; const scrollBehaviorSupported = supportsScrollBehavior(); function createBlockScrollStrategy(injector) { return new BlockScrollStrategy(injector.get(ViewportRuler), injector.get(DOCUMENT)); } class BlockScrollStrategy { _viewportRuler; _previousHTMLStyles = { top: '', left: '' }; _previousScrollPosition; _isEnabled = false; _document; constructor(_viewportRuler, document) { this._viewportRuler = _viewportRuler; this._document = document; } attach() {} enable() { if (this._canBeEnabled()) { const root = this._document.documentElement; this._previousScrollPosition = this._viewportRuler.getViewportScrollPosition(); this._previousHTMLStyles.left = root.style.left || ''; this._previousHTMLStyles.top = root.style.top || ''; root.style.left = coerceCssPixelValue(-this._previousScrollPosition.left); root.style.top = coerceCssPixelValue(-this._previousScrollPosition.top); root.classList.add('cdk-global-scrollblock'); this._isEnabled = true; } } disable() { if (this._isEnabled) { const html = this._document.documentElement; const body = this._document.body; const htmlStyle = html.style; const bodyStyle = body.style; const previousHtmlScrollBehavior = htmlStyle.scrollBehavior || ''; const previousBodyScrollBehavior = bodyStyle.scrollBehavior || ''; this._isEnabled = false; htmlStyle.left = this._previousHTMLStyles.left; htmlStyle.top = this._previousHTMLStyles.top; html.classList.remove('cdk-global-scrollblock'); if (scrollBehaviorSupported) { htmlStyle.scrollBehavior = bodyStyle.scrollBehavior = 'auto'; } window.scroll(this._previousScrollPosition.left, this._previousScrollPosition.top); if (scrollBehaviorSupported) { htmlStyle.scrollBehavior = previousHtmlScrollBehavior; bodyStyle.scrollBehavior = previousBodyScrollBehavior; } } } _canBeEnabled() { const html = this._document.documentElement; if (html.classList.contains('cdk-global-scrollblock') || this._isEnabled) { return false; } const rootElement = this._document.documentElement; const viewport = this._viewportRuler.getViewportSize(); return rootElement.scrollHeight > viewport.height || rootElement.scrollWidth > viewport.width; } } function getMatScrollStrategyAlreadyAttachedError() { return Error(`Scroll strategy has already been attached.`); } function createCloseScrollStrategy(injector, config) { return new CloseScrollStrategy(injector.get(ScrollDispatcher), injector.get(NgZone), injector.get(ViewportRuler), config); } class CloseScrollStrategy { _scrollDispatcher; _ngZone; _viewportRuler; _config; _scrollSubscription = null; _overlayRef; _initialScrollPosition; constructor(_scrollDispatcher, _ngZone, _viewportRuler, _config) { this._scrollDispatcher = _scrollDispatcher; this._ngZone = _ngZone; this._viewportRuler = _viewportRuler; this._config = _config; } attach(overlayRef) { if (this._overlayRef && (typeof ngDevMode === 'undefined' || ngDevMode)) { throw getMatScrollStrategyAlreadyAttachedError(); } this._overlayRef = overlayRef; } enable() { if (this._scrollSubscription) { return; } const stream = this._scrollDispatcher.scrolled(0).pipe(filter(scrollable => { return !scrollable || !this._overlayRef.overlayElement.contains(scrollable.getElementRef().nativeElement); })); if (this._config && this._config.threshold && this._config.threshold > 1) { this._initialScrollPosition = this._viewportRuler.getViewportScrollPosition().top; this._scrollSubscription = stream.subscribe(() => { const scrollPosition = this._viewportRuler.getViewportScrollPosition().top; if (Math.abs(scrollPosition - this._initialScrollPosition) > this._config.threshold) { this._detach(); } else { this._overlayRef.updatePosition(); } }); } else { this._scrollSubscription = stream.subscribe(this._detach); } } disable() { if (this._scrollSubscription) { this._scrollSubscription.unsubscribe(); this._scrollSubscription = null; } } detach() { this.disable(); this._overlayRef = null; } _detach = () => { this.disable(); if (this._overlayRef.hasAttached()) { this._ngZone.run(() => this._overlayRef.detach()); } }; } function createNoopScrollStrategy() { return new NoopScrollStrategy(); } class NoopScrollStrategy { enable() {} disable() {} attach() {} } function isElementScrolledOutsideView(element, scrollContainers) { return scrollContainers.some(containerBounds => { const outsideAbove = element.bottom < containerBounds.top; const outsideBelow = element.top > containerBounds.bottom; const outsideLeft = element.right < containerBounds.left; const outsideRight = element.left > containerBounds.right; return outsideAbove || outsideBelow || outsideLeft || outsideRight; }); } function isElementClippedByScrolling(element, scrollContainers) { return scrollContainers.some(scrollContainerRect => { const clippedAbove = element.top < scrollContainerRect.top; const clippedBelow = element.bottom > scrollContainerRect.bottom; const clippedLeft = element.left < scrollContainerRect.left; const clippedRight = element.right > scrollContainerRect.right; return clippedAbove || clippedBelow || clippedLeft || clippedRight; }); } function createRepositionScrollStrategy(injector, config) { return new RepositionScrollStrategy(injector.get(ScrollDispatcher), injector.get(ViewportRuler), injector.get(NgZone), config); } class RepositionScrollStrategy { _scrollDispatcher; _viewportRuler; _ngZone; _config; _scrollSubscription = null; _overlayRef; constructor(_scrollDispatcher, _viewportRuler, _ngZone, _config) { this._scrollDispatcher = _scrollDispatcher; this._viewportRuler = _viewportRuler; this._ngZone = _ngZone; this._config = _config; } attach(overlayRef) { if (this._overlayRef && (typeof ngDevMode === 'undefined' || ngDevMode)) { throw getMatScrollStrategyAlreadyAttachedError(); } this._overlayRef = overlayRef; } enable() { if (!this._scrollSubscription) { const throttle = this._config ? this._config.scrollThrottle : 0; this._scrollSubscription = this._scrollDispatcher.scrolled(throttle).subscribe(() => { this._overlayRef.updatePosition(); if (this._config && this._config.autoClose) { const overlayRect = this._overlayRef.overlayElement.getBoundingClientRect(); const { width, height } = this._viewportRuler.getViewportSize(); const parentRects = [{ width, height, bottom: height, right: width, top: 0, left: 0 }]; if (isElementScrolledOutsideView(overlayRect, parentRects)) { this.disable(); this._ngZone.run(() => this._overlayRef.detach()); } } }); } } disable() { if (this._scrollSubscription) { this._scrollSubscription.unsubscribe(); this._scrollSubscription = null; } } detach() { this.disable(); this._overlayRef = null; } } class ScrollStrategyOptions { _injector = inject(Injector); constructor() {} noop = () => new NoopScrollStrategy(); close = config => createCloseScrollStrategy(this._injector, config); block = () => createBlockScrollStrategy(this._injector); reposition = config => createRepositionScrollStrategy(this._injector, config); static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: ScrollStrategyOptions, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: ScrollStrategyOptions, providedIn: 'root' }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: ScrollStrategyOptions, decorators: [{ type: Injectable, args: [{ providedIn: 'root' }] }], ctorParameters: () => [] }); class OverlayConfig { positionStrategy; scrollStrategy = new NoopScrollStrategy(); panelClass = ''; hasBackdrop = false; backdropClass = 'cdk-overlay-dark-backdrop'; disableAnimations; width; height; minWidth; minHeight; maxWidth; maxHeight; direction; disposeOnNavigation = false; usePopover; constructor(config) { if (config) { const configKeys = Object.keys(config); for (const key of configKeys) { if (config[key] !== undefined) { this[key] = config[key]; } } } } } class ConnectionPositionPair { offsetX; offsetY; panelClass; originX; originY; overlayX; overlayY; 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; } } class ScrollingVisibility { isOriginClipped; isOriginOutsideView; isOverlayClipped; isOverlayOutsideView; } class ConnectedOverlayPositionChange { connectionPair; scrollableViewProperties; constructor(connectionPair, scrollableViewProperties) { this.connectionPair = connectionPair; this.scrollableViewProperties = scrollableViewProperties; } } function validateVerticalPosition(property, value) { if (value !== 'top' && value !== 'bottom' && value !== 'center') { throw Error(`ConnectedPosition: Invalid ${property} "${value}". ` + `Expected "top", "bottom" or "center".`); } } function validateHorizontalPosition(property, value) { if (value !== 'start' && value !== 'end' && value !== 'center') { throw Error(`ConnectedPosition: Invalid ${property} "${value}". ` + `Expected "start", "end" or "center".`); } } class BaseOverlayDispatcher { _attachedOverlays = []; _document = inject(DOCUMENT); _isAttached; constructor() {} ngOnDestroy() { this.detach(); } add(overlayRef) { this.remove(overlayRef); this._attachedOverlays.push(overlayRef); } remove(overlayRef) { const index = this._attachedOverlays.indexOf(overlayRef); if (index > -1) { this._attachedOverlays.splice(index, 1); } if (this._attachedOverlays.length === 0) { this.detach(); } } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: BaseOverlayDispatcher, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: BaseOverlayDispatcher, providedIn: 'root' }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: BaseOverlayDispatcher, decorators: [{ type: Injectable, args: [{ providedIn: 'root' }] }], ctorParameters: () => [] }); class OverlayKeyboardDispatcher extends BaseOverlayDispatcher { _ngZone = inject(NgZone); _renderer = inject(RendererFactory2).createRenderer(null, null); _cleanupKeydown; add(overlayRef) { super.add(overlayRef); if (!this._isAttached) { this._ngZone.runOutsideAngular(() => { this._cleanupKeydown = this._renderer.listen('body', 'keydown', this._keydownListener); }); this._isAttached = true; } } detach() { if (this._isAttached) { this._cleanupKeydown?.(); this._isAttached = false; } } _keydownListener = event => { const overlays = this._attachedOverlays; for (let i = overlays.length - 1; i > -1; i--) { if (overlays[i]._keydownEvents.observers.length > 0) { this._ngZone.run(() => overlays[i]._keydownEvents.next(event)); break; } } }; static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: OverlayKeyboardDispatcher, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: OverlayKeyboardDispatcher, providedIn: 'root' }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: OverlayKeyboardDispatcher, decorators: [{ type: Injectable, args: [{ providedIn: 'root' }] }] }); class OverlayOutsideClickDispatcher extends BaseOverlayDispatcher { _platform = inject(Platform); _ngZone = inject(NgZone); _renderer = inject(RendererFactory2).createRenderer(null, null); _cursorOriginalValue; _cursorStyleIsSet = false; _pointerDownEventTarget; _cleanups; add(overlayRef) { super.add(overlayRef); if (!this._isAttached) { const body = this._document.body; const eventOptions = { capture: true }; const renderer = this._renderer; this._cleanups = this._ngZone.runOutsideAngular(() => [renderer.listen(body, 'pointerdown', this._pointerDownListener, eventOptions), renderer.listen(body, 'click', this._clickListener, eventOptions), renderer.listen(body, 'auxclick', this._clickListener, eventOptions), renderer.listen(body, 'contextmenu', this._clickListener, eventOptions)]); if (this._platform.IOS && !this._cursorStyleIsSet) { this._cursorOriginalValue = body.style.cursor; body.style.cursor = 'pointer'; this._cursorStyleIsSet = true; } this._isAttached = true; } } detach() { if (this._isAttached) { this._cleanups?.forEach(cleanup => cleanup()); this._cleanups = undefined; if (this._platform.IOS && this._cursorStyleIsSet) { this._document.body.style.cursor = this._cursorOriginalValue; this._cursorStyleIsSet = false; } this._isAttached = false; } } _pointerDownListener = event => { this._pointerDownEventTarget = _getEventTarget(event); }; _clickListener = event => { const target = _getEventTarget(event); const origin = event.type === 'click' && this._pointerDownEventTarget ? this._pointerDownEventTarget : target; this._pointerDownEventTarget = null; const overlays = this._attachedOverlays.slice(); for (let i = overlays.length - 1; i > -1; i--) { const overlayRef = overlays[i]; if (overlayRef._outsidePointerEvents.observers.length < 1 || !overlayRef.hasAttached()) { continue; } if (containsPierceShadowDom(overlayRef.overlayElement, target) || containsPierceShadowDom(overlayRef.overlayElement, origin)) { break; } const outsidePointerEvents = overlayRef._outsidePointerEvents; if (this._ngZone) { this._ngZone.run(() => outsidePointerEvents.next(event)); } else { outsidePointerEvents.next(event); } } }; static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: OverlayOutsideClickDispatcher, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: OverlayOutsideClickDispatcher, providedIn: 'root' }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: OverlayOutsideClickDispatcher, decorators: [{ type: Injectable, args: [{ providedIn: 'root' }] }] }); function containsPierceShadowDom(parent, child) { const supportsShadowRoot = typeof ShadowRoot !== 'undefined' && ShadowRoot; let current = child; while (current) { if (current === parent) { return true; } current = supportsShadowRoot && current instanceof ShadowRoot ? current.host : current.parentNode; } return false; } class _CdkOverlayStyleLoader { static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: _CdkOverlayStyleLoader, deps: [], target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.0", type: _CdkOverlayStyleLoader, isStandalone: true, selector: "ng-component", host: { attributes: { "cdk-overlay-style-loader": "" } }, ngImport: i0, template: '', isInline: true, styles: [".cdk-overlay-container,.cdk-global-overlay-wrapper{pointer-events:none;top:0;left:0;height:100%;width:100%}.cdk-overlay-container{position:fixed}@layer cdk-overlay{.cdk-overlay-container{z-index:1000}}.cdk-overlay-container:empty{display:none}.cdk-global-overlay-wrapper{display:flex;position:absolute}@layer cdk-overlay{.cdk-global-overlay-wrapper{z-index:1000}}.cdk-overlay-pane{position:absolute;pointer-events:auto;box-sizing:border-box;display:flex;max-width:100%;max-height:100%}@layer cdk-overlay{.cdk-overlay-pane{z-index:1000}}.cdk-overlay-backdrop{position:absolute;top:0;bottom:0;left:0;right:0;pointer-events:auto;-webkit-tap-highlight-color:rgba(0,0,0,0);opacity:0;touch-action:manipulation}@layer cdk-overlay{.cdk-overlay-backdrop{z-index:1000;transition:opacity 400ms cubic-bezier(0.25, 0.8, 0.25, 1)}}@media(prefers-reduced-motion){.cdk-overlay-backdrop{transition-duration:1ms}}.cdk-overlay-backdrop-showing{opacity:1}@media(forced-colors: active){.cdk-overlay-backdrop-showing{opacity:.6}}@layer cdk-overlay{.cdk-overlay-dark-backdrop{background:rgba(0,0,0,.32)}}.cdk-overlay-transparent-backdrop{transition:visibility 1ms linear,opacity 1ms linear;visibility:hidden;opacity:1}.cdk-overlay-transparent-backdrop.cdk-overlay-backdrop-showing,.cdk-high-contrast-active .cdk-overlay-transparent-backdrop{opacity:0;visibility:visible}.cdk-overlay-backdrop-noop-animation{transition:none}.cdk-overlay-connected-position-bounding-box{position:absolute;display:flex;flex-direction:column;min-width:1px;min-height:1px}@layer cdk-overlay{.cdk-overlay-connected-position-bounding-box{z-index:1000}}.cdk-global-scrollblock{position:fixed;width:100%;overflow-y:scroll}.cdk-overlay-popover{background:none;border:none;padding:0;outline:0;overflow:visible;position:fixed;pointer-events:none;white-space:normal;color:inherit;text-decoration:none;width:100%;height:100%;inset:auto;top:0;left:0}.cdk-overlay-popover::backdrop{display:none}.cdk-overlay-popover .cdk-overlay-backdrop{position:fixed;z-index:auto}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: _CdkOverlayStyleLoader, decorators: [{ type: Component, args: [{ template: '', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, host: { 'cdk-overlay-style-loader': '' }, styles: [".cdk-overlay-container,.cdk-global-overlay-wrapper{pointer-events:none;top:0;left:0;height:100%;width:100%}.cdk-overlay-container{position:fixed}@layer cdk-overlay{.cdk-overlay-container{z-index:1000}}.cdk-overlay-container:empty{display:none}.cdk-global-overlay-wrapper{display:flex;position:absolute}@layer cdk-overlay{.cdk-global-overlay-wrapper{z-index:1000}}.cdk-overlay-pane{position:absolute;pointer-events:auto;box-sizing:border-box;display:flex;max-width:100%;max-height:100%}@layer cdk-overlay{.cdk-overlay-pane{z-index:1000}}.cdk-overlay-backdrop{position:absolute;top:0;bottom:0;left:0;right:0;pointer-events:auto;-webkit-tap-highlight-color:rgba(0,0,0,0);opacity:0;touch-action:manipulation}@layer cdk-overlay{.cdk-overlay-backdrop{z-index:1000;transition:opacity 400ms cubic-bezier(0.25, 0.8, 0.25, 1)}}@media(prefers-reduced-motion){.cdk-overlay-backdrop{transition-duration:1ms}}.cdk-overlay-backdrop-showing{opacity:1}@media(forced-colors: active){.cdk-overlay-backdrop-showing{opacity:.6}}@layer cdk-overlay{.cdk-overlay-dark-backdrop{background:rgba(0,0,0,.32)}}.cdk-overlay-transparent-backdrop{transition:visibility 1ms linear,opacity 1ms linear;visibility:hidden;opacity:1}.cdk-overlay-transparent-backdrop.cdk-overlay-backdrop-showing,.cdk-high-contrast-active .cdk-overlay-transparent-backdrop{opacity:0;visibility:visible}.cdk-overlay-backdrop-noop-animation{transition:none}.cdk-overlay-connected-position-bounding-box{position:absolute;display:flex;flex-direction:column;min-width:1px;min-height:1px}@layer cdk-overlay{.cdk-overlay-connected-position-bounding-box{z-index:1000}}.cdk-global-scrollblock{position:fixed;width:100%;overflow-y:scroll}.cdk-overlay-popover{background:none;border:none;padding:0;outline:0;overflow:visible;position:fixed;pointer-events:none;white-space:normal;color:inherit;text-decoration:none;width:100%;height:100%;inset:auto;top:0;left:0}.cdk-overlay-popover::backdrop{display:none}.cdk-overlay-popover .cdk-overlay-backdrop{position:fixed;z-index:auto}\n"] }] }] }); class OverlayContainer { _platform = inject(Platform); _containerElement; _document = inject(DOCUMENT); _styleLoader = inject(_CdkPrivateStyleLoader); constructor() {} ngOnDestroy() { this._containerElement?.remove(); } getContainerElement() { this._loadStyles(); if (!this._containerElement) { this._createContainer(); } return this._containerElement; } _createContainer() { const containerClass = 'cdk-overlay-container'; if (this._platform.isBrowser || _isTestEnvironment()) { const oppositePlatformContainers = this._document.querySelectorAll(`.${containerClass}[platform="server"], ` + `.${containerClass}[platform="test"]`); for (let i = 0; i < oppositePlatformContainers.length; i++) { oppositePlatformContainers[i].remove(); } } const container = this._document.createElement('div'); container.classList.add(containerClass); if (_isTestEnvironment()) { container.setAttribute('platform', 'test'); } else if (!this._platform.isBrowser) { container.setAttribute('platform', 'server'); } this._document.body.appendChild(container); this._containerElement = container; } _loadStyles() { this._styleLoader.load(_CdkOverlayStyleLoader); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: OverlayContainer, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: OverlayContainer, providedIn: 'root' }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: OverlayContainer, decorators: [{ type: Injectable, args: [{ providedIn: 'root' }] }], ctorParameters: () => [] }); class BackdropRef { _renderer; _ngZone; element; _cleanupClick; _cleanupTransitionEnd; _fallbackTimeout; constructor(document, _renderer, _ngZone, onClick) { this._renderer = _renderer; this._ngZone = _ngZone; this.element = document.createElement('div'); this.element.classList.add('cdk-overlay-backdrop'); this._cleanupClick = _renderer.listen(this.element, 'click', onClick); } detach() { this._ngZone.runOutsideAngular(() => { const element = this.element; clearTimeout(this._fallbackTimeout); this._cleanupTransitionEnd?.(); this._cleanupTransitionEnd = this._renderer.listen(element, 'transitionend', this.dispose); this._fallbackTimeout = setTimeout(this.dispose, 500); element.style.pointerEvents = 'none'; element.classList.remove('cdk-overlay-backdrop-showing'); }); } dispose = () => { clearTimeout(this._fallbackTimeout); this._cleanupClick?.(); this._cleanupTransitionEnd?.(); this._cleanupClick = this._cleanupTransitionEnd = this._fallbackTimeout = undefined; this.element.remove(); }; } function isElement(value) { return value && value.nodeType === 1; } class OverlayRef { _portalOutlet; _host; _pane; _config; _ngZone; _keyboardDispatcher; _document; _location; _outsideClickDispatcher; _animationsDisabled; _injector; _renderer; _backdropClick = new Subject(); _attachments = new Subject(); _detachments = new Subject(); _positionStrategy; _scrollStrategy; _locationChanges = Subscription.EMPTY; _backdropRef = null; _detachContentMutationObserver; _detachContentAfterRenderRef; _previousHostParent; _keydownEvents = new Subject(); _outsidePointerEvents = new Subject(); _afterNextRenderRef; constructor(_portalOutlet, _host, _pane, _config, _ngZone, _keyboardDispatcher, _document, _location, _outsideClickDispatcher, _animationsDisabled = false, _injector, _renderer) { 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._outsideClickDispatcher = _outsideClickDispatcher; this._animationsDisabled = _animationsDisabled; this._injector = _injector; this._renderer = _renderer; if (_config.scrollStrategy) { this._scrollStrategy = _config.scrollStrategy; this._scrollStrategy.attach(this); } this._positionStrategy = _config.positionStrategy; } get overlayElement() { return this._pane; } get backdropElement() { return this._backdropRef?.element || null; } get hostElement() { return this._host; } attach(portal) { this._attachHost(); const attachResult = this._portalOutlet.attach(portal); this._positionStrategy?.attach(this); this._updateStackingOrder(); this._updateElementSize(); this._updateElementDirection(); if (this._scrollStrategy) { this._scrollStrategy.enable(); } this._afterNextRenderRef?.destroy(); this._afterNextRenderRef = afterNextRender(() => { if (this.hasAttached()) { this.updatePosition(); } }, { injector: this._injector }); this._togglePointerEvents(true); if (this._config.hasBackdrop) { this._attachBackdrop(); } if (this._config.panelClass) { this._toggleClasses(this._pane, this._config.panelClass, true); } this._attachments.next(); this._completeDetachContent(); this._keyboardDispatcher.add(this); if (this._config.disposeOnNavigation) { this._locationChanges = this._location.subscribe(() => this.dispose()); } this._outsideClickDispatcher.add(this); if (typeof attachResult?.onDestroy === 'function') { attachResult.onDestroy(() => { if (this.hasAttached()) { this._ngZone.runOutsideAngular(() => Promise.resolve().then(() => this.detach())); } }); } return attachResult; } detach() { if (!this.hasAttached()) { return; } this.detachBackdrop(); this._togglePointerEvents(false); if (this._positionStrategy && this._positionStrategy.detach) { this._positionStrategy.detach(); } if (this._scrollStrategy) { this._scrollStrategy.disable(); } const detachmentResult = this._portalOutlet.detach(); this._detachments.next(); this._completeDetachContent(); this._keyboardDispatcher.remove(this); this._detachContentWhenEmpty(); this._locationChanges.unsubscribe(); this._outsideClickDispatcher.remove(this); return detachmentResult; } dispose() { const isAttached = this.hasAttached(); if (this._positionStrategy) { this._positionStrategy.dispose(); } this._disposeScrollStrategy(); this._backdropRef?.dispose(); this._locationChanges.unsubscribe(); this._keyboardDispatcher.remove(this); this._portalOutlet.dispose(); this._attachments.complete(); this._backdropClick.complete(); this._keydownEvents.complete(); this._outsidePointerEvents.complete(); this._outsideClickDispatcher.remove(this); this._host?.remove(); this._afterNextRenderRef?.destroy(); this._previousHostParent = this._pane = this._host = this._backdropRef = null; if (isAttached) { this._detachments.next(); } this._detachments.complete(); this._completeDetachContent(); } hasAttached() { return this._portalOutlet.hasAttached(); } backdropClick() { return this._backdropClick; } attachments() { return this._attachments; } detachments() { return this._detachments; } keydownEvents() { return this._keydownEvents; } outsidePointerEvents() { return this._outsidePointerEvents; } getConfig() { return this._config; } updatePosition() { if (this._positionStrategy) { this._positionStrategy.apply(); } } updatePositionStrategy(strategy) { if (strategy === this._positionStrategy) { return; } if (this._positionStrategy) { this._positionStrategy.dispose(); } this._positionStrategy = strategy; if (this.hasAttached()) { strategy.attach(this); this.updatePosition(); } } updateSize(sizeConfig) { this._config = { ...this._config, ...sizeConfig }; this._updateElementSize(); } setDirection(dir) { this._config = { ...this._config, direction: dir }; this._updateElementDirection(); } addPanelClass(classes) { if (this._pane) { this._toggleClasses(this._pane, classes, true); } } removePanelClass(classes) { if (this._pane) { this._toggleClasses(this._pane, classes, false); } } getDirection() { const direction = this._config.direction; if (!direction) { return 'ltr'; } return typeof direction === 'string' ? direction : direction.value; } updateScrollStrategy(strategy) { if (strategy === this._scrollStrategy) { return; } this._disposeScrollStrategy(); this._scrollStrategy = strategy; if (this.hasAttached()) { strategy.attach(this); strategy.enable(); } } _updateElementDirection() { this._host.setAttribute('dir', this.getDirection()); } _updateElementSize() { if (!this._pane) { return; } const style = this._pane.style; style.width = coerceCssPixelValue(this._config.width); style.height = coerceCssPixelValue(this._config.height); style.minWidth = coerceCssPixelValue(this._config.minWidth); style.minHeight = coerceCssPixelValue(this._config.minHeight); style.maxWidth = coerceCssPixelValue(this._config.maxWidth); style.maxHeight = coerceCssPixelValue(this._config.maxHeight); } _togglePointerEvents(enablePointer) { this._pane.style.pointerEvents = enablePointer ? '' : 'none'; } _attachHost() { if (!this._host.parentElement) { const customInsertionPoint = this._config.usePopover ? this._positionStrategy?.getPopoverInsertionPoint?.() : null; if (isElement(customInsertionPoint)) { customInsertionPoint.after(this._host); } else if (customInsertionPoint?.type === 'parent') { customInsertionPoint.element.appendChild(this._host); } else { this._previousHostParent?.appendChild(this._host); } } if (this._config.usePopover) { try { this._host['showPopover'](); } catch {} } } _attachBackdrop() { const showingClass = 'cdk-overlay-backdrop-showing'; this._backdropRef?.dispose(); this._backdropRef = new BackdropRef(this._document, this._renderer, this._ngZone, event => { this._backdropClick.next(event); }); if (this._animationsDisabled) { this._backdropRef.element.classList.add('cdk-overlay-backdrop-noop-animation'); } if (this._config.backdropClass) { this._toggleClasses(this._backdropRef.element, this._config.backdropClass, true); } if (this._config.usePopover) { this._host.prepend(this._backdropRef.element); } else { this._host.parentElement.insertBefore(this._backdropRef.element, this._host); } if (!this._animationsDisabled && typeof requestAnimationFrame !== 'undefined') { this._ngZone.runOutsideAngular(() => { requestAnimationFrame(() => this._backdropRef?.element.classList.add(showingClass)); }); } else { this._backdropRef.element.classList.add(showingClass); } } _updateStackingOrder() { if (!this._config.usePopover && this._host.nextSibling) { this._host.parentNode.appendChild(this._host); } } detachBackdrop() { if (this._animationsDisabled) { this._backdropRef?.dispose(); this._backdropRef = null; } else { this._backdropRef?.detach(); } } _toggleClasses(element, cssClasses, isAdd) { const classes = coerceArray(cssClasses || []).filter(c => !!c); if (classes.length) { isAdd ? element.classList.add(...classes) : element.classList.remove(...classes); } } _detachContentWhenEmpty() { let rethrow = false; try { this._detachContentAfterRenderRef = afterNextRender(() => { rethrow = true; this._detachContent(); }, { injector: this._injector }); } catch (e) { if (rethrow) { throw e; } this._detachContent(); } if (globalThis.MutationObserver && this._pane) { this._detachContentMutationObserver ||= new globalThis.MutationObserver(() => { this._detachContent(); }); this._detachContentMutationObserver.observe(this._pane, { childList: true }); } } _detachContent() { if (!this._pane || !this._host || this._pane.children.length === 0) { if (this._pane && this._config.panelClass) { this._toggleClasses(this._pane, this._config.panelClass, false); } if (this._host && this._host.parentElement) { this._previousHostParent = this._host.parentElement; this._host.remove(); } this._completeDetachContent(); } } _completeDetachContent() { this._detachContentAfterRenderRef?.destroy(); this._detachContentAfterRenderRef = undefined; this._detachContentMutationObserver?.disconnect(); } _disposeScrollStrategy() { const scrollStrategy = this._scrollStrategy; scrollStrategy?.disable(); scrollStrategy?.detach?.(); } } const boundingBoxClass = 'cdk-overlay-connected-position-bounding-box'; const cssUnitPattern = /([A-Za-z%]+)$/; function createFlexibleConnectedPositionStrategy(injector, origin) { return new FlexibleConnectedPositionStrategy(origin, injector.get(ViewportRuler), injector.get(DOCUMENT), injector.get(Platform), injector.get(OverlayContainer)); } class FlexibleConnectedPositionStrategy { _viewportRuler; _document; _platform; _overlayContainer; _overlayRef; _isInitialRender; _lastBoundingBoxSize = { width: 0, height: 0 }; _isPushed = false; _canPush = true; _growAfterOpen = false; _hasFlexibleDimensions = true; _positionLocked = false; _originRect; _overlayRect; _viewportRect; _containerRect; _viewportMargin = 0; _scrollables = []; _preferredPositions = []; _origin; _pane; _isDisposed; _boundingBox; _lastPosition; _lastScrollVisibility; _positionChanges = new Subject(); _resizeSubscription = Subscription.EMPTY; _offsetX = 0; _offsetY = 0; _transformOriginSelector; _appliedPanelClasses = []; _previousPushAmount; _popoverLocation = 'global'; positionChanges = this._positionChanges; get positions() { return this._preferredPositions; } constructor(connectedTo, _viewportRuler, _document, _platform, _overlayContainer) { this._viewportRuler = _viewportRuler; this._document = _document; this._platform = _platform; this._overlayContainer = _overlayContainer; this.setOrigin(connectedTo); } attach(overlayRef) { if (this._overlayRef && overlayRef !== this._overlayRef && (typeof ngDevMode === 'undefined' || ngDevMode)) { throw Error('This position strategy is already attached to an overlay'); } this._validatePositions(); overlayRef.hostElement.classList.add(boundingBoxClass); this._overlayRef = overlayRef; this._boundingBox = overlayRef.hostElement; this._pane = overlayRef.overlayElement; this._isDisposed = false; this._isInitialRender = true; this._lastPosition = null; this._resizeSubscription.unsubscribe(); this._resizeSubscription = this._viewportRuler.change().subscribe(() => { this._isInitialRender = true; this.apply(); }); } apply() { if (this._isDisposed || !this._platform.isBrowser) { return; } if (!this._isInitialRender && this._positionLocked && this._lastPosition) { this.reapplyLastPosition(); return; } this._clearPanelClasses(); this._resetOverlayElementStyles(); this._resetBoundingBoxStyles(); this._viewportRect = this._getNarrowedViewportRect(); this._originRect = this._getOriginRect(); this._overlayRect = this._pane.getBoundingClientRect(); this._containerRect = this._overlayContainer.getContainerElement().getBoundingClientRect(); const originRect = this._originRect; const overlayRect = this._overlayRect; const viewportRect = this._viewportRect; const containerRect = this._containerRect; const flexibleFits = []; let fallback; for (let pos of this._preferredPositions) { let originPoint = this._getOriginPoint(originRect, containerRect, pos); let overlayPoint = this._getOverlayPoint(originPoint, overlayRect, pos); let overlayFit = this._getOverlayFit(overlayPoint, overlayRect, viewportRect, pos); if (overlayFit.isCompletelyWithinViewport) { this._isPushed = false; this._applyPosition(pos, originPoint); return; } if (this._canFitWithFlexibleDimensions(overlayFit, overlayPoint, viewportRect)) { flexibleFits.push({ position: pos, origin: originPoint, overlayRect, boundingBoxRect: this._calculateBoundingBoxRect(originPoint, pos) }); continue; } if (!fallback || fallback.overlayFit.visibleArea < overlayFit.visibleArea) { fallback = { overlayFit, overlayPoint, originPoint, position: pos, overlayRect }; } } if (flexibleFits.length) { let bestFit = null; let bestScore = -1; for (const fit of flexibleFits) { const score = fit.boundingBoxRect.width * fit.boundingBoxRect.height * (fit.position.weight || 1); if (score > bestScore) { bestScore = score; bestFit = fit; } } this._isPushed = false; this._applyPosition(bestFit.position, bestFit.origin); return; } if (this._canPush) { this._isPushed = true; this._applyPosition(fallback.position, fallback.originPoint); return; } this._applyPosition(fallback.position, fallback.originPoint); } detach() { this._clearPanelClasses(); this._lastPosition = null; this._previousPushAmount = null; this._resizeSubscription.unsubscribe(); } dispose() { if (this._isDisposed) { return; } if (this._boundingBox) { extendStyles(this._boundingBox.style, { top: '', left: '', right: '', bottom: '', height: '', width: '', alignItems: '', justifyContent: '' }); } if (this._pane) { this._resetOverlayElementStyles(); } if (this._overlayRef) { this._overlayRef.hostElement.classList.remove(boundingBoxClass); } this.detach(); this._positionChanges.complete(); this._overlayRef = this._boundingBox = null; this._isDisposed = true; } reapplyLastPosition() { if (this._isDisposed || !this._platform.isBrowser) { return; } const lastPosition = this._lastPosition; if (lastPosition) { this._originRect = this._getOriginRect(); this._overlayRect = this._pane.getBoundingClientRect(); this._viewportRect = this._getNarrowedViewportRect(); this._containerRect = this._overlayContainer.getContainerElement().getBoundingClientRect(); const originPoint = this._getOriginPoint(this._originRect, this._containerRect, lastPosition); this._applyPosition(lastPosition, originPoint); } else { this.apply(); } } withScrollableContainers(scrollables) { this._scrollables = scrollables; return this; } withPositions(positions) { this._preferredPositions = positions; if (positions.indexOf(this._lastPosition) === -1) { this._lastPosition = null; } this._validatePositions(); return this; } withViewportMargin(margin) { this._viewportMargin = margin; return this; } withFlexibleDimensions(flexibleDimensions = true) { this._hasFlexibleDimensions = flexibleDimensions; return this; } withGrowAfterOpen(growAfterOpen = true) { this._growAfterOpen = growAfterOpen; return this; } withPush(canPush = true) { this._canPush = canPush; return this; } withLockedPosition(isLocked = true) { this._positionLocked = isLocked; return this; } setOrigin(origin) { this._origin = origin; return this; } withDefaultOffsetX(offset) { this._offsetX = offset; return this; } withDefaultOffsetY(offset) { this._offsetY = offset; return this; } withTransformOriginOn(selector) { this._transformOriginSelector = selector; return this; } withPopoverLocation(location) { this._popoverLocation = location; return this; } getPopoverInsertionPoint() { if (this._popoverLocation === 'global') { return null; } else if (this._popoverLocation !== 'inline') { return this._popoverLocation; } if (this._origin instanceof ElementRef) { return this._origin.nativeElement; } else if (isElement(this._origin)) { return this._origin; } else { return null; } } _getOriginPoint(originRect, containerRect, pos) { let x; if (pos.originX == 'center') { x = originRect.left + originRect.width / 2; } else { const startX = this._isRtl() ? originRect.right : originRect.left; const endX = this._isRtl() ? originRect.left : originRect.right; x = pos.originX == 'start' ? startX : endX; } if (containerRect.left < 0) { x -= containerRect.left; } let y; if (pos.originY == 'center') { y = originRect.top + originRect.height / 2; } else { y = pos.originY == 'top' ? originRect.top : originRect.bottom; } if (containerRect.top < 0) { y -= containerRect.top; } return { x, y }; } _getOverlayPoint(originPoint, overlayRect, pos) { let overlayStartX; if (pos.overlayX == 'center') { overlayStartX = -overlayRect.width / 2; } else if (pos.overlayX === 'start') { overlayStartX = this._isRtl() ? -overlayRect.width : 0; } else { overlayStartX = this._isRtl() ? 0 : -overlayRect.width; } let overlayStartY; if (pos.overlayY == 'center') { overlayStartY = -overlayRect.height / 2; } else { overlayStartY = pos.overlayY == 'top' ? 0 : -overlayRect.height; } return { x: originPoint.x + overlayStartX, y: originPoint.y + overlayStartY }; } _getOverlayFit(point, rawOverlayRect, viewport, position) { const overlay = getRoundedBoundingClientRect(rawOverlayRect); let { x, y } = point; let offsetX = this._getOffset(position, 'x'); let offsetY = this._getOffset(position, 'y'); if (offsetX) { x += offsetX; } if (offsetY) { y += offsetY; } let leftOverflow = 0 - x; let rightOverflow = x + overlay.width - viewport.width; let topOverflow = 0 - y; let bottomOverflow = y + overlay.height - viewport.height; let visibleWidth = this._subtractOverflows(overlay.width, leftOverflow, rightOverflow); let visibleHeight = this._subtractOverflows(overlay.height, topOverflow, bottomOverflow); let visibleArea = visibleWidth * visibleHeight; return { visibleArea, isCompletelyWithinViewport: overlay.width * overlay.height === visibleArea, fitsInViewportVertically: visibleHeight === overlay.height, fitsInViewportHorizontally: visibleWidth == overlay.width }; } _canFitWithFlexibleDimensions(fit, point, viewport) { if (this._hasFlexibleDimensions) { const availableHeight = viewport.bottom - point.y; const availableWidth = viewport.right - point.x; const minHeight = getPixelValue(this._overlayRef.getConfig().minHeight); const minWidth = getPixelValue(this._overlayRef.getConfig().minWidth); const verticalFit = fit.fitsInViewportVertically || minHeight != null && minHeight <= availableHeight; const horizontalFit = fit.fitsInViewportHorizontally || minWidth != null && minWidth <= availableWidth; return verticalFit && horizontalFit; } return false; } _pushOverlayOnScreen(start, rawOverlayRect, scrollPosition) { if (this._previousPushAmount && this._positionLocked) { return { x: start.x + this._previousPushAmount.x, y: start.y + this._previousPushAmount.y }; } const overlay = getRoundedBoundingClientRect(rawOverlayRect); const viewport = this._viewportRect; const overflowRight = Math.max(start.x + overlay.width - viewport.width, 0); const overflowBottom = Math.max(start.y + overlay.height - viewport.height, 0); const overflowTop = Math.max(viewport.top - scrollPosition.top - start.y, 0); const overflowLeft = Math.max(viewport.left - scrollPosition.left - start.x, 0); let pushX = 0; let pushY = 0; if (overlay.width <= viewport.width) { pushX = overflowLeft || -overflowRight; } else { pushX = start.x < this._getViewportMarginStart() ? viewport.left - scrollPosition.left - start.x : 0; } if (overlay.height <= viewport.height) { pushY = overflowTop || -overflowBottom; } else { pushY = start.y < this._getViewportMarginTop() ? viewport.top - scrollPosition.top - start.y : 0; } this._previousPushAmount = { x: pushX, y: pushY }; return { x: start.x + pushX, y: start.y + pushY }; } _applyPosition(position, originPoint) { this._setTransformOrigin(position); this._setOverlayElementStyles(originPoint, position); this._setBoundingBoxStyles(originPoint, position); if (position.panelClass) { this._addPanelClasses(position.panelClass); } if (this._positionChanges.observers.length) { const scrollVisibility = this._getScrollVisibility(); if (position !== this._lastPosition || !this._lastScrollVisibility || !compareScrollVisibility(this._lastScrollVisibility, scrollVisibility)) { const changeEvent = new ConnectedOverlayPositionChange(position, scrollVisibility); this._positionChanges.next(changeEvent); } this._lastScrollVisibility = scrollVisibility; } this._lastPosition = position; this._isInitialRender = false; } _setTransformOrigin(position) { if (!this._transformOriginSelector) { return; } const elements = this._boundingBox.querySelectorAll(this._transformOriginSelector); let xOrigin; let yOrigin = position.overlayY; if (position.overlayX === 'center') { xOrigin = 'center'; } else if (this._isRtl()) { xOrigin = position.overlayX === 'start' ? 'right' : 'left'; } else { xOrigin = position.overlayX === 'start' ? 'left' : 'right'; } for (let i = 0; i < elements.length; i++) { elements[i].style.transformOrigin = `${xOrigin} ${yOrigin}`; } } _calculateBoundingBoxRect(origin, position) { const viewport = this._viewportRect; const isRtl = this._isRtl(); let height, top, bottom; if (position.overlayY === 'top') { top = origin.y; height = viewport.height - top + this._getViewportMarginBottom(); } else if (position.