diff --git a/src/atlasViewer/atlasViewer.component.ts b/src/atlasViewer/atlasViewer.component.ts index a781efcb72a4905395bcb4852804bfbafc9765f7..59d7751b8340a5bfa5004369d4f3adeebadfae4a 100644 --- a/src/atlasViewer/atlasViewer.component.ts +++ b/src/atlasViewer/atlasViewer.component.ts @@ -1,4 +1,4 @@ -import { Component, HostBinding, ViewChild, ViewContainerRef, ComponentFactoryResolver, ComponentFactory, OnDestroy, ElementRef, ComponentRef, AfterViewInit, OnInit, HostListener, Renderer2, TemplateRef } from "@angular/core"; +import { Component, HostBinding, ViewChild, ViewContainerRef, OnDestroy, ElementRef, OnInit, HostListener, TemplateRef } from "@angular/core"; import { Store, select } from "@ngrx/store"; import { ViewerStateInterface, isDefined, FETCHED_SPATIAL_DATA, UPDATE_SPATIAL_DATA, TOGGLE_SIDE_PANEL, safeFilter } from "../services/stateStore.service"; import { Observable, Subscription, combineLatest } from "rxjs"; @@ -7,12 +7,11 @@ import { AtlasViewerDataService } from "./atlasViewer.dataService.service"; import { WidgetServices } from "./widgetUnit/widgetService.service"; import { LayoutMainSide } from "../layouts/mainside/mainside.component"; import { Chart } from 'chart.js' -import { AtlasViewerConstantsServices, SUPPORT_LIBRARY_MAP } from "./atlasViewer.constantService.service"; +import { AtlasViewerConstantsServices } from "./atlasViewer.constantService.service"; import { BsModalService } from "ngx-bootstrap/modal"; import { ModalUnit } from "./modalUnit/modalUnit.component"; import { AtlasViewerURLService } from "./atlasViewer.urlService.service"; import { AtlasViewerAPIServices } from "./atlasViewer.apiService.service"; -import { PluginServices } from "./atlasViewer.pluginService.service"; import '../res/css/extra_styles.css' import { NehubaContainer } from "../ui/nehubaContainer/nehubaContainer.component"; @@ -29,19 +28,18 @@ import { colorAnimation } from "./atlasViewer.animation" ] }) -export class AtlasViewer implements OnDestroy, OnInit, AfterViewInit { +export class AtlasViewer implements OnDestroy, OnInit { - @ViewChild('dockedContainer', { read: ViewContainerRef }) dockedContainer: ViewContainerRef - @ViewChild('floatingContainer', { read: ViewContainerRef }) floatingContainer: ViewContainerRef @ViewChild('databrowser', { read: ElementRef }) databrowser: ElementRef - @ViewChild('toastContainer', { read: ViewContainerRef }) toastContainer: ViewContainerRef @ViewChild('floatingMouseContextualContainer', { read: ViewContainerRef }) floatingMouseContextualContainer: ViewContainerRef - @ViewChild('pluginFactory', { read: ViewContainerRef }) pluginViewContainerRef: ViewContainerRef @ViewChild('helpComponent', {read: TemplateRef}) helpComponent : TemplateRef<any> @ViewChild(LayoutMainSide) layoutMainSide: LayoutMainSide @ViewChild(NehubaContainer) nehubaContainer: NehubaContainer + /** + * required for styling of all child components + */ @HostBinding('attr.darktheme') darktheme: boolean = false @@ -51,7 +49,6 @@ export class AtlasViewer implements OnDestroy, OnInit, AfterViewInit { private newViewer$: Observable<any> public selectedPOI$ : Observable<any[]> - public showHelp$: Observable<any> public dedicatedView$: Observable<string | null> @@ -72,8 +69,6 @@ export class AtlasViewer implements OnDestroy, OnInit, AfterViewInit { } constructor( - private pluginService: PluginServices, - private rd2: Renderer2, private store: Store<ViewerStateInterface>, public dataService: AtlasViewerDataService, private widgetServices: WidgetServices, @@ -174,59 +169,6 @@ export class AtlasViewer implements OnDestroy, OnInit, AfterViewInit { } ngOnInit() { - - this.apiService.interactiveViewer.pluginControl.loadExternalLibraries = (libraries: string[]) => new Promise((resolve, reject) => { - const srcHTMLElement = libraries.map(libraryName => ({ - name: libraryName, - srcEl: SUPPORT_LIBRARY_MAP.get(libraryName) - })) - - const rejected = srcHTMLElement.filter(scriptObj => scriptObj.srcEl === null) - if (rejected.length > 0) - return reject(`Some library names cannot be recognised. No libraries were loaded: ${rejected.map(srcObj => srcObj.name).join(', ')}`) - - Promise.all(srcHTMLElement.map(scriptObj => new Promise((rs, rj) => { - if('customElements' in window && scriptObj.name === 'webcomponentsLite'){ - return rs() - } - const existingEntry = this.apiService.loadedLibraries.get(scriptObj.name) - if (existingEntry) { - this.apiService.loadedLibraries.set(scriptObj.name, { counter: existingEntry.counter + 1, src: existingEntry.src }) - rs() - } else { - const srcEl = scriptObj.srcEl - srcEl.onload = () => rs() - srcEl.onerror = (e: any) => rj(e) - this.rd2.appendChild(document.head, srcEl) - this.apiService.loadedLibraries.set(scriptObj.name, { counter: 1, src: srcEl }) - } - }))) - .then(() => resolve()) - .catch(e => (console.warn(e), reject(e))) - }) - - this.apiService.interactiveViewer.pluginControl.unloadExternalLibraries = (libraries: string[]) => - libraries - .filter((stringname) => SUPPORT_LIBRARY_MAP.get(stringname) !== null) - .forEach(libname => { - const ledger = this.apiService.loadedLibraries.get(libname!) - if (!ledger) { - console.warn('unload external libraries error. cannot find ledger entry...', libname, this.apiService.loadedLibraries) - return - } - if (ledger.src === null) { - console.log('webcomponents is native supported. no library needs to be unloaded') - return - } - - if (ledger.counter - 1 == 0) { - this.rd2.removeChild(document.head, ledger.src) - this.apiService.loadedLibraries.delete(libname!) - } else { - this.apiService.loadedLibraries.set(libname!, { counter: ledger.counter - 1, src: ledger.src }) - } - }) - this.meetsRequirement = this.meetsRequirements() this.subscriptions.push( @@ -339,18 +281,13 @@ export class AtlasViewer implements OnDestroy, OnInit, AfterViewInit { }) } + /** + * For completeness sake. Root element should never be destroyed. + */ ngOnDestroy() { this.subscriptions.forEach(s => s.unsubscribe()) } - ngAfterViewInit() { - this.widgetServices.floatingContainer = this.floatingContainer - this.widgetServices.dockedContainer = this.dockedContainer - - this.pluginService.pluginViewContainerRef = this.pluginViewContainerRef - this.pluginService.appendSrc = (src: HTMLElement) => this.rd2.appendChild(document.head, src) - } - /** * perhaps move this to constructor? */ @@ -358,9 +295,6 @@ export class AtlasViewer implements OnDestroy, OnInit, AfterViewInit { const canvas = document.createElement('canvas') const gl = canvas.getContext('webgl2') as WebGLRenderingContext - const message: any = { - Error: this.constantsService.minReqModalHeader - } if (!gl) { return false @@ -407,20 +341,9 @@ export class AtlasViewer implements OnDestroy, OnInit, AfterViewInit { }) } - mousePos: [number, number] = [0, 0] - - @HostListener('mousemove', ['$event']) - mousemove(event: MouseEvent) { - this.mousePos = [event.clientX, event.clientY] - } - @HostBinding('attr.version') public _version : string = VERSION - get floatingMouseContextualContainerTransform() { - return `translate(${this.mousePos[0]}px,${this.mousePos[1]}px)` - } - get isMobile(){ return this.constantsService.mobile } diff --git a/src/atlasViewer/atlasViewer.template.html b/src/atlasViewer/atlasViewer.template.html index 9cbaa9666527c4dedab2b2a732d08082feb3002f..04f4b488d98fe01ee01a4995318e2a49067c9e7c 100644 --- a/src/atlasViewer/atlasViewer.template.html +++ b/src/atlasViewer/atlasViewer.template.html @@ -74,7 +74,10 @@ </div> <!-- Docked Container--> - <div dockedContainer #dockedContainer> + <div dockedContainer> + <div dockedContainerDirective> + + </div> </div> </div> @@ -148,10 +151,10 @@ <layout-floating-container zIndex = "9" #floatingOverlayContainer> - <ng-template #floatingContainer> + <div floatingContainerDirective> - </ng-template> - <div [style.transform] = "floatingMouseContextualContainerTransform" floatingMouseContextualContainer> + </div> + <div floatingMouseContextualContainer floatingMouseContextualContainerDirective> <div *ngIf = "onhoverLandmark$ | async" contextualBlock> @@ -170,8 +173,9 @@ </div> </layout-floating-container> - <ng-template #pluginFactory> - </ng-template> + <div pluginFactoryDirective> + + </div> </div> diff --git a/src/main.module.ts b/src/main.module.ts index 5dfe69c06548a3f5719791c6c31b7010de945b6e..e04add7c374f89020a29e6d011139fd397433b64 100644 --- a/src/main.module.ts +++ b/src/main.module.ts @@ -28,6 +28,10 @@ import { ToastService } from "./services/toastService.service"; import { AtlasWorkerService } from "./atlasViewer/atlasViewer.workerService.service"; import { HelpDirective } from "./util/directives/help.directive"; import { ToastContainerDirective } from "./util/directives/toastContainer.directive"; +import { DockedContainerDirective } from "./util/directives/dockedContainer.directive"; +import { FloatingContainerDirective } from "./util/directives/floatingContainer.directive"; +import { PluginFactoryDirective } from "./util/directives/pluginFactory.directive"; +import { FloatingMouseContextualContainerDirective } from "./util/directives/floatingMouseContextualContainer.directive"; @NgModule({ imports : [ @@ -64,6 +68,10 @@ import { ToastContainerDirective } from "./util/directives/toastContainer.direct GlyphiconTooltipRemoveSignDirective, HelpDirective, ToastContainerDirective, + DockedContainerDirective, + FloatingContainerDirective, + PluginFactoryDirective, + FloatingMouseContextualContainerDirective, /* pipes */ GetNamesPipe, diff --git a/src/util/directives/dockedContainer.directive.ts b/src/util/directives/dockedContainer.directive.ts new file mode 100644 index 0000000000000000000000000000000000000000..741fa61fd3d190d1320292b4ed955919938a1749 --- /dev/null +++ b/src/util/directives/dockedContainer.directive.ts @@ -0,0 +1,16 @@ +import { Directive, ViewContainerRef } from "@angular/core"; +import { WidgetServices } from "src/atlasViewer/widgetUnit/widgetService.service"; + + +@Directive({ + selector: '[dockedContainerDirective]' +}) + +export class DockedContainerDirective{ + constructor( + widgetService: WidgetServices, + viewContainerRef: ViewContainerRef + ){ + widgetService.dockedContainer = viewContainerRef + } +} \ No newline at end of file diff --git a/src/util/directives/floatingContainer.directive.ts b/src/util/directives/floatingContainer.directive.ts new file mode 100644 index 0000000000000000000000000000000000000000..740ab815a6ebeb6a7ab1c365467136e5288daacd --- /dev/null +++ b/src/util/directives/floatingContainer.directive.ts @@ -0,0 +1,15 @@ +import { Directive, ViewContainerRef } from "@angular/core"; +import { WidgetServices } from "src/atlasViewer/widgetUnit/widgetService.service"; + +@Directive({ + selector: '[floatingContainerDirective]' +}) + +export class FloatingContainerDirective{ + constructor( + widgetService: WidgetServices, + viewContainerRef: ViewContainerRef + ){ + widgetService.floatingContainer = viewContainerRef + } +} \ No newline at end of file diff --git a/src/util/directives/floatingMouseContextualContainer.directive.ts b/src/util/directives/floatingMouseContextualContainer.directive.ts new file mode 100644 index 0000000000000000000000000000000000000000..2f62b11aac7311055c9023ba1c32e83077807bbb --- /dev/null +++ b/src/util/directives/floatingMouseContextualContainer.directive.ts @@ -0,0 +1,20 @@ +import { Directive, HostListener, HostBinding } from "@angular/core"; + +@Directive({ + selector: '[floatingMouseContextualContainerDirective]' +}) + +export class FloatingMouseContextualContainerDirective{ + + private mousePos: [number, number] = [0, 0] + + @HostListener('document:mousemove', ['$event']) + mousemove(event:MouseEvent){ + this.mousePos = [event.clientX, event.clientY] + } + + @HostBinding('style.transform') + get transform(){ + return `translate(${this.mousePos[0]}px,${this.mousePos[1]}px)` + } +} \ No newline at end of file diff --git a/src/util/directives/pluginFactory.directive.ts b/src/util/directives/pluginFactory.directive.ts new file mode 100644 index 0000000000000000000000000000000000000000..87a843b8f98a5d2dc2f51cc4505ac3bb560677fc --- /dev/null +++ b/src/util/directives/pluginFactory.directive.ts @@ -0,0 +1,75 @@ +import { Directive, ViewContainerRef, Renderer2 } from "@angular/core"; +import { PluginServices } from "src/atlasViewer/atlasViewer.pluginService.service"; +import { AtlasViewerAPIServices } from "src/atlasViewer/atlasViewer.apiService.service"; +import { SUPPORT_LIBRARY_MAP } from "src/atlasViewer/atlasViewer.constantService.service"; + +@Directive({ + selector: '[pluginFactoryDirective]' +}) + +export class PluginFactoryDirective{ + constructor( + pluginService: PluginServices, + viewContainerRef: ViewContainerRef, + rd2: Renderer2, + apiService:AtlasViewerAPIServices + ){ + pluginService.pluginViewContainerRef = viewContainerRef + pluginService.appendSrc = (src: HTMLElement) => rd2.appendChild(document.head, src) + + apiService.interactiveViewer.pluginControl.loadExternalLibraries = (libraries: string[]) => new Promise((resolve, reject) => { + const srcHTMLElement = libraries.map(libraryName => ({ + name: libraryName, + srcEl: SUPPORT_LIBRARY_MAP.get(libraryName) + })) + + const rejected = srcHTMLElement.filter(scriptObj => scriptObj.srcEl === null) + if (rejected.length > 0) + return reject(`Some library names cannot be recognised. No libraries were loaded: ${rejected.map(srcObj => srcObj.name).join(', ')}`) + + Promise.all(srcHTMLElement.map(scriptObj => new Promise((rs, rj) => { + /** + * if browser already support customElements, do not append polyfill + */ + if('customElements' in window && scriptObj.name === 'webcomponentsLite'){ + return rs() + } + const existingEntry = apiService.loadedLibraries.get(scriptObj.name) + if (existingEntry) { + apiService.loadedLibraries.set(scriptObj.name, { counter: existingEntry.counter + 1, src: existingEntry.src }) + rs() + } else { + const srcEl = scriptObj.srcEl + srcEl.onload = () => rs() + srcEl.onerror = (e: any) => rj(e) + rd2.appendChild(document.head, srcEl) + apiService.loadedLibraries.set(scriptObj.name, { counter: 1, src: srcEl }) + } + }))) + .then(() => resolve()) + .catch(e => (console.warn(e), reject(e))) + }) + + apiService.interactiveViewer.pluginControl.unloadExternalLibraries = (libraries: string[]) => + libraries + .filter((stringname) => SUPPORT_LIBRARY_MAP.get(stringname) !== null) + .forEach(libname => { + const ledger = apiService.loadedLibraries.get(libname!) + if (!ledger) { + console.warn('unload external libraries error. cannot find ledger entry...', libname, apiService.loadedLibraries) + return + } + if (ledger.src === null) { + console.log('webcomponents is native supported. no library needs to be unloaded') + return + } + + if (ledger.counter - 1 == 0) { + rd2.removeChild(document.head, ledger.src) + apiService.loadedLibraries.delete(libname!) + } else { + apiService.loadedLibraries.set(libname!, { counter: ledger.counter - 1, src: ledger.src }) + } + }) + } +} \ No newline at end of file