diff --git a/src/ui/databrowserModule/databrowser/databrowser.style.css b/src/ui/databrowserModule/databrowser/databrowser.style.css index 831c351d9a0e8c6439fb3f1ff9800ae8f1e178c8..f20a0d72a07c290e74dfaacf31e9442c3a688570 100644 --- a/src/ui/databrowserModule/databrowser/databrowser.style.css +++ b/src/ui/databrowserModule/databrowser/databrowser.style.css @@ -9,12 +9,6 @@ radio-list display: block; } -:host -{ - height: 100%; - width: 100%; -} - cdk-virtual-scroll-viewport { width: calc(100% + 1em); diff --git a/src/ui/menuicons/menuicons.component.ts b/src/ui/menuicons/menuicons.component.ts deleted file mode 100644 index 2c1da620c008d0a43846e9fa2f2279d10bf462b9..0000000000000000000000000000000000000000 --- a/src/ui/menuicons/menuicons.component.ts +++ /dev/null @@ -1,271 +0,0 @@ -import { - Component, - ComponentRef, - Injector, - ComponentFactory, - ComponentFactoryResolver, - TemplateRef, - ViewChild, - OnInit, - OnDestroy, - AfterViewInit, -} from "@angular/core"; - -import { WidgetServices } from "src/atlasViewer/widgetUnit/widgetService.service"; -import { WidgetUnit } from "src/atlasViewer/widgetUnit/widgetUnit.component"; -import { DataBrowser } from "src/ui/databrowserModule/databrowser/databrowser.component"; -import { PluginBannerUI } from "../pluginBanner/pluginBanner.component"; -import { AtlasViewerConstantsServices } from "src/atlasViewer/atlasViewer.constantService.service"; -import { DatabrowserService } from "../databrowserModule/databrowser.service"; -import { PluginServices, PluginManifest } from "src/atlasViewer/atlasViewer.pluginService.service"; -import { Store, select } from "@ngrx/store"; -import { Observable, combineLatest, Subscription } from "rxjs"; -import { map, shareReplay, startWith } from "rxjs/operators"; -import { LayerBrowser } from "../layerbrowser/layerbrowser.component"; -import { MatDialogRef, MatDialog } from "@angular/material"; -import { NgLayerInterface } from "src/atlasViewer/atlasViewer.component"; -import { DataEntry } from "src/services/stateStore.service"; -import { KgSingleDatasetService } from "../databrowserModule/kgSingleDatasetService.service"; -import { determinePreviewFileType, PREVIEW_FILE_TYPES } from "../databrowserModule/preview/previewFileIcon.pipe"; -@Component({ - selector: 'menu-icons', - templateUrl: './menuicons.template.html', - styleUrls: [ - './menuicons.style.css', - '../btnShadow.style.css' - ] -}) - -export class MenuIconsBar implements OnInit, OnDestroy { - - private layerBrowserDialogRef: MatDialogRef<any> - private subscriptions: Subscription[] = [] - - public badgetPosition: string = 'above before' - - /** - * databrowser - */ - dbcf: ComponentFactory<DataBrowser> - dataBrowser: ComponentRef<DataBrowser> = null - dbWidget: ComponentRef<WidgetUnit> = null - - /** - * pluginBrowser - */ - pbcf: ComponentFactory<PluginBannerUI> - pluginBanner: ComponentRef<PluginBannerUI> = null - pbWidget: ComponentRef<WidgetUnit> = null - - isMobile: boolean = false - mobileRespBtnClass: string - - public darktheme$: Observable<boolean> - - public themedBtnClass$: Observable<string> - - public skeletonBtnClass$: Observable<string> - - public toolBtnClass$: Observable<string> - public getKgSearchBtnCls$: Observable<[Set<WidgetUnit>, string]> - - public sidebarTemplate$: Observable<TemplateRef<any>> - - public selectedTemplate$: Observable<any> - public selectedParcellation$: Observable<any> - public selectedRegions$: Observable<any> - - public getPluginBtnClass$: Observable<[Set<string>, Set<string>, string]> - public launchedPlugins$: Observable<string[]> - - searchedItemsNumber = 0 - searchLoading = false - searchMenuFrozen = false - filePreviewModalClosed = false - showSearchMenu = false - mouseHoversSearch = false - - public fetchedDatasets: DataEntry[] = [] - - constructor( - private widgetServices:WidgetServices, - private injector:Injector, - private constantService:AtlasViewerConstantsServices, - public dbService: DatabrowserService, - cfr: ComponentFactoryResolver, - public pluginServices:PluginServices, - private store: Store<any>, - private dialog: MatDialog, - private singleDatasetService: KgSingleDatasetService - ){ - - this.isMobile = this.constantService.mobile - this.mobileRespBtnClass = this.constantService.mobile ? 'btn-lg' : 'btn-sm' - - this.dbService.createDatabrowser = this.clickSearch.bind(this) - - this.dbcf = cfr.resolveComponentFactory(DataBrowser) - this.pbcf = cfr.resolveComponentFactory(PluginBannerUI) - - this.selectedTemplate$ = store.pipe( - select('viewerState'), - select('templateSelected') - ) - - this.selectedParcellation$ = store.pipe( - select('viewerState'), - select('parcellationSelected'), - ) - - this.selectedRegions$ = store.pipe( - select('viewerState'), - select('regionsSelected'), - startWith([]), - shareReplay(1) - ) - - this.themedBtnClass$ = this.constantService.darktheme$.pipe( - map(flag => flag ? 'btn-dark' : 'btn-light' ), - shareReplay(1) - ) - - this.skeletonBtnClass$ = this.constantService.darktheme$.pipe( - map(flag => `${this.mobileRespBtnClass} ${flag ? 'text-light' : 'text-dark'}`), - shareReplay(1) - ) - - this.launchedPlugins$ = this.pluginServices.launchedPlugins$.pipe( - map(set => Array.from(set)), - shareReplay(1) - ) - - /** - * TODO remove dependency on themedBtnClass$ - */ - this.getPluginBtnClass$ = combineLatest( - this.pluginServices.launchedPlugins$, - this.pluginServices.minimisedPlugins$, - this.themedBtnClass$ - ) - - this.darktheme$ = this.constantService.darktheme$ - - /** - * TODO remove dependency on themedBtnClass$ - */ - this.getKgSearchBtnCls$ = combineLatest( - this.widgetServices.minimisedWindow$, - this.themedBtnClass$ - ) - - this.sidebarTemplate$ = this.store.pipe( - select('uiState'), - select('sidebarTemplate') - ) - } - - ngOnInit(){ - } - - ngOnDestroy(){ - while(this.subscriptions.length > 0){ - this.subscriptions.pop().unsubscribe() - } - } - - /** - * TODO - * temporary measure - * migrate to nehubaOverlay - */ - public clickSearch({ regions, template, parcellation }){ - const dataBrowser = this.dbcf.create(this.injector) - dataBrowser.instance.regions = regions - dataBrowser.instance.template = template - dataBrowser.instance.parcellation = parcellation - const title = regions.length > 1 - ? `Search: ${regions.length} regions` - : `Search: ${regions[0].name}` - const widgetUnit = this.widgetServices.addNewWidget(dataBrowser, { - exitable: true, - persistency: true, - state: 'floating', - title, - titleHTML: `<i class="fas fa-search"></i> ${title}` - }) - return { - dataBrowser, - widgetUnit - } - } - - public catchError(e) { - this.constantService.catchError(e) - } - public clickPlugins(event: MouseEvent){ - if(this.pbWidget) { - this.pbWidget.destroy() - this.pbWidget = null - return - } - this.pluginBanner = this.pbcf.create(this.injector) - this.pbWidget = this.widgetServices.addNewWidget(this.pluginBanner, { - exitable: true, - persistency: true, - state: 'floating', - title: 'Plugin Browser', - titleHTML: '<i class="fas fa-tools"></i> Plugin Browser' - }) - - this.pbWidget.onDestroy(() => { - this.pbWidget = null - this.pluginBanner = null - }) - - const el = event.currentTarget as HTMLElement - const top = el.offsetTop - const left = el.offsetLeft + 50 - this.pbWidget.instance.position = [left, top] - } - - public clickPluginIcon(manifest: PluginManifest){ - this.pluginServices.launchPlugin(manifest) - .catch(err => this.constantService.catchError(err)) - } - - closeFrozenMenu() { - this.searchMenuFrozen = false - this.filePreviewModalClosed = false - this.showSearchMenu = false - } - - hideSearchMenu() { - if (this.showSearchMenu) { - setTimeout(() => { - if (!this.mouseHoversSearch && !this.searchMenuFrozen) - this.showSearchMenu = false - }, 600) - } - } - - public showKgSearchSideNav(kgSearchTemplate: TemplateRef<any> = null){ - - } - - handleNonbaseLayerEvent(layers: NgLayerInterface[]){ - if (layers.length === 0) { - this.layerBrowserDialogRef && this.layerBrowserDialogRef.close() - this.layerBrowserDialogRef = null - return - } - if (this.layerBrowserDialogRef) return - this.layerBrowserDialogRef = this.dialog.open(LayerBrowser, { - hasBackdrop: false, - autoFocus: false, - position: { - top: '1em' - }, - disableClose: true - }) - } -} diff --git a/src/ui/menuicons/menuicons.style.css b/src/ui/menuicons/menuicons.style.css deleted file mode 100644 index cbaaac39d3a0c58e9422b81a73630fa49e7f36e1..0000000000000000000000000000000000000000 --- a/src/ui/menuicons/menuicons.style.css +++ /dev/null @@ -1,26 +0,0 @@ - -.soh-row > *:not(:first-child) -{ - margin-left: 0.1em; -} - -.soh-column > *:not(:first-child) -{ - margin-top: 0.1em; -} - -layer-browser -{ - max-width: 20em; -} - -:host > * -{ - pointer-events: all; -} - -[root] > * -{ - margin-top: 1em; - display:inline-block; -} diff --git a/src/ui/menuicons/menuicons.template.html b/src/ui/menuicons/menuicons.template.html deleted file mode 100644 index 2db3ad3d3ad6bc745e10651b0ed6830aaf071bb0..0000000000000000000000000000000000000000 --- a/src/ui/menuicons/menuicons.template.html +++ /dev/null @@ -1,258 +0,0 @@ -<div class="w-0 ml-4 d-flex flex-column align-items-start" root> - - <!-- hide icons when templates has yet been selected --> - <ng-template [ngIf]="selectedTemplate$ | async"> - - <!-- TODO do not close expression changed after view checked --> - <!-- selected regions --> - <sleight-of-hand - [doNotClose]="viewerStateController.focused"> - - <!-- shown prior to mouse over --> - <div sleight-of-hand-front> - <button - [matBadge]="(selectedRegions$ | async).length > 0 ? (selectedRegions$ | async).length : null" - [matBadgePosition]="badgetPosition" - matBadgeColor="accent" - mat-icon-button - color="primary"> - <i class="fas fa-brain"></i> - </button> - </div> - - <!-- shown upon mouseover --> - <div - sleight-of-hand-back - class="d-flex flex-row align-items-center soh-row"> - - <!-- place holder icon --> - <button - [matBadge]="(selectedRegions$ | async).length > 0 ? (selectedRegions$ | async).length : null" - [matBadgePosition]="badgetPosition" - matBadgeColor="accent" - mat-icon-button - color="primary"> - <i class="fas fa-brain"></i> - </button> - - <div class="position-relative"> - - <div [class]="((darktheme$ | async) ? 'bg-dark' : 'bg-light') + ' position-absolute card'"> - <viewer-state-controller #viewerStateController></viewer-state-controller> - </div> - - <!-- invisible icon to keep height of the otherwise unstable flex block --> - <div class="invisible pe-none"> - <i class="fas fa-brain"></i> - </div> - </div> - - <ng-template #noBrainRegionSelected> - <small [class]="((darktheme$ | async) ? 'bg-dark text-light' : 'bg-light text-dark') + ' muted pl-2 pr-2 p-1 text-nowrap'"> - Double click any brain region to select it. - </small> - </ng-template> - </div> - </sleight-of-hand> - - <!-- layer browser --> - <sleight-of-hand> - <div sleight-of-hand-front> - <button - [matBadge]="layerBrowser && (layerBrowser.nonBaseNgLayers$ | async)?.length > 0 ? (layerBrowser.nonBaseNgLayers$ | async)?.length : null" - [matBadgePosition]="badgetPosition" - matBadgeColor="accent" - color="primary" - mat-icon-button> - <i class="fas fa-layer-group"></i> - </button> - </div> - <div - class="d-flex flex-row align-items-center soh-row" - sleight-of-hand-back> - - <button - [matBadge]="layerBrowser && (layerBrowser.nonBaseNgLayers$ | async)?.length > 0 ? (layerBrowser.nonBaseNgLayers$ | async)?.length : null" - [matBadgePosition]="badgetPosition" - matBadgeColor="accent" - color="primary" - mat-icon-button> - <i class="fas fa-layer-group"></i> - </button> - - <div class="position-relative d-flex align-items-center"> - - <div [ngClass]="{'invisible pe-none': (layerBrowser.nonBaseNgLayers$ | async).length === 0}" class="position-absolute"> - <mat-card> - <layer-browser (nonBaseLayersChanged)="handleNonbaseLayerEvent($event)" #layerBrowser> - </layer-browser> - </mat-card> - </div> - - <ng-container *ngIf="(layerBrowser.nonBaseNgLayers$ | async).length === 0" #noNonBaseNgLayerTemplate> - <small [class]="((darktheme$ | async) ? 'bg-dark text-light' : 'bg-light text-dark') + ' muted pl-2 pr-2 p-1 text-nowrap position-absolute'"> - No additional layers added - </small> - </ng-container> - - <!-- invisible button to prop up the size of parent block --> - <!-- otherwise, sibling block position will be wonky --> - <button - color="primary" - class="invisible pe-none" - mat-icon-button> - <i class="fas fa-layer-group"></i> - </button> - - </div> - - </div> - </sleight-of-hand> - - <!-- tools --> - <sleight-of-hand> - - <!-- shown icon prior to mouse over --> - <div sleight-of-hand-front> - <button - [matBadgePosition]="badgetPosition" - matBadgeColor="accent" - [matBadge]="(launchedPlugins$ | async)?.length > 0 ? (launchedPlugins$ | async)?.length : null" - mat-icon-button - color="primary"> - <i class="fas fa-tools"></i> - </button> - </div> - - <!-- shown after mouse over --> - <div - class="d-flex flex-row soh-row align-items-start" - sleight-of-hand-back> - - <!-- placeholder icon --> - <button - [matBadgePosition]="badgetPosition" - matBadgeColor="accent" - [matBadge]="(launchedPlugins$ | async)?.length > 0 ? (launchedPlugins$ | async)?.length : null" - mat-icon-button - color="primary"> - <i class="fas fa-tools"></i> - </button> - - <!-- render all fetched tools --> - <div class="d-flex flex-row soh-row"> - - <!-- add new tool btn --> - <button - matTooltip="Add new plugin" - matTooltipPosition="below" - mat-icon-button - color="primary"> - <i class="fas fa-plus"></i> - </button> - - <button - *ngFor="let manifest of pluginServices.fetchedPluginManifests" - mat-mini-fab - matTooltipPosition="below" - [matTooltip]="manifest.displayName || manifest.name" - [color]="getPluginBtnClass$ | async | pluginBtnFabColorPipe : manifest.name" - (click)="clickPluginIcon(manifest)"> - {{ (manifest.displayName || manifest.name).slice(0, 1) }} - </button> - </div> - </div> - </sleight-of-hand> - - <!-- kg search alt --> - <div class="d-flex align-item-start"> - <div *ngIf="(sidebarTemplate$ | async) === kgSearchDatasets; then alreadyOpened; else notYetOpened"> - </div> - <ng-template #alreadyOpened> - <button - [matBadgePosition]="badgetPosition" - matBadgeColor="accent" - [matBadge]="(fetchedDatasets && fetchedDatasets.length) ? fetchedDatasets.length : null" - matTooltip="Found datasets" - matTooltipPosition="after" - mat-mini-fab - (click)="showKgSearchSideNav()" - color="primary"> - <i class="fas fa-book"></i> - </button> - </ng-template> - <ng-template #notYetOpened> - <button - [matBadgePosition]="badgetPosition" - matBadgeColor="accent" - [matBadge]="(fetchedDatasets && fetchedDatasets.length) ? fetchedDatasets.length : null" - matTooltip="Found datasets" - matTooltipPosition="after" - mat-icon-button - (click)="showKgSearchSideNav(kgSearchDatasets)" - color="primary"> - <i class="fas fa-book"></i> - </button> - </ng-template> - </div> - - <!-- search kg --> - <!-- <sleight-of-hand>--> - <div - class="d-flex align-items-start" - (mouseenter)="mouseHoversSearch = true; showSearchMenu = true" - (mouseleave)="mouseHoversSearch = false; hideSearchMenu()" - style="z-index: 1" - #SearchMenu> - <div sleight-of-hand-front> - <button - [matTooltip]="!searchedItemsNumber && 'Please select any region to get search results'" - matTooltipPosition="after" - mat-icon-button - color="primary" - [matBadgePosition]="badgetPosition" - matBadgeColor="accent" - [matBadge]="searchedItemsNumber? searchedItemsNumber : null"> - <i class="fas fa-search"></i> - </button> - </div> - - <div *ngIf="(selectedRegions$ | async)?.length" - [hidden] = "!showSearchMenu" - class="position-absolute" - style="padding-left: 50px;"> - <mat-card class="p-0"> - <search-panel - elementOutClick - (outsideClick)="filePreviewModalClosed && $event? closeFrozenMenu() : null" - [selectedTemplate$]="selectedTemplate$" - [selectedParcellation$]="selectedParcellation$" - [selectedRegions$]="selectedRegions$" - [searchPanelPositionTop]="SearchMenu.offsetTop" - (searchedItemsNumber)="searchedItemsNumber = $event" - (searchLoading)="searchLoading = true" - (freezeSearchMenu)="searchMenuFrozen = $event" - (filePreviewModalClosed)="filePreviewModalClosed = $event" - (closeSearchMenu)="this.showSearchMenu = false"> - </search-panel> - </mat-card> - </div> - </div> - - </ng-template> -</div> - -<!-- needs the data browser to be rendered, but hidden, so that side templateref can be passed -this also bypass the necessity of doing a query --> -<div [hidden]="true"> - <ng-template [ngIf]="(selectedTemplate$ | async) && (selectedParcellation$ | async)" #kgSearchDatasets> - <data-browser - (dataentriesUpdated)="fetchedDatasets = $event" - [template]="selectedTemplate$ | async" - [regions]="selectedRegions$ | async" - [parcellation]="selectedParcellation$ | async" - #dataBrowser> - - </data-browser> - </ng-template> -</div> diff --git a/src/ui/searchItemPreview/searchItemPreview.component.ts b/src/ui/searchItemPreview/searchItemPreview.component.ts deleted file mode 100644 index d652b8ed50b0b0a7271ab22bff746b5bcf81daaf..0000000000000000000000000000000000000000 --- a/src/ui/searchItemPreview/searchItemPreview.component.ts +++ /dev/null @@ -1,124 +0,0 @@ -import {Component, EventEmitter, Input, OnDestroy, OnInit, Output, TemplateRef, ViewChild} from "@angular/core"; -import {ViewerPreviewFile} from "src/services/state/dataStore.store"; -import {DatabrowserService} from "src/ui/databrowserModule/databrowser.service"; -import {AtlasViewerConstantsServices} from "src/atlasViewer/atlasViewer.constantService.service"; -import {MatDialog} from "@angular/material"; -import {CHANGE_NAVIGATION} from "src/services/state/viewerState.store"; -import {Store} from "@ngrx/store"; -import {ToastService} from "src/services/toastService.service"; -import { KgSingleDatasetService } from "../databrowserModule/kgSingleDatasetService.service"; - -@Component({ - selector: 'search-item-preview-component', - templateUrl: './searchItemPreview.template.html', - styleUrls: ['./searchItemPreview.style.css'] -}) - -export class SearchItemPreviewComponent { - @Input() datasetName: string - @Input() selectedRegions - - - @Output() freezeFilesSubMenu: EventEmitter<boolean> = new EventEmitter() - @Output() filePreviewModalClosed: EventEmitter<boolean> = new EventEmitter() - @Output() closeSearchMenu: EventEmitter<boolean> = new EventEmitter() - - @ViewChild("previewFileModal") previewFileModal: TemplateRef<any> - - - public previewFiles: ViewerPreviewFile[] = [] - public activeFile: ViewerPreviewFile - private error: string - previewFileDialogRef - - constructor( - private dbrService: DatabrowserService, - public constantsService: AtlasViewerConstantsServices, - private singleDsService: KgSingleDatasetService, - public dialog: MatDialog, - private store: Store<any>, - private toastService: ToastService, - ) {} - - ngOnInit() { - if (this.datasetName) { - this.dbrService.fetchPreviewData(this.datasetName) - .then(json => { - this.previewFiles = json as ViewerPreviewFile[] - if (this.previewFiles.length > 0) - this.activeFile = this.previewFiles[0] - }) - .catch(e => { - this.error = JSON.stringify(e) - }) - } - } - - openPreviewDialog(previewFile) { - if (previewFile.mimetype !== 'application/nifti') { - this.freezeFilesSubMenu.emit(true) - this.filePreviewModalClosed.emit(false) - this.previewFileDialogRef = this.dialog.open(this.previewFileModal, { - width: '400px', - data: previewFile, - panelClass: ['no-scrolls'], - }) - this.previewFileDialogRef.afterClosed().subscribe(result => { - this.filePreviewModalClosed.emit(true) - }) - } else { - if (this.niftiLayerIsShowing(previewFile)) { - this.removeDedicatedViewOnAtlasViewer(previewFile) - } - else { - this.showDedicatedViewOnAtlasViewer(previewFile) - - // this.selectedRegions.forEach(sr => { - // if (sr.name.includes(' - left hemisphere')) { - // if (previewFile.filename.includes(sr.name.replace(' - left hemisphere', '')) && previewFile.filename.includes('left hemisphere')) { - // this.navigateToRegion(sr) - // this.closeSearchMenu.emit(true) - // } - // } - // if (sr.name.includes(' - right hemisphere')) { - // if (previewFile.filename.includes(sr.name.replace(' - right hemisphere', '')) && previewFile.filename.includes('right hemisphere')) { - // this.navigateToRegion(sr) - // this.closeSearchMenu.emit(true) - // } - // } - // }) - this.closeSearchMenu.emit(true) - } - } - } - - - niftiLayerIsShowing(previewFile){ - return this.singleDsService.ngLayers.has(previewFile.url) - } - - showDedicatedViewOnAtlasViewer(previewFile){ - this.singleDsService.showNewNgLayer({ url: previewFile.url }) - } - - removeDedicatedViewOnAtlasViewer(previewFile){ - this.singleDsService.removeNgLayer({ url: previewFile.url }) - } - - navigateToRegion(region) { - if (region.position) { - this.store.dispatch({ - type: CHANGE_NAVIGATION, - navigation: { - position: region.position - }, - animation: {} - }) - } else { - this.toastService.showToast(`${region.name} does not have a position defined`, { - timeout: 5000, - dismissable: true - }) - } - } -} diff --git a/src/ui/searchItemPreview/searchItemPreview.style.css b/src/ui/searchItemPreview/searchItemPreview.style.css deleted file mode 100644 index 49b2e18075c861c3d3c7f5a498790f389794ad3d..0000000000000000000000000000000000000000 --- a/src/ui/searchItemPreview/searchItemPreview.style.css +++ /dev/null @@ -1,8 +0,0 @@ -.preview-panel-container { - width: 330px; - min-height: 100px; -} - -.file-items { - max-height: 300px; -} diff --git a/src/ui/searchItemPreview/searchItemPreview.template.html b/src/ui/searchItemPreview/searchItemPreview.template.html deleted file mode 100644 index 5510c5e6373b492c01ee6cf6750a5751e4abdaee..0000000000000000000000000000000000000000 --- a/src/ui/searchItemPreview/searchItemPreview.template.html +++ /dev/null @@ -1,33 +0,0 @@ -<div class="preview-panel-container" (click)="$event.stopPropagation()"> - <div class="w-100 d-flex justify-content-center p-2"> - Files - </div> - <mat-divider></mat-divider> - - <div class="mr-2 ml-2"> - <mat-form-field class="w-100" (click)="$event.stopPropagation()"> - <input matInput - placeholder="Click here to filter" - #filterInput> - <button mat-button *ngIf="filterInput.value" matSuffix mat-icon-button aria-label="Clear" - (click)="filterInput.value=''"> - <i class="fas fa-times"></i> - </button> - </mat-form-field> - </div> - <div class="overflow-auto file-items"> - <div *ngFor="let pf of previewFiles | filterWithString : filterInput.value; let i = index" class="file-item"> - <div class="cursorPointer ml-3 mr-3 pt-2 pb-2 text-wrap d-flex justify-content-between align-items-center" - (mouseenter)="ShowIcon.hidden = false" (mouseleave)="ShowIcon.hidden = true" - (click)="openPreviewDialog(pf)"> - <div [ngClass]="ShowIcon.hidden? 'mr-3' : 'mr-1'">{{pf.name}}</div> - <i class="far fa-eye" [ngClass]="niftiLayerIsShowing(pf)? 'fa-eye-slash' : 'fa-eye'" [ngStyle]="niftiLayerIsShowing(pf) && {'color': '#e25241'}" hidden #ShowIcon></i> - </div> - <hr *ngIf="i !== previewFiles.length-1" [color]="constantsService.darktheme? '#C6C6C6' : ''" class="m-0"> - </div> - </div> -</div> - -<ng-template #previewFileModal let-previewFile> - <file-viewer [previewFile]="previewFile"></file-viewer> -</ng-template> diff --git a/src/ui/searchPanel/searchPanel.component.ts b/src/ui/searchPanel/searchPanel.component.ts deleted file mode 100644 index e985f3227bdec260d06ad8b5d1b4d5f30ba04938..0000000000000000000000000000000000000000 --- a/src/ui/searchPanel/searchPanel.component.ts +++ /dev/null @@ -1,241 +0,0 @@ -import { - Component, - ElementRef, - EventEmitter, - HostListener, - Input, - OnDestroy, - OnInit, - Output, - ViewChild -} from "@angular/core"; -import {merge, Observable, Subscription} from "rxjs"; -import {select, Store} from "@ngrx/store"; -import {DataEntry, safeFilter} from "src/services/stateStore.service"; -import {distinctUntilChanged, filter, map} from "rxjs/operators"; -import {CountedDataModality, DatabrowserService} from "src/ui/databrowserModule/databrowser.service"; -import {AtlasViewerConstantsServices} from "src/atlasViewer/atlasViewer.constantService.service"; - -@Component({ - selector: 'search-panel', - templateUrl: './searchPanel.template.html', - styleUrls: ['./searchPanel.style.css'] -}) - -export class SearchPanel implements OnInit, OnDestroy { - @Input() selectedTemplate$: Observable<any> - @Input() selectedParcellation$: Observable<any> - @Input() selectedRegions$: Observable<any> - @Input() searchPanelPositionTop - selectedTemplate - selectedParcellation - - @Output() searchedItemsNumber: EventEmitter<number> = new EventEmitter() - @Output() searchLoading: EventEmitter<boolean> = new EventEmitter() - @Output() freezeSearchMenu: EventEmitter<boolean> = new EventEmitter() - @Output() filePreviewModalClosed: EventEmitter<boolean> = new EventEmitter() - @Output() closeSearchMenu: EventEmitter<boolean> = new EventEmitter() - - public dataentries: DataEntry[] = [] - public countedDataM: CountedDataModality[] = [] - public visibleCountedDataM: CountedDataModality[] = [] - public fetchingFlag: boolean = false - public fetchError: boolean = false - private subscriptions: Subscription[] = [] - - showPreview: number = null - visiblePreview: number = null - previewWindowTopPosition - windowInnerheight - - filterVisible = false - filterHovering - filesMenuFrozen - filePreviewModalClosedValue = false - showSelectedRegions = false - selectedRegionsHovering - mouseHoversFilesMenu - - @HostListener('window:resize', ['$event']) - onResize(event) { - this.windowInnerheight = event.target.innerHeight - } - - setPreviewHeight(searchBody, searchItem) { - this.previewWindowTopPosition = +searchItem.offsetTop - +searchBody.scrollTop - +this.searchPanelPositionTop + 270 - setTimeout(() => { - if (this.searchPreview) { - if ((+this.previewWindowTopPosition + +this.searchPreview.nativeElement.offsetHeight + +this.searchPanelPositionTop) > window.innerHeight) { - this.previewWindowTopPosition -= ((+this.previewWindowTopPosition + +this.searchPreview.nativeElement.offsetHeight + +this.searchPanelPositionTop)-window.innerHeight)+10 - } - } - this.visiblePreview = this.showPreview - }, 200) - } - - @ViewChild('SearchBody', {read: ElementRef}) searchBody: ElementRef - @ViewChild('SearchPreview', {read: ElementRef}) searchPreview: ElementRef - - constructor(private store: Store<any>, - private dbService: DatabrowserService, - private constantsService: AtlasViewerConstantsServices) { - this.windowInnerheight = window.innerHeight - } - - ngOnInit(): void { - this.subscriptions.push( - this.selectedTemplate$.subscribe(template => { - this.selectedTemplate = template - }) - ) - this.subscriptions.push( - this.selectedParcellation$.subscribe(parcellation => { - this.clearSearchResults() - this.selectedParcellation = parcellation - }) - ) - - // ToDo Put saved selections instead of selected regions - this.subscriptions.push( - this.selectedRegions$.subscribe(r => { - if (this.searchBody && this.searchBody.nativeElement) { - this.searchBody.nativeElement.scrollTop = 0 - } - if (r && r.length && this.selectedTemplate && this.selectedParcellation && this.selectedParcellation.regions) { - this.loadKGSearchData(this.selectedTemplate, this.selectedParcellation, r) - } else { - this.clearSearchResults() - } - }) - ) - } - - ngOnDestroy(): void { - this.subscriptions.forEach(s => s.unsubscribe()) - } - - loadKGSearchData(template, parcellation, regions: any = '') { - if (regions) { - regions = regions.map(r => { - /** - * TODO to be replaced with properly region UUIDs from KG - */ - return { - id: `${this.selectedParcellation.name}/${r.name}`, - ...r - } - }) - } - this.fetchingFlag = true - this.searchLoading.emit(true) - this.dbService.getDataByRegion({regions, parcellation, template}) - .then(de => { - this.dataentries = de - return de - }) - .then(this.dbService.getModalityFromDE) - .then(modalities => { - this.countedDataM = modalities - }) - .catch(e => { - this.fetchError = true - }) - .finally(() => { - if (this.dataentries && this.dataentries.length) - this.fetchError = false - this.searchedItemsNumber.emit(this.dataentries.length) - this.fetchingFlag = false - this.searchLoading.emit(false) - }) - - - this.subscriptions.push( - merge( - this.dbService.fetchDataObservable$ - ).subscribe(() => { - this.clearAll() - }) - ) - - } - - openDatasetUrl(url) { - window.open('https://doi.org/' + url, "_blank"); - } - - retryFetchData(event: MouseEvent){ - event.preventDefault() - this.dbService.manualFetchDataset$.next(null) - } - - resetFilters(event?:MouseEvent){ - this.clearAll() - } - - clearAll(){ - this.countedDataM = this.countedDataM.map(cdm => { - return { - ...cdm, - visible: false - } - }) - this.visibleCountedDataM = [] - } - - clearSearchResults() { - this.dataentries = [] - this.countedDataM = [] - this.visibleCountedDataM = [] - this.fetchingFlag = false - this.fetchError = false - this.searchedItemsNumber.emit(this.dataentries.length) - } - - handleModalityFilterEvent(modalityFilter:CountedDataModality[]){ - this.countedDataM = modalityFilter - this.visibleCountedDataM = modalityFilter.filter(dm => dm.visible) - } - - frozeSearchMenu() { - this.freezeSearchMenu.emit(true) - this.filesMenuFrozen = true - } - - unfrozeSearchMenu() { - this.filesMenuFrozen = false - this.freezeSearchMenu.emit(false) - } - closeFrozenMenu() { - this.filePreviewModalClosedValue = false - this.filesMenuFrozen = false - } - - hideFilter() { - setTimeout(() => { - if (!this.filterHovering) { - this.filterVisible = false - } - }, 500) - } - - hideSelectedRegions() { - setTimeout(() => { - if (!this.selectedRegionsHovering) { - this.showSelectedRegions = false - } - }, 500) - } - - hideSearchMenu() { - setTimeout(() => { - if (!this.mouseHoversFilesMenu && !this.filesMenuFrozen) { - this.showPreview = null - } - }, 500) - } - - get windowHeight() { - return window.innerHeight - } - -} diff --git a/src/ui/searchPanel/searchPanel.style.css b/src/ui/searchPanel/searchPanel.style.css deleted file mode 100644 index efc0ea1e75481a0c3c6249408304f5bec82ca348..0000000000000000000000000000000000000000 --- a/src/ui/searchPanel/searchPanel.style.css +++ /dev/null @@ -1,22 +0,0 @@ -.search-panel-container { - width: 330px; - min-height: 100px; -} - -.search-header { - height: 40px; -} - -.search-item:hover { - background-color: rgba(255, 255, 255, 0.1); -} - -.floating-container-mp { - margin-left: 20px; - padding-left: 15px; -} - -.floating-container-selected-regions { - margin-left: 35px; - padding-left: 30px; -} diff --git a/src/ui/searchPanel/searchPanel.template.html b/src/ui/searchPanel/searchPanel.template.html deleted file mode 100644 index d78eac28e2f6b9baa31d6d4ad753847df3b41995..0000000000000000000000000000000000000000 --- a/src/ui/searchPanel/searchPanel.template.html +++ /dev/null @@ -1,107 +0,0 @@ -<div class="search-panel-container" - [ngStyle]="{'max-height' : (+windowHeight - +searchPanelPositionTop - 20) + 'px'}"> - - <!-- Header with Filter --> - <div class="w-100 search-header d-flex align-items-center justify-content-between pl-2 pr-4" (mouseleave)="filterHovering = false; hideFilter()"> - <div class="d-flex text-nowrap"> - Data related to current selection - </div> - <div class="d-flex align-items-start" (mouseenter)="filterHovering = true; filterVisible = true; showSelectedRegions = false; showPreview = null;"> - <i class="fas fa-filter"></i> - <div [hidden]="!filterVisible" class="position-absolute ml-3 "> - <mat-card> - <modality-picker - (click)="$event.stopPropagation();" - class="mw-100" - [countedDataM]="countedDataM" - (modalityFilterEmitter)="handleModalityFilterEvent($event)"> - </modality-picker> - </mat-card> - </div> - </div> - </div> - <!-- END Header with Filter --> - - - <!-- Selected regions Container --> - <div class="ml-3 mr-3 pt-2 pb-2 text-wrap d-flex justify-content-between align-items-center cursorPointer" - (mouseenter)="selectedRegionsHovering = true; showSelectedRegions = true; showPreview = null; filterVisible = false" - (mouseleave)="selectedRegionsHovering = false; hideSelectedRegions()"> - <strong>Currently selected regions</strong> - <div class="d-flex align-items-start"> - <div class="d-flex align-items-center"> - <span>{{(selectedRegions$ | async)?.length}}</span> - <i class="fas fa-chevron-right ml-3"></i> - </div> - <div class="position-absolute floating-container-mp" *ngIf="showSelectedRegions"> - <selected-regions - [selectedRegions$]="selectedRegions$"> - </selected-regions> - </div> - </div> - </div> - <!-- END Selected regions Container--> - - <!-- Loaded Datasets container FULL --> - <div> - <div *ngIf="fetchingFlag" class="spinnerAnimationCircleContainer w-100 d-flex justify-content-center"> - <div class="spinnerAnimationCircle"></div> - <div>Fetching datasets...</div> - </div> - - <div class="ml-2 mr-2 alert alert-danger" *ngIf="fetchError"> - <i class="fas fa-exclamation-triangle"></i> Error fetching data. <a href="#" (click)="retryFetchData($event)" class="btn btn-link text-info">retry</a> - </div> - - <div *ngIf="dataentries && dataentries | filterDataEntriesByMethods : visibleCountedDataM as filteredDataEntry"> - <div class="dataEntry"> - <div class="ml-2 mr-2 mt-1"> - <i *ngIf="dataentries.length > 0"> - {{ dataentries.length }} total results. - <span *ngIf="visibleCountedDataM.length > 0 "> - {{ filteredDataEntry.length }} filtered results. - <a href="#" - class="btn btn-sm btn-link" - (click)="resetFilters($event)"> - reset filters - </a> - </span> - </i> - <i *ngIf="dataentries.length === 0 && (selectedRegions$ | async)?.length"> - No results to show. - </i> - </div> - <div class="overflow-auto mt-2" #SearchBody (mouseleave)="mouseHoversFilesMenu = false; hideSearchMenu()" - (mouseenter)="mouseHoversFilesMenu = true; filesMenuFrozen? unfrozeSearchMenu() : null" - [ngStyle]="{'max-height' : (+windowHeight - +searchPanelPositionTop - 150) + 'px'}" - elementOutClick - (outsideClick)="filePreviewModalClosedValue && closeFrozenMenu(); !filesMenuFrozen? showPreview = null : null"> - <div *ngFor="let dataset of filteredDataEntry; let i = index" class="search-item"> - <hr [color]="constantsService.darktheme? '#C6C6C6' : ''" class="m-0" style="margin: 0; padding: 0;"> - <div class="ml-3 mr-3 text-wrap d-flex justify-content-between align-items-center" #SearchItem (mouseenter)="showPreview = i; filterVisible = false; showSelectedRegions = false"> - <div class="cursorPointer pt-2 pb-2" (click)="openDatasetUrl(dataset.kgReference)" (mouseenter)="visiblePreview !== i && setPreviewHeight(SearchBody, SearchItem)">{{dataset.name}}</div> - <div *ngIf="dataset.preview" class="d-flex align-items-start "> - <i class="fas fa-chevron-right ml-3" (mouseenter)="visiblePreview !== i && setPreviewHeight(SearchBody, SearchItem)"></i> - <div class="position-absolute floating-container-mp" [ngStyle]="{'top': previewWindowTopPosition + 'px'}" *ngIf="showPreview === i" #SearchPreview> - <mat-card class="p-0"> - <search-item-preview-component - [datasetName]="dataset.name" - [ngStyle]="{'max-height': +windowInnerheight-100 + 'px'}" - [selectedRegions]="selectedRegions$ | async" - (freezeFilesSubMenu)="frozeSearchMenu()" - (filePreviewModalClosed)="filePreviewModalClosedValue = $event; this.filePreviewModalClosed.emit($event)" - (closeSearchMenu)="this.closeSearchMenu.emit(true)"> - </search-item-preview-component> - </mat-card> - </div> - </div> - </div> - </div> - <hr [color]="constantsService.darktheme? '#C6C6C6' : ''" class="mt-0"> - </div> - </div> - </div> - </div> - <!-- END Loaded Datasets container FULL --> - -</div> diff --git a/src/ui/searchSideNav/searchSideNav.template.html b/src/ui/searchSideNav/searchSideNav.template.html index 10d9c6953f74461f8d05ceb9178c549437a92fc6..a12387b37fa30b2bb1e94a2c148b339e8c4f1343 100644 --- a/src/ui/searchSideNav/searchSideNav.template.html +++ b/src/ui/searchSideNav/searchSideNav.template.html @@ -1,5 +1,5 @@ <div class="d-flex flex-column h-100"> - <viewer-state-controller class="pe-all mb-2" #viewerStateController> + <viewer-state-controller class="pe-all flex-grow-0 flex-shrink-0 mb-2" #viewerStateController> <!-- content append --> <ng-container card-content="append"> @@ -26,7 +26,7 @@ </viewer-state-controller> <data-browser - class="pe-all" + class="pe-all flex-grow-1 flex-shrink-1" *ngIf="showDataset" [template]="viewerStateController.templateSelected$ | async" [parcellation]="viewerStateController.parcellationSelected$ | async" diff --git a/src/ui/selectedRegions/selectedRegions.component.ts b/src/ui/selectedRegions/selectedRegions.component.ts deleted file mode 100644 index c77ed43add4d84d95d227365137f0f23058fbe18..0000000000000000000000000000000000000000 --- a/src/ui/selectedRegions/selectedRegions.component.ts +++ /dev/null @@ -1,59 +0,0 @@ -import {Component, Input} from "@angular/core"; -import {Observable} from "rxjs"; -import {CHANGE_NAVIGATION, SELECT_REGIONS} from "src/services/state/viewerState.store"; -import {Store} from "@ngrx/store"; -import {ToastService} from "src/services/toastService.service"; -import {DomSanitizer} from "@angular/platform-browser"; - -@Component({ - selector: 'selected-regions', - templateUrl: './selectedRegions.template.html', - styleUrls: ['./selectedRegions.style.css'] -}) - -export class SelectedRegionsComponent { - @Input() selectedRegions$: Observable<any> - - filterInput = '' - - constructor(private store: Store<any>, - private toastService: ToastService, - private sanitizer: DomSanitizer) {} - - removeRegionFromSelectedList(region, selectedRegions) { - this.store.dispatch({ - type: SELECT_REGIONS, - selectRegions: selectedRegions.filter(r => r.name !== region.name) - }) - } - - navigateToRegion(region) { - if (region.position) { - this.store.dispatch({ - type: CHANGE_NAVIGATION, - navigation: { - position: region.position - }, - animation: {} - }) - } else { - this.toastService.showToast(`${region.name} does not have a position defined`, { - timeout: 5000, - dismissable: true - }) - } - } - - deselectAllDisplayedRegions(regions, filterInputValue, deselectVisibleRegions){ - const filteredRegions = regions.filter(pf => pf.name.toLowerCase().includes(filterInputValue.toLowerCase())) - this.filterInput = '' - this.store.dispatch({ - type: SELECT_REGIONS, - selectRegions: regions.filter(r => deselectVisibleRegions? !filteredRegions.includes(r) : filteredRegions.includes(r)) - }) - } - - regionColorAsBackground (region) { - return this.sanitizer.bypassSecurityTrustStyle('background-color: rgb(' + region['rgb'][0] + ',' + region['rgb'][1] + ',' + region['rgb'][2] + ')') - } -} diff --git a/src/ui/selectedRegions/selectedRegions.style.css b/src/ui/selectedRegions/selectedRegions.style.css deleted file mode 100644 index ce6edc21185845d828cd895bf2d5729dc5f883c9..0000000000000000000000000000000000000000 --- a/src/ui/selectedRegions/selectedRegions.style.css +++ /dev/null @@ -1,16 +0,0 @@ -.selected-regions-panel-container { - width: 300px; -} - -.selectedRegionsList { - max-height: 250px; -} - -.dot { - height: 10px; - width: 10px; - min-height: 10px; - min-width: 10px; - border-radius: 50%; - display: inline-block; -} diff --git a/src/ui/selectedRegions/selectedRegions.template.html b/src/ui/selectedRegions/selectedRegions.template.html deleted file mode 100644 index 296597f3be7d5ecdfa12cd3a05c5356cbf2f78e8..0000000000000000000000000000000000000000 --- a/src/ui/selectedRegions/selectedRegions.template.html +++ /dev/null @@ -1,34 +0,0 @@ -<mat-card class="p-0 selected-regions-panel-container" - *ngIf="(selectedRegions$ | async) as sr"> - <div class="mr-2 ml-2"> - <mat-form-field class="w-100" (click)="$event.stopPropagation()"> - <input matInput - placeholder=" Click here to filter" - [(ngModel)]="filterInput" - class="pr-2 pl-2"> - <button mat-button *ngIf="filterInput" matSuffix mat-icon-button aria-label="Clear" - (click)="filterInput=''"> - <i class="fas fa-times"></i> - </button> - </mat-form-field> - </div> - <div class="d-flex align-items-end flex-column mr-2"> - <button mat-button (click)="deselectAllDisplayedRegions(sr, filterInput, true)">Deselect all {{filterInput && 'filtered'}} regions</button> - <button mat-button *ngIf="filterInput" (click)="deselectAllDisplayedRegions(sr, filterInput, false)">Select only filtered regions</button> - </div> - <div class="overflow-auto p-2 selectedRegionsList"> - <mat-chip-list> - <mat-chip *ngFor="let selectedRegion of sr | filterWithString : filterInput" - class="w-100 d-flex justify-content-between align-items-center" - removable="true" - (removed)="removeRegionFromSelectedList(selectedRegion, sr)" - (click)="navigateToRegion(selectedRegion)"> - <span class="d-flex align-items-center"> - <span class="dot mr-2 ml-n2" [style]="regionColorAsBackground(selectedRegion)"></span> - {{selectedRegion.name}} - </span> - <i class="fas fa-times-circle ml-1" matChipRemove (click)="$event.stopPropagation()"></i> - </mat-chip> - </mat-chip-list> - </div> -</mat-card> diff --git a/src/ui/ui.module.ts b/src/ui/ui.module.ts index 884c73ce549b57b8ca30c3337b963832525d35cf..cfe46add16f17edf5e3d3e73da4d3270758e3768 100644 --- a/src/ui/ui.module.ts +++ b/src/ui/ui.module.ts @@ -1,15 +1,14 @@ import { NgModule } from "@angular/core"; -import { ComponentsModule } from "../components/components.module"; +import { ComponentsModule } from "src/components/components.module"; import { NehubaViewerUnit } from "./nehubaContainer/nehubaViewer/nehubaViewer.component"; import { NehubaContainer } from "./nehubaContainer/nehubaContainer.component"; import { SplashScreen, GetTemplateImageSrcPipe, ImgSrcSetPipe } from "./nehubaContainer/splashScreen/splashScreen.component"; -import { LayoutModule } from "../layouts/layout.module"; +import { LayoutModule } from "src/layouts/layout.module"; import { FormsModule } from "@angular/forms"; -import { GroupDatasetByRegion } from "../util/pipes/groupDataEntriesByRegion.pipe"; -import { filterRegionDataEntries } from "../util/pipes/filterRegionDataEntries.pipe"; -import { MenuIconsBar } from './menuicons/menuicons.component' +import { GroupDatasetByRegion } from "src/util/pipes/groupDataEntriesByRegion.pipe"; +import { filterRegionDataEntries } from "src/util/pipes/filterRegionDataEntries.pipe"; import { GetUniquePipe } from "src/util/pipes/getUnique.pipe"; @@ -62,10 +61,7 @@ import { MaximmisePanelButton } from "./nehubaContainer/maximisePanelButton/maxi import { TouchSideClass } from "./nehubaContainer/touchSideClass.directive"; import { ReorderPanelIndexPipe } from "./nehubaContainer/reorderPanelIndex.pipe"; -import {SearchPanel} from "src/ui/searchPanel/searchPanel.component"; import {ElementOutClickDirective} from "src/util/directives/elementOutClick.directive"; -import {SearchItemPreviewComponent} from "src/ui/searchItemPreview/searchItemPreview.component"; -import {SelectedRegionsComponent} from "src/ui/selectedRegions/selectedRegions.component"; import {FilterWithStringPipe} from "src/util/pipes/filterWithString.pipe"; import { SearchSideNav } from "./searchSideNav/searchSideNav.component"; @@ -95,7 +91,6 @@ import { SearchSideNav } from "./searchSideNav/searchSideNav.component"; MobileOverlay, HelpComponent, ConfigComponent, - MenuIconsBar, SigninBanner, SigninModal, StatusCardComponent, @@ -109,9 +104,6 @@ import { SearchSideNav } from "./searchSideNav/searchSideNav.component"; ViewerStateController, MaximmisePanelButton, - SearchPanel, - SearchItemPreviewComponent, - SelectedRegionsComponent, SearchSideNav, /* pipes */ @@ -166,13 +158,11 @@ import { SearchSideNav } from "./searchSideNav/searchSideNav.component"; MobileOverlay, HelpComponent, ConfigComponent, - MenuIconsBar, SigninBanner, SigninModal, CookieAgreement, KGToS, StatusCardComponent, - SearchPanel, ElementOutClickDirective, SearchSideNav, ]