import { Platform, _CdkPrivateStyleLoader, _IdGenerator, _getEventTarget, _getShadowRoot, coerceArray, coerceElement, coerceNumberProperty, isFakeMousedownFromScreenReader, isFakeTouchstartFromScreenReader } from "./chunk-R6KALAQM.js"; import "./chunk-76DXN4JH.js"; import "./chunk-4X6VR2I6.js"; import { ApplicationRef, ChangeDetectionStrategy, ChangeDetectorRef, Component, Directive, ElementRef, EventEmitter, Inject, Injectable, Input, IterableDiffers, NgModule, NgZone, Optional, Output, Renderer2, RendererFactory2, TemplateRef, ViewChild, ViewContainerRef, ViewEncapsulation, afterNextRender, booleanAttribute, numberAttribute, setClassMetadata, ɵɵInheritDefinitionFeature, ɵɵNgOnChangesFeature, ɵɵProvidersFeature, ɵɵadvance, ɵɵattribute, ɵɵclassProp, ɵɵdefineComponent, ɵɵdefineDirective, ɵɵdefineNgModule, ɵɵdomElement, ɵɵdomElementEnd, ɵɵdomElementStart, ɵɵloadQuery, ɵɵprojection, ɵɵprojectionDef, ɵɵqueryRefresh, ɵɵstyleProp, ɵɵviewQuery } from "./chunk-UEBPW2IJ.js"; import { DOCUMENT, DestroyRef, InjectionToken, Injector, effect, forwardRef, inject, signal, untracked, ɵɵdefineInjectable, ɵɵdefineInjector } from "./chunk-QLJXSR7F.js"; import { BehaviorSubject, ConnectableObservable, Observable, Subject, Subscription, animationFrameScheduler, asapScheduler, auditTime, distinctUntilChanged, filter, interval, isObservable, map, merge, of, pairwise, shareReplay, startWith, switchMap, take, takeUntil, tap } from "./chunk-RSS3ODKE.js"; import "./chunk-TKSB4YUA.js"; // node_modules/@angular/cdk/fesm2022/directionality.mjs var DIR_DOCUMENT = new InjectionToken("cdk-dir-doc", { providedIn: "root", factory: DIR_DOCUMENT_FACTORY }); function DIR_DOCUMENT_FACTORY() { return inject(DOCUMENT); } var RTL_LOCALE_PATTERN = /^(ar|ckb|dv|he|iw|fa|nqo|ps|sd|ug|ur|yi|.*[-_](Adlm|Arab|Hebr|Nkoo|Rohg|Thaa))(?!.*[-_](Latn|Cyrl)($|-|_))($|-|_)/i; function _resolveDirectionality(rawValue) { const value = rawValue?.toLowerCase() || ""; if (value === "auto" && typeof navigator !== "undefined" && navigator?.language) { return RTL_LOCALE_PATTERN.test(navigator.language) ? "rtl" : "ltr"; } return value === "rtl" ? "rtl" : "ltr"; } var Directionality = class _Directionality { /** The current 'ltr' or 'rtl' value. */ get value() { return this.valueSignal(); } /** * The current 'ltr' or 'rtl' value. */ valueSignal = signal("ltr", ...ngDevMode ? [{ debugName: "valueSignal" }] : []); /** Stream that emits whenever the 'ltr' / 'rtl' state changes. */ change = new EventEmitter(); constructor() { const _document = inject(DIR_DOCUMENT, { optional: true }); if (_document) { const bodyDir = _document.body ? _document.body.dir : null; const htmlDir = _document.documentElement ? _document.documentElement.dir : null; this.valueSignal.set(_resolveDirectionality(bodyDir || htmlDir || "ltr")); } } ngOnDestroy() { this.change.complete(); } static ɵfac = function Directionality_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || _Directionality)(); }; static ɵprov = ɵɵdefineInjectable({ token: _Directionality, factory: _Directionality.ɵfac, providedIn: "root" }); }; (() => { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(Directionality, [{ type: Injectable, args: [{ providedIn: "root" }] }], () => [], null); })(); // node_modules/@angular/cdk/fesm2022/scrolling2.mjs var RtlScrollAxisType; (function(RtlScrollAxisType2) { RtlScrollAxisType2[RtlScrollAxisType2["NORMAL"] = 0] = "NORMAL"; RtlScrollAxisType2[RtlScrollAxisType2["NEGATED"] = 1] = "NEGATED"; RtlScrollAxisType2[RtlScrollAxisType2["INVERTED"] = 2] = "INVERTED"; })(RtlScrollAxisType || (RtlScrollAxisType = {})); var rtlScrollAxisType; var scrollBehaviorSupported; function supportsScrollBehavior() { if (scrollBehaviorSupported == null) { if (typeof document !== "object" || !document || typeof Element !== "function" || !Element) { scrollBehaviorSupported = false; return scrollBehaviorSupported; } if ("scrollBehavior" in document.documentElement.style) { scrollBehaviorSupported = true; } else { const scrollToFunction = Element.prototype.scrollTo; if (scrollToFunction) { scrollBehaviorSupported = !/\{\s*\[native code\]\s*\}/.test(scrollToFunction.toString()); } else { scrollBehaviorSupported = false; } } } return scrollBehaviorSupported; } function getRtlScrollAxisType() { if (typeof document !== "object" || !document) { return RtlScrollAxisType.NORMAL; } if (rtlScrollAxisType == null) { const scrollContainer = document.createElement("div"); const containerStyle = scrollContainer.style; scrollContainer.dir = "rtl"; containerStyle.width = "1px"; containerStyle.overflow = "auto"; containerStyle.visibility = "hidden"; containerStyle.pointerEvents = "none"; containerStyle.position = "absolute"; const content = document.createElement("div"); const contentStyle = content.style; contentStyle.width = "2px"; contentStyle.height = "1px"; scrollContainer.appendChild(content); document.body.appendChild(scrollContainer); rtlScrollAxisType = RtlScrollAxisType.NORMAL; if (scrollContainer.scrollLeft === 0) { scrollContainer.scrollLeft = 1; rtlScrollAxisType = scrollContainer.scrollLeft === 0 ? RtlScrollAxisType.NEGATED : RtlScrollAxisType.INVERTED; } scrollContainer.remove(); } return rtlScrollAxisType; } // node_modules/@angular/cdk/fesm2022/bidi.mjs var Dir = class _Dir { /** Whether the `value` has been set to its initial value. */ _isInitialized = false; /** Direction as passed in by the consumer. */ _rawDir; /** Event emitted when the direction changes. */ change = new EventEmitter(); /** @docs-private */ get dir() { return this.valueSignal(); } set dir(value) { const previousValue = this.valueSignal(); this.valueSignal.set(_resolveDirectionality(value)); this._rawDir = value; if (previousValue !== this.valueSignal() && this._isInitialized) { this.change.emit(this.valueSignal()); } } /** Current layout direction of the element. */ get value() { return this.dir; } valueSignal = signal("ltr", ...ngDevMode ? [{ debugName: "valueSignal" }] : []); /** Initialize once default value has been set. */ ngAfterContentInit() { this._isInitialized = true; } ngOnDestroy() { this.change.complete(); } static ɵfac = function Dir_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || _Dir)(); }; static ɵdir = ɵɵdefineDirective({ type: _Dir, selectors: [["", "dir", ""]], hostVars: 1, hostBindings: function Dir_HostBindings(rf, ctx) { if (rf & 2) { ɵɵattribute("dir", ctx._rawDir); } }, inputs: { dir: "dir" }, outputs: { change: "dirChange" }, exportAs: ["dir"], features: [ɵɵProvidersFeature([{ provide: Directionality, useExisting: _Dir }])] }); }; (() => { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(Dir, [{ type: Directive, args: [{ selector: "[dir]", providers: [{ provide: Directionality, useExisting: Dir }], host: { "[attr.dir]": "_rawDir" }, exportAs: "dir" }] }], null, { change: [{ type: Output, args: ["dirChange"] }], dir: [{ type: Input }] }); })(); var BidiModule = class _BidiModule { static ɵfac = function BidiModule_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || _BidiModule)(); }; static ɵmod = ɵɵdefineNgModule({ type: _BidiModule, imports: [Dir], exports: [Dir] }); static ɵinj = ɵɵdefineInjector({}); }; (() => { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(BidiModule, [{ type: NgModule, args: [{ imports: [Dir], exports: [Dir] }] }], null, null); })(); // node_modules/@angular/cdk/fesm2022/data-source.mjs var DataSource = class { }; function isDataSource(value) { return value && typeof value.connect === "function" && !(value instanceof ConnectableObservable); } // node_modules/@angular/cdk/fesm2022/recycle-view-repeater-strategy.mjs var ArrayDataSource = class extends DataSource { _data; constructor(_data) { super(); this._data = _data; } connect() { return isObservable(this._data) ? this._data : of(this._data); } disconnect() { } }; var _ViewRepeaterOperation; (function(_ViewRepeaterOperation2) { _ViewRepeaterOperation2[_ViewRepeaterOperation2["REPLACED"] = 0] = "REPLACED"; _ViewRepeaterOperation2[_ViewRepeaterOperation2["INSERTED"] = 1] = "INSERTED"; _ViewRepeaterOperation2[_ViewRepeaterOperation2["MOVED"] = 2] = "MOVED"; _ViewRepeaterOperation2[_ViewRepeaterOperation2["REMOVED"] = 3] = "REMOVED"; })(_ViewRepeaterOperation || (_ViewRepeaterOperation = {})); var _VIEW_REPEATER_STRATEGY = new InjectionToken("_ViewRepeater"); var _RecycleViewRepeaterStrategy = class { /** * The size of the cache used to store unused views. * Setting the cache size to `0` will disable caching. Defaults to 20 views. */ viewCacheSize = 20; /** * View cache that stores embedded view instances that have been previously stamped out, * but don't are not currently rendered. The view repeater will reuse these views rather than * creating brand new ones. * * TODO(michaeljamesparsons) Investigate whether using a linked list would improve performance. */ _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 ? _ViewRepeaterOperation.INSERTED : _ViewRepeaterOperation.REPLACED; } else if (currentIndex == null) { this._detachAndCacheView(adjustedPreviousIndex, viewContainerRef); operation = _ViewRepeaterOperation.REMOVED; } else { view = this._moveView(adjustedPreviousIndex, currentIndex, viewContainerRef, itemValueResolver(record)); operation = _ViewRepeaterOperation.MOVED; } 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; } }; // node_modules/@angular/cdk/fesm2022/scrolling.mjs var _c0 = ["contentWrapper"]; var _c1 = ["*"]; var VIRTUAL_SCROLL_STRATEGY = new InjectionToken("VIRTUAL_SCROLL_STRATEGY"); var FixedSizeVirtualScrollStrategy = class { _scrolledIndexChange = new Subject(); /** @docs-private Implemented as part of VirtualScrollStrategy. */ scrolledIndexChange = this._scrolledIndexChange.pipe(distinctUntilChanged()); /** The attached viewport. */ _viewport = null; /** The size of the items in the virtually scrolling list. */ _itemSize; /** The minimum amount of buffer rendered beyond the viewport (in pixels). */ _minBufferPx; /** The number of buffer items to render beyond the edge of the viewport (in pixels). */ _maxBufferPx; /** * @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._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 { /** The size of the items in the list (in pixels). */ get itemSize() { return this._itemSize; } set itemSize(value) { this._itemSize = coerceNumberProperty(value); } _itemSize = 20; /** * 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); } _minBufferPx = 100; /** * 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); } _maxBufferPx = 200; /** The scroll strategy used by this directive. */ _scrollStrategy = new FixedSizeVirtualScrollStrategy(this.itemSize, this.minBufferPx, this.maxBufferPx); ngOnChanges() { this._scrollStrategy.updateItemAndBufferSize(this.itemSize, this.minBufferPx, this.maxBufferPx); } static ɵfac = function CdkFixedSizeVirtualScroll_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || _CdkFixedSizeVirtualScroll)(); }; static ɵdir = ɵɵdefineDirective({ type: _CdkFixedSizeVirtualScroll, selectors: [["cdk-virtual-scroll-viewport", "itemSize", ""]], inputs: { itemSize: "itemSize", minBufferPx: "minBufferPx", maxBufferPx: "maxBufferPx" }, features: [ɵɵProvidersFeature([{ provide: VIRTUAL_SCROLL_STRATEGY, useFactory: _fixedSizeVirtualScrollStrategyFactory, deps: [forwardRef(() => _CdkFixedSizeVirtualScroll)] }]), ɵɵNgOnChangesFeature] }); }; (() => { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(CdkFixedSizeVirtualScroll, [{ type: Directive, args: [{ selector: "cdk-virtual-scroll-viewport[itemSize]", 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 { _ngZone = inject(NgZone); _platform = inject(Platform); _renderer = inject(RendererFactory2).createRenderer(null, null); _cleanupGlobalListener; constructor() { } /** Subject for notifying that a registered scrollable reference element has been scrolled. */ _scrolled = new Subject(); /** Keeps track of the amount of subscriptions to `scrolled`. Used for cleaning up afterwards. */ _scrolledCount = 0; /** * Map of all the scrollable references that are registered with the service and their * scroll event subscriptions. */ scrollContainers = /* @__PURE__ */ new Map(); /** * 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._cleanupGlobalListener) { this._cleanupGlobalListener = this._ngZone.runOutsideAngular(() => this._renderer.listen("document", "scroll", () => this._scrolled.next())); } 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._cleanupGlobalListener?.(); this._cleanupGlobalListener = void 0; } }; }); } ngOnDestroy() { this._cleanupGlobalListener?.(); this._cleanupGlobalListener = void 0; 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) => !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; } /** 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; } static ɵfac = function ScrollDispatcher_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || _ScrollDispatcher)(); }; static ɵprov = ɵɵdefineInjectable({ token: _ScrollDispatcher, factory: _ScrollDispatcher.ɵfac, providedIn: "root" }); }; (() => { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(ScrollDispatcher, [{ type: Injectable, args: [{ providedIn: "root" }] }], () => [], null); })(); var CdkScrollable = class _CdkScrollable { elementRef = inject(ElementRef); scrollDispatcher = inject(ScrollDispatcher); ngZone = inject(NgZone); dir = inject(Directionality, { optional: true }); _scrollElement = this.elementRef.nativeElement; _destroyed = new Subject(); _renderer = inject(Renderer2); _cleanupScroll; _elementScrolled = new Subject(); constructor() { } ngOnInit() { this._cleanupScroll = this.ngZone.runOutsideAngular(() => this._renderer.listen(this._scrollElement, "scroll", (event) => this._elementScrolled.next(event))); this.scrollDispatcher.register(this); } ngOnDestroy() { this._cleanupScroll?.(); this._elementScrolled.complete(); 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() != RtlScrollAxisType.NORMAL) { if (options.left != null) { options.right = el.scrollWidth - el.clientWidth - options.left; } if (getRtlScrollAxisType() == RtlScrollAxisType.INVERTED) { options.left = options.right; } else if (getRtlScrollAxisType() == RtlScrollAxisType.NEGATED) { 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() == RtlScrollAxisType.INVERTED) { if (from == LEFT) { return el.scrollWidth - el.clientWidth - el.scrollLeft; } else { return el.scrollLeft; } } else if (isRtl && getRtlScrollAxisType() == RtlScrollAxisType.NEGATED) { 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; } } } static ɵfac = function CdkScrollable_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || _CdkScrollable)(); }; static ɵdir = ɵɵdefineDirective({ type: _CdkScrollable, selectors: [["", "cdk-scrollable", ""], ["", "cdkScrollable", ""]] }); }; (() => { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(CdkScrollable, [{ type: Directive, args: [{ selector: "[cdk-scrollable], [cdkScrollable]" }] }], () => [], null); })(); var DEFAULT_RESIZE_TIME = 20; var ViewportRuler = class _ViewportRuler { _platform = inject(Platform); _listeners; /** Cached viewport dimensions. */ _viewportSize; /** Stream of viewport change events. */ _change = new Subject(); /** Used to reference correct document/window */ _document = inject(DOCUMENT); constructor() { const ngZone = inject(NgZone); const renderer = inject(RendererFactory2).createRenderer(null, null); ngZone.runOutsideAngular(() => { if (this._platform.isBrowser) { const changeListener = (event) => this._change.next(event); this._listeners = [renderer.listen("window", "resize", changeListener), renderer.listen("window", "orientationchange", changeListener)]; } this.change().subscribe(() => this._viewportSize = null); }); } ngOnDestroy() { this._listeners?.forEach((cleanup) => cleanup()); 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 DOMRect 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 }; } static ɵfac = function ViewportRuler_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || _ViewportRuler)(); }; static ɵprov = ɵɵdefineInjectable({ token: _ViewportRuler, factory: _ViewportRuler.ɵfac, providedIn: "root" }); }; (() => { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(ViewportRuler, [{ type: Injectable, args: [{ providedIn: "root" }] }], () => [], null); })(); var VIRTUAL_SCROLLABLE = new InjectionToken("VIRTUAL_SCROLLABLE"); var CdkVirtualScrollable = class _CdkVirtualScrollable extends CdkScrollable { constructor() { super(); } /** * 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; } static ɵfac = function CdkVirtualScrollable_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || _CdkVirtualScrollable)(); }; static ɵdir = ɵɵdefineDirective({ type: _CdkVirtualScrollable, features: [ɵɵInheritDefinitionFeature] }); }; (() => { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(CdkVirtualScrollable, [{ type: Directive }], () => [], 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 { elementRef = inject(ElementRef); _changeDetectorRef = inject(ChangeDetectorRef); _scrollStrategy = inject(VIRTUAL_SCROLL_STRATEGY, { optional: true }); scrollable = inject(VIRTUAL_SCROLLABLE, { optional: true }); _platform = inject(Platform); /** Emits when the viewport is detached from a CdkVirtualForOf. */ _detachedSubject = new Subject(); /** Emits when the rendered range changes. */ _renderedRangeSubject = new Subject(); /** The direction the viewport scrolls. */ get orientation() { return this._orientation; } set orientation(orientation) { if (this._orientation !== orientation) { this._orientation = orientation; this._calculateSpacerSize(); } } _orientation = "vertical"; /** * Whether rendered items should persist in the DOM after scrolling out of view. By default, items * will be removed. */ appendOnly = false; // Note: we don't use the typical EventEmitter here because we need to subscribe to the scroll // strategy lazily (i.e. only if the user is actually listening to the events). We do this because // depending on how the strategy calculates the scrolled index, it may come at a cost to // performance. /** Emits when the index of the first element visible in the viewport changes. */ scrolledIndexChange = new Observable((observer) => this._scrollStrategy.scrolledIndexChange.subscribe((index) => Promise.resolve().then(() => this.ngZone.run(() => observer.next(index))))); /** The element that wraps the rendered content. */ _contentWrapper; /** A stream that emits whenever the rendered range changes. */ renderedRangeStream = this._renderedRangeSubject; /** * The total size of all content (in pixels), including content that is not currently rendered. */ _totalContentSize = 0; /** A string representing the `style.width` property value to be used for the spacer element. */ _totalContentWidth = signal("", ...ngDevMode ? [{ debugName: "_totalContentWidth" }] : []); /** A string representing the `style.height` property value to be used for the spacer element. */ _totalContentHeight = signal("", ...ngDevMode ? [{ debugName: "_totalContentHeight" }] : []); /** * The CSS transform applied to the rendered subset of items so that they appear within the bounds * of the visible viewport. */ _renderedContentTransform; /** The currently rendered range of indices. */ _renderedRange = { start: 0, end: 0 }; /** The length of the data bound to this viewport (in number of items). */ _dataLength = 0; /** The size of the viewport (in pixels). */ _viewportSize = 0; /** the currently attached CdkVirtualScrollRepeater. */ _forOf; /** The last rendered content offset that was set. */ _renderedContentOffset = 0; /** * Whether the last rendered content offset was to the end of the content (and therefore needs to * be rewritten as an offset to the start of the content). */ _renderedContentOffsetNeedsRewrite = false; _changeDetectionNeeded = signal(false, ...ngDevMode ? [{ debugName: "_changeDetectionNeeded" }] : []); /** A list of functions to run after the next change detection cycle. */ _runAfterChangeDetection = []; /** Subscription to changes in the viewport size. */ _viewportChanges = Subscription.EMPTY; _injector = inject(Injector); _isDestroyed = false; constructor() { super(); const viewportRuler = inject(ViewportRuler); if (!this._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; } const ref = effect(() => { if (this._changeDetectionNeeded()) { this._doChangeDetection(); } }, ...ngDevMode ? [{ debugName: "ref", injector: inject(ApplicationRef).injector }] : [ // Using ApplicationRef injector is important here because we want this to be a root // effect that runs before change detection of any application views (since we're depending on markForCheck marking parents dirty) { injector: inject(ApplicationRef).injector } ]); inject(DestroyRef).onDestroy(() => void ref.destroy()); } 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(); this._isDestroyed = true; 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 (untracked(this._changeDetectionNeeded)) { return; } this.ngZone.runOutsideAngular(() => { Promise.resolve().then(() => { this.ngZone.run(() => { this._changeDetectionNeeded.set(true); }); }); }); } /** Run change detection. */ _doChangeDetection() { if (this._isDestroyed) { return; } this.ngZone.run(() => { this._changeDetectorRef.markForCheck(); this._contentWrapper.nativeElement.style.transform = this._renderedContentTransform; afterNextRender(() => { this._changeDetectionNeeded.set(false); const runAfterChangeDetection = this._runAfterChangeDetection; this._runAfterChangeDetection = []; for (const fn of runAfterChangeDetection) { fn(); } }, { injector: this._injector }); }); } /** Calculates the `style.width` and `style.height` for the spacer element. */ _calculateSpacerSize() { this._totalContentHeight.set(this.orientation === "horizontal" ? "" : `${this._totalContentSize}px`); this._totalContentWidth.set(this.orientation === "horizontal" ? `${this._totalContentSize}px` : ""); } static ɵfac = function CdkVirtualScrollViewport_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || _CdkVirtualScrollViewport)(); }; static ɵ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: [2, "appendOnly", "appendOnly", booleanAttribute] }, outputs: { scrolledIndexChange: "scrolledIndexChange" }, features: [ɵɵProvidersFeature([{ provide: CdkScrollable, useFactory: (virtualScrollable, viewport) => virtualScrollable || viewport, deps: [[new Optional(), new Inject(VIRTUAL_SCROLLABLE)], _CdkVirtualScrollViewport] }]), ɵɵInheritDefinitionFeature], ngContentSelectors: _c1, decls: 4, vars: 4, consts: [["contentWrapper", ""], [1, "cdk-virtual-scroll-content-wrapper"], [1, "cdk-virtual-scroll-spacer"]], template: function CdkVirtualScrollViewport_Template(rf, ctx) { if (rf & 1) { ɵɵprojectionDef(); ɵɵdomElementStart(0, "div", 1, 0); ɵɵprojection(2); ɵɵdomElementEnd(); ɵɵdomElement(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}.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}\n"], encapsulation: 2, changeDetection: 0 }); }; (() => { (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.None, changeDetection: ChangeDetectionStrategy.OnPush, 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}.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}\n"] }] }], () => [], { 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 { _viewContainerRef = inject(ViewContainerRef); _template = inject(TemplateRef); _differs = inject(IterableDiffers); _viewRepeater = inject(_VIEW_REPEATER_STRATEGY); _viewport = inject(CdkVirtualScrollViewport, { skipSelf: true }); /** Emits when the rendered view of the data changes. */ viewChange = new Subject(); /** Subject that emits when a new DataSource instance is given. */ _dataSourceChanges = new Subject(); /** 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 || []))); } } _cdkVirtualForOf; /** * 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; } _cdkVirtualForTrackBy; /** 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); } /** Emits whenever the data in the current DataSource changes. */ 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) ); /** The differ used to calculate changes to the data. */ _differ = null; /** The most recent data emitted from the DataSource. */ _data; /** The currently rendered items. */ _renderedItems; /** The currently rendered range of indices. */ _renderedRange; /** Whether the rendered data should be updated during the next ngDoCheck cycle. */ _needsUpdate = false; _destroyed = new Subject(); constructor() { const ngZone = inject(NgZone); 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 }; } static ngTemplateContextGuard(directive, context) { return true; } static ɵfac = function CdkVirtualForOf_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || _CdkVirtualForOf)(); }; static ɵdir = ɵɵdefineDirective({ type: _CdkVirtualForOf, selectors: [["", "cdkVirtualFor", "", "cdkVirtualForOf", ""]], inputs: { cdkVirtualForOf: "cdkVirtualForOf", cdkVirtualForTrackBy: "cdkVirtualForTrackBy", cdkVirtualForTemplate: "cdkVirtualForTemplate", cdkVirtualForTemplateCacheSize: "cdkVirtualForTemplateCacheSize" }, features: [ɵɵProvidersFeature([{ provide: _VIEW_REPEATER_STRATEGY, useClass: _RecycleViewRepeaterStrategy }])] }); }; (() => { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(CdkVirtualForOf, [{ type: Directive, args: [{ selector: "[cdkVirtualFor][cdkVirtualForOf]", providers: [{ provide: _VIEW_REPEATER_STRATEGY, useClass: _RecycleViewRepeaterStrategy }] }] }], () => [], { cdkVirtualForOf: [{ type: Input }], cdkVirtualForTrackBy: [{ type: Input }], cdkVirtualForTemplate: [{ type: Input }], cdkVirtualForTemplateCacheSize: [{ type: Input }] }); })(); var CdkVirtualScrollableElement = class _CdkVirtualScrollableElement extends CdkVirtualScrollable { constructor() { super(); } measureBoundingClientRectWithScrollOffset(from) { return this.getElementRef().nativeElement.getBoundingClientRect()[from] - this.measureScrollOffset(from); } static ɵfac = function CdkVirtualScrollableElement_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || _CdkVirtualScrollableElement)(); }; static ɵdir = ɵɵdefineDirective({ type: _CdkVirtualScrollableElement, selectors: [["", "cdkVirtualScrollingElement", ""]], hostAttrs: [1, "cdk-virtual-scrollable"], features: [ɵɵProvidersFeature([{ provide: VIRTUAL_SCROLLABLE, useExisting: _CdkVirtualScrollableElement }]), ɵɵInheritDefinitionFeature] }); }; (() => { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(CdkVirtualScrollableElement, [{ type: Directive, args: [{ selector: "[cdkVirtualScrollingElement]", providers: [{ provide: VIRTUAL_SCROLLABLE, useExisting: CdkVirtualScrollableElement }], host: { "class": "cdk-virtual-scrollable" } }] }], () => [], null); })(); var CdkVirtualScrollableWindow = class _CdkVirtualScrollableWindow extends CdkVirtualScrollable { constructor() { super(); const document2 = inject(DOCUMENT); this.elementRef = new ElementRef(document2.documentElement); this._scrollElement = document2; } measureBoundingClientRectWithScrollOffset(from) { return this.getElementRef().nativeElement.getBoundingClientRect()[from]; } static ɵfac = function CdkVirtualScrollableWindow_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || _CdkVirtualScrollableWindow)(); }; static ɵdir = ɵɵdefineDirective({ type: _CdkVirtualScrollableWindow, selectors: [["cdk-virtual-scroll-viewport", "scrollWindow", ""]], features: [ɵɵProvidersFeature([{ provide: VIRTUAL_SCROLLABLE, useExisting: _CdkVirtualScrollableWindow }]), ɵɵInheritDefinitionFeature] }); }; (() => { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(CdkVirtualScrollableWindow, [{ type: Directive, args: [{ selector: "cdk-virtual-scroll-viewport[scrollWindow]", providers: [{ provide: VIRTUAL_SCROLLABLE, useExisting: CdkVirtualScrollableWindow }] }] }], () => [], null); })(); var CdkScrollableModule = class _CdkScrollableModule { static ɵfac = function CdkScrollableModule_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || _CdkScrollableModule)(); }; static ɵmod = ɵɵdefineNgModule({ type: _CdkScrollableModule, imports: [CdkScrollable], exports: [CdkScrollable] }); static ɵinj = ɵɵdefineInjector({}); }; (() => { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(CdkScrollableModule, [{ type: NgModule, args: [{ exports: [CdkScrollable], imports: [CdkScrollable] }] }], null, null); })(); var ScrollingModule = class _ScrollingModule { static ɵfac = function ScrollingModule_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || _ScrollingModule)(); }; static ɵmod = ɵɵdefineNgModule({ type: _ScrollingModule, imports: [BidiModule, CdkScrollableModule, CdkVirtualScrollViewport, CdkFixedSizeVirtualScroll, CdkVirtualForOf, CdkVirtualScrollableWindow, CdkVirtualScrollableElement], exports: [BidiModule, CdkScrollableModule, CdkFixedSizeVirtualScroll, CdkVirtualForOf, CdkVirtualScrollViewport, CdkVirtualScrollableWindow, CdkVirtualScrollableElement] }); static ɵinj = ɵɵdefineInjector({ imports: [BidiModule, CdkScrollableModule, BidiModule, CdkScrollableModule] }); }; (() => { (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/drag-drop.mjs function deepCloneNode(node) { const clone = node.cloneNode(true); const descendantsWithId = clone.querySelectorAll("[id]"); const nodeName = node.nodeName.toLowerCase(); clone.removeAttribute("id"); for (let i = 0; i < descendantsWithId.length; i++) { descendantsWithId[i].removeAttribute("id"); } if (nodeName === "canvas") { transferCanvasData(node, clone); } else if (nodeName === "input" || nodeName === "select" || nodeName === "textarea") { transferInputData(node, clone); } transferData("canvas", node, clone, transferCanvasData); transferData("input, textarea, select", node, clone, transferInputData); return clone; } function transferData(selector, node, clone, callback) { const descendantElements = node.querySelectorAll(selector); if (descendantElements.length) { const cloneElements = clone.querySelectorAll(selector); for (let i = 0; i < descendantElements.length; i++) { callback(descendantElements[i], cloneElements[i]); } } } var cloneUniqueId = 0; function transferInputData(source, clone) { if (clone.type !== "file") { clone.value = source.value; } if (clone.type === "radio" && clone.name) { clone.name = `mat-clone-${clone.name}-${cloneUniqueId++}`; } } function transferCanvasData(source, clone) { const context = clone.getContext("2d"); if (context) { try { context.drawImage(source, 0, 0); } catch { } } } function getMutableClientRect(element) { const rect = element.getBoundingClientRect(); return { top: rect.top, right: rect.right, bottom: rect.bottom, left: rect.left, width: rect.width, height: rect.height, x: rect.x, y: rect.y }; } function isInsideClientRect(clientRect, x, y) { const { top, bottom, left, right } = clientRect; return y >= top && y <= bottom && x >= left && x <= right; } function isOverflowingParent(parentRect, childRect) { const isLeftOverflowing = childRect.left < parentRect.left; const isRightOverflowing = childRect.left + childRect.width > parentRect.right; const isTopOverflowing = childRect.top < parentRect.top; const isBottomOverflowing = childRect.top + childRect.height > parentRect.bottom; return isLeftOverflowing || isRightOverflowing || isTopOverflowing || isBottomOverflowing; } function adjustDomRect(domRect, top, left) { domRect.top += top; domRect.bottom = domRect.top + domRect.height; domRect.left += left; domRect.right = domRect.left + domRect.width; } function isPointerNearDomRect(rect, threshold, pointerX, pointerY) { const { top, right, bottom, left, width, height } = rect; const xThreshold = width * threshold; const yThreshold = height * threshold; return pointerY > top - yThreshold && pointerY < bottom + yThreshold && pointerX > left - xThreshold && pointerX < right + xThreshold; } var ParentPositionTracker = class { _document; /** Cached positions of the scrollable parent elements. */ positions = /* @__PURE__ */ new Map(); constructor(_document) { this._document = _document; } /** Clears the cached positions. */ clear() { this.positions.clear(); } /** Caches the positions. Should be called at the beginning of a drag sequence. */ cache(elements) { this.clear(); this.positions.set(this._document, { scrollPosition: this.getViewportScrollPosition() }); elements.forEach((element) => { this.positions.set(element, { scrollPosition: { top: element.scrollTop, left: element.scrollLeft }, clientRect: getMutableClientRect(element) }); }); } /** Handles scrolling while a drag is taking place. */ handleScroll(event) { const target = _getEventTarget(event); const cachedPosition = this.positions.get(target); if (!cachedPosition) { return null; } const scrollPosition = cachedPosition.scrollPosition; let newTop; let newLeft; if (target === this._document) { const viewportScrollPosition = this.getViewportScrollPosition(); newTop = viewportScrollPosition.top; newLeft = viewportScrollPosition.left; } else { newTop = target.scrollTop; newLeft = target.scrollLeft; } const topDifference = scrollPosition.top - newTop; const leftDifference = scrollPosition.left - newLeft; this.positions.forEach((position, node) => { if (position.clientRect && target !== node && target.contains(node)) { adjustDomRect(position.clientRect, topDifference, leftDifference); } }); scrollPosition.top = newTop; scrollPosition.left = newLeft; return { top: topDifference, left: leftDifference }; } /** * Gets the scroll position of the viewport. Note that we use the scrollX and scrollY directly, * instead of going through the `ViewportRuler`, because the first value the ruler looks at is * the top/left offset of the `document.documentElement` which works for most cases, but breaks * if the element is offset by something like the `BlockScrollStrategy`. */ getViewportScrollPosition() { return { top: window.scrollY, left: window.scrollX }; } }; function getRootNode(viewRef, _document) { const rootNodes = viewRef.rootNodes; if (rootNodes.length === 1 && rootNodes[0].nodeType === _document.ELEMENT_NODE) { return rootNodes[0]; } const wrapper = _document.createElement("div"); rootNodes.forEach((node) => wrapper.appendChild(node)); return wrapper; } function extendStyles(dest, source, importantProperties2) { for (let key in source) { if (source.hasOwnProperty(key)) { const value = source[key]; if (value) { dest.setProperty(key, value, importantProperties2?.has(key) ? "important" : ""); } else { dest.removeProperty(key); } } } return dest; } function toggleNativeDragInteractions(element, enable) { const userSelect = enable ? "" : "none"; extendStyles(element.style, { "touch-action": enable ? "" : "none", "-webkit-user-drag": enable ? "" : "none", "-webkit-tap-highlight-color": enable ? "" : "transparent", "user-select": userSelect, "-ms-user-select": userSelect, "-webkit-user-select": userSelect, "-moz-user-select": userSelect }); } function toggleVisibility(element, enable, importantProperties2) { extendStyles(element.style, { position: enable ? "" : "fixed", top: enable ? "" : "0", opacity: enable ? "" : "0", left: enable ? "" : "-999em" }, importantProperties2); } function combineTransforms(transform, initialTransform) { return initialTransform && initialTransform != "none" ? transform + " " + initialTransform : transform; } function matchElementSize(target, sourceRect) { target.style.width = `${sourceRect.width}px`; target.style.height = `${sourceRect.height}px`; target.style.transform = getTransform(sourceRect.left, sourceRect.top); } function getTransform(x, y) { return `translate3d(${Math.round(x)}px, ${Math.round(y)}px, 0)`; } function parseCssTimeUnitsToMs(value) { const multiplier = value.toLowerCase().indexOf("ms") > -1 ? 1 : 1e3; return parseFloat(value) * multiplier; } function getTransformTransitionDurationInMs(element) { const computedStyle = getComputedStyle(element); const transitionedProperties = parseCssPropertyValue(computedStyle, "transition-property"); const property = transitionedProperties.find((prop) => prop === "transform" || prop === "all"); if (!property) { return 0; } const propertyIndex = transitionedProperties.indexOf(property); const rawDurations = parseCssPropertyValue(computedStyle, "transition-duration"); const rawDelays = parseCssPropertyValue(computedStyle, "transition-delay"); return parseCssTimeUnitsToMs(rawDurations[propertyIndex]) + parseCssTimeUnitsToMs(rawDelays[propertyIndex]); } function parseCssPropertyValue(computedStyle, name) { const value = computedStyle.getPropertyValue(name); return value.split(",").map((part) => part.trim()); } var importantProperties = /* @__PURE__ */ new Set([ // Needs to be important, because some `mat-table` sets `position: sticky !important`. See #22781. "position" ]); var PreviewRef = class { _document; _rootElement; _direction; _initialDomRect; _previewTemplate; _previewClass; _pickupPositionOnPage; _initialTransform; _zIndex; _renderer; /** Reference to the view of the preview element. */ _previewEmbeddedView; /** Reference to the preview element. */ _preview; get element() { return this._preview; } constructor(_document, _rootElement, _direction, _initialDomRect, _previewTemplate, _previewClass, _pickupPositionOnPage, _initialTransform, _zIndex, _renderer) { this._document = _document; this._rootElement = _rootElement; this._direction = _direction; this._initialDomRect = _initialDomRect; this._previewTemplate = _previewTemplate; this._previewClass = _previewClass; this._pickupPositionOnPage = _pickupPositionOnPage; this._initialTransform = _initialTransform; this._zIndex = _zIndex; this._renderer = _renderer; } attach(parent) { this._preview = this._createPreview(); parent.appendChild(this._preview); if (supportsPopover(this._preview)) { this._preview["showPopover"](); } } destroy() { this._preview.remove(); this._previewEmbeddedView?.destroy(); this._preview = this._previewEmbeddedView = null; } setTransform(value) { this._preview.style.transform = value; } getBoundingClientRect() { return this._preview.getBoundingClientRect(); } addClass(className) { this._preview.classList.add(className); } getTransitionDuration() { return getTransformTransitionDurationInMs(this._preview); } addEventListener(name, handler) { return this._renderer.listen(this._preview, name, handler); } _createPreview() { const previewConfig = this._previewTemplate; const previewClass = this._previewClass; const previewTemplate = previewConfig ? previewConfig.template : null; let preview; if (previewTemplate && previewConfig) { const rootRect = previewConfig.matchSize ? this._initialDomRect : null; const viewRef = previewConfig.viewContainer.createEmbeddedView(previewTemplate, previewConfig.context); viewRef.detectChanges(); preview = getRootNode(viewRef, this._document); this._previewEmbeddedView = viewRef; if (previewConfig.matchSize) { matchElementSize(preview, rootRect); } else { preview.style.transform = getTransform(this._pickupPositionOnPage.x, this._pickupPositionOnPage.y); } } else { preview = deepCloneNode(this._rootElement); matchElementSize(preview, this._initialDomRect); if (this._initialTransform) { preview.style.transform = this._initialTransform; } } extendStyles(preview.style, { // It's important that we disable the pointer events on the preview, because // it can throw off the `document.elementFromPoint` calls in the `CdkDropList`. "pointer-events": "none", // If the preview has a margin, it can throw off our positioning so we reset it. The reset // value for `margin-right` needs to be `auto` when opened as a popover, because our // positioning is always top/left based, but native popover seems to position itself // to the top/right if `` or `` have `dir="rtl"` (see #29604). Setting it // to `auto` pushed it to the top/left corner in RTL and is a noop in LTR. "margin": supportsPopover(preview) ? "0 auto 0 0" : "0", "position": "fixed", "top": "0", "left": "0", "z-index": this._zIndex + "" }, importantProperties); toggleNativeDragInteractions(preview, false); preview.classList.add("cdk-drag-preview"); preview.setAttribute("popover", "manual"); preview.setAttribute("dir", this._direction); if (previewClass) { if (Array.isArray(previewClass)) { previewClass.forEach((className) => preview.classList.add(className)); } else { preview.classList.add(previewClass); } } return preview; } }; function supportsPopover(element) { return "showPopover" in element; } var passiveEventListenerOptions = { passive: true }; var activeEventListenerOptions = { passive: false }; var activeCapturingEventOptions$1 = { passive: false, capture: true }; var MOUSE_EVENT_IGNORE_TIME = 800; var PLACEHOLDER_CLASS = "cdk-drag-placeholder"; var dragImportantProperties = /* @__PURE__ */ new Set([ // Needs to be important, because some `mat-table` sets `position: sticky !important`. See #22781. "position" ]); var DragRef = class { _config; _document; _ngZone; _viewportRuler; _dragDropRegistry; _renderer; _rootElementCleanups; _cleanupShadowRootSelectStart; /** Element displayed next to the user's pointer while the element is dragged. */ _preview; /** Container into which to insert the preview. */ _previewContainer; /** Reference to the view of the placeholder element. */ _placeholderRef; /** Element that is rendered instead of the draggable item while it is being sorted. */ _placeholder; /** Coordinates within the element at which the user picked up the element. */ _pickupPositionInElement; /** Coordinates on the page at which the user picked up the element. */ _pickupPositionOnPage; /** * Marker node used to save the place in the DOM where the element was * picked up so that it can be restored at the end of the drag sequence. */ _marker; /** * Element indicating the position from which the item was picked up initially. */ _anchor = null; /** * CSS `transform` applied to the element when it isn't being dragged. We need a * passive transform in order for the dragged element to retain its new position * after the user has stopped dragging and because we need to know the relative * position in case they start dragging again. This corresponds to `element.style.transform`. */ _passiveTransform = { x: 0, y: 0 }; /** CSS `transform` that is applied to the element while it's being dragged. */ _activeTransform = { x: 0, y: 0 }; /** Inline `transform` value that the element had before the first dragging sequence. */ _initialTransform; /** * Whether the dragging sequence has been started. Doesn't * necessarily mean that the element has been moved. */ _hasStartedDragging = signal(false, ...ngDevMode ? [{ debugName: "_hasStartedDragging" }] : []); /** Whether the element has moved since the user started dragging it. */ _hasMoved; /** Drop container in which the DragRef resided when dragging began. */ _initialContainer; /** Index at which the item started in its initial container. */ _initialIndex; /** Cached positions of scrollable parent elements. */ _parentPositions; /** Emits when the item is being moved. */ _moveEvents = new Subject(); /** Keeps track of the direction in which the user is dragging along each axis. */ _pointerDirectionDelta; /** Pointer position at which the last change in the delta occurred. */ _pointerPositionAtLastDirectionChange; /** Position of the pointer at the last pointer event. */ _lastKnownPointerPosition; /** * Root DOM node of the drag instance. This is the element that will * be moved around as the user is dragging. */ _rootElement; /** * Nearest ancestor SVG, relative to which coordinates are calculated if dragging SVGElement */ _ownerSVGElement; /** * Inline style value of `-webkit-tap-highlight-color` at the time the * dragging was started. Used to restore the value once we're done dragging. */ _rootElementTapHighlight; /** Subscription to pointer movement events. */ _pointerMoveSubscription = Subscription.EMPTY; /** Subscription to the event that is dispatched when the user lifts their pointer. */ _pointerUpSubscription = Subscription.EMPTY; /** Subscription to the viewport being scrolled. */ _scrollSubscription = Subscription.EMPTY; /** Subscription to the viewport being resized. */ _resizeSubscription = Subscription.EMPTY; /** * Time at which the last touch event occurred. Used to avoid firing the same * events multiple times on touch devices where the browser will fire a fake * mouse event for each touch event, after a certain time. */ _lastTouchEventTime; /** Time at which the last dragging sequence was started. */ _dragStartTime; /** Cached reference to the boundary element. */ _boundaryElement = null; /** Whether the native dragging interactions have been enabled on the root element. */ _nativeInteractionsEnabled = true; /** Client rect of the root element when the dragging sequence has started. */ _initialDomRect; /** Cached dimensions of the preview element. Should be read via `_getPreviewRect`. */ _previewRect; /** Cached dimensions of the boundary element. */ _boundaryRect; /** Element that will be used as a template to create the draggable item's preview. */ _previewTemplate; /** Template for placeholder element rendered to show where a draggable would be dropped. */ _placeholderTemplate; /** Elements that can be used to drag the draggable item. */ _handles = []; /** Registered handles that are currently disabled. */ _disabledHandles = /* @__PURE__ */ new Set(); /** Droppable container that the draggable is a part of. */ _dropContainer; /** Layout direction of the item. */ _direction = "ltr"; /** Ref that the current drag item is nested in. */ _parentDragRef; /** * Cached shadow root that the element is placed in. `null` means that the element isn't in * the shadow DOM and `undefined` means that it hasn't been resolved yet. Should be read via * `_getShadowRoot`, not directly. */ _cachedShadowRoot; /** Axis along which dragging is locked. */ lockAxis = null; /** * Amount of milliseconds to wait after the user has put their * pointer down before starting to drag the element. */ dragStartDelay = 0; /** Class to be added to the preview element. */ previewClass; /** * If the parent of the dragged element has a `scale` transform, it can throw off the * positioning when the user starts dragging. Use this input to notify the CDK of the scale. */ scale = 1; /** Whether starting to drag this element is disabled. */ get disabled() { return this._disabled || !!(this._dropContainer && this._dropContainer.disabled); } set disabled(value) { if (value !== this._disabled) { this._disabled = value; this._toggleNativeDragInteractions(); this._handles.forEach((handle) => toggleNativeDragInteractions(handle, value)); } } _disabled = false; /** Emits as the drag sequence is being prepared. */ beforeStarted = new Subject(); /** Emits when the user starts dragging the item. */ started = new Subject(); /** Emits when the user has released a drag item, before any animations have started. */ released = new Subject(); /** Emits when the user stops dragging an item in the container. */ ended = new Subject(); /** Emits when the user has moved the item into a new container. */ entered = new Subject(); /** Emits when the user removes the item its container by dragging it into another container. */ exited = new Subject(); /** Emits when the user drops the item inside a container. */ dropped = new Subject(); /** * Emits as the user is dragging the item. Use with caution, * because this event will fire for every pixel that the user has dragged. */ moved = this._moveEvents; /** Arbitrary data that can be attached to the drag item. */ data; /** * Function that can be used to customize the logic of how the position of the drag item * is limited while it's being dragged. Gets called with a point containing the current position * of the user's pointer on the page, a reference to the item being dragged and its dimensions. * Should return a point describing where the item should be rendered. */ constrainPosition; constructor(element, _config, _document, _ngZone, _viewportRuler, _dragDropRegistry, _renderer) { this._config = _config; this._document = _document; this._ngZone = _ngZone; this._viewportRuler = _viewportRuler; this._dragDropRegistry = _dragDropRegistry; this._renderer = _renderer; this.withRootElement(element).withParent(_config.parentDragRef || null); this._parentPositions = new ParentPositionTracker(_document); _dragDropRegistry.registerDragItem(this); } /** * Returns the element that is being used as a placeholder * while the current element is being dragged. */ getPlaceholderElement() { return this._placeholder; } /** Returns the root draggable element. */ getRootElement() { return this._rootElement; } /** * Gets the currently-visible element that represents the drag item. * While dragging this is the placeholder, otherwise it's the root element. */ getVisibleElement() { return this.isDragging() ? this.getPlaceholderElement() : this.getRootElement(); } /** Registers the handles that can be used to drag the element. */ withHandles(handles) { this._handles = handles.map((handle) => coerceElement(handle)); this._handles.forEach((handle) => toggleNativeDragInteractions(handle, this.disabled)); this._toggleNativeDragInteractions(); const disabledHandles = /* @__PURE__ */ new Set(); this._disabledHandles.forEach((handle) => { if (this._handles.indexOf(handle) > -1) { disabledHandles.add(handle); } }); this._disabledHandles = disabledHandles; return this; } /** * Registers the template that should be used for the drag preview. * @param template Template that from which to stamp out the preview. */ withPreviewTemplate(template) { this._previewTemplate = template; return this; } /** * Registers the template that should be used for the drag placeholder. * @param template Template that from which to stamp out the placeholder. */ withPlaceholderTemplate(template) { this._placeholderTemplate = template; return this; } /** * Sets an alternate drag root element. The root element is the element that will be moved as * the user is dragging. Passing an alternate root element is useful when trying to enable * dragging on an element that you might not have access to. */ withRootElement(rootElement) { const element = coerceElement(rootElement); if (element !== this._rootElement) { this._removeRootElementListeners(); const renderer = this._renderer; this._rootElementCleanups = this._ngZone.runOutsideAngular(() => [renderer.listen(element, "mousedown", this._pointerDown, activeEventListenerOptions), renderer.listen(element, "touchstart", this._pointerDown, passiveEventListenerOptions), renderer.listen(element, "dragstart", this._nativeDragStart, activeEventListenerOptions)]); this._initialTransform = void 0; this._rootElement = element; } if (typeof SVGElement !== "undefined" && this._rootElement instanceof SVGElement) { this._ownerSVGElement = this._rootElement.ownerSVGElement; } return this; } /** * Element to which the draggable's position will be constrained. */ withBoundaryElement(boundaryElement) { this._boundaryElement = boundaryElement ? coerceElement(boundaryElement) : null; this._resizeSubscription.unsubscribe(); if (boundaryElement) { this._resizeSubscription = this._viewportRuler.change(10).subscribe(() => this._containInsideBoundaryOnResize()); } return this; } /** Sets the parent ref that the ref is nested in. */ withParent(parent) { this._parentDragRef = parent; return this; } /** Removes the dragging functionality from the DOM element. */ dispose() { this._removeRootElementListeners(); if (this.isDragging()) { this._rootElement?.remove(); } this._marker?.remove(); this._destroyPreview(); this._destroyPlaceholder(); this._dragDropRegistry.removeDragItem(this); this._removeListeners(); this.beforeStarted.complete(); this.started.complete(); this.released.complete(); this.ended.complete(); this.entered.complete(); this.exited.complete(); this.dropped.complete(); this._moveEvents.complete(); this._handles = []; this._disabledHandles.clear(); this._dropContainer = void 0; this._resizeSubscription.unsubscribe(); this._parentPositions.clear(); this._boundaryElement = this._rootElement = this._ownerSVGElement = this._placeholderTemplate = this._previewTemplate = this._marker = this._parentDragRef = null; } /** Checks whether the element is currently being dragged. */ isDragging() { return this._hasStartedDragging() && this._dragDropRegistry.isDragging(this); } /** Resets a standalone drag item to its initial position. */ reset() { this._rootElement.style.transform = this._initialTransform || ""; this._activeTransform = { x: 0, y: 0 }; this._passiveTransform = { x: 0, y: 0 }; } /** Resets drag item to end of boundary element. */ resetToBoundary() { if ( // can be null if the drag item was never dragged. this._boundaryElement && this._rootElement && // check if we are overflowing off our boundary element isOverflowingParent(this._boundaryElement.getBoundingClientRect(), this._rootElement.getBoundingClientRect()) ) { const parentRect = this._boundaryElement.getBoundingClientRect(); const childRect = this._rootElement.getBoundingClientRect(); let offsetX = 0; let offsetY = 0; if (childRect.left < parentRect.left) { offsetX = parentRect.left - childRect.left; } else if (childRect.right > parentRect.right) { offsetX = parentRect.right - childRect.right; } if (childRect.top < parentRect.top) { offsetY = parentRect.top - childRect.top; } else if (childRect.bottom > parentRect.bottom) { offsetY = parentRect.bottom - childRect.bottom; } const currentLeft = this._activeTransform.x; const currentTop = this._activeTransform.y; let x = currentLeft + offsetX, y = currentTop + offsetY; this._rootElement.style.transform = getTransform(x, y); this._activeTransform = { x, y }; this._passiveTransform = { x, y }; } } /** * Sets a handle as disabled. While a handle is disabled, it'll capture and interrupt dragging. * @param handle Handle element that should be disabled. */ disableHandle(handle) { if (!this._disabledHandles.has(handle) && this._handles.indexOf(handle) > -1) { this._disabledHandles.add(handle); toggleNativeDragInteractions(handle, true); } } /** * Enables a handle, if it has been disabled. * @param handle Handle element to be enabled. */ enableHandle(handle) { if (this._disabledHandles.has(handle)) { this._disabledHandles.delete(handle); toggleNativeDragInteractions(handle, this.disabled); } } /** Sets the layout direction of the draggable item. */ withDirection(direction) { this._direction = direction; return this; } /** Sets the container that the item is part of. */ _withDropContainer(container) { this._dropContainer = container; } /** * Gets the current position in pixels the draggable outside of a drop container. */ getFreeDragPosition() { const position = this.isDragging() ? this._activeTransform : this._passiveTransform; return { x: position.x, y: position.y }; } /** * Sets the current position in pixels the draggable outside of a drop container. * @param value New position to be set. */ setFreeDragPosition(value) { this._activeTransform = { x: 0, y: 0 }; this._passiveTransform.x = value.x; this._passiveTransform.y = value.y; if (!this._dropContainer) { this._applyRootElementTransform(value.x, value.y); } return this; } /** * Sets the container into which to insert the preview element. * @param value Container into which to insert the preview. */ withPreviewContainer(value) { this._previewContainer = value; return this; } /** Updates the item's sort order based on the last-known pointer position. */ _sortFromLastPointerPosition() { const position = this._lastKnownPointerPosition; if (position && this._dropContainer) { this._updateActiveDropContainer(this._getConstrainedPointerPosition(position), position); } } /** Unsubscribes from the global subscriptions. */ _removeListeners() { this._pointerMoveSubscription.unsubscribe(); this._pointerUpSubscription.unsubscribe(); this._scrollSubscription.unsubscribe(); this._cleanupShadowRootSelectStart?.(); this._cleanupShadowRootSelectStart = void 0; } /** Destroys the preview element and its ViewRef. */ _destroyPreview() { this._preview?.destroy(); this._preview = null; } /** Destroys the placeholder element and its ViewRef. */ _destroyPlaceholder() { this._anchor?.remove(); this._placeholder?.remove(); this._placeholderRef?.destroy(); this._placeholder = this._anchor = this._placeholderRef = null; } /** Handler for the `mousedown`/`touchstart` events. */ _pointerDown = (event) => { this.beforeStarted.next(); if (this._handles.length) { const targetHandle = this._getTargetHandle(event); if (targetHandle && !this._disabledHandles.has(targetHandle) && !this.disabled) { this._initializeDragSequence(targetHandle, event); } } else if (!this.disabled) { this._initializeDragSequence(this._rootElement, event); } }; /** Handler that is invoked when the user moves their pointer after they've initiated a drag. */ _pointerMove = (event) => { const pointerPosition = this._getPointerPositionOnPage(event); if (!this._hasStartedDragging()) { const distanceX = Math.abs(pointerPosition.x - this._pickupPositionOnPage.x); const distanceY = Math.abs(pointerPosition.y - this._pickupPositionOnPage.y); const isOverThreshold = distanceX + distanceY >= this._config.dragStartThreshold; if (isOverThreshold) { const isDelayElapsed = Date.now() >= this._dragStartTime + this._getDragStartDelay(event); const container = this._dropContainer; if (!isDelayElapsed) { this._endDragSequence(event); return; } if (!container || !container.isDragging() && !container.isReceiving()) { if (event.cancelable) { event.preventDefault(); } this._hasStartedDragging.set(true); this._ngZone.run(() => this._startDragSequence(event)); } } return; } if (event.cancelable) { event.preventDefault(); } const constrainedPointerPosition = this._getConstrainedPointerPosition(pointerPosition); this._hasMoved = true; this._lastKnownPointerPosition = pointerPosition; this._updatePointerDirectionDelta(constrainedPointerPosition); if (this._dropContainer) { this._updateActiveDropContainer(constrainedPointerPosition, pointerPosition); } else { const offset = this.constrainPosition ? this._initialDomRect : this._pickupPositionOnPage; const activeTransform = this._activeTransform; activeTransform.x = constrainedPointerPosition.x - offset.x + this._passiveTransform.x; activeTransform.y = constrainedPointerPosition.y - offset.y + this._passiveTransform.y; this._applyRootElementTransform(activeTransform.x, activeTransform.y); } if (this._moveEvents.observers.length) { this._ngZone.run(() => { this._moveEvents.next({ source: this, pointerPosition: constrainedPointerPosition, event, distance: this._getDragDistance(constrainedPointerPosition), delta: this._pointerDirectionDelta }); }); } }; /** Handler that is invoked when the user lifts their pointer up, after initiating a drag. */ _pointerUp = (event) => { this._endDragSequence(event); }; /** * Clears subscriptions and stops the dragging sequence. * @param event Browser event object that ended the sequence. */ _endDragSequence(event) { if (!this._dragDropRegistry.isDragging(this)) { return; } this._removeListeners(); this._dragDropRegistry.stopDragging(this); this._toggleNativeDragInteractions(); if (this._handles) { this._rootElement.style.webkitTapHighlightColor = this._rootElementTapHighlight; } if (!this._hasStartedDragging()) { return; } this.released.next({ source: this, event }); if (this._dropContainer) { this._dropContainer._stopScrolling(); this._animatePreviewToPlaceholder().then(() => { this._cleanupDragArtifacts(event); this._cleanupCachedDimensions(); this._dragDropRegistry.stopDragging(this); }); } else { this._passiveTransform.x = this._activeTransform.x; const pointerPosition = this._getPointerPositionOnPage(event); this._passiveTransform.y = this._activeTransform.y; this._ngZone.run(() => { this.ended.next({ source: this, distance: this._getDragDistance(pointerPosition), dropPoint: pointerPosition, event }); }); this._cleanupCachedDimensions(); this._dragDropRegistry.stopDragging(this); } } /** Starts the dragging sequence. */ _startDragSequence(event) { if (isTouchEvent(event)) { this._lastTouchEventTime = Date.now(); } this._toggleNativeDragInteractions(); const shadowRoot = this._getShadowRoot(); const dropContainer = this._dropContainer; if (shadowRoot) { this._ngZone.runOutsideAngular(() => { this._cleanupShadowRootSelectStart = this._renderer.listen(shadowRoot, "selectstart", shadowDomSelectStart, activeCapturingEventOptions$1); }); } if (dropContainer) { const element = this._rootElement; const parent = element.parentNode; const placeholder = this._placeholder = this._createPlaceholderElement(); const marker = this._marker = this._marker || this._document.createComment(typeof ngDevMode === "undefined" || ngDevMode ? "cdk-drag-marker" : ""); parent.insertBefore(marker, element); this._initialTransform = element.style.transform || ""; this._preview = new PreviewRef(this._document, this._rootElement, this._direction, this._initialDomRect, this._previewTemplate || null, this.previewClass || null, this._pickupPositionOnPage, this._initialTransform, this._config.zIndex || 1e3, this._renderer); this._preview.attach(this._getPreviewInsertionPoint(parent, shadowRoot)); toggleVisibility(element, false, dragImportantProperties); this._document.body.appendChild(parent.replaceChild(placeholder, element)); this.started.next({ source: this, event }); dropContainer.start(); this._initialContainer = dropContainer; this._initialIndex = dropContainer.getItemIndex(this); } else { this.started.next({ source: this, event }); this._initialContainer = this._initialIndex = void 0; } this._parentPositions.cache(dropContainer ? dropContainer.getScrollableParents() : []); } /** * Sets up the different variables and subscriptions * that will be necessary for the dragging sequence. * @param referenceElement Element that started the drag sequence. * @param event Browser event object that started the sequence. */ _initializeDragSequence(referenceElement, event) { if (this._parentDragRef) { event.stopPropagation(); } const isDragging = this.isDragging(); const isTouchSequence = isTouchEvent(event); const isAuxiliaryMouseButton = !isTouchSequence && event.button !== 0; const rootElement = this._rootElement; const target = _getEventTarget(event); const isSyntheticEvent = !isTouchSequence && this._lastTouchEventTime && this._lastTouchEventTime + MOUSE_EVENT_IGNORE_TIME > Date.now(); const isFakeEvent = isTouchSequence ? isFakeTouchstartFromScreenReader(event) : isFakeMousedownFromScreenReader(event); if (target && target.draggable && event.type === "mousedown") { event.preventDefault(); } if (isDragging || isAuxiliaryMouseButton || isSyntheticEvent || isFakeEvent) { return; } if (this._handles.length) { const rootStyles = rootElement.style; this._rootElementTapHighlight = rootStyles.webkitTapHighlightColor || ""; rootStyles.webkitTapHighlightColor = "transparent"; } this._hasMoved = false; this._hasStartedDragging.set(this._hasMoved); this._removeListeners(); this._initialDomRect = this._rootElement.getBoundingClientRect(); this._pointerMoveSubscription = this._dragDropRegistry.pointerMove.subscribe(this._pointerMove); this._pointerUpSubscription = this._dragDropRegistry.pointerUp.subscribe(this._pointerUp); this._scrollSubscription = this._dragDropRegistry.scrolled(this._getShadowRoot()).subscribe((scrollEvent) => this._updateOnScroll(scrollEvent)); if (this._boundaryElement) { this._boundaryRect = getMutableClientRect(this._boundaryElement); } const previewTemplate = this._previewTemplate; this._pickupPositionInElement = previewTemplate && previewTemplate.template && !previewTemplate.matchSize ? { x: 0, y: 0 } : this._getPointerPositionInElement(this._initialDomRect, referenceElement, event); const pointerPosition = this._pickupPositionOnPage = this._lastKnownPointerPosition = this._getPointerPositionOnPage(event); this._pointerDirectionDelta = { x: 0, y: 0 }; this._pointerPositionAtLastDirectionChange = { x: pointerPosition.x, y: pointerPosition.y }; this._dragStartTime = Date.now(); this._dragDropRegistry.startDragging(this, event); } /** Cleans up the DOM artifacts that were added to facilitate the element being dragged. */ _cleanupDragArtifacts(event) { toggleVisibility(this._rootElement, true, dragImportantProperties); this._marker.parentNode.replaceChild(this._rootElement, this._marker); this._destroyPreview(); this._destroyPlaceholder(); this._initialDomRect = this._boundaryRect = this._previewRect = this._initialTransform = void 0; this._ngZone.run(() => { const container = this._dropContainer; const currentIndex = container.getItemIndex(this); const pointerPosition = this._getPointerPositionOnPage(event); const distance = this._getDragDistance(pointerPosition); const isPointerOverContainer = container._isOverContainer(pointerPosition.x, pointerPosition.y); this.ended.next({ source: this, distance, dropPoint: pointerPosition, event }); this.dropped.next({ item: this, currentIndex, previousIndex: this._initialIndex, container, previousContainer: this._initialContainer, isPointerOverContainer, distance, dropPoint: pointerPosition, event }); container.drop(this, currentIndex, this._initialIndex, this._initialContainer, isPointerOverContainer, distance, pointerPosition, event); this._dropContainer = this._initialContainer; }); } /** * Updates the item's position in its drop container, or moves it * into a new one, depending on its current drag position. */ _updateActiveDropContainer({ x, y }, { x: rawX, y: rawY }) { let newContainer = this._initialContainer._getSiblingContainerFromPosition(this, x, y); if (!newContainer && this._dropContainer !== this._initialContainer && this._initialContainer._isOverContainer(x, y)) { newContainer = this._initialContainer; } if (newContainer && newContainer !== this._dropContainer) { this._ngZone.run(() => { const exitIndex = this._dropContainer.getItemIndex(this); const nextItemElement = this._dropContainer.getItemAtIndex(exitIndex + 1)?.getVisibleElement() || null; this.exited.next({ item: this, container: this._dropContainer }); this._dropContainer.exit(this); this._conditionallyInsertAnchor(newContainer, this._dropContainer, nextItemElement); this._dropContainer = newContainer; this._dropContainer.enter( this, x, y, // If we're re-entering the initial container and sorting is disabled, // put item the into its starting index to begin with. newContainer === this._initialContainer && newContainer.sortingDisabled ? this._initialIndex : void 0 ); this.entered.next({ item: this, container: newContainer, currentIndex: newContainer.getItemIndex(this) }); }); } if (this.isDragging()) { this._dropContainer._startScrollingIfNecessary(rawX, rawY); this._dropContainer._sortItem(this, x, y, this._pointerDirectionDelta); if (this.constrainPosition) { this._applyPreviewTransform(x, y); } else { this._applyPreviewTransform(x - this._pickupPositionInElement.x, y - this._pickupPositionInElement.y); } } } /** * Animates the preview element from its current position to the location of the drop placeholder. * @returns Promise that resolves when the animation completes. */ _animatePreviewToPlaceholder() { if (!this._hasMoved) { return Promise.resolve(); } const placeholderRect = this._placeholder.getBoundingClientRect(); this._preview.addClass("cdk-drag-animating"); this._applyPreviewTransform(placeholderRect.left, placeholderRect.top); const duration = this._preview.getTransitionDuration(); if (duration === 0) { return Promise.resolve(); } return this._ngZone.runOutsideAngular(() => { return new Promise((resolve) => { const handler = (event) => { if (!event || this._preview && _getEventTarget(event) === this._preview.element && event.propertyName === "transform") { cleanupListener(); resolve(); clearTimeout(timeout); } }; const timeout = setTimeout(handler, duration * 1.5); const cleanupListener = this._preview.addEventListener("transitionend", handler); }); }); } /** Creates an element that will be shown instead of the current element while dragging. */ _createPlaceholderElement() { const placeholderConfig = this._placeholderTemplate; const placeholderTemplate = placeholderConfig ? placeholderConfig.template : null; let placeholder; if (placeholderTemplate) { this._placeholderRef = placeholderConfig.viewContainer.createEmbeddedView(placeholderTemplate, placeholderConfig.context); this._placeholderRef.detectChanges(); placeholder = getRootNode(this._placeholderRef, this._document); } else { placeholder = deepCloneNode(this._rootElement); } placeholder.style.pointerEvents = "none"; placeholder.classList.add(PLACEHOLDER_CLASS); return placeholder; } /** * Figures out the coordinates at which an element was picked up. * @param referenceElement Element that initiated the dragging. * @param event Event that initiated the dragging. */ _getPointerPositionInElement(elementRect, referenceElement, event) { const handleElement = referenceElement === this._rootElement ? null : referenceElement; const referenceRect = handleElement ? handleElement.getBoundingClientRect() : elementRect; const point = isTouchEvent(event) ? event.targetTouches[0] : event; const scrollPosition = this._getViewportScrollPosition(); const x = point.pageX - referenceRect.left - scrollPosition.left; const y = point.pageY - referenceRect.top - scrollPosition.top; return { x: referenceRect.left - elementRect.left + x, y: referenceRect.top - elementRect.top + y }; } /** Determines the point of the page that was touched by the user. */ _getPointerPositionOnPage(event) { const scrollPosition = this._getViewportScrollPosition(); const point = isTouchEvent(event) ? ( // `touches` will be empty for start/end events so we have to fall back to `changedTouches`. // Also note that on real devices we're guaranteed for either `touches` or `changedTouches` // to have a value, but Firefox in device emulation mode has a bug where both can be empty // for `touchstart` and `touchend` so we fall back to a dummy object in order to avoid // throwing an error. The value returned here will be incorrect, but since this only // breaks inside a developer tool and the value is only used for secondary information, // we can get away with it. See https://bugzilla.mozilla.org/show_bug.cgi?id=1615824. event.touches[0] || event.changedTouches[0] || { pageX: 0, pageY: 0 } ) : event; const x = point.pageX - scrollPosition.left; const y = point.pageY - scrollPosition.top; if (this._ownerSVGElement) { const svgMatrix = this._ownerSVGElement.getScreenCTM(); if (svgMatrix) { const svgPoint = this._ownerSVGElement.createSVGPoint(); svgPoint.x = x; svgPoint.y = y; return svgPoint.matrixTransform(svgMatrix.inverse()); } } return { x, y }; } /** Gets the pointer position on the page, accounting for any position constraints. */ _getConstrainedPointerPosition(point) { const dropContainerLock = this._dropContainer ? this._dropContainer.lockAxis : null; let { x, y } = this.constrainPosition ? this.constrainPosition(point, this, this._initialDomRect, this._pickupPositionInElement) : point; if (this.lockAxis === "x" || dropContainerLock === "x") { y = this._pickupPositionOnPage.y - (this.constrainPosition ? this._pickupPositionInElement.y : 0); } else if (this.lockAxis === "y" || dropContainerLock === "y") { x = this._pickupPositionOnPage.x - (this.constrainPosition ? this._pickupPositionInElement.x : 0); } if (this._boundaryRect) { const { x: pickupX, y: pickupY } = !this.constrainPosition ? this._pickupPositionInElement : { x: 0, y: 0 }; const boundaryRect = this._boundaryRect; const { width: previewWidth, height: previewHeight } = this._getPreviewRect(); const minY = boundaryRect.top + pickupY; const maxY = boundaryRect.bottom - (previewHeight - pickupY); const minX = boundaryRect.left + pickupX; const maxX = boundaryRect.right - (previewWidth - pickupX); x = clamp$1(x, minX, maxX); y = clamp$1(y, minY, maxY); } return { x, y }; } /** Updates the current drag delta, based on the user's current pointer position on the page. */ _updatePointerDirectionDelta(pointerPositionOnPage) { const { x, y } = pointerPositionOnPage; const delta = this._pointerDirectionDelta; const positionSinceLastChange = this._pointerPositionAtLastDirectionChange; const changeX = Math.abs(x - positionSinceLastChange.x); const changeY = Math.abs(y - positionSinceLastChange.y); if (changeX > this._config.pointerDirectionChangeThreshold) { delta.x = x > positionSinceLastChange.x ? 1 : -1; positionSinceLastChange.x = x; } if (changeY > this._config.pointerDirectionChangeThreshold) { delta.y = y > positionSinceLastChange.y ? 1 : -1; positionSinceLastChange.y = y; } return delta; } /** Toggles the native drag interactions, based on how many handles are registered. */ _toggleNativeDragInteractions() { if (!this._rootElement || !this._handles) { return; } const shouldEnable = this._handles.length > 0 || !this.isDragging(); if (shouldEnable !== this._nativeInteractionsEnabled) { this._nativeInteractionsEnabled = shouldEnable; toggleNativeDragInteractions(this._rootElement, shouldEnable); } } /** Removes the manually-added event listeners from the root element. */ _removeRootElementListeners() { this._rootElementCleanups?.forEach((cleanup) => cleanup()); this._rootElementCleanups = void 0; } /** * Applies a `transform` to the root element, taking into account any existing transforms on it. * @param x New transform value along the X axis. * @param y New transform value along the Y axis. */ _applyRootElementTransform(x, y) { const scale = 1 / this.scale; const transform = getTransform(x * scale, y * scale); const styles = this._rootElement.style; if (this._initialTransform == null) { this._initialTransform = styles.transform && styles.transform != "none" ? styles.transform : ""; } styles.transform = combineTransforms(transform, this._initialTransform); } /** * Applies a `transform` to the preview, taking into account any existing transforms on it. * @param x New transform value along the X axis. * @param y New transform value along the Y axis. */ _applyPreviewTransform(x, y) { const initialTransform = this._previewTemplate?.template ? void 0 : this._initialTransform; const transform = getTransform(x, y); this._preview.setTransform(combineTransforms(transform, initialTransform)); } /** * Gets the distance that the user has dragged during the current drag sequence. * @param currentPosition Current position of the user's pointer. */ _getDragDistance(currentPosition) { const pickupPosition = this._pickupPositionOnPage; if (pickupPosition) { return { x: currentPosition.x - pickupPosition.x, y: currentPosition.y - pickupPosition.y }; } return { x: 0, y: 0 }; } /** Cleans up any cached element dimensions that we don't need after dragging has stopped. */ _cleanupCachedDimensions() { this._boundaryRect = this._previewRect = void 0; this._parentPositions.clear(); } /** * Checks whether the element is still inside its boundary after the viewport has been resized. * If not, the position is adjusted so that the element fits again. */ _containInsideBoundaryOnResize() { let { x, y } = this._passiveTransform; if (x === 0 && y === 0 || this.isDragging() || !this._boundaryElement) { return; } const elementRect = this._rootElement.getBoundingClientRect(); const boundaryRect = this._boundaryElement.getBoundingClientRect(); if (boundaryRect.width === 0 && boundaryRect.height === 0 || elementRect.width === 0 && elementRect.height === 0) { return; } const leftOverflow = boundaryRect.left - elementRect.left; const rightOverflow = elementRect.right - boundaryRect.right; const topOverflow = boundaryRect.top - elementRect.top; const bottomOverflow = elementRect.bottom - boundaryRect.bottom; if (boundaryRect.width > elementRect.width) { if (leftOverflow > 0) { x += leftOverflow; } if (rightOverflow > 0) { x -= rightOverflow; } } else { x = 0; } if (boundaryRect.height > elementRect.height) { if (topOverflow > 0) { y += topOverflow; } if (bottomOverflow > 0) { y -= bottomOverflow; } } else { y = 0; } if (x !== this._passiveTransform.x || y !== this._passiveTransform.y) { this.setFreeDragPosition({ y, x }); } } /** Gets the drag start delay, based on the event type. */ _getDragStartDelay(event) { const value = this.dragStartDelay; if (typeof value === "number") { return value; } else if (isTouchEvent(event)) { return value.touch; } return value ? value.mouse : 0; } /** Updates the internal state of the draggable element when scrolling has occurred. */ _updateOnScroll(event) { const scrollDifference = this._parentPositions.handleScroll(event); if (scrollDifference) { const target = _getEventTarget(event); if (this._boundaryRect && target !== this._boundaryElement && target.contains(this._boundaryElement)) { adjustDomRect(this._boundaryRect, scrollDifference.top, scrollDifference.left); } this._pickupPositionOnPage.x += scrollDifference.left; this._pickupPositionOnPage.y += scrollDifference.top; if (!this._dropContainer) { this._activeTransform.x -= scrollDifference.left; this._activeTransform.y -= scrollDifference.top; this._applyRootElementTransform(this._activeTransform.x, this._activeTransform.y); } } } /** Gets the scroll position of the viewport. */ _getViewportScrollPosition() { return this._parentPositions.positions.get(this._document)?.scrollPosition || this._parentPositions.getViewportScrollPosition(); } /** * Lazily resolves and returns the shadow root of the element. We do this in a function, rather * than saving it in property directly on init, because we want to resolve it as late as possible * in order to ensure that the element has been moved into the shadow DOM. Doing it inside the * constructor might be too early if the element is inside of something like `ngFor` or `ngIf`. */ _getShadowRoot() { if (this._cachedShadowRoot === void 0) { this._cachedShadowRoot = _getShadowRoot(this._rootElement); } return this._cachedShadowRoot; } /** Gets the element into which the drag preview should be inserted. */ _getPreviewInsertionPoint(initialParent, shadowRoot) { const previewContainer = this._previewContainer || "global"; if (previewContainer === "parent") { return initialParent; } if (previewContainer === "global") { const documentRef = this._document; return shadowRoot || documentRef.fullscreenElement || documentRef.webkitFullscreenElement || documentRef.mozFullScreenElement || documentRef.msFullscreenElement || documentRef.body; } return coerceElement(previewContainer); } /** Lazily resolves and returns the dimensions of the preview. */ _getPreviewRect() { if (!this._previewRect || !this._previewRect.width && !this._previewRect.height) { this._previewRect = this._preview ? this._preview.getBoundingClientRect() : this._initialDomRect; } return this._previewRect; } /** Handles a native `dragstart` event. */ _nativeDragStart = (event) => { if (this._handles.length) { const targetHandle = this._getTargetHandle(event); if (targetHandle && !this._disabledHandles.has(targetHandle) && !this.disabled) { event.preventDefault(); } } else if (!this.disabled) { event.preventDefault(); } }; /** Gets a handle that is the target of an event. */ _getTargetHandle(event) { return this._handles.find((handle) => { return event.target && (event.target === handle || handle.contains(event.target)); }); } /** Inserts the anchor element, if it's valid. */ _conditionallyInsertAnchor(newContainer, exitContainer, nextItemElement) { if (newContainer === this._initialContainer) { this._anchor?.remove(); this._anchor = null; } else if (exitContainer === this._initialContainer && exitContainer.hasAnchor) { const anchor = this._anchor ??= deepCloneNode(this._placeholder); anchor.classList.remove(PLACEHOLDER_CLASS); anchor.classList.add("cdk-drag-anchor"); anchor.style.transform = ""; if (nextItemElement) { nextItemElement.before(anchor); } else { coerceElement(exitContainer.element).appendChild(anchor); } } } }; function clamp$1(value, min, max) { return Math.max(min, Math.min(max, value)); } function isTouchEvent(event) { return event.type[0] === "t"; } function shadowDomSelectStart(event) { event.preventDefault(); } function moveItemInArray(array, fromIndex, toIndex) { const from = clamp(fromIndex, array.length - 1); const to = clamp(toIndex, array.length - 1); if (from === to) { return; } const target = array[from]; const delta = to < from ? -1 : 1; for (let i = from; i !== to; i += delta) { array[i] = array[i + delta]; } array[to] = target; } function transferArrayItem(currentArray, targetArray, currentIndex, targetIndex) { const from = clamp(currentIndex, currentArray.length - 1); const to = clamp(targetIndex, targetArray.length); if (currentArray.length) { targetArray.splice(to, 0, currentArray.splice(from, 1)[0]); } } function copyArrayItem(currentArray, targetArray, currentIndex, targetIndex) { const to = clamp(targetIndex, targetArray.length); if (currentArray.length) { targetArray.splice(to, 0, currentArray[currentIndex]); } } function clamp(value, max) { return Math.max(0, Math.min(max, value)); } var SingleAxisSortStrategy = class { _dragDropRegistry; /** Root element container of the drop list. */ _element; /** Function used to determine if an item can be sorted into a specific index. */ _sortPredicate; /** Cache of the dimensions of all the items inside the container. */ _itemPositions = []; /** * Draggable items that are currently active inside the container. Includes the items * that were there at the start of the sequence, as well as any items that have been dragged * in, but haven't been dropped yet. */ _activeDraggables; /** Direction in which the list is oriented. */ orientation = "vertical"; /** Layout direction of the drop list. */ direction; constructor(_dragDropRegistry) { this._dragDropRegistry = _dragDropRegistry; } /** * Keeps track of the item that was last swapped with the dragged item, as well as what direction * the pointer was moving in when the swap occurred and whether the user's pointer continued to * overlap with the swapped item after the swapping occurred. */ _previousSwap = { drag: null, delta: 0, overlaps: false }; /** * To be called when the drag sequence starts. * @param items Items that are currently in the list. */ start(items) { this.withItems(items); } /** * To be called when an item is being sorted. * @param item Item to be sorted. * @param pointerX Position of the item along the X axis. * @param pointerY Position of the item along the Y axis. * @param pointerDelta Direction in which the pointer is moving along each axis. */ sort(item, pointerX, pointerY, pointerDelta) { const siblings = this._itemPositions; const newIndex = this._getItemIndexFromPointerPosition(item, pointerX, pointerY, pointerDelta); if (newIndex === -1 && siblings.length > 0) { return null; } const isHorizontal = this.orientation === "horizontal"; const currentIndex = siblings.findIndex((currentItem) => currentItem.drag === item); const siblingAtNewPosition = siblings[newIndex]; const currentPosition = siblings[currentIndex].clientRect; const newPosition = siblingAtNewPosition.clientRect; const delta = currentIndex > newIndex ? 1 : -1; const itemOffset = this._getItemOffsetPx(currentPosition, newPosition, delta); const siblingOffset = this._getSiblingOffsetPx(currentIndex, siblings, delta); const oldOrder = siblings.slice(); moveItemInArray(siblings, currentIndex, newIndex); siblings.forEach((sibling, index) => { if (oldOrder[index] === sibling) { return; } const isDraggedItem = sibling.drag === item; const offset = isDraggedItem ? itemOffset : siblingOffset; const elementToOffset = isDraggedItem ? item.getPlaceholderElement() : sibling.drag.getRootElement(); sibling.offset += offset; const transformAmount = Math.round(sibling.offset * (1 / sibling.drag.scale)); if (isHorizontal) { elementToOffset.style.transform = combineTransforms(`translate3d(${transformAmount}px, 0, 0)`, sibling.initialTransform); adjustDomRect(sibling.clientRect, 0, offset); } else { elementToOffset.style.transform = combineTransforms(`translate3d(0, ${transformAmount}px, 0)`, sibling.initialTransform); adjustDomRect(sibling.clientRect, offset, 0); } }); this._previousSwap.overlaps = isInsideClientRect(newPosition, pointerX, pointerY); this._previousSwap.drag = siblingAtNewPosition.drag; this._previousSwap.delta = isHorizontal ? pointerDelta.x : pointerDelta.y; return { previousIndex: currentIndex, currentIndex: newIndex }; } /** * Called when an item is being moved into the container. * @param item Item that was moved into the container. * @param pointerX Position of the item along the X axis. * @param pointerY Position of the item along the Y axis. * @param index Index at which the item entered. If omitted, the container will try to figure it * out automatically. */ enter(item, pointerX, pointerY, index) { const newIndex = index == null || index < 0 ? ( // We use the coordinates of where the item entered the drop // zone to figure out at which index it should be inserted. this._getItemIndexFromPointerPosition(item, pointerX, pointerY) ) : index; const activeDraggables = this._activeDraggables; const currentIndex = activeDraggables.indexOf(item); const placeholder = item.getPlaceholderElement(); let newPositionReference = activeDraggables[newIndex]; if (newPositionReference === item) { newPositionReference = activeDraggables[newIndex + 1]; } if (!newPositionReference && (newIndex == null || newIndex === -1 || newIndex < activeDraggables.length - 1) && this._shouldEnterAsFirstChild(pointerX, pointerY)) { newPositionReference = activeDraggables[0]; } if (currentIndex > -1) { activeDraggables.splice(currentIndex, 1); } if (newPositionReference && !this._dragDropRegistry.isDragging(newPositionReference)) { const element = newPositionReference.getRootElement(); element.parentElement.insertBefore(placeholder, element); activeDraggables.splice(newIndex, 0, item); } else { this._element.appendChild(placeholder); activeDraggables.push(item); } placeholder.style.transform = ""; this._cacheItemPositions(); } /** Sets the items that are currently part of the list. */ withItems(items) { this._activeDraggables = items.slice(); this._cacheItemPositions(); } /** Assigns a sort predicate to the strategy. */ withSortPredicate(predicate) { this._sortPredicate = predicate; } /** Resets the strategy to its initial state before dragging was started. */ reset() { this._activeDraggables?.forEach((item) => { const rootElement = item.getRootElement(); if (rootElement) { const initialTransform = this._itemPositions.find((p) => p.drag === item)?.initialTransform; rootElement.style.transform = initialTransform || ""; } }); this._itemPositions = []; this._activeDraggables = []; this._previousSwap.drag = null; this._previousSwap.delta = 0; this._previousSwap.overlaps = false; } /** * Gets a snapshot of items currently in the list. * Can include items that we dragged in from another list. */ getActiveItemsSnapshot() { return this._activeDraggables; } /** Gets the index of a specific item. */ getItemIndex(item) { return this._getVisualItemPositions().findIndex((currentItem) => currentItem.drag === item); } /** Gets the item at a specific index. */ getItemAtIndex(index) { return this._getVisualItemPositions()[index]?.drag || null; } /** Used to notify the strategy that the scroll position has changed. */ updateOnScroll(topDifference, leftDifference) { this._itemPositions.forEach(({ clientRect }) => { adjustDomRect(clientRect, topDifference, leftDifference); }); this._itemPositions.forEach(({ drag }) => { if (this._dragDropRegistry.isDragging(drag)) { drag._sortFromLastPointerPosition(); } }); } withElementContainer(container) { this._element = container; } /** Refreshes the position cache of the items and sibling containers. */ _cacheItemPositions() { const isHorizontal = this.orientation === "horizontal"; this._itemPositions = this._activeDraggables.map((drag) => { const elementToMeasure = drag.getVisibleElement(); return { drag, offset: 0, initialTransform: elementToMeasure.style.transform || "", clientRect: getMutableClientRect(elementToMeasure) }; }).sort((a, b) => { return isHorizontal ? a.clientRect.left - b.clientRect.left : a.clientRect.top - b.clientRect.top; }); } _getVisualItemPositions() { return this.orientation === "horizontal" && this.direction === "rtl" ? this._itemPositions.slice().reverse() : this._itemPositions; } /** * Gets the offset in pixels by which the item that is being dragged should be moved. * @param currentPosition Current position of the item. * @param newPosition Position of the item where the current item should be moved. * @param delta Direction in which the user is moving. */ _getItemOffsetPx(currentPosition, newPosition, delta) { const isHorizontal = this.orientation === "horizontal"; let itemOffset = isHorizontal ? newPosition.left - currentPosition.left : newPosition.top - currentPosition.top; if (delta === -1) { itemOffset += isHorizontal ? newPosition.width - currentPosition.width : newPosition.height - currentPosition.height; } return itemOffset; } /** * Gets the offset in pixels by which the items that aren't being dragged should be moved. * @param currentIndex Index of the item currently being dragged. * @param siblings All of the items in the list. * @param delta Direction in which the user is moving. */ _getSiblingOffsetPx(currentIndex, siblings, delta) { const isHorizontal = this.orientation === "horizontal"; const currentPosition = siblings[currentIndex].clientRect; const immediateSibling = siblings[currentIndex + delta * -1]; let siblingOffset = currentPosition[isHorizontal ? "width" : "height"] * delta; if (immediateSibling) { const start = isHorizontal ? "left" : "top"; const end = isHorizontal ? "right" : "bottom"; if (delta === -1) { siblingOffset -= immediateSibling.clientRect[start] - currentPosition[end]; } else { siblingOffset += currentPosition[start] - immediateSibling.clientRect[end]; } } return siblingOffset; } /** * Checks if pointer is entering in the first position * @param pointerX Position of the user's pointer along the X axis. * @param pointerY Position of the user's pointer along the Y axis. */ _shouldEnterAsFirstChild(pointerX, pointerY) { if (!this._activeDraggables.length) { return false; } const itemPositions = this._itemPositions; const isHorizontal = this.orientation === "horizontal"; const reversed = itemPositions[0].drag !== this._activeDraggables[0]; if (reversed) { const lastItemRect = itemPositions[itemPositions.length - 1].clientRect; return isHorizontal ? pointerX >= lastItemRect.right : pointerY >= lastItemRect.bottom; } else { const firstItemRect = itemPositions[0].clientRect; return isHorizontal ? pointerX <= firstItemRect.left : pointerY <= firstItemRect.top; } } /** * Gets the index of an item in the drop container, based on the position of the user's pointer. * @param item Item that is being sorted. * @param pointerX Position of the user's pointer along the X axis. * @param pointerY Position of the user's pointer along the Y axis. * @param delta Direction in which the user is moving their pointer. */ _getItemIndexFromPointerPosition(item, pointerX, pointerY, delta) { const isHorizontal = this.orientation === "horizontal"; const index = this._itemPositions.findIndex(({ drag, clientRect }) => { if (drag === item) { return false; } if (delta) { const direction = isHorizontal ? delta.x : delta.y; if (drag === this._previousSwap.drag && this._previousSwap.overlaps && direction === this._previousSwap.delta) { return false; } } return isHorizontal ? ( // Round these down since most browsers report client rects with // sub-pixel precision, whereas the pointer coordinates are rounded to pixels. pointerX >= Math.floor(clientRect.left) && pointerX < Math.floor(clientRect.right) ) : pointerY >= Math.floor(clientRect.top) && pointerY < Math.floor(clientRect.bottom); }); return index === -1 || !this._sortPredicate(index, item) ? -1 : index; } }; var MixedSortStrategy = class { _document; _dragDropRegistry; /** Root element container of the drop list. */ _element; /** Function used to determine if an item can be sorted into a specific index. */ _sortPredicate; /** Lazily-resolved root node containing the list. Use `_getRootNode` to read this. */ _rootNode; /** * Draggable items that are currently active inside the container. Includes the items * that were there at the start of the sequence, as well as any items that have been dragged * in, but haven't been dropped yet. */ _activeItems; /** * Keeps track of the item that was last swapped with the dragged item, as well as what direction * the pointer was moving in when the swap occurred and whether the user's pointer continued to * overlap with the swapped item after the swapping occurred. */ _previousSwap = { drag: null, deltaX: 0, deltaY: 0, overlaps: false }; /** * Keeps track of the relationship between a node and its next sibling. This information * is used to restore the DOM to the order it was in before dragging started. */ _relatedNodes = []; constructor(_document, _dragDropRegistry) { this._document = _document; this._dragDropRegistry = _dragDropRegistry; } /** * To be called when the drag sequence starts. * @param items Items that are currently in the list. */ start(items) { const childNodes = this._element.childNodes; this._relatedNodes = []; for (let i = 0; i < childNodes.length; i++) { const node = childNodes[i]; this._relatedNodes.push([node, node.nextSibling]); } this.withItems(items); } /** * To be called when an item is being sorted. * @param item Item to be sorted. * @param pointerX Position of the item along the X axis. * @param pointerY Position of the item along the Y axis. * @param pointerDelta Direction in which the pointer is moving along each axis. */ sort(item, pointerX, pointerY, pointerDelta) { const newIndex = this._getItemIndexFromPointerPosition(item, pointerX, pointerY); const previousSwap = this._previousSwap; if (newIndex === -1 || this._activeItems[newIndex] === item) { return null; } const toSwapWith = this._activeItems[newIndex]; if (previousSwap.drag === toSwapWith && previousSwap.overlaps && previousSwap.deltaX === pointerDelta.x && previousSwap.deltaY === pointerDelta.y) { return null; } const previousIndex = this.getItemIndex(item); const current = item.getPlaceholderElement(); const overlapElement = toSwapWith.getRootElement(); if (newIndex > previousIndex) { overlapElement.after(current); } else { overlapElement.before(current); } moveItemInArray(this._activeItems, previousIndex, newIndex); const newOverlapElement = this._getRootNode().elementFromPoint(pointerX, pointerY); previousSwap.deltaX = pointerDelta.x; previousSwap.deltaY = pointerDelta.y; previousSwap.drag = toSwapWith; previousSwap.overlaps = overlapElement === newOverlapElement || overlapElement.contains(newOverlapElement); return { previousIndex, currentIndex: newIndex }; } /** * Called when an item is being moved into the container. * @param item Item that was moved into the container. * @param pointerX Position of the item along the X axis. * @param pointerY Position of the item along the Y axis. * @param index Index at which the item entered. If omitted, the container will try to figure it * out automatically. */ enter(item, pointerX, pointerY, index) { const currentIndex = this._activeItems.indexOf(item); if (currentIndex > -1) { this._activeItems.splice(currentIndex, 1); } let enterIndex = index == null || index < 0 ? this._getItemIndexFromPointerPosition(item, pointerX, pointerY) : index; if (enterIndex === -1) { enterIndex = this._getClosestItemIndexToPointer(item, pointerX, pointerY); } const targetItem = this._activeItems[enterIndex]; if (targetItem && !this._dragDropRegistry.isDragging(targetItem)) { this._activeItems.splice(enterIndex, 0, item); targetItem.getRootElement().before(item.getPlaceholderElement()); } else { this._activeItems.push(item); this._element.appendChild(item.getPlaceholderElement()); } } /** Sets the items that are currently part of the list. */ withItems(items) { this._activeItems = items.slice(); } /** Assigns a sort predicate to the strategy. */ withSortPredicate(predicate) { this._sortPredicate = predicate; } /** Resets the strategy to its initial state before dragging was started. */ reset() { const root = this._element; const previousSwap = this._previousSwap; for (let i = this._relatedNodes.length - 1; i > -1; i--) { const [node, nextSibling] = this._relatedNodes[i]; if (node.parentNode === root && node.nextSibling !== nextSibling) { if (nextSibling === null) { root.appendChild(node); } else if (nextSibling.parentNode === root) { root.insertBefore(node, nextSibling); } } } this._relatedNodes = []; this._activeItems = []; previousSwap.drag = null; previousSwap.deltaX = previousSwap.deltaY = 0; previousSwap.overlaps = false; } /** * Gets a snapshot of items currently in the list. * Can include items that we dragged in from another list. */ getActiveItemsSnapshot() { return this._activeItems; } /** Gets the index of a specific item. */ getItemIndex(item) { return this._activeItems.indexOf(item); } /** Gets the item at a specific index. */ getItemAtIndex(index) { return this._activeItems[index] || null; } /** Used to notify the strategy that the scroll position has changed. */ updateOnScroll() { this._activeItems.forEach((item) => { if (this._dragDropRegistry.isDragging(item)) { item._sortFromLastPointerPosition(); } }); } withElementContainer(container) { if (container !== this._element) { this._element = container; this._rootNode = void 0; } } /** * Gets the index of an item in the drop container, based on the position of the user's pointer. * @param item Item that is being sorted. * @param pointerX Position of the user's pointer along the X axis. * @param pointerY Position of the user's pointer along the Y axis. * @param delta Direction in which the user is moving their pointer. */ _getItemIndexFromPointerPosition(item, pointerX, pointerY) { const elementAtPoint = this._getRootNode().elementFromPoint(Math.floor(pointerX), Math.floor(pointerY)); const index = elementAtPoint ? this._activeItems.findIndex((item2) => { const root = item2.getRootElement(); return elementAtPoint === root || root.contains(elementAtPoint); }) : -1; return index === -1 || !this._sortPredicate(index, item) ? -1 : index; } /** Lazily resolves the list's root node. */ _getRootNode() { if (!this._rootNode) { this._rootNode = _getShadowRoot(this._element) || this._document; } return this._rootNode; } /** * Finds the index of the item that's closest to the item being dragged. * @param item Item being dragged. * @param pointerX Position of the user's pointer along the X axis. * @param pointerY Position of the user's pointer along the Y axis. */ _getClosestItemIndexToPointer(item, pointerX, pointerY) { if (this._activeItems.length === 0) { return -1; } if (this._activeItems.length === 1) { return 0; } let minDistance = Infinity; let minIndex = -1; for (let i = 0; i < this._activeItems.length; i++) { const current = this._activeItems[i]; if (current !== item) { const { x, y } = current.getRootElement().getBoundingClientRect(); const distance = Math.hypot(pointerX - x, pointerY - y); if (distance < minDistance) { minDistance = distance; minIndex = i; } } } return minIndex; } }; var DROP_PROXIMITY_THRESHOLD = 0.05; var SCROLL_PROXIMITY_THRESHOLD = 0.05; var AutoScrollVerticalDirection; (function(AutoScrollVerticalDirection2) { AutoScrollVerticalDirection2[AutoScrollVerticalDirection2["NONE"] = 0] = "NONE"; AutoScrollVerticalDirection2[AutoScrollVerticalDirection2["UP"] = 1] = "UP"; AutoScrollVerticalDirection2[AutoScrollVerticalDirection2["DOWN"] = 2] = "DOWN"; })(AutoScrollVerticalDirection || (AutoScrollVerticalDirection = {})); var AutoScrollHorizontalDirection; (function(AutoScrollHorizontalDirection2) { AutoScrollHorizontalDirection2[AutoScrollHorizontalDirection2["NONE"] = 0] = "NONE"; AutoScrollHorizontalDirection2[AutoScrollHorizontalDirection2["LEFT"] = 1] = "LEFT"; AutoScrollHorizontalDirection2[AutoScrollHorizontalDirection2["RIGHT"] = 2] = "RIGHT"; })(AutoScrollHorizontalDirection || (AutoScrollHorizontalDirection = {})); var DropListRef = class { _dragDropRegistry; _ngZone; _viewportRuler; /** Element that the drop list is attached to. */ element; /** Whether starting a dragging sequence from this container is disabled. */ disabled = false; /** Whether sorting items within the list is disabled. */ sortingDisabled = false; /** Locks the position of the draggable elements inside the container along the specified axis. */ lockAxis = null; /** * Whether auto-scrolling the view when the user * moves their pointer close to the edges is disabled. */ autoScrollDisabled = false; /** Number of pixels to scroll for each frame when auto-scrolling an element. */ autoScrollStep = 2; /** * Whether the items in the list should leave an anchor node when leaving the initial container. */ hasAnchor = false; /** * Function that is used to determine whether an item * is allowed to be moved into a drop container. */ enterPredicate = () => true; /** Function that is used to determine whether an item can be sorted into a particular index. */ sortPredicate = () => true; /** Emits right before dragging has started. */ beforeStarted = new Subject(); /** * Emits when the user has moved a new drag item into this container. */ entered = new Subject(); /** * Emits when the user removes an item from the container * by dragging it into another container. */ exited = new Subject(); /** Emits when the user drops an item inside the container. */ dropped = new Subject(); /** Emits as the user is swapping items while actively dragging. */ sorted = new Subject(); /** Emits when a dragging sequence is started in a list connected to the current one. */ receivingStarted = new Subject(); /** Emits when a dragging sequence is stopped from a list connected to the current one. */ receivingStopped = new Subject(); /** Arbitrary data that can be attached to the drop list. */ data; /** Element that is the direct parent of the drag items. */ _container; /** Whether an item in the list is being dragged. */ _isDragging = false; /** Keeps track of the positions of any parent scrollable elements. */ _parentPositions; /** Strategy being used to sort items within the list. */ _sortStrategy; /** Cached `DOMRect` of the drop list. */ _domRect; /** Draggable items in the container. */ _draggables = []; /** Drop lists that are connected to the current one. */ _siblings = []; /** Connected siblings that currently have a dragged item. */ _activeSiblings = /* @__PURE__ */ new Set(); /** Subscription to the window being scrolled. */ _viewportScrollSubscription = Subscription.EMPTY; /** Vertical direction in which the list is currently scrolling. */ _verticalScrollDirection = AutoScrollVerticalDirection.NONE; /** Horizontal direction in which the list is currently scrolling. */ _horizontalScrollDirection = AutoScrollHorizontalDirection.NONE; /** Node that is being auto-scrolled. */ _scrollNode; /** Used to signal to the current auto-scroll sequence when to stop. */ _stopScrollTimers = new Subject(); /** Shadow root of the current element. Necessary for `elementFromPoint` to resolve correctly. */ _cachedShadowRoot = null; /** Reference to the document. */ _document; /** Elements that can be scrolled while the user is dragging. */ _scrollableElements = []; /** Initial value for the element's `scroll-snap-type` style. */ _initialScrollSnap; /** Direction of the list's layout. */ _direction = "ltr"; constructor(element, _dragDropRegistry, _document, _ngZone, _viewportRuler) { this._dragDropRegistry = _dragDropRegistry; this._ngZone = _ngZone; this._viewportRuler = _viewportRuler; const coercedElement = this.element = coerceElement(element); this._document = _document; this.withOrientation("vertical").withElementContainer(coercedElement); _dragDropRegistry.registerDropContainer(this); this._parentPositions = new ParentPositionTracker(_document); } /** Removes the drop list functionality from the DOM element. */ dispose() { this._stopScrolling(); this._stopScrollTimers.complete(); this._viewportScrollSubscription.unsubscribe(); this.beforeStarted.complete(); this.entered.complete(); this.exited.complete(); this.dropped.complete(); this.sorted.complete(); this.receivingStarted.complete(); this.receivingStopped.complete(); this._activeSiblings.clear(); this._scrollNode = null; this._parentPositions.clear(); this._dragDropRegistry.removeDropContainer(this); } /** Whether an item from this list is currently being dragged. */ isDragging() { return this._isDragging; } /** Starts dragging an item. */ start() { this._draggingStarted(); this._notifyReceivingSiblings(); } /** * Attempts to move an item into the container. * @param item Item that was moved into the container. * @param pointerX Position of the item along the X axis. * @param pointerY Position of the item along the Y axis. * @param index Index at which the item entered. If omitted, the container will try to figure it * out automatically. */ enter(item, pointerX, pointerY, index) { this._draggingStarted(); if (index == null && this.sortingDisabled) { index = this._draggables.indexOf(item); } this._sortStrategy.enter(item, pointerX, pointerY, index); this._cacheParentPositions(); this._notifyReceivingSiblings(); this.entered.next({ item, container: this, currentIndex: this.getItemIndex(item) }); } /** * Removes an item from the container after it was dragged into another container by the user. * @param item Item that was dragged out. */ exit(item) { this._reset(); this.exited.next({ item, container: this }); } /** * Drops an item into this container. * @param item Item being dropped into the container. * @param currentIndex Index at which the item should be inserted. * @param previousIndex Index of the item when dragging started. * @param previousContainer Container from which the item got dragged in. * @param isPointerOverContainer Whether the user's pointer was over the * container when the item was dropped. * @param distance Distance the user has dragged since the start of the dragging sequence. * @param event Event that triggered the dropping sequence. * * @breaking-change 15.0.0 `previousIndex` and `event` parameters to become required. */ drop(item, currentIndex, previousIndex, previousContainer, isPointerOverContainer, distance, dropPoint, event = {}) { this._reset(); this.dropped.next({ item, currentIndex, previousIndex, container: this, previousContainer, isPointerOverContainer, distance, dropPoint, event }); } /** * Sets the draggable items that are a part of this list. * @param items Items that are a part of this list. */ withItems(items) { const previousItems = this._draggables; this._draggables = items; items.forEach((item) => item._withDropContainer(this)); if (this.isDragging()) { const draggedItems = previousItems.filter((item) => item.isDragging()); if (draggedItems.every((item) => items.indexOf(item) === -1)) { this._reset(); } else { this._sortStrategy.withItems(this._draggables); } } return this; } /** Sets the layout direction of the drop list. */ withDirection(direction) { this._direction = direction; if (this._sortStrategy instanceof SingleAxisSortStrategy) { this._sortStrategy.direction = direction; } return this; } /** * Sets the containers that are connected to this one. When two or more containers are * connected, the user will be allowed to transfer items between them. * @param connectedTo Other containers that the current containers should be connected to. */ connectedTo(connectedTo) { this._siblings = connectedTo.slice(); return this; } /** * Sets the orientation of the container. * @param orientation New orientation for the container. */ withOrientation(orientation) { if (orientation === "mixed") { this._sortStrategy = new MixedSortStrategy(this._document, this._dragDropRegistry); } else { const strategy = new SingleAxisSortStrategy(this._dragDropRegistry); strategy.direction = this._direction; strategy.orientation = orientation; this._sortStrategy = strategy; } this._sortStrategy.withElementContainer(this._container); this._sortStrategy.withSortPredicate((index, item) => this.sortPredicate(index, item, this)); return this; } /** * Sets which parent elements are can be scrolled while the user is dragging. * @param elements Elements that can be scrolled. */ withScrollableParents(elements) { const element = this._container; this._scrollableElements = elements.indexOf(element) === -1 ? [element, ...elements] : elements.slice(); return this; } /** * Configures the drop list so that a different element is used as the container for the * dragged items. This is useful for the cases when one might not have control over the * full DOM that sets up the dragging. * Note that the alternate container needs to be a descendant of the drop list. * @param container New element container to be assigned. */ withElementContainer(container) { if (container === this._container) { return this; } const element = coerceElement(this.element); if ((typeof ngDevMode === "undefined" || ngDevMode) && container !== element && !element.contains(container)) { throw new Error("Invalid DOM structure for drop list. Alternate container element must be a descendant of the drop list."); } const oldContainerIndex = this._scrollableElements.indexOf(this._container); const newContainerIndex = this._scrollableElements.indexOf(container); if (oldContainerIndex > -1) { this._scrollableElements.splice(oldContainerIndex, 1); } if (newContainerIndex > -1) { this._scrollableElements.splice(newContainerIndex, 1); } if (this._sortStrategy) { this._sortStrategy.withElementContainer(container); } this._cachedShadowRoot = null; this._scrollableElements.unshift(container); this._container = container; return this; } /** Gets the scrollable parents that are registered with this drop container. */ getScrollableParents() { return this._scrollableElements; } /** * Figures out the index of an item in the container. * @param item Item whose index should be determined. */ getItemIndex(item) { return this._isDragging ? this._sortStrategy.getItemIndex(item) : this._draggables.indexOf(item); } /** * Gets the item at a specific index. * @param index Index at which to retrieve the item. */ getItemAtIndex(index) { return this._isDragging ? this._sortStrategy.getItemAtIndex(index) : this._draggables[index] || null; } /** * Whether the list is able to receive the item that * is currently being dragged inside a connected drop list. */ isReceiving() { return this._activeSiblings.size > 0; } /** * Sorts an item inside the container based on its position. * @param item Item to be sorted. * @param pointerX Position of the item along the X axis. * @param pointerY Position of the item along the Y axis. * @param pointerDelta Direction in which the pointer is moving along each axis. */ _sortItem(item, pointerX, pointerY, pointerDelta) { if (this.sortingDisabled || !this._domRect || !isPointerNearDomRect(this._domRect, DROP_PROXIMITY_THRESHOLD, pointerX, pointerY)) { return; } const result = this._sortStrategy.sort(item, pointerX, pointerY, pointerDelta); if (result) { this.sorted.next({ previousIndex: result.previousIndex, currentIndex: result.currentIndex, container: this, item }); } } /** * Checks whether the user's pointer is close to the edges of either the * viewport or the drop list and starts the auto-scroll sequence. * @param pointerX User's pointer position along the x axis. * @param pointerY User's pointer position along the y axis. */ _startScrollingIfNecessary(pointerX, pointerY) { if (this.autoScrollDisabled) { return; } let scrollNode; let verticalScrollDirection = AutoScrollVerticalDirection.NONE; let horizontalScrollDirection = AutoScrollHorizontalDirection.NONE; this._parentPositions.positions.forEach((position, element) => { if (element === this._document || !position.clientRect || scrollNode) { return; } if (isPointerNearDomRect(position.clientRect, DROP_PROXIMITY_THRESHOLD, pointerX, pointerY)) { [verticalScrollDirection, horizontalScrollDirection] = getElementScrollDirections(element, position.clientRect, this._direction, pointerX, pointerY); if (verticalScrollDirection || horizontalScrollDirection) { scrollNode = element; } } }); if (!verticalScrollDirection && !horizontalScrollDirection) { const { width, height } = this._viewportRuler.getViewportSize(); const domRect = { width, height, top: 0, right: width, bottom: height, left: 0 }; verticalScrollDirection = getVerticalScrollDirection(domRect, pointerY); horizontalScrollDirection = getHorizontalScrollDirection(domRect, pointerX); scrollNode = window; } if (scrollNode && (verticalScrollDirection !== this._verticalScrollDirection || horizontalScrollDirection !== this._horizontalScrollDirection || scrollNode !== this._scrollNode)) { this._verticalScrollDirection = verticalScrollDirection; this._horizontalScrollDirection = horizontalScrollDirection; this._scrollNode = scrollNode; if ((verticalScrollDirection || horizontalScrollDirection) && scrollNode) { this._ngZone.runOutsideAngular(this._startScrollInterval); } else { this._stopScrolling(); } } } /** Stops any currently-running auto-scroll sequences. */ _stopScrolling() { this._stopScrollTimers.next(); } /** Starts the dragging sequence within the list. */ _draggingStarted() { const styles = this._container.style; this.beforeStarted.next(); this._isDragging = true; if ((typeof ngDevMode === "undefined" || ngDevMode) && // Prevent the check from running on apps not using an alternate container. Ideally we // would always run it, but introducing it at this stage would be a breaking change. this._container !== coerceElement(this.element)) { for (const drag of this._draggables) { if (!drag.isDragging() && drag.getVisibleElement().parentNode !== this._container) { throw new Error("Invalid DOM structure for drop list. All items must be placed directly inside of the element container."); } } } this._initialScrollSnap = styles.msScrollSnapType || styles.scrollSnapType || ""; styles.scrollSnapType = styles.msScrollSnapType = "none"; this._sortStrategy.start(this._draggables); this._cacheParentPositions(); this._viewportScrollSubscription.unsubscribe(); this._listenToScrollEvents(); } /** Caches the positions of the configured scrollable parents. */ _cacheParentPositions() { this._parentPositions.cache(this._scrollableElements); this._domRect = this._parentPositions.positions.get(this._container).clientRect; } /** Resets the container to its initial state. */ _reset() { this._isDragging = false; const styles = this._container.style; styles.scrollSnapType = styles.msScrollSnapType = this._initialScrollSnap; this._siblings.forEach((sibling) => sibling._stopReceiving(this)); this._sortStrategy.reset(); this._stopScrolling(); this._viewportScrollSubscription.unsubscribe(); this._parentPositions.clear(); } /** Starts the interval that'll auto-scroll the element. */ _startScrollInterval = () => { this._stopScrolling(); interval(0, animationFrameScheduler).pipe(takeUntil(this._stopScrollTimers)).subscribe(() => { const node = this._scrollNode; const scrollStep = this.autoScrollStep; if (this._verticalScrollDirection === AutoScrollVerticalDirection.UP) { node.scrollBy(0, -scrollStep); } else if (this._verticalScrollDirection === AutoScrollVerticalDirection.DOWN) { node.scrollBy(0, scrollStep); } if (this._horizontalScrollDirection === AutoScrollHorizontalDirection.LEFT) { node.scrollBy(-scrollStep, 0); } else if (this._horizontalScrollDirection === AutoScrollHorizontalDirection.RIGHT) { node.scrollBy(scrollStep, 0); } }); }; /** * Checks whether the user's pointer is positioned over the container. * @param x Pointer position along the X axis. * @param y Pointer position along the Y axis. */ _isOverContainer(x, y) { return this._domRect != null && isInsideClientRect(this._domRect, x, y); } /** * Figures out whether an item should be moved into a sibling * drop container, based on its current position. * @param item Drag item that is being moved. * @param x Position of the item along the X axis. * @param y Position of the item along the Y axis. */ _getSiblingContainerFromPosition(item, x, y) { return this._siblings.find((sibling) => sibling._canReceive(item, x, y)); } /** * Checks whether the drop list can receive the passed-in item. * @param item Item that is being dragged into the list. * @param x Position of the item along the X axis. * @param y Position of the item along the Y axis. */ _canReceive(item, x, y) { if (!this._domRect || !isInsideClientRect(this._domRect, x, y) || !this.enterPredicate(item, this)) { return false; } const elementFromPoint = this._getShadowRoot().elementFromPoint(x, y); if (!elementFromPoint) { return false; } return elementFromPoint === this._container || this._container.contains(elementFromPoint); } /** * Called by one of the connected drop lists when a dragging sequence has started. * @param sibling Sibling in which dragging has started. */ _startReceiving(sibling, items) { const activeSiblings = this._activeSiblings; if (!activeSiblings.has(sibling) && items.every((item) => { return this.enterPredicate(item, this) || this._draggables.indexOf(item) > -1; })) { activeSiblings.add(sibling); this._cacheParentPositions(); this._listenToScrollEvents(); this.receivingStarted.next({ initiator: sibling, receiver: this, items }); } } /** * Called by a connected drop list when dragging has stopped. * @param sibling Sibling whose dragging has stopped. */ _stopReceiving(sibling) { this._activeSiblings.delete(sibling); this._viewportScrollSubscription.unsubscribe(); this.receivingStopped.next({ initiator: sibling, receiver: this }); } /** * Starts listening to scroll events on the viewport. * Used for updating the internal state of the list. */ _listenToScrollEvents() { this._viewportScrollSubscription = this._dragDropRegistry.scrolled(this._getShadowRoot()).subscribe((event) => { if (this.isDragging()) { const scrollDifference = this._parentPositions.handleScroll(event); if (scrollDifference) { this._sortStrategy.updateOnScroll(scrollDifference.top, scrollDifference.left); } } else if (this.isReceiving()) { this._cacheParentPositions(); } }); } /** * Lazily resolves and returns the shadow root of the element. We do this in a function, rather * than saving it in property directly on init, because we want to resolve it as late as possible * in order to ensure that the element has been moved into the shadow DOM. Doing it inside the * constructor might be too early if the element is inside of something like `ngFor` or `ngIf`. */ _getShadowRoot() { if (!this._cachedShadowRoot) { const shadowRoot = _getShadowRoot(this._container); this._cachedShadowRoot = shadowRoot || this._document; } return this._cachedShadowRoot; } /** Notifies any siblings that may potentially receive the item. */ _notifyReceivingSiblings() { const draggedItems = this._sortStrategy.getActiveItemsSnapshot().filter((item) => item.isDragging()); this._siblings.forEach((sibling) => sibling._startReceiving(this, draggedItems)); } }; function getVerticalScrollDirection(clientRect, pointerY) { const { top, bottom, height } = clientRect; const yThreshold = height * SCROLL_PROXIMITY_THRESHOLD; if (pointerY >= top - yThreshold && pointerY <= top + yThreshold) { return AutoScrollVerticalDirection.UP; } else if (pointerY >= bottom - yThreshold && pointerY <= bottom + yThreshold) { return AutoScrollVerticalDirection.DOWN; } return AutoScrollVerticalDirection.NONE; } function getHorizontalScrollDirection(clientRect, pointerX) { const { left, right, width } = clientRect; const xThreshold = width * SCROLL_PROXIMITY_THRESHOLD; if (pointerX >= left - xThreshold && pointerX <= left + xThreshold) { return AutoScrollHorizontalDirection.LEFT; } else if (pointerX >= right - xThreshold && pointerX <= right + xThreshold) { return AutoScrollHorizontalDirection.RIGHT; } return AutoScrollHorizontalDirection.NONE; } function getElementScrollDirections(element, clientRect, direction, pointerX, pointerY) { const computedVertical = getVerticalScrollDirection(clientRect, pointerY); const computedHorizontal = getHorizontalScrollDirection(clientRect, pointerX); let verticalScrollDirection = AutoScrollVerticalDirection.NONE; let horizontalScrollDirection = AutoScrollHorizontalDirection.NONE; if (computedVertical) { const scrollTop = element.scrollTop; if (computedVertical === AutoScrollVerticalDirection.UP) { if (scrollTop > 0) { verticalScrollDirection = AutoScrollVerticalDirection.UP; } } else if (element.scrollHeight - scrollTop > element.clientHeight) { verticalScrollDirection = AutoScrollVerticalDirection.DOWN; } } if (computedHorizontal) { const scrollLeft = element.scrollLeft; if (direction === "rtl") { if (computedHorizontal === AutoScrollHorizontalDirection.RIGHT) { if (scrollLeft < 0) { horizontalScrollDirection = AutoScrollHorizontalDirection.RIGHT; } } else if (element.scrollWidth + scrollLeft > element.clientWidth) { horizontalScrollDirection = AutoScrollHorizontalDirection.LEFT; } } else { if (computedHorizontal === AutoScrollHorizontalDirection.LEFT) { if (scrollLeft > 0) { horizontalScrollDirection = AutoScrollHorizontalDirection.LEFT; } } else if (element.scrollWidth - scrollLeft > element.clientWidth) { horizontalScrollDirection = AutoScrollHorizontalDirection.RIGHT; } } } return [verticalScrollDirection, horizontalScrollDirection]; } var capturingEventOptions = { capture: true }; var activeCapturingEventOptions = { passive: false, capture: true }; var _ResetsLoader = class __ResetsLoader { static ɵfac = function _ResetsLoader_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || __ResetsLoader)(); }; static ɵcmp = ɵɵdefineComponent({ type: __ResetsLoader, selectors: [["ng-component"]], hostAttrs: ["cdk-drag-resets-container", ""], decls: 0, vars: 0, template: function _ResetsLoader_Template(rf, ctx) { }, styles: ["@layer cdk-resets{.cdk-drag-preview{background:none;border:none;padding:0;color:inherit;inset:auto}}.cdk-drag-placeholder *,.cdk-drag-preview *{pointer-events:none !important}\n"], encapsulation: 2, changeDetection: 0 }); }; (() => { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(_ResetsLoader, [{ type: Component, args: [{ encapsulation: ViewEncapsulation.None, template: "", changeDetection: ChangeDetectionStrategy.OnPush, host: { "cdk-drag-resets-container": "" }, styles: ["@layer cdk-resets{.cdk-drag-preview{background:none;border:none;padding:0;color:inherit;inset:auto}}.cdk-drag-placeholder *,.cdk-drag-preview *{pointer-events:none !important}\n"] }] }], null, null); })(); var DragDropRegistry = class _DragDropRegistry { _ngZone = inject(NgZone); _document = inject(DOCUMENT); _styleLoader = inject(_CdkPrivateStyleLoader); _renderer = inject(RendererFactory2).createRenderer(null, null); _cleanupDocumentTouchmove; _scroll = new Subject(); /** Registered drop container instances. */ _dropInstances = /* @__PURE__ */ new Set(); /** Registered drag item instances. */ _dragInstances = /* @__PURE__ */ new Set(); /** Drag item instances that are currently being dragged. */ _activeDragInstances = signal([], ...ngDevMode ? [{ debugName: "_activeDragInstances" }] : []); /** Keeps track of the event listeners that we've bound to the `document`. */ _globalListeners; /** * Predicate function to check if an item is being dragged. Moved out into a property, * because it'll be called a lot and we don't want to create a new function every time. */ _draggingPredicate = (item) => item.isDragging(); /** * Map tracking DOM nodes and their corresponding drag directives. Note that this is different * from looking through the `_dragInstances` and getting their root node, because the root node * isn't necessarily the node that the directive is set on. */ _domNodesToDirectives = null; /** * Emits the `touchmove` or `mousemove` events that are dispatched * while the user is dragging a drag item instance. */ pointerMove = new Subject(); /** * Emits the `touchend` or `mouseup` events that are dispatched * while the user is dragging a drag item instance. */ pointerUp = new Subject(); constructor() { } /** Adds a drop container to the registry. */ registerDropContainer(drop) { if (!this._dropInstances.has(drop)) { this._dropInstances.add(drop); } } /** Adds a drag item instance to the registry. */ registerDragItem(drag) { this._dragInstances.add(drag); if (this._dragInstances.size === 1) { this._ngZone.runOutsideAngular(() => { this._cleanupDocumentTouchmove?.(); this._cleanupDocumentTouchmove = this._renderer.listen(this._document, "touchmove", this._persistentTouchmoveListener, activeCapturingEventOptions); }); } } /** Removes a drop container from the registry. */ removeDropContainer(drop) { this._dropInstances.delete(drop); } /** Removes a drag item instance from the registry. */ removeDragItem(drag) { this._dragInstances.delete(drag); this.stopDragging(drag); if (this._dragInstances.size === 0) { this._cleanupDocumentTouchmove?.(); } } /** * Starts the dragging sequence for a drag instance. * @param drag Drag instance which is being dragged. * @param event Event that initiated the dragging. */ startDragging(drag, event) { if (this._activeDragInstances().indexOf(drag) > -1) { return; } this._styleLoader.load(_ResetsLoader); this._activeDragInstances.update((instances) => [...instances, drag]); if (this._activeDragInstances().length === 1) { const isTouchEvent2 = event.type.startsWith("touch"); const endEventHandler = (e) => this.pointerUp.next(e); const toBind = [ // Use capturing so that we pick up scroll changes in any scrollable nodes that aren't // the document. See https://github.com/angular/components/issues/17144. ["scroll", (e) => this._scroll.next(e), capturingEventOptions], // Preventing the default action on `mousemove` isn't enough to disable text selection // on Safari so we need to prevent the selection event as well. Alternatively this can // be done by setting `user-select: none` on the `body`, however it has causes a style // recalculation which can be expensive on pages with a lot of elements. ["selectstart", this._preventDefaultWhileDragging, activeCapturingEventOptions] ]; if (isTouchEvent2) { toBind.push(["touchend", endEventHandler, capturingEventOptions], ["touchcancel", endEventHandler, capturingEventOptions]); } else { toBind.push(["mouseup", endEventHandler, capturingEventOptions]); } if (!isTouchEvent2) { toBind.push(["mousemove", (e) => this.pointerMove.next(e), activeCapturingEventOptions]); } this._ngZone.runOutsideAngular(() => { this._globalListeners = toBind.map(([name, handler, options]) => this._renderer.listen(this._document, name, handler, options)); }); } } /** Stops dragging a drag item instance. */ stopDragging(drag) { this._activeDragInstances.update((instances) => { const index = instances.indexOf(drag); if (index > -1) { instances.splice(index, 1); return [...instances]; } return instances; }); if (this._activeDragInstances().length === 0) { this._clearGlobalListeners(); } } /** Gets whether a drag item instance is currently being dragged. */ isDragging(drag) { return this._activeDragInstances().indexOf(drag) > -1; } /** * Gets a stream that will emit when any element on the page is scrolled while an item is being * dragged. * @param shadowRoot Optional shadow root that the current dragging sequence started from. * Top-level listeners won't pick up events coming from the shadow DOM so this parameter can * be used to include an additional top-level listener at the shadow root level. */ scrolled(shadowRoot) { const streams = [this._scroll]; if (shadowRoot && shadowRoot !== this._document) { streams.push(new Observable((observer) => { return this._ngZone.runOutsideAngular(() => { const cleanup = this._renderer.listen(shadowRoot, "scroll", (event) => { if (this._activeDragInstances().length) { observer.next(event); } }, capturingEventOptions); return () => { cleanup(); }; }); })); } return merge(...streams); } /** * Tracks the DOM node which has a draggable directive. * @param node Node to track. * @param dragRef Drag directive set on the node. */ registerDirectiveNode(node, dragRef) { this._domNodesToDirectives ??= /* @__PURE__ */ new WeakMap(); this._domNodesToDirectives.set(node, dragRef); } /** * Stops tracking a draggable directive node. * @param node Node to stop tracking. */ removeDirectiveNode(node) { this._domNodesToDirectives?.delete(node); } /** * Gets the drag directive corresponding to a specific DOM node, if any. * @param node Node for which to do the lookup. */ getDragDirectiveForNode(node) { return this._domNodesToDirectives?.get(node) || null; } ngOnDestroy() { this._dragInstances.forEach((instance) => this.removeDragItem(instance)); this._dropInstances.forEach((instance) => this.removeDropContainer(instance)); this._domNodesToDirectives = null; this._clearGlobalListeners(); this.pointerMove.complete(); this.pointerUp.complete(); } /** * Event listener that will prevent the default browser action while the user is dragging. * @param event Event whose default action should be prevented. */ _preventDefaultWhileDragging = (event) => { if (this._activeDragInstances().length > 0) { event.preventDefault(); } }; /** Event listener for `touchmove` that is bound even if no dragging is happening. */ _persistentTouchmoveListener = (event) => { if (this._activeDragInstances().length > 0) { if (this._activeDragInstances().some(this._draggingPredicate)) { event.preventDefault(); } this.pointerMove.next(event); } }; /** Clears out the global event listeners from the `document`. */ _clearGlobalListeners() { this._globalListeners?.forEach((cleanup) => cleanup()); this._globalListeners = void 0; } static ɵfac = function DragDropRegistry_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || _DragDropRegistry)(); }; static ɵprov = ɵɵdefineInjectable({ token: _DragDropRegistry, factory: _DragDropRegistry.ɵfac, providedIn: "root" }); }; (() => { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(DragDropRegistry, [{ type: Injectable, args: [{ providedIn: "root" }] }], () => [], null); })(); var DEFAULT_CONFIG = { dragStartThreshold: 5, pointerDirectionChangeThreshold: 5 }; var DragDrop = class _DragDrop { _document = inject(DOCUMENT); _ngZone = inject(NgZone); _viewportRuler = inject(ViewportRuler); _dragDropRegistry = inject(DragDropRegistry); _renderer = inject(RendererFactory2).createRenderer(null, null); constructor() { } /** * Turns an element into a draggable item. * @param element Element to which to attach the dragging functionality. * @param config Object used to configure the dragging behavior. */ createDrag(element, config = DEFAULT_CONFIG) { return new DragRef(element, config, this._document, this._ngZone, this._viewportRuler, this._dragDropRegistry, this._renderer); } /** * Turns an element into a drop list. * @param element Element to which to attach the drop list functionality. */ createDropList(element) { return new DropListRef(element, this._dragDropRegistry, this._document, this._ngZone, this._viewportRuler); } static ɵfac = function DragDrop_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || _DragDrop)(); }; static ɵprov = ɵɵdefineInjectable({ token: _DragDrop, factory: _DragDrop.ɵfac, providedIn: "root" }); }; (() => { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(DragDrop, [{ type: Injectable, args: [{ providedIn: "root" }] }], () => [], null); })(); var CDK_DRAG_PARENT = new InjectionToken("CDK_DRAG_PARENT"); function assertElementNode(node, name) { if (node.nodeType !== 1) { throw Error(`${name} must be attached to an element node. Currently attached to "${node.nodeName}".`); } } var CDK_DRAG_HANDLE = new InjectionToken("CdkDragHandle"); var CdkDragHandle = class _CdkDragHandle { element = inject(ElementRef); _parentDrag = inject(CDK_DRAG_PARENT, { optional: true, skipSelf: true }); _dragDropRegistry = inject(DragDropRegistry); /** Emits when the state of the handle has changed. */ _stateChanges = new Subject(); /** Whether starting to drag through this handle is disabled. */ get disabled() { return this._disabled; } set disabled(value) { this._disabled = value; this._stateChanges.next(this); } _disabled = false; constructor() { if (typeof ngDevMode === "undefined" || ngDevMode) { assertElementNode(this.element.nativeElement, "cdkDragHandle"); } this._parentDrag?._addHandle(this); } ngAfterViewInit() { if (!this._parentDrag) { let parent = this.element.nativeElement.parentElement; while (parent) { const ref = this._dragDropRegistry.getDragDirectiveForNode(parent); if (ref) { this._parentDrag = ref; ref._addHandle(this); break; } parent = parent.parentElement; } } } ngOnDestroy() { this._parentDrag?._removeHandle(this); this._stateChanges.complete(); } static ɵfac = function CdkDragHandle_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || _CdkDragHandle)(); }; static ɵdir = ɵɵdefineDirective({ type: _CdkDragHandle, selectors: [["", "cdkDragHandle", ""]], hostAttrs: [1, "cdk-drag-handle"], inputs: { disabled: [2, "cdkDragHandleDisabled", "disabled", booleanAttribute] }, features: [ɵɵProvidersFeature([{ provide: CDK_DRAG_HANDLE, useExisting: _CdkDragHandle }])] }); }; (() => { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(CdkDragHandle, [{ type: Directive, args: [{ selector: "[cdkDragHandle]", host: { "class": "cdk-drag-handle" }, providers: [{ provide: CDK_DRAG_HANDLE, useExisting: CdkDragHandle }] }] }], () => [], { disabled: [{ type: Input, args: [{ alias: "cdkDragHandleDisabled", transform: booleanAttribute }] }] }); })(); var CDK_DRAG_CONFIG = new InjectionToken("CDK_DRAG_CONFIG"); var CDK_DROP_LIST = new InjectionToken("CdkDropList"); var CdkDrag = class _CdkDrag { element = inject(ElementRef); dropContainer = inject(CDK_DROP_LIST, { optional: true, skipSelf: true }); _ngZone = inject(NgZone); _viewContainerRef = inject(ViewContainerRef); _dir = inject(Directionality, { optional: true }); _changeDetectorRef = inject(ChangeDetectorRef); _selfHandle = inject(CDK_DRAG_HANDLE, { optional: true, self: true }); _parentDrag = inject(CDK_DRAG_PARENT, { optional: true, skipSelf: true }); _dragDropRegistry = inject(DragDropRegistry); _destroyed = new Subject(); _handles = new BehaviorSubject([]); _previewTemplate; _placeholderTemplate; /** Reference to the underlying drag instance. */ _dragRef; /** Arbitrary data to attach to this drag instance. */ data; /** Locks the position of the dragged element along the specified axis. */ lockAxis = null; /** * Selector that will be used to determine the root draggable element, starting from * the `cdkDrag` element and going up the DOM. Passing an alternate root element is useful * when trying to enable dragging on an element that you might not have access to. */ rootElementSelector; /** * Node or selector that will be used to determine the element to which the draggable's * position will be constrained. If a string is passed in, it'll be used as a selector that * will be matched starting from the element's parent and going up the DOM until a match * has been found. */ boundaryElement; /** * Amount of milliseconds to wait after the user has put their * pointer down before starting to drag the element. */ dragStartDelay; /** * Sets the position of a `CdkDrag` that is outside of a drop container. * Can be used to restore the element's position for a returning user. */ freeDragPosition; /** Whether starting to drag this element is disabled. */ get disabled() { return this._disabled || !!(this.dropContainer && this.dropContainer.disabled); } set disabled(value) { this._disabled = value; this._dragRef.disabled = this._disabled; } _disabled; /** * Function that can be used to customize the logic of how the position of the drag item * is limited while it's being dragged. Gets called with a point containing the current position * of the user's pointer on the page, a reference to the item being dragged and its dimensions. * Should return a point describing where the item should be rendered. */ constrainPosition; /** Class to be added to the preview element. */ previewClass; /** * Configures the place into which the preview of the item will be inserted. Can be configured * globally through `CDK_DROP_LIST`. Possible values: * - `global` - Preview will be inserted at the bottom of the ``. The advantage is that * you don't have to worry about `overflow: hidden` or `z-index`, but the item won't retain * its inherited styles. * - `parent` - Preview will be inserted into the parent of the drag item. The advantage is that * inherited styles will be preserved, but it may be clipped by `overflow: hidden` or not be * visible due to `z-index`. Furthermore, the preview is going to have an effect over selectors * like `:nth-child` and some flexbox configurations. * - `ElementRef | HTMLElement` - Preview will be inserted into a specific element. * Same advantages and disadvantages as `parent`. */ previewContainer; /** * If the parent of the dragged element has a `scale` transform, it can throw off the * positioning when the user starts dragging. Use this input to notify the CDK of the scale. */ scale = 1; /** Emits when the user starts dragging the item. */ started = new EventEmitter(); /** Emits when the user has released a drag item, before any animations have started. */ released = new EventEmitter(); /** Emits when the user stops dragging an item in the container. */ ended = new EventEmitter(); /** Emits when the user has moved the item into a new container. */ entered = new EventEmitter(); /** Emits when the user removes the item its container by dragging it into another container. */ exited = new EventEmitter(); /** Emits when the user drops the item inside a container. */ dropped = new EventEmitter(); /** * Emits as the user is dragging the item. Use with caution, * because this event will fire for every pixel that the user has dragged. */ moved = new Observable((observer) => { const subscription = this._dragRef.moved.pipe(map((movedEvent) => ({ source: this, pointerPosition: movedEvent.pointerPosition, event: movedEvent.event, delta: movedEvent.delta, distance: movedEvent.distance }))).subscribe(observer); return () => { subscription.unsubscribe(); }; }); _injector = inject(Injector); constructor() { const dropContainer = this.dropContainer; const config = inject(CDK_DRAG_CONFIG, { optional: true }); const dragDrop = inject(DragDrop); this._dragRef = dragDrop.createDrag(this.element, { dragStartThreshold: config && config.dragStartThreshold != null ? config.dragStartThreshold : 5, pointerDirectionChangeThreshold: config && config.pointerDirectionChangeThreshold != null ? config.pointerDirectionChangeThreshold : 5, zIndex: config?.zIndex }); this._dragRef.data = this; this._dragDropRegistry.registerDirectiveNode(this.element.nativeElement, this); if (config) { this._assignDefaults(config); } if (dropContainer) { dropContainer.addItem(this); dropContainer._dropListRef.beforeStarted.pipe(takeUntil(this._destroyed)).subscribe(() => { this._dragRef.scale = this.scale; }); } this._syncInputs(this._dragRef); this._handleEvents(this._dragRef); } /** * Returns the element that is being used as a placeholder * while the current element is being dragged. */ getPlaceholderElement() { return this._dragRef.getPlaceholderElement(); } /** Returns the root draggable element. */ getRootElement() { return this._dragRef.getRootElement(); } /** Resets a standalone drag item to its initial position. */ reset() { this._dragRef.reset(); } /** Resets drag item to end of boundary element. */ resetToBoundary() { this._dragRef.resetToBoundary(); } /** * Gets the pixel coordinates of the draggable outside of a drop container. */ getFreeDragPosition() { return this._dragRef.getFreeDragPosition(); } /** * Sets the current position in pixels the draggable outside of a drop container. * @param value New position to be set. */ setFreeDragPosition(value) { this._dragRef.setFreeDragPosition(value); } ngAfterViewInit() { afterNextRender(() => { this._updateRootElement(); this._setupHandlesListener(); this._dragRef.scale = this.scale; if (this.freeDragPosition) { this._dragRef.setFreeDragPosition(this.freeDragPosition); } }, { injector: this._injector }); } ngOnChanges(changes) { const rootSelectorChange = changes["rootElementSelector"]; const positionChange = changes["freeDragPosition"]; if (rootSelectorChange && !rootSelectorChange.firstChange) { this._updateRootElement(); } this._dragRef.scale = this.scale; if (positionChange && !positionChange.firstChange && this.freeDragPosition) { this._dragRef.setFreeDragPosition(this.freeDragPosition); } } ngOnDestroy() { if (this.dropContainer) { this.dropContainer.removeItem(this); } this._dragDropRegistry.removeDirectiveNode(this.element.nativeElement); this._ngZone.runOutsideAngular(() => { this._handles.complete(); this._destroyed.next(); this._destroyed.complete(); this._dragRef.dispose(); }); } _addHandle(handle) { const handles = this._handles.getValue(); handles.push(handle); this._handles.next(handles); } _removeHandle(handle) { const handles = this._handles.getValue(); const index = handles.indexOf(handle); if (index > -1) { handles.splice(index, 1); this._handles.next(handles); } } _setPreviewTemplate(preview) { this._previewTemplate = preview; } _resetPreviewTemplate(preview) { if (preview === this._previewTemplate) { this._previewTemplate = null; } } _setPlaceholderTemplate(placeholder) { this._placeholderTemplate = placeholder; } _resetPlaceholderTemplate(placeholder) { if (placeholder === this._placeholderTemplate) { this._placeholderTemplate = null; } } /** Syncs the root element with the `DragRef`. */ _updateRootElement() { const element = this.element.nativeElement; let rootElement = element; if (this.rootElementSelector) { rootElement = element.closest !== void 0 ? element.closest(this.rootElementSelector) : ( // Comment tag doesn't have closest method, so use parent's one. element.parentElement?.closest(this.rootElementSelector) ); } if (rootElement && (typeof ngDevMode === "undefined" || ngDevMode)) { assertElementNode(rootElement, "cdkDrag"); } this._dragRef.withRootElement(rootElement || element); } /** Gets the boundary element, based on the `boundaryElement` value. */ _getBoundaryElement() { const boundary = this.boundaryElement; if (!boundary) { return null; } if (typeof boundary === "string") { return this.element.nativeElement.closest(boundary); } return coerceElement(boundary); } /** Syncs the inputs of the CdkDrag with the options of the underlying DragRef. */ _syncInputs(ref) { ref.beforeStarted.subscribe(() => { if (!ref.isDragging()) { const dir = this._dir; const dragStartDelay = this.dragStartDelay; const placeholder = this._placeholderTemplate ? { template: this._placeholderTemplate.templateRef, context: this._placeholderTemplate.data, viewContainer: this._viewContainerRef } : null; const preview = this._previewTemplate ? { template: this._previewTemplate.templateRef, context: this._previewTemplate.data, matchSize: this._previewTemplate.matchSize, viewContainer: this._viewContainerRef } : null; ref.disabled = this.disabled; ref.lockAxis = this.lockAxis; ref.scale = this.scale; ref.dragStartDelay = typeof dragStartDelay === "object" && dragStartDelay ? dragStartDelay : coerceNumberProperty(dragStartDelay); ref.constrainPosition = this.constrainPosition; ref.previewClass = this.previewClass; ref.withBoundaryElement(this._getBoundaryElement()).withPlaceholderTemplate(placeholder).withPreviewTemplate(preview).withPreviewContainer(this.previewContainer || "global"); if (dir) { ref.withDirection(dir.value); } } }); ref.beforeStarted.pipe(take(1)).subscribe(() => { if (this._parentDrag) { ref.withParent(this._parentDrag._dragRef); return; } let parent = this.element.nativeElement.parentElement; while (parent) { const parentDrag = this._dragDropRegistry.getDragDirectiveForNode(parent); if (parentDrag) { ref.withParent(parentDrag._dragRef); break; } parent = parent.parentElement; } }); } /** Handles the events from the underlying `DragRef`. */ _handleEvents(ref) { ref.started.subscribe((startEvent) => { this.started.emit({ source: this, event: startEvent.event }); this._changeDetectorRef.markForCheck(); }); ref.released.subscribe((releaseEvent) => { this.released.emit({ source: this, event: releaseEvent.event }); }); ref.ended.subscribe((endEvent) => { this.ended.emit({ source: this, distance: endEvent.distance, dropPoint: endEvent.dropPoint, event: endEvent.event }); this._changeDetectorRef.markForCheck(); }); ref.entered.subscribe((enterEvent) => { this.entered.emit({ container: enterEvent.container.data, item: this, currentIndex: enterEvent.currentIndex }); }); ref.exited.subscribe((exitEvent) => { this.exited.emit({ container: exitEvent.container.data, item: this }); }); ref.dropped.subscribe((dropEvent) => { this.dropped.emit({ previousIndex: dropEvent.previousIndex, currentIndex: dropEvent.currentIndex, previousContainer: dropEvent.previousContainer.data, container: dropEvent.container.data, isPointerOverContainer: dropEvent.isPointerOverContainer, item: this, distance: dropEvent.distance, dropPoint: dropEvent.dropPoint, event: dropEvent.event }); }); } /** Assigns the default input values based on a provided config object. */ _assignDefaults(config) { const { lockAxis, dragStartDelay, constrainPosition, previewClass, boundaryElement, draggingDisabled, rootElementSelector, previewContainer } = config; this.disabled = draggingDisabled == null ? false : draggingDisabled; this.dragStartDelay = dragStartDelay || 0; this.lockAxis = lockAxis || null; if (constrainPosition) { this.constrainPosition = constrainPosition; } if (previewClass) { this.previewClass = previewClass; } if (boundaryElement) { this.boundaryElement = boundaryElement; } if (rootElementSelector) { this.rootElementSelector = rootElementSelector; } if (previewContainer) { this.previewContainer = previewContainer; } } /** Sets up the listener that syncs the handles with the drag ref. */ _setupHandlesListener() { this._handles.pipe( // Sync the new handles with the DragRef. tap((handles) => { const handleElements = handles.map((handle) => handle.element); if (this._selfHandle && this.rootElementSelector) { handleElements.push(this.element); } this._dragRef.withHandles(handleElements); }), // Listen if the state of any of the handles changes. switchMap((handles) => { return merge(...handles.map((item) => item._stateChanges.pipe(startWith(item)))); }), takeUntil(this._destroyed) ).subscribe((handleInstance) => { const dragRef = this._dragRef; const handle = handleInstance.element.nativeElement; handleInstance.disabled ? dragRef.disableHandle(handle) : dragRef.enableHandle(handle); }); } static ɵfac = function CdkDrag_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || _CdkDrag)(); }; static ɵdir = ɵɵdefineDirective({ type: _CdkDrag, selectors: [["", "cdkDrag", ""]], hostAttrs: [1, "cdk-drag"], hostVars: 4, hostBindings: function CdkDrag_HostBindings(rf, ctx) { if (rf & 2) { ɵɵclassProp("cdk-drag-disabled", ctx.disabled)("cdk-drag-dragging", ctx._dragRef.isDragging()); } }, inputs: { data: [0, "cdkDragData", "data"], lockAxis: [0, "cdkDragLockAxis", "lockAxis"], rootElementSelector: [0, "cdkDragRootElement", "rootElementSelector"], boundaryElement: [0, "cdkDragBoundary", "boundaryElement"], dragStartDelay: [0, "cdkDragStartDelay", "dragStartDelay"], freeDragPosition: [0, "cdkDragFreeDragPosition", "freeDragPosition"], disabled: [2, "cdkDragDisabled", "disabled", booleanAttribute], constrainPosition: [0, "cdkDragConstrainPosition", "constrainPosition"], previewClass: [0, "cdkDragPreviewClass", "previewClass"], previewContainer: [0, "cdkDragPreviewContainer", "previewContainer"], scale: [2, "cdkDragScale", "scale", numberAttribute] }, outputs: { started: "cdkDragStarted", released: "cdkDragReleased", ended: "cdkDragEnded", entered: "cdkDragEntered", exited: "cdkDragExited", dropped: "cdkDragDropped", moved: "cdkDragMoved" }, exportAs: ["cdkDrag"], features: [ɵɵProvidersFeature([{ provide: CDK_DRAG_PARENT, useExisting: _CdkDrag }]), ɵɵNgOnChangesFeature] }); }; (() => { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(CdkDrag, [{ type: Directive, args: [{ selector: "[cdkDrag]", exportAs: "cdkDrag", host: { "class": "cdk-drag", "[class.cdk-drag-disabled]": "disabled", "[class.cdk-drag-dragging]": "_dragRef.isDragging()" }, providers: [{ provide: CDK_DRAG_PARENT, useExisting: CdkDrag }] }] }], () => [], { data: [{ type: Input, args: ["cdkDragData"] }], lockAxis: [{ type: Input, args: ["cdkDragLockAxis"] }], rootElementSelector: [{ type: Input, args: ["cdkDragRootElement"] }], boundaryElement: [{ type: Input, args: ["cdkDragBoundary"] }], dragStartDelay: [{ type: Input, args: ["cdkDragStartDelay"] }], freeDragPosition: [{ type: Input, args: ["cdkDragFreeDragPosition"] }], disabled: [{ type: Input, args: [{ alias: "cdkDragDisabled", transform: booleanAttribute }] }], constrainPosition: [{ type: Input, args: ["cdkDragConstrainPosition"] }], previewClass: [{ type: Input, args: ["cdkDragPreviewClass"] }], previewContainer: [{ type: Input, args: ["cdkDragPreviewContainer"] }], scale: [{ type: Input, args: [{ alias: "cdkDragScale", transform: numberAttribute }] }], started: [{ type: Output, args: ["cdkDragStarted"] }], released: [{ type: Output, args: ["cdkDragReleased"] }], ended: [{ type: Output, args: ["cdkDragEnded"] }], entered: [{ type: Output, args: ["cdkDragEntered"] }], exited: [{ type: Output, args: ["cdkDragExited"] }], dropped: [{ type: Output, args: ["cdkDragDropped"] }], moved: [{ type: Output, args: ["cdkDragMoved"] }] }); })(); var CDK_DROP_LIST_GROUP = new InjectionToken("CdkDropListGroup"); var CdkDropListGroup = class _CdkDropListGroup { /** Drop lists registered inside the group. */ _items = /* @__PURE__ */ new Set(); /** Whether starting a dragging sequence from inside this group is disabled. */ disabled = false; ngOnDestroy() { this._items.clear(); } static ɵfac = function CdkDropListGroup_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || _CdkDropListGroup)(); }; static ɵdir = ɵɵdefineDirective({ type: _CdkDropListGroup, selectors: [["", "cdkDropListGroup", ""]], inputs: { disabled: [2, "cdkDropListGroupDisabled", "disabled", booleanAttribute] }, exportAs: ["cdkDropListGroup"], features: [ɵɵProvidersFeature([{ provide: CDK_DROP_LIST_GROUP, useExisting: _CdkDropListGroup }])] }); }; (() => { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(CdkDropListGroup, [{ type: Directive, args: [{ selector: "[cdkDropListGroup]", exportAs: "cdkDropListGroup", providers: [{ provide: CDK_DROP_LIST_GROUP, useExisting: CdkDropListGroup }] }] }], null, { disabled: [{ type: Input, args: [{ alias: "cdkDropListGroupDisabled", transform: booleanAttribute }] }] }); })(); var CdkDropList = class _CdkDropList { element = inject(ElementRef); _changeDetectorRef = inject(ChangeDetectorRef); _scrollDispatcher = inject(ScrollDispatcher); _dir = inject(Directionality, { optional: true }); _group = inject(CDK_DROP_LIST_GROUP, { optional: true, skipSelf: true }); /** Refs that have been synced with the drop ref most recently. */ _latestSortedRefs; /** Emits when the list has been destroyed. */ _destroyed = new Subject(); /** Whether the element's scrollable parents have been resolved. */ _scrollableParentsResolved; /** Keeps track of the drop lists that are currently on the page. */ static _dropLists = []; /** Reference to the underlying drop list instance. */ _dropListRef; /** * Other draggable containers that this container is connected to and into which the * container's items can be transferred. Can either be references to other drop containers, * or their unique IDs. */ connectedTo = []; /** Arbitrary data to attach to this container. */ data; /** Direction in which the list is oriented. */ orientation; /** * Unique ID for the drop zone. Can be used as a reference * in the `connectedTo` of another `CdkDropList`. */ id = inject(_IdGenerator).getId("cdk-drop-list-"); /** Locks the position of the draggable elements inside the container along the specified axis. */ lockAxis = null; /** Whether starting a dragging sequence from this container is disabled. */ get disabled() { return this._disabled || !!this._group && this._group.disabled; } set disabled(value) { this._dropListRef.disabled = this._disabled = value; } _disabled; /** Whether sorting within this drop list is disabled. */ sortingDisabled; /** * Function that is used to determine whether an item * is allowed to be moved into a drop container. */ enterPredicate = () => true; /** Functions that is used to determine whether an item can be sorted into a particular index. */ sortPredicate = () => true; /** Whether to auto-scroll the view when the user moves their pointer close to the edges. */ autoScrollDisabled; /** Number of pixels to scroll for each frame when auto-scrolling an element. */ autoScrollStep; /** * Selector that will be used to resolve an alternate element container for the drop list. * Passing an alternate container is useful for the cases where one might not have control * over the parent node of the draggable items within the list (e.g. due to content projection). * This allows for usages like: * * ``` *
*
*
*
*
* ``` */ elementContainerSelector; /** * By default when an item leaves its initial container, its placeholder will be transferred * to the new container. If that's not desirable for your use case, you can enable this option * which will clone the placeholder and leave it inside the original container. If the item is * returned to the initial container, the anchor element will be removed automatically. * * The cloned placeholder can be styled by targeting the `cdk-drag-anchor` class. * * This option is useful in combination with `cdkDropListSortingDisabled` to implement copying * behavior in a drop list. */ hasAnchor; /** Emits when the user drops an item inside the container. */ dropped = new EventEmitter(); /** * Emits when the user has moved a new drag item into this container. */ entered = new EventEmitter(); /** * Emits when the user removes an item from the container * by dragging it into another container. */ exited = new EventEmitter(); /** Emits as the user is swapping items while actively dragging. */ sorted = new EventEmitter(); /** * Keeps track of the items that are registered with this container. Historically we used to * do this with a `ContentChildren` query, however queries don't handle transplanted views very * well which means that we can't handle cases like dragging the headers of a `mat-table` * correctly. What we do instead is to have the items register themselves with the container * and then we sort them based on their position in the DOM. */ _unsortedItems = /* @__PURE__ */ new Set(); constructor() { const dragDrop = inject(DragDrop); const config = inject(CDK_DRAG_CONFIG, { optional: true }); if (typeof ngDevMode === "undefined" || ngDevMode) { assertElementNode(this.element.nativeElement, "cdkDropList"); } this._dropListRef = dragDrop.createDropList(this.element); this._dropListRef.data = this; if (config) { this._assignDefaults(config); } this._dropListRef.enterPredicate = (drag, drop) => { return this.enterPredicate(drag.data, drop.data); }; this._dropListRef.sortPredicate = (index, drag, drop) => { return this.sortPredicate(index, drag.data, drop.data); }; this._setupInputSyncSubscription(this._dropListRef); this._handleEvents(this._dropListRef); _CdkDropList._dropLists.push(this); if (this._group) { this._group._items.add(this); } } /** Registers an items with the drop list. */ addItem(item) { this._unsortedItems.add(item); item._dragRef._withDropContainer(this._dropListRef); if (this._dropListRef.isDragging()) { this._syncItemsWithRef(this.getSortedItems().map((item2) => item2._dragRef)); } } /** Removes an item from the drop list. */ removeItem(item) { this._unsortedItems.delete(item); if (this._latestSortedRefs) { const index = this._latestSortedRefs.indexOf(item._dragRef); if (index > -1) { this._latestSortedRefs.splice(index, 1); this._syncItemsWithRef(this._latestSortedRefs); } } } /** Gets the registered items in the list, sorted by their position in the DOM. */ getSortedItems() { return Array.from(this._unsortedItems).sort((a, b) => { const documentPosition = a._dragRef.getVisibleElement().compareDocumentPosition(b._dragRef.getVisibleElement()); return documentPosition & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : 1; }); } ngOnDestroy() { const index = _CdkDropList._dropLists.indexOf(this); if (index > -1) { _CdkDropList._dropLists.splice(index, 1); } if (this._group) { this._group._items.delete(this); } this._latestSortedRefs = void 0; this._unsortedItems.clear(); this._dropListRef.dispose(); this._destroyed.next(); this._destroyed.complete(); } /** Syncs the inputs of the CdkDropList with the options of the underlying DropListRef. */ _setupInputSyncSubscription(ref) { if (this._dir) { this._dir.change.pipe(startWith(this._dir.value), takeUntil(this._destroyed)).subscribe((value) => ref.withDirection(value)); } ref.beforeStarted.subscribe(() => { const siblings = coerceArray(this.connectedTo).map((drop) => { if (typeof drop === "string") { const correspondingDropList = _CdkDropList._dropLists.find((list) => list.id === drop); if (!correspondingDropList && (typeof ngDevMode === "undefined" || ngDevMode)) { console.warn(`CdkDropList could not find connected drop list with id "${drop}"`); } return correspondingDropList; } return drop; }); if (this._group) { this._group._items.forEach((drop) => { if (siblings.indexOf(drop) === -1) { siblings.push(drop); } }); } if (!this._scrollableParentsResolved) { const scrollableParents = this._scrollDispatcher.getAncestorScrollContainers(this.element).map((scrollable) => scrollable.getElementRef().nativeElement); this._dropListRef.withScrollableParents(scrollableParents); this._scrollableParentsResolved = true; } if (this.elementContainerSelector) { const container = this.element.nativeElement.querySelector(this.elementContainerSelector); if (!container && (typeof ngDevMode === "undefined" || ngDevMode)) { throw new Error(`CdkDropList could not find an element container matching the selector "${this.elementContainerSelector}"`); } ref.withElementContainer(container); } ref.disabled = this.disabled; ref.lockAxis = this.lockAxis; ref.sortingDisabled = this.sortingDisabled; ref.autoScrollDisabled = this.autoScrollDisabled; ref.autoScrollStep = coerceNumberProperty(this.autoScrollStep, 2); ref.hasAnchor = this.hasAnchor; ref.connectedTo(siblings.filter((drop) => drop && drop !== this).map((list) => list._dropListRef)).withOrientation(this.orientation); }); } /** Handles events from the underlying DropListRef. */ _handleEvents(ref) { ref.beforeStarted.subscribe(() => { this._syncItemsWithRef(this.getSortedItems().map((item) => item._dragRef)); this._changeDetectorRef.markForCheck(); }); ref.entered.subscribe((event) => { this.entered.emit({ container: this, item: event.item.data, currentIndex: event.currentIndex }); }); ref.exited.subscribe((event) => { this.exited.emit({ container: this, item: event.item.data }); this._changeDetectorRef.markForCheck(); }); ref.sorted.subscribe((event) => { this.sorted.emit({ previousIndex: event.previousIndex, currentIndex: event.currentIndex, container: this, item: event.item.data }); }); ref.dropped.subscribe((dropEvent) => { this.dropped.emit({ previousIndex: dropEvent.previousIndex, currentIndex: dropEvent.currentIndex, previousContainer: dropEvent.previousContainer.data, container: dropEvent.container.data, item: dropEvent.item.data, isPointerOverContainer: dropEvent.isPointerOverContainer, distance: dropEvent.distance, dropPoint: dropEvent.dropPoint, event: dropEvent.event }); this._changeDetectorRef.markForCheck(); }); merge(ref.receivingStarted, ref.receivingStopped).subscribe(() => this._changeDetectorRef.markForCheck()); } /** Assigns the default input values based on a provided config object. */ _assignDefaults(config) { const { lockAxis, draggingDisabled, sortingDisabled, listAutoScrollDisabled, listOrientation } = config; this.disabled = draggingDisabled == null ? false : draggingDisabled; this.sortingDisabled = sortingDisabled == null ? false : sortingDisabled; this.autoScrollDisabled = listAutoScrollDisabled == null ? false : listAutoScrollDisabled; this.orientation = listOrientation || "vertical"; this.lockAxis = lockAxis || null; } /** Syncs up the registered drag items with underlying drop list ref. */ _syncItemsWithRef(items) { this._latestSortedRefs = items; this._dropListRef.withItems(items); } static ɵfac = function CdkDropList_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || _CdkDropList)(); }; static ɵdir = ɵɵdefineDirective({ type: _CdkDropList, selectors: [["", "cdkDropList", ""], ["cdk-drop-list"]], hostAttrs: [1, "cdk-drop-list"], hostVars: 7, hostBindings: function CdkDropList_HostBindings(rf, ctx) { if (rf & 2) { ɵɵattribute("id", ctx.id); ɵɵclassProp("cdk-drop-list-disabled", ctx.disabled)("cdk-drop-list-dragging", ctx._dropListRef.isDragging())("cdk-drop-list-receiving", ctx._dropListRef.isReceiving()); } }, inputs: { connectedTo: [0, "cdkDropListConnectedTo", "connectedTo"], data: [0, "cdkDropListData", "data"], orientation: [0, "cdkDropListOrientation", "orientation"], id: "id", lockAxis: [0, "cdkDropListLockAxis", "lockAxis"], disabled: [2, "cdkDropListDisabled", "disabled", booleanAttribute], sortingDisabled: [2, "cdkDropListSortingDisabled", "sortingDisabled", booleanAttribute], enterPredicate: [0, "cdkDropListEnterPredicate", "enterPredicate"], sortPredicate: [0, "cdkDropListSortPredicate", "sortPredicate"], autoScrollDisabled: [2, "cdkDropListAutoScrollDisabled", "autoScrollDisabled", booleanAttribute], autoScrollStep: [0, "cdkDropListAutoScrollStep", "autoScrollStep"], elementContainerSelector: [0, "cdkDropListElementContainer", "elementContainerSelector"], hasAnchor: [2, "cdkDropListHasAnchor", "hasAnchor", booleanAttribute] }, outputs: { dropped: "cdkDropListDropped", entered: "cdkDropListEntered", exited: "cdkDropListExited", sorted: "cdkDropListSorted" }, exportAs: ["cdkDropList"], features: [ɵɵProvidersFeature([ // Prevent child drop lists from picking up the same group as their parent. { provide: CDK_DROP_LIST_GROUP, useValue: void 0 }, { provide: CDK_DROP_LIST, useExisting: _CdkDropList } ])] }); }; (() => { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(CdkDropList, [{ type: Directive, args: [{ selector: "[cdkDropList], cdk-drop-list", exportAs: "cdkDropList", providers: [ // Prevent child drop lists from picking up the same group as their parent. { provide: CDK_DROP_LIST_GROUP, useValue: void 0 }, { provide: CDK_DROP_LIST, useExisting: CdkDropList } ], host: { "class": "cdk-drop-list", "[attr.id]": "id", "[class.cdk-drop-list-disabled]": "disabled", "[class.cdk-drop-list-dragging]": "_dropListRef.isDragging()", "[class.cdk-drop-list-receiving]": "_dropListRef.isReceiving()" } }] }], () => [], { connectedTo: [{ type: Input, args: ["cdkDropListConnectedTo"] }], data: [{ type: Input, args: ["cdkDropListData"] }], orientation: [{ type: Input, args: ["cdkDropListOrientation"] }], id: [{ type: Input }], lockAxis: [{ type: Input, args: ["cdkDropListLockAxis"] }], disabled: [{ type: Input, args: [{ alias: "cdkDropListDisabled", transform: booleanAttribute }] }], sortingDisabled: [{ type: Input, args: [{ alias: "cdkDropListSortingDisabled", transform: booleanAttribute }] }], enterPredicate: [{ type: Input, args: ["cdkDropListEnterPredicate"] }], sortPredicate: [{ type: Input, args: ["cdkDropListSortPredicate"] }], autoScrollDisabled: [{ type: Input, args: [{ alias: "cdkDropListAutoScrollDisabled", transform: booleanAttribute }] }], autoScrollStep: [{ type: Input, args: ["cdkDropListAutoScrollStep"] }], elementContainerSelector: [{ type: Input, args: ["cdkDropListElementContainer"] }], hasAnchor: [{ type: Input, args: [{ alias: "cdkDropListHasAnchor", transform: booleanAttribute }] }], dropped: [{ type: Output, args: ["cdkDropListDropped"] }], entered: [{ type: Output, args: ["cdkDropListEntered"] }], exited: [{ type: Output, args: ["cdkDropListExited"] }], sorted: [{ type: Output, args: ["cdkDropListSorted"] }] }); })(); var CDK_DRAG_PREVIEW = new InjectionToken("CdkDragPreview"); var CdkDragPreview = class _CdkDragPreview { templateRef = inject(TemplateRef); _drag = inject(CDK_DRAG_PARENT, { optional: true }); /** Context data to be added to the preview template instance. */ data; /** Whether the preview should preserve the same size as the item that is being dragged. */ matchSize = false; constructor() { this._drag?._setPreviewTemplate(this); } ngOnDestroy() { this._drag?._resetPreviewTemplate(this); } static ɵfac = function CdkDragPreview_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || _CdkDragPreview)(); }; static ɵdir = ɵɵdefineDirective({ type: _CdkDragPreview, selectors: [["ng-template", "cdkDragPreview", ""]], inputs: { data: "data", matchSize: [2, "matchSize", "matchSize", booleanAttribute] }, features: [ɵɵProvidersFeature([{ provide: CDK_DRAG_PREVIEW, useExisting: _CdkDragPreview }])] }); }; (() => { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(CdkDragPreview, [{ type: Directive, args: [{ selector: "ng-template[cdkDragPreview]", providers: [{ provide: CDK_DRAG_PREVIEW, useExisting: CdkDragPreview }] }] }], () => [], { data: [{ type: Input }], matchSize: [{ type: Input, args: [{ transform: booleanAttribute }] }] }); })(); var CDK_DRAG_PLACEHOLDER = new InjectionToken("CdkDragPlaceholder"); var CdkDragPlaceholder = class _CdkDragPlaceholder { templateRef = inject(TemplateRef); _drag = inject(CDK_DRAG_PARENT, { optional: true }); /** Context data to be added to the placeholder template instance. */ data; constructor() { this._drag?._setPlaceholderTemplate(this); } ngOnDestroy() { this._drag?._resetPlaceholderTemplate(this); } static ɵfac = function CdkDragPlaceholder_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || _CdkDragPlaceholder)(); }; static ɵdir = ɵɵdefineDirective({ type: _CdkDragPlaceholder, selectors: [["ng-template", "cdkDragPlaceholder", ""]], inputs: { data: "data" }, features: [ɵɵProvidersFeature([{ provide: CDK_DRAG_PLACEHOLDER, useExisting: _CdkDragPlaceholder }])] }); }; (() => { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(CdkDragPlaceholder, [{ type: Directive, args: [{ selector: "ng-template[cdkDragPlaceholder]", providers: [{ provide: CDK_DRAG_PLACEHOLDER, useExisting: CdkDragPlaceholder }] }] }], () => [], { data: [{ type: Input }] }); })(); var DRAG_DROP_DIRECTIVES = [CdkDropList, CdkDropListGroup, CdkDrag, CdkDragHandle, CdkDragPreview, CdkDragPlaceholder]; var DragDropModule = class _DragDropModule { static ɵfac = function DragDropModule_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || _DragDropModule)(); }; static ɵmod = ɵɵdefineNgModule({ type: _DragDropModule, imports: [CdkDropList, CdkDropListGroup, CdkDrag, CdkDragHandle, CdkDragPreview, CdkDragPlaceholder], exports: [CdkScrollableModule, CdkDropList, CdkDropListGroup, CdkDrag, CdkDragHandle, CdkDragPreview, CdkDragPlaceholder] }); static ɵinj = ɵɵdefineInjector({ providers: [DragDrop], imports: [CdkScrollableModule] }); }; (() => { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(DragDropModule, [{ type: NgModule, args: [{ imports: DRAG_DROP_DIRECTIVES, exports: [CdkScrollableModule, ...DRAG_DROP_DIRECTIVES], providers: [DragDrop] }] }], null, null); })(); export { CDK_DRAG_CONFIG, CDK_DRAG_HANDLE, CDK_DRAG_PARENT, CDK_DRAG_PLACEHOLDER, CDK_DRAG_PREVIEW, CDK_DROP_LIST, CDK_DROP_LIST_GROUP, CdkDrag, CdkDragHandle, CdkDragPlaceholder, CdkDragPreview, CdkDropList, CdkDropListGroup, DragDrop, DragDropModule, DragDropRegistry, DragRef, DropListRef, copyArrayItem, moveItemInArray, transferArrayItem, CdkScrollable as ɵɵCdkScrollable }; //# sourceMappingURL=@angular_cdk_drag-drop.js.map