diff --git a/src/atlasViewer/atlasViewer.template.html b/src/atlasViewer/atlasViewer.template.html index ad269e072883fedfb5df745e09cc2bc5e10bace4..a47e50f72a7c9fece33dd6c05e01a236c0054b8a 100644 --- a/src/atlasViewer/atlasViewer.template.html +++ b/src/atlasViewer/atlasViewer.template.html @@ -66,7 +66,8 @@ </signin-banner> <!-- atlas selector --> - <div class="iv-custom-comp bg card m-2 mat-elevation-z2"> + <div *ngIf="uiNehubaContainer.viewerLoaded" + class="iv-custom-comp bg card m-2 mat-elevation-z2"> <atlas-dropdown-selector class="pe-all mt-2"> </atlas-dropdown-selector> </div> @@ -75,123 +76,151 @@ <!-- bottom left content transclusion --> <div ui-nehuba-container-overlay-bottom-left class="d-inline-flex pe-none w-100 align-items-end m-2 mb-4"> - <!-- Viewer Selector Container--> - <atlas-layer-selector - #alSelector="atlasLayerSelector" - class="pe-all" - (iav-outsideClick)="alSelector.selectorExpanded = false"> - </atlas-layer-selector> - <mat-chip-list class="mb-2"> - <!-- additional layer --> - <ng-container *ngIf="!alSelector.selectorExpanded"> - <ng-container *ngTemplateOutlet="currParcellationTmpl; context: { addParc: (selectedAdditionalLayers$ | async), parc: selectedParcellation }"> + + <!-- only load atlas layer selector and chips if viewer is loaded --> + <ng-template [ngIf]="uiNehubaContainer.viewerLoaded"> + + <!-- Viewer Selector Container--> + <atlas-layer-selector + #alSelector="atlasLayerSelector" + class="pe-all" + (iav-outsideClick)="alSelector.selectorExpanded = false"> + </atlas-layer-selector> + <mat-chip-list class="mb-2"> + <!-- additional layer --> + <ng-container *ngIf="!alSelector.selectorExpanded"> + <ng-container *ngTemplateOutlet="currParcellationTmpl; context: { addParc: (selectedAdditionalLayers$ | async), parc: selectedParcellation }"> + </ng-container> </ng-container> - </ng-container> - <!-- any selected region(s) --> - <ng-container *ngIf="!uiNehubaContainer.navSideDrawerMainSwitch.switchState || !uiNehubaContainer.navSideDrawerMinorSwitch.switchState"> - <ng-container *ngTemplateOutlet="selectedRegionTmpl"> + <!-- any selected region(s) --> + <ng-container *ngIf="!uiNehubaContainer.navSideDrawerMainSwitch.switchState || !uiNehubaContainer.navSideDrawerMinorSwitch.switchState"> + <ng-container *ngTemplateOutlet="selectedRegionTmpl"> + </ng-container> </ng-container> - </ng-container> - </mat-chip-list> - - <!-- current layer tmpl --> - <ng-template #currParcellationTmpl let-parc="parc" let-addParc="addParc"> - <ng-template [ngIf]="addParc.length > 0" [ngIfElse]="defaultParcTmpl"> - <ng-container *ngFor="let p of addParc"> + </mat-chip-list> + + <!-- current layer tmpl --> + <ng-template #currParcellationTmpl let-parc="parc" let-addParc="addParc"> + <ng-template [ngIf]="addParc.length > 0" [ngIfElse]="defaultParcTmpl"> + <ng-container *ngFor="let p of addParc"> + <ng-container *ngTemplateOutlet="chipTmpl; context: { + parcel: p, + selected: true, + dismissable: true + }"> + </ng-container> + </ng-container> + </ng-template> + <ng-template #defaultParcTmpl> <ng-container *ngTemplateOutlet="chipTmpl; context: { - parcel: p, - selected: true, - dismissable: true + parcel: parc, + selected: false, + dismissable: false }"> </ng-container> - </ng-container> - </ng-template> - <ng-template #defaultParcTmpl> - <ng-container *ngTemplateOutlet="chipTmpl; context: { - parcel: parc, - selected: false, - dismissable: false - }"> - </ng-container> - </ng-template> - - <!-- render parc templ --> - <ng-template #chipTmpl - let-parcel="parcel" - let-selected="selected" - let-dismissable="dismissable"> - <mat-chip class="pe-all" - (click)="alSelector.selectorExpanded = true" - [selected]="selected"> - - {{ parcel?.groupName ? (parcel?.groupName + ' - ') : '' }}{{ parcel?.name }} - - <!-- info icon --> - <ng-template [ngIf]="parcel?.originDatasets?.length > 0" - [ngIfElse]="infoIconBasic"> - + </ng-template> + + <!-- render parc templ --> + <ng-template #chipTmpl + let-parcel="parcel" + let-selected="selected" + let-dismissable="dismissable"> + <mat-chip class="pe-all" + (click)="alSelector.selectorExpanded = true" + [selected]="selected"> + + {{ parcel?.groupName ? (parcel?.groupName + ' - ') : '' }}{{ parcel?.name }} + + <!-- info icon --> + <ng-template [ngIf]="parcel?.originDatasets?.length > 0" + [ngIfElse]="infoIconBasic"> + + <mat-icon + *ngFor="let ds of parcel.originDatasets" + fontSet="fas" + fontIcon="fa-info-circle" + iav-stop="click" + iav-dataset-show-dataset-dialog + [iav-dataset-show-dataset-dialog-kgid]="ds['kgId']" + [iav-dataset-show-dataset-dialog-kgschema]="ds['kgSchema']" + [iav-dataset-show-dataset-dialog-name]="parcel?.properties?.name" + [iav-dataset-show-dataset-dialog-description]="parcel?.properties?.description"> + </mat-icon> + + </ng-template> + + <ng-template #infoIconBasic> + <mat-icon *ngIf="parcel?.properties?.name && parcel?.properties?.description" + fontSet="fas" + fontIcon="fa-info-circle" + iav-stop="click" + iav-dataset-show-dataset-dialog + [iav-dataset-show-dataset-dialog-name]="parcel.properties.name" + [iav-dataset-show-dataset-dialog-description]="parcel.properties.description"> + + </mat-icon> + </ng-template> + + <!-- dismiss icon --> <mat-icon - *ngFor="let ds of parcel.originDatasets" + *ngIf="dismissable" + (click)="clearAdditionalLayer(parcel)" fontSet="fas" - fontIcon="fa-info-circle" - iav-stop="click" - iav-dataset-show-dataset-dialog - [iav-dataset-show-dataset-dialog-kgid]="ds['kgId']" - [iav-dataset-show-dataset-dialog-kgschema]="ds['kgSchema']" - [iav-dataset-show-dataset-dialog-name]="parcel?.properties?.name" - [iav-dataset-show-dataset-dialog-description]="parcel?.properties?.description"> + fontIcon="fa-times"> </mat-icon> + </mat-chip> + </ng-template> + </ng-template> - </ng-template> - - <ng-template #infoIconBasic> - <mat-icon *ngIf="parcel?.properties?.name && parcel?.properties?.description" + <ng-template #selectedRegionTmpl> + <ng-container *ngFor="let r of (selectedRegions$ | async)"> + + <!-- region chip for discrete map --> + <mat-chip + iav-region + (click)="uiNehubaContainer.matDrawerMinor.open() && uiNehubaContainer.navSideDrawerMainSwitch.open()" + [region]="r" + class="pe-all" + [ngClass]="{ + 'darktheme':regionDirective.rgbDarkmode === true, + 'lighttheme': regionDirective.rgbDarkmode === false + }" + [style.backgroundColor]="regionDirective.rgbString" + #regionDirective="iavRegion"> + <span class="iv-custom-comp text text-truncate d-inline"> + {{ r.name }} + </span> + <mat-icon + class="iv-custom-comp text" + (click)="clearSelectedRegions()" fontSet="fas" - fontIcon="fa-info-circle" - iav-stop="click" - iav-dataset-show-dataset-dialog - [iav-dataset-show-dataset-dialog-name]="parcel.properties.name" - [iav-dataset-show-dataset-dialog-description]="parcel.properties.description"> - + fontIcon="fa-times"> </mat-icon> - </ng-template> - - <!-- dismiss icon --> - <mat-icon - *ngIf="dismissable" - (click)="clearAdditionalLayer(parcel)" - fontSet="fas" - fontIcon="fa-times"> - </mat-icon> - </mat-chip> + </mat-chip> + + <!-- chips for previewing origin datasets --> + <ng-container *ngFor="let originDataset of (r.originDatasets || []); let index = index"> + <div class="hidden" + iav-dataset-preview-dataset-file + [iav-dataset-preview-dataset-file-kgid]="originDataset.kgId" + [iav-dataset-preview-dataset-file-filename]="originDataset.filename" + #previewDirective="iavDatasetPreviewDatasetFile"> + </div> + <mat-chip *ngIf="previewDirective.active" + (click)="uiNehubaContainer.matDrawerMinor.open() && uiNehubaContainer.navSideDrawerMainSwitch.open()" + class="pe-all"> + {{ regionDirective.regionOriginDatasetLabels$ | async | renderViewOriginDatasetlabel : index }} + <mat-icon (click)="previewDirective.onClick()" + fontSet="fas" + fontIcon="fa-times"> + </mat-icon> + </mat-chip> + </ng-container> + </ng-container> </ng-template> - </ng-template> - <ng-template #selectedRegionTmpl> - <mat-chip *ngFor="let r of (selectedRegions$ | async)" - iav-region - (click)="uiNehubaContainer.matDrawerMinor.open() && uiNehubaContainer.navSideDrawerMainSwitch.open()" - [region]="r" - class="pe-all" - [ngClass]="{ - 'darktheme':regionDirective.rgbDarkmode === true, - 'lighttheme': regionDirective.rgbDarkmode === false - }" - [style.backgroundColor]="regionDirective.rgbString" - #regionDirective="iavRegion"> - <span class="iv-custom-comp text text-truncate d-inline"> - {{ r.name }} - </span> - <mat-icon - class="iv-custom-comp text" - (click)="clearSelectedRegions()" - fontSet="fas" - fontIcon="fa-times"> - </mat-icon> - </mat-chip> </ng-template> - </div> <!-- top left content transclusion --> diff --git a/src/glue.spec.ts b/src/glue.spec.ts index e3b6d940d851d5dad9e0e5b6da28c044c05e9abc..8db3fe5728f78304f5fec5400dd714f5b6b27e96 100644 --- a/src/glue.spec.ts +++ b/src/glue.spec.ts @@ -1,5 +1,5 @@ import { TestBed, tick, fakeAsync } from "@angular/core/testing" -import { DatasetPreviewGlue, glueSelectorGetUiStatePreviewingFiles, glueActionRemoveDatasetPreview, datasetPreviewMetaReducer, glueActionAddDatasetPreview } from "./glue" +import { DatasetPreviewGlue, glueSelectorGetUiStatePreviewingFiles, glueActionRemoveDatasetPreview, datasetPreviewMetaReducer, glueActionAddDatasetPreview, GlueEffects } from "./glue" import { ACTION_TO_WIDGET_TOKEN, EnumActionToWidget } from "./widget" import { provideMockStore, MockStore } from "@ngrx/store/testing" import { getRandomHex } from 'common/util' @@ -532,4 +532,85 @@ describe('> glue.ts', () => { }) }) }) -}) \ No newline at end of file + + describe('> GlueEffects', () => { + + const defaultState = { + viewerState: { + templateSelected: null, + parcellationSelected: null, + regionsSelected: [] + }, + uiState: { + previewingDatasetFiles: [] + } + } + beforeEach(() => { + + TestBed.configureTestingModule({ + providers: [ + GlueEffects, + provideMockStore({ + initialState: defaultState + }) + ] + }) + }) + + describe('> regionTemplateParcChange$', () => { + + const copiedState0 = JSON.parse(JSON.stringify(defaultState)) + copiedState0.viewerState.regionsSelected = [{ name: 'coffee' }] + copiedState0.viewerState.parcellationSelected = { name: 'chicken' } + copiedState0.viewerState.templateSelected = { name: 'spinach' } + + const generateTest = (m1, m2) => { + + const mockStore = TestBed.inject(MockStore) + mockStore.setState(copiedState0) + const glueEffects = TestBed.inject(GlueEffects) + /** + * couldn't get jasmine-marble to coopoerate + * TODO test proper with jasmine marble (?) + */ + let numOfEmit = 0 + const sub = glueEffects.regionTemplateParcChange$.subscribe(() => { + numOfEmit += 1 + }) + + const copiedState1 = JSON.parse(JSON.stringify(copiedState0)) + m1(copiedState1) + mockStore.setState(copiedState1) + expect(numOfEmit).toEqual(1) + + const copiedState2 = JSON.parse(JSON.stringify(copiedState0)) + m2(copiedState2) + mockStore.setState(copiedState2) + expect(numOfEmit).toEqual(2) + + sub.unsubscribe() + } + + it('> on change of region, should emit', () => { + generateTest( + copiedState1 => copiedState1.viewerState.regionsSelected = [{ name: 'cake' }], + copiedState2 => copiedState2.viewerState.regionsSelected = [{ name: 't bone' }] + ) + }) + + it('> on change of parcellation, should emit', () => { + generateTest( + copiedState1 => copiedState1.viewerState.parcellationSelected = { name: 'pizza' }, + copiedState2 => copiedState2.viewerState.parcellationSelected = { name: 'pizza on pineapple' } + ) + }) + + it('> on change of template, should emit', () => { + generateTest( + copiedState1 => copiedState1.viewerState.templateSelected = { name: 'calzone' }, + copiedState2 => copiedState2.viewerState.templateSelected = { name: 'calzone on pineapple' } + ) + }) + }) + }) +}) diff --git a/src/glue.ts b/src/glue.ts index 696129f96f5558c4e0c36cd3ce5523c7059bc53b..3dc0ac87c492ed9b8953b3400575e1cd6c5e76c1 100644 --- a/src/glue.ts +++ b/src/glue.ts @@ -1,9 +1,9 @@ -import { uiActionSetPreviewingDatasetFiles, TypeOpenedWidget, EnumWidgetTypes, IDatasetPreviewData, uiStateShowBottomSheet } from "./services/state/uiState.store.helper" +import { uiActionSetPreviewingDatasetFiles, IDatasetPreviewData, uiStateShowBottomSheet, uiStatePreviewingDatasetFilesSelector } from "./services/state/uiState.store.helper" import { OnDestroy, Injectable, Optional, Inject, InjectionToken } from "@angular/core" -import { PreviewComponentWrapper, DatasetPreview, determinePreviewFileType, EnumPreviewFileTypes, IKgDataEntry, getKgSchemaIdFromFullId } from "./ui/databrowserModule" -import { Subscription, Observable, forkJoin, of } from "rxjs" +import { PreviewComponentWrapper, DatasetPreview, determinePreviewFileType, EnumPreviewFileTypes, IKgDataEntry, getKgSchemaIdFromFullId } from "./ui/databrowserModule/pure" +import { Subscription, Observable, forkJoin, of, merge } from "rxjs" import { select, Store, ActionReducer, createAction, props, createSelector, Action } from "@ngrx/store" -import { startWith, map, shareReplay, pairwise, debounceTime, distinctUntilChanged, tap, switchMap, withLatestFrom } from "rxjs/operators" +import { startWith, map, shareReplay, pairwise, debounceTime, distinctUntilChanged, tap, switchMap, withLatestFrom, mapTo, switchMapTo, filter, skip } from "rxjs/operators" import { TypeActionToWidget, EnumActionToWidget, ACTION_TO_WIDGET_TOKEN } from "./widget" import { getIdObj } from 'common/util' import { MatDialogRef } from "@angular/material/dialog" @@ -13,6 +13,8 @@ import { ngViewerActionAddNgLayer, ngViewerActionRemoveNgLayer, INgLayerInterfac import { ARIA_LABELS } from 'common/constants' import { NgLayersService } from "src/ui/layerbrowser/ngLayerService.service" import { EnumColorMapName } from "./util/colorMaps" +import { Effect } from "@ngrx/effects" +import { viewerStateSelectedRegionsSelector, viewerStateSelectedTemplateSelector, viewerStateSelectedParcellationSelector } from "./services/state/viewerState/selectors" const PREVIEW_FILE_TYPES_NO_UI = [ EnumPreviewFileTypes.NIFTI, @@ -21,11 +23,6 @@ const PREVIEW_FILE_TYPES_NO_UI = [ const DATASET_PREVIEW_ANNOTATION = `DATASET_PREVIEW_ANNOTATION` -export const glueActionPreviewDataset = createAction( - '[glue] previewDataset', - props<IDatasetPreviewData>() -) - export const glueActionToggleDatasetPreview = createAction( '[glue] toggleDatasetPreview', props<{ datasetPreviewFile: IDatasetPreviewData }>() @@ -55,6 +52,58 @@ export interface IDatasetPreviewGlue{ providedIn: 'root' }) +export class GlueEffects { + + public regionTemplateParcChange$ = merge( + this.store$.pipe( + select(viewerStateSelectedRegionsSelector), + map(rs => (rs || []).map(r => r['name']).sort().join(',')), + distinctUntilChanged(), + skip(1), + ), + this.store$.pipe( + select(viewerStateSelectedTemplateSelector), + map(tmpl => tmpl + ? tmpl['@id'] || tmpl['name'] + : null), + distinctUntilChanged(), + skip(1) + ), + this.store$.pipe( + select(viewerStateSelectedParcellationSelector), + map(parc => parc + ? parc['@id'] || parc['name'] + : null), + distinctUntilChanged(), + skip(1) + ) + ).pipe( + mapTo(true) + ) + + @Effect() + resetDatasetPreview$: Observable<any> = this.store$.pipe( + select(uiStatePreviewingDatasetFilesSelector), + distinctUntilChanged(), + filter(previews => previews?.length > 0), + switchMapTo(this.regionTemplateParcChange$) + ).pipe( + mapTo(uiActionSetPreviewingDatasetFiles({ + previewingDatasetFiles: [] + })) + ) + + constructor( + private store$: Store<any> + ){ + + } +} + +@Injectable({ + providedIn: 'root' +}) + export class DatasetPreviewGlue implements IDatasetPreviewGlue, OnDestroy{ static readonly DEFAULT_DIALOG_OPTION = { diff --git a/src/main.module.ts b/src/main.module.ts index 0e86995d887e66cba6ce00957ce908cec51ef3cb..769e816943afb2a5e806e4ea90183dfa1dd93b03 100644 --- a/src/main.module.ts +++ b/src/main.module.ts @@ -54,7 +54,7 @@ import 'hammerjs' import 'src/res/css/extra_styles.css' import 'src/res/css/version.css' import 'src/theme.scss' -import { DatasetPreviewGlue, datasetPreviewMetaReducer, IDatasetPreviewGlue } from './glue'; +import { DatasetPreviewGlue, datasetPreviewMetaReducer, IDatasetPreviewGlue, GlueEffects } from './glue'; import { viewerStateHelperReducer, viewerStateFleshOutDetail, viewerStateMetaReducers, ViewerStateHelperEffect } from './services/state/viewerState.store.helper'; export function debug(reducer: ActionReducer<any>): ActionReducer<any> { @@ -96,7 +96,8 @@ export function debug(reducer: ActionReducer<any>): ActionReducer<any> { AtlasViewerHistoryUseEffect, UiStateUseEffect, NewTemplateUseEffect, - ViewerStateHelperEffect + ViewerStateHelperEffect, + GlueEffects ]), StoreModule.forRoot({ pluginState, diff --git a/src/res/css/extra_styles.css b/src/res/css/extra_styles.css index 3c91a3b4665e7f98249b20a0f59f36da62cf3882..2274445cc2a7888affbc3f84f472b1c70eca8790 100644 --- a/src/res/css/extra_styles.css +++ b/src/res/css/extra_styles.css @@ -711,6 +711,14 @@ kg-dataset-previewer > img margin-right: -1rem!important; } +.ml-5-n +{ + margin-left: -1.25rem!important; +} +.mr-5-n +{ + margin-right: -1.25rem!important; +} .mt-4-n { margin-top: -1rem!important; diff --git a/src/services/state/uiState.store.helper.ts b/src/services/state/uiState.store.helper.ts index 8f3ead0a17d7911b8bafedec1612cfc3a2cfa451..376c4d788b2dd1caee61928d39d0175a77eb2eef 100644 --- a/src/services/state/uiState.store.helper.ts +++ b/src/services/state/uiState.store.helper.ts @@ -1,38 +1,32 @@ // TODO merge with uiState.store.ts after refactor completes -import { createAction, props } from '@ngrx/store' -import { TemplateRef } from '@angular/core' -import { MatBottomSheetConfig } from '@angular/material/bottom-sheet' - -export const uiStateCloseSidePanel = createAction( - '[uiState] closeSidePanel' -) - -export const uiStateOpenSidePanel = createAction( - '[uiState] openSidePanel' -) - -export const uiStateCollapseSidePanel = createAction( - '[uiState] collapseSidePanelCurrentView' -) - -export const uiStateExpandSidePanel = createAction( - '[uiState] expandSidePanelCurrentView' -) - -export const uiStateShowBottomSheet = createAction( - '[uiState] showBottomSheet', - props<{ bottomSheetTemplate: TemplateRef<unknown>, config?: MatBottomSheetConfig }>() -) +import { + uiActionSetPreviewingDatasetFiles, + uiActionShowSidePanelConnectivity, + uiStateCloseSidePanel, + uiStateCollapseSidePanel, + uiStateExpandSidePanel, + uiStateOpenSidePanel, + uiStateShowBottomSheet +} from './uiState/actions' + +export { + uiActionSetPreviewingDatasetFiles, + uiActionShowSidePanelConnectivity, + uiStateCloseSidePanel, + uiStateCollapseSidePanel, + uiStateExpandSidePanel, + uiStateOpenSidePanel, + uiStateShowBottomSheet +} -export const uiActionSetPreviewingDatasetFiles = createAction( - `[uiState] setDatasetPreviews`, - props<{previewingDatasetFiles: {datasetId: string, filename: string}[]}>() -) +import { + uiStatePreviewingDatasetFilesSelector +} from './uiState/selectors' -export const uiActionShowSidePanelConnectivity = createAction( - `[uiState] showSidePanelConnectivity` -) +export { + uiStatePreviewingDatasetFilesSelector +} export enum EnumWidgetTypes{ DATASET_PREVIEW, diff --git a/src/services/state/uiState/actions.ts b/src/services/state/uiState/actions.ts new file mode 100644 index 0000000000000000000000000000000000000000..9db6183506e44a67930ee0801f0c4d3c9d8339f4 --- /dev/null +++ b/src/services/state/uiState/actions.ts @@ -0,0 +1,34 @@ + +import { createAction, props } from '@ngrx/store' +import { TemplateRef } from '@angular/core' +import { MatBottomSheetConfig } from '@angular/material/bottom-sheet' + +export const uiStateCloseSidePanel = createAction( + '[uiState] closeSidePanel' +) + +export const uiStateOpenSidePanel = createAction( + '[uiState] openSidePanel' +) + +export const uiStateCollapseSidePanel = createAction( + '[uiState] collapseSidePanelCurrentView' +) + +export const uiStateExpandSidePanel = createAction( + '[uiState] expandSidePanelCurrentView' +) + +export const uiStateShowBottomSheet = createAction( + '[uiState] showBottomSheet', + props<{ bottomSheetTemplate: TemplateRef<unknown>, config?: MatBottomSheetConfig }>() +) + +export const uiActionSetPreviewingDatasetFiles = createAction( + `[uiState] setDatasetPreviews`, + props<{previewingDatasetFiles: {datasetId: string, filename: string}[]}>() +) + +export const uiActionShowSidePanelConnectivity = createAction( + `[uiState] showSidePanelConnectivity` +) diff --git a/src/services/state/uiState/selectors.ts b/src/services/state/uiState/selectors.ts new file mode 100644 index 0000000000000000000000000000000000000000..a4db9adfc902235a510a8aa378dd9e7201ff98e8 --- /dev/null +++ b/src/services/state/uiState/selectors.ts @@ -0,0 +1,6 @@ +import { createSelector } from "@ngrx/store"; + +export const uiStatePreviewingDatasetFilesSelector = createSelector( + state => state['uiState'], + uiState => uiState['previewingDatasetFiles'] +) diff --git a/src/services/state/viewerState.store.helper.ts b/src/services/state/viewerState.store.helper.ts index a4a1c2faf92d6f75a47192be1f73b65ba8373095..b9172be04ea57d70d12104f91e9870fe50534d38 100644 --- a/src/services/state/viewerState.store.helper.ts +++ b/src/services/state/viewerState.store.helper.ts @@ -44,11 +44,19 @@ export { viewreStateRemoveUserLandmarks } -export { +import { viewerStateAllParcellationsSelector, - viewerStateSelectedRegionsSelector + viewerStateSelectedRegionsSelector, + viewerStateSelectedTemplateSelector, + viewerStateSelectedParcellationSelector } from './viewerState/selectors' +export { + viewerStateAllParcellationsSelector, + viewerStateSelectedRegionsSelector, + viewerStateSelectedTemplateSelector, + viewerStateSelectedParcellationSelector +} interface IViewerStateHelperStore{ fetchedAtlases: any[] @@ -88,22 +96,22 @@ export const viewerStateMetaReducers = [ export class ViewerStateHelperEffect{ @Effect() - selectParcellationWithId$: Observable<any> + selectParcellationWithId$: Observable<any> = this.actions$.pipe( + ofType(viewerStateRemoveAdditionalLayer.type), + withLatestFrom(this.store$.pipe( + select(viewerStateGetSelectedAtlas) + )), + map(([ { payload }, selectedAtlas ]) => { + const baseLayer = selectedAtlas['parcellations'].find(p => p['baseLayer']) + return viewerStateHelperSelectParcellationWithId({ payload: baseLayer }) + }) + ) constructor( private store$: Store<any>, private actions$: Actions ){ - this.selectParcellationWithId$ = this.actions$.pipe( - ofType(viewerStateRemoveAdditionalLayer.type), - withLatestFrom(this.store$.pipe( - select(viewerStateGetSelectedAtlas) - )), - map(([ { payload }, selectedAtlas ]) => { - const baseLayer = selectedAtlas['parcellations'].find(p => p['baseLayer']) - return viewerStateHelperSelectParcellationWithId({ payload: baseLayer }) - }) - ) + } } diff --git a/src/services/state/viewerState/selectors.ts b/src/services/state/viewerState/selectors.ts index 6d32c879ee234c99fc49aadc62c71770776ac675..8b541af1f2ecb9a8f2ca043499ca9bb4af1cb2f7 100644 --- a/src/services/state/viewerState/selectors.ts +++ b/src/services/state/viewerState/selectors.ts @@ -20,4 +20,14 @@ export const viewerStateAllParcellationsSelector = createSelector( return acc.concat( parcelations ) }, []) } -) \ No newline at end of file +) + +export const viewerStateSelectedTemplateSelector = createSelector( + state => state['viewerState'], + viewerState => viewerState['templateSelected'] +) + +export const viewerStateSelectedParcellationSelector = createSelector( + state => state['viewerState'], + viewerState => viewerState['parcellationSelected'] +) diff --git a/src/ui/databrowserModule/constants.ts b/src/ui/databrowserModule/constants.ts index 43b28ab1066f14e94243bfbc7ebe90ff5e495a9e..36163582e6666bd05746f97a7d495a62a35183e9 100644 --- a/src/ui/databrowserModule/constants.ts +++ b/src/ui/databrowserModule/constants.ts @@ -1,6 +1,5 @@ import { InjectionToken } from "@angular/core"; import { LOCAL_STORAGE_CONST } from "src/util/constants"; -export { DatasetPreview } from "./databrowser.module"; export const OVERRIDE_IAV_DATASET_PREVIEW_DATASET_FN = new InjectionToken<(file: any, dataset: any) => void>('OVERRIDE_IAV_DATASET_PREVIEW_DATASET_FN') export const DATASTORE_DEFAULT_STATE = { @@ -89,3 +88,8 @@ export interface IKgDataEntry { id: string fullId: string } + +export interface DatasetPreview { + datasetId: string + filename: string +} diff --git a/src/ui/databrowserModule/databrowser/databrowser.component.ts b/src/ui/databrowserModule/databrowser/databrowser.component.ts index 252308bbe725f2eb905c3c4c48246b06860158ed..0227d909293c45f9a1c88272ae65a111eed3c4b5 100644 --- a/src/ui/databrowserModule/databrowser/databrowser.component.ts +++ b/src/ui/databrowserModule/databrowser/databrowser.component.ts @@ -1,7 +1,7 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output, ViewChild } from "@angular/core"; import { merge, Observable, Subscription } from "rxjs"; import { LoggingService } from "src/logging"; -import { IDataEntry } from "src/services/stateStore.service"; +import { IDataEntry } from "src/services/state/dataStore.store"; import { CountedDataModality, DatabrowserService } from "../databrowser.service"; import { ModalityPicker } from "../modalityPicker/modalityPicker.component"; import { ARIA_LABELS } from 'common/constants.js' diff --git a/src/ui/databrowserModule/index.ts b/src/ui/databrowserModule/index.ts index 1c914548b729760c8e2b5cac7fc9a61dce2da8c3..3343f5c8a81f51da9f244c086efb4fb05e0120e1 100644 --- a/src/ui/databrowserModule/index.ts +++ b/src/ui/databrowserModule/index.ts @@ -1,11 +1,11 @@ export { DatabrowserModule, - DatasetPreview, IAV_DATASET_PREVIEW_ACTIVE, TypePreviewDispalyed, - getKgSchemaIdFromFullId, } from './databrowser.module' +export { DataBrowserFeatureStore, DATESTORE_FEATURE_KEY } from './store.module' + export { DATASTORE_DEFAULT_STATE, OVERRIDE_IAV_DATASET_PREVIEW_DATASET_FN, @@ -15,8 +15,8 @@ export { IKgDataEntry, IKgParcellationRegion, IKgPublication, - IKgReferenceSpace -} from './constants' - -export { PreviewComponentWrapper } from './preview/previewComponentWrapper/previewCW.component' -export { DataBrowserFeatureStore, DATESTORE_FEATURE_KEY } from './store.module' \ No newline at end of file + IKgReferenceSpace, + DatasetPreview, + PreviewComponentWrapper, + getKgSchemaIdFromFullId +} from './pure' diff --git a/src/ui/databrowserModule/preview/previewComponentWrapper/previewCW.component.ts b/src/ui/databrowserModule/preview/previewComponentWrapper/previewCW.component.ts index 984f89702fc1f4df4e255c4bff4233a607cf300d..379007832268c0b019bac4f35d102873df7524da 100644 --- a/src/ui/databrowserModule/preview/previewComponentWrapper/previewCW.component.ts +++ b/src/ui/databrowserModule/preview/previewComponentWrapper/previewCW.component.ts @@ -1,11 +1,11 @@ import { Component, Input, Inject, ViewChild, ElementRef } from "@angular/core"; import { MAT_DIALOG_DATA } from "@angular/material/dialog"; -import { AtlasViewerConstantsServices } from "src/atlasViewer/atlasViewer.constantService.service"; import { Observable, fromEvent, Subscription, of, throwError } from "rxjs"; import { switchMapTo, catchError, take, concatMap, map, retryWhen, delay } from "rxjs/operators"; import { DomSanitizer, SafeResourceUrl } from "@angular/platform-browser"; import { ARIA_LABELS } from 'common/constants' import { DS_PREVIEW_URL } from 'src/util/constants' +import { PureContantService } from "src/util"; const { DOWNLOAD_PREVIEW, @@ -68,11 +68,11 @@ export class PreviewComponentWrapper{ constructor( @Inject(MAT_DIALOG_DATA) data: any, - private constantService: AtlasViewerConstantsServices, + private pureConstantService: PureContantService, private sanitizer: DomSanitizer ){ - this.darktheme$ = this.constantService.darktheme$ + this.darktheme$ = this.pureConstantService.darktheme$ if (data) { const { filename, kgId, backendUrl, datasetName } = data this.filename = filename diff --git a/src/ui/databrowserModule/pure.spec.ts b/src/ui/databrowserModule/pure.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..30d3bbf8e308180db32d061fbce6fdc81796a0f8 --- /dev/null +++ b/src/ui/databrowserModule/pure.spec.ts @@ -0,0 +1,7 @@ +import * as _ from './pure' + +describe('> pure.ts', () => { + it('> should be importable without hiccups', () => { + console.log(Object.keys(_)) + }) +}) diff --git a/src/ui/databrowserModule/pure.ts b/src/ui/databrowserModule/pure.ts new file mode 100644 index 0000000000000000000000000000000000000000..a7833a7efa4756cab19e7d37eb23f5f511997793 --- /dev/null +++ b/src/ui/databrowserModule/pure.ts @@ -0,0 +1,15 @@ +export { getKgSchemaIdFromFullId } from './util/getKgSchemaIdFromFullId.pipe' +export { + DATASTORE_DEFAULT_STATE, + OVERRIDE_IAV_DATASET_PREVIEW_DATASET_FN, + EnumPreviewFileTypes, + determinePreviewFileType, + IKgActivity, + IKgDataEntry, + IKgParcellationRegion, + IKgPublication, + IKgReferenceSpace, + DatasetPreview +} from './constants' + +export { PreviewComponentWrapper } from './preview/previewComponentWrapper/previewCW.component' diff --git a/src/ui/nehubaContainer/statusCard/statusCard.template.html b/src/ui/nehubaContainer/statusCard/statusCard.template.html index 9265d0084ebebd458777315eebefa94e50be0fee..5bcfe73e32674811245c8d4a21e8b333ac32a0c5 100644 --- a/src/ui/nehubaContainer/statusCard/statusCard.template.html +++ b/src/ui/nehubaContainer/statusCard/statusCard.template.html @@ -100,7 +100,7 @@ <!-- minimised status bar --> <ng-template #showMin> - <div class="iv-custom-comp text overflow-visible text-nowrap d-inline-flex align-items-center pb-2" + <div class="iv-custom-comp text overflow-visible text-nowrap d-inline-flex align-items-center m-1 mt-3" iav-media-query #media="iavMediaQuery"> diff --git a/src/ui/parcellationRegion/region.base.ts b/src/ui/parcellationRegion/region.base.ts index 31f3ef3734ac48c0adabb78b283ce55eaaad47f5..5cd0adbee794dbf5f60e7e08e21bb3750df472a4 100644 --- a/src/ui/parcellationRegion/region.base.ts +++ b/src/ui/parcellationRegion/region.base.ts @@ -183,9 +183,9 @@ export const getRegionParentParcRefSpace = createSelector( export class RenderViewOriginDatasetLabelPipe implements PipeTransform{ public transform(originDatasetlabels: { name: string }[], index: string|number){ if (!!originDatasetlabels && !!originDatasetlabels[index] && !!originDatasetlabels[index].name) { - return `View ${originDatasetlabels[index]['name']}` + return `${originDatasetlabels[index]['name']}` } - return `View origin dataset` + return `origin dataset` } } diff --git a/src/ui/parcellationRegion/regionMenu/regionMenu.template.html b/src/ui/parcellationRegion/regionMenu/regionMenu.template.html index b3f5408d37394f6a7d9dd6f704e4b0d2217a01f6..ba865d1c594d25fa17a255fdd80046f31c761f02 100644 --- a/src/ui/parcellationRegion/regionMenu/regionMenu.template.html +++ b/src/ui/parcellationRegion/regionMenu/regionMenu.template.html @@ -11,11 +11,36 @@ <small *ngIf="region.status"> ({{region.status}})</small> </div> </mat-card-title> - <mat-card-subtitle class="mb-0"> - <i class="fas fa-brain"></i> - <span class="text"> - Brain region - </span> + + <!-- other info about brain region --> + <mat-card-subtitle class="mb-0 ml-5-n mr-5-n"> + + <!-- origin datas --> + <button mat-button + [color]="previewDirective.active ? 'primary' : 'basic'" + *ngFor="let originDataset of (region.originDatasets || []); let index = index" + iav-dataset-preview-dataset-file + [iav-dataset-preview-dataset-file-kgid]="originDataset.kgId" + [iav-dataset-preview-dataset-file-filename]="originDataset.filename" + #previewDirective="iavDatasetPreviewDatasetFile" + iv-custom-comp + [attr.primary]="previewDirective.active || null" + role="switch" + [attr.aria-checked]="previewDirective.active" + [attr.aria-label]="SHOW_ORIGIN_DATASET"> + <mat-icon fontSet="fas" fontIcon="fa-eye"></mat-icon> + <span> + View {{ regionOriginDatasetLabels$ | async | renderViewOriginDatasetlabel : index }} + </span> + </button> + + <!-- position --> + <button mat-icon-button *ngIf="region?.position" + (click)="navigateToRegion()" + [matTooltip]="region.position | nmToMm | addUnitAndJoin : 'mm'"> + <mat-icon fontSet="fas" fontIcon="fa-map-marked-alt"> + </mat-icon> + </button> </mat-card-subtitle> </div> @@ -34,32 +59,6 @@ <mat-list class="action-list sm"> - <!-- position --> - <mat-list-item *ngIf="region?.position" (click)="navigateToRegion()" mat-ripple> - <mat-icon scaled-down fontSet="fas" fontIcon="fa-map-marked-alt" mat-list-icon></mat-icon> - <div mat-line> - {{ region.position | nmToMm | addUnitAndJoin : 'mm' }} - </div> - </mat-list-item> - - <!-- originData --> - <mat-list-item *ngFor="let originDataset of (region.originDatasets || []); let index = index" - iav-dataset-preview-dataset-file - [iav-dataset-preview-dataset-file-kgid]="originDataset.kgId" - [iav-dataset-preview-dataset-file-filename]="originDataset.filename" - #previewDirective="iavDatasetPreviewDatasetFile" - iv-custom-comp - [attr.primary]="previewDirective.active || null" - role="switch" - [attr.aria-checked]="previewDirective.active" - [attr.aria-label]="SHOW_ORIGIN_DATASET" - mat-ripple> - <mat-icon fontSet="fas" fontIcon="fa-eye" mat-list-icon></mat-icon> - <div mat-line> - {{ regionOriginDatasetLabels$ | async | renderViewOriginDatasetlabel : index }} - </div> - </mat-list-item> - <!-- connectivity --> <div *ngIf="hasConnectivity" iav-switch #connectivitySwitch="iavSwitch"> diff --git a/src/ui/ui.module.ts b/src/ui/ui.module.ts index 20b726949972a58d9365f54c269b936cdb6defcf..663c7c961be367feddfc1c8135a6ffeecde4755a 100644 --- a/src/ui/ui.module.ts +++ b/src/ui/ui.module.ts @@ -234,6 +234,7 @@ import { RenderViewOriginDatasetLabelPipe } from "./parcellationRegion/region.ba LandmarkUIComponent, NehubaViewerTouchDirective, AtlasDropdownSelector, + RenderViewOriginDatasetLabelPipe, ], schemas: [ CUSTOM_ELEMENTS_SCHEMA,