diff --git a/README.md b/README.md index e13fb1cd8a41fe1312cf17df8604df119ec2b21e..3495978508902c6bb88b4e389e49586052647642 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Interactive Atlas Viewer is an frontend module wrapping around [nehuba](https:// A live version of the Interactive Atlas Viewer is available at [https://interactive-viewer.apps.hbp.eu](https://interactive-viewer.apps.hbp.eu). This section is useful for developers who would like to develop this project. ### General information -Interactive atlas viewer is built with [Angular (v6.0)](https://angular.io/), [Bootstrap (v4)](http://getbootstrap.com/), and [fontawesome icons](https://fontawesome.com/). Some other notable packages used are: [ng2-charts](https://valor-software.com/ng2-charts/) for charts visualisation, [ngx-bootstrap](https://valor-software.com/ngx-bootstrap/) for UI and [ngrx/store](https://github.com/ngrx/platform) for state management. +Interactive atlas viewer is built with [Angular (v6.0)](https://angular.io/), [Bootstrap (v4)](http://getbootstrap.com/), and [fontawesome icons](https://fontawesome.com/). Some other notable packages used are: [ngx-bootstrap](https://valor-software.com/ngx-bootstrap/) for UI and [ngrx/store](https://github.com/ngrx/platform) for state management. Releases newer than [v0.2.9](https://github.com/HumanBrainProject/interactive-viewer/tree/v0.2.9) also uses a nodejs backend, which uses [passportjs](http://www.passportjs.org/) for user authentication, [express](https://expressjs.com/) as a http framework. diff --git a/common/util.js b/common/util.js index 871481b91fc3d3b021aa4302a9d5aa71dd0a1e38..330e538fe5bee7fcfed7cb1bca3d51289575edcc 100644 --- a/common/util.js +++ b/common/util.js @@ -2,7 +2,7 @@ exports.getIdFromFullId = fullId => { if (!fullId) return null if (typeof fullId === 'string') { - const re = /\/([a-z]{1,}\/[a-z]{1,}\/[a-z]{1,}\/v[0-9.]{1,}\/[0-9a-z-]{1,}$)/.exec(fullId) + const re = /([a-z]{1,}\/[a-z]{1,}\/[a-z]{1,}\/v[0-9.]{1,}\/[0-9a-z-]{1,}$)/.exec(fullId) if (re) return re[1] return null } else { diff --git a/package.json b/package.json index 73ca6d8e73543bffcd07b232a2562c5d79eec537..3800114bcea82a892be260eae8e04a33ad1bf79a 100644 --- a/package.json +++ b/package.json @@ -41,14 +41,12 @@ "@ngrx/effects": "^7.4.0", "@ngrx/store": "^7.4.0", "@ngtools/webpack": "^6.0.5", - "@types/chart.js": "^2.7.20", "@types/jasmine": "^3.3.12", "@types/node": "^12.0.0", "@types/webpack-env": "^1.13.6", "@typescript-eslint/eslint-plugin": "^2.12.0", "@typescript-eslint/parser": "^2.12.0", "angular2-template-loader": "^0.6.2", - "chart.js": "^2.7.2", "codelyzer": "^5.0.1", "core-js": "^3.0.1", "css-loader": "^3.2.0", @@ -69,9 +67,9 @@ "karma-jasmine": "^2.0.1", "karma-typescript": "^4.1.1", "karma-webpack": "^3.0.0", + "kg-dataset-previewer": "0.0.8", "lodash.merge": "^4.6.2", "mini-css-extract-plugin": "^0.8.0", - "ng2-charts": "^1.6.0", "ngx-bootstrap": "3.0.1", "node-sass": "^4.12.0", "protractor": "^5.4.2", diff --git a/src/index.html b/src/index.html index 51f07004e81a25768bbaa6b38fe1458a05318ffc..c70a7c418c63fe94907af7032a87b579604c21ea 100644 --- a/src/index.html +++ b/src/index.html @@ -13,6 +13,23 @@ <link rel="stylesheet" href="version.css"> <title>Interactive Atlas Viewer</title> + <script type="application/ld+json"> + { + "@context": "http://schema.org", + "@type": "Map", + "url": "https://interactive-viewer.apps.hbp.eu", + "name": "Human Brain Project Interactive Atlas Viewer", + "reference space": { + "@type": "Place", + "parcellation atlas": { + "@type": "Place", + "regions": [{ + "@type": "Place" + }] + } + } + } + </script> </head> <body> <atlas-viewer> diff --git a/src/main-common.ts b/src/main-common.ts index 14a3447d5a1c147f247771021dd3f613c3252af3..f20dbd91dcc4bdb56aeb68db06b1f85333f8a72e 100644 --- a/src/main-common.ts +++ b/src/main-common.ts @@ -2,7 +2,8 @@ import 'zone.js' import 'third_party/testSafari.js' import { enableProdMode } from '@angular/core'; -import { defineCustomElements } from 'hbp-connectivity-component/dist/loader' +import { defineCustomElements as defineConnectivityComponent } from 'hbp-connectivity-component/dist/loader' +import { defineCustomElements as definePreviewComponent } from 'kg-dataset-previewer/loader' import { platformBrowserDynamic } from '@angular/platform-browser-dynamic' import { MainModule } from './main.module'; @@ -16,4 +17,5 @@ requireAll(require.context(`./plugin_examples`, true)) platformBrowserDynamic().bootstrapModule(MainModule) -defineCustomElements(window) +defineConnectivityComponent(window) +definePreviewComponent(window) \ No newline at end of file diff --git a/src/main.module.ts b/src/main.module.ts index 096027f590d93de7afb12d7a1d8311744be0f2bd..d0e25476f263d78df2f028e2f6352d291372f8a5 100644 --- a/src/main.module.ts +++ b/src/main.module.ts @@ -79,7 +79,7 @@ import {TemplateCoordinatesTransformation} from "src/services/templateCoordinate NgViewerUseEffect, PluginServiceUseEffect, AtlasViewerHistoryUseEffect, - UiStateUseEffect, + UiStateUseEffect ]), StoreModule.forRoot({ pluginState, diff --git a/src/res/ext/MNI152.json b/src/res/ext/MNI152.json index cc88e19c20b47c922116ffdb97b56756cd6b8f91..679909551745b1a7170ceecd416154fa0d96a960 100644 --- a/src/res/ext/MNI152.json +++ b/src/res/ext/MNI152.json @@ -1,5 +1,6 @@ { "name": "MNI 152 ICBM 2009c Nonlinear Asymmetric", + "fullId": "minds/core/referencespace/v1.0.0/dafcffc5-4826-4bf1-8ff6-46b8a31ff8e2", "type": "template", "species": "Human", "useTheme": "dark", diff --git a/src/res/ext/bigbrain.json b/src/res/ext/bigbrain.json index abff253c1bcfe71b4fc29561fb23804a84882302..7ac96b80533d17e1a521a62a9b0465c599c03dfe 100644 --- a/src/res/ext/bigbrain.json +++ b/src/res/ext/bigbrain.json @@ -1,5 +1,6 @@ { "name": "Big Brain (Histology)", + "fullId": "minds/core/referencespace/v1.0.0/a1655b99-82f1-420f-a3c2-fe80fd4c8588", "type": "template", "species": "Human", "useTheme": "light", diff --git a/src/res/ext/colin.json b/src/res/ext/colin.json index c9a3d19638ccd97efbd20da73e12547e6ce87a5a..d80629a3b3dedf68cf0958d94ce55353ac0cbde0 100644 --- a/src/res/ext/colin.json +++ b/src/res/ext/colin.json @@ -1,5 +1,6 @@ { "name": "MNI Colin 27", + "fullId": "minds/core/referencespace/v1.0.0/7f39f7be-445b-47c0-9791-e971c0b6d992", "type": "template", "species": "Human", "useTheme": "dark", diff --git a/src/res/populateBigBrainRelatedAreas.js b/src/res/populateBigBrainRelatedAreas.js new file mode 100644 index 0000000000000000000000000000000000000000..1243d1cf8f86410f2120ae22fee0d2865bf97474 --- /dev/null +++ b/src/res/populateBigBrainRelatedAreas.js @@ -0,0 +1,7 @@ +const fs = require('fs') +const path = require('path') + +const bigbrain = fs.readFileSync(path.join(__dirname, './ext/bigbrain.json'), 'utf-8') +const colin = fs.readFileSync(path.join(__dirname, './ext/mni152.json'), 'utf-8') + +const bigbrainJson = JSON.parse(bigbrain) diff --git a/src/services/localFile.service.ts b/src/services/localFile.service.ts index 2d49e4919759f9b1cee303c4b04f527825c058a9..d4386edeca620ea7fc2ded16cd54623a265b6c97 100644 --- a/src/services/localFile.service.ts +++ b/src/services/localFile.service.ts @@ -3,6 +3,7 @@ import { Store } from "@ngrx/store"; import { KgSingleDatasetService } from "src/ui/databrowserModule/kgSingleDatasetService.service"; import { SNACKBAR_MESSAGE } from "./state/uiState.store"; import { IavRootStoreInterface } from "./stateStore.service"; +import { DATASETS_ACTIONS_TYPES } from "./state/dataStore.store"; /** * experimental service handling local user files such as nifti and gifti @@ -70,8 +71,15 @@ export class LocalFileService { URL.revokeObjectURL(this.niiUrl) } this.niiUrl = URL.createObjectURL(file) - this.singleDsService.showNewNgLayer({ - url: this.niiUrl, + + this.store.dispatch({ + type: DATASETS_ACTIONS_TYPES.PREVIEW_DATASET, + payload: { + file: { + mimetype: 'application/json', + url: this.niiUrl + } + } }) this.showLocalWarning() diff --git a/src/services/state/dataStore.store.ts b/src/services/state/dataStore.store.ts index 209bddbc53abe7b8c8291e4a1e24ab24bbf0f3fb..b20e8014b4ced68235888910cc51d5586cb8b0d3 100644 --- a/src/services/state/dataStore.store.ts +++ b/src/services/state/dataStore.store.ts @@ -4,16 +4,23 @@ import { Action } from '@ngrx/store' * TODO merge with databrowser.usereffect.ts */ +interface DatasetPreview { + dataset?: IDataEntry + file: Partial<ViewerPreviewFile>&{filename: string} +} + export interface IStateInterface { fetchedDataEntries: IDataEntry[] favDataEntries: IDataEntry[] fetchedSpatialData: IDataEntry[] + datasetPreviews: DatasetPreview[] } export const defaultState = { fetchedDataEntries: [], favDataEntries: [], fetchedSpatialData: [], + datasetPreviews: [], } export const getStateStore = ({ state: state = defaultState } = {}) => (prevState: IStateInterface = state, action: Partial<IActionInterface>) => { @@ -38,6 +45,18 @@ export const getStateStore = ({ state: state = defaultState } = {}) => (prevStat favDataEntries, } } + case ACTION_TYPES.PREVIEW_DATASET: { + + const { payload = {}} = action + const { file , dataset } = payload + return { + ...prevState, + datasetPreviews: prevState.datasetPreviews.concat({ + dataset, + file + }) + } + } default: return prevState } } @@ -59,6 +78,7 @@ export interface IActionInterface extends Action { favDataEntries: IDataEntry[] fetchedDataEntries: IDataEntry[] fetchedSpatialData: IDataEntry[] + payload?: any } export const FETCHED_DATAENTRIES = 'FETCHED_DATAENTRIES' @@ -167,6 +187,10 @@ export interface ViewerPreviewFile { name: string filename: string mimetype: string + referenceSpaces: { + name: string + fullId: string + }[] url?: string data?: any position?: any @@ -181,6 +205,7 @@ const ACTION_TYPES = { UPDATE_FAV_DATASETS: `UPDATE_FAV_DATASETS`, UNFAV_DATASET: 'UNFAV_DATASET', TOGGLE_FAV_DATASET: 'TOGGLE_FAV_DATASET', + PREVIEW_DATASET: 'PREVIEW_DATASET', } export const DATASETS_ACTIONS_TYPES = ACTION_TYPES diff --git a/src/services/state/uiState.store.ts b/src/services/state/uiState.store.ts index 18c29e926e90b85da53013c154f6c0ff4373f38a..af9ad54ddb6dbe866b1ef7ec948938389e9ee3cd 100644 --- a/src/services/state/uiState.store.ts +++ b/src/services/state/uiState.store.ts @@ -20,7 +20,6 @@ export const defaultState: StateInterface = { sidePanelExploreCurrentViewIsOpen: false, snackbarMessage: null, - bottomSheetTemplate: null, pluginRegionSelectionEnabled: false, diff --git a/src/ui/databrowserModule/databrowser.module.ts b/src/ui/databrowserModule/databrowser.module.ts index ca573d51709bca4b6a550a9955ad3ac242baabf6..0bd7944d6a3d7c258113146be6e3ac09b49d3192 100644 --- a/src/ui/databrowserModule/databrowser.module.ts +++ b/src/ui/databrowserModule/databrowser.module.ts @@ -1,23 +1,15 @@ import { CommonModule } from "@angular/common"; -import { NgModule } from "@angular/core"; +import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from "@angular/core"; import { FormsModule } from "@angular/forms"; -import { Chart } from 'chart.js' -import { ChartsModule } from "ng2-charts"; import { PopoverModule } from "ngx-bootstrap/popover"; import { TooltipModule } from "ngx-bootstrap/tooltip"; -import { AtlasViewerConstantsServices } from "src/atlasViewer/atlasViewer.constantService.service"; import { ComponentsModule } from "src/components/components.module"; import { AngularMaterialModule } from 'src/ui/sharedModules/angularMaterial.module' import { DoiParserPipe } from "src/util/pipes/doiPipe.pipe"; import { UtilModule } from "src/util/util.module"; import { DataBrowser } from "./databrowser/databrowser.component"; -import { LineChart } from "./fileviewer/chart/line/line.chart.component"; -import { RadarChart } from "./fileviewer/chart/radar/radar.chart.component"; -import { DedicatedViewer } from "./fileviewer/dedicated/dedicated.component"; -import { FileViewer } from "./fileviewer/fileviewer.component"; import { KgSingleDatasetService } from "./kgSingleDatasetService.service" import { ModalityPicker } from "./modalityPicker/modalityPicker.component"; -import { PreviewComponent } from "./preview/preview.component"; import { SingleDatasetView } from './singleDataset/detailedView/singleDataset.component' import { AggregateArrayIntoRootPipe } from "./util/aggregateArrayIntoRoot.pipe"; import { CopyPropertyPipe } from "./util/copyProperty.pipe"; @@ -34,10 +26,11 @@ import { SingleDatasetListView } from "./singleDataset/listView/singleDatasetLis import { AppendFilerModalityPipe } from "./util/appendFilterModality.pipe"; import { GetKgSchemaIdFromFullIdPipe } from "./util/getKgSchemaIdFromFullId.pipe"; import { ResetCounterModalityPipe } from "./util/resetCounterModality.pipe"; +import { PreviewFileVisibleInSelectedReferenceTemplatePipe } from "./util/previewFileDisabledByReferenceSpace.pipe"; +import { DatasetPreviewList, UnavailableTooltip } from "./singleDataset/datasetPreviews/datasetPreviewsList/datasetPreviewList.component"; @NgModule({ imports: [ - ChartsModule, CommonModule, ComponentsModule, ScrollingModule, @@ -50,13 +43,9 @@ import { ResetCounterModalityPipe } from "./util/resetCounterModality.pipe"; declarations: [ DataBrowser, ModalityPicker, - PreviewComponent, - FileViewer, - RadarChart, - LineChart, - DedicatedViewer, SingleDatasetView, SingleDatasetListView, + DatasetPreviewList, /** * pipes @@ -74,94 +63,31 @@ import { ResetCounterModalityPipe } from "./util/resetCounterModality.pipe"; PreviewFileTypePipe, AppendFilerModalityPipe, ResetCounterModalityPipe, + PreviewFileVisibleInSelectedReferenceTemplatePipe, + UnavailableTooltip, ], exports: [ DataBrowser, SingleDatasetView, SingleDatasetListView, - PreviewComponent, ModalityPicker, FilterDataEntriesbyMethods, - FileViewer, GetKgSchemaIdFromFullIdPipe, ], entryComponents: [ DataBrowser, - FileViewer, SingleDatasetView, ], providers: [ KgSingleDatasetService, ], + schemas: [ + CUSTOM_ELEMENTS_SCHEMA + ] /** * shouldn't need bootstrap, so no need for browser module */ }) export class DatabrowserModule { - constructor( - constantsService: AtlasViewerConstantsServices, - ) { - /** - * Because there is no easy way to display standard deviation natively, use a plugin - * */ - Chart.pluginService.register({ - - /* patching background color fill, so saved images do not look completely white */ - beforeDraw: (chart) => { - const ctx = chart.ctx as CanvasRenderingContext2D; - ctx.fillStyle = constantsService.darktheme ? - `rgba(50,50,50,0.8)` : - `rgba(255,255,255,0.8)` - - if (chart.canvas) { ctx.fillRect(0, 0, chart.canvas.width, chart.canvas.height) } - - }, - - /* patching standard deviation for polar (potentially also line/bar etc) graph */ - afterInit: (chart) => { - if (chart.config.options && chart.config.options.tooltips) { - - chart.config.options.tooltips.callbacks = { - label(tooltipItem, data) { - let sdValue - if (data.datasets && typeof tooltipItem.datasetIndex != 'undefined' && data.datasets[tooltipItem.datasetIndex].label) { - const sdLabel = data.datasets[tooltipItem.datasetIndex].label + '_sd' - const sd = data.datasets.find(dataset => typeof dataset.label != 'undefined' && dataset.label == sdLabel) - if (sd && sd.data && typeof tooltipItem.index != 'undefined' && typeof tooltipItem.yLabel != 'undefined') { sdValue = Number(sd.data[tooltipItem.index]) - Number(tooltipItem.yLabel) } - } - return `${tooltipItem.yLabel} ${sdValue ? '(' + sdValue + ')' : ''}` - }, - } - } - if (chart.data.datasets) { - chart.data.datasets = chart.data.datasets - .map(dataset => { - if (dataset.label && /_sd$/.test(dataset.label)) { - const originalDS = chart.data.datasets.find(baseDS => typeof baseDS.label !== 'undefined' && (baseDS.label == dataset.label.replace(/_sd$/, ''))) - if (originalDS) { - return Object.assign({}, dataset, { - data: (originalDS.data as number[]).map((datapoint, idx) => (Number(datapoint) + Number((dataset.data as number[])[idx]))), - ... constantsService.chartSdStyle, - }) - } else { - return dataset - } - } else if (dataset.label) { - const sdDS = chart.data.datasets.find(sdDS => typeof sdDS.label !== 'undefined' && (sdDS.label == dataset.label + '_sd')) - if (sdDS) { - return Object.assign({}, dataset, { - ...constantsService.chartBaseStyle, - }) - } else { - return dataset - } - } else { - return dataset - } - }) - } - }, - }) - } } diff --git a/src/ui/databrowserModule/databrowser.useEffect.ts b/src/ui/databrowserModule/databrowser.useEffect.ts index b448910731a83aa3780277850e35c76574172b40..4529a75447575ff32e38ae13658f779b3240b128 100644 --- a/src/ui/databrowserModule/databrowser.useEffect.ts +++ b/src/ui/databrowserModule/databrowser.useEffect.ts @@ -1,14 +1,18 @@ -import { Injectable, OnDestroy } from "@angular/core"; +import { Injectable, OnDestroy, TemplateRef } from "@angular/core"; import { Actions, Effect, ofType } from "@ngrx/effects"; import { select, Store } from "@ngrx/store"; import { from, merge, Observable, of, Subscription } from "rxjs"; -import { catchError, filter, map, scan, switchMap, withLatestFrom } from "rxjs/operators"; +import { catchError, filter, map, scan, switchMap, withLatestFrom, mapTo, shareReplay } from "rxjs/operators"; import { LoggingService } from "src/services/logging.service"; -import { DATASETS_ACTIONS_TYPES, IDataEntry } from "src/services/state/dataStore.store"; -import { IavRootStoreInterface } from "src/services/stateStore.service"; +import { DATASETS_ACTIONS_TYPES, IDataEntry, ViewerPreviewFile } from "src/services/state/dataStore.store"; +import { IavRootStoreInterface, ADD_NG_LAYER, CHANGE_NAVIGATION } from "src/services/stateStore.service"; import { LOCAL_STORAGE_CONST } from "src/util/constants"; import { getIdFromDataEntry } from "./databrowser.service"; import { KgSingleDatasetService } from "./kgSingleDatasetService.service"; +import { determinePreviewFileType, PREVIEW_FILE_TYPES } from "./preview/previewFileIcon.pipe"; +import { GLSL_COLORMAP_JET } from "src/atlasViewer/atlasViewer.constantService.service"; +import { SHOW_BOTTOM_SHEET } from "src/services/state/uiState.store"; +import { MatSnackBar, MatDialog } from "@angular/material"; const savedFav$ = of(window.localStorage.getItem(LOCAL_STORAGE_CONST.FAV_DATASET)).pipe( map(string => JSON.parse(string)), @@ -33,12 +37,80 @@ export class DataBrowserUseEffect implements OnDestroy { private subscriptions: Subscription[] = [] + // ng layer (currently only nifti file) needs to be previewed + @Effect() + previewNgLayer$: Observable<any> + + // when bottom sheet should be hidden (currently only when ng layer is visualised) + @Effect() + hideBottomSheet$: Observable<any> + + // when the preview effect has a ROI defined + @Effect() + navigateToPreviewPosition$: Observable<any> + + public previewDatasetFile$: Observable<ViewerPreviewFile> + constructor( private store$: Store<IavRootStoreInterface>, private actions$: Actions<any>, private kgSingleDatasetService: KgSingleDatasetService, private log: LoggingService, + private snackbar: MatSnackBar ) { + + this.previewDatasetFile$ = actions$.pipe( + ofType(DATASETS_ACTIONS_TYPES.PREVIEW_DATASET), + map(actionBody => { + const { payload = {} } = actionBody as any + const { file = null } = payload as { file: ViewerPreviewFile, dataset: IDataEntry } + return file + }), + shareReplay(1) + ) + + this.navigateToPreviewPosition$ = this.previewDatasetFile$.pipe( + filter(({ position }) => !!position), + switchMap(({ position }) => + this.snackbar.open(`Postion of interest found.`, 'Go there', { + duration: 5000, + }).afterDismissed().pipe( + filter(({ dismissedByAction }) => dismissedByAction), + mapTo({ + type: CHANGE_NAVIGATION, + navigation: { + position, + animation: {} + } + }) + ) + ) + ) + + this.previewNgLayer$ = this.previewDatasetFile$.pipe( + filter(file => + determinePreviewFileType(file) === PREVIEW_FILE_TYPES.NIFTI + ), + map(({ url }) => { + const layer = { + name: url, + source : `nifti://${url}`, + mixability : 'nonmixable', + shader : GLSL_COLORMAP_JET, + } + return { + type: ADD_NG_LAYER, + layer + } + }) + ) + + this.hideBottomSheet$ = this.previewNgLayer$.pipe( + mapTo({ + type: SHOW_BOTTOM_SHEET, + bottomSheetTemplate: null + }) + ) this.favDataEntries$ = this.store$.pipe( select('dataStore'), select('favDataEntries'), diff --git a/src/ui/databrowserModule/databrowser/databrowser.component.ts b/src/ui/databrowserModule/databrowser/databrowser.component.ts index 0d0b3a3f2572df1b7d5b04738338c6dd00c3b390..2dd92a75a3889871bf8d00f6ec71b30f8011c630 100644 --- a/src/ui/databrowserModule/databrowser/databrowser.component.ts +++ b/src/ui/databrowserModule/databrowser/databrowser.component.ts @@ -1,15 +1,10 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output, ViewChild } from "@angular/core"; import { merge, Observable, Subscription } from "rxjs"; -import { scan, shareReplay } from "rxjs/operators"; import { LoggingService } from "src/services/logging.service"; -import { ViewerPreviewFile } from "src/services/state/dataStore.store"; import { IDataEntry } from "src/services/stateStore.service"; import { CountedDataModality, DatabrowserService } from "../databrowser.service"; -import { KgSingleDatasetService } from "../kgSingleDatasetService.service"; import { ModalityPicker } from "../modalityPicker/modalityPicker.component"; -const scanFn: (acc: any[], curr: any) => any[] = (acc, curr) => [curr, ...acc] - @Component({ selector : 'data-browser', templateUrl : './databrowser.template.html', @@ -45,8 +40,6 @@ export class DataBrowser implements OnChanges, OnDestroy, OnInit { public countedDataM: CountedDataModality[] = [] public visibleCountedDataM: CountedDataModality[] = [] - public history$: Observable<Array<{file: ViewerPreviewFile, dataset: IDataEntry}>> - @ViewChild(ModalityPicker) public modalityPicker: ModalityPicker @@ -63,14 +56,9 @@ export class DataBrowser implements OnChanges, OnDestroy, OnInit { constructor( private dbService: DatabrowserService, private cdr: ChangeDetectorRef, - private singleDatasetSservice: KgSingleDatasetService, private log: LoggingService, ) { this.favDataentries$ = this.dbService.favedDataentries$ - this.history$ = this.singleDatasetSservice.previewingFile$.pipe( - scan(scanFn, []), - shareReplay(1), - ) } public ngOnChanges() { diff --git a/src/ui/databrowserModule/databrowser/databrowser.template.html b/src/ui/databrowserModule/databrowser/databrowser.template.html index 3005273d5cbbed7939009d9600f1272fc54455a8..9e0c3bd02a2db9bc6217186a3beb0092dd366c74 100644 --- a/src/ui/databrowserModule/databrowser/databrowser.template.html +++ b/src/ui/databrowserModule/databrowser/databrowser.template.html @@ -94,14 +94,6 @@ </ng-container> </ng-template> -<ng-template #filePreviewTemplate> - <preview-component - *ngIf="filePreviewName" - [datasetName]="filePreviewName"> - - </preview-component> -</ng-template> - <!-- modality picker / filter --> <ng-template #modalitySelector> <mat-accordion class="flex-grow-0 flex-shrink-0"> diff --git a/src/ui/databrowserModule/fileviewer/chart/chart.base.ts b/src/ui/databrowserModule/fileviewer/chart/chart.base.ts deleted file mode 100644 index d87209be74093f02e24a2519741d6f9849fd79ae..0000000000000000000000000000000000000000 --- a/src/ui/databrowserModule/fileviewer/chart/chart.base.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { ElementRef, ViewChild } from "@angular/core"; -import { DomSanitizer, SafeUrl } from "@angular/platform-browser"; -import { from, interval, Observable, Subject, Subscription } from "rxjs"; -import { filter, map, mapTo, shareReplay, switchMap, switchMapTo, take } from "rxjs/operators"; - -export class ChartBase { - @ViewChild('canvas') public canvas: ElementRef - - private _csvData: string - - public csvDataUrl: SafeUrl - public csvTitle: string - public imageTitle: string - - private _subscriptions: Subscription[] = [] - private _newChart$: Subject<any> = new Subject() - - private _csvUrl: string - private _pngUrl: string - - public csvUrl$: Observable<SafeUrl> - public pngUrl$: Observable<SafeUrl> - - constructor(private sanitizer: DomSanitizer) { - this.csvUrl$ = this._newChart$.pipe( - mapTo(this._csvData), - map(data => { - const blob = new Blob([data], { type: 'data:text/csv;charset=utf-8' }) - if (this._csvUrl) { window.URL.revokeObjectURL(this._csvUrl) } - this._csvUrl = window.URL.createObjectURL(blob) - return this.sanitizer.bypassSecurityTrustUrl(this._csvUrl) - }), - shareReplay(1), - ) - - this.pngUrl$ = this._newChart$.pipe( - switchMapTo( - interval(500).pipe( - map(() => this.canvas && this.canvas.nativeElement), - filter(v => !!v), - switchMap(el => - from( - new Promise(rs => el.toBlob(blob => rs(blob), 'image/png')), - ) as Observable<Blob>, - ), - filter(v => !!v), - take(1), - ), - ), - map(blob => { - if (this._pngUrl) { window.URL.revokeObjectURL(this._pngUrl) } - this._pngUrl = window.URL.createObjectURL(blob) - return this.sanitizer.bypassSecurityTrustUrl(this._pngUrl) - }), - shareReplay(1), - ) - - // necessary - this._subscriptions.push( - this.pngUrl$.subscribe(), - ) - - this._subscriptions.push( - this.csvUrl$.subscribe(), - ) - } - - public superOnDestroy() { - if (this._csvUrl) { window.URL.revokeObjectURL(this._csvUrl) } - if (this._pngUrl) { window.URL.revokeObjectURL(this._pngUrl) } - while (this._subscriptions.length > 0) { this._subscriptions.pop().unsubscribe() } - } - - public generateNewCsv(csvData: string) { - this._csvData = csvData - this.csvDataUrl = this.sanitizer.bypassSecurityTrustUrl(`data:text/csv;charset=utf-8,${csvData}`) - this._newChart$.next(null) - } - -} diff --git a/src/ui/databrowserModule/fileviewer/chart/chart.interface.ts b/src/ui/databrowserModule/fileviewer/chart/chart.interface.ts deleted file mode 100644 index 9af31b70279ef4e6cee85c43f81a83d8983650fd..0000000000000000000000000000000000000000 --- a/src/ui/databrowserModule/fileviewer/chart/chart.interface.ts +++ /dev/null @@ -1,84 +0,0 @@ -import { ChartOptions } from "chart.js"; - -import { ElementRef } from "@angular/core"; -import { SafeUrl } from "@angular/platform-browser"; -import merge from 'lodash.merge' - -export interface ScaleOptionInterface { - type?: string - display?: boolean - gridLines?: GridLinesOptionInterface - ticks?: ScaleTickOptionInterface - scaleLabel?: ScaleLabelInterface -} - -export interface GridLinesOptionInterface { - display?: boolean - color?: string -} - -export interface ScaleTickOptionInterface { - min?: number - max?: number - stepSize?: number - backdropColor?: string - showLabelBackdrop?: boolean - suggestedMin?: number - suggestedMax?: number - beginAtZero?: boolean - fontColor?: string -} - -export interface ChartColor { - backgroundColor?: string - borderColor?: string - pointBackgroundColor?: string - pointBorderColor?: string - pointHoverBackgroundColor?: string - pointHoverBorderColor?: string -} - -export interface DatasetInterface { - labels: string[] | number[] - datasets: Dataset[] -} - -export interface Dataset { - data: number[] - label?: string - borderWidth?: number - borderDash?: number[] - fill?: string|number|boolean - backgroundColor: string -} - -export interface LegendInterface { - display?: boolean - labels?: { - fontColor?: string - } -} - -export interface TitleInterfacce { - display?: boolean - text?: string - fontColor?: string -} - -export interface ScaleLabelInterface { - labelString?: string - fontColor?: string - display?: boolean -} - -export interface CommonChartInterface { - csvDataUrl: SafeUrl - csvTitle: string - imageTitle: string - - canvas: ElementRef -} - -export const applyOption = (defaultOption: ChartOptions, option?: Partial<ChartOptions>) => { - merge(defaultOption, option) -} diff --git a/src/ui/databrowserModule/fileviewer/chart/line/line.chart.component.ts b/src/ui/databrowserModule/fileviewer/chart/line/line.chart.component.ts deleted file mode 100644 index 983e682c6b7f11eae2cc93484c27d83468b4d588..0000000000000000000000000000000000000000 --- a/src/ui/databrowserModule/fileviewer/chart/line/line.chart.component.ts +++ /dev/null @@ -1,181 +0,0 @@ -import { Component, Input, OnChanges } from '@angular/core' -import { applyOption, ChartColor, CommonChartInterface, DatasetInterface, LegendInterface, ScaleOptionInterface, TitleInterfacce } from '../chart.interface' - -import { DomSanitizer } from '@angular/platform-browser'; -import { ChartDataSets, ChartOptions, LinearTickOptions } from 'chart.js'; -import { Color } from 'ng2-charts'; -import { ChartBase } from '../chart.base'; - -@Component({ - selector : `line-chart`, - templateUrl : './line.chart.template.html', - styleUrls : [ - `./line.chart.style.css`, - ], - exportAs: 'iavLineChart', -}) -export class LineChart extends ChartBase implements OnChanges, CommonChartInterface { - - /** - * labels of each of the columns, spider web edges - */ - @Input() public labels: string[] - - /** - * shown on the legend, different lines - */ - @Input() public lineDatasets: LineDatasetInputInterface[] = [] - - /** - * colors of the datasetes - */ - @Input() public colors: ChartColor[] = [] - - @Input() public options: any - - public mousescroll(_ev: MouseWheelEvent) { - - /** - * temporarily disabled - */ - - } - - public maxY: number - - public xAxesTicks: LinearTickOptions = { - stepSize: 20, - fontColor: 'white', - } - public chartOption: Partial<LineChartOption> = { - responsive: true, - scales: { - xAxes: [{ - type: 'linear', - gridLines: { - color: 'rgba(128,128,128,0.5)', - }, - ticks: this.xAxesTicks, - scaleLabel: { - display: true, - labelString: 'X Axes label', - fontColor: 'rgba(200,200,200,1.0)', - }, - }], - yAxes: [{ - gridLines: { - color: 'rgba(128,128,128,0.5)', - }, - ticks: { - fontColor: 'white', - }, - scaleLabel: { - display: true, - labelString: 'Y Axes label', - fontColor: 'rgba(200,200,200,1.0)', - }, - }], - }, - legend: { - display: true, - }, - title: { - display: true, - text: 'Title', - fontColor: 'rgba(255,255,255,1.0)', - }, - color: [{ - backgroundColor: `rgba(255,255,255,0.2)`, - }], - animation : undefined, - elements: - { - point: { - radius: 0, - hoverRadius: 8, - hitRadius: 4, - }, - }, - - } - - public chartDataset: DatasetInterface = { - labels: [], - datasets: [], - } - - public shapedLineChartDatasets: ChartDataSets[] - - constructor(sanitizer: DomSanitizer) { - super(sanitizer) - } - - public ngOnChanges() { - this.shapedLineChartDatasets = this.lineDatasets.map(lineDataset => ({ - data: lineDataset.data.map((v, idx) => ({ - x: idx, - y: v, - })), - fill: 'origin', - })) - - this.maxY = this.chartDataset.datasets.reduce((max, dataset) => { - return Math.max( - max, - dataset.data.reduce((max, number) => { - return Math.max(number, max) - }, 0)) - }, 0) - - applyOption(this.chartOption, this.options) - - this.generateDataUrl() - } - - public getDataPointString(input: any): string { - return typeof input === 'number' || typeof input === 'string' - ? input.toString() - : this.getDataPointString(input.y) - } - - private generateDataUrl() { - const row0 = [this.chartOption.scales.xAxes[0].scaleLabel.labelString].concat(this.chartOption.scales.yAxes[0].scaleLabel.labelString).join(',') - const maxRows = this.lineDatasets.reduce((acc, lds) => Math.max(acc, lds.data.length), 0) - const rows = Array.from(Array(maxRows)).map((_, idx) => [idx.toString()].concat(this.shapedLineChartDatasets.map(v => v.data[idx] ? this.getDataPointString(v.data[idx]) : '').join(','))).join('\n') - const csvData = `${row0}\n${rows}` - - this.generateNewCsv(csvData) - - this.csvTitle = `${this.getGraphTitleAsString().replace(/\s/g, '_')}.csv` - this.imageTitle = `${this.getGraphTitleAsString().replace(/\s/g, '_')}.png` - } - - private getGraphTitleAsString(): string { - try { - return this.chartOption.title.text.constructor === Array - ? (this.chartOption.title.text as string[]).join(' ') - : this.chartOption.title.text as string - } catch (e) { - return `Untitled` - } - } -} - -export interface LineDatasetInputInterface { - label?: string - data: number[] -} - -export interface LinearChartOptionInterface { - scales?: { - xAxes?: ScaleOptionInterface[] - yAxes?: ScaleOptionInterface[] - } - legend?: LegendInterface - title?: TitleInterfacce - color?: Color[] -} - -interface LineChartOption extends ChartOptions { - color?: Color[] -} diff --git a/src/ui/databrowserModule/fileviewer/chart/line/line.chart.style.css b/src/ui/databrowserModule/fileviewer/chart/line/line.chart.style.css deleted file mode 100644 index 7575b8e3247550c8d145a91ba034199fddc2feb9..0000000000000000000000000000000000000000 --- a/src/ui/databrowserModule/fileviewer/chart/line/line.chart.style.css +++ /dev/null @@ -1,4 +0,0 @@ -:host -{ - display: block; -} diff --git a/src/ui/databrowserModule/fileviewer/chart/line/line.chart.template.html b/src/ui/databrowserModule/fileviewer/chart/line/line.chart.template.html deleted file mode 100644 index fb69619ff98aebbddc81b1535ee3b0eb20b5662e..0000000000000000000000000000000000000000 --- a/src/ui/databrowserModule/fileviewer/chart/line/line.chart.template.html +++ /dev/null @@ -1,14 +0,0 @@ -<div *ngIf="shapedLineChartDatasets" - class="position-relative col-12"> - <canvas baseChart - (mousewheel)="mousescroll($event)" - height="500" - width="500" - chartType="line" - [options]="chartOption" - [colors]="colors" - [datasets]="shapedLineChartDatasets" - [labels]="chartDataset.labels" - #canvas> - </canvas> -</div> \ No newline at end of file diff --git a/src/ui/databrowserModule/fileviewer/chart/radar/radar.chart.component.ts b/src/ui/databrowserModule/fileviewer/chart/radar/radar.chart.component.ts deleted file mode 100644 index f86da382d5106de808a3e859e65819909724c701..0000000000000000000000000000000000000000 --- a/src/ui/databrowserModule/fileviewer/chart/radar/radar.chart.component.ts +++ /dev/null @@ -1,171 +0,0 @@ -import { Component, Input, OnChanges, OnDestroy } from '@angular/core' - -import { DomSanitizer } from '@angular/platform-browser'; -import { RadialChartOptions } from 'chart.js' -import { Color } from 'ng2-charts'; -import { ChartBase } from '../chart.base'; -import { applyOption, ChartColor, CommonChartInterface, DatasetInterface, LegendInterface, ScaleOptionInterface, TitleInterfacce } from '../chart.interface'; -@Component({ - selector : `radar-chart`, - templateUrl : './radar.chart.template.html', - styleUrls : [ - `./radar.chart.style.css`, - ], - exportAs: 'iavRadarChart', -}) -export class RadarChart extends ChartBase implements OnDestroy, OnChanges, CommonChartInterface { - - /** - * labels of each of the columns, spider web edges - */ - @Input() public labels: string[] = [] - - /** - * shown on the legend, different lines - */ - @Input() public radarDatasets: RadarDatasetInputInterface[] = [] - - /** - * colors of the datasetes - */ - @Input() public colors: ChartColor[] = [] - - @Input() public options: any - - public mousescroll(_ev: MouseWheelEvent) { - - /** - * mouse wheel zooming disabled for now - */ - /** - * TODO the sroll up event sometimes does not get prevented for some reasons... - */ - - // ev.stopPropagation() - // ev.preventDefault() - - // this.maxY *= ev.deltaY > 0 ? 1.2 : 0.8 - // const newTicksObj = { - // stepSize : Math.ceil( this.maxY / 500 ) * 100, - // max : this.maxY - // } - - // const combineTicksObj = Object.assign({},this.chartOption.scale!.ticks,newTicksObj) - // const combineScale = Object.assign({},this.chartOption.scale,{ticks:combineTicksObj}) - // this.chartOption = Object.assign({},this.chartOption,{scale : combineScale,animation : false}) - } - - public maxY: number - - public chartOption: Partial<RadialChartOptions> = { - responsive: true, - scale : { - gridLines : { - color : 'rgba(128,128,128,0.5)', - }, - ticks : { - showLabelBackdrop : false, - fontColor : 'white', - }, - angleLines : { - color : 'rgba(128,128,128,0.2)', - }, - pointLabels : { - fontColor : 'white', - }, - }, - legend : { - display: true, - labels : { - fontColor : 'white', - }, - }, - title : { - text : 'Radar graph', - display : true, - fontColor : 'rgba(255,255,255,1.0)', - }, - animation: null, - } - - public chartDataset: DatasetInterface = { - labels : [], - datasets : [], - } - - constructor(sanitizer: DomSanitizer) { - super(sanitizer) - } - - public ngOnDestroy() { - this.superOnDestroy() - } - - public ngOnChanges() { - this.chartDataset = { - labels : this.labels, - datasets : this.radarDatasets.map(ds => Object.assign({}, ds, {backgroundColor : 'rgba(255,255,255,0.2)'})), - } - // this.chartDataset.datasets[0] - - this.maxY = this.chartDataset.datasets.reduce((max, dataset) => { - return Math.max( - max, - dataset.data.reduce((max, number) => { - return Math.max(number, max) - }, 0)) - }, 0) - - applyOption(this.chartOption, this.options) - - this.generateDataUrl() - } - - private generateDataUrl() { - const row0 = ['Receptors', ...this.chartDataset.datasets.map(ds => ds.label || 'no label')].join(',') - const otherRows = (this.chartDataset.labels as string[]) - .map((label, index) => [ label, ...this.chartDataset.datasets.map(ds => ds.data[index]) ].join(',')).join('\n') - const csvData = `${row0}\n${otherRows}` - - this.generateNewCsv(csvData) - - this.csvTitle = `${this.getGraphTitleAsString().replace(/\s/g, '_')}.csv` - this.imageTitle = `${this.getGraphTitleAsString().replace(/\s/g, '_')}.png` - } - - private getGraphTitleAsString(): string { - try { - return this.chartOption.title.text as string - } catch (e) { - return `Untitled` - } - } -} - -export interface RadarDatasetInputInterface { - label: string - data: number[] -} - -export interface RadarChartOptionInterface { - scale?: ScaleOptionInterface&RadarScaleOptionAdditionalInterface - animation?: any - legend?: LegendInterface - title?: TitleInterfacce - color?: Color[] -} - -interface RadarScaleOptionAdditionalInterface { - angleLines?: AngleLineInterface - pointLabels?: PointLabelInterface -} - -interface AngleLineInterface { - display?: boolean - color?: string - lineWidth?: number -} - -interface PointLabelInterface { - fontColor?: string -} diff --git a/src/ui/databrowserModule/fileviewer/chart/radar/radar.chart.style.css b/src/ui/databrowserModule/fileviewer/chart/radar/radar.chart.style.css deleted file mode 100644 index 7575b8e3247550c8d145a91ba034199fddc2feb9..0000000000000000000000000000000000000000 --- a/src/ui/databrowserModule/fileviewer/chart/radar/radar.chart.style.css +++ /dev/null @@ -1,4 +0,0 @@ -:host -{ - display: block; -} diff --git a/src/ui/databrowserModule/fileviewer/chart/radar/radar.chart.template.html b/src/ui/databrowserModule/fileviewer/chart/radar/radar.chart.template.html deleted file mode 100644 index 17defea39a2b3af6ca2e561295d7f759a2f4a493..0000000000000000000000000000000000000000 --- a/src/ui/databrowserModule/fileviewer/chart/radar/radar.chart.template.html +++ /dev/null @@ -1,21 +0,0 @@ -<div *ngIf="chartDataset.datasets.length > 0 && chartDataset.labels.length > 0" - class="position-relative col-12"> - - <!-- baseChart directive is needed by ng2-chart --> - <canvas baseChart - (mousewheel)="mousescroll($event)" - class="h-100 w-100" - height="500" - width="500" - chartType="radar" - [options]="chartOption" - [colors]="colors" - [datasets]="chartDataset.datasets" - [labels]="chartDataset.labels" - #canvas> - </canvas> -</div> - -<span *ngIf="chartDataset.datasets.length === 0 || chartDataset.labels.length === 0"> - datasets and labels are required to display radar -</span> \ No newline at end of file diff --git a/src/ui/databrowserModule/fileviewer/dedicated/dedicated.component.ts b/src/ui/databrowserModule/fileviewer/dedicated/dedicated.component.ts deleted file mode 100644 index 623c48aa3d5506387db09d2f676ba1018c60818e..0000000000000000000000000000000000000000 --- a/src/ui/databrowserModule/fileviewer/dedicated/dedicated.component.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { Component, Input } from "@angular/core"; -import { ViewerPreviewFile } from "src/services/state/dataStore.store"; -import { KgSingleDatasetService } from "../../kgSingleDatasetService.service"; - -@Component({ - selector : 'dedicated-viewer', - templateUrl : './dedicated.template.html', - styleUrls : [ - `./dedicated.style.css`, - ], -}) - -export class DedicatedViewer { - @Input() public previewFile: ViewerPreviewFile - - constructor( - private singleKgDsService: KgSingleDatasetService, - ) { - - } - - get isShowing() { - return this.singleKgDsService.ngLayers.has(this.previewFile.url) - } - - public showDedicatedView() { - this.singleKgDsService.showNewNgLayer({ url: this.previewFile.url }) - } - - public removeDedicatedView() { - this.singleKgDsService.removeNgLayer({ url: this.previewFile.url }) - } - - public click(event: MouseEvent) { - event.preventDefault() - this.isShowing - ? this.removeDedicatedView() - : this.showDedicatedView() - } -} diff --git a/src/ui/databrowserModule/fileviewer/dedicated/dedicated.style.css b/src/ui/databrowserModule/fileviewer/dedicated/dedicated.style.css deleted file mode 100644 index c281f74383e41b752f15be648e2d05cebda0e41a..0000000000000000000000000000000000000000 --- a/src/ui/databrowserModule/fileviewer/dedicated/dedicated.style.css +++ /dev/null @@ -1,5 +0,0 @@ -a -{ - margin: 0 1em; - transition: all 200ms ease; -} diff --git a/src/ui/databrowserModule/fileviewer/dedicated/dedicated.template.html b/src/ui/databrowserModule/fileviewer/dedicated/dedicated.template.html deleted file mode 100644 index 75343992eaf8ecab64286c466a4ae70d0e856e10..0000000000000000000000000000000000000000 --- a/src/ui/databrowserModule/fileviewer/dedicated/dedicated.template.html +++ /dev/null @@ -1,22 +0,0 @@ -<div class="alert"> - You can directly preview this nifti file overlaying the atlas viewer. -</div> - - -<div class="w-100 d-flex align-items-center flex-column"> - -<div *ngIf="isShowing" class="d-flex flex-column align-items-center"> - <i class="far fa-eye h3" [ngStyle]="isShowing && {'color' : '#04D924'}"></i> - <span>File is added</span> -</div> - - <button *ngIf="!isShowing" mat-button class="p-3 outline-none mat-raised-button" [disabled]="isShowing" color="primary" (click)="!isShowing && showDedicatedView()"> - <span class="ml-2">Click to add file in Atlas Viewer</span> - </button> - - <button mat-button *ngIf="isShowing" class="mat-raised-button p-3 outline-none" color="primary" (click)="removeDedicatedView()"> - <i class="fas fa-eye-slash" style="color: #D93D04"></i> - Remove file from <b>Atlas Viewer</b> - </button> - -</div> diff --git a/src/ui/databrowserModule/fileviewer/fileviewer.component.ts b/src/ui/databrowserModule/fileviewer/fileviewer.component.ts deleted file mode 100644 index 1ae371c7d48dfa85e5efca873734a7fede49826d..0000000000000000000000000000000000000000 --- a/src/ui/databrowserModule/fileviewer/fileviewer.component.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { ChangeDetectorRef, Component, Inject, Input, OnChanges, Optional, ViewChild } from '@angular/core' - -import { MAT_DIALOG_DATA } from '@angular/material'; -import { ViewerPreviewFile } from 'src/services/state/dataStore.store'; -import { ChartBase } from './chart/chart.base'; - -@Component({ - selector : 'file-viewer', - templateUrl : './fileviewer.template.html' , - styleUrls : [ - './fileviewer.style.css', - ], -}) - -export class FileViewer implements OnChanges { - - public childChart: ChartBase - - @ViewChild('childChart') - set setChildChart(childChart: ChartBase) { - this.childChart = childChart - this.cdr.detectChanges() - } - - /** - * fetched directly from KG - */ - @Input() public previewFile: ViewerPreviewFile - - constructor( - private cdr: ChangeDetectorRef, - @Optional() @Inject(MAT_DIALOG_DATA) data, - ) { - if (data) { - this.previewFile = data.previewFile - this.downloadUrl = this.previewFile.url - } - } - - public downloadUrl: string - public ngOnChanges() { - this.downloadUrl = this.previewFile.url - } -} diff --git a/src/ui/databrowserModule/fileviewer/fileviewer.style.css b/src/ui/databrowserModule/fileviewer/fileviewer.style.css deleted file mode 100644 index e850661e5c5879af558758be2476da087685349a..0000000000000000000000000000000000000000 --- a/src/ui/databrowserModule/fileviewer/fileviewer.style.css +++ /dev/null @@ -1,34 +0,0 @@ -small#captions -{ - display:inline-block; - margin:0 1em; -} - -div[emptyRow] -{ - margin-bottom:-1em; -} - -img -{ - width: 100%; -} - -[citationContainer] -{ - display:inline-block; - max-width: 100%; - box-sizing: border-box; - padding: 0.5em 1em; -} - -kg-entry-viewer -{ - padding: 1em; - display: block; -} - -div[mimetypeTextContainer] -{ - margin:1em; -} diff --git a/src/ui/databrowserModule/fileviewer/fileviewer.template.html b/src/ui/databrowserModule/fileviewer/fileviewer.template.html deleted file mode 100644 index cee788c3974a892baf17bb5839a9b50eba728f19..0000000000000000000000000000000000000000 --- a/src/ui/databrowserModule/fileviewer/fileviewer.template.html +++ /dev/null @@ -1,113 +0,0 @@ -<div class="w-100 d-flex justify-content-center mt-2 font-weight-bold"> - {{previewFile.name}} -</div> - -<div *ngIf = "!previewFile"> - previewFile as an input is required for this component to work... -</div> -<div - *ngIf = "previewFile" - [ngSwitch]="previewFile.mimetype"> - - <div emptyRow *ngSwitchCase = "'application/octet-stream'"> - <!-- downloadable data --> - </div> - - <!-- image --> - <div *ngSwitchCase="'image/jpeg'" > - <!-- TODO figure out a more elegant way of dealing with draggable img --> - <img draggable="false" [src]="previewFile.url" /> - </div> - - <!-- data container --> - <div [ngSwitch]="previewFile.data.chartType" - *ngSwitchCase="'application/json'"> - - <ng-container *ngSwitchCase="'radar'"> - <radar-chart - #childChart="iavRadarChart" - [colors]="previewFile.data.colors" - [options]="previewFile.data.chartOptions" - [labels]="previewFile.data.labels" - [radarDatasets]="previewFile.data.datasets"> - - </radar-chart> - - </ng-container> - - <ng-container *ngSwitchCase="'line'"> - <line-chart - #childChart="iavLineChart" - [colors]="previewFile.data.colors" - [options]="previewFile.data.chartOptions" - [lineDatasets]="previewFile.data.datasets"> - - </line-chart> - </ng-container> - <div *ngSwitchDefault> - The json file is not a chart. I mean, we could dump the json, but would it really help? - </div> - </div> - - <div *ngSwitchCase="'application/hibop'"> - <div mimetypeTextContainer> - You will need to install the HiBoP software on your computer, click the 'Download File' button and open the .hibop file. - </div> - </div> - - <div *ngSwitchCase="'application/nifti'"> - <dedicated-viewer - [previewFile]="previewFile"> - </dedicated-viewer> - </div> - - <div *ngSwitchCase="'application/nehuba-layer'"> - APPLICATION NEHUBA LAYER - NOT YET IMPLEMENTED - </div> - - <div *ngSwitchDefault> - <div mimetypeTextContainer> - The selected file with the mimetype {{ previewFile.mimetype }} could not be displayed. - </div> - </div> - -</div> - -<div class="mt-1 w-100 d-flex justify-content-end pl-1 pr-1 pt-2 pb-2"> - - <a mat-icon-button - matTooltip="Download line graph as csv" - [hidden]="!(childChart && childChart.csvDataUrl)" - - target="_blank" - [download]="childChart && childChart.csvTitle" - [href]="childChart && childChart.csvDataUrl"> - - <i class="fas fa-file-csv"></i> - </a> - - <!-- nb --> - <!-- cross origin download attribute will be ignored --> - <!-- this effective means that when dev on localhost:8080, resource request to localhost:3000 will always open in a new window --> - <!-- link: https://developers.google.com/web/updates/2018/02/chrome-65-deprecations#block_cross-origin_wzxhzdk5a_download --> - <a mat-icon-button - *ngIf="downloadUrl" - [href]="downloadUrl" - target="_blank" - download - matTooltip="Download File"> - <i class="fas fa-download"></i> - </a> - - - <a mat-icon-button - *ngIf="childChart" - target="_blank" - [href]="childChart.pngUrl$ | async" - [download]="childChart && childChart.imageTitle" - - matTooltip="Download chart as an image"> - <i class="fas fa-download "></i> - </a> - -</div> diff --git a/src/ui/databrowserModule/kgSingleDatasetService.service.ts b/src/ui/databrowserModule/kgSingleDatasetService.service.ts index 06c45708d49220725416492eb384ed32ca9a2b8b..9544ef15f53013ffac9e3d238bb7b5ca2678ef74 100644 --- a/src/ui/databrowserModule/kgSingleDatasetService.service.ts +++ b/src/ui/databrowserModule/kgSingleDatasetService.service.ts @@ -1,22 +1,17 @@ import { HttpClient } from "@angular/common/http"; import { Injectable, OnDestroy, TemplateRef } from "@angular/core"; -import { MatDialog, MatSnackBar } from "@angular/material"; import { select, Store } from "@ngrx/store"; -import { Subject, Subscription } from "rxjs"; +import { Subscription } from "rxjs"; import { filter } from "rxjs/operators"; -import { AtlasViewerConstantsServices, GLSL_COLORMAP_JET } from "src/atlasViewer/atlasViewer.constantService.service" -import { IDataEntry, ViewerPreviewFile } from "src/services/state/dataStore.store"; +import { AtlasViewerConstantsServices } from "src/atlasViewer/atlasViewer.constantService.service" +import { IDataEntry, ViewerPreviewFile, DATASETS_ACTIONS_TYPES } from "src/services/state/dataStore.store"; import { SHOW_BOTTOM_SHEET } from "src/services/state/uiState.store"; -import { ADD_NG_LAYER, CHANGE_NAVIGATION, IavRootStoreInterface, REMOVE_NG_LAYER } from "src/services/stateStore.service"; -import { FileViewer } from "./fileviewer/fileviewer.component"; -import { determinePreviewFileType, PREVIEW_FILE_TYPES } from "./preview/previewFileIcon.pipe"; +import { IavRootStoreInterface, REMOVE_NG_LAYER } from "src/services/stateStore.service"; import { GetKgSchemaIdFromFullIdPipe } from "./util/getKgSchemaIdFromFullId.pipe"; @Injectable({ providedIn: 'root' }) export class KgSingleDatasetService implements OnDestroy { - public previewingFile$: Subject<{file: ViewerPreviewFile, dataset: IDataEntry}> = new Subject() - private subscriptions: Subscription[] = [] public ngLayers: Set<string> = new Set() @@ -25,9 +20,7 @@ export class KgSingleDatasetService implements OnDestroy { constructor( private constantService: AtlasViewerConstantsServices, private store$: Store<IavRootStoreInterface>, - private dialog: MatDialog, private http: HttpClient, - private snackBar: MatSnackBar, ) { this.subscriptions.push( @@ -46,6 +39,7 @@ export class KgSingleDatasetService implements OnDestroy { } } + // TODO deprecate, in favour of web component public datasetHasPreview({ name }: { name: string } = { name: null }) { if (!name) { throw new Error('kgSingleDatasetService#datasetHashPreview name must be defined') } const _url = new URL(`datasets/hasPreview`, this.constantService.backendUrl ) @@ -82,60 +76,12 @@ export class KgSingleDatasetService implements OnDestroy { } public previewFile(file: ViewerPreviewFile, dataset: IDataEntry) { - this.previewingFile$.next({ - file, - dataset, - }) - - const { position } = file - if (position) { - this.snackBar.open(`Postion of interest found.`, 'Go there', { - duration: 5000, - }) - .afterDismissed() - .subscribe(({ dismissedByAction }) => { - if (dismissedByAction) { - this.store$.dispatch({ - type: CHANGE_NAVIGATION, - navigation: { - position, - animation: {}, - }, - }) - } - }) - } - - const type = determinePreviewFileType(file) - if (type === PREVIEW_FILE_TYPES.NIFTI) { - this.store$.dispatch({ - type: SHOW_BOTTOM_SHEET, - bottomSheetTemplate: null, - }) - const { url } = file - this.showNewNgLayer({ url }) - return - } - - this.dialog.open(FileViewer, { - data: { - previewFile: file, - }, - autoFocus: false, - }) - } - - public showNewNgLayer({ url }): void { - - const layer = { - name : url, - source : `nifti://${url}`, - mixability : 'nonmixable', - shader : GLSL_COLORMAP_JET, - } this.store$.dispatch({ - type: ADD_NG_LAYER, - layer, + type: DATASETS_ACTIONS_TYPES.PREVIEW_DATASET, + payload: { + file, + dataset + } }) } diff --git a/src/ui/databrowserModule/preview/preview.component.ts b/src/ui/databrowserModule/preview/preview.component.ts deleted file mode 100644 index f5bf30d994669019aa11672bd8263e68e59de5cf..0000000000000000000000000000000000000000 --- a/src/ui/databrowserModule/preview/preview.component.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output } from "@angular/core"; -import { ViewerPreviewFile } from "src/services/state/dataStore.store"; -import { DatabrowserService } from "../databrowser.service"; - -const getRenderNodeFn = ({name : activeFileName = ''} = {}) => ({name = '', path = 'unpathed'}) => name - ? activeFileName === name - ? `<span class="text-warning">${name}</span>` - : name - : path - -@Component({ - selector: 'preview-component', - templateUrl: './previewList.template.html', - styleUrls: [ - './preview.style.css', - ], - changeDetection: ChangeDetectionStrategy.OnPush, -}) - -export class PreviewComponent implements OnInit { - @Input() public datasetName: string - @Output() public previewFile: EventEmitter<ViewerPreviewFile> = new EventEmitter() - - public fetchCompleteFlag: boolean = false - - public previewFiles: ViewerPreviewFile[] = [] - public activeFile: ViewerPreviewFile - private error: string - - constructor( - private dbrService: DatabrowserService, - private cdr: ChangeDetectorRef, - ) { - this.renderNode = getRenderNodeFn() - } - - public previewFileClick(ev, el) { - - ev.event.preventDefault() - ev.event.stopPropagation() - - if (ev.inputItem.children.length > 0) { - el.toggleCollapse(ev.inputItem) - } else { - this.activeFile = ev.inputItem - this.renderNode = getRenderNodeFn(this.activeFile) - } - - this.cdr.markForCheck() - } - - public renderNode: (obj: any) => string - - public 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] - this.renderNode = getRenderNodeFn(this.activeFile) - } - }) - .catch(e => { - this.error = JSON.stringify(e) - }) - .finally(() => { - this.fetchCompleteFlag = true - this.cdr.markForCheck() - }) - } - } -} diff --git a/src/ui/databrowserModule/preview/preview.style.css b/src/ui/databrowserModule/preview/preview.style.css deleted file mode 100644 index 627947e7c8a27910c5dc1007cdb650ca895791ea..0000000000000000000000000000000000000000 --- a/src/ui/databrowserModule/preview/preview.style.css +++ /dev/null @@ -1,6 +0,0 @@ -.readmore-wrapper -{ - font-size: 80%; - max-height: 25em; - overflow: auto; -} \ No newline at end of file diff --git a/src/ui/databrowserModule/preview/preview.template.html b/src/ui/databrowserModule/preview/preview.template.html deleted file mode 100644 index 1eb28f46694f3252ffc923011d1106230a6543b8..0000000000000000000000000000000000000000 --- a/src/ui/databrowserModule/preview/preview.template.html +++ /dev/null @@ -1,22 +0,0 @@ -<div *ngIf="activeFile"> - {{ activeFile.name }} -</div> -<file-viewer - *ngIf="activeFile" - [previewFile]="activeFile"> - -</file-viewer> - -<hr /> -<div class="readmore-wrapper"> - - <flat-tree-component - #flatTreeNode - *ngIf="previewFiles | copyProperty : 'filename' : 'path' | pathToNestedChildren | aggregateArrayIntoRootPipe : 'Files'; let rootFile" - [useDefaultList]="true" - [renderNode]="renderNode" - [inputItem]="rootFile" - (treeNodeClick)="previewFileClick($event, flatTreeNode)"> - - </flat-tree-component> -</div> \ No newline at end of file diff --git a/src/ui/databrowserModule/preview/previewFileIcon.pipe.ts b/src/ui/databrowserModule/preview/previewFileIcon.pipe.ts index 038f48a980771893863f23e9faec1eefd7cdb112..bbf950646e38b46fe37b66151c8508f7df6b3e29 100644 --- a/src/ui/databrowserModule/preview/previewFileIcon.pipe.ts +++ b/src/ui/databrowserModule/preview/previewFileIcon.pipe.ts @@ -34,8 +34,9 @@ export class PreviewFileIconPipe implements PipeTransform { } export const determinePreviewFileType = (previewFile: ViewerPreviewFile) => { + if (!previewFile) return null const { mimetype, data } = previewFile - const { chartType = null } = data || {} + const chartType = data && data['chart.js'] && data['chart.js'].type if ( mimetype === 'application/nifti' ) { return PREVIEW_FILE_TYPES.NIFTI } if ( /^image/.test(mimetype)) { return PREVIEW_FILE_TYPES.IMAGE } if ( /application\/json/.test(mimetype) && (chartType === 'line' || chartType === 'radar')) { return PREVIEW_FILE_TYPES.CHART } diff --git a/src/ui/databrowserModule/preview/previewList.template.html b/src/ui/databrowserModule/preview/previewList.template.html deleted file mode 100644 index de6a34f6e020dcd1bda060abab0af5afd41eac7a..0000000000000000000000000000000000000000 --- a/src/ui/databrowserModule/preview/previewList.template.html +++ /dev/null @@ -1,24 +0,0 @@ -<mat-nav-list *ngIf="fetchCompleteFlag; else loadingPlaceholder"> - <h3 mat-subheader>Available Preview Files</h3> - <mat-list-item - *ngFor="let file of previewFiles" - (click)="previewFile.emit(file)"> - <mat-icon - [fontSet]="(file | previewFileIconPipe).fontSet" - [fontIcon]="(file | previewFileIconPipe).fontIcon" - matListIcon> - </mat-icon> - <h4 mat-line>{{ file.name }}</h4> - <p mat-line>mimetype: {{ file.mimetype }}</p> - </mat-list-item> - - <small *ngIf="previewFiles.length === 0" - class="text-muted"> - There are no preview files in this parcellation/template space. - </small> - -</mat-nav-list> - -<ng-template #loadingPlaceholder> - <div class="d-inline-block spinnerAnimationCircle"></div> loading previews ... -</ng-template> \ No newline at end of file diff --git a/src/ui/databrowserModule/singleDataset/datasetPreviews/datasetPreviewsList/datasetPreviewList.component.ts b/src/ui/databrowserModule/singleDataset/datasetPreviews/datasetPreviewsList/datasetPreviewList.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..e7844a95db69d0df4e9d2b6307d7cae6704c4ba7 --- /dev/null +++ b/src/ui/databrowserModule/singleDataset/datasetPreviews/datasetPreviewsList/datasetPreviewList.component.ts @@ -0,0 +1,57 @@ +import { Component, Input, ChangeDetectorRef, Output, EventEmitter, Pipe, PipeTransform } from "@angular/core"; +import { ViewerPreviewFile } from "src/services/state/dataStore.store"; +import { Store, select } from "@ngrx/store"; +import { IavRootStoreInterface } from "src/services/stateStore.service"; +import { Observable } from "rxjs"; + +@Component({ + selector: 'dataset-preview-list', + templateUrl: './datasetPreviewList.template.html' +}) + +export class DatasetPreviewList{ + + @Output() public previewingFile: EventEmitter<ViewerPreviewFile> = new EventEmitter() + + public datasetPreviewList: any[] = [] + public loadingDatasetPreviewList: boolean = false + public selectedTemplateSpace$: Observable<any> + + constructor( + private cdr: ChangeDetectorRef, + store$: Store<IavRootStoreInterface> + ){ + this.selectedTemplateSpace$ = store$.pipe( + select('viewerState'), + select('templateSelected') + ) + } + + @Input() + kgId: string + + handleKgDsPrvUpdated(event: CustomEvent){ + const { detail } = event + const { datasetFiles, loadingFlag } = detail + + this.loadingDatasetPreviewList = loadingFlag + this.datasetPreviewList = datasetFiles + + this.cdr.markForCheck() + } + public handlePreviewFile(file: ViewerPreviewFile) { + + this.previewingFile.emit(file) + } +} + +@Pipe({ + name: 'unavailableTooltip' +}) + +export class UnavailableTooltip implements PipeTransform{ + public transform(file: ViewerPreviewFile): string{ + if (file.referenceSpaces.length === 0) return `This preview is not available to be viewed in any reference space.` + else return `This preview is available in the following reference space: ${file.referenceSpaces.map(({ name }) => name).join(', ')}` + } +} \ No newline at end of file diff --git a/src/ui/databrowserModule/singleDataset/datasetPreviews/datasetPreviewsList/datasetPreviewList.template.html b/src/ui/databrowserModule/singleDataset/datasetPreviews/datasetPreviewsList/datasetPreviewList.template.html new file mode 100644 index 0000000000000000000000000000000000000000..a92572cd75eb656dc60f388d0f4bd22c807402c3 --- /dev/null +++ b/src/ui/databrowserModule/singleDataset/datasetPreviews/datasetPreviewsList/datasetPreviewList.template.html @@ -0,0 +1,53 @@ +<kg-dataset-list + (kgDsPrvUpdated)="handleKgDsPrvUpdated($event)" + class="d-none" + [kgId]="kgId"> + +</kg-dataset-list> + +<div *ngIf="loadingDatasetPreviewList; else datasetList" class="spinnerAnimationCircle"></div> + +<ng-template #datasetList> + + <mat-nav-list> + <h3 mat-subheader>Available Preview Files</h3> + + <ng-container *ngFor="let file of datasetPreviewList"> + + <!-- preview available --> + <ng-template [ngIf]="(selectedTemplateSpace$ | async | previewFileVisibleInSelectedReferenceTemplatePipe : file)" [ngIfElse]="notAvailalbePreview" > + <mat-list-item (click)=" handlePreviewFile(file)"> + <mat-icon + [fontSet]="(file | previewFileIconPipe).fontSet" + [fontIcon]="(file | previewFileIconPipe).fontIcon" + matListIcon> + </mat-icon> + <h4 mat-line>{{ file.name }}</h4> + <p mat-line>mimetype: {{ file.mimetype }}</p> + </mat-list-item> + </ng-template> + + <!-- preview not available in this reference space --> + <ng-template #notAvailalbePreview> + <mat-list-item + [matTooltip]="file | unavailableTooltip" + [matTooltipDisabled]="selectedTemplateSpace$ | async | previewFileVisibleInSelectedReferenceTemplatePipe : file" + [ngClass]="{'text-muted': !(selectedTemplateSpace$ | async | previewFileVisibleInSelectedReferenceTemplatePipe : file)}"> + <mat-icon + [fontSet]="(file | previewFileIconPipe).fontSet" + [fontIcon]="(file | previewFileIconPipe).fontIcon" + matListIcon> + </mat-icon> + <h4 mat-line>{{ file.name }}</h4> + <p mat-line>mimetype: {{ file.mimetype }}</p> + </mat-list-item> + </ng-template> + + </ng-container> + <small *ngIf="datasetPreviewList.length === 0" + class="text-muted"> + There are no preview files in this parcellation/template space. + </small> + + </mat-nav-list> +</ng-template> diff --git a/src/ui/databrowserModule/singleDataset/detailedView/singleDataset.component.ts b/src/ui/databrowserModule/singleDataset/detailedView/singleDataset.component.ts index 06228a4fc6cb8320a042e0f3129884655498a694..9dae8425811fec73ef377b61573ddb341cda54e9 100644 --- a/src/ui/databrowserModule/singleDataset/detailedView/singleDataset.component.ts +++ b/src/ui/databrowserModule/singleDataset/detailedView/singleDataset.component.ts @@ -28,4 +28,5 @@ export class SingleDatasetView extends SingleDatasetBase { ) { super(dbService, singleDatasetService, cdr, constantService, data) } + } diff --git a/src/ui/databrowserModule/singleDataset/detailedView/singleDataset.template.html b/src/ui/databrowserModule/singleDataset/detailedView/singleDataset.template.html index 372748eb628a6ce175acb08e79f93d5bae9d3bd0..5871fdd9f58deb3fb43dc4f541c53448a060d2cf 100644 --- a/src/ui/databrowserModule/singleDataset/detailedView/singleDataset.template.html +++ b/src/ui/databrowserModule/singleDataset/detailedView/singleDataset.template.html @@ -140,8 +140,9 @@ <mat-card-footer></mat-card-footer> <ng-template #previewFilesListTemplate> - <preview-component - (previewFile)="handlePreviewFile($event)" - [datasetName]="name"> - </preview-component> -</ng-template> \ No newline at end of file + <dataset-preview-list + [kgId]="kgId" + (previewingFile)="handlePreviewFile($event)"> + + </dataset-preview-list> +</ng-template> diff --git a/src/ui/databrowserModule/singleDataset/listView/singleDatasetListView.template.html b/src/ui/databrowserModule/singleDataset/listView/singleDatasetListView.template.html index 452f019150bdfd99c1367a571cd1f1cf5495aedb..ac442f6602363777ea6a667217a66f2da9f82f46 100644 --- a/src/ui/databrowserModule/singleDataset/listView/singleDatasetListView.template.html +++ b/src/ui/databrowserModule/singleDataset/listView/singleDatasetListView.template.html @@ -102,10 +102,11 @@ </mat-menu> <ng-template #previewFilesListTemplate> - <preview-component - (previewFile)="handlePreviewFile($event)" - [datasetName]="name"> - </preview-component> + <dataset-preview-list + [kgId]="kgId" + (previewingFile)="handlePreviewFile($event)"> + + </dataset-preview-list> </ng-template> <ng-template #fullIcons> diff --git a/src/ui/databrowserModule/singleDataset/singleDataset.base.ts b/src/ui/databrowserModule/singleDataset/singleDataset.base.ts index 831330149fbedbfbec7392177623432b08429369..510232f2495fd069c13e931b2938773603e981e1 100644 --- a/src/ui/databrowserModule/singleDataset/singleDataset.base.ts +++ b/src/ui/databrowserModule/singleDataset/singleDataset.base.ts @@ -1,4 +1,4 @@ -import { ChangeDetectorRef, EventEmitter, Input, OnInit, Output, TemplateRef } from "@angular/core"; +import { ChangeDetectorRef, EventEmitter, Input, OnInit, Output, TemplateRef, ViewChild } from "@angular/core"; import { Observable } from "rxjs"; import { AtlasViewerConstantsServices } from "src/atlasViewer/atlasViewer.constantService.service"; import { IDataEntry, IFile, IPublication, ViewerPreviewFile } from 'src/services/state/dataStore.store' @@ -31,8 +31,6 @@ export class SingleDatasetBase implements OnInit { @Input() public dataset: any = null @Input() public simpleMode: boolean = false - @Output() public previewingFile: EventEmitter<ViewerPreviewFile> = new EventEmitter() - public preview: boolean = false private humanReadableFileSizePipe: HumanReadableFileSizePipe = new HumanReadableFileSizePipe() @@ -42,10 +40,6 @@ export class SingleDatasetBase implements OnInit { public kgReference: string[] = [] public files: IFile[] = [] private methods: string[] = [] - /** - * sic! - */ - private parcellationRegion: Array<{ name: string }> private error: string = null @@ -54,6 +48,8 @@ export class SingleDatasetBase implements OnInit { public dlFromKgHref: string = null + public selectedTemplateSpace$: Observable<any> + public favedDataentries$: Observable<IDataEntry[]> constructor( private dbService: DatabrowserService, @@ -63,6 +59,7 @@ export class SingleDatasetBase implements OnInit { dataset?: any, ) { + this.favedDataentries$ = this.dbService.favedDataentries$ if (dataset) { this.dataset = dataset @@ -155,7 +152,6 @@ export class SingleDatasetBase implements OnInit { } public handlePreviewFile(file: ViewerPreviewFile) { - this.previewingFile.emit(file) this.singleDatasetService.previewFile(file, this.dataset) } } diff --git a/src/ui/databrowserModule/util/previewFileDisabledByReferenceSpace.pipe.ts b/src/ui/databrowserModule/util/previewFileDisabledByReferenceSpace.pipe.ts new file mode 100644 index 0000000000000000000000000000000000000000..12b9065d24393d5ec5d2a59d8acd2b8c07c67039 --- /dev/null +++ b/src/ui/databrowserModule/util/previewFileDisabledByReferenceSpace.pipe.ts @@ -0,0 +1,20 @@ +import { Pipe, PipeTransform } from "@angular/core"; +import { ViewerPreviewFile } from "src/services/state/dataStore.store"; +import { getIdFromFullId } from "common/util" + +@Pipe({ + name: 'previewFileVisibleInSelectedReferenceTemplatePipe' +}) + +export class PreviewFileVisibleInSelectedReferenceTemplatePipe implements PipeTransform{ + public transform(selectedReferenceSpace: any, file: ViewerPreviewFile): boolean{ + const { referenceSpaces = [] } = file + if (referenceSpaces.some(({ name, fullId }) => name === '*' && fullId === '*')) return true + const { fullId } = selectedReferenceSpace + if (!fullId) return false + return referenceSpaces.some(({ fullId: rsFullId }) => { + const compare = getIdFromFullId(rsFullId) === getIdFromFullId(fullId) + return compare + }) + } +} \ No newline at end of file diff --git a/src/ui/searchSideNav/searchSideNav.component.ts b/src/ui/searchSideNav/searchSideNav.component.ts index b8533a630ee23712fe543613f74f7cd551b173d4..8fdddeea8da25117a0a4e48940bbde47976a3fe0 100644 --- a/src/ui/searchSideNav/searchSideNav.component.ts +++ b/src/ui/searchSideNav/searchSideNav.component.ts @@ -13,6 +13,7 @@ import { import { IavRootStoreInterface, SELECT_REGIONS } from "src/services/stateStore.service"; import { LayerBrowser } from "../layerbrowser/layerbrowser.component"; import { trackRegionBy } from '../viewerStateController/regionHierachy/regionHierarchy.component' +import { determinePreviewFileType, PREVIEW_FILE_TYPES } from "../databrowserModule/preview/previewFileIcon.pipe"; @Component({ selector: 'search-side-nav', @@ -31,18 +32,24 @@ export class SearchSideNav implements OnDestroy { @Output() public dismiss: EventEmitter<any> = new EventEmitter() @ViewChild('layerBrowserTmpl', {read: TemplateRef}) public layerBrowserTmpl: TemplateRef<any> + @ViewChild('kgDatasetPreviewer', {read: TemplateRef}) private kgDatasetPreview: TemplateRef<any> public autoOpenSideNavDataset$: Observable<any> public sidePanelExploreCurrentViewIsOpen$: Observable<any> public sidePanelCurrentViewContent: Observable<any> + public darktheme$: Observable<boolean> + constructor( public dialog: MatDialog, private store$: Store<IavRootStoreInterface>, private snackBar: MatSnackBar, private constantService: AtlasViewerConstantsServices, ) { + + this.darktheme$ = this.constantService.darktheme$ + this.autoOpenSideNavDataset$ = this.store$.pipe( select('viewerState'), select('regionsSelected'), @@ -62,8 +69,33 @@ export class SearchSideNav implements OnDestroy { select('uiState'), select("sidePanelCurrentViewContent"), ) + + this.subscriptions.push( + this.store$.pipe( + select('dataStore'), + select('datasetPreviews'), + filter(datasetPreviews => datasetPreviews.length > 0), + map((datasetPreviews) => datasetPreviews[datasetPreviews.length - 1]), + filter(({ file }) => determinePreviewFileType(file) !== PREVIEW_FILE_TYPES.NIFTI) + ).subscribe(({ dataset, file }) => { + + const { fullId } = dataset + const { filename } = file + + // TODO replace with common/util/getIdFromFullId + this.previewKgId = /\/([a-f0-9-]{1,})$/.exec(fullId)[1] + this.previewFilename = filename + this.dialog.open(this.kgDatasetPreview, { + minWidth: '50vw', + minHeight: '50vh' + }) + }) + ) } + public previewKgId: string + public previewFilename: string + public collapseSidePanelCurrentView() { this.store$.dispatch({ type: COLLAPSE_SIDE_PANEL_CURRENT_VIEW, diff --git a/src/ui/searchSideNav/searchSideNav.style.css b/src/ui/searchSideNav/searchSideNav.style.css index 5bb036cfb94a7098f23e386101b641f6fbae208a..f8d38603b6c77b036449a644636964d8ce64d176 100644 --- a/src/ui/searchSideNav/searchSideNav.style.css +++ b/src/ui/searchSideNav/searchSideNav.style.css @@ -19,5 +19,15 @@ } connectivity-browser { - max-height: calc(100% - 200px); -} \ No newline at end of file + max-height: calc(100% - 220px); +} + +.min-w-50vw +{ + min-width: 50vw!important; +} + +.min-h-50vh +{ + min-height: 50vh!important; +} diff --git a/src/ui/searchSideNav/searchSideNav.template.html b/src/ui/searchSideNav/searchSideNav.template.html index 8331fba31b1645fcc3f8bb2a3935509165db40a2..5628e3aaf482e4d4017bec3d0e8924c969316e29 100644 --- a/src/ui/searchSideNav/searchSideNav.template.html +++ b/src/ui/searchSideNav/searchSideNav.template.html @@ -168,3 +168,14 @@ </ng-template> </ng-container> </ng-template> + + +<ng-template #kgDatasetPreviewer> + <kg-dataset-previewer + [darkmode]="darktheme$ | async" + [filename]="previewFilename" + [kgId]="previewKgId" + kg-ds-prv-backend-url="https://hbp-kg-dataset-previewer.apps.hbp.eu/datasetPreview"> + + </kg-dataset-previewer> +</ng-template> \ No newline at end of file