diff --git a/deploy/saneUrl/index.spec.js b/deploy/saneUrl/index.spec.js index 939d318459a534194d72307017b04d7d47487dba..3b9a66ec157e6db8e16c75dd8105cadc7fd4b8d9 100644 --- a/deploy/saneUrl/index.spec.js +++ b/deploy/saneUrl/index.spec.js @@ -230,7 +230,6 @@ describe('> saneUrl/index.js', () => { }) }) it('> checks if the name is available', async () => { - debugger await got(`http://localhost:50000/${name}`, { method: 'POST', headers: { diff --git a/src/atlasComponents/databrowserModule/databrowser.module.ts b/src/atlasComponents/databrowserModule/databrowser.module.ts index b1faaa923efd736c1c7b689e5ef2c1676ed986d8..e105fdb3bcb13450b2a9e1f08fc90bb6cfbaa0f0 100644 --- a/src/atlasComponents/databrowserModule/databrowser.module.ts +++ b/src/atlasComponents/databrowserModule/databrowser.module.ts @@ -36,7 +36,6 @@ import { LayerBrowserModule } from "../../ui/layerbrowser"; import { ContributorModule } from "./contributor"; import { DatabrowserService } from "./databrowser.service"; -import { ShownDatasetDirective } from "./shownDataset.directive"; import { RegionalFeaturesModule } from "../regionalFeatures"; import { SingleDatasetDirective } from "./singleDataset/singleDataset.directive"; import { KgDatasetModule } from "../regionalFeatures/bsFeatures/kgDataset"; @@ -75,7 +74,6 @@ const previewEmitFactory = ( overrideFn: (file: any, dataset: any) => void) => { */ PreviewDatasetFile, ShownPreviewsDirective, - ShownDatasetDirective, /** * pipes @@ -106,7 +104,6 @@ const previewEmitFactory = ( overrideFn: (file: any, dataset: any) => void) => { PreviewDatasetFile, PreviewFileTypePipe, ShownPreviewsDirective, - ShownDatasetDirective, ], entryComponents: [ PreviewComponentWrapper diff --git a/src/atlasComponents/databrowserModule/shownDataset.directive.ts b/src/atlasComponents/databrowserModule/shownDataset.directive.ts deleted file mode 100644 index dd46b310241b7f35cefb95637b3174bfb27457a1..0000000000000000000000000000000000000000 --- a/src/atlasComponents/databrowserModule/shownDataset.directive.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { Directive } from "@angular/core"; -import { select, Store } from "@ngrx/store"; -import { Observable } from "rxjs"; -import { uiStateShownDatasetIdSelector } from "src/services/state/uiState/selectors"; - -@Directive({ - selector: '[iav-shown-dataset]', - exportAs: 'iavShownDataset' -}) - -export class ShownDatasetDirective{ - public shownDatasetId$: Observable<string[]> - constructor( - store$: Store<any> - ){ - this.shownDatasetId$ = store$.pipe( - select(uiStateShownDatasetIdSelector) - ) - } -} diff --git a/src/atlasComponents/databrowserModule/util/filterDataEntriesByRegion.pipe.ts b/src/atlasComponents/databrowserModule/util/filterDataEntriesByRegion.pipe.ts index cea1159819a66e785405a6eb0fb40445f7a8edf4..8995507c1841077639afd22f4f3bfcea41d7ed3d 100644 --- a/src/atlasComponents/databrowserModule/util/filterDataEntriesByRegion.pipe.ts +++ b/src/atlasComponents/databrowserModule/util/filterDataEntriesByRegion.pipe.ts @@ -20,7 +20,6 @@ const isSubRegion = (high, low) => regionsEqual(high, low) const filterSubSelect = (dataEntry, selectedRegions) => { - if (dataEntry.name === 'Density measurements of different receptors for Area 7A (SPL) [human, v1.0]') console.log(dataEntry) return dataEntry.parcellationRegion.some(pr => selectedRegions.some(sr => isSubRegion(pr, sr))) } diff --git a/src/atlasComponents/parcellationRegion/region.base.ts b/src/atlasComponents/parcellationRegion/region.base.ts index c91ef45d1e3d6b89f7a687d6f139523b87720380..c69a2f94658760f599f896f16affca4a70ab277c 100644 --- a/src/atlasComponents/parcellationRegion/region.base.ts +++ b/src/atlasComponents/parcellationRegion/region.base.ts @@ -61,7 +61,7 @@ export class RegionBase { return this._region } - private region$: BehaviorSubject<any> = new BehaviorSubject(null) + public region$: BehaviorSubject<any> = new BehaviorSubject(null) @Input() public isSelected: boolean = false diff --git a/src/atlasComponents/parcellationRegion/regionMenu/regionMenu.template.html b/src/atlasComponents/parcellationRegion/regionMenu/regionMenu.template.html index 1c4f8bc1b0b95c611feca70196633e6018870033..52e6a76f5ef109bf25a83c722781dac41ac65722 100644 --- a/src/atlasComponents/parcellationRegion/regionMenu/regionMenu.template.html +++ b/src/atlasComponents/parcellationRegion/regionMenu/regionMenu.template.html @@ -86,24 +86,6 @@ </ng-container> </ng-template> - <!-- receptor --> - <div bs-features-receptor-directive - (bs-features-receptor-directive-fetching-flag$)="handleBusySignal('receptor', $event)" - [region]="region" - #bsFeatureReceptorDirective="bsFeatureReceptorDirective"> - </div> - <ng-template #regionalReceptorTmpl> - <bs-features-receptor-entry [region]="region"> - </bs-features-receptor-entry> - </ng-template> - <ng-container *ngTemplateOutlet="ngMatAccordionTmpl; context: { - title: 'ReceptorDistribution', - iconClass: 'fas fa-info', - iavNgIf: bsFeatureReceptorDirective.hasReceptor$ | async, - content: regionalReceptorTmpl - }"> - </ng-container> - <!-- Explore in other template --> <ng-container *ngIf="regionInOtherTemplates$ | async as regionInOtherTemplates"> @@ -135,23 +117,17 @@ <!-- kg regional features list --> <ng-template #kgRegionalFeatureList> - <kg-regional-features-list [region]="region"> - </kg-regional-features-list> + <regional-feature-wrapper [region]="region$ | async"> + </regional-feature-wrapper> </ng-template> - <div kg-regional-features-list-directive - [region]="region" - (kg-regional-features-list-directive-busy)="handleBusySignal('regionFeatureList', $event)" - #kgRegFeatlist="kgRegionalFeaturesListDirective"> - </div> - <ng-container *ngTemplateOutlet="ngMatAccordionTmpl; context: { title: CONST.REGIONAL_FEATURES, iconClass: 'fas fa-database', content: kgRegionalFeatureList, - desc: kgRegFeatlist.kgRegionalFeatures.length, - iconTooltip: kgRegFeatlist.kgRegionalFeatures.length | regionAccordionTooltipTextPipe : 'regionalFeatures', - iavNgIf: (kgRegFeatlist.kgRegionalFeatures$ | async ).length + desc: '', + iconTooltip: 'Regional Features', + iavNgIf: true }"> </ng-container> diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/bsRegionInputBase.ts b/src/atlasComponents/regionalFeatures/bsFeatures/bsRegionInputBase.ts index 9ea4ebbceb006224a914cfef04db02b1337be386..6eed3fb1bcc28e7f59b3133acb4b354a90b5a0e7 100644 --- a/src/atlasComponents/regionalFeatures/bsFeatures/bsRegionInputBase.ts +++ b/src/atlasComponents/regionalFeatures/bsFeatures/bsRegionInputBase.ts @@ -1,7 +1,7 @@ import { BehaviorSubject, throwError } from "rxjs"; import { map, switchMap } from "rxjs/operators"; import { TRegion, IBSSummaryResponse, IBSDetailResponse } from "./type"; -import { BsFeatureService } from "./service"; +import { BsFeatureService, TFeatureCmpInput } from "./service"; import { flattenReducer } from 'common/util' import { Input } from "@angular/core"; @@ -21,8 +21,13 @@ export class BsRegionInputBase{ } constructor( - private svc: BsFeatureService - ){} + private svc: BsFeatureService, + data?: TFeatureCmpInput + ){ + if (data) { + this.region = data.region + } + } protected featuresList$ = this.region$.pipe( switchMap(region => this.svc.listFeatures(region)), diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/constants.ts b/src/atlasComponents/regionalFeatures/bsFeatures/constants.ts index c7060327105a8de788c95a959af676b3c2f871ee..3746ac94431b1b414a26a5e5fb1d6aa3b46ab0a4 100644 --- a/src/atlasComponents/regionalFeatures/bsFeatures/constants.ts +++ b/src/atlasComponents/regionalFeatures/bsFeatures/constants.ts @@ -3,3 +3,4 @@ import { Observable } from "rxjs"; export { BS_ENDPOINT } from 'src/util/constants' export const BS_DARKTHEME = new InjectionToken<Observable<boolean>>('BS_DARKTHEME') +export const REGISTERED_FEATURE_INJECT_DATA = new InjectionToken('REGISTERED_FEATURE_INJECT_DATA') diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/kgDataset/showDataset/showDataset.directive.ts b/src/atlasComponents/regionalFeatures/bsFeatures/kgDataset/showDataset/showDataset.directive.ts index e8052369bf5226a29285ab37e7d65093f29d5728..f8c2aa3df8cc42b0e2af1bee890a93de6bb0fb69 100644 --- a/src/atlasComponents/regionalFeatures/bsFeatures/kgDataset/showDataset/showDataset.directive.ts +++ b/src/atlasComponents/regionalFeatures/bsFeatures/kgDataset/showDataset/showDataset.directive.ts @@ -1,7 +1,9 @@ -import { Component, Directive, HostListener, Inject, Input, Optional } from "@angular/core"; +import { Directive, HostListener, Inject, Input, Optional } from "@angular/core"; import { MatDialog, MatDialogConfig } from "@angular/material/dialog"; import { MatSnackBar } from "@angular/material/snack-bar"; import { OVERWRITE_SHOW_DATASET_DIALOG_TOKEN, TOverwriteShowDatasetDialog } from "src/util/interfaces"; +import { TRegion as TSiibraRegion } from "src/util/siibraApiConstants/types"; +import { TRegion as TContextRegion } from 'src/atlasComponents/regionalFeatures/bsFeatures/type' export const IAV_DATASET_SHOW_DATASET_DIALOG_CMP = 'IAV_DATASET_SHOW_DATASET_DIALOG_CMP' export const IAV_DATASET_SHOW_DATASET_DIALOG_CONFIG = `IAV_DATASET_SHOW_DATASET_DIALOG_CONFIG` @@ -37,6 +39,9 @@ export class ShowDatasetDialogDirective{ doi: string }[] = [] + @Input('iav-dataset-show-dataset-dialog-contexted-region') + region: TSiibraRegion & TContextRegion + constructor( private matDialog: MatDialog, private snackbar: MatSnackBar, @@ -63,7 +68,7 @@ export class ShowDatasetDialogDirective{ } if (this.overwriteFn) { - return this.overwriteFn(data) + return this.overwriteFn(data, this) } if (!this.dialogCmp) throw new Error(`IAV_DATASET_SHOW_DATASET_DIALOG_CMP not provided!`) diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/kgDataset/util.ts b/src/atlasComponents/regionalFeatures/bsFeatures/kgDataset/util.ts deleted file mode 100644 index d94e309d3d4eefc1aa39b1e63797f12586f0676d..0000000000000000000000000000000000000000 --- a/src/atlasComponents/regionalFeatures/bsFeatures/kgDataset/util.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { TBSDetail, TCountedDataModality } from "./type"; - -export function filterKgFeatureByModailty(modalities: TCountedDataModality[]){ - const visibleModNames = modalities - .filter(mod => mod.visible) - .map(mod => mod.name) - const visibleModNameSet = new Set(visibleModNames) - return function(feature: TBSDetail){ - if (modalities.every(m => !m.visible)) return true - return feature.__detail.methods.some(m => visibleModNameSet.has(m)) - } -} diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/kgRegList/kgRegList.component.ts b/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/kgRegList/kgRegList.component.ts index 2623472ab4b4f369358562f9d1540caec88d4ab4..fd5b0afac18cbfd515e7ef21718e71084f75c0b2 100644 --- a/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/kgRegList/kgRegList.component.ts +++ b/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/kgRegList/kgRegList.component.ts @@ -1,12 +1,12 @@ -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnDestroy, ViewChild } from "@angular/core"; -import { Subscription } from "rxjs"; +import { ChangeDetectionStrategy, Component, Inject, Input, OnDestroy, Optional } from "@angular/core"; +import { BehaviorSubject, Subscription } from "rxjs"; import { filter, switchMap, tap } from "rxjs/operators"; import { TCountedDataModality } from '../../kgDataset' import { BsRegionInputBase } from "../../bsRegionInputBase"; -import { BsFeatureService } from "../../service"; +import { BsFeatureService, TFeatureCmpInput } from "../../service"; import { KG_REGIONAL_FEATURE_KEY, TBSDetail, TBSSummary } from "../type"; import { ARIA_LABELS } from 'common/constants' -import { filterKgFeatureByModailty } from "../../kgDataset/util"; +import { REGISTERED_FEATURE_INJECT_DATA } from "../../constants"; @Component({ selector: 'kg-regional-features-list', @@ -29,12 +29,25 @@ export class KgRegionalFeaturesList extends BsRegionInputBase implements OnDestr public visibleRegionalFeatures: TBSSummary[] = [] public kgRegionalFeatures: TBSSummary[] = [] public kgRegionalFeatures$ = this.region$.pipe( - filter(v => !!v), + filter(v => { + this.busy$.next(false) + return !!v + }), // must not use switchmapto here - switchMap(() => this.getFeatureInstancesList(KG_REGIONAL_FEATURE_KEY)) + switchMap(() => { + this.busy$.next(true) + return this.getFeatureInstancesList(KG_REGIONAL_FEATURE_KEY).pipe( + tap(() => { + this.busy$.next(false) + }) + ) + }) ) - constructor(private cdr: ChangeDetectorRef, svc: BsFeatureService){ - super(svc) + constructor( + svc: BsFeatureService, + @Optional() @Inject(REGISTERED_FEATURE_INJECT_DATA) data: TFeatureCmpInput + ){ + super(svc, data) this.sub.push( this.kgRegionalFeatures$.subscribe(val => { this.kgRegionalFeatures = val @@ -72,26 +85,5 @@ export class KgRegionalFeaturesList extends BsRegionInputBase implements OnDestr this.dataModalities = [...this.dataModalities] } - public handleModalityVisbilityChange(modalityFilter: TCountedDataModality[]){ - this.dataModalities = modalityFilter - const visibleCountedDataM = modalityFilter.filter(dm => dm.visible) - - const filterFunc = filterKgFeatureByModailty(visibleCountedDataM) - this.visibleRegionalFeatures = this.kgRegionalFeatures.filter(sum => { - const detail = this.detailDict[sum['@id']] - if (!detail) return false - return filterFunc(detail) - }) - this.cdr.markForCheck() - } - - public clearFilters(){ - const dataModalities = this.dataModalities.map(v => { - return { - ...v, - visible: false - } - }) - this.handleModalityVisbilityChange(dataModalities) - } + public busy$ = new BehaviorSubject(false) } diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/kgRegList/kgRegList.template.html b/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/kgRegList/kgRegList.template.html index 1dd9791e0de3cbf35194f9f357cb5c2610dc33a6..4426e64ff84da575448d824158bc850e69c9aeaa 100644 --- a/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/kgRegList/kgRegList.template.html +++ b/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/kgRegList/kgRegList.template.html @@ -1,7 +1,6 @@ -<ng-container *ngTemplateOutlet="virtualScrollTmpl"> -</ng-container> +<spinner-cmp *ngIf="busy$ | async; else contentTmpl"></spinner-cmp> -<ng-template #virtualScrollTmpl> +<ng-template #contentTmpl> <cdk-virtual-scroll-viewport [attr.aria-label]="ARIA_LABELS.LIST_OF_DATASETS_ARIA_LABEL" class="h-100" @@ -18,6 +17,7 @@ mat-ripple iav-dataset-show-dataset-dialog [iav-dataset-show-dataset-dialog-fullid]="dataset['@id']" + [iav-dataset-show-dataset-dialog-contexted-region]="region" class="d-block pb-1 pt-1" [region]="region" [loadFull]="false" diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/kgRegList/kgReglist.directive.ts b/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/kgRegList/kgReglist.directive.ts index 53d4ce42ad73df004454cae121db8f81d6ffa684..60e38ee33d056bc7ebecc9ee975e6569228fefd3 100644 --- a/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/kgRegList/kgReglist.directive.ts +++ b/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/kgRegList/kgReglist.directive.ts @@ -2,15 +2,16 @@ import { Directive, EventEmitter, OnDestroy, Output } from "@angular/core"; import { KG_REGIONAL_FEATURE_KEY, TBSSummary } from "../type"; import { BsFeatureService } from "../../service"; import { BsRegionInputBase } from "../../bsRegionInputBase"; -import { Subscription } from "rxjs"; -import { filter, startWith, switchMap, tap } from "rxjs/operators"; +import { merge, of, Subscription } from "rxjs"; +import { filter, mapTo, startWith, switchMap, tap } from "rxjs/operators"; +import { IRegionalFeatureReadyDirective } from "../../type"; @Directive({ selector: '[kg-regional-features-list-directive]', exportAs: 'kgRegionalFeaturesListDirective' }) -export class KgRegionalFeaturesListDirective extends BsRegionInputBase implements OnDestroy { +export class KgRegionalFeaturesListDirective extends BsRegionInputBase implements IRegionalFeatureReadyDirective, OnDestroy { public kgRegionalFeatures: TBSSummary[] = [] public kgRegionalFeatures$ = this.region$.pipe( filter(v => !!v), @@ -18,7 +19,9 @@ export class KgRegionalFeaturesListDirective extends BsRegionInputBase implement switchMap(() => { this.busyEmitter.emit(true) return this.getFeatureInstancesList(KG_REGIONAL_FEATURE_KEY).pipe( - tap(() => this.busyEmitter.emit(false)) + tap(() => { + this.busyEmitter.emit(false) + }) ) }), startWith([]) @@ -37,6 +40,16 @@ export class KgRegionalFeaturesListDirective extends BsRegionInputBase implement while (this.sub.length) this.sub.pop().unsubscribe() } + results$ = this.kgRegionalFeatures$ + busy$ = this.region$.pipe( + switchMap(() => merge( + of(true), + this.results$.pipe( + mapTo(false) + ) + )) + ) + @Output('kg-regional-features-list-directive-busy') busyEmitter = new EventEmitter<boolean>() } \ No newline at end of file diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/module.ts b/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/module.ts index 0698f46c7e07d519434f34642557593aa0e25450..637ab5fea0ae9fa30b92a3788c03ff59aa05f8a8 100644 --- a/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/module.ts +++ b/src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature/module.ts @@ -9,6 +9,7 @@ import { KgDatasetModule } from "../kgDataset"; import { IAV_DATASET_SHOW_DATASET_DIALOG_CMP } from "../kgDataset/showDataset/showDataset.directive"; import { UtilModule } from "src/util"; import { ComponentsModule } from "src/components"; +import { BsFeatureService } from "../service"; @Component({ selector: 'blabla', @@ -56,4 +57,13 @@ export class ShowDsDialogCmp{} ] }) -export class KgRegionalFeatureModule{} +export class KgRegionalFeatureModule{ + constructor(svc: BsFeatureService){ + svc.registerFeature({ + name: 'ebrains datasets', + icon: 'fas fa-ellipsis-h', + View: KgRegionalFeaturesList, + Ctrl: KgRegionalFeaturesListDirective, + }) + } +} diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/module.ts b/src/atlasComponents/regionalFeatures/bsFeatures/module.ts index f646c4f733dcf72fac4097b1b5b7774f7fc53a3a..1c104136c9457d109335164fa8c39ab524411e69 100644 --- a/src/atlasComponents/regionalFeatures/bsFeatures/module.ts +++ b/src/atlasComponents/regionalFeatures/bsFeatures/module.ts @@ -1,21 +1,28 @@ import { CommonModule } from "@angular/common"; import { NgModule } from "@angular/core"; +import { ComponentsModule } from "src/components"; +import { AngularMaterialModule } from "src/ui/sharedModules/angularMaterial.module"; import { KgRegionalFeatureModule } from "./kgRegionalFeature"; import { BSFeatureReceptorModule } from "./receptor"; +import { RegionalFeatureWrapperCmp } from "./regionalFeatureWrapper/regionalFeatureWrapper.component"; import { BsFeatureService } from "./service"; @NgModule({ imports: [ + AngularMaterialModule, CommonModule, - BSFeatureReceptorModule, KgRegionalFeatureModule, + BSFeatureReceptorModule, + ComponentsModule, + ], + declarations: [ + RegionalFeatureWrapperCmp, ], providers: [ BsFeatureService ], exports: [ - BSFeatureReceptorModule, - KgRegionalFeatureModule, + RegionalFeatureWrapperCmp, ] }) diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/receptor/entry/entry.component.ts b/src/atlasComponents/regionalFeatures/bsFeatures/receptor/entry/entry.component.ts index f739d6af10b216efdec304935383f8c0c175e473..24b35a96d9c5b07ea67de8d1c89e6086ae2f0972 100644 --- a/src/atlasComponents/regionalFeatures/bsFeatures/receptor/entry/entry.component.ts +++ b/src/atlasComponents/regionalFeatures/bsFeatures/receptor/entry/entry.component.ts @@ -1,8 +1,9 @@ -import { Component, OnDestroy } from "@angular/core"; +import { Component, Inject, OnDestroy, Optional } from "@angular/core"; import { Observable, of, Subject, Subscription } from "rxjs"; import { filter, map, shareReplay, startWith, switchMap, tap } from "rxjs/operators"; import { BsRegionInputBase } from "../../bsRegionInputBase"; -import { BsFeatureService } from "../../service"; +import { REGISTERED_FEATURE_INJECT_DATA } from "../../constants"; +import { BsFeatureService, TFeatureCmpInput } from "../../service"; import { TBSDetail } from "../type"; @Component({ @@ -66,9 +67,10 @@ export class BsFeatureReceptorEntry extends BsRegionInputBase implements OnDestr ) constructor( - svc: BsFeatureService + svc: BsFeatureService, + @Optional() @Inject(REGISTERED_FEATURE_INJECT_DATA) data: TFeatureCmpInput ){ - super(svc) + super(svc, data) this.sub.push( this.selectedReceptor$.subscribe() ) diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/receptor/hasReceptor.directive.ts b/src/atlasComponents/regionalFeatures/bsFeatures/receptor/hasReceptor.directive.ts index 73ec965c35bccf346916b121c8da1526ab91d5cf..6ef215934ac65dda3977d9e938d328ca45956f22 100644 --- a/src/atlasComponents/regionalFeatures/bsFeatures/receptor/hasReceptor.directive.ts +++ b/src/atlasComponents/regionalFeatures/bsFeatures/receptor/hasReceptor.directive.ts @@ -1,42 +1,52 @@ -import { Directive, EventEmitter, OnDestroy, Output } from "@angular/core"; -import { merge, of, Subscription } from "rxjs"; +import { Directive, EventEmitter, Inject, OnDestroy, Optional, Output } from "@angular/core"; +import { merge, Observable, of, Subscription } from "rxjs"; import { catchError, map, mapTo, switchMap } from "rxjs/operators"; import { BsRegionInputBase } from "../bsRegionInputBase"; -import { BsFeatureService } from "../service"; +import { REGISTERED_FEATURE_INJECT_DATA } from "../constants"; +import { BsFeatureService, TFeatureCmpInput } from "../service"; +import { IBSSummaryResponse, IRegionalFeatureReadyDirective } from "../type"; @Directive({ selector: '[bs-features-receptor-directive]', exportAs: 'bsFeatureReceptorDirective' }) -export class BsFeatureReceptorDirective extends BsRegionInputBase implements OnDestroy { +export class BsFeatureReceptorDirective extends BsRegionInputBase implements IRegionalFeatureReadyDirective, OnDestroy { private sub: Subscription[] = [] ngOnDestroy(){ while (this.sub.length > 0) this.sub.pop().unsubscribe() } - - public hasReceptor$ = this.region$.pipe( - switchMap(val => merge( - of(null), + public results$: Observable<IBSSummaryResponse['ReceptorDistribution'][]> = this.region$.pipe( + switchMap(() => merge( + of([]), this.getFeatureInstancesList('ReceptorDistribution').pipe( - map(arr => arr.length > 0), - catchError(() => of(false)) + catchError(() => of([])) ) )), ) - public fetching$ = this.hasReceptor$.pipe( - map(v => v === null), + public hasReceptor$ = this.results$.pipe( + map(arr => arr.length > 0) + ) + + public busy$ = this.region$.pipe( + switchMap(() => merge( + of(true), + this.results$.pipe( + mapTo(false) + ) + )) ) constructor( - svc: BsFeatureService + svc: BsFeatureService, + @Optional() @Inject(REGISTERED_FEATURE_INJECT_DATA) data: TFeatureCmpInput, ){ - super(svc) + super(svc, data) this.sub.push( - this.fetching$.subscribe(flag => this.fetchingFlagEmitter.emit(flag)) + this.busy$.subscribe(flag => this.fetchingFlagEmitter.emit(flag)) ) } diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/receptor/module.ts b/src/atlasComponents/regionalFeatures/bsFeatures/receptor/module.ts index 79b0306b85974468a104ace9eb79e6f7254e110f..6f860b8f4c427fd5933b696f4e9a458a48b1ce24 100644 --- a/src/atlasComponents/regionalFeatures/bsFeatures/receptor/module.ts +++ b/src/atlasComponents/regionalFeatures/bsFeatures/receptor/module.ts @@ -3,6 +3,7 @@ import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from "@angular/core"; import { FormsModule } from "@angular/forms"; import { AngularMaterialModule } from "src/ui/sharedModules/angularMaterial.module"; import { UtilModule } from "src/util"; +import { BsFeatureService } from "../service"; import { BsFeatureReceptorAR } from "./ar/autoradiograph.component"; import { BsFeatureReceptorEntry } from "./entry/entry.component"; import { BsFeatureReceptorFingerprint } from "./fp/fp.component"; @@ -32,4 +33,13 @@ import { BsFeatureReceptorProfile } from "./profile/profile.component"; ] }) -export class BSFeatureReceptorModule{} +export class BSFeatureReceptorModule{ + constructor(svc: BsFeatureService){ + svc.registerFeature({ + name: 'receptor', + icon: 'fas fa-info', + View: BsFeatureReceptorEntry, + Ctrl: BsFeatureReceptorDirective, + }) + } +} diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/receptor/type.ts b/src/atlasComponents/regionalFeatures/bsFeatures/receptor/type.ts index 4776b5166a74b2d840b976e90483b23be14fc9d7..4fccfe85caf38951e0e1121769d5bf17962cc582 100644 --- a/src/atlasComponents/regionalFeatures/bsFeatures/receptor/type.ts +++ b/src/atlasComponents/regionalFeatures/bsFeatures/receptor/type.ts @@ -29,6 +29,15 @@ export type TBSSummary = { ['@id']: string name: string info: string + origin_datainfos?: ({ + name: string + description: string + } | { + urls: { + doi: string + cite?: string + }[] + })[] } export type TBSDetail = TBSSummary & { diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/regionalFeatureWrapper/regionalFeatureWrapper.component.ts b/src/atlasComponents/regionalFeatures/bsFeatures/regionalFeatureWrapper/regionalFeatureWrapper.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..0df3cd98f0519b9cfc0f22d236a9f26b2f13f619 --- /dev/null +++ b/src/atlasComponents/regionalFeatures/bsFeatures/regionalFeatureWrapper/regionalFeatureWrapper.component.ts @@ -0,0 +1,101 @@ +import { Component, ComponentFactory, ComponentFactoryResolver, Injector, Input, OnChanges, ViewChild, ViewContainerRef } from "@angular/core"; +import { TRegion } from "../type"; +import { BsFeatureService, TFeatureCmpInput, TRegisteredFeature } from "../service"; +import { BehaviorSubject, Observable } from "rxjs"; +import { map } from "rxjs/operators"; +import { REGISTERED_FEATURE_INJECT_DATA } from "../constants"; + +@Component({ + selector: 'regional-feature-wrapper', + templateUrl: './regionalFeatureWrapper.template.html', + styleUrls: [ + './regionalFeatureWrapper.style.css' + ] +}) + +export class RegionalFeatureWrapperCmp implements OnChanges{ + + @Input() + region: TRegion + + @ViewChild('regionalFeatureContainerTmpl', { read: ViewContainerRef }) + regionalFeatureContainerRef: ViewContainerRef + + private weakmap = new WeakMap<(new () => any), ComponentFactory<any>>() + + public registeredFeatures$ = this.svc.registeredFeatures$ + constructor( + private svc: BsFeatureService, + private cfr: ComponentFactoryResolver, + private injector: Injector, + ){ + + } + + private regionOnDestroyCb: (() => void)[] = [] + private setupRegionalFeatureCtrl(){ + if (!this.region) return + const { region } = this + for (const feat of this.svc.registeredFeatures){ + const ctrl = new feat.Ctrl(this.svc, { region }) + const sub = ctrl.busy$.subscribe( + flag => { + this.busyMasterStream$.next({ + [feat.name]: flag + }) + } + ) + this.regionOnDestroyCb.push(() => sub.unsubscribe()) + } + } + private cleanUpRegionalFeature(){ + while (this.regionOnDestroyCb.length) this.regionOnDestroyCb.pop()() + this.busyMasterStream$.next({}) + } + + private busyMasterStream$ = new BehaviorSubject<{ + [key: string]: boolean + }>({}) + public busy$: Observable<boolean> = this.busyMasterStream$.pipe( + map(obj => { + for (const key in obj) { + if(obj[key]) return true + } + return false + }), + ) + + ngOnChanges(){ + this.cleanUpRegionalFeature() + this.setupRegionalFeatureCtrl() + } + + public activatedFeatureName: string = null + activateFeature(feat: TRegisteredFeature){ + this.activatedFeatureName = feat.name + if (!this.regionalFeatureContainerRef) { + console.warn(`regionalFeatureContainerRef not defined.`) + return + } + this.regionalFeatureContainerRef.clear() + + const cf = (() => { + const mapped = this.weakmap.get(feat.View) + if (mapped) return mapped + const _cf = this.cfr.resolveComponentFactory(feat.View) + this.weakmap.set(feat.View ,_cf) + return _cf + })() + + const { region } = this + + const injector = Injector.create({ + providers: [{ + provide: REGISTERED_FEATURE_INJECT_DATA, + useValue: { region } as TFeatureCmpInput + }], + parent: this.injector + }) + this.regionalFeatureContainerRef.createComponent(cf, null, injector) + } +} diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/regionalFeatureWrapper/regionalFeatureWrapper.style.css b/src/atlasComponents/regionalFeatures/bsFeatures/regionalFeatureWrapper/regionalFeatureWrapper.style.css new file mode 100644 index 0000000000000000000000000000000000000000..325d83c9efcd92943efdb37b76303d1a702fcce8 --- /dev/null +++ b/src/atlasComponents/regionalFeatures/bsFeatures/regionalFeatureWrapper/regionalFeatureWrapper.style.css @@ -0,0 +1,6 @@ +.button-text +{ + white-space: normal; + line-height: 1.5rem; + text-align: center; +} diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/regionalFeatureWrapper/regionalFeatureWrapper.template.html b/src/atlasComponents/regionalFeatures/bsFeatures/regionalFeatureWrapper/regionalFeatureWrapper.template.html new file mode 100644 index 0000000000000000000000000000000000000000..6d501b94fc2dbbf271ddd5a74fcff91c4113a096 --- /dev/null +++ b/src/atlasComponents/regionalFeatures/bsFeatures/regionalFeatureWrapper/regionalFeatureWrapper.template.html @@ -0,0 +1,44 @@ +<!-- icons container --> +<div class="d-flex flex-row align-items-stretch"> + + <!-- icon container --> + <div *ngFor="let feature of registeredFeatures$ | async; let last = last" + class="d-flex flex-row"> + + <!-- hoverable/clickable --> + <div class="p-2 flex-grow-1 flex-shrink-1 w-5em d-inline-flex flex-column align-items-center iv-custom-comp hoverable" + (click)="activateFeature(feature)" + [ngClass]="{ + 'text primary': activatedFeatureName === feature.name + }" + mat-ripple> + + <!-- icon container --> + <div class="h-4rem d-flex align-items-center justify-content-center"> + <i class="d-inline-block" [ngClass]="feature.icon"></i> + </div> + + <!-- text --> + <span class="d-inline-block button-text"> + {{ feature.name }} + </span> + </div> + + <!-- divider --> + <mat-divider *ngIf="!last" vertical="true" + class="flex-grow-0 flex-shrink-0"> + </mat-divider> + + </div> +</div> + +<spinner-cmp *ngIf="busy$ | async"></spinner-cmp> + + +<span *ngIf="!activatedFeatureName" + class="text-muted pt-4"> + select type of regional feature +</span> + +<ng-template #regionalFeatureContainerTmpl> +</ng-template> diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/service.ts b/src/atlasComponents/regionalFeatures/bsFeatures/service.ts index 8b3e647382fe15c777a948fed5361b0d73f9e225..84ea01bc96978dbc38c330664d182fafad11bcba 100644 --- a/src/atlasComponents/regionalFeatures/bsFeatures/service.ts +++ b/src/atlasComponents/regionalFeatures/bsFeatures/service.ts @@ -1,17 +1,33 @@ import { HttpClient } from "@angular/common/http"; import { Inject, Injectable } from "@angular/core"; +import { BehaviorSubject } from "rxjs"; import { shareReplay } from "rxjs/operators"; import { CachedFunction } from "src/util/fn"; import { BS_ENDPOINT } from "./constants"; -import { IBSSummaryResponse, IBSDetailResponse, TRegion, IFeatureList } from './type' +import { IBSSummaryResponse, IBSDetailResponse, TRegion, IFeatureList, IRegionalFeatureReadyDirective } from './type' function processRegion(region: TRegion) { return `${region.name} ${region.status ? region.status : '' }` } -@Injectable() +export type TFeatureCmpInput = { + region: TRegion +} + +export type TRegisteredFeature<V = any> = { + name: string + icon: string // fontawesome font class, e.g. `fas fa-link-alt` + View: new (...arg: any[]) => V + Ctrl: new (svc: BsFeatureService, data: TFeatureCmpInput) => IRegionalFeatureReadyDirective +} + +@Injectable({ + providedIn: 'root' +}) export class BsFeatureService{ + public registeredFeatures: TRegisteredFeature[] = [] + public registeredFeatures$ = new BehaviorSubject<TRegisteredFeature[]>(this.registeredFeatures) public getAllFeatures$ = this.http.get(`${this.bsEndpoint}/features`).pipe( shareReplay(1) ) @@ -51,6 +67,19 @@ export class BsFeatureService{ shareReplay(1) ) } + + public registerFeature(feature: TRegisteredFeature){ + if (this.registeredFeatures.find(v => v.name === feature.name)) { + throw new Error(`feature ${feature.name} already registered`) + } + this.registeredFeatures.push(feature) + this.registeredFeatures$.next(this.registeredFeatures) + } + + public deregisterFeature(name: string){ + this.registeredFeatures = this.registeredFeatures.filter(v => v.name !== name) + this.registeredFeatures$.next(this.registeredFeatures) + } constructor( private http: HttpClient, diff --git a/src/atlasComponents/regionalFeatures/bsFeatures/type.ts b/src/atlasComponents/regionalFeatures/bsFeatures/type.ts index ff20d84d7886f95ffd544e24293250ec811328d1..e3ef2050e376fad43346482b99d55d8878078722 100644 --- a/src/atlasComponents/regionalFeatures/bsFeatures/type.ts +++ b/src/atlasComponents/regionalFeatures/bsFeatures/type.ts @@ -1,6 +1,7 @@ import { IHasId } from "src/util/interfaces"; import { TBSDetail as TReceptorDetail, TBSSummary as TReceptorSummary } from "./receptor/type"; import { KG_REGIONAL_FEATURE_KEY, TBSDetail as TKGDetail, TBSSummary as TKGSummary } from './kgRegionalFeature/type' +import { Observable } from "rxjs"; /** * change KgRegionalFeature -> EbrainsRegionalDataset in prod @@ -31,3 +32,9 @@ export interface IFeatureList { [key: string]: string }[] } + +export interface IRegionalFeatureReadyDirective { + ngOnDestroy(): void + busy$: Observable<boolean> + results$: Observable<IBSSummaryResponse[keyof IBSSummaryResponse][]> +} diff --git a/src/res/css/extra_styles.css b/src/res/css/extra_styles.css index 9be9a689c0d989b75d8596cd57f7c9b25255065b..fffbc752f26ee4c9886b8b739ea950e9f5a2259b 100644 --- a/src/res/css/extra_styles.css +++ b/src/res/css/extra_styles.css @@ -395,6 +395,11 @@ markdown-dom p height: 2rem!important; } +.h-4rem +{ + height: 4rem!important; +} + .h-50vh { height: 50vh!important; @@ -525,11 +530,11 @@ markdown-dom p } .outline-none { - outline: none; + outline: none; } .cursor-pointer { - cursor: pointer!important; + cursor: pointer!important; } .z-index-1 diff --git a/src/services/state/uiState.store.helper.ts b/src/services/state/uiState.store.helper.ts index 0abc17ccea5b66746c37e68ae7912e125c7c3975..6f1c187c15b94170fcdc5db6b5ba3d0664d6da75 100644 --- a/src/services/state/uiState.store.helper.ts +++ b/src/services/state/uiState.store.helper.ts @@ -8,8 +8,6 @@ export { uiStateExpandSidePanel, uiStateOpenSidePanel, uiStateShowBottomSheet, - uiActionHideDatasetWithId, - uiActionShowDatasetWtihId, uiActionSnackbarMessage, uiActionMouseoverLandmark, uiActionMouseoverSegments, @@ -19,7 +17,6 @@ export { uiStatePreviewingDatasetFilesSelector, uiStateMouseOverSegmentsSelector, uiStateMouseoverUserLandmark, - uiStateShownDatasetIdSelector, } from './uiState/selectors' export enum EnumWidgetTypes{ diff --git a/src/services/state/uiState.store.ts b/src/services/state/uiState.store.ts index 3fad571ab9dcc33203ff4fee3fdcffb18cd22d48..cab51af1edb88fdbcf360fb5e5a3628e9809dc51 100644 --- a/src/services/state/uiState.store.ts +++ b/src/services/state/uiState.store.ts @@ -10,9 +10,8 @@ import { MatBottomSheetRef, MatBottomSheet } from '@angular/material/bottom-shee import { uiStateCloseSidePanel, uiStateOpenSidePanel, uiStateCollapseSidePanel, uiStateExpandSidePanel, uiActionSetPreviewingDatasetFiles, uiStateShowBottomSheet, uiActionShowSidePanelConnectivity } from './uiState.store.helper'; import { viewerStateMouseOverCustomLandmark } from './viewerState/actions'; import { IUiState } from './uiState/common' -import { uiActionHideAllDatasets, uiActionHideDatasetWithId, uiActionMouseoverLandmark, uiActionMouseoverSegments, uiActionShowDatasetWtihId, uiActionSnackbarMessage } from './uiState/actions'; +import { uiActionMouseoverLandmark, uiActionMouseoverSegments, uiActionSnackbarMessage } from './uiState/actions'; export const defaultState: IUiState = { - shownDatasetId: [], previewingDatasetFiles: [], @@ -39,27 +38,7 @@ export { IUiState } export const getStateStore = ({ state = defaultState } = {}) => (prevState: IUiState = state, action: ActionInterface) => { switch (action.type) { - case uiActionHideDatasetWithId.type:{ - return { - ...prevState, - shownDatasetId: prevState.shownDatasetId.filter(id => id !== (action as any).id) - } - } - case uiActionHideAllDatasets.type:{ - return { - ...prevState, - shownDatasetId: [] - } - } - case uiActionShowDatasetWtihId.type: { - return { - ...prevState, - shownDatasetId: prevState.shownDatasetId.concat( - (action as any).id - ) - } - } - + case uiActionSetPreviewingDatasetFiles.type: { const { previewingDatasetFiles } = action as any return { diff --git a/src/services/state/uiState/actions.ts b/src/services/state/uiState/actions.ts index 60c71113bbd08b529f8bdfaf1c7f5cc12cb830d7..7f3f864483c29e791532642da6351cfd42e0b395 100644 --- a/src/services/state/uiState/actions.ts +++ b/src/services/state/uiState/actions.ts @@ -43,20 +43,6 @@ export const uiActionShowSidePanelConnectivity = createAction( `[uiState] showSidePanelConnectivity` ) -export const uiActionShowDatasetWtihId = createAction( - `[uiState] showDatasetWithId`, - props<{ id: string }>() -) - -export const uiActionHideDatasetWithId = createAction( - `[uiState] hideDatasetWithId`, - props<{ id: string }>() -) - -export const uiActionHideAllDatasets = createAction( - `[uiState] hideAllDatasets` -) - export const uiActionSnackbarMessage = createAction( `[uiState] snackbarMessage`, props<{snackbarMessage: string}>() diff --git a/src/services/state/uiState/common.ts b/src/services/state/uiState/common.ts index a5c833a42fc0c1a91fc2afeade927012e54f8d8b..7c3210c4df97b8399584f837dddf68db52db4d8e 100644 --- a/src/services/state/uiState/common.ts +++ b/src/services/state/uiState/common.ts @@ -1,5 +1,4 @@ export interface IUiState{ - shownDatasetId: string[] previewingDatasetFiles: {datasetId: string, filename: string}[] diff --git a/src/services/state/uiState/selectors.ts b/src/services/state/uiState/selectors.ts index 62064da340cce049eec2236c739bb7bd7eb15ab7..f72757b436adbf77e19becde70cff55213484504 100644 --- a/src/services/state/uiState/selectors.ts +++ b/src/services/state/uiState/selectors.ts @@ -26,8 +26,3 @@ export const uiStateMouseoverUserLandmark = createSelector( state => state['uiState'], uiState => uiState['mouseOverUserLandmark'] ) - -export const uiStateShownDatasetIdSelector = createSelector( - state => state['uiState'], - uiState => uiState['shownDatasetId'] -) diff --git a/src/state/effects/viewerState.useEffect.ts b/src/state/effects/viewerState.useEffect.ts index 9bc25f0f68015a9ab1542c4ae65fddf87d38d1a4..0764f558afe0a66d924e368d174fc3b8098a8f02 100644 --- a/src/state/effects/viewerState.useEffect.ts +++ b/src/state/effects/viewerState.useEffect.ts @@ -11,7 +11,6 @@ import { ngViewerSelectorClearViewEntries } from "src/services/state/ngViewerSta import { ngViewerActionClearView } from "src/services/state/ngViewerState/actions"; import { PureContantService } from "src/util"; import { CONST } from 'common/constants' -import { uiActionHideAllDatasets } from "src/services/state/uiState/actions"; import { viewerStateFetchedAtlasesSelector, viewerStateGetSelectedAtlas } from "src/services/state/viewerState/selectors"; import { viewerStateChangeNavigation } from "src/services/state/viewerState/actions"; import { cvtNavigationObjToNehubaConfig } from 'src/viewerModule/nehuba/util' @@ -147,16 +146,6 @@ export class ViewerStateControllerUseEffect implements OnDestroy { }) ) - /** - * on region selected change (clear, select, or change selection), clear selected dataset ids - */ - @Effect() - public clearShownDatasetIdOnRegionClear: Observable<any> = this.store$.pipe( - select(viewerStateSelectedRegionsSelector), - mapTo( - uiActionHideAllDatasets() - ) - ) @Effect() public selectParcellation$: Observable<any> diff --git a/src/theme.scss b/src/theme.scss index 85ecba40b9d80b41f5692c7b9a4ea88bd195d075..f311da58eb993b86a235179246c4ab88dcbc79c1 100644 --- a/src/theme.scss +++ b/src/theme.scss @@ -40,7 +40,7 @@ &[bg], &.bg { - background-color: mat-color($background, background) + background-color: mat-color($background, background); } &[darker-bg], @@ -71,6 +71,15 @@ { color: mat-color($warn); } + + &.hoverable + { + &:hover + { + background-color: mat-color($background, hover); + cursor: pointer; + } + } } } diff --git a/src/util/interfaces.ts b/src/util/interfaces.ts index 0486e1159e61f8280456a32abd44a76df37f5051..d389168ba3299303bd78192ffb1c0632b0ed6897 100644 --- a/src/util/interfaces.ts +++ b/src/util/interfaces.ts @@ -14,7 +14,7 @@ export interface IHasFullId{ } -export type TOverwriteShowDatasetDialog = (dataset: { fullId: string } | { name: string, description: string }) => void +export type TOverwriteShowDatasetDialog = (dataset: { fullId: string } | { name: string, description: string }, arg?: any) => void export const OVERWRITE_SHOW_DATASET_DIALOG_TOKEN = new InjectionToken<TOverwriteShowDatasetDialog>('OVERWRITE_SHOW_DATASET_DIALOG_TOKEN') diff --git a/src/viewerModule/module.ts b/src/viewerModule/module.ts index 566471f479781b9646e644e19b0f8f63445693b1..7abfbb1bed3c1eb8135526e51a3ddb2a6f8755e2 100644 --- a/src/viewerModule/module.ts +++ b/src/viewerModule/module.ts @@ -24,6 +24,7 @@ import { NEHUBA_INSTANCE_INJTKN } from "./nehuba/util"; import { map } from "rxjs/operators"; import { TContextArg } from "./viewer.interface"; import { ViewerStateBreadCrumbModule } from "./viewerStateBreadCrumb/module"; +import { KgRegionalFeatureModule } from "src/atlasComponents/regionalFeatures/bsFeatures/kgRegionalFeature"; @NgModule({ imports: [ @@ -45,6 +46,7 @@ import { ViewerStateBreadCrumbModule } from "./viewerStateBreadCrumb/module"; QuickTourModule, ContextMenuModule, ViewerStateBreadCrumbModule, + KgRegionalFeatureModule, ], declarations: [ ViewerCmp, diff --git a/src/viewerModule/viewerCmp/viewerCmp.component.ts b/src/viewerModule/viewerCmp/viewerCmp.component.ts index ff22be0f407c5d114e0956686febae7d0e5c92c1..6b0ca9cbc0a397655fab6fa70bf42ecb8ef1d7d5 100644 --- a/src/viewerModule/viewerCmp/viewerCmp.component.ts +++ b/src/viewerModule/viewerCmp/viewerCmp.component.ts @@ -11,7 +11,6 @@ import { viewerStateViewerModeSelector } from "src/services/state/viewerState/selectors" import { CONST, ARIA_LABELS, QUICKTOUR_DESC } from 'common/constants' -import { uiActionHideAllDatasets, uiActionHideDatasetWithId, uiActionShowDatasetWtihId } from "src/services/state/uiState/actions"; import { OVERWRITE_SHOW_DATASET_DIALOG_TOKEN, REGION_OF_INTEREST } from "src/util/interfaces"; import { animate, state, style, transition, trigger } from "@angular/animations"; import { SwitchDirective } from "src/util/directives/switch.directive"; @@ -21,6 +20,26 @@ import { PureContantService } from "src/util"; import { EnumViewerEvt, TContextArg, TSupportedViewers, TViewerEvent } from "../viewer.interface"; import { getGetRegionFromLabelIndexId } from "src/util/fn"; import { ContextMenuService, TContextMenuReg } from "src/contextMenuModule"; +import { ComponentStore } from "../componentStore"; + +interface IOverlayTypes { + ebrainsRegionalDataset: { + datasetId: string + atlasId: string + parcId: string + region: any + spaceId?: string + } +} + +type TOverlaySideNav<T extends keyof IOverlayTypes> = { + '@type': T + context: IOverlayTypes[T] +} + +type TCStoreViewerCmp = { + overlaySideNav: TOverlaySideNav<keyof IOverlayTypes> +} @Component({ selector: 'iav-cmp-viewer-container', @@ -90,19 +109,31 @@ import { ContextMenuService, TContextMenuReg } from "src/contextMenuModule"; }, { provide: OVERWRITE_SHOW_DATASET_DIALOG_TOKEN, - useFactory: (store: Store) => { - return function overwriteShowDatasetDialog( arg: { fullId?: string, name: string, description: string } ){ - if (arg.fullId) { - store.dispatch( - uiActionShowDatasetWtihId({ - id: arg.fullId - }) - ) - } + useFactory: (cStore: ComponentStore<TCStoreViewerCmp>) => { + return function overwriteShowDatasetDialog( arg: { fullId?: string, name: string, description: string }, data: any ){ + + const { region } = data + const datasetId = arg.fullId + const atlasId = data?.region?.context?.atlas?.['@id'] + const parcId = data?.region?.context?.parcellation?.['@id'] + const spaceId = data?.region?.context?.template?.['@id'] + cStore.setState({ + overlaySideNav: { + '@type': 'ebrainsRegionalDataset', + context: { + datasetId, + atlasId, + parcId, + region, + spaceId, + } + } + }) } }, - deps: [ Store ] + deps: [ ComponentStore ] }, + ComponentStore ] }) @@ -156,6 +187,8 @@ export class ViewerCmp implements OnDestroy { select(viewerStateViewerModeSelector), ) + public overlaySidenav$ = this.cStore.select(s => s.overlaySideNav) + public useViewer$: Observable<TSupportedViewers | 'notsupported'> = combineLatest([ this.templateSelected$, this.isStandaloneVolumes$, @@ -203,10 +236,14 @@ export class ViewerCmp implements OnDestroy { constructor( private store$: Store<any>, private viewerModuleSvc: ContextMenuService<TContextArg<'threeSurfer' | 'nehuba'>>, + private cStore: ComponentStore<TCStoreViewerCmp>, @Optional() @Inject(REGION_OF_INTEREST) public regionOfInterest$: Observable<any> ){ this.subscriptions.push( + this.selectedRegions$.subscribe(() => { + this.clearPreviewingDataset() + }), this.alwaysHideMinorPanel$.pipe( distinctUntilChanged(), filter(flag => !flag), @@ -314,15 +351,13 @@ export class ViewerCmp implements OnDestroy { this.sidenavTopSwitch && this.sidenavTopSwitch.open() } - public clearPreviewingDataset(id?: string){ + public clearPreviewingDataset(){ /** * clear all preview */ - this.store$.dispatch( - id - ? uiActionHideDatasetWithId({ id }) - : uiActionHideAllDatasets() - ) + this.cStore.setState({ + overlaySideNav: null + }) } @ViewChild('regionSelRef', { read: ElementRef }) diff --git a/src/viewerModule/viewerCmp/viewerCmp.template.html b/src/viewerModule/viewerCmp/viewerCmp.template.html index 0d8938c15635d9856ee6ba14a3c16933a0225528..38a567c91715a90926fd1a322c52422f420c50d7 100644 --- a/src/viewerModule/viewerCmp/viewerCmp.template.html +++ b/src/viewerModule/viewerCmp/viewerCmp.template.html @@ -189,60 +189,43 @@ (@openClose.done)="$event.toState === 'closed' && matDrawerLeft.close()" [disableClose]="true"> - <div class="position-relative d-flex flex-column h-100"> + <!-- check if preview volume --> + <div *ngIf="overlaySidenav$ | async as overlaySideNav" class="position-relative d-flex flex-column h-100"> + <div class="position-relative ml-15px-n mr-15px-n"> + + <!-- back btn --> + <button mat-button + (click)="clearPreviewingDataset()" + [attr.aria-label]="ARIA_LABELS.CLOSE" + class="position-absolute z-index-10 m-2"> + <i class="fas fa-chevron-left"></i> + <span class="ml-1"> + Back + </span> + </button> - <!-- TODO dataset preview will become deprecated in the future. - Regional feature/data feature will replace it --> - <div class="hidden" - iav-shown-dataset - #iavShownDataset="iavShownDataset"> + <!-- ebrains region --> + <kg-regional-feature-detail + [summary]="{'@id': overlaySideNav['context']['datasetId']}" + [region]="overlaySideNav['context']['region']"> + </kg-regional-feature-detail> </div> + </div> - <div class="hidden" - iav-shown-previews - (emitter)="iavAdditionalLayers$.next($event)" - #previews="iavShownPreviews"> - </div> + <div [ngClass]="{ + 'invisible overflow-hidden h-0': overlaySidenav$ | async, + 'h-100': !(overlaySidenav$ | async) + }" class="position-relative d-flex flex-column"> - <!-- sidenav datasets --> - <ng-container *ngIf="iavShownDataset.shownDatasetId$ | async as shownDatasetId"> - <ng-template [ngIf]="shownDatasetId.length > 0" [ngIfElse]="sideNavVolumePreview"> - - <div class="position-relative ml-15px-n mr-15px-n"> - - <!-- back btn --> - <button mat-button - (click)="clearPreviewingDataset()" - [attr.aria-label]="ARIA_LABELS.CLOSE" - class="position-absolute z-index-10 m-2"> - <i class="fas fa-chevron-left"></i> - <span class="ml-1"> - Back - </span> - </button> - - <!-- ebrains region --> - <kg-regional-feature-detail - *ngFor="let datasetId of shownDatasetId" - [summary]="{'@id': datasetId}" - [region]="selectedRegions$ | async | getNthElement : 0"> - </kg-regional-feature-detail> - </div> - </ng-template> + <ng-container *ngTemplateOutlet="sidenavRegionTmpl"> </ng-container> - <!-- preview volumes --> - <ng-template #sideNavVolumePreview> - <ng-container *ngIf="previews.iavAdditionalLayers$ | async as volumePreviews"> - <ng-template [ngIf]="volumePreviews.length > 0" [ngIfElse]="sidenavRegionTmpl"> - <ng-container *ngFor="let vPreview of volumePreviews"> - <ng-container *ngTemplateOutlet="sidenavDsPreviewTmpl; context: vPreview"> - - </ng-container> - </ng-container> - </ng-template> - </ng-container> - </ng-template> + <!-- TODO dataset preview will become deprecated in the future. + Regional feature/data feature will replace it --> + <!-- <div class="hidden" + iav-shown-dataset + #iavShownDataset="iavShownDataset"> + </div> --> </div> </mat-drawer> @@ -525,16 +508,16 @@ </mat-expansion-panel> </ng-template> -<ng-template #sidenavDsPreviewTmpl let-file="file" let-filename="filename" let-datasetId="datasetId"> +<!-- misc dataset (e.g. PLI) --> +<!-- <ng-template #sidenavDsPreviewTmpl let-file="file" let-filename="filename" let-datasetId="datasetId"> <div class="w-100 flex-grow-1 d-flex flex-column"> Previewing misc dataset - <!-- collapse btn --> <ng-container *ngTemplateOutlet="collapseBtn"> </ng-container> </div> -</ng-template> +</ng-template> --> <!-- select region error... for whatever reason --> <ng-template #selectRegionErrorTmpl>