diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000000000000000000000000000000000000..7073e92f60c04685964761920c916969cee8c876 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,17 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "node", + "request": "launch", + "name": "Launch Program", + "program": "${workspaceFolder}/src/ui/databrowserModule/databrowser.service.ts", + "outFiles": [ + "${workspaceFolder}/**/*.js" + ] + } + ] +} \ No newline at end of file diff --git a/package.json b/package.json index cc9f3cb40799d0a003cb80d65087ecc980f51cdc..b49118e6391468c7484a242ccf1757fdb1c90abd 100644 --- a/package.json +++ b/package.json @@ -79,6 +79,7 @@ "@angular/cdk": "^7.3.7", "@angular/material": "^7.3.7", "@angular/router": "^7.2.15", + "hammerjs": "^2.0.8", "zone.js": "^0.9.1" } } diff --git a/src/atlasViewer/atlasViewer.component.ts b/src/atlasViewer/atlasViewer.component.ts index 529491df7a003c24bb0d2ffb8ecc238907922edb..65251c14b234e51572186b9d2f3a4b068f0c4468 100644 --- a/src/atlasViewer/atlasViewer.component.ts +++ b/src/atlasViewer/atlasViewer.component.ts @@ -1,6 +1,6 @@ import { Component, HostBinding, ViewChild, ViewContainerRef, OnDestroy, OnInit, TemplateRef, AfterViewInit } from "@angular/core"; import { Store, select, ActionsSubject } from "@ngrx/store"; -import { ViewerStateInterface, isDefined, FETCHED_SPATIAL_DATA, UPDATE_SPATIAL_DATA, TOGGLE_SIDE_PANEL, safeFilter } from "../services/stateStore.service"; +import { ViewerStateInterface, isDefined, FETCHED_SPATIAL_DATA, UPDATE_SPATIAL_DATA, TOGGLE_SIDE_PANEL, safeFilter, UIStateInterface, OPEN_SIDE_PANEL, CLOSE_SIDE_PANEL } from "../services/stateStore.service"; import { Observable, Subscription, combineLatest, interval, merge, of } from "rxjs"; import { map, filter, distinctUntilChanged, delay, concatMap, debounceTime, withLatestFrom } from "rxjs/operators"; import { AtlasViewerDataService } from "./atlasViewer.dataService.service"; @@ -38,6 +38,7 @@ export class AtlasViewer implements OnDestroy, OnInit, AfterViewInit { @ViewChild('cookieAgreementComponent', {read: TemplateRef}) cookieAgreementComponent : TemplateRef<any> @ViewChild('kgToS', {read: TemplateRef}) kgTosComponent: TemplateRef<any> @ViewChild(LayoutMainSide) layoutMainSide: LayoutMainSide + @ViewChild('dockedContainer', {read: ViewContainerRef}) dockedContainer: ViewContainerRef @ViewChild(NehubaContainer) nehubaContainer: NehubaContainer @@ -74,12 +75,16 @@ export class AtlasViewer implements OnDestroy, OnInit, AfterViewInit { public unsupportedPreviewIdx: number = 0 public unsupportedPreviews: any[] = UNSUPPORTED_PREVIEW + public sidePanelOpen$: Observable<boolean> + mobileMenuOpened: boolean = false; + get toggleMessage(){ return this.constantsService.toggleMessage } constructor( private store: Store<ViewerStateInterface>, + private uiStore: Store<UIStateInterface>, public dataService: AtlasViewerDataService, private widgetServices: WidgetServices, private constantsService: AtlasViewerConstantsServices, @@ -103,6 +108,12 @@ export class AtlasViewer implements OnDestroy, OnInit, AfterViewInit { map(state => state.focusedSidePanel) ) + this.sidePanelOpen$ = this.store.pipe( + select('uiState'), + filter(state => isDefined(state)), + map(state => state.sidePanelOpen) + ) + this.showHelp$ = this.constantsService.showHelpSubject$.pipe( debounceTime(170) ) @@ -280,6 +291,11 @@ export class AtlasViewer implements OnDestroy, OnInit, AfterViewInit { ).subscribe(v => this.layoutMainSide.showSide = isDefined(v)) ) + this.subscriptions.push( + this.sidePanelOpen$ + .subscribe(v => this.mobileMenuOpened = v) + ) + } ngAfterViewInit() { @@ -321,6 +337,7 @@ export class AtlasViewer implements OnDestroy, OnInit, AfterViewInit { } }) }) + this.widgetServices.dockedContainer = this.dockedContainer this.onhoverSegmentForFixed$ = this.rClContextualMenu.onShow.pipe( withLatestFrom(this.onhoverSegment$), @@ -415,6 +432,12 @@ export class AtlasViewer implements OnDestroy, OnInit, AfterViewInit { searchRegion(regions:any[]){ this.rClContextualMenu.hide() this.databrowserService.queryData({ regions, parcellation: this.selectedParcellation, template: this.selectedTemplate }) + // if (this.isMobile) this.mobileMenuOpened = !this.mobileMenuOpened + if (this.isMobile) { + this.uiStore.dispatch({ + type : OPEN_SIDE_PANEL + }) + } } @HostBinding('attr.version') @@ -423,6 +446,18 @@ export class AtlasViewer implements OnDestroy, OnInit, AfterViewInit { get isMobile(){ return this.constantsService.mobile } + + changeMenuState() { + if (this.mobileMenuOpened) { + this.uiStore.dispatch({ + type : CLOSE_SIDE_PANEL + }) + } else { + this.uiStore.dispatch({ + type : OPEN_SIDE_PANEL + }) + } + } } export interface NgLayerInterface{ diff --git a/src/atlasViewer/atlasViewer.style.css b/src/atlasViewer/atlasViewer.style.css index 69d373af673f859e2cea2e3cece647aaea4c9b11..6b62d804f2d8f125f2b07dfb0aab0c39b945bdec 100644 --- a/src/atlasViewer/atlasViewer.style.css +++ b/src/atlasViewer/atlasViewer.style.css @@ -23,7 +23,7 @@ ui-nehuba-container div[bannerWrapper] { - z-index:5; + z-index:1000; position:absolute; width:100%; @@ -150,4 +150,28 @@ div[imageContainer] div.displayCard { opacity: 0.8; +} + +.mobileNavigationPanel { + width: calc(100% - 50px); + background-color: white; + left: 0; + padding: 20px 5px; +} + +.menuButtonMobile { + position: absolute; + top: 10px; + right: 10px; +} + +.logoContainerMobile { + width: 100%; + display:flex; + justify-content: center; +} + +mat-card { + box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23); + margin: 5px; } \ No newline at end of file diff --git a/src/atlasViewer/atlasViewer.template.html b/src/atlasViewer/atlasViewer.template.html index 8fd332b78717921d6d4e1b3a89528fab60a9a905..cd9a06e2ee68126bde5256f97f5aa7df69c590be 100644 --- a/src/atlasViewer/atlasViewer.template.html +++ b/src/atlasViewer/atlasViewer.template.html @@ -4,10 +4,46 @@ </ui-nehuba-container> <div bannerWrapper> - <menu-icons iconWrapper> + <menu-icons *ngIf ="!isMobile" iconWrapper> </menu-icons> - <signin-banner signinWrapper> + <signin-banner *ngIf ="!isMobile" signinWrapper> </signin-banner> + + <div *ngIf ="isMobile" > + <mat-sidenav-container> + <mat-sidenav #sidenav + [fixedInViewport]="true" + [(opened)]="mobileMenuOpened" + class="mobileNavigationPanel" + [ngStyle]="{'background-color': darktheme? 'black' : 'white'}" > + <logo-container class="logoContainerMobile"></logo-container> + <mat-card> + <mat-card-title [ngStyle]="{'color': darktheme? '#D7D7D7' : 'black'}" >Template Selection</mat-card-title> + <signin-banner [darktheme] = "darktheme" signinWrapper></signin-banner> + </mat-card> + + <mat-card> + <mat-card-title [ngStyle]="{'color': darktheme? '#D7D7D7' : 'black'}">Panel Containers</mat-card-title> + <menu-icons iconWrapper hidden #MenuIcons> + </menu-icons> + <ng-template #dockedContainer> + </ng-template> + </mat-card> + </mat-sidenav> + + <mat-sidenav-content> + </mat-sidenav-content> + + </mat-sidenav-container> + <div class="btnWrapper btnWrapper-lg menuButtonMobile" (click)="changeMenuState()"> + <div + class="shadow btn btn-sm btn-outline-secondary rounded-circle"> + <i class="fas fa-bars"></i> + </div> + </div> + </div> + + </div> <layout-floating-container diff --git a/src/atlasViewer/widgetUnit/widgetUnit.component.ts b/src/atlasViewer/widgetUnit/widgetUnit.component.ts index e4f112439a75a831707cfa8aa6fe6ceb5301f0bc..fc32210cd0774a8b1b75126c25f9e10b2de9195a 100644 --- a/src/atlasViewer/widgetUnit/widgetUnit.component.ts +++ b/src/atlasViewer/widgetUnit/widgetUnit.component.ts @@ -1,5 +1,6 @@ import { Component, ViewChild, ViewContainerRef,ComponentRef, HostBinding, HostListener, Output, EventEmitter, Input, ElementRef, OnInit } from "@angular/core"; import { WidgetServices } from "./widgetService.service"; +import { AtlasViewerConstantsServices } from "../atlasViewer.constantService.service"; @Component({ @@ -60,7 +61,9 @@ export class WidgetUnit implements OnInit{ public cf : ComponentRef<WidgetUnit> public id: string - constructor(){ + constructor( + private constantsService: AtlasViewerConstantsServices + ){ this.id = Date.now().toString() } @@ -121,4 +124,8 @@ export class WidgetUnit implements OnInit{ /* floating widget specific functionalities */ position : [number,number] = [400,100] + + get isMobile(){ + return this.constantsService.mobile + } } \ No newline at end of file diff --git a/src/atlasViewer/widgetUnit/widgetUnit.style.css b/src/atlasViewer/widgetUnit/widgetUnit.style.css index 4f7729b93e7880a942cc2a2425de40717ce4aa8c..10289f924763603c034766cd15e23a819cb0e857 100644 --- a/src/atlasViewer/widgetUnit/widgetUnit.style.css +++ b/src/atlasViewer/widgetUnit/widgetUnit.style.css @@ -34,7 +34,6 @@ panel-component[widgetUnitPanel] { display:block; min-width:280px; - max-width:300px; } :host-context([state='floating']) div[widgetUnitHeading]:hover diff --git a/src/atlasViewer/widgetUnit/widgetUnit.template.html b/src/atlasViewer/widgetUnit/widgetUnit.template.html index bc19ac9535b79282e1a85b3f5110f467818a5839..a7cfd51ef1f2b839190f4cb060b3c4038ee58270 100644 --- a/src/atlasViewer/widgetUnit/widgetUnit.template.html +++ b/src/atlasViewer/widgetUnit/widgetUnit.template.html @@ -4,7 +4,9 @@ widgetUnitPanel [bodyCollapsable] = "state === 'docked'" [cdkDragDisabled]="state === 'docked'" - cdkDrag> + cdkDrag + [ngStyle]="{'max-width': isMobile? '100%' : '300px', + 'margin-bottom': isMobile? '5px': '0'}"> <div widgetUnitHeading heading @@ -20,16 +22,17 @@ </div> <div icons> <i + *ngIf="!isMobile" (click)="widgetServices.minimise(this)" class="fas fa-window-minimize" hoverable> </i> - <i *ngIf = "canBeDocked && state === 'floating'" + <i *ngIf = "canBeDocked && state === 'floating' && !isMobile" (click) = "dock($event)" class = "fas fa-window-minimize" hoverable></i> - <i *ngIf = "state === 'docked'" + <i *ngIf = "state === 'docked' && !isMobile" (click) = "undock($event)" class = "fas fa-window-restore" hoverable></i> diff --git a/src/components/dropdown/dropdown.component.ts b/src/components/dropdown/dropdown.component.ts index 4e5e5cb4063fd91868d0c97f730c1a73c2c96a00..76528551240752f70f4b5dda1a4c42de608c1c34 100644 --- a/src/components/dropdown/dropdown.component.ts +++ b/src/components/dropdown/dropdown.component.ts @@ -21,6 +21,8 @@ export class DropdownComponent{ @Input() listDisplay : (obj:any)=>string = (obj)=>obj.name @Input() activeDisplay : (obj:any|null)=>string = (obj)=>obj ? obj.name : `Please select an item.` + @Input() isMobile: boolean; + @Output() itemSelected : EventEmitter<any> = new EventEmitter() @ViewChild('dropdownToggle',{read:ElementRef}) dropdownToggle : ElementRef diff --git a/src/components/dropdown/dropdown.template.html b/src/components/dropdown/dropdown.template.html index a228bc51b3a211b3d7d46aa4b89fa530fa225487..04208714899aa3df877aaa75cc8f5d56662892fe 100644 --- a/src/components/dropdown/dropdown.template.html +++ b/src/components/dropdown/dropdown.template.html @@ -18,6 +18,7 @@ [listDisplay]="listDisplay" [selectedItem]="selectedItem" [inputArray]="inputArray" - [@showState]="openState ? 'show' : 'hide'"> + [@showState]="openState ? 'show' : 'hide'" + [isMobile] = "isMobile"> </radio-list> diff --git a/src/components/panel/panel.component.ts b/src/components/panel/panel.component.ts index 04c869d4f252b04370473ead6f9eb8677eb43b41..ac73366ea7fac1a5709432c908a1ad3dc2ae94bc 100644 --- a/src/components/panel/panel.component.ts +++ b/src/components/panel/panel.component.ts @@ -64,6 +64,8 @@ export class PanelComponent extends ParseAttributeDirective implements AfterCont toggleCollapseBody(event:Event){ if(this.bodyCollapsable){ this.collapseBody = !this.collapseBody + this.showBody = !this.showBody + this.showFooter = !this.showFooter // this.fullHeight = (this.efPanelBody ? this.efPanelBody.nativeElement.offsetHeight : 0) + // (this.efPanelFooter ? this.efPanelFooter.nativeElement.offsetHeight : 0) diff --git a/src/components/radiolist/radiolist.component.ts b/src/components/radiolist/radiolist.component.ts index e9fbab060397fa5f5e8ab4c8bc46473665833e7f..1981f203ff022afd190d4ea5e53828209ca4c9eb 100644 --- a/src/components/radiolist/radiolist.component.ts +++ b/src/components/radiolist/radiolist.component.ts @@ -40,4 +40,6 @@ export class RadioList{ @Input() ulClass: string = '' + + @Input() isMobile: boolean; } \ No newline at end of file diff --git a/src/components/radiolist/radiolist.style.css b/src/components/radiolist/radiolist.style.css index 62f96ccb830eaa32b648f418bad0e2b257f1befc..d264f83b0676a602570939980e60c9abc93c2294 100644 --- a/src/components/radiolist/radiolist.style.css +++ b/src/components/radiolist/radiolist.style.css @@ -1,7 +1,7 @@ :host-context([darktheme="true"]) ul { - background-color:rgba(30,30,30,0.8); + background-color:rgba(30,30,30); } :host-context([darktheme="true"]) li[role="menuitem"] @@ -18,9 +18,8 @@ :host-context([darktheme="false"]) ul { - background-color:rgba(255,255,255,0.8); + background-color:rgba(255,255,255); } - :host-context([darktheme="false"]) li[role="menuitem"]:hover { cursor:default; diff --git a/src/components/radiolist/radiolist.template.html b/src/components/radiolist/radiolist.template.html index 170cbf0d950d945e22243e110b9138a745481f9e..608d628cb351121a68a3419eeaf882914d0ad692 100644 --- a/src/components/radiolist/radiolist.template.html +++ b/src/components/radiolist/radiolist.template.html @@ -1,5 +1,6 @@ <ul [ngClass]="ulClass" + [ngStyle]="{'opacity': isMobile? '1' : '0.8'}" role="menu"> <li *ngFor="let input of inputArray" diff --git a/src/services/state/ngViewerState.store.ts b/src/services/state/ngViewerState.store.ts index d4626cbffb8a0b504a2e0671a693acf5118abc5c..d1dd8b27a79daa1b2a73ad56df959cac71f87636 100644 --- a/src/services/state/ngViewerState.store.ts +++ b/src/services/state/ngViewerState.store.ts @@ -3,14 +3,18 @@ import { Action } from '@ngrx/store' export interface NgViewerStateInterface{ layers : NgLayerInterface[] forceShowSegment : boolean | null + nehubaReady: boolean } export interface NgViewerAction extends Action{ layer : NgLayerInterface forceShowSegment : boolean + nehubaReady: boolean } -export function ngViewerState(prevState:NgViewerStateInterface = {layers:[], forceShowSegment:null}, action:NgViewerAction):NgViewerStateInterface{ +const defaultState:NgViewerStateInterface = {layers:[], forceShowSegment:null, nehubaReady: false} + +export function ngViewerState(prevState:NgViewerStateInterface = defaultState, action:NgViewerAction):NgViewerStateInterface{ switch(action.type){ case ADD_NG_LAYER: return Object.assign({}, prevState, { @@ -55,6 +59,12 @@ export function ngViewerState(prevState:NgViewerStateInterface = {layers:[], for return Object.assign({}, prevState, { forceShowSegment : action.forceShowSegment }) as NgViewerStateInterface + case NEHUBA_READY: + const { nehubaReady } = action + return { + ...prevState, + nehubaReady + } default: return prevState } @@ -65,6 +75,7 @@ export const REMOVE_NG_LAYER = 'REMOVE_NG_LAYER' export const SHOW_NG_LAYER = 'SHOW_NG_LAYER' export const HIDE_NG_LAYER = 'HIDE_NG_LAYER' export const FORCE_SHOW_SEGMENT = `FORCE_SHOW_SEGMENT` +export const NEHUBA_READY = `NEHUBA_READY` interface NgLayerInterface{ name : string diff --git a/src/ui/layerbrowser/layerbrowser.component.ts b/src/ui/layerbrowser/layerbrowser.component.ts index 4e419c1150791f615013ad7f5a6c827b4c2b4cf2..404ad5ec9771dd87caabf5e382f749a6fc5b4061 100644 --- a/src/ui/layerbrowser/layerbrowser.component.ts +++ b/src/ui/layerbrowser/layerbrowser.component.ts @@ -3,7 +3,8 @@ import { NgLayerInterface } from "../../atlasViewer/atlasViewer.component"; import { Store, select } from "@ngrx/store"; import { ViewerStateInterface, isDefined, REMOVE_NG_LAYER, FORCE_SHOW_SEGMENT, safeFilter } from "../../services/stateStore.service"; import { Subscription, Observable } from "rxjs"; -import { filter, distinctUntilChanged, map, delay } from "rxjs/operators"; +import { filter, distinctUntilChanged, map, delay, throttle,tap, take, withLatestFrom, mergeMap, buffer } from "rxjs/operators"; +import { AtlasViewerConstantsServices } from "src/atlasViewer/atlasViewer.constantService.service"; @Component({ selector : 'layer-browser', @@ -31,7 +32,9 @@ export class LayerBrowser implements OnDestroy{ /* TODO temporary measure. when datasetID can be used, will use */ public fetchedDataEntries$ : Observable<any> - constructor(private store : Store<ViewerStateInterface>){ + constructor( + private store : Store<ViewerStateInterface>, + private constantsService: AtlasViewerConstantsServices){ this.ngLayers$ = store.pipe( select('viewerState'), @@ -58,14 +61,36 @@ export class LayerBrowser implements OnDestroy{ map(state => state.forceShowSegment) ) - this.subscriptions.push(this.store.pipe( + const throttleObs = this.store.pipe( + select('ngViewerState'), + + filter(state => state && state.nehubaReady), + ) + + throttleObs.subscribe((val) => { + console.log('throtld obs emitted', val) + }) + + /** + * TODO leakage? after change of template still hanging the reference? + */ + this.subscriptions.push( + + + this.store.pipe( select('viewerState'), filter(state => isDefined(state) && isDefined(state.templateSelected)), distinctUntilChanged((o,n) => o.templateSelected.name === n.templateSelected.name), map(state => Object.keys(state.templateSelected.nehubaConfig.dataset.initialNgState.layers)), - delay(0) + buffer(this.store.pipe( + select('ngViewerState'), + select('nehubaReady'), + filter(flag => flag) + )), + delay(0), + map(arr => arr[arr.length - 1]) ).subscribe((lockedLayerNames:string[]) => { - + console.log('subscription') /** * TODO * if layerbrowser is init before nehuba @@ -155,7 +180,7 @@ export class LayerBrowser implements OnDestroy{ segmentationTooltip(){ return `toggle segments visibility: -${this.forceShowSegmentCurrentState === true ? 'always show' : this.forceShowSegmentCurrentState === false ? 'always hide' : 'auto'}` + ${this.forceShowSegmentCurrentState === true ? 'always show' : this.forceShowSegmentCurrentState === false ? 'always hide' : 'auto'}` } get segmentationAdditionalClass(){ @@ -167,4 +192,8 @@ ${this.forceShowSegmentCurrentState === true ? 'always show' : this.forceShowSeg ? 'muted' : 'red' } + + get isMobile(){ + return this.constantsService.mobile + } } diff --git a/src/ui/layerbrowser/layerbrowser.template.html b/src/ui/layerbrowser/layerbrowser.template.html index 0dd64f021ac5757ccf2bdb99c1fb93557e5066d8..03e5a4cdf1c639c0ef521f235cb6d80622c58d41 100644 --- a/src/ui/layerbrowser/layerbrowser.template.html +++ b/src/ui/layerbrowser/layerbrowser.template.html @@ -1,7 +1,8 @@ <ng-container *ngIf="ngLayers$ | async | filterNgLayer : ngLayers as filteredNgLayers; else noLayerPlaceHolder"> <ng-container *ngIf="filteredNgLayers.length > 0; else noLayerPlaceHolder"> - <div + <div class="layerContainer" + [ngStyle] = "isMobile && {'overflow': 'hidden'}" *ngFor = "let ngLayer of filteredNgLayers"> <div @@ -40,6 +41,7 @@ </i> </div> <panel-component + [ngClass] = "{'muted-text muted' : !classVisible(ngLayer)}"> <div heading> diff --git a/src/ui/menuicons/menuicons.component.ts b/src/ui/menuicons/menuicons.component.ts index e5da46c7fccefc6c77fca5afb29f99f69787d278..e0f04704b1abbb3bdb7ef24bf426c89c1c67db5c 100644 --- a/src/ui/menuicons/menuicons.component.ts +++ b/src/ui/menuicons/menuicons.component.ts @@ -1,4 +1,4 @@ -import { Component, ViewChild, TemplateRef, ComponentRef, Injector, ComponentFactory, ComponentFactoryResolver } from "@angular/core"; +import { Component, ViewChild, TemplateRef, ComponentRef, Injector, ComponentFactory, ComponentFactoryResolver, OnInit, AfterViewInit } from "@angular/core"; import { WidgetServices } from "src/atlasViewer/widgetUnit/widgetService.service"; import { WidgetUnit } from "src/atlasViewer/widgetUnit/widgetUnit.component"; @@ -18,7 +18,7 @@ import { PluginServices } from "src/atlasViewer/atlasViewer.pluginService.servic ] }) -export class MenuIconsBar{ +export class MenuIconsBar implements AfterViewInit{ /** * databrowser @@ -91,6 +91,24 @@ export class MenuIconsBar{ } + + ngAfterViewInit() { + if (this.isMobile) setTimeout(() => this.createLayerBrowserComponentForMobile(), 0) + } + + createLayerBrowserComponentForMobile() { + setTimeout(() => { + this.layerBrowser = this.lbcf.create(this.injector) + this.lbWidget = this.widgetServices.addNewWidget(this.layerBrowser, { + exitable: true, + persistency: true, + state: 'floating', + title: 'Layer Browser', + titleHTML: '<i class="fas fa-layer-group"></i> Layer Browser' + }) + }) + } + public clickLayer(event: MouseEvent){ if (this.lbWidget) { diff --git a/src/ui/menuicons/menuicons.template.html b/src/ui/menuicons/menuicons.template.html index 8897cbb6b517be6e2e1e28c1fc50e1dad82c1454..ceb819a8427e5542df3a66185fe784222b89edef 100644 --- a/src/ui/menuicons/menuicons.template.html +++ b/src/ui/menuicons/menuicons.template.html @@ -1,7 +1,7 @@ -<logo-container> +<logo-container *ngIf="!isMobile"> </logo-container> -<div +<!-- <div *ngIf="isMobile" [ngClass]="isMobile ? 'btnWrapper-lg' : ''" class="btnWrapper"> @@ -11,7 +11,7 @@ </i> </div> -</div> +</div> --> <div *ngIf="false" diff --git a/src/ui/nehubaContainer/mobileOverlay/mobileOverlay.style.css b/src/ui/nehubaContainer/mobileOverlay/mobileOverlay.style.css index f1532380e3d32e8e4c5fad2f137e5185800f7095..df352ebd76c28846b95958ebb0e4ed143da236d2 100644 --- a/src/ui/nehubaContainer/mobileOverlay/mobileOverlay.style.css +++ b/src/ui/nehubaContainer/mobileOverlay/mobileOverlay.style.css @@ -5,7 +5,7 @@ top: 0; left: 0; position: absolute; - z-index: 1000; + z-index: 999; pointer-events: none; } diff --git a/src/ui/nehubaContainer/nehubaContainer.component.ts b/src/ui/nehubaContainer/nehubaContainer.component.ts index 215b16ac9d5d63533b37f025b8e486f97478db02..378494853832f0fb5f7b39215dc9a83884599e8a 100644 --- a/src/ui/nehubaContainer/nehubaContainer.component.ts +++ b/src/ui/nehubaContainer/nehubaContainer.component.ts @@ -10,6 +10,7 @@ import { AtlasViewerDataService } from "../../atlasViewer/atlasViewer.dataServic import { AtlasViewerConstantsServices } from "../../atlasViewer/atlasViewer.constantService.service"; import { ViewerConfiguration } from "src/services/state/viewerConfig.store"; import { pipeFromArray } from "rxjs/internal/util/pipe"; +import { NEHUBA_READY } from "src/services/state/ngViewerState.store"; @Component({ selector : 'ui-nehuba-container', @@ -59,7 +60,7 @@ export class NehubaContainer implements OnInit, OnDestroy{ private selectedRegionIndexSet : Set<number> = new Set() public fetchedSpatialData : Landmark[] = [] - private ngLayersRegister : NgViewerStateInterface = {layers : [], forceShowSegment: null} + private ngLayersRegister : Partial<NgViewerStateInterface> = {layers : [], forceShowSegment: null} private ngLayers$ : Observable<NgViewerStateInterface> public selectedParcellation : any | null @@ -389,7 +390,10 @@ export class NehubaContainer implements OnInit, OnDestroy{ return deepCopiedState }) ).subscribe((state)=>{ - + this.store.dispatch({ + type: NEHUBA_READY, + nehubaReady: false + }) this.nehubaViewerSubscriptions.forEach(s=>s.unsubscribe()) this.selectedTemplate = state.templateSelected @@ -695,6 +699,18 @@ export class NehubaContainer implements OnInit, OnDestroy{ this.nehubaViewer.debouncedViewerPositionChange.subscribe(this.handleEmittedNavigationChange.bind(this)) ) + this.nehubaViewerSubscriptions.push( + /** + * TODO when user selects new template, window.viewer + */ + this.nehubaViewer.nehubaReady.subscribe(() => { + this.store.dispatch({ + type: NEHUBA_READY, + nehubaReady: true + }) + }) + ) + this.nehubaViewerSubscriptions.push( this.nehubaViewer.debouncedViewerPositionChange.pipe( distinctUntilChanged((a,b) => diff --git a/src/ui/nehubaContainer/nehubaContainer.template.html b/src/ui/nehubaContainer/nehubaContainer.template.html index 25e2eb085a4da2256a56e189a996475937dc1b34..cad73a3d8e72af26813d62f5e7ab2e202d5a3c7a 100644 --- a/src/ui/nehubaContainer/nehubaContainer.template.html +++ b/src/ui/nehubaContainer/nehubaContainer.template.html @@ -71,7 +71,7 @@ <layout-floating-container *ngIf="viewerLoaded"> <!-- StatusCard container--> - <ui-status-card [selectedTemplate]="selectedTemplate" [isMobile]="isMobile" +<ui-status-card [selectedTemplate]="selectedTemplate" [isMobile]="isMobile" [onHoverSegmentName]="onHoverSegmentName$ | async" [nehubaViewer]="nehubaViewer"> </ui-status-card> </layout-floating-container> diff --git a/src/ui/nehubaContainer/nehubaViewer/nehubaViewer.component.ts b/src/ui/nehubaContainer/nehubaViewer/nehubaViewer.component.ts index 488f5181dc98b597fe640f027ca7b8c5aab52837..dd34a2bc2cf25e2adf664c0c3f77b235d52406a2 100644 --- a/src/ui/nehubaContainer/nehubaViewer/nehubaViewer.component.ts +++ b/src/ui/nehubaContainer/nehubaViewer/nehubaViewer.component.ts @@ -18,6 +18,7 @@ import { pipeFromArray } from "rxjs/internal/util/pipe"; export class NehubaViewerUnit implements OnDestroy{ + @Output() nehubaReady: EventEmitter<null> = new EventEmitter() @Output() debouncedViewerPositionChange : EventEmitter<any> = new EventEmitter() @Output() mouseoverSegmentEmitter : EventEmitter<any | number | null> = new EventEmitter() @Output() mouseoverLandmarkEmitter : EventEmitter<number | null> = new EventEmitter() @@ -326,6 +327,7 @@ export class NehubaViewerUnit implements OnDestroy{ /* creation of the layout is done on next frame, hence the settimeout */ setTimeout(() => { window['viewer'].display.panels.forEach(patchSliceViewPanel) + this.nehubaReady.emit(null) }) this.newViewerInit() diff --git a/src/ui/regionHierachy/regionHierarchy.component.ts b/src/ui/regionHierachy/regionHierarchy.component.ts index 382f1f4074a8bc2f44d270f757de4ef5f2abceb6..0555327a6837ba8b66db4e4d1d934f06803cc02e 100644 --- a/src/ui/regionHierachy/regionHierarchy.component.ts +++ b/src/ui/regionHierachy/regionHierarchy.component.ts @@ -35,6 +35,8 @@ export class RegionHierarchy implements OnInit, AfterViewInit{ @Input() public selectedParcellation: any + @Input() isMobile: boolean; + private _showRegionTree: boolean = false @Output() diff --git a/src/ui/regionHierachy/regionHierarchy.template.html b/src/ui/regionHierachy/regionHierarchy.template.html index e2afcc286169fb6c05c74b38e251f51a1403b276..36ab15e5278d6327517df36f71e137fc96c18f33 100644 --- a/src/ui/regionHierachy/regionHierarchy.template.html +++ b/src/ui/regionHierachy/regionHierarchy.template.html @@ -3,7 +3,7 @@ #searchTermInput tabindex="0" (keydown.esc)="escape($event)" - (focus)="showRegionTree = true" + (focus)="showRegionTree = true && !isMobile" [value]="searchTerm" class="form-control form-control-sm" type="text" diff --git a/src/ui/sharedModules/angularMaterial.module.ts b/src/ui/sharedModules/angularMaterial.module.ts new file mode 100644 index 0000000000000000000000000000000000000000..2a56177760024d9a9c60fe85ef4dd642d547126e --- /dev/null +++ b/src/ui/sharedModules/angularMaterial.module.ts @@ -0,0 +1,8 @@ +import {MatButtonModule, MatCheckboxModule, MatSidenavModule, MatCardModule} from '@angular/material'; +import { NgModule } from '@angular/core'; + +@NgModule({ + imports: [MatButtonModule, MatCheckboxModule, MatSidenavModule, MatCardModule], + exports: [MatButtonModule, MatCheckboxModule, MatSidenavModule, MatCardModule], +}) +export class AngularMaterialModule { } \ No newline at end of file diff --git a/src/ui/signinBanner/signinBanner.components.ts b/src/ui/signinBanner/signinBanner.components.ts index 4f81cf657910e1f611d57740df17fc1cf22f4ced..3feeb1b1936fedd68cfda1396e93b4d19efe6c0f 100644 --- a/src/ui/signinBanner/signinBanner.components.ts +++ b/src/ui/signinBanner/signinBanner.components.ts @@ -1,4 +1,4 @@ -import { Component, ChangeDetectionStrategy, OnDestroy, OnInit } from "@angular/core"; +import { Component, ChangeDetectionStrategy, OnDestroy, OnInit, Input } from "@angular/core"; import { AtlasViewerConstantsServices } from "src/atlasViewer/atlasViewer.constantService.service"; import { AuthService, User } from "src/services/auth.service"; import { Store, select } from "@ngrx/store"; @@ -26,6 +26,7 @@ export class SigninBanner implements OnInit, OnDestroy{ public selectedParcellation$: Observable<any> public selectedRegions$: Observable<any[]> private selectedRegions: any[] = [] + @Input() darktheme: boolean constructor( private constantService: AtlasViewerConstantsServices, diff --git a/src/ui/signinBanner/signinBanner.style.css b/src/ui/signinBanner/signinBanner.style.css index f07029736eefa53d8ebbcfdfdb1f72bf25608458..e3573d450eb4300689a759820b20aac34aa319bf 100644 --- a/src/ui/signinBanner/signinBanner.style.css +++ b/src/ui/signinBanner/signinBanner.style.css @@ -1,8 +1,6 @@ :host { - display: flex; - flex-direction: row; - align-items: flex-start; + } :host > * @@ -33,4 +31,40 @@ dropdown-component { font-size:80%; +} + +.whenMobile { + display: flex; + flex-direction: column; + align-items: center; + min-width: 100%; +} + +.whenNotMobile { + display: flex; +} + +.login-icon { + height: 28px; + margin: 0 3px; +} + +.login-button-mobile { + order: -1; + align-self: flex-start; +} +.login-button-mobile > button { + outline: none; + background-color: transparent; +} + + +.selectTemplateDetailsMobile { + width: 100%; + margin-top: 5px; +} + + +.selectTemplateDetaiNoMobile { + margin: 0 3px; } \ No newline at end of file diff --git a/src/ui/signinBanner/signinBanner.template.html b/src/ui/signinBanner/signinBanner.template.html index 81aa6e58c9e8d1987f069357563bc9ce82cca52b..d668c513ea07a3d9ea81764e492faed0c3ff7f98 100644 --- a/src/ui/signinBanner/signinBanner.template.html +++ b/src/ui/signinBanner/signinBanner.template.html @@ -1,45 +1,61 @@ -<dropdown-component - *ngIf="!isMobile" - (itemSelected)="changeTemplate($event)" - [activeDisplay]="displayActiveTemplate" - [selectedItem]="selectedTemplate$ | async" - [inputArray]="loadedTemplates$ | async | filterNull"> +<div [ngClass]="isMobile? 'whenMobile' : 'whenNotMobile'" > -</dropdown-component> - -<ng-container *ngIf="selectedTemplate$ | async as selectedTemplate"> <dropdown-component - *ngIf="selectedParcellation$ | async as selectedParcellation" - (itemSelected)="changeParcellation($event)" - [activeDisplay]="displayActiveParcellation" - [selectedItem]="selectedParcellation" - [inputArray]="selectedTemplate.parcellations"> - + (itemSelected)="changeTemplate($event)" + [activeDisplay]="displayActiveTemplate" + [selectedItem]="selectedTemplate$ | async" + [inputArray]="loadedTemplates$ | async | filterNull" + [isMobile] = "isMobile" + [ngClass]="isMobile ? 'selectTemplateDetailsMobile' : 'selectTemplateDetaiNoMobile'"> </dropdown-component> - <region-hierarchy - [selectedRegions]="selectedRegions$ | async | filterNull" - (singleClickRegion)="handleRegionClick({ mode: 'single', region: $event })" - (doubleClickRegion)="handleRegionClick({ mode: 'double', region: $event })" - (clearAllRegions)="clearAllRegions()" - *ngIf="selectedParcellation$ | async as selectedParcellation" - [selectedParcellation]="selectedParcellation"> - - </region-hierarchy> -</ng-container> - -<!-- help btn --> -<div - *ngIf="!isMobile" - (click)="showHelp()" - class="btn btn-outline-secondary btn-sm rounded-circle"> - <i class="fas fa-question-circle"></i> -</div> - -<!-- signin --> -<div - (click)="showSignin()" - class="btn btn-outline-secondary btn-sm rounded-circle"> - <i - [ngClass]="user ? 'fa-user' : 'fa-sign-in-alt'" - class="fas"></i> + + <ng-container *ngIf="selectedTemplate$ | async as selectedTemplate"> + <dropdown-component + *ngIf="selectedParcellation$ | async as selectedParcellation" + (itemSelected)="changeParcellation($event)" + [activeDisplay]="displayActiveParcellation" + [selectedItem]="selectedParcellation" + [inputArray]="selectedTemplate.parcellations" + [isMobile] = "isMobile" + [ngClass]="isMobile ? 'selectTemplateDetailsMobile' : 'selectTemplateDetaiNoMobile'"> + + </dropdown-component> + <region-hierarchy + [selectedRegions]="selectedRegions$ | async | filterNull" + (singleClickRegion)="handleRegionClick({ mode: 'single', region: $event })" + (doubleClickRegion)="handleRegionClick({ mode: 'double', region: $event })" + (clearAllRegions)="clearAllRegions()" + [isMobile] = "isMobile" + *ngIf="selectedParcellation$ | async as selectedParcellation" + [selectedParcellation]="selectedParcellation" + [ngClass]="isMobile ? 'selectTemplateDetailsMobile' : 'selectTemplateDetaiNoMobile'"> + + </region-hierarchy> + </ng-container> + + <!-- help btn --> + <div + *ngIf="!isMobile" + (click)="showHelp()" + class="btn btn-outline-secondary btn-sm rounded-circle login-icon"> + <i class="fas fa-question-circle"></i> + </div> + + <!-- signin --> + <div + *ngIf="!isMobile" + (click)="showSignin()" + class="btn btn-outline-secondary btn-sm rounded-circle login-icon"> + <i + [ngClass]="user ? 'fa-user' : 'fa-sign-in-alt'" + class="fas"></i> + </div> + + <div + *ngIf="isMobile" + (click)="showSignin()" + class="login-button-mobile"> + <button mat-button [ngStyle]="{'color': darktheme? '#D7D7D7' : 'black'}">Log In</button> + </div> + </div> \ No newline at end of file diff --git a/src/ui/ui.module.ts b/src/ui/ui.module.ts index ee3bc1cec4359e4dc9d6c3ed15808ccbc824c721..4fda873757bf2534d7adac2465ce01925ec500ca 100644 --- a/src/ui/ui.module.ts +++ b/src/ui/ui.module.ts @@ -29,7 +29,7 @@ import { DownloadDirective } from "../util/directives/download.directive"; import { LogoContainer } from "./logoContainer/logoContainer.component"; import { TemplateParcellationCitationsContainer } from "./templateParcellationCitations/templateParcellationCitations.component"; import { MobileOverlay } from "./nehubaContainer/mobileOverlay/mobileOverlay.component"; -import { FilterNullPipe } from "../util/pipes/filterNull.pipe"; +import { FilterNullPipe } from "../util/pipes/filterNull.pipe";AngularMaterialModule import { ShowToastDirective } from "../util/directives/showToast.directive"; import { HelpComponent } from "./help/help.component"; import { ConfigComponent } from './config/config.component' @@ -45,6 +45,7 @@ import { FilterNameBySearch } from "./regionHierachy/filterNameBySearch.pipe"; import { StatusCardComponent } from "./nehubaContainer/statusCard/statusCard.component"; import { CookieAgreement } from "./cookieAgreement/cookieAgreement.component"; import { KGToS } from "./kgtos/kgtos.component"; +import { AngularMaterialModule } from "./sharedModules/angularMaterial.module"; @NgModule({ @@ -54,6 +55,7 @@ import { KGToS } from "./kgtos/kgtos.component"; ComponentsModule, DatabrowserModule, UtilModule, + AngularMaterialModule, PopoverModule.forRoot(), TooltipModule.forRoot() @@ -122,7 +124,8 @@ import { KGToS } from "./kgtos/kgtos.component"; SigninBanner, SigninModal, CookieAgreement, - KGToS + KGToS, + AngularMaterialModule ] })