UNPKG

@angular/cdk

Version:

Angular Material Component Development Kit

457 lines (451 loc) 13.1 kB
import * as i0 from '@angular/core'; import { ElementRef, NgModuleRef, EnvironmentInjector, createComponent, Injector, inject, TemplateRef, ViewContainerRef, Directive, DOCUMENT, EventEmitter, Input, Output, NgModule } from '@angular/core'; 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'); } class Portal { _attachedHost; attach(host) { if (typeof ngDevMode === 'undefined' || ngDevMode) { if (host == null) { throwNullPortalOutletError(); } if (host.hasAttached()) { throwPortalAlreadyAttachedError(); } } this._attachedHost = host; return host.attach(this); } detach() { let host = this._attachedHost; if (host != null) { this._attachedHost = null; host.detach(); } else if (typeof ngDevMode === 'undefined' || ngDevMode) { throwNoPortalAttachedError(); } } get isAttached() { return this._attachedHost != null; } setAttachedHost(host) { this._attachedHost = host; } } class ComponentPortal extends Portal { component; viewContainerRef; injector; projectableNodes; constructor(component, viewContainerRef, injector, projectableNodes) { super(); this.component = component; this.viewContainerRef = viewContainerRef; this.injector = injector; this.projectableNodes = projectableNodes; } } class TemplatePortal 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(host, context = this.context) { this.context = context; return super.attach(host); } detach() { this.context = undefined; return super.detach(); } } class DomPortal extends Portal { element; constructor(element) { super(); this.element = element instanceof ElementRef ? element.nativeElement : element; } } class BasePortalOutlet { _attachedPortal; _disposeFn; _isDisposed = false; hasAttached() { return !!this._attachedPortal; } 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(); } } attachDomPortal = null; detach() { if (this._attachedPortal) { this._attachedPortal.setAttachedHost(null); this._attachedPortal = null; } this._invokeDisposeFn(); } dispose() { if (this.hasAttached()) { this.detach(); } this._invokeDisposeFn(); this._isDisposed = true; } setDisposeFn(fn) { this._disposeFn = fn; } _invokeDisposeFn() { if (this._disposeFn) { this._disposeFn(); this._disposeFn = null; } } } class DomPortalOutlet extends BasePortalOutlet { outletElement; _appRef; _defaultInjector; constructor(outletElement, _appRef, _defaultInjector) { super(); this.outletElement = outletElement; this._appRef = _appRef; this._defaultInjector = _defaultInjector; } attachComponentPortal(portal) { let componentRef; if (portal.viewContainerRef) { const injector = portal.injector || portal.viewContainerRef.injector; const ngModuleRef = injector.get(NgModuleRef, null, { optional: true }) || undefined; componentRef = portal.viewContainerRef.createComponent(portal.component, { index: portal.viewContainerRef.length, injector, ngModuleRef, projectableNodes: portal.projectableNodes || undefined }); 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 || undefined }); 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; } 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; } 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); } }); }; dispose() { super.dispose(); this.outletElement.remove(); } _getComponentRootNode(componentRef) { return componentRef.hostView.rootNodes[0]; } } class CdkPortal extends TemplatePortal { constructor() { const templateRef = inject(TemplateRef); const viewContainerRef = inject(ViewContainerRef); super(templateRef, viewContainerRef); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: CdkPortal, deps: [], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.0.0", type: CdkPortal, isStandalone: true, selector: "[cdkPortal]", exportAs: ["cdkPortal"], usesInheritance: true, ngImport: i0 }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: CdkPortal, decorators: [{ type: Directive, args: [{ selector: '[cdkPortal]', exportAs: 'cdkPortal' }] }], ctorParameters: () => [] }); class CdkPortalOutlet extends BasePortalOutlet { _moduleRef = inject(NgModuleRef, { optional: true }); _document = inject(DOCUMENT); _viewContainerRef = inject(ViewContainerRef); _isInitialized = false; _attachedRef; constructor() { super(); } 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; } attached = new EventEmitter(); get attachedRef() { return this._attachedRef; } ngOnInit() { this._isInitialized = true; } ngOnDestroy() { super.dispose(); this._attachedRef = this._attachedPortal = null; } 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 || undefined, ngModuleRef: this._moduleRef || undefined }); 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; } 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; } 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); } }); }; _getRootNode() { const nativeElement = this._viewContainerRef.element.nativeElement; return nativeElement.nodeType === nativeElement.ELEMENT_NODE ? nativeElement : nativeElement.parentNode; } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: CdkPortalOutlet, deps: [], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.0.0", type: CdkPortalOutlet, isStandalone: true, selector: "[cdkPortalOutlet]", inputs: { portal: ["cdkPortalOutlet", "portal"] }, outputs: { attached: "attached" }, exportAs: ["cdkPortalOutlet"], usesInheritance: true, ngImport: i0 }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: CdkPortalOutlet, decorators: [{ type: Directive, args: [{ selector: '[cdkPortalOutlet]', exportAs: 'cdkPortalOutlet' }] }], ctorParameters: () => [], propDecorators: { portal: [{ type: Input, args: ['cdkPortalOutlet'] }], attached: [{ type: Output }] } }); class PortalModule { static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: PortalModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.0.0", ngImport: i0, type: PortalModule, imports: [CdkPortal, CdkPortalOutlet], exports: [CdkPortal, CdkPortalOutlet] }); static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: PortalModule }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.0", ngImport: i0, type: PortalModule, decorators: [{ type: NgModule, args: [{ imports: [CdkPortal, CdkPortalOutlet], exports: [CdkPortal, CdkPortalOutlet] }] }] }); export { BasePortalOutlet, CdkPortal, CdkPortalOutlet, ComponentPortal, DomPortal, DomPortalOutlet, Portal, PortalModule, TemplatePortal }; //# sourceMappingURL=portal.mjs.map