diff --git a/src/atlasViewer/atlasViewer.template.html b/src/atlasViewer/atlasViewer.template.html index 205d21f7fe4231bc9d960ae93a32c8c8e446556c..e6f5ac669baa096ca1e84ee760fa608514b856e3 100644 --- a/src/atlasViewer/atlasViewer.template.html +++ b/src/atlasViewer/atlasViewer.template.html @@ -133,6 +133,7 @@ </div> + <!-- TODO move to nehuba overlay container --> <panel-component class="shadow" fixedMouseContextualContainerDirective #rClContextMenu> <div heading> <h5 class="pe-all p-2 m-0"> @@ -147,14 +148,14 @@ data-toggle="tooltip" data-placement="top" [title]="onhoverSegmentFixed.name"> - Search KG for {{ onhoverSegmentFixed.name }} + Search for data related to {{ onhoverSegmentFixed.name }} </div> <div *ngIf="(selectedRegions$ | async)?.length > 0 && (selectedRegions$ | async); let selectedRegions" (click)="searchRegion(selectedRegions)" class="ws-no-wrap text-left pe-all mt-0 btn btn-sm btn-secondary btn-block"> - Search KG for {{ selectedRegions && selectedRegions.length }} selected regions + Search for data related to all {{ selectedRegions && selectedRegions.length }} selected regions </div> <div diff --git a/src/atlasViewer/atlasViewer.urlService.service.ts b/src/atlasViewer/atlasViewer.urlService.service.ts index 225cf6af97a909b8bb693c34abd3f9e0322c180f..5cd06b1ba499e34537cce32dc2caa872250c53a3 100644 --- a/src/atlasViewer/atlasViewer.urlService.service.ts +++ b/src/atlasViewer/atlasViewer.urlService.service.ts @@ -51,6 +51,9 @@ export class AtlasViewerURLService{ scan((acc,val)=>Object.assign({},acc,val),{}) ) + /** + * TODO change additionalNgLayer to id, querying node backend for actual urls + */ this.additionalNgLayers$ = combineLatest( this.changeQueryObservable$.pipe( map(state => state.templateSelected) diff --git a/src/ui/layerbrowser/layerbrowser.component.ts b/src/ui/layerbrowser/layerbrowser.component.ts index 862d956f984985d2d05817f56d8778fa73666855..8a8cdbd4a952a73e8e05a0b8cba3d2ac08e19cb5 100644 --- a/src/ui/layerbrowser/layerbrowser.component.ts +++ b/src/ui/layerbrowser/layerbrowser.component.ts @@ -9,7 +9,10 @@ import { AtlasViewerConstantsServices } from "src/atlasViewer/atlasViewer.consta @Component({ selector : 'layer-browser', templateUrl : './layerbrowser.template.html', - styleUrls : [ './layerbrowser.style.css' ] + styleUrls : [ + './layerbrowser.style.css', + '../btnShadow.style.css' + ] }) export class LayerBrowser implements OnDestroy{ @@ -66,11 +69,11 @@ export class LayerBrowser implements OnDestroy{ * TODO leakage? after change of template still hanging the reference? */ this.subscriptions.push( - this.store.pipe( + this.store.pipe( select('viewerState'), - filter(state => isDefined(state) && isDefined(state.templateSelected)), + select('templateSelected'), distinctUntilChanged((o,n) => o.templateSelected.name === n.templateSelected.name), - map(state => Object.keys(state.templateSelected.nehubaConfig.dataset.initialNgState.layers)), + map(templateSelected => Object.keys(templateSelected.nehubaConfig.dataset.initialNgState.layers)), buffer(this.store.pipe( select('ngViewerState'), select('nehubaReady'), @@ -102,7 +105,6 @@ export class LayerBrowser implements OnDestroy{ } ngLayersChangeHandler(){ - this.ngLayers = (window['viewer'].layerManager.managedLayers as any[]).map(obj => ({ name : obj.name, type : obj.initialSpecification.type, @@ -119,8 +121,8 @@ export class LayerBrowser implements OnDestroy{ checkLocked(ngLayer:NgLayerInterface):boolean{ if(!this.lockedLayers){ - /* locked layer undefined. always return true for locked layer check */ - return true + /* locked layer undefined. always return false */ + return false }else return this.lockedLayers.findIndex(l => l === ngLayer.name) >= 0 } diff --git a/src/ui/layerbrowser/layerbrowser.template.html b/src/ui/layerbrowser/layerbrowser.template.html index 03e5a4cdf1c639c0ef521f235cb6d80622c58d41..e307ecb73020de8802c64c4ad33ca591d33501e6 100644 --- a/src/ui/layerbrowser/layerbrowser.template.html +++ b/src/ui/layerbrowser/layerbrowser.template.html @@ -1,51 +1,58 @@ <ng-container *ngIf="ngLayers$ | async | filterNgLayer : ngLayers as filteredNgLayers; else noLayerPlaceHolder"> <ng-container *ngIf="filteredNgLayers.length > 0; else noLayerPlaceHolder"> <div - class="layerContainer" - [ngStyle] = "isMobile && {'overflow': 'hidden'}" + class="layerContainer overflow-hidden" *ngFor = "let ngLayer of filteredNgLayers"> - <div - (click) = "checkLocked(ngLayer) ? null : toggleVisibility(ngLayer)" - class="btn btn-sm btn-outline-secondary rounded-circle"> - <i + <!-- toggle visibility --> + <div class="btnWrapper"> + <div container = "body" placement = "bottom" [tooltip] = "checkLocked(ngLayer) ? 'base layer cannot be hidden' : 'toggle visibility'" - [ngClass] = "checkLocked(ngLayer) ? 'fas fa-lock muted' :ngLayer.visible ? 'far fa-eye' : 'far fa-eye-slash'"> - - </i> + (click) = "checkLocked(ngLayer) ? null : toggleVisibility(ngLayer)" + class="btn btn-sm btn-outline-secondary rounded-circle"> + <i [ngClass] = "checkLocked(ngLayer) ? 'fas fa-lock muted' :ngLayer.visible ? 'far fa-eye' : 'far fa-eye-slash'"> + </i> + </div> </div> - <div *ngIf="advancedMode" - (click) = "forceSegment.hide();toggleForceShowSegment(ngLayer)" - class="btn btn-sm btn-outline-secondary rounded-circle"> - <i - container = "body" - placement = "bottom" - [tooltip] = "ngLayer.type === 'segmentation' ? segmentationTooltip() : 'only segmentation layer can hide/show segments'" - class = "fas" - #forceSegment = "bs-tooltip" - [ngClass] = "ngLayer.type === 'segmentation' ? ('fa-th-large ' + segmentationAdditionalClass) : 'fa-lock muted' "> - - </i> + + <!-- advanced mode only: toggle force show segmentation --> + <div class="btnWrapper"> + <div + *ngIf="advancedMode" + container="body" + placement="bottom" + [tooltip]="ngLayer.type === 'segmentation' ? segmentationTooltip() : 'only segmentation layer can hide/show segments'" + #forceSegment="bs-tooltip" + (click)="forceSegment.hide();toggleForceShowSegment(ngLayer)" + class="btn btn-sm btn-outline-secondary rounded-circle"> + <i + class="fas" + [ngClass]="ngLayer.type === 'segmentation' ? ('fa-th-large ' + segmentationAdditionalClass) : 'fa-lock muted' "> + + </i> + </div> </div> - <div - (click) = "removeLayer(ngLayer)" - class="btn btn-sm btn-outline-secondary rounded-circle"> - <i - container = "body" - placement = "bottom" - [tooltip] = "checkLocked(ngLayer) ? 'base layers cannot be removed' : 'remove layer'" - [ngClass] = "checkLocked(ngLayer) ? 'fas fa-lock muted' : 'far fa-times-circle'"> - - </i> + + <!-- remove layer --> + <div class="btnWrapper"> + <div + container="body" + placement="bottom" + [tooltip]="checkLocked(ngLayer) ? 'base layers cannot be removed' : 'remove layer'" + (click)="removeLayer(ngLayer)" + class="btn btn-sm btn-outline-secondary rounded-circle"> + <i [ngClass]="checkLocked(ngLayer) ? 'fas fa-lock muted' : 'far fa-times-circle'"> + </i> + </div> </div> - <panel-component - - [ngClass] = "{'muted-text muted' : !classVisible(ngLayer)}"> + + <!-- layer description --> + <panel-component [ngClass]="{'muted-text muted' : !classVisible(ngLayer)}"> <div heading> - {{ ngLayer.name | getLayerNameFromDatasets : (fetchedDataEntries$ | async) }} : {{ ngLayer.type }} + {{ ngLayer.name | getLayerNameFromDatasets : (fetchedDataEntries$ | async) }} </div> <div bodyy> @@ -56,6 +63,7 @@ </ng-container> </ng-container> +<!-- fall back when no layers are showing --> <ng-template #noLayerPlaceHolder> <h5 class="noLayerPlaceHolder text-muted"> No additional layers added. diff --git a/src/ui/menuicons/menuicons.component.ts b/src/ui/menuicons/menuicons.component.ts index 6751331d0d4e8d122ca78af616dc227a4092496b..57250e2b4963c7a15b1685e7b1a5dc32a50b7199 100644 --- a/src/ui/menuicons/menuicons.component.ts +++ b/src/ui/menuicons/menuicons.component.ts @@ -72,8 +72,8 @@ export class MenuIconsBar{ dataBrowser.instance.template = template dataBrowser.instance.parcellation = parcellation const title = regions.length > 1 - ? `Data associated with ${regions.length} regions` - : `Data associated with ${regions[0].name}` + ? `Search: ${regions.length} regions` + : `Search: ${regions[0].name}` const widgetUnit = this.widgetServices.addNewWidget(dataBrowser, { exitable: true, persistency: true, diff --git a/src/ui/nehubaContainer/nehubaContainer.template.html b/src/ui/nehubaContainer/nehubaContainer.template.html index 83066042e55fd28269815978ba971520c65bdca9..4356da032ab0fb41b1761d4ac770ed3cbfb31e4a 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/signinBanner/signinBanner.components.ts b/src/ui/signinBanner/signinBanner.components.ts index 60f8ce728f760a834f9daf309c2c22c63f57509e..e3870a457c8225554a9c2e124b0f560b6d318345 100644 --- a/src/ui/signinBanner/signinBanner.components.ts +++ b/src/ui/signinBanner/signinBanner.components.ts @@ -4,9 +4,10 @@ import { AuthService, User } from "src/services/auth.service"; import { Store, select } from "@ngrx/store"; import { ViewerConfiguration } from "src/services/state/viewerConfig.store"; import { Subscription, Observable } from "rxjs"; -import { safeFilter, isDefined, NEWVIEWER, SELECT_REGIONS, SELECT_PARCELLATION } from "src/services/stateStore.service"; +import { safeFilter, isDefined, NEWVIEWER, SELECT_REGIONS, SELECT_PARCELLATION, CHANGE_NAVIGATION } from "src/services/stateStore.service"; import { map, filter, distinctUntilChanged } from "rxjs/operators"; import { regionFlattener } from "src/util/regionFlattener"; +import { ToastService } from "src/services/toastService.service"; @Component({ selector: 'signin-banner', @@ -31,7 +32,8 @@ export class SigninBanner implements OnInit, OnDestroy{ constructor( private constantService: AtlasViewerConstantsServices, private authService: AuthService, - private store: Store<ViewerConfiguration> + private store: Store<ViewerConfiguration>, + private toastService: ToastService ){ this.loadedTemplates$ = this.store.pipe( select('viewerState'), @@ -98,16 +100,48 @@ export class SigninBanner implements OnInit, OnDestroy{ handleRegionClick({ mode = 'single', region }){ if (!region) return - const flattenedRegion = regionFlattener(region).filter(r => isDefined(r.labelIndex)) - const flattenedRegionNames = new Set(flattenedRegion.map(r => r.name)) - const selectedRegionNames = new Set(this.selectedRegions.map(r => r.name)) - const selectAll = flattenedRegion.every(r => !selectedRegionNames.has(r.name)) - this.store.dispatch({ - type: SELECT_REGIONS, - selectRegions: selectAll - ? this.selectedRegions.concat(flattenedRegion) - : this.selectedRegions.filter(r => !flattenedRegionNames.has(r.name)) - }) + + /** + * single click on region hierarchy => toggle selection + */ + if (mode === 'single') { + const flattenedRegion = regionFlattener(region).filter(r => isDefined(r.labelIndex)) + const flattenedRegionNames = new Set(flattenedRegion.map(r => r.name)) + const selectedRegionNames = new Set(this.selectedRegions.map(r => r.name)) + const selectAll = flattenedRegion.every(r => !selectedRegionNames.has(r.name)) + this.store.dispatch({ + type: SELECT_REGIONS, + selectRegions: selectAll + ? this.selectedRegions.concat(flattenedRegion) + : this.selectedRegions.filter(r => !flattenedRegionNames.has(r.name)) + }) + } + + /** + * double click on region hierarchy => navigate to region area if it exists + */ + if (mode === 'double') { + + /** + * if position is defined, go to position (in nm) + * if not, show error messagea s toast + * + * nb: currently, only supports a single triplet + */ + if (region.position) { + this.store.dispatch({ + type: CHANGE_NAVIGATION, + navigation: { + position: region.position + } + }) + } else { + this.toastService.showToast(`${region.name} does not have a position defined`, { + timeout: 5000, + dismissable: true + }) + } + } } displayActiveParcellation(parcellation:any){