UNPKG

@rxap/window-system

Version:

A module for creating and managing windows within an Angular application. It provides components for window containers, toolbars, resizers, action bars, and task bars, along with services for managing window instances and configurations. This library allo

738 lines (723 loc) 58.2 kB
import * as i0 from '@angular/core'; import { InjectionToken, Component, ChangeDetectionStrategy, Inject, ComponentRef, isDevMode, ViewChild, EventEmitter, Input, Output, inject, INJECTOR, runInInjectionContext, ContentChild, HostBinding, InjectFlags, Injector, ViewContainerRef, ComponentFactoryResolver, Injectable, Optional, ChangeDetectorRef, NgModule, TemplateRef, Directive } from '@angular/core'; import * as i4 from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button'; import { IconDirective } from '@rxap/material-directives/icon'; import * as i1$1 from '@angular/material/icon'; import { MatIconModule } from '@angular/material/icon'; import * as i2 from '@angular/cdk/portal'; import { PortalModule, TemplatePortal, ComponentPortal, CdkPortalOutlet } from '@angular/cdk/portal'; import { NgIf, AsyncPipe, NgClass, NgStyle, NgFor } from '@angular/common'; import * as i1 from '@angular/material/toolbar'; import { MatToolbarModule } from '@angular/material/toolbar'; import * as i1$3 from '@angular/cdk/overlay'; import { OverlayConfig, Overlay } from '@angular/cdk/overlay'; import * as i1$2 from '@rxap/services'; import { LoadingIndicatorService } from '@rxap/services'; import { GenerateRandomString } from '@rxap/utilities'; import { isObservable, TimeoutError, throwError, Subscription, fromEvent, merge, Subject, BehaviorSubject, ReplaySubject } from 'rxjs'; import { startWith, take, tap, filter, delay, timeout, catchError, switchMap, takeUntil, map, finalize } from 'rxjs/operators'; import { isDefined, ToggleSubject } from '@rxap/rxjs'; import { CdkDrag, CdkDragHandle } from '@angular/cdk/drag-drop'; import { toSignal } from '@angular/core/rxjs-interop'; import * as i3 from '@angular/material/progress-bar'; import { MatProgressBarModule } from '@angular/material/progress-bar'; import { StopPropagationDirective } from '@rxap/directives'; import * as i2$1 from '@angular/material/badge'; import { MatBadgeModule } from '@angular/material/badge'; const RXAP_WINDOW_CONTEXT = new InjectionToken('rxap/core/window/WINDOW_CONTEXT'); const RXAP_WINDOW_DATA = new InjectionToken('rxap/core/window/WINDOW_DATA'); const RXAP_WINDOW_CONTAINER_CONTEXT = new InjectionToken('rxap/core/window/WINDOW_CONTAINER_CONTEXT'); const RXAP_WINDOW_REF = new InjectionToken('rxap/window-system/WINDOW_REF'); const RXAP_WINDOW_SETTINGS = new InjectionToken('rxap/window-system/window-settings'); const RXAP_WINDOW_DEFAULT_SETTINGS = new InjectionToken('rxap/window-system/default-window-settings'); class WindowToolBarComponent { constructor(context) { this.windowRef = context.windowRef; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: WindowToolBarComponent, deps: [{ token: RXAP_WINDOW_CONTEXT }], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.1", type: WindowToolBarComponent, isStandalone: true, selector: "rxap-window-tool-bar", ngImport: i0, template: "<mat-toolbar class=\"header flex flex-row items-center justify-between gap-x-4\">\n <div class=\"flex flex-row justify-end items-center gap-x-3\">\n <ng-template [ngIfElse]=\"titleFromSettings\" [ngIf]=\"windowRef.titlePortal$ | async\" let-titlePortal>\n <ng-template [cdkPortalOutlet]=\"titlePortal\"></ng-template>\n </ng-template>\n <ng-template #titleFromSettings>\n <ng-template [ngIf]=\"windowRef.settings$ | async\" let-settings>\n <mat-icon *ngIf=\"settings.icon\" [rxapIcon]=\"settings.icon\"></mat-icon>\n <h3 *ngIf=\"settings.title\">{{settings.title}}</h3>\n </ng-template>\n </ng-template>\n </div>\n <div class=\"flex flex-row justify-end items-center\">\n <button (click)=\"windowRef.minimize()\" *ngIf=\"windowRef.minimizable !== false\"\n mat-icon-button type=\"button\">\n <mat-icon>minimize</mat-icon>\n </button>\n <button (click)=\"windowRef.fullScreen()\" mat-icon-button type=\"button\">\n <mat-icon>fullscreen</mat-icon>\n </button>\n <button (click)=\"windowRef.complete()\" mat-icon-button type=\"button\">\n <mat-icon>close</mat-icon>\n </button>\n </div>\n</mat-toolbar>\n", styles: [":host{width:100%}.header{height:64px}\n"], dependencies: [{ kind: "ngmodule", type: MatToolbarModule }, { kind: "component", type: i1.MatToolbar, selector: "mat-toolbar", inputs: ["color"], exportAs: ["matToolbar"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: PortalModule }, { kind: "directive", type: i2.CdkPortalOutlet, selector: "[cdkPortalOutlet]", inputs: ["cdkPortalOutlet"], outputs: ["attached"], exportAs: ["cdkPortalOutlet"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i1$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: IconDirective, selector: "mat-icon[rxapIcon]", inputs: ["rxapIcon"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i4.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.Default }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: WindowToolBarComponent, decorators: [{ type: Component, args: [{ selector: 'rxap-window-tool-bar', changeDetection: ChangeDetectionStrategy.Default, imports: [ MatToolbarModule, NgIf, PortalModule, MatIconModule, IconDirective, MatButtonModule, AsyncPipe, ], template: "<mat-toolbar class=\"header flex flex-row items-center justify-between gap-x-4\">\n <div class=\"flex flex-row justify-end items-center gap-x-3\">\n <ng-template [ngIfElse]=\"titleFromSettings\" [ngIf]=\"windowRef.titlePortal$ | async\" let-titlePortal>\n <ng-template [cdkPortalOutlet]=\"titlePortal\"></ng-template>\n </ng-template>\n <ng-template #titleFromSettings>\n <ng-template [ngIf]=\"windowRef.settings$ | async\" let-settings>\n <mat-icon *ngIf=\"settings.icon\" [rxapIcon]=\"settings.icon\"></mat-icon>\n <h3 *ngIf=\"settings.title\">{{settings.title}}</h3>\n </ng-template>\n </ng-template>\n </div>\n <div class=\"flex flex-row justify-end items-center\">\n <button (click)=\"windowRef.minimize()\" *ngIf=\"windowRef.minimizable !== false\"\n mat-icon-button type=\"button\">\n <mat-icon>minimize</mat-icon>\n </button>\n <button (click)=\"windowRef.fullScreen()\" mat-icon-button type=\"button\">\n <mat-icon>fullscreen</mat-icon>\n </button>\n <button (click)=\"windowRef.complete()\" mat-icon-button type=\"button\">\n <mat-icon>close</mat-icon>\n </button>\n </div>\n</mat-toolbar>\n", styles: [":host{width:100%}.header{height:64px}\n"] }] }], ctorParameters: () => [{ type: undefined, decorators: [{ type: Inject, args: [RXAP_WINDOW_CONTEXT] }] }] }); class WindowContentComponent { constructor(context, windowRef, injector, viewContainerRef, loadingIndicatorService) { this.windowRef = windowRef; this.injector = injector; this.viewContainerRef = viewContainerRef; this.loadingIndicatorService = loadingIndicatorService; this.portal = null; this.context = context; } ngOnInit() { if (this.context.template) { this.portal = new TemplatePortal(this.context.template, this.viewContainerRef); } else if (this.context.component) { this.portal = new ComponentPortal(this.context.component, this.viewContainerRef, this.injector); } } ngAfterViewInit() { this.portalOutlet.attached.pipe(startWith(this.portalOutlet.attachedRef), isDefined(), take(1), tap(attachedRef => this.windowRef.setAttachedRef(attachedRef)), tap(attachedRef => { const promise = []; if (attachedRef instanceof ComponentRef) { attachedRef.changeDetectorRef.detectChanges(); const loading$ = attachedRef.instance.loading$; if (loading$ && isObservable(loading$)) { if (isDevMode()) { console.warn('The component has a loading indicator member'); } this.loadingIndicatorService.attachLoading(loading$); promise.push(loading$.pipe(filter(Boolean), take(1), delay(100), tap(() => attachedRef.changeDetectorRef.detectChanges())).toPromise()); } else { this.loadingIndicatorService.disable(); } } else { this.loadingIndicatorService.disable(); } return Promise.all(promise); }), timeout(10000), catchError(error => { if (error instanceof TimeoutError) { if (isDevMode()) { console.warn('The window content never resolved the attached ref'); } } return throwError(error); })).subscribe(); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: WindowContentComponent, deps: [{ token: RXAP_WINDOW_CONTAINER_CONTEXT }, { token: RXAP_WINDOW_REF }, { token: i0.Injector }, { token: i0.ViewContainerRef }, { token: i1$2.LoadingIndicatorService }], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.1", type: WindowContentComponent, isStandalone: true, selector: "rxap-window-content", viewQueries: [{ propertyName: "portalOutlet", first: true, predicate: CdkPortalOutlet, descendants: true }], ngImport: i0, template: "<div class=\"p-4\">\n <ng-template [cdkPortalOutlet]=\"portal\"></ng-template>\n</div>\n", styles: [""], dependencies: [{ kind: "ngmodule", type: PortalModule }, { kind: "directive", type: i2.CdkPortalOutlet, selector: "[cdkPortalOutlet]", inputs: ["cdkPortalOutlet"], outputs: ["attached"], exportAs: ["cdkPortalOutlet"] }], changeDetection: i0.ChangeDetectionStrategy.Default }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: WindowContentComponent, decorators: [{ type: Component, args: [{ selector: 'rxap-window-content', changeDetection: ChangeDetectionStrategy.Default, imports: [PortalModule], template: "<div class=\"p-4\">\n <ng-template [cdkPortalOutlet]=\"portal\"></ng-template>\n</div>\n" }] }], ctorParameters: () => [{ type: undefined, decorators: [{ type: Inject, args: [RXAP_WINDOW_CONTAINER_CONTEXT] }] }, { type: undefined, decorators: [{ type: Inject, args: [RXAP_WINDOW_REF] }] }, { type: i0.Injector }, { type: i0.ViewContainerRef }, { type: i1$2.LoadingIndicatorService }], propDecorators: { portalOutlet: [{ type: ViewChild, args: [CdkPortalOutlet] }] } }); class WindowResizerComponent { constructor(context) { // eslint-disable-next-line @angular-eslint/no-output-rename this.width$ = new EventEmitter(); // eslint-disable-next-line @angular-eslint/no-output-rename this.height$ = new EventEmitter(); // eslint-disable-next-line @angular-eslint/no-output-rename this.resizing$ = new EventEmitter(); this._subscription = new Subscription(); this.windowRef = context.windowRef; } ngOnDestroy() { this._subscription.unsubscribe(); } ngOnInit() { let originalMouseX = 0; let originalMouseY = 0; let originalWidth = 0; let originalHeight = 0; this._subscription.add(fromEvent(this.resizerRef.nativeElement, 'mousedown').pipe(tap(event => { this.resizing$.emit(true); originalMouseX = event.pageX; originalMouseY = event.pageY; originalWidth = parseFloat(getComputedStyle(this.containerRef.nativeElement, null) .getPropertyValue('width') .replace('px', '')); originalHeight = parseFloat(getComputedStyle(this.containerRef.nativeElement, null) .getPropertyValue('height') .replace('px', '')); }), switchMap(() => fromEvent(window, 'mousemove').pipe(takeUntil(fromEvent(window, 'mouseup')), map((event) => ({ width: originalWidth + (event.pageX - originalMouseX), height: originalHeight + (event.pageY - originalMouseY), })), filter(size => (Number(this.windowRef.getSizeConfig().minHeight) || 0) <= size.height && (Number(this.windowRef.getSizeConfig().minWidth) || 0) <= size.width), map(size => ({ width: size.width + 'px', height: size.height + 'px', })), tap(size => { this.width$.emit(size.width); this.height$.emit(size.height); }), tap(size => { this.windowRef.setHeight(size.height); this.windowRef.setWidth(size.width); }), finalize(() => this.resizing$.emit(false))))).subscribe()); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: WindowResizerComponent, deps: [{ token: RXAP_WINDOW_CONTEXT }], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.1", type: WindowResizerComponent, isStandalone: true, selector: "rxap-window-resizer", inputs: { containerRef: "containerRef" }, outputs: { width$: "width", height$: "height", resizing$: "resizing" }, viewQueries: [{ propertyName: "resizerRef", first: true, predicate: ["resizer"], descendants: true, static: true }], ngImport: i0, template: "<div #resizer class=\"resizer\">\n <mat-icon [ngClass]=\"{ hidden: (resizing$ | async) === false }\">texture</mat-icon>\n</div>\n", styles: [":host,.resizer{position:absolute;inset:auto 0 0 auto;width:24px;height:24px;cursor:nwse-resize;z-index:100}\n"], dependencies: [{ kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i1$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: WindowResizerComponent, decorators: [{ type: Component, args: [{ selector: 'rxap-window-resizer', changeDetection: ChangeDetectionStrategy.OnPush, imports: [MatIconModule, AsyncPipe, NgClass], template: "<div #resizer class=\"resizer\">\n <mat-icon [ngClass]=\"{ hidden: (resizing$ | async) === false }\">texture</mat-icon>\n</div>\n", styles: [":host,.resizer{position:absolute;inset:auto 0 0 auto;width:24px;height:24px;cursor:nwse-resize;z-index:100}\n"] }] }], ctorParameters: () => [{ type: undefined, decorators: [{ type: Inject, args: [RXAP_WINDOW_CONTEXT] }] }], propDecorators: { containerRef: [{ type: Input, args: [{ required: true }] }], resizerRef: [{ type: ViewChild, args: ['resizer', { static: true }] }], width$: [{ type: Output, args: ['width'] }], height$: [{ type: Output, args: ['height'] }], resizing$: [{ type: Output, args: ['resizing'] }] } }); class WindowContainerComponent { constructor() { this.injector = inject(INJECTOR); this.context = inject(RXAP_WINDOW_CONTEXT); this.windowRef = inject(RXAP_WINDOW_REF); this.windowInstance = inject(LoadingIndicatorService); } get id() { return this.context.id; } ngOnInit() { runInInjectionContext(this.injector, () => { this.width = toSignal(merge(this.windowContainerResizer.width$, this.windowRef.width$).pipe(isDefined(), filter(value => !!value?.match(/^\d+/))), { initialValue: '100%' }); this.height = toSignal(merge(this.windowContainerResizer.height$, this.windowRef.height$).pipe(isDefined(), filter(value => !!value?.match(/^\d+/))), { initialValue: '100%' }); }); } onDragEnded($event) { const nativeElement = $event.source.element.nativeElement; const pos = nativeElement.getBoundingClientRect(); $event.source.reset(); this.windowRef.setPos(pos.left + 'px', pos.top + 'px'); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: WindowContainerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.1", type: WindowContainerComponent, isStandalone: true, selector: "rxap-window-container", host: { properties: { "attr.data-id": "this.id" }, classAttribute: "rxap-window-container" }, queries: [{ propertyName: "footerContent", first: true, predicate: ["[footer]"], descendants: true, static: true }], viewQueries: [{ propertyName: "containerRef", first: true, predicate: ["container"], descendants: true, static: true }, { propertyName: "windowContainerResizer", first: true, predicate: WindowResizerComponent, descendants: true, static: true }], ngImport: i0, template: "<div #container\n (cdkDragEnded)=\"onDragEnded($event)\"\n [cdkDragDisabled]=\"windowRef.draggable === false\"\n [ngStyle]=\"{\n 'width': width(),\n 'height': height(),\n 'minWidth': windowRef.minWidth,\n 'minHeight': windowRef.minHeight,\n 'maxWidth': windowRef.maxWidth,\n 'maxHeight': windowRef.maxHeight\n }\"\n cdkDrag\n cdkDragBoundary=\".cdk-overlay-container\"\n class=\"drop-shadow-2xl bg-white dark:bg-black border-2 border-gray-400 dark:border-gray-800 rounded-xl overflow-hidden\">\n <div class=\"flex flex-col h-full max-h-full\">\n\n <div cdkDragHandle class=\"grow-0 w-full h-[64px] min-h-[64px] cursor-move\">\n <rxap-window-tool-bar></rxap-window-tool-bar>\n </div>\n\n <div class=\"grow overflow-auto w-full\">\n <ng-content></ng-content>\n </div>\n\n @if (windowRef.footerPortal$ | async; as footerPortal) {\n <mat-toolbar class=\"mat-elevation-z1 grow-0 w-full h-[64px] min-h-[64px]\">\n <ng-template [cdkPortalOutlet]=\"footerPortal\"></ng-template>\n </mat-toolbar>\n }\n\n @if (windowInstance.isLoading$ | async) {\n <mat-progress-bar mode=\"indeterminate\"></mat-progress-bar>\n }\n\n <rxap-window-resizer [containerRef]=\"containerRef\"></rxap-window-resizer>\n </div>\n</div>\n", styles: [""], dependencies: [{ kind: "directive", type: CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: CdkDragHandle, selector: "[cdkDragHandle]", inputs: ["cdkDragHandleDisabled"] }, { kind: "component", type: WindowToolBarComponent, selector: "rxap-window-tool-bar" }, { kind: "ngmodule", type: MatToolbarModule }, { kind: "component", type: i1.MatToolbar, selector: "mat-toolbar", inputs: ["color"], exportAs: ["matToolbar"] }, { kind: "ngmodule", type: PortalModule }, { kind: "directive", type: i2.CdkPortalOutlet, selector: "[cdkPortalOutlet]", inputs: ["cdkPortalOutlet"], outputs: ["attached"], exportAs: ["cdkPortalOutlet"] }, { kind: "ngmodule", type: MatProgressBarModule }, { kind: "component", type: i3.MatProgressBar, selector: "mat-progress-bar", inputs: ["color", "value", "bufferValue", "mode"], outputs: ["animationEnd"], exportAs: ["matProgressBar"] }, { kind: "component", type: WindowResizerComponent, selector: "rxap-window-resizer", inputs: ["containerRef"], outputs: ["width", "height", "resizing"] }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.Default }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: WindowContainerComponent, decorators: [{ type: Component, args: [{ selector: 'rxap-window-container', changeDetection: ChangeDetectionStrategy.Default, host: { class: 'rxap-window-container', }, imports: [ CdkDrag, NgStyle, CdkDragHandle, WindowToolBarComponent, MatToolbarModule, PortalModule, MatProgressBarModule, WindowResizerComponent, AsyncPipe, ], template: "<div #container\n (cdkDragEnded)=\"onDragEnded($event)\"\n [cdkDragDisabled]=\"windowRef.draggable === false\"\n [ngStyle]=\"{\n 'width': width(),\n 'height': height(),\n 'minWidth': windowRef.minWidth,\n 'minHeight': windowRef.minHeight,\n 'maxWidth': windowRef.maxWidth,\n 'maxHeight': windowRef.maxHeight\n }\"\n cdkDrag\n cdkDragBoundary=\".cdk-overlay-container\"\n class=\"drop-shadow-2xl bg-white dark:bg-black border-2 border-gray-400 dark:border-gray-800 rounded-xl overflow-hidden\">\n <div class=\"flex flex-col h-full max-h-full\">\n\n <div cdkDragHandle class=\"grow-0 w-full h-[64px] min-h-[64px] cursor-move\">\n <rxap-window-tool-bar></rxap-window-tool-bar>\n </div>\n\n <div class=\"grow overflow-auto w-full\">\n <ng-content></ng-content>\n </div>\n\n @if (windowRef.footerPortal$ | async; as footerPortal) {\n <mat-toolbar class=\"mat-elevation-z1 grow-0 w-full h-[64px] min-h-[64px]\">\n <ng-template [cdkPortalOutlet]=\"footerPortal\"></ng-template>\n </mat-toolbar>\n }\n\n @if (windowInstance.isLoading$ | async) {\n <mat-progress-bar mode=\"indeterminate\"></mat-progress-bar>\n }\n\n <rxap-window-resizer [containerRef]=\"containerRef\"></rxap-window-resizer>\n </div>\n</div>\n" }] }], propDecorators: { footerContent: [{ type: ContentChild, args: ['[footer]', { static: true }] }], containerRef: [{ type: ViewChild, args: ['container', { static: true }] }], windowContainerResizer: [{ type: ViewChild, args: [WindowResizerComponent, { static: true }] }], id: [{ type: HostBinding, args: ['attr.data-id'] }] } }); class DefaultWindowComponent { static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: DefaultWindowComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.1", type: DefaultWindowComponent, isStandalone: true, selector: "rxap-default-window", ngImport: i0, template: "<rxap-window-container>\n <rxap-window-content></rxap-window-content>\n</rxap-window-container>\n", styles: [""], dependencies: [{ kind: "component", type: WindowContainerComponent, selector: "rxap-window-container" }, { kind: "component", type: WindowContentComponent, selector: "rxap-window-content" }], changeDetection: i0.ChangeDetectionStrategy.Default }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: DefaultWindowComponent, decorators: [{ type: Component, args: [{ selector: 'rxap-default-window', changeDetection: ChangeDetectionStrategy.Default, imports: [WindowContainerComponent, WindowContentComponent], template: "<rxap-window-container>\n <rxap-window-content></rxap-window-content>\n</rxap-window-container>\n" }] }] }); function GetWindowStartPos() { let top = '16px'; let left = '16px'; // TODO : remove hack to get a better window starting position const rxapLayoutHeaderElements = document.getElementsByClassName('rxap-layout-header'); if (rxapLayoutHeaderElements) { const rxapLayoutHeaderElement = rxapLayoutHeaderElements.item(0); if (rxapLayoutHeaderElement && rxapLayoutHeaderElement instanceof HTMLElement) { top = (rxapLayoutHeaderElement.offsetHeight + 16) + 'px'; } } const rxapLayoutSidenavs = document.getElementsByClassName('rxap-layout-sidenav'); if (rxapLayoutSidenavs) { const rxapLayoutSidenav = rxapLayoutSidenavs.item(0); if (rxapLayoutSidenav && rxapLayoutSidenav instanceof HTMLElement) { const container = rxapLayoutSidenav.firstElementChild; if (container && container instanceof HTMLElement) { left = (container.offsetWidth + 16) + 'px'; } } } return { top, left, }; } const DEFAULT_WINDOW_CONFIG = { resizeable: true, draggable: true, injector: null, componentFactoryResolver: null, minWidth: '384px', minHeight: '192px', maxWidth: '100vw', maxHeight: '100vh', minimizable: false, }; class WindowRef extends Subject { constructor(overlayRef, overlay, settings) { super(); this.overlayRef = overlayRef; this.overlay = overlay; this.settings = settings; this.width$ = new BehaviorSubject(this.getWidth()); this.height$ = new BehaviorSubject(this.getHeight()); /** * @deprecated removed. use the subscribe method and wait for the resolve event */ this.closed$ = new Subject(); this.attachedRef$ = new ReplaySubject(1); /** * @internal */ this.footerPortal$ = new ReplaySubject(1); /** * @internal */ this.titlePortal$ = new ReplaySubject(1); this.settings$ = new ReplaySubject(1); /** * stores the window size before fullScreen * @internal */ this.oldSizes = null; } get id() { return this.settings.id; } get minimizable() { return this.settings.minimizable; } get draggable() { return this.settings.draggable; } get minWidth() { return this.settings.minWidth; } get minHeight() { return this.settings.minHeight; } get maxWidth() { return this.settings.maxWidth; } get maxHeight() { return this.settings.maxHeight; } get isMinimized() { return this.overlayRef.hostElement.style.display === 'none'; } /** * @deprecated removed. use the complete method * @param result */ close(result) { this.closed$.next(result); if (result !== undefined) { this.next(result); } this.complete(); } complete() { this.overlayRef.dispose(); super.complete(); } minimize() { this.overlayRef.hostElement.style.display = 'none'; } reopen() { const startPos = GetWindowStartPos(); this.setPos(startPos.left, startPos.top); this.overlayRef.hostElement.style.display = 'flex'; } getSizeConfig() { const config = this.overlayRef.getConfig(); return { width: config.width, height: config.height, minHeight: config.minHeight, minWidth: config.minWidth, maxHeight: config.maxHeight, maxWidth: config.maxWidth, }; } setWidth(width) { this.overlayRef.updateSize({ ...this.getSizeConfig(), width, }); this.width$.next(width); } setHeight(height) { this.overlayRef.updateSize({ ...this.getSizeConfig(), height, }); this.height$.next(height); } getWidth() { return this.getSizeConfig().width + ''; // return this.overlayRef.overlayElement.offsetWidth + 'px' } getHeight() { return this.getSizeConfig().height + ''; // return this.overlayRef.overlayElement.offsetHeight + 'px'; } setPos(x, y) { this.overlayRef.updatePositionStrategy(this .overlay .position() .global() .top(y) .left(x)); this.overlayRef.updatePosition(); } getPos() { const pos = this.overlayRef.overlayElement.getBoundingClientRect(); return { x: pos.left + 'px', y: pos.top + 'px', }; } fullScreen() { if (this.oldSizes === null) { this.oldSizes = { width: this.overlayRef.overlayElement.offsetWidth + 'px', height: this.overlayRef.overlayElement.offsetHeight + 'px', pos: this.getPos(), }; this.setWidth('100vw'); this.setHeight('100vh'); this.setPos('0px', '0px'); } else { this.setWidth(this.oldSizes.width); this.setHeight(this.oldSizes.height); this.setPos(this.oldSizes.pos.x, this.oldSizes.pos.y); this.oldSizes = null; } } /** * @param portal */ setFooterPortal(portal) { setTimeout(() => { this.footerPortal$.next(portal); }); } setTitlePortal(portal) { setTimeout(() => { this.titlePortal$.next(portal); }); } setAttachedRef(attachedRef) { if (attachedRef instanceof ComponentRef) { this.updateWindowSettings(attachedRef); } else { this.updateWindowSettings(); } this.attachedRef$.next(attachedRef); } /** * Injects the window settings from the component ref injector and overwrites * the settings * @private */ updateWindowSettings(componentRef) { const settings = componentRef?.injector.get(RXAP_WINDOW_SETTINGS, null, InjectFlags.Optional) ?? this.settings; if (settings) { // prevent change diction error: ExpressionChangedAfterItHasBeenCheckedError setTimeout(() => { this.settings$.next(Object.assign({}, this.settings, settings)); }); } } } class WindowService { constructor(injector, overlay, componentFactoryResolver, defaultWindowSettings) { this.injector = injector; this.overlay = overlay; this.componentFactoryResolver = componentFactoryResolver; this.defaultWindowSettings = defaultWindowSettings; this.activeCount$ = new Subject(); this.active = new Map(); } get hasActiveWindows() { return this.active.size !== 0; } get activeWindowCount() { return this.active.size; } get allActiveWindows() { return Array.from(this.active.values()); } get(id) { if (!this.has(id)) { throw new Error(`Active Window with id '${id}' not found`); } return this.active.get(id); } close(id) { const windowRef = this.get(id); windowRef.complete(); } has(id) { return this.active.has(id); } open(config) { // Override default configuration const windowConfig = { id: GenerateRandomString(), ...DEFAULT_WINDOW_CONFIG, ...(this.defaultWindowSettings ?? {}), ...config, }; if (this.has(windowConfig.id)) { return this.get(windowConfig.id); } // Returns an OverlayRef which is a PortalHost const overlayRef = this.createOverlay(windowConfig); // Instantiate remote control const windowRef = new WindowRef(overlayRef, this.overlay, windowConfig); const context = { id: windowConfig.id, overlayRef, windowRef, data: windowConfig.data, }; const containerContext = { template: windowConfig.template, component: windowConfig.component, }; const injector = Injector.create({ parent: windowConfig.injector ?? this.injector, providers: [ { provide: RXAP_WINDOW_CONTAINER_CONTEXT, useValue: containerContext, }, { provide: RXAP_WINDOW_CONTEXT, useValue: context, }, { provide: RXAP_WINDOW_DATA, useValue: windowConfig.data, }, { provide: RXAP_WINDOW_REF, useValue: windowRef, }, { provide: LoadingIndicatorService, useClass: LoadingIndicatorService, }, ], name: windowConfig?.injectorName ?? 'WindowService', }); const containerPortal = new ComponentPortal(windowConfig.windowComponent ?? DefaultWindowComponent, injector.get(ViewContainerRef, null, InjectFlags.Optional) ?? windowConfig.viewContainerRef ?? null, injector, injector.get(ComponentFactoryResolver, null, InjectFlags.Optional) ?? windowConfig.componentFactoryResolver ?? this.componentFactoryResolver); overlayRef.attach(containerPortal); overlayRef .backdropClick() .pipe(take(1), tap(() => windowRef.complete())) .subscribe(); windowRef.pipe(finalize(() => this.remove(windowConfig.id))).subscribe(); this.add(windowRef); return windowRef; } remove(id) { const result = this.active.delete(id); this.activeCount$.next(this.active.size); return result; } add(windowRef) { this.active.set(windowRef.id, windowRef); this.activeCount$.next(this.active.size); } createOverlay({ panelClass, width, height, minHeight, minWidth, maxHeight, maxWidth, }) { const startPos = GetWindowStartPos(); const positionStrategy = this.overlay .position() .global() .top(startPos.top) .left(startPos.left); const overlayConfig = new OverlayConfig({ hasBackdrop: false, panelClass, scrollStrategy: this.overlay.scrollStrategies.block(), width, height, maxWidth: maxWidth ?? this.defaultWindowSettings?.maxWidth ?? '100vw', maxHeight: maxHeight ?? this.defaultWindowSettings?.maxHeight ?? '100vh', minWidth: minWidth ?? this.defaultWindowSettings?.minWidth ?? '384px', minHeight: minHeight ?? this.defaultWindowSettings?.minHeight ?? '192px', positionStrategy, }); return this.overlay.create(overlayConfig); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: WindowService, deps: [{ token: Injector }, { token: Overlay }, { token: ComponentFactoryResolver }, { token: RXAP_WINDOW_DEFAULT_SETTINGS, optional: true }], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: WindowService, providedIn: 'root' }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: WindowService, decorators: [{ type: Injectable, args: [{ providedIn: 'root' }] }], ctorParameters: () => [{ type: i0.Injector, decorators: [{ type: Inject, args: [Injector] }] }, { type: i1$3.Overlay, decorators: [{ type: Inject, args: [Overlay] }] }, { type: i0.ComponentFactoryResolver, decorators: [{ type: Inject, args: [ComponentFactoryResolver] }] }, { type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [RXAP_WINDOW_DEFAULT_SETTINGS] }] }] }); class WindowTaskComponent { close() { this.window.complete(); } reopen() { this.window.reopen(); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: WindowTaskComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.1", type: WindowTaskComponent, isStandalone: true, selector: "rxap-window-task", inputs: { window: "window" }, ngImport: i0, template: "<div (click)=\"reopen()\" [ngClass]=\"{ minimized: window.isMinimized }\"\n class=\"rxap-container mat-elevation-z2 flex flex-row justify-start items-center gap-2\">\n <ng-template [ngIf]=\"window.settings$ | async\" let-settings>\n <mat-icon *ngIf=\"settings.icon\" [rxapIcon]=\"settings.icon\"></mat-icon>\n <h3 *ngIf=\"settings.title\" class=\"grow\">{{settings.title}}</h3>\n </ng-template>\n <button (click)=\"close()\" mat-icon-button rxapStopPropagation type=\"button\">\n <mat-icon>close</mat-icon>\n </button>\n</div>\n", styles: [".rxap-container{padding:5px;cursor:pointer}.rxap-container.minimized{opacity:.5}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i1$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: IconDirective, selector: "mat-icon[rxapIcon]", inputs: ["rxapIcon"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i4.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "directive", type: StopPropagationDirective, selector: "[rxapStopPropagation]" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: WindowTaskComponent, decorators: [{ type: Component, args: [{ selector: 'rxap-window-task', changeDetection: ChangeDetectionStrategy.OnPush, imports: [ NgClass, NgIf, MatIconModule, IconDirective, MatButtonModule, StopPropagationDirective, AsyncPipe, ], template: "<div (click)=\"reopen()\" [ngClass]=\"{ minimized: window.isMinimized }\"\n class=\"rxap-container mat-elevation-z2 flex flex-row justify-start items-center gap-2\">\n <ng-template [ngIf]=\"window.settings$ | async\" let-settings>\n <mat-icon *ngIf=\"settings.icon\" [rxapIcon]=\"settings.icon\"></mat-icon>\n <h3 *ngIf=\"settings.title\" class=\"grow\">{{settings.title}}</h3>\n </ng-template>\n <button (click)=\"close()\" mat-icon-button rxapStopPropagation type=\"button\">\n <mat-icon>close</mat-icon>\n </button>\n</div>\n", styles: [".rxap-container{padding:5px;cursor:pointer}.rxap-container.minimized{opacity:.5}\n"] }] }], propDecorators: { window: [{ type: Input, args: [{ required: true }] }] } }); class WindowTaskBarComponent { constructor(windowService, cdr) { this.windowService = windowService; this.cdr = cdr; this.subscriptions = new Subscription(); } ngOnInit() { this.subscriptions.add(this.windowService.activeCount$.pipe(tap(() => this.cdr.markForCheck())).subscribe()); } ngOnDestroy() { this.subscriptions.unsubscribe(); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: WindowTaskBarComponent, deps: [{ token: WindowService }, { token: ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.1", type: WindowTaskBarComponent, isStandalone: true, selector: "rxap-window-task-bar", ngImport: i0, template: "<div class=\"tasks grow-0 flex flex-row flex-wrap\">\n <rxap-window-task *ngFor=\"let window of windowService.allActiveWindows\"\n [window]=\"window\"\n class=\"grow-0\">\n </rxap-window-task>\n</div>\n", styles: [""], dependencies: [{ kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "component", type: WindowTaskComponent, selector: "rxap-window-task", inputs: ["window"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: WindowTaskBarComponent, decorators: [{ type: Component, args: [{ selector: 'rxap-window-task-bar', changeDetection: ChangeDetectionStrategy.OnPush, imports: [NgFor, WindowTaskComponent], template: "<div class=\"tasks grow-0 flex flex-row flex-wrap\">\n <rxap-window-task *ngFor=\"let window of windowService.allActiveWindows\"\n [window]=\"window\"\n class=\"grow-0\">\n </rxap-window-task>\n</div>\n" }] }], ctorParameters: () => [{ type: WindowService, decorators: [{ type: Inject, args: [WindowService] }] }, { type: i0.ChangeDetectorRef, decorators: [{ type: Inject, args: [ChangeDetectorRef] }] }] }); const RXAP_WINDOW_TASK_BAR_CONTAINER_SETTINGS = new InjectionToken('rxap/window-system/task-bar-container'); class WindowTaskBarContainerComponent { constructor(windowService, settings = null) { this.windowService = windowService; this.expand = true; if (settings) { if (settings.expand !== undefined) { this.expand = settings.expand; } } } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: WindowTaskBarContainerComponent, deps: [{ token: WindowService }, { token: RXAP_WINDOW_TASK_BAR_CONTAINER_SETTINGS, optional: true }], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.1", type: WindowTaskBarContainerComponent, isStandalone: true, selector: "rxap-window-task-bar-container", ngImport: i0, template: "<div *ngIf=\"windowService.hasActiveWindows\" class=\"rxap-container flex flex-col\">\n <div class=\"flex flex-row justify-end items-end\">\n <button (click)=\"expand = !expand\"\n [matBadgeHidden]=\"!windowService.hasActiveWindows\"\n [matBadge]=\"windowService.activeWindowCount.toFixed(0)\"\n class=\"pin\"\n color=\"primary\"\n mat-icon-button\n matBadgePosition=\"above before\"\n type=\"button\">\n <mat-icon *ngIf=\"!expand\">keyboard_arrow_up</mat-icon>\n <mat-icon *ngIf=\"expand\">keyboard_arrow_down</mat-icon>\n </button>\n </div>\n <mat-toolbar [ngClass]=\"{ hidden: !expand }\" class=\"tasks\">\n <rxap-window-task-bar></rxap-window-task-bar>\n </mat-toolbar>\n</div>\n", styles: [".rxap-container .pin{border:none;border-radius:50% 50% 0 0;cursor:pointer}.rxap-container .tasks{height:auto;padding:16px}\n"], dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i4.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "ngmodule", type: MatBadgeModule }, { kind: "directive", type: i2$1.MatBadge, selector: "[matBadge]", inputs: ["matBadgeColor", "matBadgeOverlap", "matBadgeDisabled", "matBadgePosition", "matBadge", "matBadgeDescription", "matBadgeSize", "matBadgeHidden"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i1$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatToolbarModule }, { kind: "component", type: i1.MatToolbar, selector: "mat-toolbar", inputs: ["color"], exportAs: ["matToolbar"] }, { kind: "component", type: WindowTaskBarComponent, selector: "rxap-window-task-bar" }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }] }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: WindowTaskBarContainerComponent, decorators: [{ type: Component, args: [{ selector: 'rxap-window-task-bar-container', imports: [ NgIf, MatButtonModule, MatBadgeModule, MatIconModule, MatToolbarModule, WindowTaskBarComponent, NgClass, ], template: "<div *ngIf=\"windowService.hasActiveWindows\" class=\"rxap-container flex flex-col\">\n <div class=\"flex flex-row justify-end items-end\">\n <button (click)=\"expand = !expand\"\n [matBadgeHidden]=\"!windowService.hasActiveWindows\"\n [matBadge]=\"windowService.activeWindowCount.toFixed(0)\"\n class=\"pin\"\n color=\"primary\"\n mat-icon-button\n matBadgePosition=\"above before\"\n type=\"button\">\n <mat-icon *ngIf=\"!expand\">keyboard_arrow_up</mat-icon>\n <mat-icon *ngIf=\"expand\">keyboard_arrow_down</mat-icon>\n </button>\n </div>\n <mat-toolbar [ngClass]=\"{ hidden: !expand }\" class=\"tasks\">\n <rxap-window-task-bar></rxap-window-task-bar>\n </mat-toolbar>\n</div>\n", styles: [".rxap-container .pin{border:none;border-radius:50% 50% 0 0;cursor:pointer}.rxap-container .tasks{height:auto;padding:16px}\n"] }] }], ctorParameters: () => [{ type: WindowService, decorators: [{ type: Inject, args: [WindowService] }] }, { type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [RXAP_WINDOW_TASK_BAR_CONTAINER_SETTINGS] }] }] }); class WindowTaskBarModule { static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: WindowTaskBarModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); } static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.1", ngImport: i0, type: WindowTaskBarModule, imports: [WindowTaskBarComponent, WindowTaskComponent, WindowTaskBarContainerComponent], exports: [WindowTaskBarComponent, WindowTaskBarContainerComponent] }); } static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: WindowTaskBarModule, imports: [WindowTaskBarComponent, WindowTaskComponent, WindowTaskBarContainerComponent] }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: WindowTaskBarModule, decorators: [{ type: NgModule, args: [{ imports: [ WindowTaskBarComponent, WindowTaskComponent, WindowTaskBarContainerComponent, ], exports: [WindowTaskBarComponent, WindowTaskBarContainerComponent], }] }] }); class WindowActionBarComponent { constructor(context, injector) { this.injector = injector; this.definitions = null; this.position = 'start'; if (context.windowRef.settings.actions) { this.definitions = context.windowRef.settings.actions.definitions; this.position = context.windowRef.settings.actions.position || this.position; } } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.1", ngImport: i0, type: WindowActionBarComponent, deps: [{ token: RXAP_WINDOW_CONTEXT }, { token: i0.Injector }], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.1", type: WindowActionBarComponent, isStandalone: true, selector: "rxap-window-action-bar", ngImport: i0, template: "<mat-toolbar-row *ngIf=\"definitions\" class=\"p-2 flex flex-row gap-2 justify-{{position}} items-center\">\n <span>this feature is removed</span>\n</mat-toolbar-row>\n", st