7112 lines
244 KiB
JavaScript
7112 lines
244 KiB
JavaScript
import {
|
|
animate,
|
|
query,
|
|
sequence,
|
|
stagger,
|
|
state,
|
|
style,
|
|
transition,
|
|
trigger
|
|
} from "./chunk-DW3R7JBF.js";
|
|
import {
|
|
NzConfigService,
|
|
NzIconDirective,
|
|
NzIconModule,
|
|
Platform,
|
|
RtlScrollAxisType,
|
|
_getEventTarget,
|
|
_isTestEnvironment,
|
|
getRtlScrollAxisType,
|
|
onConfigChangeEventForComponent,
|
|
supportsScrollBehavior,
|
|
takeUntilDestroyed
|
|
} from "./chunk-RKFSA2AP.js";
|
|
import {
|
|
coerceArray,
|
|
coerceCssPixelValue,
|
|
coerceElement,
|
|
coerceNumberProperty,
|
|
environment,
|
|
getEventPosition,
|
|
isTemplateRef,
|
|
isTouchEvent,
|
|
toCssPixel
|
|
} from "./chunk-5F4IS6F4.js";
|
|
import "./chunk-OAOHUKFD.js";
|
|
import "./chunk-OVEDGMY5.js";
|
|
import "./chunk-RDTKHKTF.js";
|
|
import "./chunk-ZKUNZJKW.js";
|
|
import {
|
|
Location
|
|
} from "./chunk-PSDIAYZ2.js";
|
|
import "./chunk-6UNWJBON.js";
|
|
import {
|
|
ANIMATION_MODULE_TYPE,
|
|
APP_ID,
|
|
ApplicationRef,
|
|
CSP_NONCE,
|
|
ChangeDetectionStrategy,
|
|
ChangeDetectorRef,
|
|
Component,
|
|
DOCUMENT,
|
|
DestroyRef,
|
|
Directive,
|
|
ElementRef,
|
|
EnvironmentInjector,
|
|
EventEmitter,
|
|
Inject,
|
|
Injectable,
|
|
InjectionToken,
|
|
Injector,
|
|
Input,
|
|
IterableDiffers,
|
|
NgModule,
|
|
NgModuleRef$1,
|
|
NgZone,
|
|
Optional,
|
|
Output,
|
|
Renderer2,
|
|
RendererFactory2,
|
|
RuntimeError,
|
|
TemplateRef,
|
|
ViewChild,
|
|
ViewContainerRef,
|
|
ViewEncapsulation,
|
|
afterNextRender,
|
|
booleanAttribute,
|
|
createComponent,
|
|
effect,
|
|
forwardRef,
|
|
inject,
|
|
setClassMetadata,
|
|
signal,
|
|
untracked,
|
|
ɵɵInheritDefinitionFeature,
|
|
ɵɵNgOnChangesFeature,
|
|
ɵɵProvidersFeature,
|
|
ɵɵadvance,
|
|
ɵɵattribute,
|
|
ɵɵclassMap,
|
|
ɵɵclassProp,
|
|
ɵɵconditional,
|
|
ɵɵconditionalCreate,
|
|
ɵɵdefineComponent,
|
|
ɵɵdefineDirective,
|
|
ɵɵdefineInjectable,
|
|
ɵɵdefineInjector,
|
|
ɵɵdefineNgModule,
|
|
ɵɵdomElement,
|
|
ɵɵdomElementEnd,
|
|
ɵɵdomElementStart,
|
|
ɵɵelement,
|
|
ɵɵelementContainerEnd,
|
|
ɵɵelementContainerStart,
|
|
ɵɵelementEnd,
|
|
ɵɵelementStart,
|
|
ɵɵgetCurrentView,
|
|
ɵɵgetInheritedFactory,
|
|
ɵɵinject,
|
|
ɵɵlistener,
|
|
ɵɵloadQuery,
|
|
ɵɵnextContext,
|
|
ɵɵprojection,
|
|
ɵɵprojectionDef,
|
|
ɵɵproperty,
|
|
ɵɵpureFunction2,
|
|
ɵɵqueryRefresh,
|
|
ɵɵrepeater,
|
|
ɵɵrepeaterCreate,
|
|
ɵɵrepeaterTrackByIdentity,
|
|
ɵɵresetView,
|
|
ɵɵrestoreView,
|
|
ɵɵsanitizeHtml,
|
|
ɵɵstyleMap,
|
|
ɵɵstyleProp,
|
|
ɵɵtemplate,
|
|
ɵɵviewQuery
|
|
} from "./chunk-COCNRMG2.js";
|
|
import {
|
|
animationFrameScheduler,
|
|
asapScheduler,
|
|
isObservable
|
|
} from "./chunk-576P5TAG.js";
|
|
import "./chunk-UQGLAMHP.js";
|
|
import {
|
|
ConnectableObservable,
|
|
Observable,
|
|
Subject,
|
|
Subscription,
|
|
auditTime,
|
|
combineLatest,
|
|
concat,
|
|
debounceTime,
|
|
distinctUntilChanged,
|
|
filter,
|
|
finalize,
|
|
map,
|
|
of,
|
|
pairwise,
|
|
shareReplay,
|
|
skip,
|
|
startWith,
|
|
switchMap,
|
|
take,
|
|
takeUntil,
|
|
takeWhile
|
|
} from "./chunk-2K3BB2X3.js";
|
|
import {
|
|
__spreadProps,
|
|
__spreadValues
|
|
} from "./chunk-WDMUDEB6.js";
|
|
|
|
// node_modules/@angular/cdk/fesm2022/style-loader.mjs
|
|
var appsWithLoaders = /* @__PURE__ */ new WeakMap();
|
|
var _CdkPrivateStyleLoader = class __CdkPrivateStyleLoader {
|
|
_appRef;
|
|
_injector = inject(Injector);
|
|
_environmentInjector = inject(EnvironmentInjector);
|
|
/**
|
|
* Loads a set of styles.
|
|
* @param loader Component which will be instantiated to load the styles.
|
|
*/
|
|
load(loader) {
|
|
const appRef = this._appRef = this._appRef || this._injector.get(ApplicationRef);
|
|
let data = appsWithLoaders.get(appRef);
|
|
if (!data) {
|
|
data = {
|
|
loaders: /* @__PURE__ */ new Set(),
|
|
refs: []
|
|
};
|
|
appsWithLoaders.set(appRef, data);
|
|
appRef.onDestroy(() => {
|
|
appsWithLoaders.get(appRef)?.refs.forEach((ref) => ref.destroy());
|
|
appsWithLoaders.delete(appRef);
|
|
});
|
|
}
|
|
if (!data.loaders.has(loader)) {
|
|
data.loaders.add(loader);
|
|
data.refs.push(createComponent(loader, {
|
|
environmentInjector: this._environmentInjector
|
|
}));
|
|
}
|
|
}
|
|
static ɵfac = function _CdkPrivateStyleLoader_Factory(__ngFactoryType__) {
|
|
return new (__ngFactoryType__ || __CdkPrivateStyleLoader)();
|
|
};
|
|
static ɵprov = ɵɵdefineInjectable({
|
|
token: __CdkPrivateStyleLoader,
|
|
factory: __CdkPrivateStyleLoader.ɵfac,
|
|
providedIn: "root"
|
|
});
|
|
};
|
|
(() => {
|
|
(typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(_CdkPrivateStyleLoader, [{
|
|
type: Injectable,
|
|
args: [{
|
|
providedIn: "root"
|
|
}]
|
|
}], null, null);
|
|
})();
|
|
|
|
// 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/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(Math.round(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 Wrap the rendered content in an element that will be used to offset it based on the scroll\n position.\n-->\n<div #contentWrapper class="cdk-virtual-scroll-content-wrapper">\n <ng-content></ng-content>\n</div>\n<!--\n Spacer used to force the scrolling container to the correct size for the *total* number of items\n so that the scrollbar captures the size of the entire data set.\n-->\n<div class="cdk-virtual-scroll-spacer"\n [style.width]="_totalContentWidth()" [style.height]="_totalContentHeight()"></div>\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/portal.mjs
|
|
function throwNullPortalError() {
|
|
throw Error("Must provide a portal to attach");
|
|
}
|
|
function throwPortalAlreadyAttachedError() {
|
|
throw Error("Host already has a portal attached");
|
|
}
|
|
function throwPortalOutletAlreadyDisposedError() {
|
|
throw Error("This PortalOutlet has already been disposed");
|
|
}
|
|
function throwUnknownPortalTypeError() {
|
|
throw Error("Attempting to attach an unknown Portal type. BasePortalOutlet accepts either a ComponentPortal or a TemplatePortal.");
|
|
}
|
|
function throwNullPortalOutletError() {
|
|
throw Error("Attempting to attach a portal to a null PortalOutlet");
|
|
}
|
|
function throwNoPortalAttachedError() {
|
|
throw Error("Attempting to detach a portal that is not attached to a host");
|
|
}
|
|
var Portal = class {
|
|
_attachedHost;
|
|
/** Attach this portal to a host. */
|
|
attach(host) {
|
|
if (typeof ngDevMode === "undefined" || ngDevMode) {
|
|
if (host == null) {
|
|
throwNullPortalOutletError();
|
|
}
|
|
if (host.hasAttached()) {
|
|
throwPortalAlreadyAttachedError();
|
|
}
|
|
}
|
|
this._attachedHost = host;
|
|
return host.attach(this);
|
|
}
|
|
/** Detach this portal from its host */
|
|
detach() {
|
|
let host = this._attachedHost;
|
|
if (host != null) {
|
|
this._attachedHost = null;
|
|
host.detach();
|
|
} else if (typeof ngDevMode === "undefined" || ngDevMode) {
|
|
throwNoPortalAttachedError();
|
|
}
|
|
}
|
|
/** Whether this portal is attached to a host. */
|
|
get isAttached() {
|
|
return this._attachedHost != null;
|
|
}
|
|
/**
|
|
* Sets the PortalOutlet reference without performing `attach()`. This is used directly by
|
|
* the PortalOutlet when it is performing an `attach()` or `detach()`.
|
|
*/
|
|
setAttachedHost(host) {
|
|
this._attachedHost = host;
|
|
}
|
|
};
|
|
var ComponentPortal = class extends Portal {
|
|
/** The type of the component that will be instantiated for attachment. */
|
|
component;
|
|
/**
|
|
* Where the attached component should live in Angular's *logical* component tree.
|
|
* This is different from where the component *renders*, which is determined by the PortalOutlet.
|
|
* The origin is necessary when the host is outside of the Angular application context.
|
|
*/
|
|
viewContainerRef;
|
|
/** Injector used for the instantiation of the component. */
|
|
injector;
|
|
/**
|
|
* List of DOM nodes that should be projected through `<ng-content>` of the attached component.
|
|
*/
|
|
projectableNodes;
|
|
constructor(component, viewContainerRef, injector, projectableNodes) {
|
|
super();
|
|
this.component = component;
|
|
this.viewContainerRef = viewContainerRef;
|
|
this.injector = injector;
|
|
this.projectableNodes = projectableNodes;
|
|
}
|
|
};
|
|
var TemplatePortal = class extends Portal {
|
|
templateRef;
|
|
viewContainerRef;
|
|
context;
|
|
injector;
|
|
constructor(templateRef, viewContainerRef, context, injector) {
|
|
super();
|
|
this.templateRef = templateRef;
|
|
this.viewContainerRef = viewContainerRef;
|
|
this.context = context;
|
|
this.injector = injector;
|
|
}
|
|
get origin() {
|
|
return this.templateRef.elementRef;
|
|
}
|
|
/**
|
|
* Attach the portal to the provided `PortalOutlet`.
|
|
* When a context is provided it will override the `context` property of the `TemplatePortal`
|
|
* instance.
|
|
*/
|
|
attach(host, context = this.context) {
|
|
this.context = context;
|
|
return super.attach(host);
|
|
}
|
|
detach() {
|
|
this.context = void 0;
|
|
return super.detach();
|
|
}
|
|
};
|
|
var DomPortal = class extends Portal {
|
|
/** DOM node hosting the portal's content. */
|
|
element;
|
|
constructor(element) {
|
|
super();
|
|
this.element = element instanceof ElementRef ? element.nativeElement : element;
|
|
}
|
|
};
|
|
var BasePortalOutlet = class {
|
|
/** The portal currently attached to the host. */
|
|
_attachedPortal;
|
|
/** A function that will permanently dispose this host. */
|
|
_disposeFn;
|
|
/** Whether this host has already been permanently disposed. */
|
|
_isDisposed = false;
|
|
/** Whether this host has an attached portal. */
|
|
hasAttached() {
|
|
return !!this._attachedPortal;
|
|
}
|
|
/** Attaches a portal. */
|
|
attach(portal) {
|
|
if (typeof ngDevMode === "undefined" || ngDevMode) {
|
|
if (!portal) {
|
|
throwNullPortalError();
|
|
}
|
|
if (this.hasAttached()) {
|
|
throwPortalAlreadyAttachedError();
|
|
}
|
|
if (this._isDisposed) {
|
|
throwPortalOutletAlreadyDisposedError();
|
|
}
|
|
}
|
|
if (portal instanceof ComponentPortal) {
|
|
this._attachedPortal = portal;
|
|
return this.attachComponentPortal(portal);
|
|
} else if (portal instanceof TemplatePortal) {
|
|
this._attachedPortal = portal;
|
|
return this.attachTemplatePortal(portal);
|
|
} else if (this.attachDomPortal && portal instanceof DomPortal) {
|
|
this._attachedPortal = portal;
|
|
return this.attachDomPortal(portal);
|
|
}
|
|
if (typeof ngDevMode === "undefined" || ngDevMode) {
|
|
throwUnknownPortalTypeError();
|
|
}
|
|
}
|
|
// @breaking-change 10.0.0 `attachDomPortal` to become a required abstract method.
|
|
attachDomPortal = null;
|
|
/** Detaches a previously attached portal. */
|
|
detach() {
|
|
if (this._attachedPortal) {
|
|
this._attachedPortal.setAttachedHost(null);
|
|
this._attachedPortal = null;
|
|
}
|
|
this._invokeDisposeFn();
|
|
}
|
|
/** Permanently dispose of this portal host. */
|
|
dispose() {
|
|
if (this.hasAttached()) {
|
|
this.detach();
|
|
}
|
|
this._invokeDisposeFn();
|
|
this._isDisposed = true;
|
|
}
|
|
/** @docs-private */
|
|
setDisposeFn(fn) {
|
|
this._disposeFn = fn;
|
|
}
|
|
_invokeDisposeFn() {
|
|
if (this._disposeFn) {
|
|
this._disposeFn();
|
|
this._disposeFn = null;
|
|
}
|
|
}
|
|
};
|
|
var DomPortalOutlet = class extends BasePortalOutlet {
|
|
outletElement;
|
|
_appRef;
|
|
_defaultInjector;
|
|
/**
|
|
* @param outletElement Element into which the content is projected.
|
|
* @param _appRef Reference to the application. Only used in component portals when there
|
|
* is no `ViewContainerRef` available.
|
|
* @param _defaultInjector Injector to use as a fallback when the portal being attached doesn't
|
|
* have one. Only used for component portals.
|
|
*/
|
|
constructor(outletElement, _appRef, _defaultInjector) {
|
|
super();
|
|
this.outletElement = outletElement;
|
|
this._appRef = _appRef;
|
|
this._defaultInjector = _defaultInjector;
|
|
}
|
|
/**
|
|
* Attach the given ComponentPortal to DOM element.
|
|
* @param portal Portal to be attached
|
|
* @returns Reference to the created component.
|
|
*/
|
|
attachComponentPortal(portal) {
|
|
let componentRef;
|
|
if (portal.viewContainerRef) {
|
|
const injector = portal.injector || portal.viewContainerRef.injector;
|
|
const ngModuleRef = injector.get(NgModuleRef$1, null, {
|
|
optional: true
|
|
}) || void 0;
|
|
componentRef = portal.viewContainerRef.createComponent(portal.component, {
|
|
index: portal.viewContainerRef.length,
|
|
injector,
|
|
ngModuleRef,
|
|
projectableNodes: portal.projectableNodes || void 0
|
|
});
|
|
this.setDisposeFn(() => componentRef.destroy());
|
|
} else {
|
|
if ((typeof ngDevMode === "undefined" || ngDevMode) && !this._appRef) {
|
|
throw Error("Cannot attach component portal to outlet without an ApplicationRef.");
|
|
}
|
|
const appRef = this._appRef;
|
|
const elementInjector = portal.injector || this._defaultInjector || Injector.NULL;
|
|
const environmentInjector = elementInjector.get(EnvironmentInjector, appRef.injector);
|
|
componentRef = createComponent(portal.component, {
|
|
elementInjector,
|
|
environmentInjector,
|
|
projectableNodes: portal.projectableNodes || void 0
|
|
});
|
|
appRef.attachView(componentRef.hostView);
|
|
this.setDisposeFn(() => {
|
|
if (appRef.viewCount > 0) {
|
|
appRef.detachView(componentRef.hostView);
|
|
}
|
|
componentRef.destroy();
|
|
});
|
|
}
|
|
this.outletElement.appendChild(this._getComponentRootNode(componentRef));
|
|
this._attachedPortal = portal;
|
|
return componentRef;
|
|
}
|
|
/**
|
|
* Attaches a template portal to the DOM as an embedded view.
|
|
* @param portal Portal to be attached.
|
|
* @returns Reference to the created embedded view.
|
|
*/
|
|
attachTemplatePortal(portal) {
|
|
let viewContainer = portal.viewContainerRef;
|
|
let viewRef = viewContainer.createEmbeddedView(portal.templateRef, portal.context, {
|
|
injector: portal.injector
|
|
});
|
|
viewRef.rootNodes.forEach((rootNode) => this.outletElement.appendChild(rootNode));
|
|
viewRef.detectChanges();
|
|
this.setDisposeFn(() => {
|
|
let index = viewContainer.indexOf(viewRef);
|
|
if (index !== -1) {
|
|
viewContainer.remove(index);
|
|
}
|
|
});
|
|
this._attachedPortal = portal;
|
|
return viewRef;
|
|
}
|
|
/**
|
|
* Attaches a DOM portal by transferring its content into the outlet.
|
|
* @param portal Portal to be attached.
|
|
* @deprecated To be turned into a method.
|
|
* @breaking-change 10.0.0
|
|
*/
|
|
attachDomPortal = (portal) => {
|
|
const element = portal.element;
|
|
if (!element.parentNode && (typeof ngDevMode === "undefined" || ngDevMode)) {
|
|
throw Error("DOM portal content must be attached to a parent node.");
|
|
}
|
|
const anchorNode = this.outletElement.ownerDocument.createComment("dom-portal");
|
|
element.parentNode.insertBefore(anchorNode, element);
|
|
this.outletElement.appendChild(element);
|
|
this._attachedPortal = portal;
|
|
super.setDisposeFn(() => {
|
|
if (anchorNode.parentNode) {
|
|
anchorNode.parentNode.replaceChild(element, anchorNode);
|
|
}
|
|
});
|
|
};
|
|
/**
|
|
* Clears out a portal from the DOM.
|
|
*/
|
|
dispose() {
|
|
super.dispose();
|
|
this.outletElement.remove();
|
|
}
|
|
/** Gets the root HTMLElement for an instantiated component. */
|
|
_getComponentRootNode(componentRef) {
|
|
return componentRef.hostView.rootNodes[0];
|
|
}
|
|
};
|
|
var CdkPortal = class _CdkPortal extends TemplatePortal {
|
|
constructor() {
|
|
const templateRef = inject(TemplateRef);
|
|
const viewContainerRef = inject(ViewContainerRef);
|
|
super(templateRef, viewContainerRef);
|
|
}
|
|
static ɵfac = function CdkPortal_Factory(__ngFactoryType__) {
|
|
return new (__ngFactoryType__ || _CdkPortal)();
|
|
};
|
|
static ɵdir = ɵɵdefineDirective({
|
|
type: _CdkPortal,
|
|
selectors: [["", "cdkPortal", ""]],
|
|
exportAs: ["cdkPortal"],
|
|
features: [ɵɵInheritDefinitionFeature]
|
|
});
|
|
};
|
|
(() => {
|
|
(typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(CdkPortal, [{
|
|
type: Directive,
|
|
args: [{
|
|
selector: "[cdkPortal]",
|
|
exportAs: "cdkPortal"
|
|
}]
|
|
}], () => [], null);
|
|
})();
|
|
var TemplatePortalDirective = class _TemplatePortalDirective extends CdkPortal {
|
|
static ɵfac = /* @__PURE__ */ (() => {
|
|
let ɵTemplatePortalDirective_BaseFactory;
|
|
return function TemplatePortalDirective_Factory(__ngFactoryType__) {
|
|
return (ɵTemplatePortalDirective_BaseFactory || (ɵTemplatePortalDirective_BaseFactory = ɵɵgetInheritedFactory(_TemplatePortalDirective)))(__ngFactoryType__ || _TemplatePortalDirective);
|
|
};
|
|
})();
|
|
static ɵdir = ɵɵdefineDirective({
|
|
type: _TemplatePortalDirective,
|
|
selectors: [["", "cdk-portal", ""], ["", "portal", ""]],
|
|
exportAs: ["cdkPortal"],
|
|
features: [ɵɵProvidersFeature([{
|
|
provide: CdkPortal,
|
|
useExisting: _TemplatePortalDirective
|
|
}]), ɵɵInheritDefinitionFeature]
|
|
});
|
|
};
|
|
(() => {
|
|
(typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(TemplatePortalDirective, [{
|
|
type: Directive,
|
|
args: [{
|
|
selector: "[cdk-portal], [portal]",
|
|
exportAs: "cdkPortal",
|
|
providers: [{
|
|
provide: CdkPortal,
|
|
useExisting: TemplatePortalDirective
|
|
}]
|
|
}]
|
|
}], null, null);
|
|
})();
|
|
var CdkPortalOutlet = class _CdkPortalOutlet extends BasePortalOutlet {
|
|
_moduleRef = inject(NgModuleRef$1, {
|
|
optional: true
|
|
});
|
|
_document = inject(DOCUMENT);
|
|
_viewContainerRef = inject(ViewContainerRef);
|
|
/** Whether the portal component is initialized. */
|
|
_isInitialized = false;
|
|
/** Reference to the currently-attached component/view ref. */
|
|
_attachedRef;
|
|
constructor() {
|
|
super();
|
|
}
|
|
/** Portal associated with the Portal outlet. */
|
|
get portal() {
|
|
return this._attachedPortal;
|
|
}
|
|
set portal(portal) {
|
|
if (this.hasAttached() && !portal && !this._isInitialized) {
|
|
return;
|
|
}
|
|
if (this.hasAttached()) {
|
|
super.detach();
|
|
}
|
|
if (portal) {
|
|
super.attach(portal);
|
|
}
|
|
this._attachedPortal = portal || null;
|
|
}
|
|
/** Emits when a portal is attached to the outlet. */
|
|
attached = new EventEmitter();
|
|
/** Component or view reference that is attached to the portal. */
|
|
get attachedRef() {
|
|
return this._attachedRef;
|
|
}
|
|
ngOnInit() {
|
|
this._isInitialized = true;
|
|
}
|
|
ngOnDestroy() {
|
|
super.dispose();
|
|
this._attachedRef = this._attachedPortal = null;
|
|
}
|
|
/**
|
|
* Attach the given ComponentPortal to this PortalOutlet.
|
|
*
|
|
* @param portal Portal to be attached to the portal outlet.
|
|
* @returns Reference to the created component.
|
|
*/
|
|
attachComponentPortal(portal) {
|
|
portal.setAttachedHost(this);
|
|
const viewContainerRef = portal.viewContainerRef != null ? portal.viewContainerRef : this._viewContainerRef;
|
|
const ref = viewContainerRef.createComponent(portal.component, {
|
|
index: viewContainerRef.length,
|
|
injector: portal.injector || viewContainerRef.injector,
|
|
projectableNodes: portal.projectableNodes || void 0,
|
|
ngModuleRef: this._moduleRef || void 0
|
|
});
|
|
if (viewContainerRef !== this._viewContainerRef) {
|
|
this._getRootNode().appendChild(ref.hostView.rootNodes[0]);
|
|
}
|
|
super.setDisposeFn(() => ref.destroy());
|
|
this._attachedPortal = portal;
|
|
this._attachedRef = ref;
|
|
this.attached.emit(ref);
|
|
return ref;
|
|
}
|
|
/**
|
|
* Attach the given TemplatePortal to this PortalHost as an embedded View.
|
|
* @param portal Portal to be attached.
|
|
* @returns Reference to the created embedded view.
|
|
*/
|
|
attachTemplatePortal(portal) {
|
|
portal.setAttachedHost(this);
|
|
const viewRef = this._viewContainerRef.createEmbeddedView(portal.templateRef, portal.context, {
|
|
injector: portal.injector
|
|
});
|
|
super.setDisposeFn(() => this._viewContainerRef.clear());
|
|
this._attachedPortal = portal;
|
|
this._attachedRef = viewRef;
|
|
this.attached.emit(viewRef);
|
|
return viewRef;
|
|
}
|
|
/**
|
|
* Attaches the given DomPortal to this PortalHost by moving all of the portal content into it.
|
|
* @param portal Portal to be attached.
|
|
* @deprecated To be turned into a method.
|
|
* @breaking-change 10.0.0
|
|
*/
|
|
attachDomPortal = (portal) => {
|
|
const element = portal.element;
|
|
if (!element.parentNode && (typeof ngDevMode === "undefined" || ngDevMode)) {
|
|
throw Error("DOM portal content must be attached to a parent node.");
|
|
}
|
|
const anchorNode = this._document.createComment("dom-portal");
|
|
portal.setAttachedHost(this);
|
|
element.parentNode.insertBefore(anchorNode, element);
|
|
this._getRootNode().appendChild(element);
|
|
this._attachedPortal = portal;
|
|
super.setDisposeFn(() => {
|
|
if (anchorNode.parentNode) {
|
|
anchorNode.parentNode.replaceChild(element, anchorNode);
|
|
}
|
|
});
|
|
};
|
|
/** Gets the root node of the portal outlet. */
|
|
_getRootNode() {
|
|
const nativeElement = this._viewContainerRef.element.nativeElement;
|
|
return nativeElement.nodeType === nativeElement.ELEMENT_NODE ? nativeElement : nativeElement.parentNode;
|
|
}
|
|
static ɵfac = function CdkPortalOutlet_Factory(__ngFactoryType__) {
|
|
return new (__ngFactoryType__ || _CdkPortalOutlet)();
|
|
};
|
|
static ɵdir = ɵɵdefineDirective({
|
|
type: _CdkPortalOutlet,
|
|
selectors: [["", "cdkPortalOutlet", ""]],
|
|
inputs: {
|
|
portal: [0, "cdkPortalOutlet", "portal"]
|
|
},
|
|
outputs: {
|
|
attached: "attached"
|
|
},
|
|
exportAs: ["cdkPortalOutlet"],
|
|
features: [ɵɵInheritDefinitionFeature]
|
|
});
|
|
};
|
|
(() => {
|
|
(typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(CdkPortalOutlet, [{
|
|
type: Directive,
|
|
args: [{
|
|
selector: "[cdkPortalOutlet]",
|
|
exportAs: "cdkPortalOutlet"
|
|
}]
|
|
}], () => [], {
|
|
portal: [{
|
|
type: Input,
|
|
args: ["cdkPortalOutlet"]
|
|
}],
|
|
attached: [{
|
|
type: Output
|
|
}]
|
|
});
|
|
})();
|
|
var PortalHostDirective = class _PortalHostDirective extends CdkPortalOutlet {
|
|
static ɵfac = /* @__PURE__ */ (() => {
|
|
let ɵPortalHostDirective_BaseFactory;
|
|
return function PortalHostDirective_Factory(__ngFactoryType__) {
|
|
return (ɵPortalHostDirective_BaseFactory || (ɵPortalHostDirective_BaseFactory = ɵɵgetInheritedFactory(_PortalHostDirective)))(__ngFactoryType__ || _PortalHostDirective);
|
|
};
|
|
})();
|
|
static ɵdir = ɵɵdefineDirective({
|
|
type: _PortalHostDirective,
|
|
selectors: [["", "cdkPortalHost", ""], ["", "portalHost", ""]],
|
|
inputs: {
|
|
portal: [0, "cdkPortalHost", "portal"]
|
|
},
|
|
exportAs: ["cdkPortalHost"],
|
|
features: [ɵɵProvidersFeature([{
|
|
provide: CdkPortalOutlet,
|
|
useExisting: _PortalHostDirective
|
|
}]), ɵɵInheritDefinitionFeature]
|
|
});
|
|
};
|
|
(() => {
|
|
(typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(PortalHostDirective, [{
|
|
type: Directive,
|
|
args: [{
|
|
selector: "[cdkPortalHost], [portalHost]",
|
|
exportAs: "cdkPortalHost",
|
|
inputs: [{
|
|
name: "portal",
|
|
alias: "cdkPortalHost"
|
|
}],
|
|
providers: [{
|
|
provide: CdkPortalOutlet,
|
|
useExisting: PortalHostDirective
|
|
}]
|
|
}]
|
|
}], null, null);
|
|
})();
|
|
var PortalModule = class _PortalModule {
|
|
static ɵfac = function PortalModule_Factory(__ngFactoryType__) {
|
|
return new (__ngFactoryType__ || _PortalModule)();
|
|
};
|
|
static ɵmod = ɵɵdefineNgModule({
|
|
type: _PortalModule,
|
|
imports: [CdkPortal, CdkPortalOutlet, TemplatePortalDirective, PortalHostDirective],
|
|
exports: [CdkPortal, CdkPortalOutlet, TemplatePortalDirective, PortalHostDirective]
|
|
});
|
|
static ɵinj = ɵɵdefineInjector({});
|
|
};
|
|
(() => {
|
|
(typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(PortalModule, [{
|
|
type: NgModule,
|
|
args: [{
|
|
imports: [CdkPortal, CdkPortalOutlet, TemplatePortalDirective, PortalHostDirective],
|
|
exports: [CdkPortal, CdkPortalOutlet, TemplatePortalDirective, PortalHostDirective]
|
|
}]
|
|
}], null, null);
|
|
})();
|
|
|
|
// node_modules/@angular/cdk/fesm2022/id-generator.mjs
|
|
var counters = {};
|
|
var _IdGenerator = class __IdGenerator {
|
|
_appId = inject(APP_ID);
|
|
/**
|
|
* Generates a unique ID with a specific prefix.
|
|
* @param prefix Prefix to add to the ID.
|
|
*/
|
|
getId(prefix) {
|
|
if (this._appId !== "ng") {
|
|
prefix += this._appId;
|
|
}
|
|
if (!counters.hasOwnProperty(prefix)) {
|
|
counters[prefix] = 0;
|
|
}
|
|
return `${prefix}${counters[prefix]++}`;
|
|
}
|
|
static ɵfac = function _IdGenerator_Factory(__ngFactoryType__) {
|
|
return new (__ngFactoryType__ || __IdGenerator)();
|
|
};
|
|
static ɵprov = ɵɵdefineInjectable({
|
|
token: __IdGenerator,
|
|
factory: __IdGenerator.ɵfac,
|
|
providedIn: "root"
|
|
});
|
|
};
|
|
(() => {
|
|
(typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(_IdGenerator, [{
|
|
type: Injectable,
|
|
args: [{
|
|
providedIn: "root"
|
|
}]
|
|
}], null, null);
|
|
})();
|
|
|
|
// node_modules/@angular/cdk/fesm2022/keycodes2.mjs
|
|
var ESCAPE = 27;
|
|
|
|
// node_modules/@angular/cdk/fesm2022/keycodes.mjs
|
|
function hasModifierKey(event, ...modifiers) {
|
|
if (modifiers.length) {
|
|
return modifiers.some((modifier) => event[modifier]);
|
|
}
|
|
return event.altKey || event.shiftKey || event.ctrlKey || event.metaKey;
|
|
}
|
|
|
|
// node_modules/@angular/cdk/fesm2022/overlay-module.mjs
|
|
var scrollBehaviorSupported = supportsScrollBehavior();
|
|
function createBlockScrollStrategy(injector) {
|
|
return new BlockScrollStrategy(injector.get(ViewportRuler), injector.get(DOCUMENT));
|
|
}
|
|
var BlockScrollStrategy = class {
|
|
_viewportRuler;
|
|
_previousHTMLStyles = {
|
|
top: "",
|
|
left: ""
|
|
};
|
|
_previousScrollPosition;
|
|
_isEnabled = false;
|
|
_document;
|
|
constructor(_viewportRuler, document2) {
|
|
this._viewportRuler = _viewportRuler;
|
|
this._document = document2;
|
|
}
|
|
/** Attaches this scroll strategy to an overlay. */
|
|
attach() {
|
|
}
|
|
/** Blocks page-level scroll while the attached overlay is open. */
|
|
enable() {
|
|
if (this._canBeEnabled()) {
|
|
const root = this._document.documentElement;
|
|
this._previousScrollPosition = this._viewportRuler.getViewportScrollPosition();
|
|
this._previousHTMLStyles.left = root.style.left || "";
|
|
this._previousHTMLStyles.top = root.style.top || "";
|
|
root.style.left = coerceCssPixelValue(-this._previousScrollPosition.left);
|
|
root.style.top = coerceCssPixelValue(-this._previousScrollPosition.top);
|
|
root.classList.add("cdk-global-scrollblock");
|
|
this._isEnabled = true;
|
|
}
|
|
}
|
|
/** Unblocks page-level scroll while the attached overlay is open. */
|
|
disable() {
|
|
if (this._isEnabled) {
|
|
const html = this._document.documentElement;
|
|
const body = this._document.body;
|
|
const htmlStyle = html.style;
|
|
const bodyStyle = body.style;
|
|
const previousHtmlScrollBehavior = htmlStyle.scrollBehavior || "";
|
|
const previousBodyScrollBehavior = bodyStyle.scrollBehavior || "";
|
|
this._isEnabled = false;
|
|
htmlStyle.left = this._previousHTMLStyles.left;
|
|
htmlStyle.top = this._previousHTMLStyles.top;
|
|
html.classList.remove("cdk-global-scrollblock");
|
|
if (scrollBehaviorSupported) {
|
|
htmlStyle.scrollBehavior = bodyStyle.scrollBehavior = "auto";
|
|
}
|
|
window.scroll(this._previousScrollPosition.left, this._previousScrollPosition.top);
|
|
if (scrollBehaviorSupported) {
|
|
htmlStyle.scrollBehavior = previousHtmlScrollBehavior;
|
|
bodyStyle.scrollBehavior = previousBodyScrollBehavior;
|
|
}
|
|
}
|
|
}
|
|
_canBeEnabled() {
|
|
const html = this._document.documentElement;
|
|
if (html.classList.contains("cdk-global-scrollblock") || this._isEnabled) {
|
|
return false;
|
|
}
|
|
const rootElement = this._document.documentElement;
|
|
const viewport = this._viewportRuler.getViewportSize();
|
|
return rootElement.scrollHeight > viewport.height || rootElement.scrollWidth > viewport.width;
|
|
}
|
|
};
|
|
function getMatScrollStrategyAlreadyAttachedError() {
|
|
return Error(`Scroll strategy has already been attached.`);
|
|
}
|
|
function createCloseScrollStrategy(injector, config) {
|
|
return new CloseScrollStrategy(injector.get(ScrollDispatcher), injector.get(NgZone), injector.get(ViewportRuler), config);
|
|
}
|
|
var CloseScrollStrategy = class {
|
|
_scrollDispatcher;
|
|
_ngZone;
|
|
_viewportRuler;
|
|
_config;
|
|
_scrollSubscription = null;
|
|
_overlayRef;
|
|
_initialScrollPosition;
|
|
constructor(_scrollDispatcher, _ngZone, _viewportRuler, _config) {
|
|
this._scrollDispatcher = _scrollDispatcher;
|
|
this._ngZone = _ngZone;
|
|
this._viewportRuler = _viewportRuler;
|
|
this._config = _config;
|
|
}
|
|
/** Attaches this scroll strategy to an overlay. */
|
|
attach(overlayRef) {
|
|
if (this._overlayRef && (typeof ngDevMode === "undefined" || ngDevMode)) {
|
|
throw getMatScrollStrategyAlreadyAttachedError();
|
|
}
|
|
this._overlayRef = overlayRef;
|
|
}
|
|
/** Enables the closing of the attached overlay on scroll. */
|
|
enable() {
|
|
if (this._scrollSubscription) {
|
|
return;
|
|
}
|
|
const stream = this._scrollDispatcher.scrolled(0).pipe(filter((scrollable) => {
|
|
return !scrollable || !this._overlayRef.overlayElement.contains(scrollable.getElementRef().nativeElement);
|
|
}));
|
|
if (this._config && this._config.threshold && this._config.threshold > 1) {
|
|
this._initialScrollPosition = this._viewportRuler.getViewportScrollPosition().top;
|
|
this._scrollSubscription = stream.subscribe(() => {
|
|
const scrollPosition = this._viewportRuler.getViewportScrollPosition().top;
|
|
if (Math.abs(scrollPosition - this._initialScrollPosition) > this._config.threshold) {
|
|
this._detach();
|
|
} else {
|
|
this._overlayRef.updatePosition();
|
|
}
|
|
});
|
|
} else {
|
|
this._scrollSubscription = stream.subscribe(this._detach);
|
|
}
|
|
}
|
|
/** Disables the closing the attached overlay on scroll. */
|
|
disable() {
|
|
if (this._scrollSubscription) {
|
|
this._scrollSubscription.unsubscribe();
|
|
this._scrollSubscription = null;
|
|
}
|
|
}
|
|
detach() {
|
|
this.disable();
|
|
this._overlayRef = null;
|
|
}
|
|
/** Detaches the overlay ref and disables the scroll strategy. */
|
|
_detach = () => {
|
|
this.disable();
|
|
if (this._overlayRef.hasAttached()) {
|
|
this._ngZone.run(() => this._overlayRef.detach());
|
|
}
|
|
};
|
|
};
|
|
function createNoopScrollStrategy() {
|
|
return new NoopScrollStrategy();
|
|
}
|
|
var NoopScrollStrategy = class {
|
|
/** Does nothing, as this scroll strategy is a no-op. */
|
|
enable() {
|
|
}
|
|
/** Does nothing, as this scroll strategy is a no-op. */
|
|
disable() {
|
|
}
|
|
/** Does nothing, as this scroll strategy is a no-op. */
|
|
attach() {
|
|
}
|
|
};
|
|
function isElementScrolledOutsideView(element, scrollContainers) {
|
|
return scrollContainers.some((containerBounds) => {
|
|
const outsideAbove = element.bottom < containerBounds.top;
|
|
const outsideBelow = element.top > containerBounds.bottom;
|
|
const outsideLeft = element.right < containerBounds.left;
|
|
const outsideRight = element.left > containerBounds.right;
|
|
return outsideAbove || outsideBelow || outsideLeft || outsideRight;
|
|
});
|
|
}
|
|
function isElementClippedByScrolling(element, scrollContainers) {
|
|
return scrollContainers.some((scrollContainerRect) => {
|
|
const clippedAbove = element.top < scrollContainerRect.top;
|
|
const clippedBelow = element.bottom > scrollContainerRect.bottom;
|
|
const clippedLeft = element.left < scrollContainerRect.left;
|
|
const clippedRight = element.right > scrollContainerRect.right;
|
|
return clippedAbove || clippedBelow || clippedLeft || clippedRight;
|
|
});
|
|
}
|
|
function createRepositionScrollStrategy(injector, config) {
|
|
return new RepositionScrollStrategy(injector.get(ScrollDispatcher), injector.get(ViewportRuler), injector.get(NgZone), config);
|
|
}
|
|
var RepositionScrollStrategy = class {
|
|
_scrollDispatcher;
|
|
_viewportRuler;
|
|
_ngZone;
|
|
_config;
|
|
_scrollSubscription = null;
|
|
_overlayRef;
|
|
constructor(_scrollDispatcher, _viewportRuler, _ngZone, _config) {
|
|
this._scrollDispatcher = _scrollDispatcher;
|
|
this._viewportRuler = _viewportRuler;
|
|
this._ngZone = _ngZone;
|
|
this._config = _config;
|
|
}
|
|
/** Attaches this scroll strategy to an overlay. */
|
|
attach(overlayRef) {
|
|
if (this._overlayRef && (typeof ngDevMode === "undefined" || ngDevMode)) {
|
|
throw getMatScrollStrategyAlreadyAttachedError();
|
|
}
|
|
this._overlayRef = overlayRef;
|
|
}
|
|
/** Enables repositioning of the attached overlay on scroll. */
|
|
enable() {
|
|
if (!this._scrollSubscription) {
|
|
const throttle = this._config ? this._config.scrollThrottle : 0;
|
|
this._scrollSubscription = this._scrollDispatcher.scrolled(throttle).subscribe(() => {
|
|
this._overlayRef.updatePosition();
|
|
if (this._config && this._config.autoClose) {
|
|
const overlayRect = this._overlayRef.overlayElement.getBoundingClientRect();
|
|
const {
|
|
width,
|
|
height
|
|
} = this._viewportRuler.getViewportSize();
|
|
const parentRects = [{
|
|
width,
|
|
height,
|
|
bottom: height,
|
|
right: width,
|
|
top: 0,
|
|
left: 0
|
|
}];
|
|
if (isElementScrolledOutsideView(overlayRect, parentRects)) {
|
|
this.disable();
|
|
this._ngZone.run(() => this._overlayRef.detach());
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}
|
|
/** Disables repositioning of the attached overlay on scroll. */
|
|
disable() {
|
|
if (this._scrollSubscription) {
|
|
this._scrollSubscription.unsubscribe();
|
|
this._scrollSubscription = null;
|
|
}
|
|
}
|
|
detach() {
|
|
this.disable();
|
|
this._overlayRef = null;
|
|
}
|
|
};
|
|
var ScrollStrategyOptions = class _ScrollStrategyOptions {
|
|
_injector = inject(Injector);
|
|
constructor() {
|
|
}
|
|
/** Do nothing on scroll. */
|
|
noop = () => new NoopScrollStrategy();
|
|
/**
|
|
* Close the overlay as soon as the user scrolls.
|
|
* @param config Configuration to be used inside the scroll strategy.
|
|
*/
|
|
close = (config) => createCloseScrollStrategy(this._injector, config);
|
|
/** Block scrolling. */
|
|
block = () => createBlockScrollStrategy(this._injector);
|
|
/**
|
|
* Update the overlay's position on scroll.
|
|
* @param config Configuration to be used inside the scroll strategy.
|
|
* Allows debouncing the reposition calls.
|
|
*/
|
|
reposition = (config) => createRepositionScrollStrategy(this._injector, config);
|
|
static ɵfac = function ScrollStrategyOptions_Factory(__ngFactoryType__) {
|
|
return new (__ngFactoryType__ || _ScrollStrategyOptions)();
|
|
};
|
|
static ɵprov = ɵɵdefineInjectable({
|
|
token: _ScrollStrategyOptions,
|
|
factory: _ScrollStrategyOptions.ɵfac,
|
|
providedIn: "root"
|
|
});
|
|
};
|
|
(() => {
|
|
(typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(ScrollStrategyOptions, [{
|
|
type: Injectable,
|
|
args: [{
|
|
providedIn: "root"
|
|
}]
|
|
}], () => [], null);
|
|
})();
|
|
var OverlayConfig = class {
|
|
/** Strategy with which to position the overlay. */
|
|
positionStrategy;
|
|
/** Strategy to be used when handling scroll events while the overlay is open. */
|
|
scrollStrategy = new NoopScrollStrategy();
|
|
/** Custom class to add to the overlay pane. */
|
|
panelClass = "";
|
|
/** Whether the overlay has a backdrop. */
|
|
hasBackdrop = false;
|
|
/** Custom class to add to the backdrop */
|
|
backdropClass = "cdk-overlay-dark-backdrop";
|
|
/** Whether to disable any built-in animations. */
|
|
disableAnimations;
|
|
/** The width of the overlay panel. If a number is provided, pixel units are assumed. */
|
|
width;
|
|
/** The height of the overlay panel. If a number is provided, pixel units are assumed. */
|
|
height;
|
|
/** The min-width of the overlay panel. If a number is provided, pixel units are assumed. */
|
|
minWidth;
|
|
/** The min-height of the overlay panel. If a number is provided, pixel units are assumed. */
|
|
minHeight;
|
|
/** The max-width of the overlay panel. If a number is provided, pixel units are assumed. */
|
|
maxWidth;
|
|
/** The max-height of the overlay panel. If a number is provided, pixel units are assumed. */
|
|
maxHeight;
|
|
/**
|
|
* Direction of the text in the overlay panel. If a `Directionality` instance
|
|
* is passed in, the overlay will handle changes to its value automatically.
|
|
*/
|
|
direction;
|
|
/**
|
|
* Whether the overlay should be disposed of when the user goes backwards/forwards in history.
|
|
* Note that this usually doesn't include clicking on links (unless the user is using
|
|
* the `HashLocationStrategy`).
|
|
*/
|
|
disposeOnNavigation = false;
|
|
constructor(config) {
|
|
if (config) {
|
|
const configKeys = Object.keys(config);
|
|
for (const key of configKeys) {
|
|
if (config[key] !== void 0) {
|
|
this[key] = config[key];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
var ConnectedOverlayPositionChange = class {
|
|
connectionPair;
|
|
scrollableViewProperties;
|
|
constructor(connectionPair, scrollableViewProperties) {
|
|
this.connectionPair = connectionPair;
|
|
this.scrollableViewProperties = scrollableViewProperties;
|
|
}
|
|
};
|
|
function validateVerticalPosition(property, value) {
|
|
if (value !== "top" && value !== "bottom" && value !== "center") {
|
|
throw Error(`ConnectedPosition: Invalid ${property} "${value}". Expected "top", "bottom" or "center".`);
|
|
}
|
|
}
|
|
function validateHorizontalPosition(property, value) {
|
|
if (value !== "start" && value !== "end" && value !== "center") {
|
|
throw Error(`ConnectedPosition: Invalid ${property} "${value}". Expected "start", "end" or "center".`);
|
|
}
|
|
}
|
|
var BaseOverlayDispatcher = class _BaseOverlayDispatcher {
|
|
/** Currently attached overlays in the order they were attached. */
|
|
_attachedOverlays = [];
|
|
_document = inject(DOCUMENT);
|
|
_isAttached;
|
|
constructor() {
|
|
}
|
|
ngOnDestroy() {
|
|
this.detach();
|
|
}
|
|
/** Add a new overlay to the list of attached overlay refs. */
|
|
add(overlayRef) {
|
|
this.remove(overlayRef);
|
|
this._attachedOverlays.push(overlayRef);
|
|
}
|
|
/** Remove an overlay from the list of attached overlay refs. */
|
|
remove(overlayRef) {
|
|
const index = this._attachedOverlays.indexOf(overlayRef);
|
|
if (index > -1) {
|
|
this._attachedOverlays.splice(index, 1);
|
|
}
|
|
if (this._attachedOverlays.length === 0) {
|
|
this.detach();
|
|
}
|
|
}
|
|
static ɵfac = function BaseOverlayDispatcher_Factory(__ngFactoryType__) {
|
|
return new (__ngFactoryType__ || _BaseOverlayDispatcher)();
|
|
};
|
|
static ɵprov = ɵɵdefineInjectable({
|
|
token: _BaseOverlayDispatcher,
|
|
factory: _BaseOverlayDispatcher.ɵfac,
|
|
providedIn: "root"
|
|
});
|
|
};
|
|
(() => {
|
|
(typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(BaseOverlayDispatcher, [{
|
|
type: Injectable,
|
|
args: [{
|
|
providedIn: "root"
|
|
}]
|
|
}], () => [], null);
|
|
})();
|
|
var OverlayKeyboardDispatcher = class _OverlayKeyboardDispatcher extends BaseOverlayDispatcher {
|
|
_ngZone = inject(NgZone);
|
|
_renderer = inject(RendererFactory2).createRenderer(null, null);
|
|
_cleanupKeydown;
|
|
/** Add a new overlay to the list of attached overlay refs. */
|
|
add(overlayRef) {
|
|
super.add(overlayRef);
|
|
if (!this._isAttached) {
|
|
this._ngZone.runOutsideAngular(() => {
|
|
this._cleanupKeydown = this._renderer.listen("body", "keydown", this._keydownListener);
|
|
});
|
|
this._isAttached = true;
|
|
}
|
|
}
|
|
/** Detaches the global keyboard event listener. */
|
|
detach() {
|
|
if (this._isAttached) {
|
|
this._cleanupKeydown?.();
|
|
this._isAttached = false;
|
|
}
|
|
}
|
|
/** Keyboard event listener that will be attached to the body. */
|
|
_keydownListener = (event) => {
|
|
const overlays = this._attachedOverlays;
|
|
for (let i = overlays.length - 1; i > -1; i--) {
|
|
if (overlays[i]._keydownEvents.observers.length > 0) {
|
|
this._ngZone.run(() => overlays[i]._keydownEvents.next(event));
|
|
break;
|
|
}
|
|
}
|
|
};
|
|
static ɵfac = /* @__PURE__ */ (() => {
|
|
let ɵOverlayKeyboardDispatcher_BaseFactory;
|
|
return function OverlayKeyboardDispatcher_Factory(__ngFactoryType__) {
|
|
return (ɵOverlayKeyboardDispatcher_BaseFactory || (ɵOverlayKeyboardDispatcher_BaseFactory = ɵɵgetInheritedFactory(_OverlayKeyboardDispatcher)))(__ngFactoryType__ || _OverlayKeyboardDispatcher);
|
|
};
|
|
})();
|
|
static ɵprov = ɵɵdefineInjectable({
|
|
token: _OverlayKeyboardDispatcher,
|
|
factory: _OverlayKeyboardDispatcher.ɵfac,
|
|
providedIn: "root"
|
|
});
|
|
};
|
|
(() => {
|
|
(typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(OverlayKeyboardDispatcher, [{
|
|
type: Injectable,
|
|
args: [{
|
|
providedIn: "root"
|
|
}]
|
|
}], null, null);
|
|
})();
|
|
var OverlayOutsideClickDispatcher = class _OverlayOutsideClickDispatcher extends BaseOverlayDispatcher {
|
|
_platform = inject(Platform);
|
|
_ngZone = inject(NgZone);
|
|
_renderer = inject(RendererFactory2).createRenderer(null, null);
|
|
_cursorOriginalValue;
|
|
_cursorStyleIsSet = false;
|
|
_pointerDownEventTarget;
|
|
_cleanups;
|
|
/** Add a new overlay to the list of attached overlay refs. */
|
|
add(overlayRef) {
|
|
super.add(overlayRef);
|
|
if (!this._isAttached) {
|
|
const body = this._document.body;
|
|
const eventOptions = {
|
|
capture: true
|
|
};
|
|
const renderer = this._renderer;
|
|
this._cleanups = this._ngZone.runOutsideAngular(() => [renderer.listen(body, "pointerdown", this._pointerDownListener, eventOptions), renderer.listen(body, "click", this._clickListener, eventOptions), renderer.listen(body, "auxclick", this._clickListener, eventOptions), renderer.listen(body, "contextmenu", this._clickListener, eventOptions)]);
|
|
if (this._platform.IOS && !this._cursorStyleIsSet) {
|
|
this._cursorOriginalValue = body.style.cursor;
|
|
body.style.cursor = "pointer";
|
|
this._cursorStyleIsSet = true;
|
|
}
|
|
this._isAttached = true;
|
|
}
|
|
}
|
|
/** Detaches the global keyboard event listener. */
|
|
detach() {
|
|
if (this._isAttached) {
|
|
this._cleanups?.forEach((cleanup) => cleanup());
|
|
this._cleanups = void 0;
|
|
if (this._platform.IOS && this._cursorStyleIsSet) {
|
|
this._document.body.style.cursor = this._cursorOriginalValue;
|
|
this._cursorStyleIsSet = false;
|
|
}
|
|
this._isAttached = false;
|
|
}
|
|
}
|
|
/** Store pointerdown event target to track origin of click. */
|
|
_pointerDownListener = (event) => {
|
|
this._pointerDownEventTarget = _getEventTarget(event);
|
|
};
|
|
/** Click event listener that will be attached to the body propagate phase. */
|
|
_clickListener = (event) => {
|
|
const target = _getEventTarget(event);
|
|
const origin = event.type === "click" && this._pointerDownEventTarget ? this._pointerDownEventTarget : target;
|
|
this._pointerDownEventTarget = null;
|
|
const overlays = this._attachedOverlays.slice();
|
|
for (let i = overlays.length - 1; i > -1; i--) {
|
|
const overlayRef = overlays[i];
|
|
if (overlayRef._outsidePointerEvents.observers.length < 1 || !overlayRef.hasAttached()) {
|
|
continue;
|
|
}
|
|
if (containsPierceShadowDom(overlayRef.overlayElement, target) || containsPierceShadowDom(overlayRef.overlayElement, origin)) {
|
|
break;
|
|
}
|
|
const outsidePointerEvents = overlayRef._outsidePointerEvents;
|
|
if (this._ngZone) {
|
|
this._ngZone.run(() => outsidePointerEvents.next(event));
|
|
} else {
|
|
outsidePointerEvents.next(event);
|
|
}
|
|
}
|
|
};
|
|
static ɵfac = /* @__PURE__ */ (() => {
|
|
let ɵOverlayOutsideClickDispatcher_BaseFactory;
|
|
return function OverlayOutsideClickDispatcher_Factory(__ngFactoryType__) {
|
|
return (ɵOverlayOutsideClickDispatcher_BaseFactory || (ɵOverlayOutsideClickDispatcher_BaseFactory = ɵɵgetInheritedFactory(_OverlayOutsideClickDispatcher)))(__ngFactoryType__ || _OverlayOutsideClickDispatcher);
|
|
};
|
|
})();
|
|
static ɵprov = ɵɵdefineInjectable({
|
|
token: _OverlayOutsideClickDispatcher,
|
|
factory: _OverlayOutsideClickDispatcher.ɵfac,
|
|
providedIn: "root"
|
|
});
|
|
};
|
|
(() => {
|
|
(typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(OverlayOutsideClickDispatcher, [{
|
|
type: Injectable,
|
|
args: [{
|
|
providedIn: "root"
|
|
}]
|
|
}], null, null);
|
|
})();
|
|
function containsPierceShadowDom(parent, child) {
|
|
const supportsShadowRoot = typeof ShadowRoot !== "undefined" && ShadowRoot;
|
|
let current = child;
|
|
while (current) {
|
|
if (current === parent) {
|
|
return true;
|
|
}
|
|
current = supportsShadowRoot && current instanceof ShadowRoot ? current.host : current.parentNode;
|
|
}
|
|
return false;
|
|
}
|
|
var _CdkOverlayStyleLoader = class __CdkOverlayStyleLoader {
|
|
static ɵfac = function _CdkOverlayStyleLoader_Factory(__ngFactoryType__) {
|
|
return new (__ngFactoryType__ || __CdkOverlayStyleLoader)();
|
|
};
|
|
static ɵcmp = ɵɵdefineComponent({
|
|
type: __CdkOverlayStyleLoader,
|
|
selectors: [["ng-component"]],
|
|
hostAttrs: ["cdk-overlay-style-loader", ""],
|
|
decls: 0,
|
|
vars: 0,
|
|
template: function _CdkOverlayStyleLoader_Template(rf, ctx) {
|
|
},
|
|
styles: [".cdk-overlay-container,.cdk-global-overlay-wrapper{pointer-events:none;top:0;left:0;height:100%;width:100%}.cdk-overlay-container{position:fixed}@layer cdk-overlay{.cdk-overlay-container{z-index:1000}}.cdk-overlay-container:empty{display:none}.cdk-global-overlay-wrapper{display:flex;position:absolute}@layer cdk-overlay{.cdk-global-overlay-wrapper{z-index:1000}}.cdk-overlay-pane{position:absolute;pointer-events:auto;box-sizing:border-box;display:flex;max-width:100%;max-height:100%}@layer cdk-overlay{.cdk-overlay-pane{z-index:1000}}.cdk-overlay-backdrop{position:absolute;top:0;bottom:0;left:0;right:0;pointer-events:auto;-webkit-tap-highlight-color:rgba(0,0,0,0);opacity:0;touch-action:manipulation}@layer cdk-overlay{.cdk-overlay-backdrop{z-index:1000;transition:opacity 400ms cubic-bezier(0.25, 0.8, 0.25, 1)}}@media(prefers-reduced-motion){.cdk-overlay-backdrop{transition-duration:1ms}}.cdk-overlay-backdrop-showing{opacity:1}@media(forced-colors: active){.cdk-overlay-backdrop-showing{opacity:.6}}@layer cdk-overlay{.cdk-overlay-dark-backdrop{background:rgba(0,0,0,.32)}}.cdk-overlay-transparent-backdrop{transition:visibility 1ms linear,opacity 1ms linear;visibility:hidden;opacity:1}.cdk-overlay-transparent-backdrop.cdk-overlay-backdrop-showing,.cdk-high-contrast-active .cdk-overlay-transparent-backdrop{opacity:0;visibility:visible}.cdk-overlay-backdrop-noop-animation{transition:none}.cdk-overlay-connected-position-bounding-box{position:absolute;display:flex;flex-direction:column;min-width:1px;min-height:1px}@layer cdk-overlay{.cdk-overlay-connected-position-bounding-box{z-index:1000}}.cdk-global-scrollblock{position:fixed;width:100%;overflow-y:scroll}\n"],
|
|
encapsulation: 2,
|
|
changeDetection: 0
|
|
});
|
|
};
|
|
(() => {
|
|
(typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(_CdkOverlayStyleLoader, [{
|
|
type: Component,
|
|
args: [{
|
|
template: "",
|
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
encapsulation: ViewEncapsulation.None,
|
|
host: {
|
|
"cdk-overlay-style-loader": ""
|
|
},
|
|
styles: [".cdk-overlay-container,.cdk-global-overlay-wrapper{pointer-events:none;top:0;left:0;height:100%;width:100%}.cdk-overlay-container{position:fixed}@layer cdk-overlay{.cdk-overlay-container{z-index:1000}}.cdk-overlay-container:empty{display:none}.cdk-global-overlay-wrapper{display:flex;position:absolute}@layer cdk-overlay{.cdk-global-overlay-wrapper{z-index:1000}}.cdk-overlay-pane{position:absolute;pointer-events:auto;box-sizing:border-box;display:flex;max-width:100%;max-height:100%}@layer cdk-overlay{.cdk-overlay-pane{z-index:1000}}.cdk-overlay-backdrop{position:absolute;top:0;bottom:0;left:0;right:0;pointer-events:auto;-webkit-tap-highlight-color:rgba(0,0,0,0);opacity:0;touch-action:manipulation}@layer cdk-overlay{.cdk-overlay-backdrop{z-index:1000;transition:opacity 400ms cubic-bezier(0.25, 0.8, 0.25, 1)}}@media(prefers-reduced-motion){.cdk-overlay-backdrop{transition-duration:1ms}}.cdk-overlay-backdrop-showing{opacity:1}@media(forced-colors: active){.cdk-overlay-backdrop-showing{opacity:.6}}@layer cdk-overlay{.cdk-overlay-dark-backdrop{background:rgba(0,0,0,.32)}}.cdk-overlay-transparent-backdrop{transition:visibility 1ms linear,opacity 1ms linear;visibility:hidden;opacity:1}.cdk-overlay-transparent-backdrop.cdk-overlay-backdrop-showing,.cdk-high-contrast-active .cdk-overlay-transparent-backdrop{opacity:0;visibility:visible}.cdk-overlay-backdrop-noop-animation{transition:none}.cdk-overlay-connected-position-bounding-box{position:absolute;display:flex;flex-direction:column;min-width:1px;min-height:1px}@layer cdk-overlay{.cdk-overlay-connected-position-bounding-box{z-index:1000}}.cdk-global-scrollblock{position:fixed;width:100%;overflow-y:scroll}\n"]
|
|
}]
|
|
}], null, null);
|
|
})();
|
|
var OverlayContainer = class _OverlayContainer {
|
|
_platform = inject(Platform);
|
|
_containerElement;
|
|
_document = inject(DOCUMENT);
|
|
_styleLoader = inject(_CdkPrivateStyleLoader);
|
|
constructor() {
|
|
}
|
|
ngOnDestroy() {
|
|
this._containerElement?.remove();
|
|
}
|
|
/**
|
|
* This method returns the overlay container element. It will lazily
|
|
* create the element the first time it is called to facilitate using
|
|
* the container in non-browser environments.
|
|
* @returns the container element
|
|
*/
|
|
getContainerElement() {
|
|
this._loadStyles();
|
|
if (!this._containerElement) {
|
|
this._createContainer();
|
|
}
|
|
return this._containerElement;
|
|
}
|
|
/**
|
|
* Create the overlay container element, which is simply a div
|
|
* with the 'cdk-overlay-container' class on the document body.
|
|
*/
|
|
_createContainer() {
|
|
const containerClass = "cdk-overlay-container";
|
|
if (this._platform.isBrowser || _isTestEnvironment()) {
|
|
const oppositePlatformContainers = this._document.querySelectorAll(`.${containerClass}[platform="server"], .${containerClass}[platform="test"]`);
|
|
for (let i = 0; i < oppositePlatformContainers.length; i++) {
|
|
oppositePlatformContainers[i].remove();
|
|
}
|
|
}
|
|
const container = this._document.createElement("div");
|
|
container.classList.add(containerClass);
|
|
if (_isTestEnvironment()) {
|
|
container.setAttribute("platform", "test");
|
|
} else if (!this._platform.isBrowser) {
|
|
container.setAttribute("platform", "server");
|
|
}
|
|
this._document.body.appendChild(container);
|
|
this._containerElement = container;
|
|
}
|
|
/** Loads the structural styles necessary for the overlay to work. */
|
|
_loadStyles() {
|
|
this._styleLoader.load(_CdkOverlayStyleLoader);
|
|
}
|
|
static ɵfac = function OverlayContainer_Factory(__ngFactoryType__) {
|
|
return new (__ngFactoryType__ || _OverlayContainer)();
|
|
};
|
|
static ɵprov = ɵɵdefineInjectable({
|
|
token: _OverlayContainer,
|
|
factory: _OverlayContainer.ɵfac,
|
|
providedIn: "root"
|
|
});
|
|
};
|
|
(() => {
|
|
(typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(OverlayContainer, [{
|
|
type: Injectable,
|
|
args: [{
|
|
providedIn: "root"
|
|
}]
|
|
}], () => [], null);
|
|
})();
|
|
var BackdropRef = class {
|
|
_renderer;
|
|
_ngZone;
|
|
element;
|
|
_cleanupClick;
|
|
_cleanupTransitionEnd;
|
|
_fallbackTimeout;
|
|
constructor(document2, _renderer, _ngZone, onClick) {
|
|
this._renderer = _renderer;
|
|
this._ngZone = _ngZone;
|
|
this.element = document2.createElement("div");
|
|
this.element.classList.add("cdk-overlay-backdrop");
|
|
this._cleanupClick = _renderer.listen(this.element, "click", onClick);
|
|
}
|
|
detach() {
|
|
this._ngZone.runOutsideAngular(() => {
|
|
const element = this.element;
|
|
clearTimeout(this._fallbackTimeout);
|
|
this._cleanupTransitionEnd?.();
|
|
this._cleanupTransitionEnd = this._renderer.listen(element, "transitionend", this.dispose);
|
|
this._fallbackTimeout = setTimeout(this.dispose, 500);
|
|
element.style.pointerEvents = "none";
|
|
element.classList.remove("cdk-overlay-backdrop-showing");
|
|
});
|
|
}
|
|
dispose = () => {
|
|
clearTimeout(this._fallbackTimeout);
|
|
this._cleanupClick?.();
|
|
this._cleanupTransitionEnd?.();
|
|
this._cleanupClick = this._cleanupTransitionEnd = this._fallbackTimeout = void 0;
|
|
this.element.remove();
|
|
};
|
|
};
|
|
var OverlayRef = class {
|
|
_portalOutlet;
|
|
_host;
|
|
_pane;
|
|
_config;
|
|
_ngZone;
|
|
_keyboardDispatcher;
|
|
_document;
|
|
_location;
|
|
_outsideClickDispatcher;
|
|
_animationsDisabled;
|
|
_injector;
|
|
_renderer;
|
|
_backdropClick = new Subject();
|
|
_attachments = new Subject();
|
|
_detachments = new Subject();
|
|
_positionStrategy;
|
|
_scrollStrategy;
|
|
_locationChanges = Subscription.EMPTY;
|
|
_backdropRef = null;
|
|
_detachContentMutationObserver;
|
|
_detachContentAfterRenderRef;
|
|
/**
|
|
* Reference to the parent of the `_host` at the time it was detached. Used to restore
|
|
* the `_host` to its original position in the DOM when it gets re-attached.
|
|
*/
|
|
_previousHostParent;
|
|
/** Stream of keydown events dispatched to this overlay. */
|
|
_keydownEvents = new Subject();
|
|
/** Stream of mouse outside events dispatched to this overlay. */
|
|
_outsidePointerEvents = new Subject();
|
|
/** Reference to the currently-running `afterNextRender` call. */
|
|
_afterNextRenderRef;
|
|
constructor(_portalOutlet, _host, _pane, _config, _ngZone, _keyboardDispatcher, _document, _location, _outsideClickDispatcher, _animationsDisabled = false, _injector, _renderer) {
|
|
this._portalOutlet = _portalOutlet;
|
|
this._host = _host;
|
|
this._pane = _pane;
|
|
this._config = _config;
|
|
this._ngZone = _ngZone;
|
|
this._keyboardDispatcher = _keyboardDispatcher;
|
|
this._document = _document;
|
|
this._location = _location;
|
|
this._outsideClickDispatcher = _outsideClickDispatcher;
|
|
this._animationsDisabled = _animationsDisabled;
|
|
this._injector = _injector;
|
|
this._renderer = _renderer;
|
|
if (_config.scrollStrategy) {
|
|
this._scrollStrategy = _config.scrollStrategy;
|
|
this._scrollStrategy.attach(this);
|
|
}
|
|
this._positionStrategy = _config.positionStrategy;
|
|
}
|
|
/** The overlay's HTML element */
|
|
get overlayElement() {
|
|
return this._pane;
|
|
}
|
|
/** The overlay's backdrop HTML element. */
|
|
get backdropElement() {
|
|
return this._backdropRef?.element || null;
|
|
}
|
|
/**
|
|
* Wrapper around the panel element. Can be used for advanced
|
|
* positioning where a wrapper with specific styling is
|
|
* required around the overlay pane.
|
|
*/
|
|
get hostElement() {
|
|
return this._host;
|
|
}
|
|
/**
|
|
* Attaches content, given via a Portal, to the overlay.
|
|
* If the overlay is configured to have a backdrop, it will be created.
|
|
*
|
|
* @param portal Portal instance to which to attach the overlay.
|
|
* @returns The portal attachment result.
|
|
*/
|
|
attach(portal) {
|
|
if (!this._host.parentElement && this._previousHostParent) {
|
|
this._previousHostParent.appendChild(this._host);
|
|
}
|
|
const attachResult = this._portalOutlet.attach(portal);
|
|
if (this._positionStrategy) {
|
|
this._positionStrategy.attach(this);
|
|
}
|
|
this._updateStackingOrder();
|
|
this._updateElementSize();
|
|
this._updateElementDirection();
|
|
if (this._scrollStrategy) {
|
|
this._scrollStrategy.enable();
|
|
}
|
|
this._afterNextRenderRef?.destroy();
|
|
this._afterNextRenderRef = afterNextRender(() => {
|
|
if (this.hasAttached()) {
|
|
this.updatePosition();
|
|
}
|
|
}, {
|
|
injector: this._injector
|
|
});
|
|
this._togglePointerEvents(true);
|
|
if (this._config.hasBackdrop) {
|
|
this._attachBackdrop();
|
|
}
|
|
if (this._config.panelClass) {
|
|
this._toggleClasses(this._pane, this._config.panelClass, true);
|
|
}
|
|
this._attachments.next();
|
|
this._completeDetachContent();
|
|
this._keyboardDispatcher.add(this);
|
|
if (this._config.disposeOnNavigation) {
|
|
this._locationChanges = this._location.subscribe(() => this.dispose());
|
|
}
|
|
this._outsideClickDispatcher.add(this);
|
|
if (typeof attachResult?.onDestroy === "function") {
|
|
attachResult.onDestroy(() => {
|
|
if (this.hasAttached()) {
|
|
this._ngZone.runOutsideAngular(() => Promise.resolve().then(() => this.detach()));
|
|
}
|
|
});
|
|
}
|
|
return attachResult;
|
|
}
|
|
/**
|
|
* Detaches an overlay from a portal.
|
|
* @returns The portal detachment result.
|
|
*/
|
|
detach() {
|
|
if (!this.hasAttached()) {
|
|
return;
|
|
}
|
|
this.detachBackdrop();
|
|
this._togglePointerEvents(false);
|
|
if (this._positionStrategy && this._positionStrategy.detach) {
|
|
this._positionStrategy.detach();
|
|
}
|
|
if (this._scrollStrategy) {
|
|
this._scrollStrategy.disable();
|
|
}
|
|
const detachmentResult = this._portalOutlet.detach();
|
|
this._detachments.next();
|
|
this._completeDetachContent();
|
|
this._keyboardDispatcher.remove(this);
|
|
this._detachContentWhenEmpty();
|
|
this._locationChanges.unsubscribe();
|
|
this._outsideClickDispatcher.remove(this);
|
|
return detachmentResult;
|
|
}
|
|
/** Cleans up the overlay from the DOM. */
|
|
dispose() {
|
|
const isAttached = this.hasAttached();
|
|
if (this._positionStrategy) {
|
|
this._positionStrategy.dispose();
|
|
}
|
|
this._disposeScrollStrategy();
|
|
this._backdropRef?.dispose();
|
|
this._locationChanges.unsubscribe();
|
|
this._keyboardDispatcher.remove(this);
|
|
this._portalOutlet.dispose();
|
|
this._attachments.complete();
|
|
this._backdropClick.complete();
|
|
this._keydownEvents.complete();
|
|
this._outsidePointerEvents.complete();
|
|
this._outsideClickDispatcher.remove(this);
|
|
this._host?.remove();
|
|
this._afterNextRenderRef?.destroy();
|
|
this._previousHostParent = this._pane = this._host = this._backdropRef = null;
|
|
if (isAttached) {
|
|
this._detachments.next();
|
|
}
|
|
this._detachments.complete();
|
|
this._completeDetachContent();
|
|
}
|
|
/** Whether the overlay has attached content. */
|
|
hasAttached() {
|
|
return this._portalOutlet.hasAttached();
|
|
}
|
|
/** Gets an observable that emits when the backdrop has been clicked. */
|
|
backdropClick() {
|
|
return this._backdropClick;
|
|
}
|
|
/** Gets an observable that emits when the overlay has been attached. */
|
|
attachments() {
|
|
return this._attachments;
|
|
}
|
|
/** Gets an observable that emits when the overlay has been detached. */
|
|
detachments() {
|
|
return this._detachments;
|
|
}
|
|
/** Gets an observable of keydown events targeted to this overlay. */
|
|
keydownEvents() {
|
|
return this._keydownEvents;
|
|
}
|
|
/** Gets an observable of pointer events targeted outside this overlay. */
|
|
outsidePointerEvents() {
|
|
return this._outsidePointerEvents;
|
|
}
|
|
/** Gets the current overlay configuration, which is immutable. */
|
|
getConfig() {
|
|
return this._config;
|
|
}
|
|
/** Updates the position of the overlay based on the position strategy. */
|
|
updatePosition() {
|
|
if (this._positionStrategy) {
|
|
this._positionStrategy.apply();
|
|
}
|
|
}
|
|
/** Switches to a new position strategy and updates the overlay position. */
|
|
updatePositionStrategy(strategy) {
|
|
if (strategy === this._positionStrategy) {
|
|
return;
|
|
}
|
|
if (this._positionStrategy) {
|
|
this._positionStrategy.dispose();
|
|
}
|
|
this._positionStrategy = strategy;
|
|
if (this.hasAttached()) {
|
|
strategy.attach(this);
|
|
this.updatePosition();
|
|
}
|
|
}
|
|
/** Update the size properties of the overlay. */
|
|
updateSize(sizeConfig) {
|
|
this._config = __spreadValues(__spreadValues({}, this._config), sizeConfig);
|
|
this._updateElementSize();
|
|
}
|
|
/** Sets the LTR/RTL direction for the overlay. */
|
|
setDirection(dir) {
|
|
this._config = __spreadProps(__spreadValues({}, this._config), {
|
|
direction: dir
|
|
});
|
|
this._updateElementDirection();
|
|
}
|
|
/** Add a CSS class or an array of classes to the overlay pane. */
|
|
addPanelClass(classes) {
|
|
if (this._pane) {
|
|
this._toggleClasses(this._pane, classes, true);
|
|
}
|
|
}
|
|
/** Remove a CSS class or an array of classes from the overlay pane. */
|
|
removePanelClass(classes) {
|
|
if (this._pane) {
|
|
this._toggleClasses(this._pane, classes, false);
|
|
}
|
|
}
|
|
/**
|
|
* Returns the layout direction of the overlay panel.
|
|
*/
|
|
getDirection() {
|
|
const direction = this._config.direction;
|
|
if (!direction) {
|
|
return "ltr";
|
|
}
|
|
return typeof direction === "string" ? direction : direction.value;
|
|
}
|
|
/** Switches to a new scroll strategy. */
|
|
updateScrollStrategy(strategy) {
|
|
if (strategy === this._scrollStrategy) {
|
|
return;
|
|
}
|
|
this._disposeScrollStrategy();
|
|
this._scrollStrategy = strategy;
|
|
if (this.hasAttached()) {
|
|
strategy.attach(this);
|
|
strategy.enable();
|
|
}
|
|
}
|
|
/** Updates the text direction of the overlay panel. */
|
|
_updateElementDirection() {
|
|
this._host.setAttribute("dir", this.getDirection());
|
|
}
|
|
/** Updates the size of the overlay element based on the overlay config. */
|
|
_updateElementSize() {
|
|
if (!this._pane) {
|
|
return;
|
|
}
|
|
const style2 = this._pane.style;
|
|
style2.width = coerceCssPixelValue(this._config.width);
|
|
style2.height = coerceCssPixelValue(this._config.height);
|
|
style2.minWidth = coerceCssPixelValue(this._config.minWidth);
|
|
style2.minHeight = coerceCssPixelValue(this._config.minHeight);
|
|
style2.maxWidth = coerceCssPixelValue(this._config.maxWidth);
|
|
style2.maxHeight = coerceCssPixelValue(this._config.maxHeight);
|
|
}
|
|
/** Toggles the pointer events for the overlay pane element. */
|
|
_togglePointerEvents(enablePointer) {
|
|
this._pane.style.pointerEvents = enablePointer ? "" : "none";
|
|
}
|
|
/** Attaches a backdrop for this overlay. */
|
|
_attachBackdrop() {
|
|
const showingClass = "cdk-overlay-backdrop-showing";
|
|
this._backdropRef?.dispose();
|
|
this._backdropRef = new BackdropRef(this._document, this._renderer, this._ngZone, (event) => {
|
|
this._backdropClick.next(event);
|
|
});
|
|
if (this._animationsDisabled) {
|
|
this._backdropRef.element.classList.add("cdk-overlay-backdrop-noop-animation");
|
|
}
|
|
if (this._config.backdropClass) {
|
|
this._toggleClasses(this._backdropRef.element, this._config.backdropClass, true);
|
|
}
|
|
this._host.parentElement.insertBefore(this._backdropRef.element, this._host);
|
|
if (!this._animationsDisabled && typeof requestAnimationFrame !== "undefined") {
|
|
this._ngZone.runOutsideAngular(() => {
|
|
requestAnimationFrame(() => this._backdropRef?.element.classList.add(showingClass));
|
|
});
|
|
} else {
|
|
this._backdropRef.element.classList.add(showingClass);
|
|
}
|
|
}
|
|
/**
|
|
* Updates the stacking order of the element, moving it to the top if necessary.
|
|
* This is required in cases where one overlay was detached, while another one,
|
|
* that should be behind it, was destroyed. The next time both of them are opened,
|
|
* the stacking will be wrong, because the detached element's pane will still be
|
|
* in its original DOM position.
|
|
*/
|
|
_updateStackingOrder() {
|
|
if (this._host.nextSibling) {
|
|
this._host.parentNode.appendChild(this._host);
|
|
}
|
|
}
|
|
/** Detaches the backdrop (if any) associated with the overlay. */
|
|
detachBackdrop() {
|
|
if (this._animationsDisabled) {
|
|
this._backdropRef?.dispose();
|
|
this._backdropRef = null;
|
|
} else {
|
|
this._backdropRef?.detach();
|
|
}
|
|
}
|
|
/** Toggles a single CSS class or an array of classes on an element. */
|
|
_toggleClasses(element, cssClasses, isAdd) {
|
|
const classes = coerceArray(cssClasses || []).filter((c) => !!c);
|
|
if (classes.length) {
|
|
isAdd ? element.classList.add(...classes) : element.classList.remove(...classes);
|
|
}
|
|
}
|
|
/** Detaches the overlay once the content finishes animating and is removed from the DOM. */
|
|
_detachContentWhenEmpty() {
|
|
let rethrow = false;
|
|
try {
|
|
this._detachContentAfterRenderRef = afterNextRender(() => {
|
|
rethrow = true;
|
|
this._detachContent();
|
|
}, {
|
|
injector: this._injector
|
|
});
|
|
} catch (e) {
|
|
if (rethrow) {
|
|
throw e;
|
|
}
|
|
this._detachContent();
|
|
}
|
|
if (globalThis.MutationObserver && this._pane) {
|
|
this._detachContentMutationObserver ||= new globalThis.MutationObserver(() => {
|
|
this._detachContent();
|
|
});
|
|
this._detachContentMutationObserver.observe(this._pane, {
|
|
childList: true
|
|
});
|
|
}
|
|
}
|
|
_detachContent() {
|
|
if (!this._pane || !this._host || this._pane.children.length === 0) {
|
|
if (this._pane && this._config.panelClass) {
|
|
this._toggleClasses(this._pane, this._config.panelClass, false);
|
|
}
|
|
if (this._host && this._host.parentElement) {
|
|
this._previousHostParent = this._host.parentElement;
|
|
this._host.remove();
|
|
}
|
|
this._completeDetachContent();
|
|
}
|
|
}
|
|
_completeDetachContent() {
|
|
this._detachContentAfterRenderRef?.destroy();
|
|
this._detachContentAfterRenderRef = void 0;
|
|
this._detachContentMutationObserver?.disconnect();
|
|
}
|
|
/** Disposes of a scroll strategy. */
|
|
_disposeScrollStrategy() {
|
|
const scrollStrategy = this._scrollStrategy;
|
|
scrollStrategy?.disable();
|
|
scrollStrategy?.detach?.();
|
|
}
|
|
};
|
|
var boundingBoxClass = "cdk-overlay-connected-position-bounding-box";
|
|
var cssUnitPattern = /([A-Za-z%]+)$/;
|
|
function createFlexibleConnectedPositionStrategy(injector, origin) {
|
|
return new FlexibleConnectedPositionStrategy(origin, injector.get(ViewportRuler), injector.get(DOCUMENT), injector.get(Platform), injector.get(OverlayContainer));
|
|
}
|
|
var FlexibleConnectedPositionStrategy = class {
|
|
_viewportRuler;
|
|
_document;
|
|
_platform;
|
|
_overlayContainer;
|
|
/** The overlay to which this strategy is attached. */
|
|
_overlayRef;
|
|
/** Whether we're performing the very first positioning of the overlay. */
|
|
_isInitialRender;
|
|
/** Last size used for the bounding box. Used to avoid resizing the overlay after open. */
|
|
_lastBoundingBoxSize = {
|
|
width: 0,
|
|
height: 0
|
|
};
|
|
/** Whether the overlay was pushed in a previous positioning. */
|
|
_isPushed = false;
|
|
/** Whether the overlay can be pushed on-screen on the initial open. */
|
|
_canPush = true;
|
|
/** Whether the overlay can grow via flexible width/height after the initial open. */
|
|
_growAfterOpen = false;
|
|
/** Whether the overlay's width and height can be constrained to fit within the viewport. */
|
|
_hasFlexibleDimensions = true;
|
|
/** Whether the overlay position is locked. */
|
|
_positionLocked = false;
|
|
/** Cached origin dimensions */
|
|
_originRect;
|
|
/** Cached overlay dimensions */
|
|
_overlayRect;
|
|
/** Cached viewport dimensions */
|
|
_viewportRect;
|
|
/** Cached container dimensions */
|
|
_containerRect;
|
|
/** Amount of space that must be maintained between the overlay and the edge of the viewport. */
|
|
_viewportMargin = 0;
|
|
/** The Scrollable containers used to check scrollable view properties on position change. */
|
|
_scrollables = [];
|
|
/** Ordered list of preferred positions, from most to least desirable. */
|
|
_preferredPositions = [];
|
|
/** The origin element against which the overlay will be positioned. */
|
|
_origin;
|
|
/** The overlay pane element. */
|
|
_pane;
|
|
/** Whether the strategy has been disposed of already. */
|
|
_isDisposed;
|
|
/**
|
|
* Parent element for the overlay panel used to constrain the overlay panel's size to fit
|
|
* within the viewport.
|
|
*/
|
|
_boundingBox;
|
|
/** The last position to have been calculated as the best fit position. */
|
|
_lastPosition;
|
|
/** The last calculated scroll visibility. Only tracked */
|
|
_lastScrollVisibility;
|
|
/** Subject that emits whenever the position changes. */
|
|
_positionChanges = new Subject();
|
|
/** Subscription to viewport size changes. */
|
|
_resizeSubscription = Subscription.EMPTY;
|
|
/** Default offset for the overlay along the x axis. */
|
|
_offsetX = 0;
|
|
/** Default offset for the overlay along the y axis. */
|
|
_offsetY = 0;
|
|
/** Selector to be used when finding the elements on which to set the transform origin. */
|
|
_transformOriginSelector;
|
|
/** Keeps track of the CSS classes that the position strategy has applied on the overlay panel. */
|
|
_appliedPanelClasses = [];
|
|
/** Amount by which the overlay was pushed in each axis during the last time it was positioned. */
|
|
_previousPushAmount;
|
|
/** Observable sequence of position changes. */
|
|
positionChanges = this._positionChanges;
|
|
/** Ordered list of preferred positions, from most to least desirable. */
|
|
get positions() {
|
|
return this._preferredPositions;
|
|
}
|
|
constructor(connectedTo, _viewportRuler, _document, _platform, _overlayContainer) {
|
|
this._viewportRuler = _viewportRuler;
|
|
this._document = _document;
|
|
this._platform = _platform;
|
|
this._overlayContainer = _overlayContainer;
|
|
this.setOrigin(connectedTo);
|
|
}
|
|
/** Attaches this position strategy to an overlay. */
|
|
attach(overlayRef) {
|
|
if (this._overlayRef && overlayRef !== this._overlayRef && (typeof ngDevMode === "undefined" || ngDevMode)) {
|
|
throw Error("This position strategy is already attached to an overlay");
|
|
}
|
|
this._validatePositions();
|
|
overlayRef.hostElement.classList.add(boundingBoxClass);
|
|
this._overlayRef = overlayRef;
|
|
this._boundingBox = overlayRef.hostElement;
|
|
this._pane = overlayRef.overlayElement;
|
|
this._isDisposed = false;
|
|
this._isInitialRender = true;
|
|
this._lastPosition = null;
|
|
this._resizeSubscription.unsubscribe();
|
|
this._resizeSubscription = this._viewportRuler.change().subscribe(() => {
|
|
this._isInitialRender = true;
|
|
this.apply();
|
|
});
|
|
}
|
|
/**
|
|
* Updates the position of the overlay element, using whichever preferred position relative
|
|
* to the origin best fits on-screen.
|
|
*
|
|
* The selection of a position goes as follows:
|
|
* - If any positions fit completely within the viewport as-is,
|
|
* choose the first position that does so.
|
|
* - If flexible dimensions are enabled and at least one satisfies the given minimum width/height,
|
|
* choose the position with the greatest available size modified by the positions' weight.
|
|
* - If pushing is enabled, take the position that went off-screen the least and push it
|
|
* on-screen.
|
|
* - If none of the previous criteria were met, use the position that goes off-screen the least.
|
|
* @docs-private
|
|
*/
|
|
apply() {
|
|
if (this._isDisposed || !this._platform.isBrowser) {
|
|
return;
|
|
}
|
|
if (!this._isInitialRender && this._positionLocked && this._lastPosition) {
|
|
this.reapplyLastPosition();
|
|
return;
|
|
}
|
|
this._clearPanelClasses();
|
|
this._resetOverlayElementStyles();
|
|
this._resetBoundingBoxStyles();
|
|
this._viewportRect = this._getNarrowedViewportRect();
|
|
this._originRect = this._getOriginRect();
|
|
this._overlayRect = this._pane.getBoundingClientRect();
|
|
this._containerRect = this._overlayContainer.getContainerElement().getBoundingClientRect();
|
|
const originRect = this._originRect;
|
|
const overlayRect = this._overlayRect;
|
|
const viewportRect = this._viewportRect;
|
|
const containerRect = this._containerRect;
|
|
const flexibleFits = [];
|
|
let fallback;
|
|
for (let pos of this._preferredPositions) {
|
|
let originPoint = this._getOriginPoint(originRect, containerRect, pos);
|
|
let overlayPoint = this._getOverlayPoint(originPoint, overlayRect, pos);
|
|
let overlayFit = this._getOverlayFit(overlayPoint, overlayRect, viewportRect, pos);
|
|
if (overlayFit.isCompletelyWithinViewport) {
|
|
this._isPushed = false;
|
|
this._applyPosition(pos, originPoint);
|
|
return;
|
|
}
|
|
if (this._canFitWithFlexibleDimensions(overlayFit, overlayPoint, viewportRect)) {
|
|
flexibleFits.push({
|
|
position: pos,
|
|
origin: originPoint,
|
|
overlayRect,
|
|
boundingBoxRect: this._calculateBoundingBoxRect(originPoint, pos)
|
|
});
|
|
continue;
|
|
}
|
|
if (!fallback || fallback.overlayFit.visibleArea < overlayFit.visibleArea) {
|
|
fallback = {
|
|
overlayFit,
|
|
overlayPoint,
|
|
originPoint,
|
|
position: pos,
|
|
overlayRect
|
|
};
|
|
}
|
|
}
|
|
if (flexibleFits.length) {
|
|
let bestFit = null;
|
|
let bestScore = -1;
|
|
for (const fit of flexibleFits) {
|
|
const score = fit.boundingBoxRect.width * fit.boundingBoxRect.height * (fit.position.weight || 1);
|
|
if (score > bestScore) {
|
|
bestScore = score;
|
|
bestFit = fit;
|
|
}
|
|
}
|
|
this._isPushed = false;
|
|
this._applyPosition(bestFit.position, bestFit.origin);
|
|
return;
|
|
}
|
|
if (this._canPush) {
|
|
this._isPushed = true;
|
|
this._applyPosition(fallback.position, fallback.originPoint);
|
|
return;
|
|
}
|
|
this._applyPosition(fallback.position, fallback.originPoint);
|
|
}
|
|
detach() {
|
|
this._clearPanelClasses();
|
|
this._lastPosition = null;
|
|
this._previousPushAmount = null;
|
|
this._resizeSubscription.unsubscribe();
|
|
}
|
|
/** Cleanup after the element gets destroyed. */
|
|
dispose() {
|
|
if (this._isDisposed) {
|
|
return;
|
|
}
|
|
if (this._boundingBox) {
|
|
extendStyles(this._boundingBox.style, {
|
|
top: "",
|
|
left: "",
|
|
right: "",
|
|
bottom: "",
|
|
height: "",
|
|
width: "",
|
|
alignItems: "",
|
|
justifyContent: ""
|
|
});
|
|
}
|
|
if (this._pane) {
|
|
this._resetOverlayElementStyles();
|
|
}
|
|
if (this._overlayRef) {
|
|
this._overlayRef.hostElement.classList.remove(boundingBoxClass);
|
|
}
|
|
this.detach();
|
|
this._positionChanges.complete();
|
|
this._overlayRef = this._boundingBox = null;
|
|
this._isDisposed = true;
|
|
}
|
|
/**
|
|
* This re-aligns the overlay element with the trigger in its last calculated position,
|
|
* even if a position higher in the "preferred positions" list would now fit. This
|
|
* allows one to re-align the panel without changing the orientation of the panel.
|
|
*/
|
|
reapplyLastPosition() {
|
|
if (this._isDisposed || !this._platform.isBrowser) {
|
|
return;
|
|
}
|
|
const lastPosition = this._lastPosition;
|
|
if (lastPosition) {
|
|
this._originRect = this._getOriginRect();
|
|
this._overlayRect = this._pane.getBoundingClientRect();
|
|
this._viewportRect = this._getNarrowedViewportRect();
|
|
this._containerRect = this._overlayContainer.getContainerElement().getBoundingClientRect();
|
|
const originPoint = this._getOriginPoint(this._originRect, this._containerRect, lastPosition);
|
|
this._applyPosition(lastPosition, originPoint);
|
|
} else {
|
|
this.apply();
|
|
}
|
|
}
|
|
/**
|
|
* Sets the list of Scrollable containers that host the origin element so that
|
|
* on reposition we can evaluate if it or the overlay has been clipped or outside view. Every
|
|
* Scrollable must be an ancestor element of the strategy's origin element.
|
|
*/
|
|
withScrollableContainers(scrollables) {
|
|
this._scrollables = scrollables;
|
|
return this;
|
|
}
|
|
/**
|
|
* Adds new preferred positions.
|
|
* @param positions List of positions options for this overlay.
|
|
*/
|
|
withPositions(positions) {
|
|
this._preferredPositions = positions;
|
|
if (positions.indexOf(this._lastPosition) === -1) {
|
|
this._lastPosition = null;
|
|
}
|
|
this._validatePositions();
|
|
return this;
|
|
}
|
|
/**
|
|
* Sets a minimum distance the overlay may be positioned to the edge of the viewport.
|
|
* @param margin Required margin between the overlay and the viewport edge in pixels.
|
|
*/
|
|
withViewportMargin(margin) {
|
|
this._viewportMargin = margin;
|
|
return this;
|
|
}
|
|
/** Sets whether the overlay's width and height can be constrained to fit within the viewport. */
|
|
withFlexibleDimensions(flexibleDimensions = true) {
|
|
this._hasFlexibleDimensions = flexibleDimensions;
|
|
return this;
|
|
}
|
|
/** Sets whether the overlay can grow after the initial open via flexible width/height. */
|
|
withGrowAfterOpen(growAfterOpen = true) {
|
|
this._growAfterOpen = growAfterOpen;
|
|
return this;
|
|
}
|
|
/** Sets whether the overlay can be pushed on-screen if none of the provided positions fit. */
|
|
withPush(canPush = true) {
|
|
this._canPush = canPush;
|
|
return this;
|
|
}
|
|
/**
|
|
* Sets whether the overlay's position should be locked in after it is positioned
|
|
* initially. When an overlay is locked in, it won't attempt to reposition itself
|
|
* when the position is re-applied (e.g. when the user scrolls away).
|
|
* @param isLocked Whether the overlay should locked in.
|
|
*/
|
|
withLockedPosition(isLocked = true) {
|
|
this._positionLocked = isLocked;
|
|
return this;
|
|
}
|
|
/**
|
|
* Sets the origin, relative to which to position the overlay.
|
|
* Using an element origin is useful for building components that need to be positioned
|
|
* relatively to a trigger (e.g. dropdown menus or tooltips), whereas using a point can be
|
|
* used for cases like contextual menus which open relative to the user's pointer.
|
|
* @param origin Reference to the new origin.
|
|
*/
|
|
setOrigin(origin) {
|
|
this._origin = origin;
|
|
return this;
|
|
}
|
|
/**
|
|
* Sets the default offset for the overlay's connection point on the x-axis.
|
|
* @param offset New offset in the X axis.
|
|
*/
|
|
withDefaultOffsetX(offset) {
|
|
this._offsetX = offset;
|
|
return this;
|
|
}
|
|
/**
|
|
* Sets the default offset for the overlay's connection point on the y-axis.
|
|
* @param offset New offset in the Y axis.
|
|
*/
|
|
withDefaultOffsetY(offset) {
|
|
this._offsetY = offset;
|
|
return this;
|
|
}
|
|
/**
|
|
* Configures that the position strategy should set a `transform-origin` on some elements
|
|
* inside the overlay, depending on the current position that is being applied. This is
|
|
* useful for the cases where the origin of an animation can change depending on the
|
|
* alignment of the overlay.
|
|
* @param selector CSS selector that will be used to find the target
|
|
* elements onto which to set the transform origin.
|
|
*/
|
|
withTransformOriginOn(selector) {
|
|
this._transformOriginSelector = selector;
|
|
return this;
|
|
}
|
|
/**
|
|
* Gets the (x, y) coordinate of a connection point on the origin based on a relative position.
|
|
*/
|
|
_getOriginPoint(originRect, containerRect, pos) {
|
|
let x;
|
|
if (pos.originX == "center") {
|
|
x = originRect.left + originRect.width / 2;
|
|
} else {
|
|
const startX = this._isRtl() ? originRect.right : originRect.left;
|
|
const endX = this._isRtl() ? originRect.left : originRect.right;
|
|
x = pos.originX == "start" ? startX : endX;
|
|
}
|
|
if (containerRect.left < 0) {
|
|
x -= containerRect.left;
|
|
}
|
|
let y;
|
|
if (pos.originY == "center") {
|
|
y = originRect.top + originRect.height / 2;
|
|
} else {
|
|
y = pos.originY == "top" ? originRect.top : originRect.bottom;
|
|
}
|
|
if (containerRect.top < 0) {
|
|
y -= containerRect.top;
|
|
}
|
|
return {
|
|
x,
|
|
y
|
|
};
|
|
}
|
|
/**
|
|
* Gets the (x, y) coordinate of the top-left corner of the overlay given a given position and
|
|
* origin point to which the overlay should be connected.
|
|
*/
|
|
_getOverlayPoint(originPoint, overlayRect, pos) {
|
|
let overlayStartX;
|
|
if (pos.overlayX == "center") {
|
|
overlayStartX = -overlayRect.width / 2;
|
|
} else if (pos.overlayX === "start") {
|
|
overlayStartX = this._isRtl() ? -overlayRect.width : 0;
|
|
} else {
|
|
overlayStartX = this._isRtl() ? 0 : -overlayRect.width;
|
|
}
|
|
let overlayStartY;
|
|
if (pos.overlayY == "center") {
|
|
overlayStartY = -overlayRect.height / 2;
|
|
} else {
|
|
overlayStartY = pos.overlayY == "top" ? 0 : -overlayRect.height;
|
|
}
|
|
return {
|
|
x: originPoint.x + overlayStartX,
|
|
y: originPoint.y + overlayStartY
|
|
};
|
|
}
|
|
/** Gets how well an overlay at the given point will fit within the viewport. */
|
|
_getOverlayFit(point, rawOverlayRect, viewport, position) {
|
|
const overlay = getRoundedBoundingClientRect(rawOverlayRect);
|
|
let {
|
|
x,
|
|
y
|
|
} = point;
|
|
let offsetX = this._getOffset(position, "x");
|
|
let offsetY = this._getOffset(position, "y");
|
|
if (offsetX) {
|
|
x += offsetX;
|
|
}
|
|
if (offsetY) {
|
|
y += offsetY;
|
|
}
|
|
let leftOverflow = 0 - x;
|
|
let rightOverflow = x + overlay.width - viewport.width;
|
|
let topOverflow = 0 - y;
|
|
let bottomOverflow = y + overlay.height - viewport.height;
|
|
let visibleWidth = this._subtractOverflows(overlay.width, leftOverflow, rightOverflow);
|
|
let visibleHeight = this._subtractOverflows(overlay.height, topOverflow, bottomOverflow);
|
|
let visibleArea = visibleWidth * visibleHeight;
|
|
return {
|
|
visibleArea,
|
|
isCompletelyWithinViewport: overlay.width * overlay.height === visibleArea,
|
|
fitsInViewportVertically: visibleHeight === overlay.height,
|
|
fitsInViewportHorizontally: visibleWidth == overlay.width
|
|
};
|
|
}
|
|
/**
|
|
* Whether the overlay can fit within the viewport when it may resize either its width or height.
|
|
* @param fit How well the overlay fits in the viewport at some position.
|
|
* @param point The (x, y) coordinates of the overlay at some position.
|
|
* @param viewport The geometry of the viewport.
|
|
*/
|
|
_canFitWithFlexibleDimensions(fit, point, viewport) {
|
|
if (this._hasFlexibleDimensions) {
|
|
const availableHeight = viewport.bottom - point.y;
|
|
const availableWidth = viewport.right - point.x;
|
|
const minHeight = getPixelValue(this._overlayRef.getConfig().minHeight);
|
|
const minWidth = getPixelValue(this._overlayRef.getConfig().minWidth);
|
|
const verticalFit = fit.fitsInViewportVertically || minHeight != null && minHeight <= availableHeight;
|
|
const horizontalFit = fit.fitsInViewportHorizontally || minWidth != null && minWidth <= availableWidth;
|
|
return verticalFit && horizontalFit;
|
|
}
|
|
return false;
|
|
}
|
|
/**
|
|
* Gets the point at which the overlay can be "pushed" on-screen. If the overlay is larger than
|
|
* the viewport, the top-left corner will be pushed on-screen (with overflow occurring on the
|
|
* right and bottom).
|
|
*
|
|
* @param start Starting point from which the overlay is pushed.
|
|
* @param rawOverlayRect Dimensions of the overlay.
|
|
* @param scrollPosition Current viewport scroll position.
|
|
* @returns The point at which to position the overlay after pushing. This is effectively a new
|
|
* originPoint.
|
|
*/
|
|
_pushOverlayOnScreen(start, rawOverlayRect, scrollPosition) {
|
|
if (this._previousPushAmount && this._positionLocked) {
|
|
return {
|
|
x: start.x + this._previousPushAmount.x,
|
|
y: start.y + this._previousPushAmount.y
|
|
};
|
|
}
|
|
const overlay = getRoundedBoundingClientRect(rawOverlayRect);
|
|
const viewport = this._viewportRect;
|
|
const overflowRight = Math.max(start.x + overlay.width - viewport.width, 0);
|
|
const overflowBottom = Math.max(start.y + overlay.height - viewport.height, 0);
|
|
const overflowTop = Math.max(viewport.top - scrollPosition.top - start.y, 0);
|
|
const overflowLeft = Math.max(viewport.left - scrollPosition.left - start.x, 0);
|
|
let pushX = 0;
|
|
let pushY = 0;
|
|
if (overlay.width <= viewport.width) {
|
|
pushX = overflowLeft || -overflowRight;
|
|
} else {
|
|
pushX = start.x < this._viewportMargin ? viewport.left - scrollPosition.left - start.x : 0;
|
|
}
|
|
if (overlay.height <= viewport.height) {
|
|
pushY = overflowTop || -overflowBottom;
|
|
} else {
|
|
pushY = start.y < this._viewportMargin ? viewport.top - scrollPosition.top - start.y : 0;
|
|
}
|
|
this._previousPushAmount = {
|
|
x: pushX,
|
|
y: pushY
|
|
};
|
|
return {
|
|
x: start.x + pushX,
|
|
y: start.y + pushY
|
|
};
|
|
}
|
|
/**
|
|
* Applies a computed position to the overlay and emits a position change.
|
|
* @param position The position preference
|
|
* @param originPoint The point on the origin element where the overlay is connected.
|
|
*/
|
|
_applyPosition(position, originPoint) {
|
|
this._setTransformOrigin(position);
|
|
this._setOverlayElementStyles(originPoint, position);
|
|
this._setBoundingBoxStyles(originPoint, position);
|
|
if (position.panelClass) {
|
|
this._addPanelClasses(position.panelClass);
|
|
}
|
|
if (this._positionChanges.observers.length) {
|
|
const scrollVisibility = this._getScrollVisibility();
|
|
if (position !== this._lastPosition || !this._lastScrollVisibility || !compareScrollVisibility(this._lastScrollVisibility, scrollVisibility)) {
|
|
const changeEvent = new ConnectedOverlayPositionChange(position, scrollVisibility);
|
|
this._positionChanges.next(changeEvent);
|
|
}
|
|
this._lastScrollVisibility = scrollVisibility;
|
|
}
|
|
this._lastPosition = position;
|
|
this._isInitialRender = false;
|
|
}
|
|
/** Sets the transform origin based on the configured selector and the passed-in position. */
|
|
_setTransformOrigin(position) {
|
|
if (!this._transformOriginSelector) {
|
|
return;
|
|
}
|
|
const elements = this._boundingBox.querySelectorAll(this._transformOriginSelector);
|
|
let xOrigin;
|
|
let yOrigin = position.overlayY;
|
|
if (position.overlayX === "center") {
|
|
xOrigin = "center";
|
|
} else if (this._isRtl()) {
|
|
xOrigin = position.overlayX === "start" ? "right" : "left";
|
|
} else {
|
|
xOrigin = position.overlayX === "start" ? "left" : "right";
|
|
}
|
|
for (let i = 0; i < elements.length; i++) {
|
|
elements[i].style.transformOrigin = `${xOrigin} ${yOrigin}`;
|
|
}
|
|
}
|
|
/**
|
|
* Gets the position and size of the overlay's sizing container.
|
|
*
|
|
* This method does no measuring and applies no styles so that we can cheaply compute the
|
|
* bounds for all positions and choose the best fit based on these results.
|
|
*/
|
|
_calculateBoundingBoxRect(origin, position) {
|
|
const viewport = this._viewportRect;
|
|
const isRtl = this._isRtl();
|
|
let height, top, bottom;
|
|
if (position.overlayY === "top") {
|
|
top = origin.y;
|
|
height = viewport.height - top + this._viewportMargin;
|
|
} else if (position.overlayY === "bottom") {
|
|
bottom = viewport.height - origin.y + this._viewportMargin * 2;
|
|
height = viewport.height - bottom + this._viewportMargin;
|
|
} else {
|
|
const smallestDistanceToViewportEdge = Math.min(viewport.bottom - origin.y + viewport.top, origin.y);
|
|
const previousHeight = this._lastBoundingBoxSize.height;
|
|
height = smallestDistanceToViewportEdge * 2;
|
|
top = origin.y - smallestDistanceToViewportEdge;
|
|
if (height > previousHeight && !this._isInitialRender && !this._growAfterOpen) {
|
|
top = origin.y - previousHeight / 2;
|
|
}
|
|
}
|
|
const isBoundedByRightViewportEdge = position.overlayX === "start" && !isRtl || position.overlayX === "end" && isRtl;
|
|
const isBoundedByLeftViewportEdge = position.overlayX === "end" && !isRtl || position.overlayX === "start" && isRtl;
|
|
let width, left, right;
|
|
if (isBoundedByLeftViewportEdge) {
|
|
right = viewport.width - origin.x + this._viewportMargin * 2;
|
|
width = origin.x - this._viewportMargin;
|
|
} else if (isBoundedByRightViewportEdge) {
|
|
left = origin.x;
|
|
width = viewport.right - origin.x;
|
|
} else {
|
|
const smallestDistanceToViewportEdge = Math.min(viewport.right - origin.x + viewport.left, origin.x);
|
|
const previousWidth = this._lastBoundingBoxSize.width;
|
|
width = smallestDistanceToViewportEdge * 2;
|
|
left = origin.x - smallestDistanceToViewportEdge;
|
|
if (width > previousWidth && !this._isInitialRender && !this._growAfterOpen) {
|
|
left = origin.x - previousWidth / 2;
|
|
}
|
|
}
|
|
return {
|
|
top,
|
|
left,
|
|
bottom,
|
|
right,
|
|
width,
|
|
height
|
|
};
|
|
}
|
|
/**
|
|
* Sets the position and size of the overlay's sizing wrapper. The wrapper is positioned on the
|
|
* origin's connection point and stretches to the bounds of the viewport.
|
|
*
|
|
* @param origin The point on the origin element where the overlay is connected.
|
|
* @param position The position preference
|
|
*/
|
|
_setBoundingBoxStyles(origin, position) {
|
|
const boundingBoxRect = this._calculateBoundingBoxRect(origin, position);
|
|
if (!this._isInitialRender && !this._growAfterOpen) {
|
|
boundingBoxRect.height = Math.min(boundingBoxRect.height, this._lastBoundingBoxSize.height);
|
|
boundingBoxRect.width = Math.min(boundingBoxRect.width, this._lastBoundingBoxSize.width);
|
|
}
|
|
const styles = {};
|
|
if (this._hasExactPosition()) {
|
|
styles.top = styles.left = "0";
|
|
styles.bottom = styles.right = styles.maxHeight = styles.maxWidth = "";
|
|
styles.width = styles.height = "100%";
|
|
} else {
|
|
const maxHeight = this._overlayRef.getConfig().maxHeight;
|
|
const maxWidth = this._overlayRef.getConfig().maxWidth;
|
|
styles.height = coerceCssPixelValue(boundingBoxRect.height);
|
|
styles.top = coerceCssPixelValue(boundingBoxRect.top);
|
|
styles.bottom = coerceCssPixelValue(boundingBoxRect.bottom);
|
|
styles.width = coerceCssPixelValue(boundingBoxRect.width);
|
|
styles.left = coerceCssPixelValue(boundingBoxRect.left);
|
|
styles.right = coerceCssPixelValue(boundingBoxRect.right);
|
|
if (position.overlayX === "center") {
|
|
styles.alignItems = "center";
|
|
} else {
|
|
styles.alignItems = position.overlayX === "end" ? "flex-end" : "flex-start";
|
|
}
|
|
if (position.overlayY === "center") {
|
|
styles.justifyContent = "center";
|
|
} else {
|
|
styles.justifyContent = position.overlayY === "bottom" ? "flex-end" : "flex-start";
|
|
}
|
|
if (maxHeight) {
|
|
styles.maxHeight = coerceCssPixelValue(maxHeight);
|
|
}
|
|
if (maxWidth) {
|
|
styles.maxWidth = coerceCssPixelValue(maxWidth);
|
|
}
|
|
}
|
|
this._lastBoundingBoxSize = boundingBoxRect;
|
|
extendStyles(this._boundingBox.style, styles);
|
|
}
|
|
/** Resets the styles for the bounding box so that a new positioning can be computed. */
|
|
_resetBoundingBoxStyles() {
|
|
extendStyles(this._boundingBox.style, {
|
|
top: "0",
|
|
left: "0",
|
|
right: "0",
|
|
bottom: "0",
|
|
height: "",
|
|
width: "",
|
|
alignItems: "",
|
|
justifyContent: ""
|
|
});
|
|
}
|
|
/** Resets the styles for the overlay pane so that a new positioning can be computed. */
|
|
_resetOverlayElementStyles() {
|
|
extendStyles(this._pane.style, {
|
|
top: "",
|
|
left: "",
|
|
bottom: "",
|
|
right: "",
|
|
position: "",
|
|
transform: ""
|
|
});
|
|
}
|
|
/** Sets positioning styles to the overlay element. */
|
|
_setOverlayElementStyles(originPoint, position) {
|
|
const styles = {};
|
|
const hasExactPosition = this._hasExactPosition();
|
|
const hasFlexibleDimensions = this._hasFlexibleDimensions;
|
|
const config = this._overlayRef.getConfig();
|
|
if (hasExactPosition) {
|
|
const scrollPosition = this._viewportRuler.getViewportScrollPosition();
|
|
extendStyles(styles, this._getExactOverlayY(position, originPoint, scrollPosition));
|
|
extendStyles(styles, this._getExactOverlayX(position, originPoint, scrollPosition));
|
|
} else {
|
|
styles.position = "static";
|
|
}
|
|
let transformString = "";
|
|
let offsetX = this._getOffset(position, "x");
|
|
let offsetY = this._getOffset(position, "y");
|
|
if (offsetX) {
|
|
transformString += `translateX(${offsetX}px) `;
|
|
}
|
|
if (offsetY) {
|
|
transformString += `translateY(${offsetY}px)`;
|
|
}
|
|
styles.transform = transformString.trim();
|
|
if (config.maxHeight) {
|
|
if (hasExactPosition) {
|
|
styles.maxHeight = coerceCssPixelValue(config.maxHeight);
|
|
} else if (hasFlexibleDimensions) {
|
|
styles.maxHeight = "";
|
|
}
|
|
}
|
|
if (config.maxWidth) {
|
|
if (hasExactPosition) {
|
|
styles.maxWidth = coerceCssPixelValue(config.maxWidth);
|
|
} else if (hasFlexibleDimensions) {
|
|
styles.maxWidth = "";
|
|
}
|
|
}
|
|
extendStyles(this._pane.style, styles);
|
|
}
|
|
/** Gets the exact top/bottom for the overlay when not using flexible sizing or when pushing. */
|
|
_getExactOverlayY(position, originPoint, scrollPosition) {
|
|
let styles = {
|
|
top: "",
|
|
bottom: ""
|
|
};
|
|
let overlayPoint = this._getOverlayPoint(originPoint, this._overlayRect, position);
|
|
if (this._isPushed) {
|
|
overlayPoint = this._pushOverlayOnScreen(overlayPoint, this._overlayRect, scrollPosition);
|
|
}
|
|
if (position.overlayY === "bottom") {
|
|
const documentHeight = this._document.documentElement.clientHeight;
|
|
styles.bottom = `${documentHeight - (overlayPoint.y + this._overlayRect.height)}px`;
|
|
} else {
|
|
styles.top = coerceCssPixelValue(overlayPoint.y);
|
|
}
|
|
return styles;
|
|
}
|
|
/** Gets the exact left/right for the overlay when not using flexible sizing or when pushing. */
|
|
_getExactOverlayX(position, originPoint, scrollPosition) {
|
|
let styles = {
|
|
left: "",
|
|
right: ""
|
|
};
|
|
let overlayPoint = this._getOverlayPoint(originPoint, this._overlayRect, position);
|
|
if (this._isPushed) {
|
|
overlayPoint = this._pushOverlayOnScreen(overlayPoint, this._overlayRect, scrollPosition);
|
|
}
|
|
let horizontalStyleProperty;
|
|
if (this._isRtl()) {
|
|
horizontalStyleProperty = position.overlayX === "end" ? "left" : "right";
|
|
} else {
|
|
horizontalStyleProperty = position.overlayX === "end" ? "right" : "left";
|
|
}
|
|
if (horizontalStyleProperty === "right") {
|
|
const documentWidth = this._document.documentElement.clientWidth;
|
|
styles.right = `${documentWidth - (overlayPoint.x + this._overlayRect.width)}px`;
|
|
} else {
|
|
styles.left = coerceCssPixelValue(overlayPoint.x);
|
|
}
|
|
return styles;
|
|
}
|
|
/**
|
|
* Gets the view properties of the trigger and overlay, including whether they are clipped
|
|
* or completely outside the view of any of the strategy's scrollables.
|
|
*/
|
|
_getScrollVisibility() {
|
|
const originBounds = this._getOriginRect();
|
|
const overlayBounds = this._pane.getBoundingClientRect();
|
|
const scrollContainerBounds = this._scrollables.map((scrollable) => {
|
|
return scrollable.getElementRef().nativeElement.getBoundingClientRect();
|
|
});
|
|
return {
|
|
isOriginClipped: isElementClippedByScrolling(originBounds, scrollContainerBounds),
|
|
isOriginOutsideView: isElementScrolledOutsideView(originBounds, scrollContainerBounds),
|
|
isOverlayClipped: isElementClippedByScrolling(overlayBounds, scrollContainerBounds),
|
|
isOverlayOutsideView: isElementScrolledOutsideView(overlayBounds, scrollContainerBounds)
|
|
};
|
|
}
|
|
/** Subtracts the amount that an element is overflowing on an axis from its length. */
|
|
_subtractOverflows(length, ...overflows) {
|
|
return overflows.reduce((currentValue, currentOverflow) => {
|
|
return currentValue - Math.max(currentOverflow, 0);
|
|
}, length);
|
|
}
|
|
/** Narrows the given viewport rect by the current _viewportMargin. */
|
|
_getNarrowedViewportRect() {
|
|
const width = this._document.documentElement.clientWidth;
|
|
const height = this._document.documentElement.clientHeight;
|
|
const scrollPosition = this._viewportRuler.getViewportScrollPosition();
|
|
return {
|
|
top: scrollPosition.top + this._viewportMargin,
|
|
left: scrollPosition.left + this._viewportMargin,
|
|
right: scrollPosition.left + width - this._viewportMargin,
|
|
bottom: scrollPosition.top + height - this._viewportMargin,
|
|
width: width - 2 * this._viewportMargin,
|
|
height: height - 2 * this._viewportMargin
|
|
};
|
|
}
|
|
/** Whether the we're dealing with an RTL context */
|
|
_isRtl() {
|
|
return this._overlayRef.getDirection() === "rtl";
|
|
}
|
|
/** Determines whether the overlay uses exact or flexible positioning. */
|
|
_hasExactPosition() {
|
|
return !this._hasFlexibleDimensions || this._isPushed;
|
|
}
|
|
/** Retrieves the offset of a position along the x or y axis. */
|
|
_getOffset(position, axis) {
|
|
if (axis === "x") {
|
|
return position.offsetX == null ? this._offsetX : position.offsetX;
|
|
}
|
|
return position.offsetY == null ? this._offsetY : position.offsetY;
|
|
}
|
|
/** Validates that the current position match the expected values. */
|
|
_validatePositions() {
|
|
if (typeof ngDevMode === "undefined" || ngDevMode) {
|
|
if (!this._preferredPositions.length) {
|
|
throw Error("FlexibleConnectedPositionStrategy: At least one position is required.");
|
|
}
|
|
this._preferredPositions.forEach((pair) => {
|
|
validateHorizontalPosition("originX", pair.originX);
|
|
validateVerticalPosition("originY", pair.originY);
|
|
validateHorizontalPosition("overlayX", pair.overlayX);
|
|
validateVerticalPosition("overlayY", pair.overlayY);
|
|
});
|
|
}
|
|
}
|
|
/** Adds a single CSS class or an array of classes on the overlay panel. */
|
|
_addPanelClasses(cssClasses) {
|
|
if (this._pane) {
|
|
coerceArray(cssClasses).forEach((cssClass) => {
|
|
if (cssClass !== "" && this._appliedPanelClasses.indexOf(cssClass) === -1) {
|
|
this._appliedPanelClasses.push(cssClass);
|
|
this._pane.classList.add(cssClass);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
/** Clears the classes that the position strategy has applied from the overlay panel. */
|
|
_clearPanelClasses() {
|
|
if (this._pane) {
|
|
this._appliedPanelClasses.forEach((cssClass) => {
|
|
this._pane.classList.remove(cssClass);
|
|
});
|
|
this._appliedPanelClasses = [];
|
|
}
|
|
}
|
|
/** Returns the DOMRect of the current origin. */
|
|
_getOriginRect() {
|
|
const origin = this._origin;
|
|
if (origin instanceof ElementRef) {
|
|
return origin.nativeElement.getBoundingClientRect();
|
|
}
|
|
if (origin instanceof Element) {
|
|
return origin.getBoundingClientRect();
|
|
}
|
|
const width = origin.width || 0;
|
|
const height = origin.height || 0;
|
|
return {
|
|
top: origin.y,
|
|
bottom: origin.y + height,
|
|
left: origin.x,
|
|
right: origin.x + width,
|
|
height,
|
|
width
|
|
};
|
|
}
|
|
};
|
|
function extendStyles(destination, source) {
|
|
for (let key in source) {
|
|
if (source.hasOwnProperty(key)) {
|
|
destination[key] = source[key];
|
|
}
|
|
}
|
|
return destination;
|
|
}
|
|
function getPixelValue(input) {
|
|
if (typeof input !== "number" && input != null) {
|
|
const [value, units] = input.split(cssUnitPattern);
|
|
return !units || units === "px" ? parseFloat(value) : null;
|
|
}
|
|
return input || null;
|
|
}
|
|
function getRoundedBoundingClientRect(clientRect) {
|
|
return {
|
|
top: Math.floor(clientRect.top),
|
|
right: Math.floor(clientRect.right),
|
|
bottom: Math.floor(clientRect.bottom),
|
|
left: Math.floor(clientRect.left),
|
|
width: Math.floor(clientRect.width),
|
|
height: Math.floor(clientRect.height)
|
|
};
|
|
}
|
|
function compareScrollVisibility(a, b) {
|
|
if (a === b) {
|
|
return true;
|
|
}
|
|
return a.isOriginClipped === b.isOriginClipped && a.isOriginOutsideView === b.isOriginOutsideView && a.isOverlayClipped === b.isOverlayClipped && a.isOverlayOutsideView === b.isOverlayOutsideView;
|
|
}
|
|
var wrapperClass = "cdk-global-overlay-wrapper";
|
|
function createGlobalPositionStrategy(_injector) {
|
|
return new GlobalPositionStrategy();
|
|
}
|
|
var GlobalPositionStrategy = class {
|
|
/** The overlay to which this strategy is attached. */
|
|
_overlayRef;
|
|
_cssPosition = "static";
|
|
_topOffset = "";
|
|
_bottomOffset = "";
|
|
_alignItems = "";
|
|
_xPosition = "";
|
|
_xOffset = "";
|
|
_width = "";
|
|
_height = "";
|
|
_isDisposed = false;
|
|
attach(overlayRef) {
|
|
const config = overlayRef.getConfig();
|
|
this._overlayRef = overlayRef;
|
|
if (this._width && !config.width) {
|
|
overlayRef.updateSize({
|
|
width: this._width
|
|
});
|
|
}
|
|
if (this._height && !config.height) {
|
|
overlayRef.updateSize({
|
|
height: this._height
|
|
});
|
|
}
|
|
overlayRef.hostElement.classList.add(wrapperClass);
|
|
this._isDisposed = false;
|
|
}
|
|
/**
|
|
* Sets the top position of the overlay. Clears any previously set vertical position.
|
|
* @param value New top offset.
|
|
*/
|
|
top(value = "") {
|
|
this._bottomOffset = "";
|
|
this._topOffset = value;
|
|
this._alignItems = "flex-start";
|
|
return this;
|
|
}
|
|
/**
|
|
* Sets the left position of the overlay. Clears any previously set horizontal position.
|
|
* @param value New left offset.
|
|
*/
|
|
left(value = "") {
|
|
this._xOffset = value;
|
|
this._xPosition = "left";
|
|
return this;
|
|
}
|
|
/**
|
|
* Sets the bottom position of the overlay. Clears any previously set vertical position.
|
|
* @param value New bottom offset.
|
|
*/
|
|
bottom(value = "") {
|
|
this._topOffset = "";
|
|
this._bottomOffset = value;
|
|
this._alignItems = "flex-end";
|
|
return this;
|
|
}
|
|
/**
|
|
* Sets the right position of the overlay. Clears any previously set horizontal position.
|
|
* @param value New right offset.
|
|
*/
|
|
right(value = "") {
|
|
this._xOffset = value;
|
|
this._xPosition = "right";
|
|
return this;
|
|
}
|
|
/**
|
|
* Sets the overlay to the start of the viewport, depending on the overlay direction.
|
|
* This will be to the left in LTR layouts and to the right in RTL.
|
|
* @param offset Offset from the edge of the screen.
|
|
*/
|
|
start(value = "") {
|
|
this._xOffset = value;
|
|
this._xPosition = "start";
|
|
return this;
|
|
}
|
|
/**
|
|
* Sets the overlay to the end of the viewport, depending on the overlay direction.
|
|
* This will be to the right in LTR layouts and to the left in RTL.
|
|
* @param offset Offset from the edge of the screen.
|
|
*/
|
|
end(value = "") {
|
|
this._xOffset = value;
|
|
this._xPosition = "end";
|
|
return this;
|
|
}
|
|
/**
|
|
* Sets the overlay width and clears any previously set width.
|
|
* @param value New width for the overlay
|
|
* @deprecated Pass the `width` through the `OverlayConfig`.
|
|
* @breaking-change 8.0.0
|
|
*/
|
|
width(value = "") {
|
|
if (this._overlayRef) {
|
|
this._overlayRef.updateSize({
|
|
width: value
|
|
});
|
|
} else {
|
|
this._width = value;
|
|
}
|
|
return this;
|
|
}
|
|
/**
|
|
* Sets the overlay height and clears any previously set height.
|
|
* @param value New height for the overlay
|
|
* @deprecated Pass the `height` through the `OverlayConfig`.
|
|
* @breaking-change 8.0.0
|
|
*/
|
|
height(value = "") {
|
|
if (this._overlayRef) {
|
|
this._overlayRef.updateSize({
|
|
height: value
|
|
});
|
|
} else {
|
|
this._height = value;
|
|
}
|
|
return this;
|
|
}
|
|
/**
|
|
* Centers the overlay horizontally with an optional offset.
|
|
* Clears any previously set horizontal position.
|
|
*
|
|
* @param offset Overlay offset from the horizontal center.
|
|
*/
|
|
centerHorizontally(offset = "") {
|
|
this.left(offset);
|
|
this._xPosition = "center";
|
|
return this;
|
|
}
|
|
/**
|
|
* Centers the overlay vertically with an optional offset.
|
|
* Clears any previously set vertical position.
|
|
*
|
|
* @param offset Overlay offset from the vertical center.
|
|
*/
|
|
centerVertically(offset = "") {
|
|
this.top(offset);
|
|
this._alignItems = "center";
|
|
return this;
|
|
}
|
|
/**
|
|
* Apply the position to the element.
|
|
* @docs-private
|
|
*/
|
|
apply() {
|
|
if (!this._overlayRef || !this._overlayRef.hasAttached()) {
|
|
return;
|
|
}
|
|
const styles = this._overlayRef.overlayElement.style;
|
|
const parentStyles = this._overlayRef.hostElement.style;
|
|
const config = this._overlayRef.getConfig();
|
|
const {
|
|
width,
|
|
height,
|
|
maxWidth,
|
|
maxHeight
|
|
} = config;
|
|
const shouldBeFlushHorizontally = (width === "100%" || width === "100vw") && (!maxWidth || maxWidth === "100%" || maxWidth === "100vw");
|
|
const shouldBeFlushVertically = (height === "100%" || height === "100vh") && (!maxHeight || maxHeight === "100%" || maxHeight === "100vh");
|
|
const xPosition = this._xPosition;
|
|
const xOffset = this._xOffset;
|
|
const isRtl = this._overlayRef.getConfig().direction === "rtl";
|
|
let marginLeft = "";
|
|
let marginRight = "";
|
|
let justifyContent = "";
|
|
if (shouldBeFlushHorizontally) {
|
|
justifyContent = "flex-start";
|
|
} else if (xPosition === "center") {
|
|
justifyContent = "center";
|
|
if (isRtl) {
|
|
marginRight = xOffset;
|
|
} else {
|
|
marginLeft = xOffset;
|
|
}
|
|
} else if (isRtl) {
|
|
if (xPosition === "left" || xPosition === "end") {
|
|
justifyContent = "flex-end";
|
|
marginLeft = xOffset;
|
|
} else if (xPosition === "right" || xPosition === "start") {
|
|
justifyContent = "flex-start";
|
|
marginRight = xOffset;
|
|
}
|
|
} else if (xPosition === "left" || xPosition === "start") {
|
|
justifyContent = "flex-start";
|
|
marginLeft = xOffset;
|
|
} else if (xPosition === "right" || xPosition === "end") {
|
|
justifyContent = "flex-end";
|
|
marginRight = xOffset;
|
|
}
|
|
styles.position = this._cssPosition;
|
|
styles.marginLeft = shouldBeFlushHorizontally ? "0" : marginLeft;
|
|
styles.marginTop = shouldBeFlushVertically ? "0" : this._topOffset;
|
|
styles.marginBottom = this._bottomOffset;
|
|
styles.marginRight = shouldBeFlushHorizontally ? "0" : marginRight;
|
|
parentStyles.justifyContent = justifyContent;
|
|
parentStyles.alignItems = shouldBeFlushVertically ? "flex-start" : this._alignItems;
|
|
}
|
|
/**
|
|
* Cleans up the DOM changes from the position strategy.
|
|
* @docs-private
|
|
*/
|
|
dispose() {
|
|
if (this._isDisposed || !this._overlayRef) {
|
|
return;
|
|
}
|
|
const styles = this._overlayRef.overlayElement.style;
|
|
const parent = this._overlayRef.hostElement;
|
|
const parentStyles = parent.style;
|
|
parent.classList.remove(wrapperClass);
|
|
parentStyles.justifyContent = parentStyles.alignItems = styles.marginTop = styles.marginBottom = styles.marginLeft = styles.marginRight = styles.position = "";
|
|
this._overlayRef = null;
|
|
this._isDisposed = true;
|
|
}
|
|
};
|
|
var OverlayPositionBuilder = class _OverlayPositionBuilder {
|
|
_injector = inject(Injector);
|
|
constructor() {
|
|
}
|
|
/**
|
|
* Creates a global position strategy.
|
|
*/
|
|
global() {
|
|
return createGlobalPositionStrategy();
|
|
}
|
|
/**
|
|
* Creates a flexible position strategy.
|
|
* @param origin Origin relative to which to position the overlay.
|
|
*/
|
|
flexibleConnectedTo(origin) {
|
|
return createFlexibleConnectedPositionStrategy(this._injector, origin);
|
|
}
|
|
static ɵfac = function OverlayPositionBuilder_Factory(__ngFactoryType__) {
|
|
return new (__ngFactoryType__ || _OverlayPositionBuilder)();
|
|
};
|
|
static ɵprov = ɵɵdefineInjectable({
|
|
token: _OverlayPositionBuilder,
|
|
factory: _OverlayPositionBuilder.ɵfac,
|
|
providedIn: "root"
|
|
});
|
|
};
|
|
(() => {
|
|
(typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(OverlayPositionBuilder, [{
|
|
type: Injectable,
|
|
args: [{
|
|
providedIn: "root"
|
|
}]
|
|
}], () => [], null);
|
|
})();
|
|
function createOverlayRef(injector, config) {
|
|
injector.get(_CdkPrivateStyleLoader).load(_CdkOverlayStyleLoader);
|
|
const overlayContainer = injector.get(OverlayContainer);
|
|
const doc = injector.get(DOCUMENT);
|
|
const idGenerator = injector.get(_IdGenerator);
|
|
const appRef = injector.get(ApplicationRef);
|
|
const directionality = injector.get(Directionality);
|
|
const host = doc.createElement("div");
|
|
const pane = doc.createElement("div");
|
|
pane.id = idGenerator.getId("cdk-overlay-");
|
|
pane.classList.add("cdk-overlay-pane");
|
|
host.appendChild(pane);
|
|
overlayContainer.getContainerElement().appendChild(host);
|
|
const portalOutlet = new DomPortalOutlet(pane, appRef, injector);
|
|
const overlayConfig = new OverlayConfig(config);
|
|
const renderer = injector.get(Renderer2, null, {
|
|
optional: true
|
|
}) || injector.get(RendererFactory2).createRenderer(null, null);
|
|
overlayConfig.direction = overlayConfig.direction || directionality.value;
|
|
return new OverlayRef(portalOutlet, host, pane, overlayConfig, injector.get(NgZone), injector.get(OverlayKeyboardDispatcher), doc, injector.get(Location), injector.get(OverlayOutsideClickDispatcher), config?.disableAnimations ?? injector.get(ANIMATION_MODULE_TYPE, null, {
|
|
optional: true
|
|
}) === "NoopAnimations", injector.get(EnvironmentInjector), renderer);
|
|
}
|
|
var Overlay = class _Overlay {
|
|
scrollStrategies = inject(ScrollStrategyOptions);
|
|
_positionBuilder = inject(OverlayPositionBuilder);
|
|
_injector = inject(Injector);
|
|
constructor() {
|
|
}
|
|
/**
|
|
* Creates an overlay.
|
|
* @param config Configuration applied to the overlay.
|
|
* @returns Reference to the created overlay.
|
|
*/
|
|
create(config) {
|
|
return createOverlayRef(this._injector, config);
|
|
}
|
|
/**
|
|
* Gets a position builder that can be used, via fluent API,
|
|
* to construct and configure a position strategy.
|
|
* @returns An overlay position builder.
|
|
*/
|
|
position() {
|
|
return this._positionBuilder;
|
|
}
|
|
static ɵfac = function Overlay_Factory(__ngFactoryType__) {
|
|
return new (__ngFactoryType__ || _Overlay)();
|
|
};
|
|
static ɵprov = ɵɵdefineInjectable({
|
|
token: _Overlay,
|
|
factory: _Overlay.ɵfac,
|
|
providedIn: "root"
|
|
});
|
|
};
|
|
(() => {
|
|
(typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(Overlay, [{
|
|
type: Injectable,
|
|
args: [{
|
|
providedIn: "root"
|
|
}]
|
|
}], () => [], null);
|
|
})();
|
|
var defaultPositionList = [{
|
|
originX: "start",
|
|
originY: "bottom",
|
|
overlayX: "start",
|
|
overlayY: "top"
|
|
}, {
|
|
originX: "start",
|
|
originY: "top",
|
|
overlayX: "start",
|
|
overlayY: "bottom"
|
|
}, {
|
|
originX: "end",
|
|
originY: "top",
|
|
overlayX: "end",
|
|
overlayY: "bottom"
|
|
}, {
|
|
originX: "end",
|
|
originY: "bottom",
|
|
overlayX: "end",
|
|
overlayY: "top"
|
|
}];
|
|
var CDK_CONNECTED_OVERLAY_SCROLL_STRATEGY = new InjectionToken("cdk-connected-overlay-scroll-strategy", {
|
|
providedIn: "root",
|
|
factory: () => {
|
|
const injector = inject(Injector);
|
|
return () => createRepositionScrollStrategy(injector);
|
|
}
|
|
});
|
|
var CdkOverlayOrigin = class _CdkOverlayOrigin {
|
|
elementRef = inject(ElementRef);
|
|
constructor() {
|
|
}
|
|
static ɵfac = function CdkOverlayOrigin_Factory(__ngFactoryType__) {
|
|
return new (__ngFactoryType__ || _CdkOverlayOrigin)();
|
|
};
|
|
static ɵdir = ɵɵdefineDirective({
|
|
type: _CdkOverlayOrigin,
|
|
selectors: [["", "cdk-overlay-origin", ""], ["", "overlay-origin", ""], ["", "cdkOverlayOrigin", ""]],
|
|
exportAs: ["cdkOverlayOrigin"]
|
|
});
|
|
};
|
|
(() => {
|
|
(typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(CdkOverlayOrigin, [{
|
|
type: Directive,
|
|
args: [{
|
|
selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]",
|
|
exportAs: "cdkOverlayOrigin"
|
|
}]
|
|
}], () => [], null);
|
|
})();
|
|
var CdkConnectedOverlay = class _CdkConnectedOverlay {
|
|
_dir = inject(Directionality, {
|
|
optional: true
|
|
});
|
|
_injector = inject(Injector);
|
|
_overlayRef;
|
|
_templatePortal;
|
|
_backdropSubscription = Subscription.EMPTY;
|
|
_attachSubscription = Subscription.EMPTY;
|
|
_detachSubscription = Subscription.EMPTY;
|
|
_positionSubscription = Subscription.EMPTY;
|
|
_offsetX;
|
|
_offsetY;
|
|
_position;
|
|
_scrollStrategyFactory = inject(CDK_CONNECTED_OVERLAY_SCROLL_STRATEGY);
|
|
_disposeOnNavigation = false;
|
|
_ngZone = inject(NgZone);
|
|
/** Origin for the connected overlay. */
|
|
origin;
|
|
/** Registered connected position pairs. */
|
|
positions;
|
|
/**
|
|
* This input overrides the positions input if specified. It lets users pass
|
|
* in arbitrary positioning strategies.
|
|
*/
|
|
positionStrategy;
|
|
/** The offset in pixels for the overlay connection point on the x-axis */
|
|
get offsetX() {
|
|
return this._offsetX;
|
|
}
|
|
set offsetX(offsetX) {
|
|
this._offsetX = offsetX;
|
|
if (this._position) {
|
|
this._updatePositionStrategy(this._position);
|
|
}
|
|
}
|
|
/** The offset in pixels for the overlay connection point on the y-axis */
|
|
get offsetY() {
|
|
return this._offsetY;
|
|
}
|
|
set offsetY(offsetY) {
|
|
this._offsetY = offsetY;
|
|
if (this._position) {
|
|
this._updatePositionStrategy(this._position);
|
|
}
|
|
}
|
|
/** The width of the overlay panel. */
|
|
width;
|
|
/** The height of the overlay panel. */
|
|
height;
|
|
/** The min width of the overlay panel. */
|
|
minWidth;
|
|
/** The min height of the overlay panel. */
|
|
minHeight;
|
|
/** The custom class to be set on the backdrop element. */
|
|
backdropClass;
|
|
/** The custom class to add to the overlay pane element. */
|
|
panelClass;
|
|
/** Margin between the overlay and the viewport edges. */
|
|
viewportMargin = 0;
|
|
/** Strategy to be used when handling scroll events while the overlay is open. */
|
|
scrollStrategy;
|
|
/** Whether the overlay is open. */
|
|
open = false;
|
|
/** Whether the overlay can be closed by user interaction. */
|
|
disableClose = false;
|
|
/** CSS selector which to set the transform origin. */
|
|
transformOriginSelector;
|
|
/** Whether or not the overlay should attach a backdrop. */
|
|
hasBackdrop = false;
|
|
/** Whether or not the overlay should be locked when scrolling. */
|
|
lockPosition = false;
|
|
/** Whether the overlay's width and height can be constrained to fit within the viewport. */
|
|
flexibleDimensions = false;
|
|
/** Whether the overlay can grow after the initial open when flexible positioning is turned on. */
|
|
growAfterOpen = false;
|
|
/** Whether the overlay can be pushed on-screen if none of the provided positions fit. */
|
|
push = false;
|
|
/** Whether the overlay should be disposed of when the user goes backwards/forwards in history. */
|
|
get disposeOnNavigation() {
|
|
return this._disposeOnNavigation;
|
|
}
|
|
set disposeOnNavigation(value) {
|
|
this._disposeOnNavigation = value;
|
|
}
|
|
/** Event emitted when the backdrop is clicked. */
|
|
backdropClick = new EventEmitter();
|
|
/** Event emitted when the position has changed. */
|
|
positionChange = new EventEmitter();
|
|
/** Event emitted when the overlay has been attached. */
|
|
attach = new EventEmitter();
|
|
/** Event emitted when the overlay has been detached. */
|
|
detach = new EventEmitter();
|
|
/** Emits when there are keyboard events that are targeted at the overlay. */
|
|
overlayKeydown = new EventEmitter();
|
|
/** Emits when there are mouse outside click events that are targeted at the overlay. */
|
|
overlayOutsideClick = new EventEmitter();
|
|
// TODO(jelbourn): inputs for size, scroll behavior, animation, etc.
|
|
constructor() {
|
|
const templateRef = inject(TemplateRef);
|
|
const viewContainerRef = inject(ViewContainerRef);
|
|
this._templatePortal = new TemplatePortal(templateRef, viewContainerRef);
|
|
this.scrollStrategy = this._scrollStrategyFactory();
|
|
}
|
|
/** The associated overlay reference. */
|
|
get overlayRef() {
|
|
return this._overlayRef;
|
|
}
|
|
/** The element's layout direction. */
|
|
get dir() {
|
|
return this._dir ? this._dir.value : "ltr";
|
|
}
|
|
ngOnDestroy() {
|
|
this._attachSubscription.unsubscribe();
|
|
this._detachSubscription.unsubscribe();
|
|
this._backdropSubscription.unsubscribe();
|
|
this._positionSubscription.unsubscribe();
|
|
this._overlayRef?.dispose();
|
|
}
|
|
ngOnChanges(changes) {
|
|
if (this._position) {
|
|
this._updatePositionStrategy(this._position);
|
|
this._overlayRef?.updateSize({
|
|
width: this.width,
|
|
minWidth: this.minWidth,
|
|
height: this.height,
|
|
minHeight: this.minHeight
|
|
});
|
|
if (changes["origin"] && this.open) {
|
|
this._position.apply();
|
|
}
|
|
}
|
|
if (changes["open"]) {
|
|
this.open ? this.attachOverlay() : this.detachOverlay();
|
|
}
|
|
}
|
|
/** Creates an overlay */
|
|
_createOverlay() {
|
|
if (!this.positions || !this.positions.length) {
|
|
this.positions = defaultPositionList;
|
|
}
|
|
const overlayRef = this._overlayRef = createOverlayRef(this._injector, this._buildConfig());
|
|
this._attachSubscription = overlayRef.attachments().subscribe(() => this.attach.emit());
|
|
this._detachSubscription = overlayRef.detachments().subscribe(() => this.detach.emit());
|
|
overlayRef.keydownEvents().subscribe((event) => {
|
|
this.overlayKeydown.next(event);
|
|
if (event.keyCode === ESCAPE && !this.disableClose && !hasModifierKey(event)) {
|
|
event.preventDefault();
|
|
this.detachOverlay();
|
|
}
|
|
});
|
|
this._overlayRef.outsidePointerEvents().subscribe((event) => {
|
|
const origin = this._getOriginElement();
|
|
const target = _getEventTarget(event);
|
|
if (!origin || origin !== target && !origin.contains(target)) {
|
|
this.overlayOutsideClick.next(event);
|
|
}
|
|
});
|
|
}
|
|
/** Builds the overlay config based on the directive's inputs */
|
|
_buildConfig() {
|
|
const positionStrategy = this._position = this.positionStrategy || this._createPositionStrategy();
|
|
const overlayConfig = new OverlayConfig({
|
|
direction: this._dir || "ltr",
|
|
positionStrategy,
|
|
scrollStrategy: this.scrollStrategy,
|
|
hasBackdrop: this.hasBackdrop,
|
|
disposeOnNavigation: this.disposeOnNavigation
|
|
});
|
|
if (this.width || this.width === 0) {
|
|
overlayConfig.width = this.width;
|
|
}
|
|
if (this.height || this.height === 0) {
|
|
overlayConfig.height = this.height;
|
|
}
|
|
if (this.minWidth || this.minWidth === 0) {
|
|
overlayConfig.minWidth = this.minWidth;
|
|
}
|
|
if (this.minHeight || this.minHeight === 0) {
|
|
overlayConfig.minHeight = this.minHeight;
|
|
}
|
|
if (this.backdropClass) {
|
|
overlayConfig.backdropClass = this.backdropClass;
|
|
}
|
|
if (this.panelClass) {
|
|
overlayConfig.panelClass = this.panelClass;
|
|
}
|
|
return overlayConfig;
|
|
}
|
|
/** Updates the state of a position strategy, based on the values of the directive inputs. */
|
|
_updatePositionStrategy(positionStrategy) {
|
|
const positions = this.positions.map((currentPosition) => ({
|
|
originX: currentPosition.originX,
|
|
originY: currentPosition.originY,
|
|
overlayX: currentPosition.overlayX,
|
|
overlayY: currentPosition.overlayY,
|
|
offsetX: currentPosition.offsetX || this.offsetX,
|
|
offsetY: currentPosition.offsetY || this.offsetY,
|
|
panelClass: currentPosition.panelClass || void 0
|
|
}));
|
|
return positionStrategy.setOrigin(this._getOrigin()).withPositions(positions).withFlexibleDimensions(this.flexibleDimensions).withPush(this.push).withGrowAfterOpen(this.growAfterOpen).withViewportMargin(this.viewportMargin).withLockedPosition(this.lockPosition).withTransformOriginOn(this.transformOriginSelector);
|
|
}
|
|
/** Returns the position strategy of the overlay to be set on the overlay config */
|
|
_createPositionStrategy() {
|
|
const strategy = createFlexibleConnectedPositionStrategy(this._injector, this._getOrigin());
|
|
this._updatePositionStrategy(strategy);
|
|
return strategy;
|
|
}
|
|
_getOrigin() {
|
|
if (this.origin instanceof CdkOverlayOrigin) {
|
|
return this.origin.elementRef;
|
|
} else {
|
|
return this.origin;
|
|
}
|
|
}
|
|
_getOriginElement() {
|
|
if (this.origin instanceof CdkOverlayOrigin) {
|
|
return this.origin.elementRef.nativeElement;
|
|
}
|
|
if (this.origin instanceof ElementRef) {
|
|
return this.origin.nativeElement;
|
|
}
|
|
if (typeof Element !== "undefined" && this.origin instanceof Element) {
|
|
return this.origin;
|
|
}
|
|
return null;
|
|
}
|
|
/** Attaches the overlay. */
|
|
attachOverlay() {
|
|
if (!this._overlayRef) {
|
|
this._createOverlay();
|
|
} else {
|
|
this._overlayRef.getConfig().hasBackdrop = this.hasBackdrop;
|
|
}
|
|
if (!this._overlayRef.hasAttached()) {
|
|
this._overlayRef.attach(this._templatePortal);
|
|
}
|
|
if (this.hasBackdrop) {
|
|
this._backdropSubscription = this._overlayRef.backdropClick().subscribe((event) => {
|
|
this.backdropClick.emit(event);
|
|
});
|
|
} else {
|
|
this._backdropSubscription.unsubscribe();
|
|
}
|
|
this._positionSubscription.unsubscribe();
|
|
if (this.positionChange.observers.length > 0) {
|
|
this._positionSubscription = this._position.positionChanges.pipe(takeWhile(() => this.positionChange.observers.length > 0)).subscribe((position) => {
|
|
this._ngZone.run(() => this.positionChange.emit(position));
|
|
if (this.positionChange.observers.length === 0) {
|
|
this._positionSubscription.unsubscribe();
|
|
}
|
|
});
|
|
}
|
|
this.open = true;
|
|
}
|
|
/** Detaches the overlay. */
|
|
detachOverlay() {
|
|
this._overlayRef?.detach();
|
|
this._backdropSubscription.unsubscribe();
|
|
this._positionSubscription.unsubscribe();
|
|
this.open = false;
|
|
}
|
|
static ɵfac = function CdkConnectedOverlay_Factory(__ngFactoryType__) {
|
|
return new (__ngFactoryType__ || _CdkConnectedOverlay)();
|
|
};
|
|
static ɵdir = ɵɵdefineDirective({
|
|
type: _CdkConnectedOverlay,
|
|
selectors: [["", "cdk-connected-overlay", ""], ["", "connected-overlay", ""], ["", "cdkConnectedOverlay", ""]],
|
|
inputs: {
|
|
origin: [0, "cdkConnectedOverlayOrigin", "origin"],
|
|
positions: [0, "cdkConnectedOverlayPositions", "positions"],
|
|
positionStrategy: [0, "cdkConnectedOverlayPositionStrategy", "positionStrategy"],
|
|
offsetX: [0, "cdkConnectedOverlayOffsetX", "offsetX"],
|
|
offsetY: [0, "cdkConnectedOverlayOffsetY", "offsetY"],
|
|
width: [0, "cdkConnectedOverlayWidth", "width"],
|
|
height: [0, "cdkConnectedOverlayHeight", "height"],
|
|
minWidth: [0, "cdkConnectedOverlayMinWidth", "minWidth"],
|
|
minHeight: [0, "cdkConnectedOverlayMinHeight", "minHeight"],
|
|
backdropClass: [0, "cdkConnectedOverlayBackdropClass", "backdropClass"],
|
|
panelClass: [0, "cdkConnectedOverlayPanelClass", "panelClass"],
|
|
viewportMargin: [0, "cdkConnectedOverlayViewportMargin", "viewportMargin"],
|
|
scrollStrategy: [0, "cdkConnectedOverlayScrollStrategy", "scrollStrategy"],
|
|
open: [0, "cdkConnectedOverlayOpen", "open"],
|
|
disableClose: [0, "cdkConnectedOverlayDisableClose", "disableClose"],
|
|
transformOriginSelector: [0, "cdkConnectedOverlayTransformOriginOn", "transformOriginSelector"],
|
|
hasBackdrop: [2, "cdkConnectedOverlayHasBackdrop", "hasBackdrop", booleanAttribute],
|
|
lockPosition: [2, "cdkConnectedOverlayLockPosition", "lockPosition", booleanAttribute],
|
|
flexibleDimensions: [2, "cdkConnectedOverlayFlexibleDimensions", "flexibleDimensions", booleanAttribute],
|
|
growAfterOpen: [2, "cdkConnectedOverlayGrowAfterOpen", "growAfterOpen", booleanAttribute],
|
|
push: [2, "cdkConnectedOverlayPush", "push", booleanAttribute],
|
|
disposeOnNavigation: [2, "cdkConnectedOverlayDisposeOnNavigation", "disposeOnNavigation", booleanAttribute]
|
|
},
|
|
outputs: {
|
|
backdropClick: "backdropClick",
|
|
positionChange: "positionChange",
|
|
attach: "attach",
|
|
detach: "detach",
|
|
overlayKeydown: "overlayKeydown",
|
|
overlayOutsideClick: "overlayOutsideClick"
|
|
},
|
|
exportAs: ["cdkConnectedOverlay"],
|
|
features: [ɵɵNgOnChangesFeature]
|
|
});
|
|
};
|
|
(() => {
|
|
(typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(CdkConnectedOverlay, [{
|
|
type: Directive,
|
|
args: [{
|
|
selector: "[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]",
|
|
exportAs: "cdkConnectedOverlay"
|
|
}]
|
|
}], () => [], {
|
|
origin: [{
|
|
type: Input,
|
|
args: ["cdkConnectedOverlayOrigin"]
|
|
}],
|
|
positions: [{
|
|
type: Input,
|
|
args: ["cdkConnectedOverlayPositions"]
|
|
}],
|
|
positionStrategy: [{
|
|
type: Input,
|
|
args: ["cdkConnectedOverlayPositionStrategy"]
|
|
}],
|
|
offsetX: [{
|
|
type: Input,
|
|
args: ["cdkConnectedOverlayOffsetX"]
|
|
}],
|
|
offsetY: [{
|
|
type: Input,
|
|
args: ["cdkConnectedOverlayOffsetY"]
|
|
}],
|
|
width: [{
|
|
type: Input,
|
|
args: ["cdkConnectedOverlayWidth"]
|
|
}],
|
|
height: [{
|
|
type: Input,
|
|
args: ["cdkConnectedOverlayHeight"]
|
|
}],
|
|
minWidth: [{
|
|
type: Input,
|
|
args: ["cdkConnectedOverlayMinWidth"]
|
|
}],
|
|
minHeight: [{
|
|
type: Input,
|
|
args: ["cdkConnectedOverlayMinHeight"]
|
|
}],
|
|
backdropClass: [{
|
|
type: Input,
|
|
args: ["cdkConnectedOverlayBackdropClass"]
|
|
}],
|
|
panelClass: [{
|
|
type: Input,
|
|
args: ["cdkConnectedOverlayPanelClass"]
|
|
}],
|
|
viewportMargin: [{
|
|
type: Input,
|
|
args: ["cdkConnectedOverlayViewportMargin"]
|
|
}],
|
|
scrollStrategy: [{
|
|
type: Input,
|
|
args: ["cdkConnectedOverlayScrollStrategy"]
|
|
}],
|
|
open: [{
|
|
type: Input,
|
|
args: ["cdkConnectedOverlayOpen"]
|
|
}],
|
|
disableClose: [{
|
|
type: Input,
|
|
args: ["cdkConnectedOverlayDisableClose"]
|
|
}],
|
|
transformOriginSelector: [{
|
|
type: Input,
|
|
args: ["cdkConnectedOverlayTransformOriginOn"]
|
|
}],
|
|
hasBackdrop: [{
|
|
type: Input,
|
|
args: [{
|
|
alias: "cdkConnectedOverlayHasBackdrop",
|
|
transform: booleanAttribute
|
|
}]
|
|
}],
|
|
lockPosition: [{
|
|
type: Input,
|
|
args: [{
|
|
alias: "cdkConnectedOverlayLockPosition",
|
|
transform: booleanAttribute
|
|
}]
|
|
}],
|
|
flexibleDimensions: [{
|
|
type: Input,
|
|
args: [{
|
|
alias: "cdkConnectedOverlayFlexibleDimensions",
|
|
transform: booleanAttribute
|
|
}]
|
|
}],
|
|
growAfterOpen: [{
|
|
type: Input,
|
|
args: [{
|
|
alias: "cdkConnectedOverlayGrowAfterOpen",
|
|
transform: booleanAttribute
|
|
}]
|
|
}],
|
|
push: [{
|
|
type: Input,
|
|
args: [{
|
|
alias: "cdkConnectedOverlayPush",
|
|
transform: booleanAttribute
|
|
}]
|
|
}],
|
|
disposeOnNavigation: [{
|
|
type: Input,
|
|
args: [{
|
|
alias: "cdkConnectedOverlayDisposeOnNavigation",
|
|
transform: booleanAttribute
|
|
}]
|
|
}],
|
|
backdropClick: [{
|
|
type: Output
|
|
}],
|
|
positionChange: [{
|
|
type: Output
|
|
}],
|
|
attach: [{
|
|
type: Output
|
|
}],
|
|
detach: [{
|
|
type: Output
|
|
}],
|
|
overlayKeydown: [{
|
|
type: Output
|
|
}],
|
|
overlayOutsideClick: [{
|
|
type: Output
|
|
}]
|
|
});
|
|
})();
|
|
function CDK_CONNECTED_OVERLAY_SCROLL_STRATEGY_PROVIDER_FACTORY(overlay) {
|
|
const injector = inject(Injector);
|
|
return () => createRepositionScrollStrategy(injector);
|
|
}
|
|
var CDK_CONNECTED_OVERLAY_SCROLL_STRATEGY_PROVIDER = {
|
|
provide: CDK_CONNECTED_OVERLAY_SCROLL_STRATEGY,
|
|
useFactory: CDK_CONNECTED_OVERLAY_SCROLL_STRATEGY_PROVIDER_FACTORY
|
|
};
|
|
var OverlayModule = class _OverlayModule {
|
|
static ɵfac = function OverlayModule_Factory(__ngFactoryType__) {
|
|
return new (__ngFactoryType__ || _OverlayModule)();
|
|
};
|
|
static ɵmod = ɵɵdefineNgModule({
|
|
type: _OverlayModule,
|
|
imports: [BidiModule, PortalModule, ScrollingModule, CdkConnectedOverlay, CdkOverlayOrigin],
|
|
exports: [CdkConnectedOverlay, CdkOverlayOrigin, ScrollingModule]
|
|
});
|
|
static ɵinj = ɵɵdefineInjector({
|
|
providers: [Overlay, CDK_CONNECTED_OVERLAY_SCROLL_STRATEGY_PROVIDER],
|
|
imports: [BidiModule, PortalModule, ScrollingModule, ScrollingModule]
|
|
});
|
|
};
|
|
(() => {
|
|
(typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(OverlayModule, [{
|
|
type: NgModule,
|
|
args: [{
|
|
imports: [BidiModule, PortalModule, ScrollingModule, CdkConnectedOverlay, CdkOverlayOrigin],
|
|
exports: [CdkConnectedOverlay, CdkOverlayOrigin, ScrollingModule],
|
|
providers: [Overlay, CDK_CONNECTED_OVERLAY_SCROLL_STRATEGY_PROVIDER]
|
|
}]
|
|
}], null, null);
|
|
})();
|
|
|
|
// node_modules/@angular/cdk/fesm2022/overlay.mjs
|
|
var FullscreenOverlayContainer = class _FullscreenOverlayContainer extends OverlayContainer {
|
|
_renderer = inject(RendererFactory2).createRenderer(null, null);
|
|
_fullScreenEventName;
|
|
_cleanupFullScreenListener;
|
|
constructor() {
|
|
super();
|
|
}
|
|
ngOnDestroy() {
|
|
super.ngOnDestroy();
|
|
this._cleanupFullScreenListener?.();
|
|
}
|
|
_createContainer() {
|
|
const eventName = this._getEventName();
|
|
super._createContainer();
|
|
this._adjustParentForFullscreenChange();
|
|
if (eventName) {
|
|
this._cleanupFullScreenListener?.();
|
|
this._cleanupFullScreenListener = this._renderer.listen("document", eventName, () => {
|
|
this._adjustParentForFullscreenChange();
|
|
});
|
|
}
|
|
}
|
|
_adjustParentForFullscreenChange() {
|
|
if (this._containerElement) {
|
|
const fullscreenElement = this.getFullscreenElement();
|
|
const parent = fullscreenElement || this._document.body;
|
|
parent.appendChild(this._containerElement);
|
|
}
|
|
}
|
|
_getEventName() {
|
|
if (!this._fullScreenEventName) {
|
|
const _document = this._document;
|
|
if (_document.fullscreenEnabled) {
|
|
this._fullScreenEventName = "fullscreenchange";
|
|
} else if (_document.webkitFullscreenEnabled) {
|
|
this._fullScreenEventName = "webkitfullscreenchange";
|
|
} else if (_document.mozFullScreenEnabled) {
|
|
this._fullScreenEventName = "mozfullscreenchange";
|
|
} else if (_document.msFullscreenEnabled) {
|
|
this._fullScreenEventName = "MSFullscreenChange";
|
|
}
|
|
}
|
|
return this._fullScreenEventName;
|
|
}
|
|
/**
|
|
* When the page is put into fullscreen mode, a specific element is specified.
|
|
* Only that element and its children are visible when in fullscreen mode.
|
|
*/
|
|
getFullscreenElement() {
|
|
const _document = this._document;
|
|
return _document.fullscreenElement || _document.webkitFullscreenElement || _document.mozFullScreenElement || _document.msFullscreenElement || null;
|
|
}
|
|
static ɵfac = function FullscreenOverlayContainer_Factory(__ngFactoryType__) {
|
|
return new (__ngFactoryType__ || _FullscreenOverlayContainer)();
|
|
};
|
|
static ɵprov = ɵɵdefineInjectable({
|
|
token: _FullscreenOverlayContainer,
|
|
factory: _FullscreenOverlayContainer.ɵfac,
|
|
providedIn: "root"
|
|
});
|
|
};
|
|
(() => {
|
|
(typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(FullscreenOverlayContainer, [{
|
|
type: Injectable,
|
|
args: [{
|
|
providedIn: "root"
|
|
}]
|
|
}], () => [], null);
|
|
})();
|
|
|
|
// node_modules/ng-zorro-antd/fesm2022/ng-zorro-antd-core-polyfill.mjs
|
|
var requestAnimationFrame2 = typeof globalThis.requestAnimationFrame === "function" ? globalThis.requestAnimationFrame : globalThis.setTimeout;
|
|
var cancelAnimationFrame = typeof globalThis.requestAnimationFrame === "function" ? globalThis.cancelAnimationFrame : globalThis.clearTimeout;
|
|
|
|
// node_modules/@angular/cdk/fesm2022/breakpoints-observer.mjs
|
|
var mediaQueriesForWebkitCompatibility = /* @__PURE__ */ new Set();
|
|
var mediaQueryStyleNode;
|
|
var MediaMatcher = class _MediaMatcher {
|
|
_platform = inject(Platform);
|
|
_nonce = inject(CSP_NONCE, {
|
|
optional: true
|
|
});
|
|
/** The internal matchMedia method to return back a MediaQueryList like object. */
|
|
_matchMedia;
|
|
constructor() {
|
|
this._matchMedia = this._platform.isBrowser && window.matchMedia ? (
|
|
// matchMedia is bound to the window scope intentionally as it is an illegal invocation to
|
|
// call it from a different scope.
|
|
window.matchMedia.bind(window)
|
|
) : noopMatchMedia;
|
|
}
|
|
/**
|
|
* Evaluates the given media query and returns the native MediaQueryList from which results
|
|
* can be retrieved.
|
|
* Confirms the layout engine will trigger for the selector query provided and returns the
|
|
* MediaQueryList for the query provided.
|
|
*/
|
|
matchMedia(query2) {
|
|
if (this._platform.WEBKIT || this._platform.BLINK) {
|
|
createEmptyStyleRule(query2, this._nonce);
|
|
}
|
|
return this._matchMedia(query2);
|
|
}
|
|
static ɵfac = function MediaMatcher_Factory(__ngFactoryType__) {
|
|
return new (__ngFactoryType__ || _MediaMatcher)();
|
|
};
|
|
static ɵprov = ɵɵdefineInjectable({
|
|
token: _MediaMatcher,
|
|
factory: _MediaMatcher.ɵfac,
|
|
providedIn: "root"
|
|
});
|
|
};
|
|
(() => {
|
|
(typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(MediaMatcher, [{
|
|
type: Injectable,
|
|
args: [{
|
|
providedIn: "root"
|
|
}]
|
|
}], () => [], null);
|
|
})();
|
|
function createEmptyStyleRule(query2, nonce) {
|
|
if (mediaQueriesForWebkitCompatibility.has(query2)) {
|
|
return;
|
|
}
|
|
try {
|
|
if (!mediaQueryStyleNode) {
|
|
mediaQueryStyleNode = document.createElement("style");
|
|
if (nonce) {
|
|
mediaQueryStyleNode.setAttribute("nonce", nonce);
|
|
}
|
|
mediaQueryStyleNode.setAttribute("type", "text/css");
|
|
document.head.appendChild(mediaQueryStyleNode);
|
|
}
|
|
if (mediaQueryStyleNode.sheet) {
|
|
mediaQueryStyleNode.sheet.insertRule(`@media ${query2} {body{ }}`, 0);
|
|
mediaQueriesForWebkitCompatibility.add(query2);
|
|
}
|
|
} catch (e) {
|
|
console.error(e);
|
|
}
|
|
}
|
|
function noopMatchMedia(query2) {
|
|
return {
|
|
matches: query2 === "all" || query2 === "",
|
|
media: query2,
|
|
addListener: () => {
|
|
},
|
|
removeListener: () => {
|
|
}
|
|
};
|
|
}
|
|
var BreakpointObserver = class _BreakpointObserver {
|
|
_mediaMatcher = inject(MediaMatcher);
|
|
_zone = inject(NgZone);
|
|
/** A map of all media queries currently being listened for. */
|
|
_queries = /* @__PURE__ */ new Map();
|
|
/** A subject for all other observables to takeUntil based on. */
|
|
_destroySubject = new Subject();
|
|
constructor() {
|
|
}
|
|
/** Completes the active subject, signalling to all other observables to complete. */
|
|
ngOnDestroy() {
|
|
this._destroySubject.next();
|
|
this._destroySubject.complete();
|
|
}
|
|
/**
|
|
* Whether one or more media queries match the current viewport size.
|
|
* @param value One or more media queries to check.
|
|
* @returns Whether any of the media queries match.
|
|
*/
|
|
isMatched(value) {
|
|
const queries = splitQueries(coerceArray(value));
|
|
return queries.some((mediaQuery) => this._registerQuery(mediaQuery).mql.matches);
|
|
}
|
|
/**
|
|
* Gets an observable of results for the given queries that will emit new results for any changes
|
|
* in matching of the given queries.
|
|
* @param value One or more media queries to check.
|
|
* @returns A stream of matches for the given queries.
|
|
*/
|
|
observe(value) {
|
|
const queries = splitQueries(coerceArray(value));
|
|
const observables = queries.map((query2) => this._registerQuery(query2).observable);
|
|
let stateObservable = combineLatest(observables);
|
|
stateObservable = concat(stateObservable.pipe(take(1)), stateObservable.pipe(skip(1), debounceTime(0)));
|
|
return stateObservable.pipe(map((breakpointStates) => {
|
|
const response = {
|
|
matches: false,
|
|
breakpoints: {}
|
|
};
|
|
breakpointStates.forEach(({
|
|
matches,
|
|
query: query2
|
|
}) => {
|
|
response.matches = response.matches || matches;
|
|
response.breakpoints[query2] = matches;
|
|
});
|
|
return response;
|
|
}));
|
|
}
|
|
/** Registers a specific query to be listened for. */
|
|
_registerQuery(query2) {
|
|
if (this._queries.has(query2)) {
|
|
return this._queries.get(query2);
|
|
}
|
|
const mql = this._mediaMatcher.matchMedia(query2);
|
|
const queryObservable = new Observable((observer) => {
|
|
const handler = (e) => this._zone.run(() => observer.next(e));
|
|
mql.addListener(handler);
|
|
return () => {
|
|
mql.removeListener(handler);
|
|
};
|
|
}).pipe(startWith(mql), map(({
|
|
matches
|
|
}) => ({
|
|
query: query2,
|
|
matches
|
|
})), takeUntil(this._destroySubject));
|
|
const output = {
|
|
observable: queryObservable,
|
|
mql
|
|
};
|
|
this._queries.set(query2, output);
|
|
return output;
|
|
}
|
|
static ɵfac = function BreakpointObserver_Factory(__ngFactoryType__) {
|
|
return new (__ngFactoryType__ || _BreakpointObserver)();
|
|
};
|
|
static ɵprov = ɵɵdefineInjectable({
|
|
token: _BreakpointObserver,
|
|
factory: _BreakpointObserver.ɵfac,
|
|
providedIn: "root"
|
|
});
|
|
};
|
|
(() => {
|
|
(typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(BreakpointObserver, [{
|
|
type: Injectable,
|
|
args: [{
|
|
providedIn: "root"
|
|
}]
|
|
}], () => [], null);
|
|
})();
|
|
function splitQueries(queries) {
|
|
return queries.map((query2) => query2.split(",")).reduce((a1, a2) => a1.concat(a2)).map((query2) => query2.trim());
|
|
}
|
|
|
|
// node_modules/@angular/cdk/fesm2022/layout.mjs
|
|
var LayoutModule = class _LayoutModule {
|
|
static ɵfac = function LayoutModule_Factory(__ngFactoryType__) {
|
|
return new (__ngFactoryType__ || _LayoutModule)();
|
|
};
|
|
static ɵmod = ɵɵdefineNgModule({
|
|
type: _LayoutModule
|
|
});
|
|
static ɵinj = ɵɵdefineInjector({});
|
|
};
|
|
(() => {
|
|
(typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(LayoutModule, [{
|
|
type: NgModule,
|
|
args: [{}]
|
|
}], null, null);
|
|
})();
|
|
|
|
// node_modules/ng-zorro-antd/fesm2022/ng-zorro-antd-core-services.mjs
|
|
var NOOP = () => {
|
|
};
|
|
var NzResizeService = class _NzResizeService {
|
|
ngZone = inject(NgZone);
|
|
destroyRef = inject(DestroyRef);
|
|
resizeSource$ = new Subject();
|
|
listeners = 0;
|
|
renderer = inject(RendererFactory2).createRenderer(null, null);
|
|
disposeHandle = NOOP;
|
|
handler = () => {
|
|
this.ngZone.run(() => {
|
|
this.resizeSource$.next();
|
|
});
|
|
};
|
|
constructor() {
|
|
this.destroyRef.onDestroy(() => {
|
|
this.handler = NOOP;
|
|
});
|
|
}
|
|
connect() {
|
|
this.registerListener();
|
|
return this.resizeSource$.pipe(auditTime(16), finalize(() => this.unregisterListener()));
|
|
}
|
|
disconnet() {
|
|
this.unregisterListener();
|
|
}
|
|
registerListener() {
|
|
if (this.listeners === 0) {
|
|
this.ngZone.runOutsideAngular(() => {
|
|
this.disposeHandle = this.renderer.listen("window", "resize", this.handler);
|
|
});
|
|
}
|
|
this.listeners += 1;
|
|
}
|
|
unregisterListener() {
|
|
this.listeners -= 1;
|
|
if (this.listeners === 0) {
|
|
this.disposeHandle();
|
|
this.disposeHandle = NOOP;
|
|
}
|
|
}
|
|
static ɵfac = function NzResizeService_Factory(__ngFactoryType__) {
|
|
return new (__ngFactoryType__ || _NzResizeService)();
|
|
};
|
|
static ɵprov = ɵɵdefineInjectable({
|
|
token: _NzResizeService,
|
|
factory: _NzResizeService.ɵfac,
|
|
providedIn: "root"
|
|
});
|
|
};
|
|
(() => {
|
|
(typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(NzResizeService, [{
|
|
type: Injectable,
|
|
args: [{
|
|
providedIn: "root"
|
|
}]
|
|
}], () => [], null);
|
|
})();
|
|
var testSingleRegistry = /* @__PURE__ */ new Map();
|
|
var NzSingletonService = class _NzSingletonService {
|
|
get singletonRegistry() {
|
|
return environment.isTestMode ? testSingleRegistry : this._singletonRegistry;
|
|
}
|
|
/**
|
|
* This registry is used to register singleton in dev mode.
|
|
* So that singletons get destroyed when hot module reload happens.
|
|
*
|
|
* This works in prod mode too but with no specific effect.
|
|
*/
|
|
_singletonRegistry = /* @__PURE__ */ new Map();
|
|
registerSingletonWithKey(key, target) {
|
|
const alreadyHave = this.singletonRegistry.has(key);
|
|
const item = alreadyHave ? this.singletonRegistry.get(key) : this.withNewTarget(target);
|
|
if (!alreadyHave) {
|
|
this.singletonRegistry.set(key, item);
|
|
}
|
|
}
|
|
unregisterSingletonWithKey(key) {
|
|
if (this.singletonRegistry.has(key)) {
|
|
this.singletonRegistry.delete(key);
|
|
}
|
|
}
|
|
getSingletonWithKey(key) {
|
|
return this.singletonRegistry.has(key) ? this.singletonRegistry.get(key).target : null;
|
|
}
|
|
withNewTarget(target) {
|
|
return {
|
|
target
|
|
};
|
|
}
|
|
static ɵfac = function NzSingletonService_Factory(__ngFactoryType__) {
|
|
return new (__ngFactoryType__ || _NzSingletonService)();
|
|
};
|
|
static ɵprov = ɵɵdefineInjectable({
|
|
token: _NzSingletonService,
|
|
factory: _NzSingletonService.ɵfac,
|
|
providedIn: "root"
|
|
});
|
|
};
|
|
(() => {
|
|
(typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(NzSingletonService, [{
|
|
type: Injectable,
|
|
args: [{
|
|
providedIn: "root"
|
|
}]
|
|
}], null, null);
|
|
})();
|
|
function getPagePosition(event) {
|
|
const e = getEventPosition(event);
|
|
return {
|
|
x: e.pageX,
|
|
y: e.pageY
|
|
};
|
|
}
|
|
var NzDragService = class _NzDragService {
|
|
draggingThreshold = 5;
|
|
currentDraggingSequence = null;
|
|
currentStartingPoint = null;
|
|
handleRegistry = /* @__PURE__ */ new Set();
|
|
renderer = inject(RendererFactory2).createRenderer(null, null);
|
|
requestDraggingSequence(event) {
|
|
if (!this.handleRegistry.size) {
|
|
this.registerDraggingHandler(isTouchEvent(event));
|
|
}
|
|
if (this.currentDraggingSequence) {
|
|
this.currentDraggingSequence.complete();
|
|
}
|
|
this.currentStartingPoint = getPagePosition(event);
|
|
this.currentDraggingSequence = new Subject();
|
|
return this.currentDraggingSequence.pipe(map((e) => ({
|
|
x: e.pageX - this.currentStartingPoint.x,
|
|
y: e.pageY - this.currentStartingPoint.y
|
|
})), filter((e) => Math.abs(e.x) > this.draggingThreshold || Math.abs(e.y) > this.draggingThreshold), finalize(() => this.teardownDraggingSequence()));
|
|
}
|
|
registerDraggingHandler(isTouch) {
|
|
if (isTouch) {
|
|
this.handleRegistry.add({
|
|
teardown: this.renderer.listen("document", "touchmove", (e) => {
|
|
if (this.currentDraggingSequence) {
|
|
this.currentDraggingSequence.next(e.touches[0] || e.changedTouches[0]);
|
|
}
|
|
})
|
|
});
|
|
this.handleRegistry.add({
|
|
teardown: this.renderer.listen("document", "touchend", () => {
|
|
if (this.currentDraggingSequence) {
|
|
this.currentDraggingSequence.complete();
|
|
}
|
|
})
|
|
});
|
|
} else {
|
|
this.handleRegistry.add({
|
|
teardown: this.renderer.listen("document", "mousemove", (e) => {
|
|
if (this.currentDraggingSequence) {
|
|
this.currentDraggingSequence.next(e);
|
|
}
|
|
})
|
|
});
|
|
this.handleRegistry.add({
|
|
teardown: this.renderer.listen("document", "mouseup", () => {
|
|
if (this.currentDraggingSequence) {
|
|
this.currentDraggingSequence.complete();
|
|
}
|
|
})
|
|
});
|
|
}
|
|
}
|
|
teardownDraggingSequence() {
|
|
this.currentDraggingSequence = null;
|
|
}
|
|
static ɵfac = function NzDragService_Factory(__ngFactoryType__) {
|
|
return new (__ngFactoryType__ || _NzDragService)();
|
|
};
|
|
static ɵprov = ɵɵdefineInjectable({
|
|
token: _NzDragService,
|
|
factory: _NzDragService.ɵfac,
|
|
providedIn: "root"
|
|
});
|
|
};
|
|
(() => {
|
|
(typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(NzDragService, [{
|
|
type: Injectable,
|
|
args: [{
|
|
providedIn: "root"
|
|
}]
|
|
}], null, null);
|
|
})();
|
|
function easeInOutCubic(t, b, c, d) {
|
|
const cc = c - b;
|
|
let tt = t / (d / 2);
|
|
if (tt < 1) {
|
|
return cc / 2 * tt * tt * tt + b;
|
|
} else {
|
|
return cc / 2 * ((tt -= 2) * tt * tt + 2) + b;
|
|
}
|
|
}
|
|
var NzScrollService = class _NzScrollService {
|
|
doc = inject(DOCUMENT);
|
|
ngZone = inject(NgZone);
|
|
/** Set the position of the scroll bar of `el`. */
|
|
setScrollTop(el, topValue = 0) {
|
|
if (el === window) {
|
|
this.doc.body.scrollTop = topValue;
|
|
this.doc.documentElement.scrollTop = topValue;
|
|
} else {
|
|
el.scrollTop = topValue;
|
|
}
|
|
}
|
|
/** Get position of `el` against window. */
|
|
getOffset(el) {
|
|
const ret = {
|
|
top: 0,
|
|
left: 0
|
|
};
|
|
if (!el || !el.getClientRects().length) {
|
|
return ret;
|
|
}
|
|
const rect = el.getBoundingClientRect();
|
|
if (rect.width || rect.height) {
|
|
const doc = el.ownerDocument.documentElement;
|
|
ret.top = rect.top - doc.clientTop;
|
|
ret.left = rect.left - doc.clientLeft;
|
|
} else {
|
|
ret.top = rect.top;
|
|
ret.left = rect.left;
|
|
}
|
|
return ret;
|
|
}
|
|
/** Get the position of the scoll bar of `el`. */
|
|
// TODO: remove '| Window' as the fallback already happens here
|
|
getScroll(target, top = true) {
|
|
if (typeof window === "undefined") {
|
|
return 0;
|
|
}
|
|
const method = top ? "scrollTop" : "scrollLeft";
|
|
let result = 0;
|
|
if (this.isWindow(target)) {
|
|
result = target[top ? "pageYOffset" : "pageXOffset"];
|
|
} else if (target instanceof Document) {
|
|
result = target.documentElement[method];
|
|
} else if (target) {
|
|
result = target[method];
|
|
}
|
|
if (target && !this.isWindow(target) && typeof result !== "number") {
|
|
result = (target.ownerDocument || target).documentElement[method];
|
|
}
|
|
return result;
|
|
}
|
|
isWindow(obj) {
|
|
return obj !== null && obj !== void 0 && obj === obj.window;
|
|
}
|
|
/**
|
|
* Scroll `el` to some position with animation.
|
|
*
|
|
* @param containerEl container, `window` by default
|
|
* @param y Scroll to `top`, 0 by default
|
|
* @param options Scroll animation options
|
|
*/
|
|
scrollTo(containerEl, y = 0, options = {}) {
|
|
const target = containerEl ? containerEl : window;
|
|
const scrollTop = this.getScroll(target);
|
|
const startTime = Date.now();
|
|
const {
|
|
easing,
|
|
callback,
|
|
duration = 450
|
|
} = options;
|
|
const frameFunc = () => {
|
|
const timestamp = Date.now();
|
|
const time = timestamp - startTime;
|
|
const nextScrollTop = (easing || easeInOutCubic)(time > duration ? duration : time, scrollTop, y, duration);
|
|
if (this.isWindow(target)) {
|
|
target.scrollTo(window.pageXOffset, nextScrollTop);
|
|
} else if (target instanceof HTMLDocument || target.constructor.name === "HTMLDocument") {
|
|
target.documentElement.scrollTop = nextScrollTop;
|
|
} else {
|
|
target.scrollTop = nextScrollTop;
|
|
}
|
|
if (time < duration) {
|
|
requestAnimationFrame2(frameFunc);
|
|
} else if (typeof callback === "function") {
|
|
this.ngZone.run(callback);
|
|
}
|
|
};
|
|
this.ngZone.runOutsideAngular(() => requestAnimationFrame2(frameFunc));
|
|
}
|
|
static ɵfac = function NzScrollService_Factory(__ngFactoryType__) {
|
|
return new (__ngFactoryType__ || _NzScrollService)();
|
|
};
|
|
static ɵprov = ɵɵdefineInjectable({
|
|
token: _NzScrollService,
|
|
factory: _NzScrollService.ɵfac,
|
|
providedIn: "root"
|
|
});
|
|
};
|
|
(() => {
|
|
(typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(NzScrollService, [{
|
|
type: Injectable,
|
|
args: [{
|
|
providedIn: "root"
|
|
}]
|
|
}], null, null);
|
|
})();
|
|
var NzBreakpointEnum;
|
|
(function(NzBreakpointEnum2) {
|
|
NzBreakpointEnum2["xxl"] = "xxl";
|
|
NzBreakpointEnum2["xl"] = "xl";
|
|
NzBreakpointEnum2["lg"] = "lg";
|
|
NzBreakpointEnum2["md"] = "md";
|
|
NzBreakpointEnum2["sm"] = "sm";
|
|
NzBreakpointEnum2["xs"] = "xs";
|
|
})(NzBreakpointEnum || (NzBreakpointEnum = {}));
|
|
var gridResponsiveMap = {
|
|
xs: "(max-width: 575px)",
|
|
sm: "(min-width: 576px)",
|
|
md: "(min-width: 768px)",
|
|
lg: "(min-width: 992px)",
|
|
xl: "(min-width: 1200px)",
|
|
xxl: "(min-width: 1600px)"
|
|
};
|
|
var NzBreakpointService = class _NzBreakpointService {
|
|
resizeService = inject(NzResizeService);
|
|
mediaMatcher = inject(MediaMatcher);
|
|
constructor() {
|
|
this.resizeService.connect().pipe(takeUntilDestroyed()).subscribe(() => {
|
|
});
|
|
}
|
|
subscribe(breakpointMap, fullMap) {
|
|
if (fullMap) {
|
|
const get = () => this.matchMedia(breakpointMap, true);
|
|
return this.resizeService.connect().pipe(map(get), startWith(get()), distinctUntilChanged((x, y) => x[0] === y[0]), map((x) => x[1]));
|
|
} else {
|
|
const get = () => this.matchMedia(breakpointMap);
|
|
return this.resizeService.connect().pipe(map(get), startWith(get()), distinctUntilChanged());
|
|
}
|
|
}
|
|
matchMedia(breakpointMap, fullMap) {
|
|
let bp = NzBreakpointEnum.md;
|
|
const breakpointBooleanMap = {};
|
|
Object.keys(breakpointMap).map((breakpoint) => {
|
|
const castBP = breakpoint;
|
|
const matched = this.mediaMatcher.matchMedia(gridResponsiveMap[castBP]).matches;
|
|
breakpointBooleanMap[breakpoint] = matched;
|
|
if (matched) {
|
|
bp = castBP;
|
|
}
|
|
});
|
|
if (fullMap) {
|
|
return [bp, breakpointBooleanMap];
|
|
} else {
|
|
return bp;
|
|
}
|
|
}
|
|
static ɵfac = function NzBreakpointService_Factory(__ngFactoryType__) {
|
|
return new (__ngFactoryType__ || _NzBreakpointService)();
|
|
};
|
|
static ɵprov = ɵɵdefineInjectable({
|
|
token: _NzBreakpointService,
|
|
factory: _NzBreakpointService.ɵfac,
|
|
providedIn: "root"
|
|
});
|
|
};
|
|
(() => {
|
|
(typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(NzBreakpointService, [{
|
|
type: Injectable,
|
|
args: [{
|
|
providedIn: "root"
|
|
}]
|
|
}], () => [], null);
|
|
})();
|
|
var NzDestroyService = class _NzDestroyService extends Subject {
|
|
ngOnDestroy() {
|
|
this.next();
|
|
this.complete();
|
|
}
|
|
static ɵfac = /* @__PURE__ */ (() => {
|
|
let ɵNzDestroyService_BaseFactory;
|
|
return function NzDestroyService_Factory(__ngFactoryType__) {
|
|
return (ɵNzDestroyService_BaseFactory || (ɵNzDestroyService_BaseFactory = ɵɵgetInheritedFactory(_NzDestroyService)))(__ngFactoryType__ || _NzDestroyService);
|
|
};
|
|
})();
|
|
static ɵprov = ɵɵdefineInjectable({
|
|
token: _NzDestroyService,
|
|
factory: _NzDestroyService.ɵfac
|
|
});
|
|
};
|
|
(() => {
|
|
(typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(NzDestroyService, [{
|
|
type: Injectable
|
|
}], null, null);
|
|
})();
|
|
var ImagePreloadService = class _ImagePreloadService {
|
|
counter = /* @__PURE__ */ new Map();
|
|
linkRefs = /* @__PURE__ */ new Map();
|
|
document = inject(DOCUMENT);
|
|
platform = inject(Platform);
|
|
addPreload(option) {
|
|
if (this.platform.isBrowser) {
|
|
return () => void 0;
|
|
}
|
|
const uniqueKey = `${option.src}${option.srcset}`;
|
|
let currentCount = this.counter.get(uniqueKey) || 0;
|
|
currentCount++;
|
|
this.counter.set(uniqueKey, currentCount);
|
|
if (!this.linkRefs.has(uniqueKey)) {
|
|
const linkNode = this.appendPreloadLink(option);
|
|
this.linkRefs.set(uniqueKey, linkNode);
|
|
}
|
|
return () => {
|
|
if (this.counter.has(uniqueKey)) {
|
|
let count = this.counter.get(uniqueKey);
|
|
count--;
|
|
if (count === 0) {
|
|
const linkNode = this.linkRefs.get(uniqueKey);
|
|
this.removePreloadLink(linkNode);
|
|
this.counter.delete(uniqueKey);
|
|
this.linkRefs.delete(uniqueKey);
|
|
} else {
|
|
this.counter.set(uniqueKey, count);
|
|
}
|
|
}
|
|
};
|
|
}
|
|
appendPreloadLink(option) {
|
|
const linkNode = this.document.createElement("link");
|
|
linkNode.setAttribute("rel", "preload");
|
|
linkNode.setAttribute("as", "image");
|
|
linkNode.setAttribute("href", option.src);
|
|
if (option.srcset) {
|
|
linkNode.setAttribute("imagesrcset", option.srcset);
|
|
}
|
|
this.document.head.appendChild(linkNode);
|
|
return linkNode;
|
|
}
|
|
removePreloadLink(linkNode) {
|
|
if (this.document.head.contains(linkNode)) {
|
|
this.document.head.removeChild(linkNode);
|
|
}
|
|
}
|
|
static ɵfac = function ImagePreloadService_Factory(__ngFactoryType__) {
|
|
return new (__ngFactoryType__ || _ImagePreloadService)();
|
|
};
|
|
static ɵprov = ɵɵdefineInjectable({
|
|
token: _ImagePreloadService,
|
|
factory: _ImagePreloadService.ɵfac,
|
|
providedIn: "root"
|
|
});
|
|
};
|
|
(() => {
|
|
(typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(ImagePreloadService, [{
|
|
type: Injectable,
|
|
args: [{
|
|
providedIn: "root"
|
|
}]
|
|
}], null, null);
|
|
})();
|
|
|
|
// node_modules/@angular/animations/fesm2022/animations.mjs
|
|
var AnimationBuilder = class _AnimationBuilder {
|
|
static ɵfac = function AnimationBuilder_Factory(__ngFactoryType__) {
|
|
return new (__ngFactoryType__ || _AnimationBuilder)();
|
|
};
|
|
static ɵprov = ɵɵdefineInjectable({
|
|
token: _AnimationBuilder,
|
|
factory: () => (() => inject(BrowserAnimationBuilder))(),
|
|
providedIn: "root"
|
|
});
|
|
};
|
|
(() => {
|
|
(typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(AnimationBuilder, [{
|
|
type: Injectable,
|
|
args: [{
|
|
providedIn: "root",
|
|
useFactory: () => inject(BrowserAnimationBuilder)
|
|
}]
|
|
}], null, null);
|
|
})();
|
|
var AnimationFactory = class {
|
|
};
|
|
var BrowserAnimationBuilder = class _BrowserAnimationBuilder extends AnimationBuilder {
|
|
animationModuleType = inject(ANIMATION_MODULE_TYPE, {
|
|
optional: true
|
|
});
|
|
_nextAnimationId = 0;
|
|
_renderer;
|
|
constructor(rootRenderer, doc) {
|
|
super();
|
|
const typeData = {
|
|
id: "0",
|
|
encapsulation: ViewEncapsulation.None,
|
|
styles: [],
|
|
data: {
|
|
animation: []
|
|
}
|
|
};
|
|
this._renderer = rootRenderer.createRenderer(doc.body, typeData);
|
|
if (this.animationModuleType === null && !isAnimationRenderer(this._renderer)) {
|
|
throw new RuntimeError(3600, (typeof ngDevMode === "undefined" || ngDevMode) && "Angular detected that the `AnimationBuilder` was injected, but animation support was not enabled. Please make sure that you enable animations in your application by calling `provideAnimations()` or `provideAnimationsAsync()` function.");
|
|
}
|
|
}
|
|
build(animation2) {
|
|
const id = this._nextAnimationId;
|
|
this._nextAnimationId++;
|
|
const entry = Array.isArray(animation2) ? sequence(animation2) : animation2;
|
|
issueAnimationCommand(this._renderer, null, id, "register", [entry]);
|
|
return new BrowserAnimationFactory(id, this._renderer);
|
|
}
|
|
static ɵfac = function BrowserAnimationBuilder_Factory(__ngFactoryType__) {
|
|
return new (__ngFactoryType__ || _BrowserAnimationBuilder)(ɵɵinject(RendererFactory2), ɵɵinject(DOCUMENT));
|
|
};
|
|
static ɵprov = ɵɵdefineInjectable({
|
|
token: _BrowserAnimationBuilder,
|
|
factory: _BrowserAnimationBuilder.ɵfac,
|
|
providedIn: "root"
|
|
});
|
|
};
|
|
(() => {
|
|
(typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(BrowserAnimationBuilder, [{
|
|
type: Injectable,
|
|
args: [{
|
|
providedIn: "root"
|
|
}]
|
|
}], () => [{
|
|
type: RendererFactory2
|
|
}, {
|
|
type: Document,
|
|
decorators: [{
|
|
type: Inject,
|
|
args: [DOCUMENT]
|
|
}]
|
|
}], null);
|
|
})();
|
|
var BrowserAnimationFactory = class extends AnimationFactory {
|
|
_id;
|
|
_renderer;
|
|
constructor(_id, _renderer) {
|
|
super();
|
|
this._id = _id;
|
|
this._renderer = _renderer;
|
|
}
|
|
create(element, options) {
|
|
return new RendererAnimationPlayer(this._id, element, options || {}, this._renderer);
|
|
}
|
|
};
|
|
var RendererAnimationPlayer = class {
|
|
id;
|
|
element;
|
|
_renderer;
|
|
parentPlayer = null;
|
|
_started = false;
|
|
constructor(id, element, options, _renderer) {
|
|
this.id = id;
|
|
this.element = element;
|
|
this._renderer = _renderer;
|
|
this._command("create", options);
|
|
}
|
|
_listen(eventName, callback) {
|
|
return this._renderer.listen(this.element, `@@${this.id}:${eventName}`, callback);
|
|
}
|
|
_command(command, ...args) {
|
|
issueAnimationCommand(this._renderer, this.element, this.id, command, args);
|
|
}
|
|
onDone(fn) {
|
|
this._listen("done", fn);
|
|
}
|
|
onStart(fn) {
|
|
this._listen("start", fn);
|
|
}
|
|
onDestroy(fn) {
|
|
this._listen("destroy", fn);
|
|
}
|
|
init() {
|
|
this._command("init");
|
|
}
|
|
hasStarted() {
|
|
return this._started;
|
|
}
|
|
play() {
|
|
this._command("play");
|
|
this._started = true;
|
|
}
|
|
pause() {
|
|
this._command("pause");
|
|
}
|
|
restart() {
|
|
this._command("restart");
|
|
}
|
|
finish() {
|
|
this._command("finish");
|
|
}
|
|
destroy() {
|
|
this._command("destroy");
|
|
}
|
|
reset() {
|
|
this._command("reset");
|
|
this._started = false;
|
|
}
|
|
setPosition(p) {
|
|
this._command("setPosition", p);
|
|
}
|
|
getPosition() {
|
|
return unwrapAnimationRenderer(this._renderer)?.engine?.players[this.id]?.getPosition() ?? 0;
|
|
}
|
|
totalTime = 0;
|
|
};
|
|
function issueAnimationCommand(renderer, element, id, command, args) {
|
|
renderer.setProperty(element, `@@${id}:${command}`, args);
|
|
}
|
|
function unwrapAnimationRenderer(renderer) {
|
|
const type = renderer.ɵtype;
|
|
if (type === 0) {
|
|
return renderer;
|
|
} else if (type === 1) {
|
|
return renderer.animationRenderer;
|
|
}
|
|
return null;
|
|
}
|
|
function isAnimationRenderer(renderer) {
|
|
const type = renderer.ɵtype;
|
|
return type === 0 || type === 1;
|
|
}
|
|
|
|
// node_modules/ng-zorro-antd/fesm2022/ng-zorro-antd-core-animation.mjs
|
|
var AnimationDuration = class {
|
|
static SLOW = "0.3s";
|
|
// Modal
|
|
static BASE = "0.2s";
|
|
static FAST = "0.1s";
|
|
// Tooltip
|
|
};
|
|
var AnimationCurves = class {
|
|
static EASE_BASE_OUT = "cubic-bezier(0.7, 0.3, 0.1, 1)";
|
|
static EASE_BASE_IN = "cubic-bezier(0.9, 0, 0.3, 0.7)";
|
|
static EASE_OUT = "cubic-bezier(0.215, 0.61, 0.355, 1)";
|
|
static EASE_IN = "cubic-bezier(0.55, 0.055, 0.675, 0.19)";
|
|
static EASE_IN_OUT = "cubic-bezier(0.645, 0.045, 0.355, 1)";
|
|
static EASE_OUT_BACK = "cubic-bezier(0.12, 0.4, 0.29, 1.46)";
|
|
static EASE_IN_BACK = "cubic-bezier(0.71, -0.46, 0.88, 0.6)";
|
|
static EASE_IN_OUT_BACK = "cubic-bezier(0.71, -0.46, 0.29, 1.46)";
|
|
static EASE_OUT_CIRC = "cubic-bezier(0.08, 0.82, 0.17, 1)";
|
|
static EASE_IN_CIRC = "cubic-bezier(0.6, 0.04, 0.98, 0.34)";
|
|
static EASE_IN_OUT_CIRC = "cubic-bezier(0.78, 0.14, 0.15, 0.86)";
|
|
static EASE_OUT_QUINT = "cubic-bezier(0.23, 1, 0.32, 1)";
|
|
static EASE_IN_QUINT = "cubic-bezier(0.755, 0.05, 0.855, 0.06)";
|
|
static EASE_IN_OUT_QUINT = "cubic-bezier(0.86, 0, 0.07, 1)";
|
|
};
|
|
var collapseMotion = trigger("collapseMotion", [
|
|
state("expanded", style({ height: "*" })),
|
|
state("collapsed", style({ height: 0, overflow: "hidden" })),
|
|
state("hidden", style({ height: 0, overflow: "hidden", borderTopWidth: "0" })),
|
|
transition("expanded => collapsed", animate(`150ms ${AnimationCurves.EASE_IN_OUT}`)),
|
|
transition("expanded => hidden", animate(`150ms ${AnimationCurves.EASE_IN_OUT}`)),
|
|
transition("collapsed => expanded", animate(`150ms ${AnimationCurves.EASE_IN_OUT}`)),
|
|
transition("hidden => expanded", animate(`150ms ${AnimationCurves.EASE_IN_OUT}`))
|
|
]);
|
|
var treeCollapseMotion = trigger("treeCollapseMotion", [
|
|
transition("* => *", [
|
|
query("nz-tree-node:leave,nz-tree-builtin-node:leave", [
|
|
style({ overflow: "hidden" }),
|
|
stagger(0, [
|
|
animate(`150ms ${AnimationCurves.EASE_IN_OUT}`, style({ height: 0, opacity: 0, "padding-bottom": 0 }))
|
|
])
|
|
], {
|
|
optional: true
|
|
}),
|
|
query("nz-tree-node:enter,nz-tree-builtin-node:enter", [
|
|
style({ overflow: "hidden", height: 0, opacity: 0, "padding-bottom": 0 }),
|
|
stagger(0, [
|
|
animate(`150ms ${AnimationCurves.EASE_IN_OUT}`, style({ overflow: "hidden", height: "*", opacity: "*", "padding-bottom": "*" }))
|
|
])
|
|
], {
|
|
optional: true
|
|
})
|
|
])
|
|
]);
|
|
var drawerMaskMotion = trigger("drawerMaskMotion", [
|
|
transition(":enter", [style({ opacity: 0 }), animate(`${AnimationDuration.SLOW}`, style({ opacity: 1 }))]),
|
|
transition(":leave", [style({ opacity: 1 }), animate(`${AnimationDuration.SLOW}`, style({ opacity: 0 }))])
|
|
]);
|
|
var fadeMotion = trigger("fadeMotion", [
|
|
transition("* => enter", [style({ opacity: 0 }), animate(`${AnimationDuration.BASE}`, style({ opacity: 1 }))]),
|
|
transition("* => leave, :leave", [style({ opacity: 1 }), animate(`${AnimationDuration.BASE}`, style({ opacity: 0 }))])
|
|
]);
|
|
var helpMotion = trigger("helpMotion", [
|
|
transition(":enter", [
|
|
style({
|
|
opacity: 0,
|
|
transform: "translateY(-5px)"
|
|
}),
|
|
animate(`${AnimationDuration.SLOW} ${AnimationCurves.EASE_IN_OUT}`, style({
|
|
opacity: 1,
|
|
transform: "translateY(0)"
|
|
}))
|
|
]),
|
|
transition(":leave", [
|
|
style({
|
|
opacity: 1,
|
|
transform: "translateY(0)"
|
|
}),
|
|
animate(`${AnimationDuration.SLOW} ${AnimationCurves.EASE_IN_OUT}`, style({
|
|
opacity: 0,
|
|
transform: "translateY(-5px)"
|
|
}))
|
|
])
|
|
]);
|
|
var moveUpMotion = trigger("moveUpMotion", [
|
|
transition("* => enter", [
|
|
style({
|
|
transformOrigin: "0 0",
|
|
transform: "translateY(-100%)",
|
|
opacity: 0
|
|
}),
|
|
animate(`${AnimationDuration.BASE}`, style({
|
|
transformOrigin: "0 0",
|
|
transform: "translateY(0%)",
|
|
opacity: 1
|
|
}))
|
|
]),
|
|
transition("* => leave", [
|
|
style({
|
|
transformOrigin: "0 0",
|
|
transform: "translateY(0%)",
|
|
opacity: 1
|
|
}),
|
|
animate(`${AnimationDuration.BASE}`, style({
|
|
transformOrigin: "0 0",
|
|
transform: "translateY(-100%)",
|
|
opacity: 0
|
|
}))
|
|
])
|
|
]);
|
|
var notificationMotion = trigger("notificationMotion", [
|
|
state("enterRight", style({ opacity: 1, transform: "translateX(0)" })),
|
|
transition("* => enterRight", [style({ opacity: 0, transform: "translateX(5%)" }), animate("100ms linear")]),
|
|
state("enterLeft", style({ opacity: 1, transform: "translateX(0)" })),
|
|
transition("* => enterLeft", [style({ opacity: 0, transform: "translateX(-5%)" }), animate("100ms linear")]),
|
|
state("enterTop", style({ opacity: 1, transform: "translateY(0)" })),
|
|
transition("* => enterTop", [style({ opacity: 0, transform: "translateY(-5%)" }), animate("100ms linear")]),
|
|
state("enterBottom", style({ opacity: 1, transform: "translateY(0)" })),
|
|
transition("* => enterBottom", [style({ opacity: 0, transform: "translateY(5%)" }), animate("100ms linear")]),
|
|
state("leave", style({
|
|
opacity: 0,
|
|
transform: "scaleY(0.8)",
|
|
transformOrigin: "0% 0%"
|
|
})),
|
|
transition("* => leave", [
|
|
style({
|
|
opacity: 1,
|
|
transform: "scaleY(1)",
|
|
transformOrigin: "0% 0%"
|
|
}),
|
|
animate("100ms linear")
|
|
])
|
|
]);
|
|
var ANIMATION_TRANSITION_IN = `${AnimationDuration.BASE} ${AnimationCurves.EASE_OUT_QUINT}`;
|
|
var ANIMATION_TRANSITION_OUT = `${AnimationDuration.BASE} ${AnimationCurves.EASE_IN_QUINT}`;
|
|
var slideMotion = trigger("slideMotion", [
|
|
state("void", style({
|
|
opacity: 0,
|
|
transform: "scaleY(0.8)"
|
|
})),
|
|
state("enter", style({
|
|
opacity: 1,
|
|
transform: "scaleY(1)"
|
|
})),
|
|
transition("void => *", [animate(ANIMATION_TRANSITION_IN)]),
|
|
transition("* => void", [animate(ANIMATION_TRANSITION_OUT)])
|
|
]);
|
|
var slideAlertMotion = trigger("slideAlertMotion", [
|
|
transition(":leave", [
|
|
style({ opacity: 1, transform: "scaleY(1)", transformOrigin: "0% 0%" }),
|
|
animate(`${AnimationDuration.SLOW} ${AnimationCurves.EASE_IN_OUT_CIRC}`, style({
|
|
opacity: 0,
|
|
transform: "scaleY(0)",
|
|
transformOrigin: "0% 0%"
|
|
}))
|
|
])
|
|
]);
|
|
var tabSwitchMotion = trigger("tabSwitchMotion", [
|
|
state("leave", style({
|
|
display: "none"
|
|
})),
|
|
transition("* => enter", [
|
|
style({
|
|
display: "block",
|
|
opacity: 0
|
|
}),
|
|
animate(AnimationDuration.SLOW)
|
|
]),
|
|
transition("* => leave, :leave", [
|
|
style({
|
|
position: "absolute",
|
|
top: 0,
|
|
left: 0,
|
|
width: "100%"
|
|
}),
|
|
animate(AnimationDuration.SLOW, style({
|
|
opacity: 0
|
|
})),
|
|
style({
|
|
display: "none"
|
|
})
|
|
])
|
|
]);
|
|
var thumbMotion = trigger("thumbMotion", [
|
|
state("from", style({ transform: "translateX({{ transform }}px)", width: "{{ width }}px" }), {
|
|
params: { transform: 0, width: 0 }
|
|
}),
|
|
state("to", style({ transform: "translateX({{ transform }}px)", width: "{{ width }}px" }), {
|
|
params: { transform: 100, width: 0 }
|
|
}),
|
|
state("fromVertical", style({ transform: "translateY({{ transform }}px)", width: "{{ width }}px", height: "{{ height }}px" }), {
|
|
params: { transform: 0, width: 0, height: 0 }
|
|
}),
|
|
state("toVertical", style({ transform: "translateY({{ transform }}px)", width: "{{ width }}px", height: "{{ height }}px" }), {
|
|
params: { transform: 100, width: 0, height: 0 }
|
|
}),
|
|
transition("from => to", animate(`300ms ${AnimationCurves.EASE_IN_OUT}`)),
|
|
transition("fromVertical => toVertical", animate(`300ms ${AnimationCurves.EASE_IN_OUT}`))
|
|
]);
|
|
var zoomBigMotion = trigger("zoomBigMotion", [
|
|
transition("void => active", [
|
|
style({ opacity: 0, transform: "scale(0.8)" }),
|
|
animate(`${AnimationDuration.BASE} ${AnimationCurves.EASE_OUT_CIRC}`, style({
|
|
opacity: 1,
|
|
transform: "scale(1)"
|
|
}))
|
|
]),
|
|
transition("active => void", [
|
|
style({ opacity: 1, transform: "scale(1)" }),
|
|
animate(`${AnimationDuration.BASE} ${AnimationCurves.EASE_IN_OUT_CIRC}`, style({
|
|
opacity: 0,
|
|
transform: "scale(0.8)"
|
|
}))
|
|
])
|
|
]);
|
|
var zoomBadgeMotion = trigger("zoomBadgeMotion", [
|
|
transition(":enter", [
|
|
style({ opacity: 0, transform: "scale(0) translate(50%, -50%)" }),
|
|
animate(`${AnimationDuration.SLOW} ${AnimationCurves.EASE_OUT_BACK}`, style({
|
|
opacity: 1,
|
|
transform: "scale(1) translate(50%, -50%)"
|
|
}))
|
|
]),
|
|
transition(":leave", [
|
|
style({ opacity: 1, transform: "scale(1) translate(50%, -50%)" }),
|
|
animate(`${AnimationDuration.SLOW} ${AnimationCurves.EASE_IN_BACK}`, style({
|
|
opacity: 0,
|
|
transform: "scale(0) translate(50%, -50%)"
|
|
}))
|
|
])
|
|
]);
|
|
|
|
// node_modules/ng-zorro-antd/fesm2022/ng-zorro-antd-core-outlet.mjs
|
|
var NzStringTemplateOutletDirective = class _NzStringTemplateOutletDirective {
|
|
viewContainer = inject(ViewContainerRef);
|
|
templateRef = inject(TemplateRef);
|
|
embeddedViewRef = null;
|
|
context = new NzStringTemplateOutletContext();
|
|
nzStringTemplateOutletContext = null;
|
|
nzStringTemplateOutlet = null;
|
|
static ngTemplateContextGuard(_dir, _ctx) {
|
|
return true;
|
|
}
|
|
recreateView() {
|
|
this.viewContainer.clear();
|
|
if (isTemplateRef(this.nzStringTemplateOutlet)) {
|
|
this.embeddedViewRef = this.viewContainer.createEmbeddedView(this.nzStringTemplateOutlet, this.nzStringTemplateOutletContext);
|
|
} else {
|
|
this.embeddedViewRef = this.viewContainer.createEmbeddedView(this.templateRef, this.context);
|
|
}
|
|
}
|
|
updateContext() {
|
|
const newCtx = isTemplateRef(this.nzStringTemplateOutlet) ? this.nzStringTemplateOutletContext : this.context;
|
|
const oldCtx = this.embeddedViewRef.context;
|
|
if (newCtx) {
|
|
for (const propName of Object.keys(newCtx)) {
|
|
oldCtx[propName] = newCtx[propName];
|
|
}
|
|
}
|
|
}
|
|
ngOnChanges(changes) {
|
|
const {
|
|
nzStringTemplateOutletContext,
|
|
nzStringTemplateOutlet
|
|
} = changes;
|
|
const shouldRecreateView = () => {
|
|
let shouldOutletRecreate = false;
|
|
if (nzStringTemplateOutlet) {
|
|
shouldOutletRecreate = nzStringTemplateOutlet.firstChange || isTemplateRef(nzStringTemplateOutlet.previousValue) || isTemplateRef(nzStringTemplateOutlet.currentValue);
|
|
}
|
|
const hasContextShapeChanged = (ctxChange) => {
|
|
const prevCtxKeys = Object.keys(ctxChange.previousValue || {});
|
|
const currCtxKeys = Object.keys(ctxChange.currentValue || {});
|
|
if (prevCtxKeys.length === currCtxKeys.length) {
|
|
for (const propName of currCtxKeys) {
|
|
if (prevCtxKeys.indexOf(propName) === -1) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
} else {
|
|
return true;
|
|
}
|
|
};
|
|
const shouldContextRecreate = nzStringTemplateOutletContext && hasContextShapeChanged(nzStringTemplateOutletContext);
|
|
return shouldContextRecreate || shouldOutletRecreate;
|
|
};
|
|
if (nzStringTemplateOutlet) {
|
|
this.context.$implicit = nzStringTemplateOutlet.currentValue;
|
|
}
|
|
const recreateView = shouldRecreateView();
|
|
if (recreateView) {
|
|
this.recreateView();
|
|
} else {
|
|
this.updateContext();
|
|
}
|
|
}
|
|
static ɵfac = function NzStringTemplateOutletDirective_Factory(__ngFactoryType__) {
|
|
return new (__ngFactoryType__ || _NzStringTemplateOutletDirective)();
|
|
};
|
|
static ɵdir = ɵɵdefineDirective({
|
|
type: _NzStringTemplateOutletDirective,
|
|
selectors: [["", "nzStringTemplateOutlet", ""]],
|
|
inputs: {
|
|
nzStringTemplateOutletContext: "nzStringTemplateOutletContext",
|
|
nzStringTemplateOutlet: "nzStringTemplateOutlet"
|
|
},
|
|
exportAs: ["nzStringTemplateOutlet"],
|
|
features: [ɵɵNgOnChangesFeature]
|
|
});
|
|
};
|
|
(() => {
|
|
(typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(NzStringTemplateOutletDirective, [{
|
|
type: Directive,
|
|
args: [{
|
|
selector: "[nzStringTemplateOutlet]",
|
|
exportAs: "nzStringTemplateOutlet"
|
|
}]
|
|
}], null, {
|
|
nzStringTemplateOutletContext: [{
|
|
type: Input
|
|
}],
|
|
nzStringTemplateOutlet: [{
|
|
type: Input
|
|
}]
|
|
});
|
|
})();
|
|
var NzStringTemplateOutletContext = class {
|
|
$implicit;
|
|
};
|
|
var NzOutletModule = class _NzOutletModule {
|
|
static ɵfac = function NzOutletModule_Factory(__ngFactoryType__) {
|
|
return new (__ngFactoryType__ || _NzOutletModule)();
|
|
};
|
|
static ɵmod = ɵɵdefineNgModule({
|
|
type: _NzOutletModule,
|
|
imports: [NzStringTemplateOutletDirective],
|
|
exports: [NzStringTemplateOutletDirective]
|
|
});
|
|
static ɵinj = ɵɵdefineInjector({});
|
|
};
|
|
(() => {
|
|
(typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(NzOutletModule, [{
|
|
type: NgModule,
|
|
args: [{
|
|
imports: [NzStringTemplateOutletDirective],
|
|
exports: [NzStringTemplateOutletDirective]
|
|
}]
|
|
}], null, null);
|
|
})();
|
|
|
|
// node_modules/ng-zorro-antd/fesm2022/ng-zorro-antd-message.mjs
|
|
var _c02 = (a0, a1) => ({
|
|
$implicit: a0,
|
|
data: a1
|
|
});
|
|
function NzMessageComponent_Case_3_Template(rf, ctx) {
|
|
if (rf & 1) {
|
|
ɵɵelement(0, "nz-icon", 3);
|
|
}
|
|
}
|
|
function NzMessageComponent_Case_4_Template(rf, ctx) {
|
|
if (rf & 1) {
|
|
ɵɵelement(0, "nz-icon", 4);
|
|
}
|
|
}
|
|
function NzMessageComponent_Case_5_Template(rf, ctx) {
|
|
if (rf & 1) {
|
|
ɵɵelement(0, "nz-icon", 5);
|
|
}
|
|
}
|
|
function NzMessageComponent_Case_6_Template(rf, ctx) {
|
|
if (rf & 1) {
|
|
ɵɵelement(0, "nz-icon", 6);
|
|
}
|
|
}
|
|
function NzMessageComponent_Case_7_Template(rf, ctx) {
|
|
if (rf & 1) {
|
|
ɵɵelement(0, "nz-icon", 7);
|
|
}
|
|
}
|
|
function NzMessageComponent_ng_container_8_Template(rf, ctx) {
|
|
if (rf & 1) {
|
|
ɵɵelementContainerStart(0);
|
|
ɵɵelement(1, "span", 9);
|
|
ɵɵelementContainerEnd();
|
|
}
|
|
if (rf & 2) {
|
|
const ctx_r0 = ɵɵnextContext();
|
|
ɵɵadvance();
|
|
ɵɵproperty("innerHTML", ctx_r0.instance.content, ɵɵsanitizeHtml);
|
|
}
|
|
}
|
|
function NzMessageContainerComponent_For_2_Template(rf, ctx) {
|
|
if (rf & 1) {
|
|
const _r1 = ɵɵgetCurrentView();
|
|
ɵɵelementStart(0, "nz-message", 2);
|
|
ɵɵlistener("destroyed", function NzMessageContainerComponent_For_2_Template_nz_message_destroyed_0_listener($event) {
|
|
ɵɵrestoreView(_r1);
|
|
const ctx_r1 = ɵɵnextContext();
|
|
return ɵɵresetView(ctx_r1.remove($event.id, $event.userAction));
|
|
});
|
|
ɵɵelementEnd();
|
|
}
|
|
if (rf & 2) {
|
|
const instance_r3 = ctx.$implicit;
|
|
ɵɵproperty("instance", instance_r3);
|
|
}
|
|
}
|
|
var globalCounter = 0;
|
|
var NzMNService = class {
|
|
container;
|
|
nzSingletonService = inject(NzSingletonService);
|
|
injector = inject(Injector);
|
|
remove(id) {
|
|
if (this.container) {
|
|
if (id) {
|
|
this.container.remove(id);
|
|
} else {
|
|
this.container.removeAll();
|
|
}
|
|
}
|
|
}
|
|
getInstanceId() {
|
|
return `${this.componentPrefix}-${globalCounter++}`;
|
|
}
|
|
withContainer(ctor) {
|
|
let containerInstance = this.nzSingletonService.getSingletonWithKey(this.componentPrefix);
|
|
if (containerInstance) {
|
|
return containerInstance;
|
|
}
|
|
const overlayRef = createOverlayRef(this.injector, {
|
|
hasBackdrop: false,
|
|
scrollStrategy: createNoopScrollStrategy(),
|
|
positionStrategy: createGlobalPositionStrategy(this.injector)
|
|
});
|
|
const componentPortal = new ComponentPortal(ctor, null, this.injector);
|
|
const componentRef = overlayRef.attach(componentPortal);
|
|
const overlayWrapper = overlayRef.hostElement;
|
|
overlayWrapper.style.zIndex = "1010";
|
|
if (!containerInstance) {
|
|
this.container = containerInstance = componentRef.instance;
|
|
this.nzSingletonService.registerSingletonWithKey(this.componentPrefix, containerInstance);
|
|
this.container.afterAllInstancesRemoved.subscribe(() => {
|
|
this.container = void 0;
|
|
this.nzSingletonService.unregisterSingletonWithKey(this.componentPrefix);
|
|
overlayRef.dispose();
|
|
});
|
|
}
|
|
return containerInstance;
|
|
}
|
|
};
|
|
var NzMNContainerComponent = class _NzMNContainerComponent {
|
|
config;
|
|
instances = [];
|
|
_afterAllInstancesRemoved = new Subject();
|
|
afterAllInstancesRemoved = this._afterAllInstancesRemoved.asObservable();
|
|
cdr = inject(ChangeDetectorRef);
|
|
nzConfigService = inject(NzConfigService);
|
|
constructor() {
|
|
this.subscribeConfigChange();
|
|
}
|
|
create(data) {
|
|
const instance = this.onCreate(data);
|
|
if (this.instances.length >= this.config.nzMaxStack) {
|
|
this.instances = this.instances.slice(1);
|
|
}
|
|
this.instances = [...this.instances, instance];
|
|
this.readyInstances();
|
|
return instance;
|
|
}
|
|
remove(id, userAction = false) {
|
|
this.instances.map((instance, index) => ({
|
|
index,
|
|
instance
|
|
})).filter(({
|
|
instance
|
|
}) => instance.messageId === id).forEach(({
|
|
index,
|
|
instance
|
|
}) => {
|
|
this.instances.splice(index, 1);
|
|
this.instances = [...this.instances];
|
|
this.onRemove(instance, userAction);
|
|
this.readyInstances();
|
|
});
|
|
if (!this.instances.length) {
|
|
this.onAllInstancesRemoved();
|
|
}
|
|
}
|
|
removeAll() {
|
|
this.instances.forEach((i) => this.onRemove(i, false));
|
|
this.instances = [];
|
|
this.readyInstances();
|
|
this.onAllInstancesRemoved();
|
|
}
|
|
onCreate(instance) {
|
|
instance.options = this.mergeOptions(instance.options);
|
|
instance.onClose = new Subject();
|
|
return instance;
|
|
}
|
|
onRemove(instance, userAction) {
|
|
instance.onClose.next(userAction);
|
|
instance.onClose.complete();
|
|
}
|
|
onAllInstancesRemoved() {
|
|
this._afterAllInstancesRemoved.next();
|
|
this._afterAllInstancesRemoved.complete();
|
|
}
|
|
readyInstances() {
|
|
this.cdr.detectChanges();
|
|
}
|
|
mergeOptions(options) {
|
|
const {
|
|
nzDuration,
|
|
nzAnimate,
|
|
nzPauseOnHover
|
|
} = this.config;
|
|
return __spreadValues({
|
|
nzDuration,
|
|
nzAnimate,
|
|
nzPauseOnHover
|
|
}, options);
|
|
}
|
|
static ɵfac = function NzMNContainerComponent_Factory(__ngFactoryType__) {
|
|
return new (__ngFactoryType__ || _NzMNContainerComponent)();
|
|
};
|
|
static ɵdir = ɵɵdefineDirective({
|
|
type: _NzMNContainerComponent
|
|
});
|
|
};
|
|
(() => {
|
|
(typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(NzMNContainerComponent, [{
|
|
type: Directive
|
|
}], () => [], null);
|
|
})();
|
|
var NzMNComponent = class _NzMNComponent {
|
|
cdr = inject(ChangeDetectorRef);
|
|
destroyRef = inject(DestroyRef);
|
|
animationStateChanged = new Subject();
|
|
options;
|
|
autoClose;
|
|
closeTimer;
|
|
userAction = false;
|
|
eraseTimer;
|
|
eraseTimingStart;
|
|
eraseTTL;
|
|
constructor() {
|
|
this.destroyRef.onDestroy(() => {
|
|
if (this.autoClose) {
|
|
this.clearEraseTimeout();
|
|
}
|
|
this.animationStateChanged.complete();
|
|
});
|
|
}
|
|
ngOnInit() {
|
|
this.options = this.instance.options;
|
|
if (this.options.nzAnimate) {
|
|
this.instance.state = "enter";
|
|
this.animationStateChanged.pipe(filter((event) => event.phaseName === "done" && event.toState === "leave"), take(1)).subscribe(() => {
|
|
clearTimeout(this.closeTimer);
|
|
this.destroyed.next({
|
|
id: this.instance.messageId,
|
|
userAction: this.userAction
|
|
});
|
|
});
|
|
}
|
|
this.autoClose = this.options.nzDuration > 0;
|
|
if (this.autoClose) {
|
|
this.initErase();
|
|
this.startEraseTimeout();
|
|
}
|
|
}
|
|
onEnter() {
|
|
if (this.autoClose && this.options.nzPauseOnHover) {
|
|
this.clearEraseTimeout();
|
|
this.updateTTL();
|
|
}
|
|
}
|
|
onLeave() {
|
|
if (this.autoClose && this.options.nzPauseOnHover) {
|
|
this.startEraseTimeout();
|
|
}
|
|
}
|
|
destroy(userAction = false) {
|
|
this.userAction = userAction;
|
|
if (this.options.nzAnimate) {
|
|
this.instance.state = "leave";
|
|
this.cdr.detectChanges();
|
|
this.closeTimer = setTimeout(() => {
|
|
this.closeTimer = void 0;
|
|
this.destroyed.next({
|
|
id: this.instance.messageId,
|
|
userAction
|
|
});
|
|
}, 200);
|
|
} else {
|
|
this.destroyed.next({
|
|
id: this.instance.messageId,
|
|
userAction
|
|
});
|
|
}
|
|
}
|
|
initErase() {
|
|
this.eraseTTL = this.options.nzDuration;
|
|
this.eraseTimingStart = Date.now();
|
|
}
|
|
updateTTL() {
|
|
if (this.autoClose) {
|
|
this.eraseTTL -= Date.now() - this.eraseTimingStart;
|
|
}
|
|
}
|
|
startEraseTimeout() {
|
|
if (this.eraseTTL > 0) {
|
|
this.clearEraseTimeout();
|
|
this.eraseTimer = setTimeout(() => this.destroy(), this.eraseTTL);
|
|
this.eraseTimingStart = Date.now();
|
|
} else {
|
|
this.destroy();
|
|
}
|
|
}
|
|
clearEraseTimeout() {
|
|
if (this.eraseTimer !== null) {
|
|
clearTimeout(this.eraseTimer);
|
|
this.eraseTimer = void 0;
|
|
}
|
|
}
|
|
static ɵfac = function NzMNComponent_Factory(__ngFactoryType__) {
|
|
return new (__ngFactoryType__ || _NzMNComponent)();
|
|
};
|
|
static ɵdir = ɵɵdefineDirective({
|
|
type: _NzMNComponent
|
|
});
|
|
};
|
|
(() => {
|
|
(typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(NzMNComponent, [{
|
|
type: Directive
|
|
}], () => [], null);
|
|
})();
|
|
var NzMessageComponent = class _NzMessageComponent extends NzMNComponent {
|
|
instance;
|
|
destroyed = new EventEmitter();
|
|
index;
|
|
static ɵfac = /* @__PURE__ */ (() => {
|
|
let ɵNzMessageComponent_BaseFactory;
|
|
return function NzMessageComponent_Factory(__ngFactoryType__) {
|
|
return (ɵNzMessageComponent_BaseFactory || (ɵNzMessageComponent_BaseFactory = ɵɵgetInheritedFactory(_NzMessageComponent)))(__ngFactoryType__ || _NzMessageComponent);
|
|
};
|
|
})();
|
|
static ɵcmp = ɵɵdefineComponent({
|
|
type: _NzMessageComponent,
|
|
selectors: [["nz-message"]],
|
|
inputs: {
|
|
instance: "instance"
|
|
},
|
|
outputs: {
|
|
destroyed: "destroyed"
|
|
},
|
|
exportAs: ["nzMessage"],
|
|
features: [ɵɵInheritDefinitionFeature],
|
|
decls: 9,
|
|
vars: 13,
|
|
consts: [[1, "ant-message-notice", 3, "mouseenter", "mouseleave"], [1, "ant-message-notice-content"], [1, "ant-message-custom-content"], ["nzType", "check-circle"], ["nzType", "info-circle"], ["nzType", "exclamation-circle"], ["nzType", "close-circle"], ["nzType", "loading"], [4, "nzStringTemplateOutlet", "nzStringTemplateOutletContext"], [3, "innerHTML"]],
|
|
template: function NzMessageComponent_Template(rf, ctx) {
|
|
if (rf & 1) {
|
|
ɵɵelementStart(0, "div", 0);
|
|
ɵɵlistener("@moveUpMotion.done", function NzMessageComponent_Template_div_animation_moveUpMotion_done_0_listener($event) {
|
|
return ctx.animationStateChanged.next($event);
|
|
})("mouseenter", function NzMessageComponent_Template_div_mouseenter_0_listener() {
|
|
return ctx.onEnter();
|
|
})("mouseleave", function NzMessageComponent_Template_div_mouseleave_0_listener() {
|
|
return ctx.onLeave();
|
|
});
|
|
ɵɵelementStart(1, "div", 1)(2, "div", 2);
|
|
ɵɵconditionalCreate(3, NzMessageComponent_Case_3_Template, 1, 0, "nz-icon", 3)(4, NzMessageComponent_Case_4_Template, 1, 0, "nz-icon", 4)(5, NzMessageComponent_Case_5_Template, 1, 0, "nz-icon", 5)(6, NzMessageComponent_Case_6_Template, 1, 0, "nz-icon", 6)(7, NzMessageComponent_Case_7_Template, 1, 0, "nz-icon", 7);
|
|
ɵɵtemplate(8, NzMessageComponent_ng_container_8_Template, 2, 1, "ng-container", 8);
|
|
ɵɵelementEnd()()();
|
|
}
|
|
if (rf & 2) {
|
|
let tmp_4_0;
|
|
ɵɵstyleMap(ctx.instance.options == null ? null : ctx.instance.options.nzStyle);
|
|
ɵɵclassMap(ctx.instance.options == null ? null : ctx.instance.options.nzClass);
|
|
ɵɵproperty("@moveUpMotion", ctx.instance.state);
|
|
ɵɵadvance(2);
|
|
ɵɵclassMap("ant-message-" + ctx.instance.type);
|
|
ɵɵadvance();
|
|
ɵɵconditional((tmp_4_0 = ctx.instance.type) === "success" ? 3 : tmp_4_0 === "info" ? 4 : tmp_4_0 === "warning" ? 5 : tmp_4_0 === "error" ? 6 : tmp_4_0 === "loading" ? 7 : -1);
|
|
ɵɵadvance(5);
|
|
ɵɵproperty("nzStringTemplateOutlet", ctx.instance.content)("nzStringTemplateOutletContext", ɵɵpureFunction2(10, _c02, ctx, ctx.instance.options == null ? null : ctx.instance.options.nzData));
|
|
}
|
|
},
|
|
dependencies: [NzIconModule, NzIconDirective, NzOutletModule, NzStringTemplateOutletDirective],
|
|
encapsulation: 2,
|
|
data: {
|
|
animation: [moveUpMotion]
|
|
},
|
|
changeDetection: 0
|
|
});
|
|
};
|
|
(() => {
|
|
(typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(NzMessageComponent, [{
|
|
type: Component,
|
|
args: [{
|
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
encapsulation: ViewEncapsulation.None,
|
|
selector: "nz-message",
|
|
exportAs: "nzMessage",
|
|
animations: [moveUpMotion],
|
|
template: `
|
|
<div
|
|
class="ant-message-notice"
|
|
[class]="instance.options?.nzClass"
|
|
[style]="instance.options?.nzStyle"
|
|
[@moveUpMotion]="instance.state"
|
|
(@moveUpMotion.done)="animationStateChanged.next($event)"
|
|
(mouseenter)="onEnter()"
|
|
(mouseleave)="onLeave()"
|
|
>
|
|
<div class="ant-message-notice-content">
|
|
<div class="ant-message-custom-content" [class]="'ant-message-' + instance.type">
|
|
@switch (instance.type) {
|
|
@case ('success') {
|
|
<nz-icon nzType="check-circle" />
|
|
}
|
|
@case ('info') {
|
|
<nz-icon nzType="info-circle" />
|
|
}
|
|
@case ('warning') {
|
|
<nz-icon nzType="exclamation-circle" />
|
|
}
|
|
@case ('error') {
|
|
<nz-icon nzType="close-circle" />
|
|
}
|
|
@case ('loading') {
|
|
<nz-icon nzType="loading" />
|
|
}
|
|
}
|
|
<ng-container
|
|
*nzStringTemplateOutlet="instance.content; context: { $implicit: this, data: instance.options?.nzData }"
|
|
>
|
|
<span [innerHTML]="instance.content"></span>
|
|
</ng-container>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`,
|
|
imports: [NzIconModule, NzOutletModule]
|
|
}]
|
|
}], null, {
|
|
instance: [{
|
|
type: Input
|
|
}],
|
|
destroyed: [{
|
|
type: Output
|
|
}]
|
|
});
|
|
})();
|
|
var NZ_CONFIG_COMPONENT_NAME = "message";
|
|
var NZ_MESSAGE_DEFAULT_CONFIG = {
|
|
nzAnimate: true,
|
|
nzDuration: 3e3,
|
|
nzMaxStack: 7,
|
|
nzPauseOnHover: true,
|
|
nzTop: 24,
|
|
nzDirection: "ltr"
|
|
};
|
|
var NzMessageContainerComponent = class _NzMessageContainerComponent extends NzMNContainerComponent {
|
|
dir = this.nzConfigService.getConfigForComponent(NZ_CONFIG_COMPONENT_NAME)?.nzDirection || "ltr";
|
|
top;
|
|
constructor() {
|
|
super();
|
|
this.updateConfig();
|
|
}
|
|
subscribeConfigChange() {
|
|
onConfigChangeEventForComponent(NZ_CONFIG_COMPONENT_NAME, () => {
|
|
this.updateConfig();
|
|
this.dir = this.nzConfigService.getConfigForComponent(NZ_CONFIG_COMPONENT_NAME)?.nzDirection || this.dir;
|
|
});
|
|
}
|
|
updateConfig() {
|
|
this.config = __spreadValues(__spreadValues(__spreadValues({}, NZ_MESSAGE_DEFAULT_CONFIG), this.config), this.nzConfigService.getConfigForComponent(NZ_CONFIG_COMPONENT_NAME));
|
|
this.top = toCssPixel(this.config.nzTop);
|
|
this.cdr.markForCheck();
|
|
}
|
|
static ɵfac = function NzMessageContainerComponent_Factory(__ngFactoryType__) {
|
|
return new (__ngFactoryType__ || _NzMessageContainerComponent)();
|
|
};
|
|
static ɵcmp = ɵɵdefineComponent({
|
|
type: _NzMessageContainerComponent,
|
|
selectors: [["nz-message-container"]],
|
|
exportAs: ["nzMessageContainer"],
|
|
features: [ɵɵInheritDefinitionFeature],
|
|
decls: 3,
|
|
vars: 4,
|
|
consts: [[1, "ant-message"], [3, "instance"], [3, "destroyed", "instance"]],
|
|
template: function NzMessageContainerComponent_Template(rf, ctx) {
|
|
if (rf & 1) {
|
|
ɵɵelementStart(0, "div", 0);
|
|
ɵɵrepeaterCreate(1, NzMessageContainerComponent_For_2_Template, 1, 1, "nz-message", 1, ɵɵrepeaterTrackByIdentity);
|
|
ɵɵelementEnd();
|
|
}
|
|
if (rf & 2) {
|
|
ɵɵstyleProp("top", ctx.top);
|
|
ɵɵclassProp("ant-message-rtl", ctx.dir === "rtl");
|
|
ɵɵadvance();
|
|
ɵɵrepeater(ctx.instances);
|
|
}
|
|
},
|
|
dependencies: [NzMessageComponent],
|
|
encapsulation: 2,
|
|
changeDetection: 0
|
|
});
|
|
};
|
|
(() => {
|
|
(typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(NzMessageContainerComponent, [{
|
|
type: Component,
|
|
args: [{
|
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
encapsulation: ViewEncapsulation.None,
|
|
selector: "nz-message-container",
|
|
exportAs: "nzMessageContainer",
|
|
template: `
|
|
<div class="ant-message" [class.ant-message-rtl]="dir === 'rtl'" [style.top]="top">
|
|
@for (instance of instances; track instance) {
|
|
<nz-message [instance]="instance" (destroyed)="remove($event.id, $event.userAction)" />
|
|
}
|
|
</div>
|
|
`,
|
|
imports: [NzMessageComponent]
|
|
}]
|
|
}], () => [], null);
|
|
})();
|
|
var NzMessageService = class _NzMessageService extends NzMNService {
|
|
componentPrefix = "message-";
|
|
success(content, options) {
|
|
return this.createInstance({
|
|
type: "success",
|
|
content
|
|
}, options);
|
|
}
|
|
error(content, options) {
|
|
return this.createInstance({
|
|
type: "error",
|
|
content
|
|
}, options);
|
|
}
|
|
info(content, options) {
|
|
return this.createInstance({
|
|
type: "info",
|
|
content
|
|
}, options);
|
|
}
|
|
warning(content, options) {
|
|
return this.createInstance({
|
|
type: "warning",
|
|
content
|
|
}, options);
|
|
}
|
|
loading(content, options) {
|
|
return this.createInstance({
|
|
type: "loading",
|
|
content
|
|
}, options);
|
|
}
|
|
create(type, content, options) {
|
|
return this.createInstance({
|
|
type,
|
|
content
|
|
}, options);
|
|
}
|
|
createInstance(message, options) {
|
|
this.container = this.withContainer(NzMessageContainerComponent);
|
|
return this.container.create(__spreadValues(__spreadValues({}, message), {
|
|
createdAt: /* @__PURE__ */ new Date(),
|
|
messageId: this.getInstanceId(),
|
|
options
|
|
}));
|
|
}
|
|
static ɵfac = /* @__PURE__ */ (() => {
|
|
let ɵNzMessageService_BaseFactory;
|
|
return function NzMessageService_Factory(__ngFactoryType__) {
|
|
return (ɵNzMessageService_BaseFactory || (ɵNzMessageService_BaseFactory = ɵɵgetInheritedFactory(_NzMessageService)))(__ngFactoryType__ || _NzMessageService);
|
|
};
|
|
})();
|
|
static ɵprov = ɵɵdefineInjectable({
|
|
token: _NzMessageService,
|
|
factory: _NzMessageService.ɵfac,
|
|
providedIn: "root"
|
|
});
|
|
};
|
|
(() => {
|
|
(typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(NzMessageService, [{
|
|
type: Injectable,
|
|
args: [{
|
|
providedIn: "root"
|
|
}]
|
|
}], null, null);
|
|
})();
|
|
export {
|
|
NzMNComponent,
|
|
NzMNContainerComponent,
|
|
NzMNService,
|
|
NzMessageComponent,
|
|
NzMessageContainerComponent,
|
|
NzMessageService
|
|
};
|
|
/*! Bundled license information:
|
|
|
|
@angular/animations/fesm2022/animations.mjs:
|
|
(**
|
|
* @license Angular v20.3.18
|
|
* (c) 2010-2025 Google LLC. https://angular.dev/
|
|
* License: MIT
|
|
*)
|
|
*/
|
|
//# sourceMappingURL=ng-zorro-antd_message.js.map
|