import { ESCAPE, hasModifierKey } from "./chunk-JGP5BR6B.js"; import { BidiModule, Directionality } from "./chunk-6NZ2BEBG.js"; import { Platform, _getEventTarget, _isTestEnvironment, getRtlScrollAxisType, supportsScrollBehavior } from "./chunk-IAC53GOD.js"; import { coerceArray, coerceCssPixelValue, coerceElement, coerceNumberProperty } from "./chunk-M5GY4FCO.js"; import { DOCUMENT, Location } from "./chunk-OSQBUYO6.js"; import { ANIMATION_MODULE_TYPE, ApplicationRef, ChangeDetectionStrategy, ChangeDetectorRef, Component, ComponentFactoryResolver$1, ConnectableObservable, Directive, ElementRef, EventEmitter, Inject, Injectable, InjectionToken, Injector, Input, IterableDiffers, NgModule, NgZone, Observable, Optional, Output, SkipSelf, Subject, Subscription, TemplateRef, ViewChild, ViewContainerRef, ViewEncapsulation$1, animationFrameScheduler, asapScheduler, auditTime, booleanAttribute, distinctUntilChanged, filter, forwardRef, fromEvent, inject, isObservable, merge, of, pairwise, setClassMetadata, shareReplay, startWith, switchMap, take, takeUntil, takeWhile, ɵɵInheritDefinitionFeature, ɵɵInputTransformsFeature, ɵɵNgOnChangesFeature, ɵɵProvidersFeature, ɵɵStandaloneFeature, ɵɵadvance, ɵɵclassProp, ɵɵdefineComponent, ɵɵdefineDirective, ɵɵdefineInjectable, ɵɵdefineInjector, ɵɵdefineNgModule, ɵɵdirectiveInject, ɵɵelement, ɵɵelementEnd, ɵɵelementStart, ɵɵgetInheritedFactory, ɵɵinject, ɵɵloadQuery, ɵɵprojection, ɵɵprojectionDef, ɵɵqueryRefresh, ɵɵstyleProp, ɵɵviewQuery } from "./chunk-FGESKT7O.js"; import { __spreadProps, __spreadValues } from "./chunk-HSNDBVJ3.js"; // node_modules/@angular/cdk/fesm2022/collections.mjs var DataSource = class { }; function isDataSource(value) { return value && typeof value.connect === "function" && !(value instanceof ConnectableObservable); } var ArrayDataSource = class extends DataSource { constructor(_data) { super(); this._data = _data; } connect() { return isObservable(this._data) ? this._data : of(this._data); } disconnect() { } }; var _RecycleViewRepeaterStrategy = class { constructor() { this.viewCacheSize = 20; this._viewCache = []; } /** Apply changes to the DOM. */ applyChanges(changes, viewContainerRef, itemContextFactory, itemValueResolver, itemViewChanged) { changes.forEachOperation((record, adjustedPreviousIndex, currentIndex) => { let view; let operation; if (record.previousIndex == null) { const viewArgsFactory = () => itemContextFactory(record, adjustedPreviousIndex, currentIndex); view = this._insertView(viewArgsFactory, currentIndex, viewContainerRef, itemValueResolver(record)); operation = view ? 1 : 0; } else if (currentIndex == null) { this._detachAndCacheView(adjustedPreviousIndex, viewContainerRef); operation = 3; } else { view = this._moveView(adjustedPreviousIndex, currentIndex, viewContainerRef, itemValueResolver(record)); operation = 2; } if (itemViewChanged) { itemViewChanged({ context: view?.context, operation, record }); } }); } detach() { for (const view of this._viewCache) { view.destroy(); } this._viewCache = []; } /** * Inserts a view for a new item, either from the cache or by creating a new * one. Returns `undefined` if the item was inserted into a cached view. */ _insertView(viewArgsFactory, currentIndex, viewContainerRef, value) { const cachedView = this._insertViewFromCache(currentIndex, viewContainerRef); if (cachedView) { cachedView.context.$implicit = value; return void 0; } const viewArgs = viewArgsFactory(); return viewContainerRef.createEmbeddedView(viewArgs.templateRef, viewArgs.context, viewArgs.index); } /** Detaches the view at the given index and inserts into the view cache. */ _detachAndCacheView(index, viewContainerRef) { const detachedView = viewContainerRef.detach(index); this._maybeCacheView(detachedView, viewContainerRef); } /** Moves view at the previous index to the current index. */ _moveView(adjustedPreviousIndex, currentIndex, viewContainerRef, value) { const view = viewContainerRef.get(adjustedPreviousIndex); viewContainerRef.move(view, currentIndex); view.context.$implicit = value; return view; } /** * Cache the given detached view. If the cache is full, the view will be * destroyed. */ _maybeCacheView(view, viewContainerRef) { if (this._viewCache.length < this.viewCacheSize) { this._viewCache.push(view); } else { const index = viewContainerRef.indexOf(view); if (index === -1) { view.destroy(); } else { viewContainerRef.remove(index); } } } /** Inserts a recycled view from the cache at the given index. */ _insertViewFromCache(index, viewContainerRef) { const cachedView = this._viewCache.pop(); if (cachedView) { viewContainerRef.insert(cachedView, index); } return cachedView || null; } }; var _UniqueSelectionDispatcher = class _UniqueSelectionDispatcher { constructor() { this._listeners = []; } /** * Notify other items that selection for the given name has been set. * @param id ID of the item. * @param name Name of the item. */ notify(id, name) { for (let listener of this._listeners) { listener(id, name); } } /** * Listen for future changes to item selection. * @return Function used to deregister listener */ listen(listener) { this._listeners.push(listener); return () => { this._listeners = this._listeners.filter((registered) => { return listener !== registered; }); }; } ngOnDestroy() { this._listeners = []; } }; _UniqueSelectionDispatcher.ɵfac = function UniqueSelectionDispatcher_Factory(t) { return new (t || _UniqueSelectionDispatcher)(); }; _UniqueSelectionDispatcher.ɵprov = ɵɵdefineInjectable({ token: _UniqueSelectionDispatcher, factory: _UniqueSelectionDispatcher.ɵfac, providedIn: "root" }); var UniqueSelectionDispatcher = _UniqueSelectionDispatcher; (() => { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(UniqueSelectionDispatcher, [{ type: Injectable, args: [{ providedIn: "root" }] }], null, null); })(); var _VIEW_REPEATER_STRATEGY = new InjectionToken("_ViewRepeater"); // node_modules/@angular/cdk/fesm2022/scrolling.mjs var _c0 = ["contentWrapper"]; var _c1 = ["*"]; var VIRTUAL_SCROLL_STRATEGY = new InjectionToken("VIRTUAL_SCROLL_STRATEGY"); var FixedSizeVirtualScrollStrategy = class { /** * @param itemSize The size of the items in the virtually scrolling list. * @param minBufferPx The minimum amount of buffer (in pixels) before needing to render more * @param maxBufferPx The amount of buffer (in pixels) to render when rendering more. */ constructor(itemSize, minBufferPx, maxBufferPx) { this._scrolledIndexChange = new Subject(); this.scrolledIndexChange = this._scrolledIndexChange.pipe(distinctUntilChanged()); this._viewport = null; this._itemSize = itemSize; this._minBufferPx = minBufferPx; this._maxBufferPx = maxBufferPx; } /** * Attaches this scroll strategy to a viewport. * @param viewport The viewport to attach this strategy to. */ attach(viewport) { this._viewport = viewport; this._updateTotalContentSize(); this._updateRenderedRange(); } /** Detaches this scroll strategy from the currently attached viewport. */ detach() { this._scrolledIndexChange.complete(); this._viewport = null; } /** * Update the item size and buffer size. * @param itemSize The size of the items in the virtually scrolling list. * @param minBufferPx The minimum amount of buffer (in pixels) before needing to render more * @param maxBufferPx The amount of buffer (in pixels) to render when rendering more. */ updateItemAndBufferSize(itemSize, minBufferPx, maxBufferPx) { if (maxBufferPx < minBufferPx && (typeof ngDevMode === "undefined" || ngDevMode)) { throw Error("CDK virtual scroll: maxBufferPx must be greater than or equal to minBufferPx"); } this._itemSize = itemSize; this._minBufferPx = minBufferPx; this._maxBufferPx = maxBufferPx; this._updateTotalContentSize(); this._updateRenderedRange(); } /** @docs-private Implemented as part of VirtualScrollStrategy. */ onContentScrolled() { this._updateRenderedRange(); } /** @docs-private Implemented as part of VirtualScrollStrategy. */ onDataLengthChanged() { this._updateTotalContentSize(); this._updateRenderedRange(); } /** @docs-private Implemented as part of VirtualScrollStrategy. */ onContentRendered() { } /** @docs-private Implemented as part of VirtualScrollStrategy. */ onRenderedOffsetChanged() { } /** * Scroll to the offset for the given index. * @param index The index of the element to scroll to. * @param behavior The ScrollBehavior to use when scrolling. */ scrollToIndex(index, behavior) { if (this._viewport) { this._viewport.scrollToOffset(index * this._itemSize, behavior); } } /** Update the viewport's total content size. */ _updateTotalContentSize() { if (!this._viewport) { return; } this._viewport.setTotalContentSize(this._viewport.getDataLength() * this._itemSize); } /** Update the viewport's rendered range. */ _updateRenderedRange() { if (!this._viewport) { return; } const renderedRange = this._viewport.getRenderedRange(); const newRange = { start: renderedRange.start, end: renderedRange.end }; const viewportSize = this._viewport.getViewportSize(); const dataLength = this._viewport.getDataLength(); let scrollOffset = this._viewport.measureScrollOffset(); let firstVisibleIndex = this._itemSize > 0 ? scrollOffset / this._itemSize : 0; if (newRange.end > dataLength) { const maxVisibleItems = Math.ceil(viewportSize / this._itemSize); const newVisibleIndex = Math.max(0, Math.min(firstVisibleIndex, dataLength - maxVisibleItems)); if (firstVisibleIndex != newVisibleIndex) { firstVisibleIndex = newVisibleIndex; scrollOffset = newVisibleIndex * this._itemSize; newRange.start = Math.floor(firstVisibleIndex); } newRange.end = Math.max(0, Math.min(dataLength, newRange.start + maxVisibleItems)); } const startBuffer = scrollOffset - newRange.start * this._itemSize; if (startBuffer < this._minBufferPx && newRange.start != 0) { const expandStart = Math.ceil((this._maxBufferPx - startBuffer) / this._itemSize); newRange.start = Math.max(0, newRange.start - expandStart); newRange.end = Math.min(dataLength, Math.ceil(firstVisibleIndex + (viewportSize + this._minBufferPx) / this._itemSize)); } else { const endBuffer = newRange.end * this._itemSize - (scrollOffset + viewportSize); if (endBuffer < this._minBufferPx && newRange.end != dataLength) { const expandEnd = Math.ceil((this._maxBufferPx - endBuffer) / this._itemSize); if (expandEnd > 0) { newRange.end = Math.min(dataLength, newRange.end + expandEnd); newRange.start = Math.max(0, Math.floor(firstVisibleIndex - this._minBufferPx / this._itemSize)); } } } this._viewport.setRenderedRange(newRange); this._viewport.setRenderedContentOffset(this._itemSize * newRange.start); this._scrolledIndexChange.next(Math.floor(firstVisibleIndex)); } }; function _fixedSizeVirtualScrollStrategyFactory(fixedSizeDir) { return fixedSizeDir._scrollStrategy; } var _CdkFixedSizeVirtualScroll = class _CdkFixedSizeVirtualScroll { constructor() { this._itemSize = 20; this._minBufferPx = 100; this._maxBufferPx = 200; this._scrollStrategy = new FixedSizeVirtualScrollStrategy(this.itemSize, this.minBufferPx, this.maxBufferPx); } /** The size of the items in the list (in pixels). */ get itemSize() { return this._itemSize; } set itemSize(value) { this._itemSize = coerceNumberProperty(value); } /** * The minimum amount of buffer rendered beyond the viewport (in pixels). * If the amount of buffer dips below this number, more items will be rendered. Defaults to 100px. */ get minBufferPx() { return this._minBufferPx; } set minBufferPx(value) { this._minBufferPx = coerceNumberProperty(value); } /** * The number of pixels worth of buffer to render for when rendering new items. Defaults to 200px. */ get maxBufferPx() { return this._maxBufferPx; } set maxBufferPx(value) { this._maxBufferPx = coerceNumberProperty(value); } ngOnChanges() { this._scrollStrategy.updateItemAndBufferSize(this.itemSize, this.minBufferPx, this.maxBufferPx); } }; _CdkFixedSizeVirtualScroll.ɵfac = function CdkFixedSizeVirtualScroll_Factory(t) { return new (t || _CdkFixedSizeVirtualScroll)(); }; _CdkFixedSizeVirtualScroll.ɵdir = ɵɵdefineDirective({ type: _CdkFixedSizeVirtualScroll, selectors: [["cdk-virtual-scroll-viewport", "itemSize", ""]], inputs: { itemSize: "itemSize", minBufferPx: "minBufferPx", maxBufferPx: "maxBufferPx" }, standalone: true, features: [ɵɵProvidersFeature([{ provide: VIRTUAL_SCROLL_STRATEGY, useFactory: _fixedSizeVirtualScrollStrategyFactory, deps: [forwardRef(() => _CdkFixedSizeVirtualScroll)] }]), ɵɵNgOnChangesFeature] }); var CdkFixedSizeVirtualScroll = _CdkFixedSizeVirtualScroll; (() => { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(CdkFixedSizeVirtualScroll, [{ type: Directive, args: [{ selector: "cdk-virtual-scroll-viewport[itemSize]", standalone: true, providers: [{ provide: VIRTUAL_SCROLL_STRATEGY, useFactory: _fixedSizeVirtualScrollStrategyFactory, deps: [forwardRef(() => CdkFixedSizeVirtualScroll)] }] }] }], null, { itemSize: [{ type: Input }], minBufferPx: [{ type: Input }], maxBufferPx: [{ type: Input }] }); })(); var DEFAULT_SCROLL_TIME = 20; var _ScrollDispatcher = class _ScrollDispatcher { constructor(_ngZone, _platform, document2) { this._ngZone = _ngZone; this._platform = _platform; this._scrolled = new Subject(); this._globalSubscription = null; this._scrolledCount = 0; this.scrollContainers = /* @__PURE__ */ new Map(); this._document = document2; } /** * Registers a scrollable instance with the service and listens for its scrolled events. When the * scrollable is scrolled, the service emits the event to its scrolled observable. * @param scrollable Scrollable instance to be registered. */ register(scrollable) { if (!this.scrollContainers.has(scrollable)) { this.scrollContainers.set(scrollable, scrollable.elementScrolled().subscribe(() => this._scrolled.next(scrollable))); } } /** * De-registers a Scrollable reference and unsubscribes from its scroll event observable. * @param scrollable Scrollable instance to be deregistered. */ deregister(scrollable) { const scrollableReference = this.scrollContainers.get(scrollable); if (scrollableReference) { scrollableReference.unsubscribe(); this.scrollContainers.delete(scrollable); } } /** * Returns an observable that emits an event whenever any of the registered Scrollable * references (or window, document, or body) fire a scrolled event. Can provide a time in ms * to override the default "throttle" time. * * **Note:** in order to avoid hitting change detection for every scroll event, * all of the events emitted from this stream will be run outside the Angular zone. * If you need to update any data bindings as a result of a scroll event, you have * to run the callback using `NgZone.run`. */ scrolled(auditTimeInMs = DEFAULT_SCROLL_TIME) { if (!this._platform.isBrowser) { return of(); } return new Observable((observer) => { if (!this._globalSubscription) { this._addGlobalListener(); } const subscription = auditTimeInMs > 0 ? this._scrolled.pipe(auditTime(auditTimeInMs)).subscribe(observer) : this._scrolled.subscribe(observer); this._scrolledCount++; return () => { subscription.unsubscribe(); this._scrolledCount--; if (!this._scrolledCount) { this._removeGlobalListener(); } }; }); } ngOnDestroy() { this._removeGlobalListener(); this.scrollContainers.forEach((_, container) => this.deregister(container)); this._scrolled.complete(); } /** * Returns an observable that emits whenever any of the * scrollable ancestors of an element are scrolled. * @param elementOrElementRef Element whose ancestors to listen for. * @param auditTimeInMs Time to throttle the scroll events. */ ancestorScrolled(elementOrElementRef, auditTimeInMs) { const ancestors = this.getAncestorScrollContainers(elementOrElementRef); return this.scrolled(auditTimeInMs).pipe(filter((target) => { return !target || ancestors.indexOf(target) > -1; })); } /** Returns all registered Scrollables that contain the provided element. */ getAncestorScrollContainers(elementOrElementRef) { const scrollingContainers = []; this.scrollContainers.forEach((_subscription, scrollable) => { if (this._scrollableContainsElement(scrollable, elementOrElementRef)) { scrollingContainers.push(scrollable); } }); return scrollingContainers; } /** Use defaultView of injected document if available or fallback to global window reference */ _getWindow() { return this._document.defaultView || window; } /** Returns true if the element is contained within the provided Scrollable. */ _scrollableContainsElement(scrollable, elementOrElementRef) { let element = coerceElement(elementOrElementRef); let scrollableElement = scrollable.getElementRef().nativeElement; do { if (element == scrollableElement) { return true; } } while (element = element.parentElement); return false; } /** Sets up the global scroll listeners. */ _addGlobalListener() { this._globalSubscription = this._ngZone.runOutsideAngular(() => { const window2 = this._getWindow(); return fromEvent(window2.document, "scroll").subscribe(() => this._scrolled.next()); }); } /** Cleans up the global scroll listener. */ _removeGlobalListener() { if (this._globalSubscription) { this._globalSubscription.unsubscribe(); this._globalSubscription = null; } } }; _ScrollDispatcher.ɵfac = function ScrollDispatcher_Factory(t) { return new (t || _ScrollDispatcher)(ɵɵinject(NgZone), ɵɵinject(Platform), ɵɵinject(DOCUMENT, 8)); }; _ScrollDispatcher.ɵprov = ɵɵdefineInjectable({ token: _ScrollDispatcher, factory: _ScrollDispatcher.ɵfac, providedIn: "root" }); var ScrollDispatcher = _ScrollDispatcher; (() => { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(ScrollDispatcher, [{ type: Injectable, args: [{ providedIn: "root" }] }], () => [{ type: NgZone }, { type: Platform }, { type: void 0, decorators: [{ type: Optional }, { type: Inject, args: [DOCUMENT] }] }], null); })(); var _CdkScrollable = class _CdkScrollable { constructor(elementRef, scrollDispatcher, ngZone, dir) { this.elementRef = elementRef; this.scrollDispatcher = scrollDispatcher; this.ngZone = ngZone; this.dir = dir; this._destroyed = new Subject(); this._elementScrolled = new Observable((observer) => this.ngZone.runOutsideAngular(() => fromEvent(this.elementRef.nativeElement, "scroll").pipe(takeUntil(this._destroyed)).subscribe(observer))); } ngOnInit() { this.scrollDispatcher.register(this); } ngOnDestroy() { this.scrollDispatcher.deregister(this); this._destroyed.next(); this._destroyed.complete(); } /** Returns observable that emits when a scroll event is fired on the host element. */ elementScrolled() { return this._elementScrolled; } /** Gets the ElementRef for the viewport. */ getElementRef() { return this.elementRef; } /** * Scrolls to the specified offsets. This is a normalized version of the browser's native scrollTo * method, since browsers are not consistent about what scrollLeft means in RTL. For this method * left and right always refer to the left and right side of the scrolling container irrespective * of the layout direction. start and end refer to left and right in an LTR context and vice-versa * in an RTL context. * @param options specified the offsets to scroll to. */ scrollTo(options) { const el = this.elementRef.nativeElement; const isRtl = this.dir && this.dir.value == "rtl"; if (options.left == null) { options.left = isRtl ? options.end : options.start; } if (options.right == null) { options.right = isRtl ? options.start : options.end; } if (options.bottom != null) { options.top = el.scrollHeight - el.clientHeight - options.bottom; } if (isRtl && getRtlScrollAxisType() != 0) { if (options.left != null) { options.right = el.scrollWidth - el.clientWidth - options.left; } if (getRtlScrollAxisType() == 2) { options.left = options.right; } else if (getRtlScrollAxisType() == 1) { options.left = options.right ? -options.right : options.right; } } else { if (options.right != null) { options.left = el.scrollWidth - el.clientWidth - options.right; } } this._applyScrollToOptions(options); } _applyScrollToOptions(options) { const el = this.elementRef.nativeElement; if (supportsScrollBehavior()) { el.scrollTo(options); } else { if (options.top != null) { el.scrollTop = options.top; } if (options.left != null) { el.scrollLeft = options.left; } } } /** * Measures the scroll offset relative to the specified edge of the viewport. This method can be * used instead of directly checking scrollLeft or scrollTop, since browsers are not consistent * about what scrollLeft means in RTL. The values returned by this method are normalized such that * left and right always refer to the left and right side of the scrolling container irrespective * of the layout direction. start and end refer to left and right in an LTR context and vice-versa * in an RTL context. * @param from The edge to measure from. */ measureScrollOffset(from) { const LEFT = "left"; const RIGHT = "right"; const el = this.elementRef.nativeElement; if (from == "top") { return el.scrollTop; } if (from == "bottom") { return el.scrollHeight - el.clientHeight - el.scrollTop; } const isRtl = this.dir && this.dir.value == "rtl"; if (from == "start") { from = isRtl ? RIGHT : LEFT; } else if (from == "end") { from = isRtl ? LEFT : RIGHT; } if (isRtl && getRtlScrollAxisType() == 2) { if (from == LEFT) { return el.scrollWidth - el.clientWidth - el.scrollLeft; } else { return el.scrollLeft; } } else if (isRtl && getRtlScrollAxisType() == 1) { if (from == LEFT) { return el.scrollLeft + el.scrollWidth - el.clientWidth; } else { return -el.scrollLeft; } } else { if (from == LEFT) { return el.scrollLeft; } else { return el.scrollWidth - el.clientWidth - el.scrollLeft; } } } }; _CdkScrollable.ɵfac = function CdkScrollable_Factory(t) { return new (t || _CdkScrollable)(ɵɵdirectiveInject(ElementRef), ɵɵdirectiveInject(ScrollDispatcher), ɵɵdirectiveInject(NgZone), ɵɵdirectiveInject(Directionality, 8)); }; _CdkScrollable.ɵdir = ɵɵdefineDirective({ type: _CdkScrollable, selectors: [["", "cdk-scrollable", ""], ["", "cdkScrollable", ""]], standalone: true }); var CdkScrollable = _CdkScrollable; (() => { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(CdkScrollable, [{ type: Directive, args: [{ selector: "[cdk-scrollable], [cdkScrollable]", standalone: true }] }], () => [{ type: ElementRef }, { type: ScrollDispatcher }, { type: NgZone }, { type: Directionality, decorators: [{ type: Optional }] }], null); })(); var DEFAULT_RESIZE_TIME = 20; var _ViewportRuler = class _ViewportRuler { constructor(_platform, ngZone, document2) { this._platform = _platform; this._change = new Subject(); this._changeListener = (event) => { this._change.next(event); }; this._document = document2; ngZone.runOutsideAngular(() => { if (_platform.isBrowser) { const window2 = this._getWindow(); window2.addEventListener("resize", this._changeListener); window2.addEventListener("orientationchange", this._changeListener); } this.change().subscribe(() => this._viewportSize = null); }); } ngOnDestroy() { if (this._platform.isBrowser) { const window2 = this._getWindow(); window2.removeEventListener("resize", this._changeListener); window2.removeEventListener("orientationchange", this._changeListener); } this._change.complete(); } /** Returns the viewport's width and height. */ getViewportSize() { if (!this._viewportSize) { this._updateViewportSize(); } const output = { width: this._viewportSize.width, height: this._viewportSize.height }; if (!this._platform.isBrowser) { this._viewportSize = null; } return output; } /** Gets a ClientRect for the viewport's bounds. */ getViewportRect() { const scrollPosition = this.getViewportScrollPosition(); const { width, height } = this.getViewportSize(); return { top: scrollPosition.top, left: scrollPosition.left, bottom: scrollPosition.top + height, right: scrollPosition.left + width, height, width }; } /** Gets the (top, left) scroll position of the viewport. */ getViewportScrollPosition() { if (!this._platform.isBrowser) { return { top: 0, left: 0 }; } const document2 = this._document; const window2 = this._getWindow(); const documentElement = document2.documentElement; const documentRect = documentElement.getBoundingClientRect(); const top = -documentRect.top || document2.body.scrollTop || window2.scrollY || documentElement.scrollTop || 0; const left = -documentRect.left || document2.body.scrollLeft || window2.scrollX || documentElement.scrollLeft || 0; return { top, left }; } /** * Returns a stream that emits whenever the size of the viewport changes. * This stream emits outside of the Angular zone. * @param throttleTime Time in milliseconds to throttle the stream. */ change(throttleTime = DEFAULT_RESIZE_TIME) { return throttleTime > 0 ? this._change.pipe(auditTime(throttleTime)) : this._change; } /** Use defaultView of injected document if available or fallback to global window reference */ _getWindow() { return this._document.defaultView || window; } /** Updates the cached viewport size. */ _updateViewportSize() { const window2 = this._getWindow(); this._viewportSize = this._platform.isBrowser ? { width: window2.innerWidth, height: window2.innerHeight } : { width: 0, height: 0 }; } }; _ViewportRuler.ɵfac = function ViewportRuler_Factory(t) { return new (t || _ViewportRuler)(ɵɵinject(Platform), ɵɵinject(NgZone), ɵɵinject(DOCUMENT, 8)); }; _ViewportRuler.ɵprov = ɵɵdefineInjectable({ token: _ViewportRuler, factory: _ViewportRuler.ɵfac, providedIn: "root" }); var ViewportRuler = _ViewportRuler; (() => { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(ViewportRuler, [{ type: Injectable, args: [{ providedIn: "root" }] }], () => [{ type: Platform }, { type: NgZone }, { type: void 0, decorators: [{ type: Optional }, { type: Inject, args: [DOCUMENT] }] }], null); })(); var VIRTUAL_SCROLLABLE = new InjectionToken("VIRTUAL_SCROLLABLE"); var _CdkVirtualScrollable = class _CdkVirtualScrollable extends CdkScrollable { constructor(elementRef, scrollDispatcher, ngZone, dir) { super(elementRef, scrollDispatcher, ngZone, dir); } /** * Measure the viewport size for the provided orientation. * * @param orientation The orientation to measure the size from. */ measureViewportSize(orientation) { const viewportEl = this.elementRef.nativeElement; return orientation === "horizontal" ? viewportEl.clientWidth : viewportEl.clientHeight; } }; _CdkVirtualScrollable.ɵfac = function CdkVirtualScrollable_Factory(t) { return new (t || _CdkVirtualScrollable)(ɵɵdirectiveInject(ElementRef), ɵɵdirectiveInject(ScrollDispatcher), ɵɵdirectiveInject(NgZone), ɵɵdirectiveInject(Directionality, 8)); }; _CdkVirtualScrollable.ɵdir = ɵɵdefineDirective({ type: _CdkVirtualScrollable, features: [ɵɵInheritDefinitionFeature] }); var CdkVirtualScrollable = _CdkVirtualScrollable; (() => { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(CdkVirtualScrollable, [{ type: Directive }], () => [{ type: ElementRef }, { type: ScrollDispatcher }, { type: NgZone }, { type: Directionality, decorators: [{ type: Optional }] }], null); })(); function rangesEqual(r1, r2) { return r1.start == r2.start && r1.end == r2.end; } var SCROLL_SCHEDULER = typeof requestAnimationFrame !== "undefined" ? animationFrameScheduler : asapScheduler; var _CdkVirtualScrollViewport = class _CdkVirtualScrollViewport extends CdkVirtualScrollable { /** The direction the viewport scrolls. */ get orientation() { return this._orientation; } set orientation(orientation) { if (this._orientation !== orientation) { this._orientation = orientation; this._calculateSpacerSize(); } } constructor(elementRef, _changeDetectorRef, ngZone, _scrollStrategy, dir, scrollDispatcher, viewportRuler, scrollable) { super(elementRef, scrollDispatcher, ngZone, dir); this.elementRef = elementRef; this._changeDetectorRef = _changeDetectorRef; this._scrollStrategy = _scrollStrategy; this.scrollable = scrollable; this._platform = inject(Platform); this._detachedSubject = new Subject(); this._renderedRangeSubject = new Subject(); this._orientation = "vertical"; this.appendOnly = false; this.scrolledIndexChange = new Observable((observer) => this._scrollStrategy.scrolledIndexChange.subscribe((index) => Promise.resolve().then(() => this.ngZone.run(() => observer.next(index))))); this.renderedRangeStream = this._renderedRangeSubject; this._totalContentSize = 0; this._totalContentWidth = ""; this._totalContentHeight = ""; this._renderedRange = { start: 0, end: 0 }; this._dataLength = 0; this._viewportSize = 0; this._renderedContentOffset = 0; this._renderedContentOffsetNeedsRewrite = false; this._isChangeDetectionPending = false; this._runAfterChangeDetection = []; this._viewportChanges = Subscription.EMPTY; if (!_scrollStrategy && (typeof ngDevMode === "undefined" || ngDevMode)) { throw Error('Error: cdk-virtual-scroll-viewport requires the "itemSize" property to be set.'); } this._viewportChanges = viewportRuler.change().subscribe(() => { this.checkViewportSize(); }); if (!this.scrollable) { this.elementRef.nativeElement.classList.add("cdk-virtual-scrollable"); this.scrollable = this; } } ngOnInit() { if (!this._platform.isBrowser) { return; } if (this.scrollable === this) { super.ngOnInit(); } this.ngZone.runOutsideAngular(() => Promise.resolve().then(() => { this._measureViewportSize(); this._scrollStrategy.attach(this); this.scrollable.elementScrolled().pipe( // Start off with a fake scroll event so we properly detect our initial position. startWith(null), // Collect multiple events into one until the next animation frame. This way if // there are multiple scroll events in the same frame we only need to recheck // our layout once. auditTime(0, SCROLL_SCHEDULER), // Usually `elementScrolled` is completed when the scrollable is destroyed, but // that may not be the case if a `CdkVirtualScrollableElement` is used so we have // to unsubscribe here just in case. takeUntil(this._destroyed) ).subscribe(() => this._scrollStrategy.onContentScrolled()); this._markChangeDetectionNeeded(); })); } ngOnDestroy() { this.detach(); this._scrollStrategy.detach(); this._renderedRangeSubject.complete(); this._detachedSubject.complete(); this._viewportChanges.unsubscribe(); super.ngOnDestroy(); } /** Attaches a `CdkVirtualScrollRepeater` to this viewport. */ attach(forOf) { if (this._forOf && (typeof ngDevMode === "undefined" || ngDevMode)) { throw Error("CdkVirtualScrollViewport is already attached."); } this.ngZone.runOutsideAngular(() => { this._forOf = forOf; this._forOf.dataStream.pipe(takeUntil(this._detachedSubject)).subscribe((data) => { const newLength = data.length; if (newLength !== this._dataLength) { this._dataLength = newLength; this._scrollStrategy.onDataLengthChanged(); } this._doChangeDetection(); }); }); } /** Detaches the current `CdkVirtualForOf`. */ detach() { this._forOf = null; this._detachedSubject.next(); } /** Gets the length of the data bound to this viewport (in number of items). */ getDataLength() { return this._dataLength; } /** Gets the size of the viewport (in pixels). */ getViewportSize() { return this._viewportSize; } // TODO(mmalerba): This is technically out of sync with what's really rendered until a render // cycle happens. I'm being careful to only call it after the render cycle is complete and before // setting it to something else, but its error prone and should probably be split into // `pendingRange` and `renderedRange`, the latter reflecting whats actually in the DOM. /** Get the current rendered range of items. */ getRenderedRange() { return this._renderedRange; } measureBoundingClientRectWithScrollOffset(from) { return this.getElementRef().nativeElement.getBoundingClientRect()[from]; } /** * Sets the total size of all content (in pixels), including content that is not currently * rendered. */ setTotalContentSize(size) { if (this._totalContentSize !== size) { this._totalContentSize = size; this._calculateSpacerSize(); this._markChangeDetectionNeeded(); } } /** Sets the currently rendered range of indices. */ setRenderedRange(range) { if (!rangesEqual(this._renderedRange, range)) { if (this.appendOnly) { range = { start: 0, end: Math.max(this._renderedRange.end, range.end) }; } this._renderedRangeSubject.next(this._renderedRange = range); this._markChangeDetectionNeeded(() => this._scrollStrategy.onContentRendered()); } } /** * Gets the offset from the start of the viewport to the start of the rendered data (in pixels). */ getOffsetToRenderedContentStart() { return this._renderedContentOffsetNeedsRewrite ? null : this._renderedContentOffset; } /** * Sets the offset from the start of the viewport to either the start or end of the rendered data * (in pixels). */ setRenderedContentOffset(offset, to = "to-start") { offset = this.appendOnly && to === "to-start" ? 0 : offset; const isRtl = this.dir && this.dir.value == "rtl"; const isHorizontal = this.orientation == "horizontal"; const axis = isHorizontal ? "X" : "Y"; const axisDirection = isHorizontal && isRtl ? -1 : 1; let transform = `translate${axis}(${Number(axisDirection * offset)}px)`; this._renderedContentOffset = offset; if (to === "to-end") { transform += ` translate${axis}(-100%)`; this._renderedContentOffsetNeedsRewrite = true; } if (this._renderedContentTransform != transform) { this._renderedContentTransform = transform; this._markChangeDetectionNeeded(() => { if (this._renderedContentOffsetNeedsRewrite) { this._renderedContentOffset -= this.measureRenderedContentSize(); this._renderedContentOffsetNeedsRewrite = false; this.setRenderedContentOffset(this._renderedContentOffset); } else { this._scrollStrategy.onRenderedOffsetChanged(); } }); } } /** * Scrolls to the given offset from the start of the viewport. Please note that this is not always * the same as setting `scrollTop` or `scrollLeft`. In a horizontal viewport with right-to-left * direction, this would be the equivalent of setting a fictional `scrollRight` property. * @param offset The offset to scroll to. * @param behavior The ScrollBehavior to use when scrolling. Default is behavior is `auto`. */ scrollToOffset(offset, behavior = "auto") { const options = { behavior }; if (this.orientation === "horizontal") { options.start = offset; } else { options.top = offset; } this.scrollable.scrollTo(options); } /** * Scrolls to the offset for the given index. * @param index The index of the element to scroll to. * @param behavior The ScrollBehavior to use when scrolling. Default is behavior is `auto`. */ scrollToIndex(index, behavior = "auto") { this._scrollStrategy.scrollToIndex(index, behavior); } /** * Gets the current scroll offset from the start of the scrollable (in pixels). * @param from The edge to measure the offset from. Defaults to 'top' in vertical mode and 'start' * in horizontal mode. */ measureScrollOffset(from) { let measureScrollOffset; if (this.scrollable == this) { measureScrollOffset = (_from) => super.measureScrollOffset(_from); } else { measureScrollOffset = (_from) => this.scrollable.measureScrollOffset(_from); } return Math.max(0, measureScrollOffset(from ?? (this.orientation === "horizontal" ? "start" : "top")) - this.measureViewportOffset()); } /** * Measures the offset of the viewport from the scrolling container * @param from The edge to measure from. */ measureViewportOffset(from) { let fromRect; const LEFT = "left"; const RIGHT = "right"; const isRtl = this.dir?.value == "rtl"; if (from == "start") { fromRect = isRtl ? RIGHT : LEFT; } else if (from == "end") { fromRect = isRtl ? LEFT : RIGHT; } else if (from) { fromRect = from; } else { fromRect = this.orientation === "horizontal" ? "left" : "top"; } const scrollerClientRect = this.scrollable.measureBoundingClientRectWithScrollOffset(fromRect); const viewportClientRect = this.elementRef.nativeElement.getBoundingClientRect()[fromRect]; return viewportClientRect - scrollerClientRect; } /** Measure the combined size of all of the rendered items. */ measureRenderedContentSize() { const contentEl = this._contentWrapper.nativeElement; return this.orientation === "horizontal" ? contentEl.offsetWidth : contentEl.offsetHeight; } /** * Measure the total combined size of the given range. Throws if the range includes items that are * not rendered. */ measureRangeSize(range) { if (!this._forOf) { return 0; } return this._forOf.measureRangeSize(range, this.orientation); } /** Update the viewport dimensions and re-render. */ checkViewportSize() { this._measureViewportSize(); this._scrollStrategy.onDataLengthChanged(); } /** Measure the viewport size. */ _measureViewportSize() { this._viewportSize = this.scrollable.measureViewportSize(this.orientation); } /** Queue up change detection to run. */ _markChangeDetectionNeeded(runAfter) { if (runAfter) { this._runAfterChangeDetection.push(runAfter); } if (!this._isChangeDetectionPending) { this._isChangeDetectionPending = true; this.ngZone.runOutsideAngular(() => Promise.resolve().then(() => { this._doChangeDetection(); })); } } /** Run change detection. */ _doChangeDetection() { this._isChangeDetectionPending = false; this._contentWrapper.nativeElement.style.transform = this._renderedContentTransform; this.ngZone.run(() => this._changeDetectorRef.markForCheck()); const runAfterChangeDetection = this._runAfterChangeDetection; this._runAfterChangeDetection = []; for (const fn of runAfterChangeDetection) { fn(); } } /** Calculates the `style.width` and `style.height` for the spacer element. */ _calculateSpacerSize() { this._totalContentHeight = this.orientation === "horizontal" ? "" : `${this._totalContentSize}px`; this._totalContentWidth = this.orientation === "horizontal" ? `${this._totalContentSize}px` : ""; } }; _CdkVirtualScrollViewport.ɵfac = function CdkVirtualScrollViewport_Factory(t) { return new (t || _CdkVirtualScrollViewport)(ɵɵdirectiveInject(ElementRef), ɵɵdirectiveInject(ChangeDetectorRef), ɵɵdirectiveInject(NgZone), ɵɵdirectiveInject(VIRTUAL_SCROLL_STRATEGY, 8), ɵɵdirectiveInject(Directionality, 8), ɵɵdirectiveInject(ScrollDispatcher), ɵɵdirectiveInject(ViewportRuler), ɵɵdirectiveInject(VIRTUAL_SCROLLABLE, 8)); }; _CdkVirtualScrollViewport.ɵcmp = ɵɵdefineComponent({ type: _CdkVirtualScrollViewport, selectors: [["cdk-virtual-scroll-viewport"]], viewQuery: function CdkVirtualScrollViewport_Query(rf, ctx) { if (rf & 1) { ɵɵviewQuery(_c0, 7); } if (rf & 2) { let _t; ɵɵqueryRefresh(_t = ɵɵloadQuery()) && (ctx._contentWrapper = _t.first); } }, hostAttrs: [1, "cdk-virtual-scroll-viewport"], hostVars: 4, hostBindings: function CdkVirtualScrollViewport_HostBindings(rf, ctx) { if (rf & 2) { ɵɵclassProp("cdk-virtual-scroll-orientation-horizontal", ctx.orientation === "horizontal")("cdk-virtual-scroll-orientation-vertical", ctx.orientation !== "horizontal"); } }, inputs: { orientation: "orientation", appendOnly: ["appendOnly", "appendOnly", booleanAttribute] }, outputs: { scrolledIndexChange: "scrolledIndexChange" }, standalone: true, features: [ɵɵProvidersFeature([{ provide: CdkScrollable, useFactory: (virtualScrollable, viewport) => virtualScrollable || viewport, deps: [[new Optional(), new Inject(VIRTUAL_SCROLLABLE)], _CdkVirtualScrollViewport] }]), ɵɵInputTransformsFeature, ɵɵInheritDefinitionFeature, ɵɵStandaloneFeature], ngContentSelectors: _c1, decls: 4, vars: 4, consts: [[1, "cdk-virtual-scroll-content-wrapper"], ["contentWrapper", ""], [1, "cdk-virtual-scroll-spacer"]], template: function CdkVirtualScrollViewport_Template(rf, ctx) { if (rf & 1) { ɵɵprojectionDef(); ɵɵelementStart(0, "div", 0, 1); ɵɵprojection(2); ɵɵelementEnd(); ɵɵelement(3, "div", 2); } if (rf & 2) { ɵɵadvance(3); ɵɵstyleProp("width", ctx._totalContentWidth)("height", ctx._totalContentHeight); } }, styles: ["cdk-virtual-scroll-viewport{display:block;position:relative;transform:translateZ(0)}.cdk-virtual-scrollable{overflow:auto;will-change:scroll-position;contain:strict;-webkit-overflow-scrolling:touch}.cdk-virtual-scroll-content-wrapper{position:absolute;top:0;left:0;contain:content}[dir=rtl] .cdk-virtual-scroll-content-wrapper{right:0;left:auto}.cdk-virtual-scroll-orientation-horizontal .cdk-virtual-scroll-content-wrapper{min-height:100%}.cdk-virtual-scroll-orientation-horizontal .cdk-virtual-scroll-content-wrapper>dl:not([cdkVirtualFor]),.cdk-virtual-scroll-orientation-horizontal .cdk-virtual-scroll-content-wrapper>ol:not([cdkVirtualFor]),.cdk-virtual-scroll-orientation-horizontal .cdk-virtual-scroll-content-wrapper>table:not([cdkVirtualFor]),.cdk-virtual-scroll-orientation-horizontal .cdk-virtual-scroll-content-wrapper>ul:not([cdkVirtualFor]){padding-left:0;padding-right:0;margin-left:0;margin-right:0;border-left-width:0;border-right-width:0;outline:none}.cdk-virtual-scroll-orientation-vertical .cdk-virtual-scroll-content-wrapper{min-width:100%}.cdk-virtual-scroll-orientation-vertical .cdk-virtual-scroll-content-wrapper>dl:not([cdkVirtualFor]),.cdk-virtual-scroll-orientation-vertical .cdk-virtual-scroll-content-wrapper>ol:not([cdkVirtualFor]),.cdk-virtual-scroll-orientation-vertical .cdk-virtual-scroll-content-wrapper>table:not([cdkVirtualFor]),.cdk-virtual-scroll-orientation-vertical .cdk-virtual-scroll-content-wrapper>ul:not([cdkVirtualFor]){padding-top:0;padding-bottom:0;margin-top:0;margin-bottom:0;border-top-width:0;border-bottom-width:0;outline:none}.cdk-virtual-scroll-spacer{height:1px;transform-origin:0 0;flex:0 0 auto}[dir=rtl] .cdk-virtual-scroll-spacer{transform-origin:100% 0}"], encapsulation: 2, changeDetection: 0 }); var CdkVirtualScrollViewport = _CdkVirtualScrollViewport; (() => { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(CdkVirtualScrollViewport, [{ type: Component, args: [{ selector: "cdk-virtual-scroll-viewport", host: { "class": "cdk-virtual-scroll-viewport", "[class.cdk-virtual-scroll-orientation-horizontal]": 'orientation === "horizontal"', "[class.cdk-virtual-scroll-orientation-vertical]": 'orientation !== "horizontal"' }, encapsulation: ViewEncapsulation$1.None, changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, providers: [{ provide: CdkScrollable, useFactory: (virtualScrollable, viewport) => virtualScrollable || viewport, deps: [[new Optional(), new Inject(VIRTUAL_SCROLLABLE)], CdkVirtualScrollViewport] }], template: '\n
\n \n
\n\n
\n', styles: ["cdk-virtual-scroll-viewport{display:block;position:relative;transform:translateZ(0)}.cdk-virtual-scrollable{overflow:auto;will-change:scroll-position;contain:strict;-webkit-overflow-scrolling:touch}.cdk-virtual-scroll-content-wrapper{position:absolute;top:0;left:0;contain:content}[dir=rtl] .cdk-virtual-scroll-content-wrapper{right:0;left:auto}.cdk-virtual-scroll-orientation-horizontal .cdk-virtual-scroll-content-wrapper{min-height:100%}.cdk-virtual-scroll-orientation-horizontal .cdk-virtual-scroll-content-wrapper>dl:not([cdkVirtualFor]),.cdk-virtual-scroll-orientation-horizontal .cdk-virtual-scroll-content-wrapper>ol:not([cdkVirtualFor]),.cdk-virtual-scroll-orientation-horizontal .cdk-virtual-scroll-content-wrapper>table:not([cdkVirtualFor]),.cdk-virtual-scroll-orientation-horizontal .cdk-virtual-scroll-content-wrapper>ul:not([cdkVirtualFor]){padding-left:0;padding-right:0;margin-left:0;margin-right:0;border-left-width:0;border-right-width:0;outline:none}.cdk-virtual-scroll-orientation-vertical .cdk-virtual-scroll-content-wrapper{min-width:100%}.cdk-virtual-scroll-orientation-vertical .cdk-virtual-scroll-content-wrapper>dl:not([cdkVirtualFor]),.cdk-virtual-scroll-orientation-vertical .cdk-virtual-scroll-content-wrapper>ol:not([cdkVirtualFor]),.cdk-virtual-scroll-orientation-vertical .cdk-virtual-scroll-content-wrapper>table:not([cdkVirtualFor]),.cdk-virtual-scroll-orientation-vertical .cdk-virtual-scroll-content-wrapper>ul:not([cdkVirtualFor]){padding-top:0;padding-bottom:0;margin-top:0;margin-bottom:0;border-top-width:0;border-bottom-width:0;outline:none}.cdk-virtual-scroll-spacer{height:1px;transform-origin:0 0;flex:0 0 auto}[dir=rtl] .cdk-virtual-scroll-spacer{transform-origin:100% 0}"] }] }], () => [{ type: ElementRef }, { type: ChangeDetectorRef }, { type: NgZone }, { type: void 0, decorators: [{ type: Optional }, { type: Inject, args: [VIRTUAL_SCROLL_STRATEGY] }] }, { type: Directionality, decorators: [{ type: Optional }] }, { type: ScrollDispatcher }, { type: ViewportRuler }, { type: CdkVirtualScrollable, decorators: [{ type: Optional }, { type: Inject, args: [VIRTUAL_SCROLLABLE] }] }], { orientation: [{ type: Input }], appendOnly: [{ type: Input, args: [{ transform: booleanAttribute }] }], scrolledIndexChange: [{ type: Output }], _contentWrapper: [{ type: ViewChild, args: ["contentWrapper", { static: true }] }] }); })(); function getOffset(orientation, direction, node) { const el = node; if (!el.getBoundingClientRect) { return 0; } const rect = el.getBoundingClientRect(); if (orientation === "horizontal") { return direction === "start" ? rect.left : rect.right; } return direction === "start" ? rect.top : rect.bottom; } var _CdkVirtualForOf = class _CdkVirtualForOf { /** The DataSource to display. */ get cdkVirtualForOf() { return this._cdkVirtualForOf; } set cdkVirtualForOf(value) { this._cdkVirtualForOf = value; if (isDataSource(value)) { this._dataSourceChanges.next(value); } else { this._dataSourceChanges.next(new ArrayDataSource(isObservable(value) ? value : Array.from(value || []))); } } /** * The `TrackByFunction` to use for tracking changes. The `TrackByFunction` takes the index and * the item and produces a value to be used as the item's identity when tracking changes. */ get cdkVirtualForTrackBy() { return this._cdkVirtualForTrackBy; } set cdkVirtualForTrackBy(fn) { this._needsUpdate = true; this._cdkVirtualForTrackBy = fn ? (index, item) => fn(index + (this._renderedRange ? this._renderedRange.start : 0), item) : void 0; } /** The template used to stamp out new elements. */ set cdkVirtualForTemplate(value) { if (value) { this._needsUpdate = true; this._template = value; } } /** * The size of the cache used to store templates that are not being used for re-use later. * Setting the cache size to `0` will disable caching. Defaults to 20 templates. */ get cdkVirtualForTemplateCacheSize() { return this._viewRepeater.viewCacheSize; } set cdkVirtualForTemplateCacheSize(size) { this._viewRepeater.viewCacheSize = coerceNumberProperty(size); } constructor(_viewContainerRef, _template, _differs, _viewRepeater, _viewport, ngZone) { this._viewContainerRef = _viewContainerRef; this._template = _template; this._differs = _differs; this._viewRepeater = _viewRepeater; this._viewport = _viewport; this.viewChange = new Subject(); this._dataSourceChanges = new Subject(); this.dataStream = this._dataSourceChanges.pipe( // Start off with null `DataSource`. startWith(null), // Bundle up the previous and current data sources so we can work with both. pairwise(), // Use `_changeDataSource` to disconnect from the previous data source and connect to the // new one, passing back a stream of data changes which we run through `switchMap` to give // us a data stream that emits the latest data from whatever the current `DataSource` is. switchMap(([prev, cur]) => this._changeDataSource(prev, cur)), // Replay the last emitted data when someone subscribes. shareReplay(1) ); this._differ = null; this._needsUpdate = false; this._destroyed = new Subject(); this.dataStream.subscribe((data) => { this._data = data; this._onRenderedDataChange(); }); this._viewport.renderedRangeStream.pipe(takeUntil(this._destroyed)).subscribe((range) => { this._renderedRange = range; if (this.viewChange.observers.length) { ngZone.run(() => this.viewChange.next(this._renderedRange)); } this._onRenderedDataChange(); }); this._viewport.attach(this); } /** * Measures the combined size (width for horizontal orientation, height for vertical) of all items * in the specified range. Throws an error if the range includes items that are not currently * rendered. */ measureRangeSize(range, orientation) { if (range.start >= range.end) { return 0; } if ((range.start < this._renderedRange.start || range.end > this._renderedRange.end) && (typeof ngDevMode === "undefined" || ngDevMode)) { throw Error(`Error: attempted to measure an item that isn't rendered.`); } const renderedStartIndex = range.start - this._renderedRange.start; const rangeLen = range.end - range.start; let firstNode; let lastNode; for (let i = 0; i < rangeLen; i++) { const view = this._viewContainerRef.get(i + renderedStartIndex); if (view && view.rootNodes.length) { firstNode = lastNode = view.rootNodes[0]; break; } } for (let i = rangeLen - 1; i > -1; i--) { const view = this._viewContainerRef.get(i + renderedStartIndex); if (view && view.rootNodes.length) { lastNode = view.rootNodes[view.rootNodes.length - 1]; break; } } return firstNode && lastNode ? getOffset(orientation, "end", lastNode) - getOffset(orientation, "start", firstNode) : 0; } ngDoCheck() { if (this._differ && this._needsUpdate) { const changes = this._differ.diff(this._renderedItems); if (!changes) { this._updateContext(); } else { this._applyChanges(changes); } this._needsUpdate = false; } } ngOnDestroy() { this._viewport.detach(); this._dataSourceChanges.next(void 0); this._dataSourceChanges.complete(); this.viewChange.complete(); this._destroyed.next(); this._destroyed.complete(); this._viewRepeater.detach(); } /** React to scroll state changes in the viewport. */ _onRenderedDataChange() { if (!this._renderedRange) { return; } this._renderedItems = this._data.slice(this._renderedRange.start, this._renderedRange.end); if (!this._differ) { this._differ = this._differs.find(this._renderedItems).create((index, item) => { return this.cdkVirtualForTrackBy ? this.cdkVirtualForTrackBy(index, item) : item; }); } this._needsUpdate = true; } /** Swap out one `DataSource` for another. */ _changeDataSource(oldDs, newDs) { if (oldDs) { oldDs.disconnect(this); } this._needsUpdate = true; return newDs ? newDs.connect(this) : of(); } /** Update the `CdkVirtualForOfContext` for all views. */ _updateContext() { const count = this._data.length; let i = this._viewContainerRef.length; while (i--) { const view = this._viewContainerRef.get(i); view.context.index = this._renderedRange.start + i; view.context.count = count; this._updateComputedContextProperties(view.context); view.detectChanges(); } } /** Apply changes to the DOM. */ _applyChanges(changes) { this._viewRepeater.applyChanges(changes, this._viewContainerRef, (record, _adjustedPreviousIndex, currentIndex) => this._getEmbeddedViewArgs(record, currentIndex), (record) => record.item); changes.forEachIdentityChange((record) => { const view = this._viewContainerRef.get(record.currentIndex); view.context.$implicit = record.item; }); const count = this._data.length; let i = this._viewContainerRef.length; while (i--) { const view = this._viewContainerRef.get(i); view.context.index = this._renderedRange.start + i; view.context.count = count; this._updateComputedContextProperties(view.context); } } /** Update the computed properties on the `CdkVirtualForOfContext`. */ _updateComputedContextProperties(context) { context.first = context.index === 0; context.last = context.index === context.count - 1; context.even = context.index % 2 === 0; context.odd = !context.even; } _getEmbeddedViewArgs(record, index) { return { templateRef: this._template, context: { $implicit: record.item, // It's guaranteed that the iterable is not "undefined" or "null" because we only // generate views for elements if the "cdkVirtualForOf" iterable has elements. cdkVirtualForOf: this._cdkVirtualForOf, index: -1, count: -1, first: false, last: false, odd: false, even: false }, index }; } }; _CdkVirtualForOf.ɵfac = function CdkVirtualForOf_Factory(t) { return new (t || _CdkVirtualForOf)(ɵɵdirectiveInject(ViewContainerRef), ɵɵdirectiveInject(TemplateRef), ɵɵdirectiveInject(IterableDiffers), ɵɵdirectiveInject(_VIEW_REPEATER_STRATEGY), ɵɵdirectiveInject(CdkVirtualScrollViewport, 4), ɵɵdirectiveInject(NgZone)); }; _CdkVirtualForOf.ɵdir = ɵɵdefineDirective({ type: _CdkVirtualForOf, selectors: [["", "cdkVirtualFor", "", "cdkVirtualForOf", ""]], inputs: { cdkVirtualForOf: "cdkVirtualForOf", cdkVirtualForTrackBy: "cdkVirtualForTrackBy", cdkVirtualForTemplate: "cdkVirtualForTemplate", cdkVirtualForTemplateCacheSize: "cdkVirtualForTemplateCacheSize" }, standalone: true, features: [ɵɵProvidersFeature([{ provide: _VIEW_REPEATER_STRATEGY, useClass: _RecycleViewRepeaterStrategy }])] }); var CdkVirtualForOf = _CdkVirtualForOf; (() => { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(CdkVirtualForOf, [{ type: Directive, args: [{ selector: "[cdkVirtualFor][cdkVirtualForOf]", providers: [{ provide: _VIEW_REPEATER_STRATEGY, useClass: _RecycleViewRepeaterStrategy }], standalone: true }] }], () => [{ type: ViewContainerRef }, { type: TemplateRef }, { type: IterableDiffers }, { type: _RecycleViewRepeaterStrategy, decorators: [{ type: Inject, args: [_VIEW_REPEATER_STRATEGY] }] }, { type: CdkVirtualScrollViewport, decorators: [{ type: SkipSelf }] }, { type: NgZone }], { cdkVirtualForOf: [{ type: Input }], cdkVirtualForTrackBy: [{ type: Input }], cdkVirtualForTemplate: [{ type: Input }], cdkVirtualForTemplateCacheSize: [{ type: Input }] }); })(); var _CdkVirtualScrollableElement = class _CdkVirtualScrollableElement extends CdkVirtualScrollable { constructor(elementRef, scrollDispatcher, ngZone, dir) { super(elementRef, scrollDispatcher, ngZone, dir); } measureBoundingClientRectWithScrollOffset(from) { return this.getElementRef().nativeElement.getBoundingClientRect()[from] - this.measureScrollOffset(from); } }; _CdkVirtualScrollableElement.ɵfac = function CdkVirtualScrollableElement_Factory(t) { return new (t || _CdkVirtualScrollableElement)(ɵɵdirectiveInject(ElementRef), ɵɵdirectiveInject(ScrollDispatcher), ɵɵdirectiveInject(NgZone), ɵɵdirectiveInject(Directionality, 8)); }; _CdkVirtualScrollableElement.ɵdir = ɵɵdefineDirective({ type: _CdkVirtualScrollableElement, selectors: [["", "cdkVirtualScrollingElement", ""]], hostAttrs: [1, "cdk-virtual-scrollable"], standalone: true, features: [ɵɵProvidersFeature([{ provide: VIRTUAL_SCROLLABLE, useExisting: _CdkVirtualScrollableElement }]), ɵɵInheritDefinitionFeature] }); var CdkVirtualScrollableElement = _CdkVirtualScrollableElement; (() => { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(CdkVirtualScrollableElement, [{ type: Directive, args: [{ selector: "[cdkVirtualScrollingElement]", providers: [{ provide: VIRTUAL_SCROLLABLE, useExisting: CdkVirtualScrollableElement }], standalone: true, host: { "class": "cdk-virtual-scrollable" } }] }], () => [{ type: ElementRef }, { type: ScrollDispatcher }, { type: NgZone }, { type: Directionality, decorators: [{ type: Optional }] }], null); })(); var _CdkVirtualScrollableWindow = class _CdkVirtualScrollableWindow extends CdkVirtualScrollable { constructor(scrollDispatcher, ngZone, dir) { super(new ElementRef(document.documentElement), scrollDispatcher, ngZone, dir); this._elementScrolled = new Observable((observer) => this.ngZone.runOutsideAngular(() => fromEvent(document, "scroll").pipe(takeUntil(this._destroyed)).subscribe(observer))); } measureBoundingClientRectWithScrollOffset(from) { return this.getElementRef().nativeElement.getBoundingClientRect()[from]; } }; _CdkVirtualScrollableWindow.ɵfac = function CdkVirtualScrollableWindow_Factory(t) { return new (t || _CdkVirtualScrollableWindow)(ɵɵdirectiveInject(ScrollDispatcher), ɵɵdirectiveInject(NgZone), ɵɵdirectiveInject(Directionality, 8)); }; _CdkVirtualScrollableWindow.ɵdir = ɵɵdefineDirective({ type: _CdkVirtualScrollableWindow, selectors: [["cdk-virtual-scroll-viewport", "scrollWindow", ""]], standalone: true, features: [ɵɵProvidersFeature([{ provide: VIRTUAL_SCROLLABLE, useExisting: _CdkVirtualScrollableWindow }]), ɵɵInheritDefinitionFeature] }); var CdkVirtualScrollableWindow = _CdkVirtualScrollableWindow; (() => { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(CdkVirtualScrollableWindow, [{ type: Directive, args: [{ selector: "cdk-virtual-scroll-viewport[scrollWindow]", providers: [{ provide: VIRTUAL_SCROLLABLE, useExisting: CdkVirtualScrollableWindow }], standalone: true }] }], () => [{ type: ScrollDispatcher }, { type: NgZone }, { type: Directionality, decorators: [{ type: Optional }] }], null); })(); var _CdkScrollableModule = class _CdkScrollableModule { }; _CdkScrollableModule.ɵfac = function CdkScrollableModule_Factory(t) { return new (t || _CdkScrollableModule)(); }; _CdkScrollableModule.ɵmod = ɵɵdefineNgModule({ type: _CdkScrollableModule, imports: [CdkScrollable], exports: [CdkScrollable] }); _CdkScrollableModule.ɵinj = ɵɵdefineInjector({}); var CdkScrollableModule = _CdkScrollableModule; (() => { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(CdkScrollableModule, [{ type: NgModule, args: [{ exports: [CdkScrollable], imports: [CdkScrollable] }] }], null, null); })(); var _ScrollingModule = class _ScrollingModule { }; _ScrollingModule.ɵfac = function ScrollingModule_Factory(t) { return new (t || _ScrollingModule)(); }; _ScrollingModule.ɵmod = ɵɵdefineNgModule({ type: _ScrollingModule, imports: [BidiModule, CdkScrollableModule, CdkVirtualScrollViewport, CdkFixedSizeVirtualScroll, CdkVirtualForOf, CdkVirtualScrollableWindow, CdkVirtualScrollableElement], exports: [BidiModule, CdkScrollableModule, CdkFixedSizeVirtualScroll, CdkVirtualForOf, CdkVirtualScrollViewport, CdkVirtualScrollableWindow, CdkVirtualScrollableElement] }); _ScrollingModule.ɵinj = ɵɵdefineInjector({ imports: [BidiModule, CdkScrollableModule, BidiModule, CdkScrollableModule] }); var ScrollingModule = _ScrollingModule; (() => { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(ScrollingModule, [{ type: NgModule, args: [{ imports: [BidiModule, CdkScrollableModule, CdkVirtualScrollViewport, CdkFixedSizeVirtualScroll, CdkVirtualForOf, CdkVirtualScrollableWindow, CdkVirtualScrollableElement], exports: [BidiModule, CdkScrollableModule, CdkFixedSizeVirtualScroll, CdkVirtualForOf, CdkVirtualScrollViewport, CdkVirtualScrollableWindow, CdkVirtualScrollableElement] }] }], null, null); })(); // node_modules/@angular/cdk/fesm2022/portal.mjs function throwNullPortalError() { throw Error("Must provide a portal to attach"); } function throwPortalAlreadyAttachedError() { throw Error("Host already has a portal attached"); } function throwPortalOutletAlreadyDisposedError() { throw Error("This PortalOutlet has already been disposed"); } function throwUnknownPortalTypeError() { throw Error("Attempting to attach an unknown Portal type. BasePortalOutlet accepts either a ComponentPortal or a TemplatePortal."); } function throwNullPortalOutletError() { throw Error("Attempting to attach a portal to a null PortalOutlet"); } function throwNoPortalAttachedError() { throw Error("Attempting to detach a portal that is not attached to a host"); } var Portal = class { /** Attach this portal to a host. */ attach(host) { if (typeof ngDevMode === "undefined" || ngDevMode) { if (host == null) { throwNullPortalOutletError(); } if (host.hasAttached()) { throwPortalAlreadyAttachedError(); } } this._attachedHost = host; return host.attach(this); } /** Detach this portal from its host */ detach() { let host = this._attachedHost; if (host != null) { this._attachedHost = null; host.detach(); } else if (typeof ngDevMode === "undefined" || ngDevMode) { throwNoPortalAttachedError(); } } /** Whether this portal is attached to a host. */ get isAttached() { return this._attachedHost != null; } /** * Sets the PortalOutlet reference without performing `attach()`. This is used directly by * the PortalOutlet when it is performing an `attach()` or `detach()`. */ setAttachedHost(host) { this._attachedHost = host; } }; var ComponentPortal = class extends Portal { constructor(component, viewContainerRef, injector, componentFactoryResolver, projectableNodes) { super(); this.component = component; this.viewContainerRef = viewContainerRef; this.injector = injector; this.componentFactoryResolver = componentFactoryResolver; this.projectableNodes = projectableNodes; } }; var TemplatePortal = class extends Portal { constructor(templateRef, viewContainerRef, context, injector) { super(); this.templateRef = templateRef; this.viewContainerRef = viewContainerRef; this.context = context; this.injector = injector; } get origin() { return this.templateRef.elementRef; } /** * Attach the portal to the provided `PortalOutlet`. * When a context is provided it will override the `context` property of the `TemplatePortal` * instance. */ attach(host, context = this.context) { this.context = context; return super.attach(host); } detach() { this.context = void 0; return super.detach(); } }; var DomPortal = class extends Portal { constructor(element) { super(); this.element = element instanceof ElementRef ? element.nativeElement : element; } }; var BasePortalOutlet = class { constructor() { this._isDisposed = false; this.attachDomPortal = null; } /** Whether this host has an attached portal. */ hasAttached() { return !!this._attachedPortal; } /** Attaches a portal. */ attach(portal) { if (typeof ngDevMode === "undefined" || ngDevMode) { if (!portal) { throwNullPortalError(); } if (this.hasAttached()) { throwPortalAlreadyAttachedError(); } if (this._isDisposed) { throwPortalOutletAlreadyDisposedError(); } } if (portal instanceof ComponentPortal) { this._attachedPortal = portal; return this.attachComponentPortal(portal); } else if (portal instanceof TemplatePortal) { this._attachedPortal = portal; return this.attachTemplatePortal(portal); } else if (this.attachDomPortal && portal instanceof DomPortal) { this._attachedPortal = portal; return this.attachDomPortal(portal); } if (typeof ngDevMode === "undefined" || ngDevMode) { throwUnknownPortalTypeError(); } } /** Detaches a previously attached portal. */ detach() { if (this._attachedPortal) { this._attachedPortal.setAttachedHost(null); this._attachedPortal = null; } this._invokeDisposeFn(); } /** Permanently dispose of this portal host. */ dispose() { if (this.hasAttached()) { this.detach(); } this._invokeDisposeFn(); this._isDisposed = true; } /** @docs-private */ setDisposeFn(fn) { this._disposeFn = fn; } _invokeDisposeFn() { if (this._disposeFn) { this._disposeFn(); this._disposeFn = null; } } }; var DomPortalOutlet = class extends BasePortalOutlet { /** * @param outletElement Element into which the content is projected. * @param _componentFactoryResolver Used to resolve the component factory. * Only required when attaching component portals. * @param _appRef Reference to the application. Only used in component portals when there * is no `ViewContainerRef` available. * @param _defaultInjector Injector to use as a fallback when the portal being attached doesn't * have one. Only used for component portals. * @param _document Reference to the document. Used when attaching a DOM portal. Will eventually * become a required parameter. */ constructor(outletElement, _componentFactoryResolver, _appRef, _defaultInjector, _document) { super(); this.outletElement = outletElement; this._componentFactoryResolver = _componentFactoryResolver; this._appRef = _appRef; this._defaultInjector = _defaultInjector; this.attachDomPortal = (portal) => { if (!this._document && (typeof ngDevMode === "undefined" || ngDevMode)) { throw Error("Cannot attach DOM portal without _document constructor parameter"); } const element = portal.element; if (!element.parentNode && (typeof ngDevMode === "undefined" || ngDevMode)) { throw Error("DOM portal content must be attached to a parent node."); } const anchorNode = this._document.createComment("dom-portal"); element.parentNode.insertBefore(anchorNode, element); this.outletElement.appendChild(element); this._attachedPortal = portal; super.setDisposeFn(() => { if (anchorNode.parentNode) { anchorNode.parentNode.replaceChild(element, anchorNode); } }); }; this._document = _document; } /** * Attach the given ComponentPortal to DOM element using the ComponentFactoryResolver. * @param portal Portal to be attached * @returns Reference to the created component. */ attachComponentPortal(portal) { const resolver = portal.componentFactoryResolver || this._componentFactoryResolver; if ((typeof ngDevMode === "undefined" || ngDevMode) && !resolver) { throw Error("Cannot attach component portal to outlet without a ComponentFactoryResolver."); } const componentFactory = resolver.resolveComponentFactory(portal.component); let componentRef; if (portal.viewContainerRef) { componentRef = portal.viewContainerRef.createComponent(componentFactory, portal.viewContainerRef.length, portal.injector || portal.viewContainerRef.injector, portal.projectableNodes || void 0); this.setDisposeFn(() => componentRef.destroy()); } else { if ((typeof ngDevMode === "undefined" || ngDevMode) && !this._appRef) { throw Error("Cannot attach component portal to outlet without an ApplicationRef."); } componentRef = componentFactory.create(portal.injector || this._defaultInjector || Injector.NULL); this._appRef.attachView(componentRef.hostView); this.setDisposeFn(() => { if (this._appRef.viewCount > 0) { this._appRef.detachView(componentRef.hostView); } componentRef.destroy(); }); } this.outletElement.appendChild(this._getComponentRootNode(componentRef)); this._attachedPortal = portal; return componentRef; } /** * Attaches a template portal to the DOM as an embedded view. * @param portal Portal to be attached. * @returns Reference to the created embedded view. */ attachTemplatePortal(portal) { let viewContainer = portal.viewContainerRef; let viewRef = viewContainer.createEmbeddedView(portal.templateRef, portal.context, { injector: portal.injector }); viewRef.rootNodes.forEach((rootNode) => this.outletElement.appendChild(rootNode)); viewRef.detectChanges(); this.setDisposeFn(() => { let index = viewContainer.indexOf(viewRef); if (index !== -1) { viewContainer.remove(index); } }); this._attachedPortal = portal; return viewRef; } /** * Clears out a portal from the DOM. */ dispose() { super.dispose(); this.outletElement.remove(); } /** Gets the root HTMLElement for an instantiated component. */ _getComponentRootNode(componentRef) { return componentRef.hostView.rootNodes[0]; } }; var _CdkPortal = class _CdkPortal extends TemplatePortal { constructor(templateRef, viewContainerRef) { super(templateRef, viewContainerRef); } }; _CdkPortal.ɵfac = function CdkPortal_Factory(t) { return new (t || _CdkPortal)(ɵɵdirectiveInject(TemplateRef), ɵɵdirectiveInject(ViewContainerRef)); }; _CdkPortal.ɵdir = ɵɵdefineDirective({ type: _CdkPortal, selectors: [["", "cdkPortal", ""]], exportAs: ["cdkPortal"], features: [ɵɵInheritDefinitionFeature] }); var CdkPortal = _CdkPortal; (() => { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(CdkPortal, [{ type: Directive, args: [{ selector: "[cdkPortal]", exportAs: "cdkPortal" }] }], () => [{ type: TemplateRef }, { type: ViewContainerRef }], null); })(); var _TemplatePortalDirective = class _TemplatePortalDirective extends CdkPortal { }; _TemplatePortalDirective.ɵfac = (() => { let ɵTemplatePortalDirective_BaseFactory; return function TemplatePortalDirective_Factory(t) { return (ɵTemplatePortalDirective_BaseFactory || (ɵTemplatePortalDirective_BaseFactory = ɵɵgetInheritedFactory(_TemplatePortalDirective)))(t || _TemplatePortalDirective); }; })(); _TemplatePortalDirective.ɵdir = ɵɵdefineDirective({ type: _TemplatePortalDirective, selectors: [["", "cdk-portal", ""], ["", "portal", ""]], exportAs: ["cdkPortal"], features: [ɵɵProvidersFeature([{ provide: CdkPortal, useExisting: _TemplatePortalDirective }]), ɵɵInheritDefinitionFeature] }); var TemplatePortalDirective = _TemplatePortalDirective; (() => { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(TemplatePortalDirective, [{ type: Directive, args: [{ selector: "[cdk-portal], [portal]", exportAs: "cdkPortal", providers: [{ provide: CdkPortal, useExisting: TemplatePortalDirective }] }] }], null, null); })(); var _CdkPortalOutlet = class _CdkPortalOutlet extends BasePortalOutlet { constructor(_componentFactoryResolver, _viewContainerRef, _document) { super(); this._componentFactoryResolver = _componentFactoryResolver; this._viewContainerRef = _viewContainerRef; this._isInitialized = false; this.attached = new EventEmitter(); this.attachDomPortal = (portal) => { if (!this._document && (typeof ngDevMode === "undefined" || ngDevMode)) { throw Error("Cannot attach DOM portal without _document constructor parameter"); } const element = portal.element; if (!element.parentNode && (typeof ngDevMode === "undefined" || ngDevMode)) { throw Error("DOM portal content must be attached to a parent node."); } const anchorNode = this._document.createComment("dom-portal"); portal.setAttachedHost(this); element.parentNode.insertBefore(anchorNode, element); this._getRootNode().appendChild(element); this._attachedPortal = portal; super.setDisposeFn(() => { if (anchorNode.parentNode) { anchorNode.parentNode.replaceChild(element, anchorNode); } }); }; this._document = _document; } /** Portal associated with the Portal outlet. */ get portal() { return this._attachedPortal; } set portal(portal) { if (this.hasAttached() && !portal && !this._isInitialized) { return; } if (this.hasAttached()) { super.detach(); } if (portal) { super.attach(portal); } this._attachedPortal = portal || null; } /** Component or view reference that is attached to the portal. */ get attachedRef() { return this._attachedRef; } ngOnInit() { this._isInitialized = true; } ngOnDestroy() { super.dispose(); this._attachedRef = this._attachedPortal = null; } /** * Attach the given ComponentPortal to this PortalOutlet using the ComponentFactoryResolver. * * @param portal Portal to be attached to the portal outlet. * @returns Reference to the created component. */ attachComponentPortal(portal) { portal.setAttachedHost(this); const viewContainerRef = portal.viewContainerRef != null ? portal.viewContainerRef : this._viewContainerRef; const resolver = portal.componentFactoryResolver || this._componentFactoryResolver; const componentFactory = resolver.resolveComponentFactory(portal.component); const ref = viewContainerRef.createComponent(componentFactory, viewContainerRef.length, portal.injector || viewContainerRef.injector, portal.projectableNodes || void 0); if (viewContainerRef !== this._viewContainerRef) { this._getRootNode().appendChild(ref.hostView.rootNodes[0]); } super.setDisposeFn(() => ref.destroy()); this._attachedPortal = portal; this._attachedRef = ref; this.attached.emit(ref); return ref; } /** * Attach the given TemplatePortal to this PortalHost as an embedded View. * @param portal Portal to be attached. * @returns Reference to the created embedded view. */ attachTemplatePortal(portal) { portal.setAttachedHost(this); const viewRef = this._viewContainerRef.createEmbeddedView(portal.templateRef, portal.context, { injector: portal.injector }); super.setDisposeFn(() => this._viewContainerRef.clear()); this._attachedPortal = portal; this._attachedRef = viewRef; this.attached.emit(viewRef); return viewRef; } /** Gets the root node of the portal outlet. */ _getRootNode() { const nativeElement = this._viewContainerRef.element.nativeElement; return nativeElement.nodeType === nativeElement.ELEMENT_NODE ? nativeElement : nativeElement.parentNode; } }; _CdkPortalOutlet.ɵfac = function CdkPortalOutlet_Factory(t) { return new (t || _CdkPortalOutlet)(ɵɵdirectiveInject(ComponentFactoryResolver$1), ɵɵdirectiveInject(ViewContainerRef), ɵɵdirectiveInject(DOCUMENT)); }; _CdkPortalOutlet.ɵdir = ɵɵdefineDirective({ type: _CdkPortalOutlet, selectors: [["", "cdkPortalOutlet", ""]], inputs: { portal: ["cdkPortalOutlet", "portal"] }, outputs: { attached: "attached" }, exportAs: ["cdkPortalOutlet"], features: [ɵɵInheritDefinitionFeature] }); var CdkPortalOutlet = _CdkPortalOutlet; (() => { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(CdkPortalOutlet, [{ type: Directive, args: [{ selector: "[cdkPortalOutlet]", exportAs: "cdkPortalOutlet", inputs: ["portal: cdkPortalOutlet"] }] }], () => [{ type: ComponentFactoryResolver$1 }, { type: ViewContainerRef }, { type: void 0, decorators: [{ type: Inject, args: [DOCUMENT] }] }], { attached: [{ type: Output }] }); })(); var _PortalHostDirective = class _PortalHostDirective extends CdkPortalOutlet { }; _PortalHostDirective.ɵfac = (() => { let ɵPortalHostDirective_BaseFactory; return function PortalHostDirective_Factory(t) { return (ɵPortalHostDirective_BaseFactory || (ɵPortalHostDirective_BaseFactory = ɵɵgetInheritedFactory(_PortalHostDirective)))(t || _PortalHostDirective); }; })(); _PortalHostDirective.ɵdir = ɵɵdefineDirective({ type: _PortalHostDirective, selectors: [["", "cdkPortalHost", ""], ["", "portalHost", ""]], inputs: { portal: ["cdkPortalHost", "portal"] }, exportAs: ["cdkPortalHost"], features: [ɵɵProvidersFeature([{ provide: CdkPortalOutlet, useExisting: _PortalHostDirective }]), ɵɵInheritDefinitionFeature] }); var PortalHostDirective = _PortalHostDirective; (() => { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(PortalHostDirective, [{ type: Directive, args: [{ selector: "[cdkPortalHost], [portalHost]", exportAs: "cdkPortalHost", inputs: ["portal: cdkPortalHost"], providers: [{ provide: CdkPortalOutlet, useExisting: PortalHostDirective }] }] }], null, null); })(); var _PortalModule = class _PortalModule { }; _PortalModule.ɵfac = function PortalModule_Factory(t) { return new (t || _PortalModule)(); }; _PortalModule.ɵmod = ɵɵdefineNgModule({ type: _PortalModule, declarations: [CdkPortal, CdkPortalOutlet, TemplatePortalDirective, PortalHostDirective], exports: [CdkPortal, CdkPortalOutlet, TemplatePortalDirective, PortalHostDirective] }); _PortalModule.ɵinj = ɵɵdefineInjector({}); var PortalModule = _PortalModule; (() => { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(PortalModule, [{ type: NgModule, args: [{ exports: [CdkPortal, CdkPortalOutlet, TemplatePortalDirective, PortalHostDirective], declarations: [CdkPortal, CdkPortalOutlet, TemplatePortalDirective, PortalHostDirective] }] }], null, null); })(); // node_modules/@angular/cdk/fesm2022/overlay.mjs var scrollBehaviorSupported = supportsScrollBehavior(); var BlockScrollStrategy = class { constructor(_viewportRuler, document2) { this._viewportRuler = _viewportRuler; this._previousHTMLStyles = { top: "", left: "" }; this._isEnabled = false; this._document = document2; } /** Attaches this scroll strategy to an overlay. */ attach() { } /** Blocks page-level scroll while the attached overlay is open. */ 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; } } /** Unblocks page-level scroll while the attached overlay is open. */ 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 body = this._document.body; const viewport = this._viewportRuler.getViewportSize(); return body.scrollHeight > viewport.height || body.scrollWidth > viewport.width; } }; function getMatScrollStrategyAlreadyAttachedError() { return Error(`Scroll strategy has already been attached.`); } var CloseScrollStrategy = class { constructor(_scrollDispatcher, _ngZone, _viewportRuler, _config) { this._scrollDispatcher = _scrollDispatcher; this._ngZone = _ngZone; this._viewportRuler = _viewportRuler; this._config = _config; this._scrollSubscription = null; this._detach = () => { this.disable(); if (this._overlayRef.hasAttached()) { this._ngZone.run(() => this._overlayRef.detach()); } }; } /** Attaches this scroll strategy to an overlay. */ attach(overlayRef) { if (this._overlayRef && (typeof ngDevMode === "undefined" || ngDevMode)) { throw getMatScrollStrategyAlreadyAttachedError(); } this._overlayRef = overlayRef; } /** Enables the closing of the attached overlay on scroll. */ 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); } } /** Disables the closing the attached overlay on scroll. */ disable() { if (this._scrollSubscription) { this._scrollSubscription.unsubscribe(); this._scrollSubscription = null; } } detach() { this.disable(); this._overlayRef = null; } }; var NoopScrollStrategy = class { /** Does nothing, as this scroll strategy is a no-op. */ enable() { } /** Does nothing, as this scroll strategy is a no-op. */ disable() { } /** Does nothing, as this scroll strategy is a no-op. */ 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; }); } var RepositionScrollStrategy = class { 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. */ attach(overlayRef) { if (this._overlayRef && (typeof ngDevMode === "undefined" || ngDevMode)) { throw getMatScrollStrategyAlreadyAttachedError(); } this._overlayRef = overlayRef; } /** Enables repositioning of the attached overlay on scroll. */ 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()); } } }); } } /** Disables repositioning of the attached overlay on scroll. */ disable() { if (this._scrollSubscription) { this._scrollSubscription.unsubscribe(); this._scrollSubscription = null; } } detach() { this.disable(); this._overlayRef = null; } }; var _ScrollStrategyOptions = class _ScrollStrategyOptions { constructor(_scrollDispatcher, _viewportRuler, _ngZone, document2) { this._scrollDispatcher = _scrollDispatcher; this._viewportRuler = _viewportRuler; this._ngZone = _ngZone; this.noop = () => new NoopScrollStrategy(); this.close = (config) => new CloseScrollStrategy(this._scrollDispatcher, this._ngZone, this._viewportRuler, config); this.block = () => new BlockScrollStrategy(this._viewportRuler, this._document); this.reposition = (config) => new RepositionScrollStrategy(this._scrollDispatcher, this._viewportRuler, this._ngZone, config); this._document = document2; } }; _ScrollStrategyOptions.ɵfac = function ScrollStrategyOptions_Factory(t) { return new (t || _ScrollStrategyOptions)(ɵɵinject(ScrollDispatcher), ɵɵinject(ViewportRuler), ɵɵinject(NgZone), ɵɵinject(DOCUMENT)); }; _ScrollStrategyOptions.ɵprov = ɵɵdefineInjectable({ token: _ScrollStrategyOptions, factory: _ScrollStrategyOptions.ɵfac, providedIn: "root" }); var ScrollStrategyOptions = _ScrollStrategyOptions; (() => { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(ScrollStrategyOptions, [{ type: Injectable, args: [{ providedIn: "root" }] }], () => [{ type: ScrollDispatcher }, { type: ViewportRuler }, { type: NgZone }, { type: void 0, decorators: [{ type: Inject, args: [DOCUMENT] }] }], null); })(); var OverlayConfig = class { constructor(config) { this.scrollStrategy = new NoopScrollStrategy(); this.panelClass = ""; this.hasBackdrop = false; this.backdropClass = "cdk-overlay-dark-backdrop"; this.disposeOnNavigation = false; if (config) { const configKeys = Object.keys(config); for (const key of configKeys) { if (config[key] !== void 0) { this[key] = config[key]; } } } } }; var ConnectionPositionPair = class { 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; } }; var ConnectedOverlayPositionChange = class { 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".`); } } var _BaseOverlayDispatcher = class _BaseOverlayDispatcher { constructor(document2) { this._attachedOverlays = []; this._document = document2; } ngOnDestroy() { this.detach(); } /** Add a new overlay to the list of attached overlay refs. */ add(overlayRef) { this.remove(overlayRef); this._attachedOverlays.push(overlayRef); } /** Remove an overlay from the list of attached overlay refs. */ remove(overlayRef) { const index = this._attachedOverlays.indexOf(overlayRef); if (index > -1) { this._attachedOverlays.splice(index, 1); } if (this._attachedOverlays.length === 0) { this.detach(); } } }; _BaseOverlayDispatcher.ɵfac = function BaseOverlayDispatcher_Factory(t) { return new (t || _BaseOverlayDispatcher)(ɵɵinject(DOCUMENT)); }; _BaseOverlayDispatcher.ɵprov = ɵɵdefineInjectable({ token: _BaseOverlayDispatcher, factory: _BaseOverlayDispatcher.ɵfac, providedIn: "root" }); var BaseOverlayDispatcher = _BaseOverlayDispatcher; (() => { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(BaseOverlayDispatcher, [{ type: Injectable, args: [{ providedIn: "root" }] }], () => [{ type: void 0, decorators: [{ type: Inject, args: [DOCUMENT] }] }], null); })(); var _OverlayKeyboardDispatcher = class _OverlayKeyboardDispatcher extends BaseOverlayDispatcher { constructor(document2, _ngZone) { super(document2); this._ngZone = _ngZone; this._keydownListener = (event) => { const overlays = this._attachedOverlays; for (let i = overlays.length - 1; i > -1; i--) { if (overlays[i]._keydownEvents.observers.length > 0) { const keydownEvents = overlays[i]._keydownEvents; if (this._ngZone) { this._ngZone.run(() => keydownEvents.next(event)); } else { keydownEvents.next(event); } break; } } }; } /** Add a new overlay to the list of attached overlay refs. */ add(overlayRef) { super.add(overlayRef); if (!this._isAttached) { if (this._ngZone) { this._ngZone.runOutsideAngular(() => this._document.body.addEventListener("keydown", this._keydownListener)); } else { this._document.body.addEventListener("keydown", this._keydownListener); } this._isAttached = true; } } /** Detaches the global keyboard event listener. */ detach() { if (this._isAttached) { this._document.body.removeEventListener("keydown", this._keydownListener); this._isAttached = false; } } }; _OverlayKeyboardDispatcher.ɵfac = function OverlayKeyboardDispatcher_Factory(t) { return new (t || _OverlayKeyboardDispatcher)(ɵɵinject(DOCUMENT), ɵɵinject(NgZone, 8)); }; _OverlayKeyboardDispatcher.ɵprov = ɵɵdefineInjectable({ token: _OverlayKeyboardDispatcher, factory: _OverlayKeyboardDispatcher.ɵfac, providedIn: "root" }); var OverlayKeyboardDispatcher = _OverlayKeyboardDispatcher; (() => { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(OverlayKeyboardDispatcher, [{ type: Injectable, args: [{ providedIn: "root" }] }], () => [{ type: void 0, decorators: [{ type: Inject, args: [DOCUMENT] }] }, { type: NgZone, decorators: [{ type: Optional }] }], null); })(); var _OverlayOutsideClickDispatcher = class _OverlayOutsideClickDispatcher extends BaseOverlayDispatcher { constructor(document2, _platform, _ngZone) { super(document2); this._platform = _platform; this._ngZone = _ngZone; this._cursorStyleIsSet = false; this._pointerDownListener = (event) => { this._pointerDownEventTarget = _getEventTarget(event); }; this._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 (overlayRef.overlayElement.contains(target) || overlayRef.overlayElement.contains(origin)) { break; } const outsidePointerEvents = overlayRef._outsidePointerEvents; if (this._ngZone) { this._ngZone.run(() => outsidePointerEvents.next(event)); } else { outsidePointerEvents.next(event); } } }; } /** Add a new overlay to the list of attached overlay refs. */ add(overlayRef) { super.add(overlayRef); if (!this._isAttached) { const body = this._document.body; if (this._ngZone) { this._ngZone.runOutsideAngular(() => this._addEventListeners(body)); } else { this._addEventListeners(body); } if (this._platform.IOS && !this._cursorStyleIsSet) { this._cursorOriginalValue = body.style.cursor; body.style.cursor = "pointer"; this._cursorStyleIsSet = true; } this._isAttached = true; } } /** Detaches the global keyboard event listener. */ detach() { if (this._isAttached) { const body = this._document.body; body.removeEventListener("pointerdown", this._pointerDownListener, true); body.removeEventListener("click", this._clickListener, true); body.removeEventListener("auxclick", this._clickListener, true); body.removeEventListener("contextmenu", this._clickListener, true); if (this._platform.IOS && this._cursorStyleIsSet) { body.style.cursor = this._cursorOriginalValue; this._cursorStyleIsSet = false; } this._isAttached = false; } } _addEventListeners(body) { body.addEventListener("pointerdown", this._pointerDownListener, true); body.addEventListener("click", this._clickListener, true); body.addEventListener("auxclick", this._clickListener, true); body.addEventListener("contextmenu", this._clickListener, true); } }; _OverlayOutsideClickDispatcher.ɵfac = function OverlayOutsideClickDispatcher_Factory(t) { return new (t || _OverlayOutsideClickDispatcher)(ɵɵinject(DOCUMENT), ɵɵinject(Platform), ɵɵinject(NgZone, 8)); }; _OverlayOutsideClickDispatcher.ɵprov = ɵɵdefineInjectable({ token: _OverlayOutsideClickDispatcher, factory: _OverlayOutsideClickDispatcher.ɵfac, providedIn: "root" }); var OverlayOutsideClickDispatcher = _OverlayOutsideClickDispatcher; (() => { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(OverlayOutsideClickDispatcher, [{ type: Injectable, args: [{ providedIn: "root" }] }], () => [{ type: void 0, decorators: [{ type: Inject, args: [DOCUMENT] }] }, { type: Platform }, { type: NgZone, decorators: [{ type: Optional }] }], null); })(); var _OverlayContainer = class _OverlayContainer { constructor(document2, _platform) { this._platform = _platform; this._document = document2; } ngOnDestroy() { this._containerElement?.remove(); } /** * 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. * @returns 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. */ _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; } }; _OverlayContainer.ɵfac = function OverlayContainer_Factory(t) { return new (t || _OverlayContainer)(ɵɵinject(DOCUMENT), ɵɵinject(Platform)); }; _OverlayContainer.ɵprov = ɵɵdefineInjectable({ token: _OverlayContainer, factory: _OverlayContainer.ɵfac, providedIn: "root" }); var OverlayContainer = _OverlayContainer; (() => { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(OverlayContainer, [{ type: Injectable, args: [{ providedIn: "root" }] }], () => [{ type: void 0, decorators: [{ type: Inject, args: [DOCUMENT] }] }, { type: Platform }], null); })(); var OverlayRef = class { constructor(_portalOutlet, _host, _pane, _config, _ngZone, _keyboardDispatcher, _document, _location, _outsideClickDispatcher, _animationsDisabled = false) { 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._backdropElement = null; this._backdropClick = new Subject(); this._attachments = new Subject(); this._detachments = new Subject(); this._locationChanges = Subscription.EMPTY; this._backdropClickHandler = (event) => this._backdropClick.next(event); this._backdropTransitionendHandler = (event) => { this._disposeBackdrop(event.target); }; this._keydownEvents = new Subject(); this._outsidePointerEvents = new Subject(); if (_config.scrollStrategy) { this._scrollStrategy = _config.scrollStrategy; this._scrollStrategy.attach(this); } this._positionStrategy = _config.positionStrategy; } /** The overlay's HTML element */ get overlayElement() { return this._pane; } /** The overlay's backdrop HTML element. */ 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. */ 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. * @returns The portal attachment result. */ attach(portal) { if (!this._host.parentElement && this._previousHostParent) { this._previousHostParent.appendChild(this._host); } const attachResult = this._portalOutlet.attach(portal); if (this._positionStrategy) { this._positionStrategy.attach(this); } this._updateStackingOrder(); this._updateElementSize(); this._updateElementDirection(); if (this._scrollStrategy) { this._scrollStrategy.enable(); } this._ngZone.onStable.pipe(take(1)).subscribe(() => { if (this.hasAttached()) { this.updatePosition(); } }); 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._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; } /** * Detaches an overlay from a portal. * @returns The portal detachment result. */ 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._keyboardDispatcher.remove(this); this._detachContentWhenStable(); this._locationChanges.unsubscribe(); this._outsideClickDispatcher.remove(this); return detachmentResult; } /** Cleans up the overlay from the DOM. */ dispose() { const isAttached = this.hasAttached(); if (this._positionStrategy) { this._positionStrategy.dispose(); } this._disposeScrollStrategy(); this._disposeBackdrop(this._backdropElement); 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._previousHostParent = this._pane = this._host = null; if (isAttached) { this._detachments.next(); } this._detachments.complete(); } /** Whether the overlay has attached content. */ hasAttached() { return this._portalOutlet.hasAttached(); } /** Gets an observable that emits when the backdrop has been clicked. */ backdropClick() { return this._backdropClick; } /** Gets an observable that emits when the overlay has been attached. */ attachments() { return this._attachments; } /** Gets an observable that emits when the overlay has been detached. */ detachments() { return this._detachments; } /** Gets an observable of keydown events targeted to this overlay. */ keydownEvents() { return this._keydownEvents; } /** Gets an observable of pointer events targeted outside this overlay. */ outsidePointerEvents() { return this._outsidePointerEvents; } /** Gets the current overlay configuration, which is immutable. */ getConfig() { return this._config; } /** Updates the position of the overlay based on the position strategy. */ updatePosition() { if (this._positionStrategy) { this._positionStrategy.apply(); } } /** Switches to a new position strategy and updates the overlay position. */ updatePositionStrategy(strategy) { if (strategy === this._positionStrategy) { return; } if (this._positionStrategy) { this._positionStrategy.dispose(); } this._positionStrategy = strategy; if (this.hasAttached()) { strategy.attach(this); this.updatePosition(); } } /** Update the size properties of the overlay. */ updateSize(sizeConfig) { this._config = __spreadValues(__spreadValues({}, this._config), sizeConfig); this._updateElementSize(); } /** Sets the LTR/RTL direction for the overlay. */ setDirection(dir) { this._config = __spreadProps(__spreadValues({}, this._config), { direction: dir }); this._updateElementDirection(); } /** Add a CSS class or an array of classes to the overlay pane. */ addPanelClass(classes) { if (this._pane) { this._toggleClasses(this._pane, classes, true); } } /** Remove a CSS class or an array of classes from the overlay pane. */ removePanelClass(classes) { if (this._pane) { this._toggleClasses(this._pane, classes, false); } } /** * Returns the layout direction of the overlay panel. */ getDirection() { const direction = this._config.direction; if (!direction) { return "ltr"; } return typeof direction === "string" ? direction : direction.value; } /** Switches to a new scroll strategy. */ updateScrollStrategy(strategy) { if (strategy === this._scrollStrategy) { return; } this._disposeScrollStrategy(); this._scrollStrategy = strategy; if (this.hasAttached()) { strategy.attach(this); strategy.enable(); } } /** Updates the text direction of the overlay panel. */ _updateElementDirection() { this._host.setAttribute("dir", this.getDirection()); } /** Updates the size of the overlay element based on the overlay config. */ _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); } /** Toggles the pointer events for the overlay pane element. */ _togglePointerEvents(enablePointer) { this._pane.style.pointerEvents = enablePointer ? "" : "none"; } /** Attaches a backdrop for this overlay. */ _attachBackdrop() { const showingClass = "cdk-overlay-backdrop-showing"; this._backdropElement = this._document.createElement("div"); this._backdropElement.classList.add("cdk-overlay-backdrop"); if (this._animationsDisabled) { this._backdropElement.classList.add("cdk-overlay-backdrop-noop-animation"); } if (this._config.backdropClass) { this._toggleClasses(this._backdropElement, this._config.backdropClass, true); } this._host.parentElement.insertBefore(this._backdropElement, this._host); this._backdropElement.addEventListener("click", this._backdropClickHandler); if (!this._animationsDisabled && typeof requestAnimationFrame !== "undefined") { this._ngZone.runOutsideAngular(() => { requestAnimationFrame(() => { if (this._backdropElement) { this._backdropElement.classList.add(showingClass); } }); }); } else { this._backdropElement.classList.add(showingClass); } } /** * Updates the stacking order of the element, moving it to the top if necessary. * This is required in cases where one overlay was detached, while another one, * that should be behind it, was destroyed. The next time both of them are opened, * the stacking will be wrong, because the detached element's pane will still be * in its original DOM position. */ _updateStackingOrder() { if (this._host.nextSibling) { this._host.parentNode.appendChild(this._host); } } /** Detaches the backdrop (if any) associated with the overlay. */ detachBackdrop() { const backdropToDetach = this._backdropElement; if (!backdropToDetach) { return; } if (this._animationsDisabled) { this._disposeBackdrop(backdropToDetach); return; } backdropToDetach.classList.remove("cdk-overlay-backdrop-showing"); this._ngZone.runOutsideAngular(() => { backdropToDetach.addEventListener("transitionend", this._backdropTransitionendHandler); }); backdropToDetach.style.pointerEvents = "none"; this._backdropTimeout = this._ngZone.runOutsideAngular(() => setTimeout(() => { this._disposeBackdrop(backdropToDetach); }, 500)); } /** Toggles a single CSS class or an array of classes on an element. */ _toggleClasses(element, cssClasses, isAdd) { const classes = coerceArray(cssClasses || []).filter((c) => !!c); if (classes.length) { isAdd ? element.classList.add(...classes) : element.classList.remove(...classes); } } /** Detaches the overlay content next time the zone stabilizes. */ _detachContentWhenStable() { this._ngZone.runOutsideAngular(() => { const subscription = this._ngZone.onStable.pipe(takeUntil(merge(this._attachments, this._detachments))).subscribe(() => { 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(); } subscription.unsubscribe(); } }); }); } /** Disposes of a scroll strategy. */ _disposeScrollStrategy() { const scrollStrategy = this._scrollStrategy; if (scrollStrategy) { scrollStrategy.disable(); if (scrollStrategy.detach) { scrollStrategy.detach(); } } } /** Removes a backdrop element from the DOM. */ _disposeBackdrop(backdrop) { if (backdrop) { backdrop.removeEventListener("click", this._backdropClickHandler); backdrop.removeEventListener("transitionend", this._backdropTransitionendHandler); backdrop.remove(); if (this._backdropElement === backdrop) { this._backdropElement = null; } } if (this._backdropTimeout) { clearTimeout(this._backdropTimeout); this._backdropTimeout = void 0; } } }; var boundingBoxClass = "cdk-overlay-connected-position-bounding-box"; var cssUnitPattern = /([A-Za-z%]+)$/; var FlexibleConnectedPositionStrategy = class { /** Ordered list of preferred positions, from most to least desirable. */ get positions() { return this._preferredPositions; } constructor(connectedTo, _viewportRuler, _document, _platform, _overlayContainer) { this._viewportRuler = _viewportRuler; this._document = _document; this._platform = _platform; this._overlayContainer = _overlayContainer; this._lastBoundingBoxSize = { width: 0, height: 0 }; this._isPushed = false; this._canPush = true; this._growAfterOpen = false; this._hasFlexibleDimensions = true; this._positionLocked = false; this._viewportMargin = 0; this._scrollables = []; this._preferredPositions = []; this._positionChanges = new Subject(); this._resizeSubscription = Subscription.EMPTY; this._offsetX = 0; this._offsetY = 0; this._appliedPanelClasses = []; this.positionChanges = this._positionChanges; this.setOrigin(connectedTo); } /** Attaches this position strategy to an overlay. */ 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(); }); } /** * Updates the position of the overlay element, using whichever preferred position relative * to the origin best fits on-screen. * * The selection of a position goes as follows: * - If any positions fit completely within the viewport as-is, * choose the first position that does so. * - If flexible dimensions are enabled and at least one satisfies the given minimum width/height, * choose the position with the greatest available size modified by the positions' weight. * - If pushing is enabled, take the position that went off-screen the least and push it * on-screen. * - If none of the previous criteria were met, use the position that goes off-screen the least. * @docs-private */ 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(); } /** Cleanup after the element gets destroyed. */ 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; } /** * This re-aligns the overlay element with the trigger in its last calculated position, * even if a position higher in the "preferred positions" list would now fit. This * allows one to re-align the panel without changing the orientation of the panel. */ 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(); } } /** * Sets the list of Scrollable containers that host the origin element so that * on reposition we can evaluate if it or the overlay has been clipped or outside view. Every * Scrollable must be an ancestor element of the strategy's origin element. */ withScrollableContainers(scrollables) { this._scrollables = scrollables; return this; } /** * Adds new preferred positions. * @param positions List of positions options for this overlay. */ withPositions(positions) { this._preferredPositions = positions; if (positions.indexOf(this._lastPosition) === -1) { this._lastPosition = null; } this._validatePositions(); return this; } /** * Sets a minimum distance the overlay may be positioned to the edge of the viewport. * @param margin Required margin between the overlay and the viewport edge in pixels. */ withViewportMargin(margin) { this._viewportMargin = margin; return this; } /** Sets whether the overlay's width and height can be constrained to fit within the viewport. */ withFlexibleDimensions(flexibleDimensions = true) { this._hasFlexibleDimensions = flexibleDimensions; return this; } /** Sets whether the overlay can grow after the initial open via flexible width/height. */ withGrowAfterOpen(growAfterOpen = true) { this._growAfterOpen = growAfterOpen; return this; } /** Sets whether the overlay can be pushed on-screen if none of the provided positions fit. */ withPush(canPush = true) { this._canPush = canPush; return this; } /** * Sets whether the overlay's position should be locked in after it is positioned * initially. When an overlay is locked in, it won't attempt to reposition itself * when the position is re-applied (e.g. when the user scrolls away). * @param isLocked Whether the overlay should locked in. */ withLockedPosition(isLocked = true) { this._positionLocked = isLocked; return this; } /** * Sets the origin, relative to which to position the overlay. * Using an element origin is useful for building components that need to be positioned * relatively to a trigger (e.g. dropdown menus or tooltips), whereas using a point can be * used for cases like contextual menus which open relative to the user's pointer. * @param origin Reference to the new origin. */ setOrigin(origin) { this._origin = origin; return this; } /** * Sets the default offset for the overlay's connection point on the x-axis. * @param offset New offset in the X axis. */ withDefaultOffsetX(offset) { this._offsetX = offset; return this; } /** * Sets the default offset for the overlay's connection point on the y-axis. * @param offset New offset in the Y axis. */ withDefaultOffsetY(offset) { this._offsetY = offset; return this; } /** * Configures that the position strategy should set a `transform-origin` on some elements * inside the overlay, depending on the current position that is being applied. This is * useful for the cases where the origin of an animation can change depending on the * alignment of the overlay. * @param selector CSS selector that will be used to find the target * elements onto which to set the transform origin. */ withTransformOriginOn(selector) { this._transformOriginSelector = selector; return this; } /** * Gets the (x, y) coordinate of a connection point on the origin based on a relative position. */ _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 }; } /** * Gets the (x, y) coordinate of the top-left corner of the overlay given a given position and * origin point to which the overlay should be connected. */ _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 }; } /** Gets how well an overlay at the given point will fit within the viewport. */ _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 }; } /** * Whether the overlay can fit within the viewport when it may resize either its width or height. * @param fit How well the overlay fits in the viewport at some position. * @param point The (x, y) coordinates of the overlay at some position. * @param viewport The geometry of the viewport. */ _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; } /** * Gets the point at which the overlay can be "pushed" on-screen. If the overlay is larger than * the viewport, the top-left corner will be pushed on-screen (with overflow occurring on the * right and bottom). * * @param start Starting point from which the overlay is pushed. * @param rawOverlayRect Dimensions of the overlay. * @param scrollPosition Current viewport scroll position. * @returns The point at which to position the overlay after pushing. This is effectively a new * originPoint. */ _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._viewportMargin ? viewport.left - scrollPosition.left - start.x : 0; } if (overlay.height <= viewport.height) { pushY = overflowTop || -overflowBottom; } else { pushY = start.y < this._viewportMargin ? viewport.top - scrollPosition.top - start.y : 0; } this._previousPushAmount = { x: pushX, y: pushY }; return { x: start.x + pushX, y: start.y + pushY }; } /** * Applies a computed position to the overlay and emits a position change. * @param position The position preference * @param originPoint The point on the origin element where the overlay is connected. */ _applyPosition(position, originPoint) { this._setTransformOrigin(position); this._setOverlayElementStyles(originPoint, position); this._setBoundingBoxStyles(originPoint, position); if (position.panelClass) { this._addPanelClasses(position.panelClass); } this._lastPosition = position; if (this._positionChanges.observers.length) { const scrollableViewProperties = this._getScrollVisibility(); const changeEvent = new ConnectedOverlayPositionChange(position, scrollableViewProperties); this._positionChanges.next(changeEvent); } this._isInitialRender = false; } /** Sets the transform origin based on the configured selector and the passed-in position. */ _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}`; } } /** * Gets the position and size of the overlay's sizing container. * * This method does no measuring and applies no styles so that we can cheaply compute the * bounds for all positions and choose the best fit based on these results. */ _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._viewportMargin; } else if (position.overlayY === "bottom") { bottom = viewport.height - origin.y + this._viewportMargin * 2; height = viewport.height - bottom + this._viewportMargin; } else { const smallestDistanceToViewportEdge = Math.min(viewport.bottom - origin.y + viewport.top, origin.y); const previousHeight = this._lastBoundingBoxSize.height; height = smallestDistanceToViewportEdge * 2; top = origin.y - smallestDistanceToViewportEdge; if (height > previousHeight && !this._isInitialRender && !this._growAfterOpen) { top = origin.y - previousHeight / 2; } } const isBoundedByRightViewportEdge = position.overlayX === "start" && !isRtl || position.overlayX === "end" && isRtl; const isBoundedByLeftViewportEdge = position.overlayX === "end" && !isRtl || position.overlayX === "start" && isRtl; let width, left, right; if (isBoundedByLeftViewportEdge) { right = viewport.width - origin.x + this._viewportMargin; width = origin.x - this._viewportMargin; } else if (isBoundedByRightViewportEdge) { left = origin.x; width = viewport.right - origin.x; } else { const smallestDistanceToViewportEdge = Math.min(viewport.right - origin.x + viewport.left, origin.x); const previousWidth = this._lastBoundingBoxSize.width; width = smallestDistanceToViewportEdge * 2; left = origin.x - smallestDistanceToViewportEdge; if (width > previousWidth && !this._isInitialRender && !this._growAfterOpen) { left = origin.x - previousWidth / 2; } } return { top, left, bottom, right, width, height }; } /** * Sets the position and size of the overlay's sizing wrapper. The wrapper is positioned on the * origin's connection point and stretches to the bounds of the viewport. * * @param origin The point on the origin element where the overlay is connected. * @param position The position preference */ _setBoundingBoxStyles(origin, position) { const boundingBoxRect = this._calculateBoundingBoxRect(origin, position); if (!this._isInitialRender && !this._growAfterOpen) { boundingBoxRect.height = Math.min(boundingBoxRect.height, this._lastBoundingBoxSize.height); boundingBoxRect.width = Math.min(boundingBoxRect.width, this._lastBoundingBoxSize.width); } const styles = {}; if (this._hasExactPosition()) { styles.top = styles.left = "0"; styles.bottom = styles.right = styles.maxHeight = styles.maxWidth = ""; styles.width = styles.height = "100%"; } else { const maxHeight = this._overlayRef.getConfig().maxHeight; const maxWidth = this._overlayRef.getConfig().maxWidth; styles.height = coerceCssPixelValue(boundingBoxRect.height); styles.top = coerceCssPixelValue(boundingBoxRect.top); styles.bottom = coerceCssPixelValue(boundingBoxRect.bottom); styles.width = coerceCssPixelValue(boundingBoxRect.width); styles.left = coerceCssPixelValue(boundingBoxRect.left); styles.right = coerceCssPixelValue(boundingBoxRect.right); if (position.overlayX === "center") { styles.alignItems = "center"; } else { styles.alignItems = position.overlayX === "end" ? "flex-end" : "flex-start"; } if (position.overlayY === "center") { styles.justifyContent = "center"; } else { styles.justifyContent = position.overlayY === "bottom" ? "flex-end" : "flex-start"; } if (maxHeight) { styles.maxHeight = coerceCssPixelValue(maxHeight); } if (maxWidth) { styles.maxWidth = coerceCssPixelValue(maxWidth); } } this._lastBoundingBoxSize = boundingBoxRect; extendStyles(this._boundingBox.style, styles); } /** Resets the styles for the bounding box so that a new positioning can be computed. */ _resetBoundingBoxStyles() { extendStyles(this._boundingBox.style, { top: "0", left: "0", right: "0", bottom: "0", height: "", width: "", alignItems: "", justifyContent: "" }); } /** Resets the styles for the overlay pane so that a new positioning can be computed. */ _resetOverlayElementStyles() { extendStyles(this._pane.style, { top: "", left: "", bottom: "", right: "", position: "", transform: "" }); } /** Sets positioning styles to the overlay element. */ _setOverlayElementStyles(originPoint, position) { const styles = {}; const hasExactPosition = this._hasExactPosition(); const hasFlexibleDimensions = this._hasFlexibleDimensions; const config = this._overlayRef.getConfig(); if (hasExactPosition) { const scrollPosition = this._viewportRuler.getViewportScrollPosition(); extendStyles(styles, this._getExactOverlayY(position, originPoint, scrollPosition)); extendStyles(styles, this._getExactOverlayX(position, originPoint, scrollPosition)); } else { styles.position = "static"; } let transformString = ""; let offsetX = this._getOffset(position, "x"); let offsetY = this._getOffset(position, "y"); if (offsetX) { transformString += `translateX(${offsetX}px) `; } if (offsetY) { transformString += `translateY(${offsetY}px)`; } styles.transform = transformString.trim(); if (config.maxHeight) { if (hasExactPosition) { styles.maxHeight = coerceCssPixelValue(config.maxHeight); } else if (hasFlexibleDimensions) { styles.maxHeight = ""; } } if (config.maxWidth) { if (hasExactPosition) { styles.maxWidth = coerceCssPixelValue(config.maxWidth); } else if (hasFlexibleDimensions) { styles.maxWidth = ""; } } extendStyles(this._pane.style, styles); } /** Gets the exact top/bottom for the overlay when not using flexible sizing or when pushing. */ _getExactOverlayY(position, originPoint, scrollPosition) { let styles = { top: "", bottom: "" }; let overlayPoint = this._getOverlayPoint(originPoint, this._overlayRect, position); if (this._isPushed) { overlayPoint = this._pushOverlayOnScreen(overlayPoint, this._overlayRect, scrollPosition); } if (position.overlayY === "bottom") { const documentHeight = this._document.documentElement.clientHeight; styles.bottom = `${documentHeight - (overlayPoint.y + this._overlayRect.height)}px`; } else { styles.top = coerceCssPixelValue(overlayPoint.y); } return styles; } /** Gets the exact left/right for the overlay when not using flexible sizing or when pushing. */ _getExactOverlayX(position, originPoint, scrollPosition) { let styles = { left: "", right: "" }; let overlayPoint = this._getOverlayPoint(originPoint, this._overlayRect, position); if (this._isPushed) { overlayPoint = this._pushOverlayOnScreen(overlayPoint, this._overlayRect, scrollPosition); } let horizontalStyleProperty; if (this._isRtl()) { horizontalStyleProperty = position.overlayX === "end" ? "left" : "right"; } else { horizontalStyleProperty = position.overlayX === "end" ? "right" : "left"; } if (horizontalStyleProperty === "right") { const documentWidth = this._document.documentElement.clientWidth; styles.right = `${documentWidth - (overlayPoint.x + this._overlayRect.width)}px`; } else { styles.left = coerceCssPixelValue(overlayPoint.x); } return styles; } /** * Gets the view properties of the trigger and overlay, including whether they are clipped * or completely outside the view of any of the strategy's scrollables. */ _getScrollVisibility() { const originBounds = this._getOriginRect(); const overlayBounds = this._pane.getBoundingClientRect(); const scrollContainerBounds = this._scrollables.map((scrollable) => { return scrollable.getElementRef().nativeElement.getBoundingClientRect(); }); return { isOriginClipped: isElementClippedByScrolling(originBounds, scrollContainerBounds), isOriginOutsideView: isElementScrolledOutsideView(originBounds, scrollContainerBounds), isOverlayClipped: isElementClippedByScrolling(overlayBounds, scrollContainerBounds), isOverlayOutsideView: isElementScrolledOutsideView(overlayBounds, scrollContainerBounds) }; } /** Subtracts the amount that an element is overflowing on an axis from its length. */ _subtractOverflows(length, ...overflows) { return overflows.reduce((currentValue, currentOverflow) => { return currentValue - Math.max(currentOverflow, 0); }, length); } /** Narrows the given viewport rect by the current _viewportMargin. */ _getNarrowedViewportRect() { const width = this._document.documentElement.clientWidth; const height = this._document.documentElement.clientHeight; const scrollPosition = this._viewportRuler.getViewportScrollPosition(); return { top: scrollPosition.top + this._viewportMargin, left: scrollPosition.left + this._viewportMargin, right: scrollPosition.left + width - this._viewportMargin, bottom: scrollPosition.top + height - this._viewportMargin, width: width - 2 * this._viewportMargin, height: height - 2 * this._viewportMargin }; } /** Whether the we're dealing with an RTL context */ _isRtl() { return this._overlayRef.getDirection() === "rtl"; } /** Determines whether the overlay uses exact or flexible positioning. */ _hasExactPosition() { return !this._hasFlexibleDimensions || this._isPushed; } /** Retrieves the offset of a position along the x or y axis. */ _getOffset(position, axis) { if (axis === "x") { return position.offsetX == null ? this._offsetX : position.offsetX; } return position.offsetY == null ? this._offsetY : position.offsetY; } /** Validates that the current position match the expected values. */ _validatePositions() { if (typeof ngDevMode === "undefined" || ngDevMode) { if (!this._preferredPositions.length) { throw Error("FlexibleConnectedPositionStrategy: At least one position is required."); } this._preferredPositions.forEach((pair) => { validateHorizontalPosition("originX", pair.originX); validateVerticalPosition("originY", pair.originY); validateHorizontalPosition("overlayX", pair.overlayX); validateVerticalPosition("overlayY", pair.overlayY); }); } } /** Adds a single CSS class or an array of classes on the overlay panel. */ _addPanelClasses(cssClasses) { if (this._pane) { coerceArray(cssClasses).forEach((cssClass) => { if (cssClass !== "" && this._appliedPanelClasses.indexOf(cssClass) === -1) { this._appliedPanelClasses.push(cssClass); this._pane.classList.add(cssClass); } }); } } /** Clears the classes that the position strategy has applied from the overlay panel. */ _clearPanelClasses() { if (this._pane) { this._appliedPanelClasses.forEach((cssClass) => { this._pane.classList.remove(cssClass); }); this._appliedPanelClasses = []; } } /** Returns the ClientRect of the current origin. */ _getOriginRect() { const origin = this._origin; if (origin instanceof ElementRef) { return origin.nativeElement.getBoundingClientRect(); } if (origin instanceof Element) { return origin.getBoundingClientRect(); } const width = origin.width || 0; const height = origin.height || 0; return { top: origin.y, bottom: origin.y + height, left: origin.x, right: origin.x + width, height, width }; } }; function extendStyles(destination, source) { for (let key in source) { if (source.hasOwnProperty(key)) { destination[key] = source[key]; } } return destination; } function getPixelValue(input) { if (typeof input !== "number" && input != null) { const [value, units] = input.split(cssUnitPattern); return !units || units === "px" ? parseFloat(value) : null; } return input || null; } function getRoundedBoundingClientRect(clientRect) { return { top: Math.floor(clientRect.top), right: Math.floor(clientRect.right), bottom: Math.floor(clientRect.bottom), left: Math.floor(clientRect.left), width: Math.floor(clientRect.width), height: Math.floor(clientRect.height) }; } var wrapperClass = "cdk-global-overlay-wrapper"; var GlobalPositionStrategy = class { constructor() { this._cssPosition = "static"; this._topOffset = ""; this._bottomOffset = ""; this._alignItems = ""; this._xPosition = ""; this._xOffset = ""; this._width = ""; this._height = ""; this._isDisposed = false; } attach(overlayRef) { const config = overlayRef.getConfig(); this._overlayRef = overlayRef; if (this._width && !config.width) { overlayRef.updateSize({ width: this._width }); } if (this._height && !config.height) { overlayRef.updateSize({ height: this._height }); } overlayRef.hostElement.classList.add(wrapperClass); this._isDisposed = false; } /** * Sets the top position of the overlay. Clears any previously set vertical position. * @param value New top offset. */ top(value = "") { this._bottomOffset = ""; this._topOffset = value; this._alignItems = "flex-start"; return this; } /** * Sets the left position of the overlay. Clears any previously set horizontal position. * @param value New left offset. */ left(value = "") { this._xOffset = value; this._xPosition = "left"; return this; } /** * Sets the bottom position of the overlay. Clears any previously set vertical position. * @param value New bottom offset. */ bottom(value = "") { this._topOffset = ""; this._bottomOffset = value; this._alignItems = "flex-end"; return this; } /** * Sets the right position of the overlay. Clears any previously set horizontal position. * @param value New right offset. */ right(value = "") { this._xOffset = value; this._xPosition = "right"; return this; } /** * Sets the overlay to the start of the viewport, depending on the overlay direction. * This will be to the left in LTR layouts and to the right in RTL. * @param offset Offset from the edge of the screen. */ start(value = "") { this._xOffset = value; this._xPosition = "start"; return this; } /** * Sets the overlay to the end of the viewport, depending on the overlay direction. * This will be to the right in LTR layouts and to the left in RTL. * @param offset Offset from the edge of the screen. */ end(value = "") { this._xOffset = value; this._xPosition = "end"; return this; } /** * Sets the overlay width and clears any previously set width. * @param value New width for the overlay * @deprecated Pass the `width` through the `OverlayConfig`. * @breaking-change 8.0.0 */ width(value = "") { if (this._overlayRef) { this._overlayRef.updateSize({ width: value }); } else { this._width = value; } return this; } /** * Sets the overlay height and clears any previously set height. * @param value New height for the overlay * @deprecated Pass the `height` through the `OverlayConfig`. * @breaking-change 8.0.0 */ height(value = "") { if (this._overlayRef) { this._overlayRef.updateSize({ height: value }); } else { this._height = value; } return this; } /** * Centers the overlay horizontally with an optional offset. * Clears any previously set horizontal position. * * @param offset Overlay offset from the horizontal center. */ centerHorizontally(offset = "") { this.left(offset); this._xPosition = "center"; return this; } /** * Centers the overlay vertically with an optional offset. * Clears any previously set vertical position. * * @param offset Overlay offset from the vertical center. */ centerVertically(offset = "") { this.top(offset); this._alignItems = "center"; return this; } /** * Apply the position to the element. * @docs-private */ apply() { if (!this._overlayRef || !this._overlayRef.hasAttached()) { return; } const styles = this._overlayRef.overlayElement.style; const parentStyles = this._overlayRef.hostElement.style; const config = this._overlayRef.getConfig(); const { width, height, maxWidth, maxHeight } = config; const shouldBeFlushHorizontally = (width === "100%" || width === "100vw") && (!maxWidth || maxWidth === "100%" || maxWidth === "100vw"); const shouldBeFlushVertically = (height === "100%" || height === "100vh") && (!maxHeight || maxHeight === "100%" || maxHeight === "100vh"); const xPosition = this._xPosition; const xOffset = this._xOffset; const isRtl = this._overlayRef.getConfig().direction === "rtl"; let marginLeft = ""; let marginRight = ""; let justifyContent = ""; if (shouldBeFlushHorizontally) { justifyContent = "flex-start"; } else if (xPosition === "center") { justifyContent = "center"; if (isRtl) { marginRight = xOffset; } else { marginLeft = xOffset; } } else if (isRtl) { if (xPosition === "left" || xPosition === "end") { justifyContent = "flex-end"; marginLeft = xOffset; } else if (xPosition === "right" || xPosition === "start") { justifyContent = "flex-start"; marginRight = xOffset; } } else if (xPosition === "left" || xPosition === "start") { justifyContent = "flex-start"; marginLeft = xOffset; } else if (xPosition === "right" || xPosition === "end") { justifyContent = "flex-end"; marginRight = xOffset; } styles.position = this._cssPosition; styles.marginLeft = shouldBeFlushHorizontally ? "0" : marginLeft; styles.marginTop = shouldBeFlushVertically ? "0" : this._topOffset; styles.marginBottom = this._bottomOffset; styles.marginRight = shouldBeFlushHorizontally ? "0" : marginRight; parentStyles.justifyContent = justifyContent; parentStyles.alignItems = shouldBeFlushVertically ? "flex-start" : this._alignItems; } /** * Cleans up the DOM changes from the position strategy. * @docs-private */ dispose() { if (this._isDisposed || !this._overlayRef) { return; } const styles = this._overlayRef.overlayElement.style; const parent = this._overlayRef.hostElement; const parentStyles = parent.style; parent.classList.remove(wrapperClass); parentStyles.justifyContent = parentStyles.alignItems = styles.marginTop = styles.marginBottom = styles.marginLeft = styles.marginRight = styles.position = ""; this._overlayRef = null; this._isDisposed = true; } }; var _OverlayPositionBuilder = class _OverlayPositionBuilder { constructor(_viewportRuler, _document, _platform, _overlayContainer) { this._viewportRuler = _viewportRuler; this._document = _document; this._platform = _platform; this._overlayContainer = _overlayContainer; } /** * Creates a global position strategy. */ global() { return new GlobalPositionStrategy(); } /** * Creates a flexible position strategy. * @param origin Origin relative to which to position the overlay. */ flexibleConnectedTo(origin) { return new FlexibleConnectedPositionStrategy(origin, this._viewportRuler, this._document, this._platform, this._overlayContainer); } }; _OverlayPositionBuilder.ɵfac = function OverlayPositionBuilder_Factory(t) { return new (t || _OverlayPositionBuilder)(ɵɵinject(ViewportRuler), ɵɵinject(DOCUMENT), ɵɵinject(Platform), ɵɵinject(OverlayContainer)); }; _OverlayPositionBuilder.ɵprov = ɵɵdefineInjectable({ token: _OverlayPositionBuilder, factory: _OverlayPositionBuilder.ɵfac, providedIn: "root" }); var OverlayPositionBuilder = _OverlayPositionBuilder; (() => { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(OverlayPositionBuilder, [{ type: Injectable, args: [{ providedIn: "root" }] }], () => [{ type: ViewportRuler }, { type: void 0, decorators: [{ type: Inject, args: [DOCUMENT] }] }, { type: Platform }, { type: OverlayContainer }], null); })(); var nextUniqueId = 0; var _Overlay = class _Overlay { constructor(scrollStrategies, _overlayContainer, _componentFactoryResolver, _positionBuilder, _keyboardDispatcher, _injector, _ngZone, _document, _directionality, _location, _outsideClickDispatcher, _animationsModuleType) { this.scrollStrategies = scrollStrategies; this._overlayContainer = _overlayContainer; this._componentFactoryResolver = _componentFactoryResolver; this._positionBuilder = _positionBuilder; this._keyboardDispatcher = _keyboardDispatcher; this._injector = _injector; this._ngZone = _ngZone; this._document = _document; this._directionality = _directionality; this._location = _location; this._outsideClickDispatcher = _outsideClickDispatcher; this._animationsModuleType = _animationsModuleType; } /** * Creates an overlay. * @param config Configuration applied to the overlay. * @returns Reference to the created overlay. */ create(config) { const host = this._createHostElement(); const pane = this._createPaneElement(host); const portalOutlet = this._createPortalOutlet(pane); const overlayConfig = new OverlayConfig(config); overlayConfig.direction = overlayConfig.direction || this._directionality.value; return new OverlayRef(portalOutlet, host, pane, overlayConfig, this._ngZone, this._keyboardDispatcher, this._document, this._location, this._outsideClickDispatcher, this._animationsModuleType === "NoopAnimations"); } /** * Gets a position builder that can be used, via fluent API, * to construct and configure a position strategy. * @returns An overlay position builder. */ position() { return this._positionBuilder; } /** * Creates the DOM element for an overlay and appends it to the overlay container. * @returns Newly-created pane element */ _createPaneElement(host) { const pane = this._document.createElement("div"); pane.id = `cdk-overlay-${nextUniqueId++}`; pane.classList.add("cdk-overlay-pane"); host.appendChild(pane); return pane; } /** * Creates the host element that wraps around an overlay * and can be used for advanced positioning. * @returns Newly-create host element. */ _createHostElement() { const host = this._document.createElement("div"); this._overlayContainer.getContainerElement().appendChild(host); return host; } /** * Create a DomPortalOutlet into which the overlay content can be loaded. * @param pane The DOM element to turn into a portal outlet. * @returns A portal outlet for the given DOM element. */ _createPortalOutlet(pane) { if (!this._appRef) { this._appRef = this._injector.get(ApplicationRef); } return new DomPortalOutlet(pane, this._componentFactoryResolver, this._appRef, this._injector, this._document); } }; _Overlay.ɵfac = function Overlay_Factory(t) { return new (t || _Overlay)(ɵɵinject(ScrollStrategyOptions), ɵɵinject(OverlayContainer), ɵɵinject(ComponentFactoryResolver$1), ɵɵinject(OverlayPositionBuilder), ɵɵinject(OverlayKeyboardDispatcher), ɵɵinject(Injector), ɵɵinject(NgZone), ɵɵinject(DOCUMENT), ɵɵinject(Directionality), ɵɵinject(Location), ɵɵinject(OverlayOutsideClickDispatcher), ɵɵinject(ANIMATION_MODULE_TYPE, 8)); }; _Overlay.ɵprov = ɵɵdefineInjectable({ token: _Overlay, factory: _Overlay.ɵfac, providedIn: "root" }); var Overlay = _Overlay; (() => { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(Overlay, [{ type: Injectable, args: [{ providedIn: "root" }] }], () => [{ type: ScrollStrategyOptions }, { type: OverlayContainer }, { type: ComponentFactoryResolver$1 }, { type: OverlayPositionBuilder }, { type: OverlayKeyboardDispatcher }, { type: Injector }, { type: NgZone }, { type: void 0, decorators: [{ type: Inject, args: [DOCUMENT] }] }, { type: Directionality }, { type: Location }, { type: OverlayOutsideClickDispatcher }, { type: void 0, decorators: [{ type: Inject, args: [ANIMATION_MODULE_TYPE] }, { type: Optional }] }], null); })(); var defaultPositionList = [{ originX: "start", originY: "bottom", overlayX: "start", overlayY: "top" }, { originX: "start", originY: "top", overlayX: "start", overlayY: "bottom" }, { originX: "end", originY: "top", overlayX: "end", overlayY: "bottom" }, { originX: "end", originY: "bottom", overlayX: "end", overlayY: "top" }]; var CDK_CONNECTED_OVERLAY_SCROLL_STRATEGY = new InjectionToken("cdk-connected-overlay-scroll-strategy", { providedIn: "root", factory: () => { const overlay = inject(Overlay); return () => overlay.scrollStrategies.reposition(); } }); var _CdkOverlayOrigin = class _CdkOverlayOrigin { constructor(elementRef) { this.elementRef = elementRef; } }; _CdkOverlayOrigin.ɵfac = function CdkOverlayOrigin_Factory(t) { return new (t || _CdkOverlayOrigin)(ɵɵdirectiveInject(ElementRef)); }; _CdkOverlayOrigin.ɵdir = ɵɵdefineDirective({ type: _CdkOverlayOrigin, selectors: [["", "cdk-overlay-origin", ""], ["", "overlay-origin", ""], ["", "cdkOverlayOrigin", ""]], exportAs: ["cdkOverlayOrigin"], standalone: true }); var CdkOverlayOrigin = _CdkOverlayOrigin; (() => { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(CdkOverlayOrigin, [{ type: Directive, args: [{ selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]", exportAs: "cdkOverlayOrigin", standalone: true }] }], () => [{ type: ElementRef }], null); })(); var _CdkConnectedOverlay = class _CdkConnectedOverlay { /** The offset in pixels for the overlay connection point on the x-axis */ get offsetX() { return this._offsetX; } set offsetX(offsetX) { this._offsetX = offsetX; if (this._position) { this._updatePositionStrategy(this._position); } } /** The offset in pixels for the overlay connection point on the y-axis */ get offsetY() { return this._offsetY; } set offsetY(offsetY) { this._offsetY = offsetY; if (this._position) { this._updatePositionStrategy(this._position); } } /** Whether the overlay should be disposed of when the user goes backwards/forwards in history. */ get disposeOnNavigation() { return this._disposeOnNavigation; } set disposeOnNavigation(value) { this._disposeOnNavigation = value; } // TODO(jelbourn): inputs for size, scroll behavior, animation, etc. constructor(_overlay, templateRef, viewContainerRef, scrollStrategyFactory, _dir) { this._overlay = _overlay; this._dir = _dir; this._backdropSubscription = Subscription.EMPTY; this._attachSubscription = Subscription.EMPTY; this._detachSubscription = Subscription.EMPTY; this._positionSubscription = Subscription.EMPTY; this._disposeOnNavigation = false; this.viewportMargin = 0; this.open = false; this.disableClose = false; this.hasBackdrop = false; this.lockPosition = false; this.flexibleDimensions = false; this.growAfterOpen = false; this.push = false; this.backdropClick = new EventEmitter(); this.positionChange = new EventEmitter(); this.attach = new EventEmitter(); this.detach = new EventEmitter(); this.overlayKeydown = new EventEmitter(); this.overlayOutsideClick = new EventEmitter(); this._templatePortal = new TemplatePortal(templateRef, viewContainerRef); this._scrollStrategyFactory = scrollStrategyFactory; this.scrollStrategy = this._scrollStrategyFactory(); } /** The associated overlay reference. */ get overlayRef() { return this._overlayRef; } /** The element's layout direction. */ get dir() { return this._dir ? this._dir.value : "ltr"; } ngOnDestroy() { this._attachSubscription.unsubscribe(); this._detachSubscription.unsubscribe(); this._backdropSubscription.unsubscribe(); this._positionSubscription.unsubscribe(); if (this._overlayRef) { this._overlayRef.dispose(); } } ngOnChanges(changes) { if (this._position) { this._updatePositionStrategy(this._position); this._overlayRef.updateSize({ width: this.width, minWidth: this.minWidth, height: this.height, minHeight: this.minHeight }); if (changes["origin"] && this.open) { this._position.apply(); } } if (changes["open"]) { this.open ? this._attachOverlay() : this._detachOverlay(); } } /** Creates an overlay */ _createOverlay() { if (!this.positions || !this.positions.length) { this.positions = defaultPositionList; } const overlayRef = this._overlayRef = this._overlay.create(this._buildConfig()); this._attachSubscription = overlayRef.attachments().subscribe(() => this.attach.emit()); this._detachSubscription = overlayRef.detachments().subscribe(() => this.detach.emit()); overlayRef.keydownEvents().subscribe((event) => { this.overlayKeydown.next(event); if (event.keyCode === ESCAPE && !this.disableClose && !hasModifierKey(event)) { event.preventDefault(); this._detachOverlay(); } }); this._overlayRef.outsidePointerEvents().subscribe((event) => { this.overlayOutsideClick.next(event); }); } /** Builds the overlay config based on the directive's inputs */ _buildConfig() { const positionStrategy = this._position = this.positionStrategy || this._createPositionStrategy(); const overlayConfig = new OverlayConfig({ direction: this._dir, positionStrategy, scrollStrategy: this.scrollStrategy, hasBackdrop: this.hasBackdrop, disposeOnNavigation: this.disposeOnNavigation }); if (this.width || this.width === 0) { overlayConfig.width = this.width; } if (this.height || this.height === 0) { overlayConfig.height = this.height; } if (this.minWidth || this.minWidth === 0) { overlayConfig.minWidth = this.minWidth; } if (this.minHeight || this.minHeight === 0) { overlayConfig.minHeight = this.minHeight; } if (this.backdropClass) { overlayConfig.backdropClass = this.backdropClass; } if (this.panelClass) { overlayConfig.panelClass = this.panelClass; } return overlayConfig; } /** Updates the state of a position strategy, based on the values of the directive inputs. */ _updatePositionStrategy(positionStrategy) { const positions = this.positions.map((currentPosition) => ({ originX: currentPosition.originX, originY: currentPosition.originY, overlayX: currentPosition.overlayX, overlayY: currentPosition.overlayY, offsetX: currentPosition.offsetX || this.offsetX, offsetY: currentPosition.offsetY || this.offsetY, panelClass: currentPosition.panelClass || void 0 })); return positionStrategy.setOrigin(this._getFlexibleConnectedPositionStrategyOrigin()).withPositions(positions).withFlexibleDimensions(this.flexibleDimensions).withPush(this.push).withGrowAfterOpen(this.growAfterOpen).withViewportMargin(this.viewportMargin).withLockedPosition(this.lockPosition).withTransformOriginOn(this.transformOriginSelector); } /** Returns the position strategy of the overlay to be set on the overlay config */ _createPositionStrategy() { const strategy = this._overlay.position().flexibleConnectedTo(this._getFlexibleConnectedPositionStrategyOrigin()); this._updatePositionStrategy(strategy); return strategy; } _getFlexibleConnectedPositionStrategyOrigin() { if (this.origin instanceof CdkOverlayOrigin) { return this.origin.elementRef; } else { return this.origin; } } /** Attaches the overlay and subscribes to backdrop clicks if backdrop exists */ _attachOverlay() { if (!this._overlayRef) { this._createOverlay(); } else { this._overlayRef.getConfig().hasBackdrop = this.hasBackdrop; } if (!this._overlayRef.hasAttached()) { this._overlayRef.attach(this._templatePortal); } if (this.hasBackdrop) { this._backdropSubscription = this._overlayRef.backdropClick().subscribe((event) => { this.backdropClick.emit(event); }); } else { this._backdropSubscription.unsubscribe(); } this._positionSubscription.unsubscribe(); if (this.positionChange.observers.length > 0) { this._positionSubscription = this._position.positionChanges.pipe(takeWhile(() => this.positionChange.observers.length > 0)).subscribe((position) => { this.positionChange.emit(position); if (this.positionChange.observers.length === 0) { this._positionSubscription.unsubscribe(); } }); } } /** Detaches the overlay and unsubscribes to backdrop clicks if backdrop exists */ _detachOverlay() { if (this._overlayRef) { this._overlayRef.detach(); } this._backdropSubscription.unsubscribe(); this._positionSubscription.unsubscribe(); } }; _CdkConnectedOverlay.ɵfac = function CdkConnectedOverlay_Factory(t) { return new (t || _CdkConnectedOverlay)(ɵɵdirectiveInject(Overlay), ɵɵdirectiveInject(TemplateRef), ɵɵdirectiveInject(ViewContainerRef), ɵɵdirectiveInject(CDK_CONNECTED_OVERLAY_SCROLL_STRATEGY), ɵɵdirectiveInject(Directionality, 8)); }; _CdkConnectedOverlay.ɵdir = ɵɵdefineDirective({ type: _CdkConnectedOverlay, selectors: [["", "cdk-connected-overlay", ""], ["", "connected-overlay", ""], ["", "cdkConnectedOverlay", ""]], inputs: { origin: ["cdkConnectedOverlayOrigin", "origin"], positions: ["cdkConnectedOverlayPositions", "positions"], positionStrategy: ["cdkConnectedOverlayPositionStrategy", "positionStrategy"], offsetX: ["cdkConnectedOverlayOffsetX", "offsetX"], offsetY: ["cdkConnectedOverlayOffsetY", "offsetY"], width: ["cdkConnectedOverlayWidth", "width"], height: ["cdkConnectedOverlayHeight", "height"], minWidth: ["cdkConnectedOverlayMinWidth", "minWidth"], minHeight: ["cdkConnectedOverlayMinHeight", "minHeight"], backdropClass: ["cdkConnectedOverlayBackdropClass", "backdropClass"], panelClass: ["cdkConnectedOverlayPanelClass", "panelClass"], viewportMargin: ["cdkConnectedOverlayViewportMargin", "viewportMargin"], scrollStrategy: ["cdkConnectedOverlayScrollStrategy", "scrollStrategy"], open: ["cdkConnectedOverlayOpen", "open"], disableClose: ["cdkConnectedOverlayDisableClose", "disableClose"], transformOriginSelector: ["cdkConnectedOverlayTransformOriginOn", "transformOriginSelector"], hasBackdrop: ["cdkConnectedOverlayHasBackdrop", "hasBackdrop", booleanAttribute], lockPosition: ["cdkConnectedOverlayLockPosition", "lockPosition", booleanAttribute], flexibleDimensions: ["cdkConnectedOverlayFlexibleDimensions", "flexibleDimensions", booleanAttribute], growAfterOpen: ["cdkConnectedOverlayGrowAfterOpen", "growAfterOpen", booleanAttribute], push: ["cdkConnectedOverlayPush", "push", booleanAttribute], disposeOnNavigation: ["cdkConnectedOverlayDisposeOnNavigation", "disposeOnNavigation", booleanAttribute] }, outputs: { backdropClick: "backdropClick", positionChange: "positionChange", attach: "attach", detach: "detach", overlayKeydown: "overlayKeydown", overlayOutsideClick: "overlayOutsideClick" }, exportAs: ["cdkConnectedOverlay"], standalone: true, features: [ɵɵInputTransformsFeature, ɵɵNgOnChangesFeature] }); var CdkConnectedOverlay = _CdkConnectedOverlay; (() => { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(CdkConnectedOverlay, [{ type: Directive, args: [{ selector: "[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]", exportAs: "cdkConnectedOverlay", standalone: true }] }], () => [{ type: Overlay }, { type: TemplateRef }, { type: ViewContainerRef }, { type: void 0, decorators: [{ type: Inject, args: [CDK_CONNECTED_OVERLAY_SCROLL_STRATEGY] }] }, { type: Directionality, decorators: [{ type: Optional }] }], { origin: [{ type: Input, args: ["cdkConnectedOverlayOrigin"] }], positions: [{ type: Input, args: ["cdkConnectedOverlayPositions"] }], positionStrategy: [{ type: Input, args: ["cdkConnectedOverlayPositionStrategy"] }], offsetX: [{ type: Input, args: ["cdkConnectedOverlayOffsetX"] }], offsetY: [{ type: Input, args: ["cdkConnectedOverlayOffsetY"] }], width: [{ type: Input, args: ["cdkConnectedOverlayWidth"] }], height: [{ type: Input, args: ["cdkConnectedOverlayHeight"] }], minWidth: [{ type: Input, args: ["cdkConnectedOverlayMinWidth"] }], minHeight: [{ type: Input, args: ["cdkConnectedOverlayMinHeight"] }], backdropClass: [{ type: Input, args: ["cdkConnectedOverlayBackdropClass"] }], panelClass: [{ type: Input, args: ["cdkConnectedOverlayPanelClass"] }], viewportMargin: [{ type: Input, args: ["cdkConnectedOverlayViewportMargin"] }], scrollStrategy: [{ type: Input, args: ["cdkConnectedOverlayScrollStrategy"] }], open: [{ type: Input, args: ["cdkConnectedOverlayOpen"] }], disableClose: [{ type: Input, args: ["cdkConnectedOverlayDisableClose"] }], transformOriginSelector: [{ type: Input, args: ["cdkConnectedOverlayTransformOriginOn"] }], hasBackdrop: [{ type: Input, args: [{ alias: "cdkConnectedOverlayHasBackdrop", transform: booleanAttribute }] }], lockPosition: [{ type: Input, args: [{ alias: "cdkConnectedOverlayLockPosition", transform: booleanAttribute }] }], flexibleDimensions: [{ type: Input, args: [{ alias: "cdkConnectedOverlayFlexibleDimensions", transform: booleanAttribute }] }], growAfterOpen: [{ type: Input, args: [{ alias: "cdkConnectedOverlayGrowAfterOpen", transform: booleanAttribute }] }], push: [{ type: Input, args: [{ alias: "cdkConnectedOverlayPush", transform: booleanAttribute }] }], disposeOnNavigation: [{ type: Input, args: [{ alias: "cdkConnectedOverlayDisposeOnNavigation", transform: booleanAttribute }] }], backdropClick: [{ type: Output }], positionChange: [{ type: Output }], attach: [{ type: Output }], detach: [{ type: Output }], overlayKeydown: [{ type: Output }], overlayOutsideClick: [{ type: Output }] }); })(); function CDK_CONNECTED_OVERLAY_SCROLL_STRATEGY_PROVIDER_FACTORY(overlay) { return () => overlay.scrollStrategies.reposition(); } var CDK_CONNECTED_OVERLAY_SCROLL_STRATEGY_PROVIDER = { provide: CDK_CONNECTED_OVERLAY_SCROLL_STRATEGY, deps: [Overlay], useFactory: CDK_CONNECTED_OVERLAY_SCROLL_STRATEGY_PROVIDER_FACTORY }; var _OverlayModule = class _OverlayModule { }; _OverlayModule.ɵfac = function OverlayModule_Factory(t) { return new (t || _OverlayModule)(); }; _OverlayModule.ɵmod = ɵɵdefineNgModule({ type: _OverlayModule, imports: [BidiModule, PortalModule, ScrollingModule, CdkConnectedOverlay, CdkOverlayOrigin], exports: [CdkConnectedOverlay, CdkOverlayOrigin, ScrollingModule] }); _OverlayModule.ɵinj = ɵɵdefineInjector({ providers: [Overlay, CDK_CONNECTED_OVERLAY_SCROLL_STRATEGY_PROVIDER], imports: [BidiModule, PortalModule, ScrollingModule, ScrollingModule] }); var OverlayModule = _OverlayModule; (() => { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(OverlayModule, [{ type: NgModule, args: [{ imports: [BidiModule, PortalModule, ScrollingModule, CdkConnectedOverlay, CdkOverlayOrigin], exports: [CdkConnectedOverlay, CdkOverlayOrigin, ScrollingModule], providers: [Overlay, CDK_CONNECTED_OVERLAY_SCROLL_STRATEGY_PROVIDER] }] }], null, null); })(); var _FullscreenOverlayContainer = class _FullscreenOverlayContainer extends OverlayContainer { constructor(_document, platform) { super(_document, platform); } ngOnDestroy() { super.ngOnDestroy(); if (this._fullScreenEventName && this._fullScreenListener) { this._document.removeEventListener(this._fullScreenEventName, this._fullScreenListener); } } _createContainer() { super._createContainer(); this._adjustParentForFullscreenChange(); this._addFullscreenChangeListener(() => this._adjustParentForFullscreenChange()); } _adjustParentForFullscreenChange() { if (!this._containerElement) { return; } const fullscreenElement = this.getFullscreenElement(); const parent = fullscreenElement || this._document.body; parent.appendChild(this._containerElement); } _addFullscreenChangeListener(fn) { const eventName = this._getEventName(); if (eventName) { if (this._fullScreenListener) { this._document.removeEventListener(eventName, this._fullScreenListener); } this._document.addEventListener(eventName, fn); this._fullScreenListener = fn; } } _getEventName() { if (!this._fullScreenEventName) { const _document = this._document; if (_document.fullscreenEnabled) { this._fullScreenEventName = "fullscreenchange"; } else if (_document.webkitFullscreenEnabled) { this._fullScreenEventName = "webkitfullscreenchange"; } else if (_document.mozFullScreenEnabled) { this._fullScreenEventName = "mozfullscreenchange"; } else if (_document.msFullscreenEnabled) { this._fullScreenEventName = "MSFullscreenChange"; } } return this._fullScreenEventName; } /** * When the page is put into fullscreen mode, a specific element is specified. * Only that element and its children are visible when in fullscreen mode. */ getFullscreenElement() { const _document = this._document; return _document.fullscreenElement || _document.webkitFullscreenElement || _document.mozFullScreenElement || _document.msFullscreenElement || null; } }; _FullscreenOverlayContainer.ɵfac = function FullscreenOverlayContainer_Factory(t) { return new (t || _FullscreenOverlayContainer)(ɵɵinject(DOCUMENT), ɵɵinject(Platform)); }; _FullscreenOverlayContainer.ɵprov = ɵɵdefineInjectable({ token: _FullscreenOverlayContainer, factory: _FullscreenOverlayContainer.ɵfac, providedIn: "root" }); var FullscreenOverlayContainer = _FullscreenOverlayContainer; (() => { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(FullscreenOverlayContainer, [{ type: Injectable, args: [{ providedIn: "root" }] }], () => [{ type: void 0, decorators: [{ type: Inject, args: [DOCUMENT] }] }, { type: Platform }], null); })(); export { ViewportRuler, ComponentPortal, TemplatePortal, CdkPortalOutlet, PortalModule, OverlayConfig, ConnectionPositionPair, OverlayKeyboardDispatcher, Overlay, CdkOverlayOrigin, CdkConnectedOverlay, OverlayModule }; //# sourceMappingURL=chunk-6U656R5M.js.map