diff --git a/src/atlasViewer/atlasViewer.component.ts b/src/atlasViewer/atlasViewer.component.ts index cbb974e796c0d419a70140299671dd77ddeba8c0..c220280cf6e8e3619d89bbb45e5f0d6251beae87 100644 --- a/src/atlasViewer/atlasViewer.component.ts +++ b/src/atlasViewer/atlasViewer.component.ts @@ -39,7 +39,6 @@ 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 @@ -338,7 +337,6 @@ export class AtlasViewer implements OnDestroy, OnInit, AfterViewInit { } }) }) - this.widgetServices.dockedContainer = this.dockedContainer this.onhoverSegmentForFixed$ = this.rClContextualMenu.onShow.pipe( withLatestFrom(this.onhoverSegment$), diff --git a/src/atlasViewer/atlasViewer.style.css b/src/atlasViewer/atlasViewer.style.css index d6ce2d9390032ce2d828c4117bf74717df26494f..b56b1579651b45235cf8f5cc43f5c4f5a2f05be9 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%; diff --git a/src/atlasViewer/atlasViewer.template.html b/src/atlasViewer/atlasViewer.template.html index a08b77900a2a2e6d0aaf60f457509b369c14a060..e6f5ac669baa096ca1e84ee760fa608514b856e3 100644 --- a/src/atlasViewer/atlasViewer.template.html +++ b/src/atlasViewer/atlasViewer.template.html @@ -38,8 +38,8 @@ <layer-browser></layer-browser> </div> </panel-component> - <ng-template #dockedContainer> - </ng-template> + <div dockedContainerDirective> + </div> </tab> </tabset> @@ -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/components/dropdown/dropdown.style.css b/src/components/dropdown/dropdown.style.css index 4fc671675510d3cd760aeaf5bdd2728503e1f35d..e13bd32eb58f18b15d6de37040e1547df1360f30 100644 --- a/src/components/dropdown/dropdown.style.css +++ b/src/components/dropdown/dropdown.style.css @@ -56,24 +56,3 @@ radio-list overflow-x:hidden; display:block; } - -.drop-down-open-dark { - border-width: 1px 1px 0 1px; - border-style: solid; - border-color: white -} -.drop-down-open-light { - border-width: 1px 1px 0 1px solid black; - border-style: solid; - border-color: black; -} -.drop-down-closed-dark { - border-width: 1px; - border-style: solid; - border-color: white -} -.drop-down-closed-light { - border: 1px; - border-style: solid; - border-color: black -} \ No newline at end of file diff --git a/src/components/dropdown/dropdown.template.html b/src/components/dropdown/dropdown.template.html index 165044e39ef8471d513711b645dc47fd442c0081..e448400b8324b98dcfc159263517856732694cfc 100644 --- a/src/components/dropdown/dropdown.template.html +++ b/src/components/dropdown/dropdown.template.html @@ -1,3 +1,4 @@ +<!-- TODO remove dependency on ismobile --> <span class = "btn btn-default" [innerHTML] = "activeDisplay(selectedItem)" @@ -5,18 +6,18 @@ #dropdownToggle dropdownToggle - [ngStyle]="{'border': isMobile? '1px solid' : 'none', 'border-color': darktheme? 'white' : 'black', 'border-width': openState? '1px 1px 0 1px' : ''}"> + [ngStyle]="{'border': isMobile? 'none' : 'none', 'border-color': darktheme? 'white' : 'black', 'border-width': openState? '1px 1px 0 1px' : ''}"> </span> <!-- needed to ensure dropdown width matches --> -<ul class="m-0 h-0 o-h"> +<ul class="m-0 h-0 overflow-hidden"> <li *ngFor="let item of inputArray"> {{ listDisplay(item) }} </li> </ul> <radio-list - [ulClass]="'dropdown-menu'" + [ulClass]="'dropdown-menu shadow'" (itemSelected)="itemSelected.emit($event)" [listDisplay]="listDisplay" [selectedItem]="selectedItem" diff --git a/src/components/radiolist/radiolist.style.css b/src/components/radiolist/radiolist.style.css index 1d329b0eb4cbfdb8b0cfee42302c7ac7f108eb59..d264f83b0676a602570939980e60c9abc93c2294 100644 --- a/src/components/radiolist/radiolist.style.css +++ b/src/components/radiolist/radiolist.style.css @@ -55,8 +55,3 @@ ul,span.dropdown-item-1 white-space: nowrap; border:none; } - -.radioListBorder { - border-style: solid; - border-width: 0px 1px 1px 1px; -} \ No newline at end of file diff --git a/src/components/radiolist/radiolist.template.html b/src/components/radiolist/radiolist.template.html index 2f0236c6b9373c194a8d8f25ab1290f97e132068..3562d8685015d63191472420e4bf47423df80d5c 100644 --- a/src/components/radiolist/radiolist.template.html +++ b/src/components/radiolist/radiolist.template.html @@ -1,5 +1,5 @@ <ul - [ngClass]="[ulClass, isMobile? 'radioListBorder' : '']" + [ngClass]="ulClass" [ngStyle]="{'opacity': isMobile? '1' : '0.8', 'border-color': darktheme? 'white' : 'black'}" role="menu"> <li diff --git a/src/res/css/extra_styles.css b/src/res/css/extra_styles.css index 6b557d4719eb2bb309afa53c0033dbf7ef779581..a498518957812668c31c71a62e0ae47a88569cc5 100644 --- a/src/res/css/extra_styles.css +++ b/src/res/css/extra_styles.css @@ -306,11 +306,6 @@ markdown-dom pre code transition: all ease 500ms; } -.o-h -{ - overflow:hidden; -} - .h-0 { height: 0px; diff --git a/src/ui/databrowserModule/databrowser/databrowser.template.html b/src/ui/databrowserModule/databrowser/databrowser.template.html index be1302be1b51de651fdfd3d599f882601f4982dd..890576252a034b478ac5b63a642461b46da53349 100644 --- a/src/ui/databrowserModule/databrowser/databrowser.template.html +++ b/src/ui/databrowserModule/databrowser/databrowser.template.html @@ -15,11 +15,11 @@ Datasets relevant to <span *ngFor="let region of regions" - class="badge badge-secondary mr-1"> + class="badge badge-secondary mr-1 mw-100"> <span [ngStyle]="{backgroundColor:getBackgroundColorStyleFromRegion(region)}" class="dot"> </span> - <span> + <span class="d-inline-block mw-100 overflow-hidden text-truncate"> {{ region.name }} </span> </span> @@ -118,13 +118,14 @@ [dataset]="dataset"> <div regionTagsContainer> + <!-- TODO may want to separate the region badge into a separate component --> <span *ngFor="let region of dataset.parcellationRegion" - class="badge badge-secondary mr-1"> + class="badge badge-secondary mr-1 mw-100"> <span [ngStyle]="{backgroundColor:getBackgroundColorStyleFromRegion(region)}" class="dot"> </span> - <span> + <span class="d-inline-block mw-100 overflow-hidden text-truncate"> {{ region.name }} </span> </span> 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){