From fe5763b0fb3278c2b14ede79954c6e7c4304439e Mon Sep 17 00:00:00 2001 From: Xiao Gui <xgui3783@gmail.com> Date: Fri, 6 Nov 2020 17:23:42 +0100 Subject: [PATCH] expmt: regional features redesign --- common/util.js | 16 +++ deploy/datasets/query.js | 14 +- deploy/regionalFeatures/index.js | 24 +++- .../databrowserModule/databrowser.service.ts | 4 +- .../databrowser/databrowser.base.ts | 89 ++++++------ .../databrowser/databrowser.component.ts | 7 +- .../databrowser/databrowser.directive.ts | 8 +- .../showDatasetDialog.directive.ts | 43 +++--- .../detailedView/singleDataset.component.ts | 3 + .../detailedView/singleDataset.template.html | 22 +-- .../nehubaContainer.template.html | 17 +-- src/ui/regionalFeatures/module.ts | 2 + .../regionalFeature.service.ts | 4 +- .../regionalFeaturesCmp.component.ts | 45 +++++- .../regionalFeaturesCmp.template.html | 130 ++++++++---------- src/util/interfaces.ts | 7 + 16 files changed, 248 insertions(+), 187 deletions(-) diff --git a/common/util.js b/common/util.js index f506a1cfb..b2da27670 100644 --- a/common/util.js +++ b/common/util.js @@ -13,6 +13,22 @@ } } + const setsContain = (set1, set2) => { + for (const el of set2){ + if (!set1.has(el)) return false + } + return true + } + + exports.setsContain = setsContain + + exports.setsEql = (set1, set2) => { + if (set1.size !== set2.size) return false + if (!setsContain(set1, set2)) return false + if (!setsContain(set2, set1)) return false + return true + } + /** * * https://stackoverflow.com/a/16348977/6059235 diff --git a/deploy/datasets/query.js b/deploy/datasets/query.js index 091bc7fcd..2219f2da8 100644 --- a/deploy/datasets/query.js +++ b/deploy/datasets/query.js @@ -6,6 +6,7 @@ const archiver = require('archiver') const { getPreviewFile, hasPreview } = require('./supplements/previewFile') const { constants, init: kgQueryUtilInit, getUserKGRequestParam, filterDatasets, filterDatasetsByRegion } = require('./util') const ibc = require('./importIBS') +const { returnAdditionalDatasets } = require('../regionalFeatures') let cachedData = null @@ -108,9 +109,16 @@ const getPublicDs = async () => { } -const getDs = ({ user }) => user - ? fetchDatasetFromKg({ user }).then(({ results }) => results) - : getPublicDs() +const getDs = ({ user }) => (user + ? fetchDatasetFromKg({ user }).then(({ results }) => results) + : getPublicDs() + ).then(async datasets => { + + return [ + ...datasets, + ...(await returnAdditionalDatasets()), + ] + }) const getExternalSchemaDatasets = (kgId, kgSchema) => { if (kgSchema === ibc.IBC_SCHEMA) { diff --git a/deploy/regionalFeatures/index.js b/deploy/regionalFeatures/index.js index d505c4282..dc844d787 100644 --- a/deploy/regionalFeatures/index.js +++ b/deploy/regionalFeatures/index.js @@ -18,6 +18,8 @@ const regionIdToDataIdMap = new Map() const datasetIdToDataMap = new Map() const datasetIdDetailMap = new Map() +let additionalDatasets = [] +const returnAdditionalDatasets = async () => additionalDatasets let isReady = false const ITERABLE_KEY_SYMBOL = Symbol('ITERABLE_KEY_SYMBOL') @@ -27,7 +29,7 @@ const ITERABLE_KEY_SYMBOL = Symbol('ITERABLE_KEY_SYMBOL') * async await would mean it is fetched one at a time */ -const init = Promise.all( +Promise.all( arrayToFetch.map(url => new Promise((rs, rj) => { request.get(url, (err, _resp, body) => { @@ -73,7 +75,24 @@ const init = Promise.all( }) }) ) -).then(() => isReady = true) +).then(() => { + const map = new Map() + for (const [regionId, regionObj] of regionIdToDataIdMap.entries()) { + for (const datasetId of regionObj[ITERABLE_KEY_SYMBOL]) { + const newArr = (map.get(datasetId) || []).concat(regionId) + map.set(datasetId, newArr) + } + } + + for (const [ datasetId, arrRegionIds ] of map.entries()) { + additionalDatasets = additionalDatasets.concat({ + fullId: datasetId, + parcellationRegion: arrRegionIds.map(id => ({ fullId: id })) + }) + } + + isReady = true +}) const getFeatureMiddleware = (req, res, next) => { const { featureFullId } = req.params @@ -221,4 +240,5 @@ const regionalFeatureIsReady = async () => isReady module.exports = { router, regionalFeatureIsReady, + returnAdditionalDatasets, } diff --git a/src/ui/databrowserModule/databrowser.service.ts b/src/ui/databrowserModule/databrowser.service.ts index 25c601d5e..fb1c8cc09 100644 --- a/src/ui/databrowserModule/databrowser.service.ts +++ b/src/ui/databrowserModule/databrowser.service.ts @@ -64,7 +64,7 @@ export class DatabrowserService implements OnDestroy { }) } public createDatabrowser: (arg: {regions: any[], template: any, parcellation: any}) => {dataBrowser: ComponentRef<DataBrowser>, widgetUnit: ComponentRef<WidgetUnit>} - public getDataByRegion: ({ regions, parcellation, template }: {regions: any[], parcellation: any, template: any}) => Promise<IDataEntry[]> = ({regions, parcellation, template}) => + public getDataByRegion: (arg: {regions: any[] }) => Observable<IDataEntry[]> = ({ regions }) => forkJoin(regions.map(this.getDatasetsByRegion.bind(this))).pipe( map( (arrOfArr: IDataEntry[][]) => arrOfArr.reduce( @@ -81,7 +81,7 @@ export class DatabrowserService implements OnDestroy { [] ) ) - ).toPromise() + ) private filterDEByRegion: FilterDataEntriesByRegion = new FilterDataEntriesByRegion() private dataentries: IDataEntry[] = [] diff --git a/src/ui/databrowserModule/databrowser/databrowser.base.ts b/src/ui/databrowserModule/databrowser/databrowser.base.ts index 04b10b309..a3ec2a4fc 100644 --- a/src/ui/databrowserModule/databrowser/databrowser.base.ts +++ b/src/ui/databrowserModule/databrowser/databrowser.base.ts @@ -1,23 +1,32 @@ -import { Input, Output, EventEmitter } from "@angular/core" +import { Input, Output, EventEmitter, OnDestroy } from "@angular/core" import { LoggingService } from "src/logging" import { DatabrowserService } from "../singleDataset/singleDataset.base" -import { Observable } from "rxjs" +import { Observable, Subject, Subscription } from "rxjs" import { IDataEntry } from "src/services/stateStore.service" -import { getUniqueRegionId } from 'common/util' +import { getIdFromFullId, setsEql } from 'common/util' +import { switchMap, tap } from "rxjs/operators" -export class DatabrowserBase{ +export class DatabrowserBase implements OnDestroy{ + + private _subscriptions: Subscription[] = [] @Output() public dataentriesUpdated: EventEmitter<IDataEntry[]> = new EventEmitter() + private _regions: any[] = [] + + public regions$ = new Subject<any[]>() + get regions(){ + return this._regions + } @Input() - regions: any[] = [] - - @Input() - public template: any - - @Input() - public parcellation: any + set regions(arr: any[]){ + const currentSet = new Set(this._regions.map(r => getIdFromFullId(r.fullId))) + const newSet = new Set(arr.map(r => getIdFromFullId(r.fullId)).filter(v => !!v)) + if (setsEql(newSet, currentSet)) return + this._regions = arr.filter(r => !!getIdFromFullId(r.fullId)) + this.regions$.next(this._regions) + } public fetchError: boolean = false public fetchingFlag = false @@ -32,47 +41,27 @@ export class DatabrowserBase{ ){ this.favDataentries$ = this.dbService.favedDataentries$ + + this._subscriptions.push( + this.regions$.pipe( + tap(() => this.fetchingFlag = true), + switchMap(regions => this.dbService.getDataByRegion({ regions })), + ).subscribe( + de => { + this.fetchingFlag = false + this.dataentries = de + this.dataentriesUpdated.emit(de) + }, + e => { + this.log.error(e) + this.fetchError = true + } + ) + ) } - ngOnChanges(){ - - const { regions, parcellation, template } = this - this.regions = this.regions.map(r => { - /** - * TODO to be replaced with properly region UUIDs from KG - */ - const uniqueRegionId = getUniqueRegionId(template, parcellation, r) - return { - fullId: uniqueRegionId, - id: uniqueRegionId, - ...r, - } - }) - this.fetchingFlag = true - - // input may be undefined/null - if (!parcellation) { return } - - /** - * reconstructing parcellation region is async (done on worker thread) - * if parcellation region is not yet defined, return. - * parccellation will eventually be updated with the correct region - */ - if (!parcellation.regions) { return } - - this.dbService.getDataByRegion({ regions, parcellation, template }) - .then(de => { - this.dataentries = de - return de - }) - .catch(e => { - this.log.error(e) - this.fetchError = true - }) - .finally(() => { - this.fetchingFlag = false - this.dataentriesUpdated.emit(this.dataentries) - }) + ngOnDestroy(){ + while(this._subscriptions.length > 0) this._subscriptions.pop().unsubscribe() } public retryFetchData(event: MouseEvent) { diff --git a/src/ui/databrowserModule/databrowser/databrowser.component.ts b/src/ui/databrowserModule/databrowser/databrowser.component.ts index 4ed2d667c..fb9513254 100644 --- a/src/ui/databrowserModule/databrowser/databrowser.component.ts +++ b/src/ui/databrowserModule/databrowser/databrowser.component.ts @@ -20,7 +20,7 @@ const { MODALITY_FILTER, LIST_OF_DATASETS } = ARIA_LABELS changeDetection: ChangeDetectionStrategy.OnPush, }) -export class DataBrowser extends DatabrowserBase implements OnChanges, OnDestroy, OnInit { +export class DataBrowser extends DatabrowserBase implements OnDestroy, OnInit { @Input() disableVirtualScroll: boolean = false @@ -59,10 +59,6 @@ export class DataBrowser extends DatabrowserBase implements OnChanges, OnDestroy super(dataService, log) } - public ngOnChanges() { - super.ngOnChanges() - } - public ngOnInit() { this.subscriptions.push( @@ -100,6 +96,7 @@ export class DataBrowser extends DatabrowserBase implements OnChanges, OnDestroy } public ngOnDestroy() { + super.ngOnDestroy() this.subscriptions.forEach(s => s.unsubscribe()) } diff --git a/src/ui/databrowserModule/databrowser/databrowser.directive.ts b/src/ui/databrowserModule/databrowser/databrowser.directive.ts index 5eeeacf5f..911a8a2b9 100644 --- a/src/ui/databrowserModule/databrowser/databrowser.directive.ts +++ b/src/ui/databrowserModule/databrowser/databrowser.directive.ts @@ -1,4 +1,4 @@ -import { Directive } from "@angular/core"; +import { Directive, OnDestroy } from "@angular/core"; import { DatabrowserBase } from "./databrowser.base"; import { DatabrowserService } from "../singleDataset/singleDataset.base"; import { LoggingService } from "src/logging"; @@ -8,11 +8,15 @@ import { LoggingService } from "src/logging"; exportAs: 'iavDatabrowserDirective' }) -export class DatabrowserDirective extends DatabrowserBase{ +export class DatabrowserDirective extends DatabrowserBase implements OnDestroy{ constructor( dataService: DatabrowserService, log: LoggingService, ){ super(dataService, log) } + + ngOnDestroy(){ + super.ngOnDestroy() + } } diff --git a/src/ui/databrowserModule/showDatasetDialog.directive.ts b/src/ui/databrowserModule/showDatasetDialog.directive.ts index 1542f5139..0cdd393e6 100644 --- a/src/ui/databrowserModule/showDatasetDialog.directive.ts +++ b/src/ui/databrowserModule/showDatasetDialog.directive.ts @@ -1,6 +1,7 @@ -import { Directive, Input, HostListener, Inject } from "@angular/core"; +import { Directive, Input, HostListener, Inject, InjectionToken, Optional } from "@angular/core"; import { MatDialog } from "@angular/material/dialog"; import { MatSnackBar } from "@angular/material/snack-bar"; +import { OVERWRITE_SHOW_DATASET_DIALOG_TOKEN, TOverwriteShowDatasetDialog } from "src/util/interfaces"; 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` @@ -34,29 +35,35 @@ export class ShowDatasetDialogDirective{ constructor( private matDialog: MatDialog, private snackbar: MatSnackBar, - @Inject(IAV_DATASET_SHOW_DATASET_DIALOG_CMP) private dialogCmp: any + @Inject(IAV_DATASET_SHOW_DATASET_DIALOG_CMP) private dialogCmp: any, + @Optional() @Inject(OVERWRITE_SHOW_DATASET_DIALOG_TOKEN) private overwriteFn: TOverwriteShowDatasetDialog ){ } @HostListener('click') onClick(){ - - if (this.fullId || (this.kgSchema && this.kgId)) { - - this.matDialog.open(this.dialogCmp, { - ...ShowDatasetDialogDirective.defaultDialogConfig, - data: { + const data = (() => { + if (this.fullId || (this.kgSchema && this.kgId)) { + return { fullId: this.fullId || `${this.kgSchema}/${this.kgId}` } - }) - - } else if (this.name || this.description) { - const { name, description } = this - this.matDialog.open(this.dialogCmp, { - ...ShowDatasetDialogDirective.defaultDialogConfig, - data: { name, description } - }) - } else { - this.snackbar.open(`Cannot show dataset. Neither fullId nor kgId provided.`) + } + if (this.name || this.description) { + const { name, description } = this + return { name, description } + } + })() + + if (!data) { + return this.snackbar.open(`Cannot show dataset. Neither fullId nor kgId provided.`) } + + if (this.overwriteFn) { + return this.overwriteFn(data) + } + + this.matDialog.open(this.dialogCmp, { + ...ShowDatasetDialogDirective.defaultDialogConfig, + data + }) } } diff --git a/src/ui/databrowserModule/singleDataset/detailedView/singleDataset.component.ts b/src/ui/databrowserModule/singleDataset/detailedView/singleDataset.component.ts index 2d34d3971..d273ee2f1 100644 --- a/src/ui/databrowserModule/singleDataset/detailedView/singleDataset.component.ts +++ b/src/ui/databrowserModule/singleDataset/detailedView/singleDataset.component.ts @@ -42,4 +42,7 @@ export class SingleDatasetView extends SingleDatasetBase { @Input() hideDownloadBtn = false + + @Input() + useSmallIcon = false } diff --git a/src/ui/databrowserModule/singleDataset/detailedView/singleDataset.template.html b/src/ui/databrowserModule/singleDataset/detailedView/singleDataset.template.html index 617182ba4..ac830cd4f 100644 --- a/src/ui/databrowserModule/singleDataset/detailedView/singleDataset.template.html +++ b/src/ui/databrowserModule/singleDataset/detailedView/singleDataset.template.html @@ -56,7 +56,9 @@ <!-- footer --> <mat-card-actions iav-media-query #iavMediaQuery="iavMediaQuery"> - <ng-container *ngTemplateOutlet="actionBtns; context: { $implicit: (iavMediaQuery.mediaBreakPoint$ | async) }" > + <ng-container *ngTemplateOutlet="actionBtns; context: { + $implicit: useSmallIcon || (iavMediaQuery.mediaBreakPoint$ | async) > 1 + }" > </ng-container> </mat-card-actions> @@ -70,7 +72,7 @@ </ng-template> <!-- using ng template for context binding of media breakpoints --> -<ng-template #actionBtns let-mediaBreakPoint> +<ng-template #actionBtns let-useSmallIcon> <!-- explore --> <ng-container *ngIf="!strictLocal && !hideExplore"> @@ -79,10 +81,10 @@ [href]="kgRef | doiParserPipe" target="_blank"> <iav-dynamic-mat-button - [iav-dynamic-mat-button-style]="mediaBreakPoint < 2 ? 'mat-raised-button' : 'mat-icon-button'" + [iav-dynamic-mat-button-style]="useSmallIcon ? 'mat-icon-button' : 'mat-raised-button'" iav-dynamic-mat-button-color="primary"> - <span *ngIf="mediaBreakPoint < 2"> + <span *ngIf="!useSmallIcon"> Explore </span> <i class="fas fa-external-link-alt"></i> @@ -102,10 +104,10 @@ (click)="isFav ? undoableRemoveFav() : undoableAddFav()" iav-stop="click mousedown" [iav-dynamic-mat-button-aria-label]="PIN_DATASET_ARIA_LABEL" - [iav-dynamic-mat-button-style]="mediaBreakPoint < 2 ? 'mat-button' : 'mat-icon-button'" + [iav-dynamic-mat-button-style]="useSmallIcon ? 'mat-icon-button' : 'mat-button'" [iav-dynamic-mat-button-color]="isFav ? 'primary' : 'basic'"> - <span *ngIf="mediaBreakPoint < 2"> + <span *ngIf="!useSmallIcon"> {{ isFav ? 'Unpin this dataset' : 'Pin this dataset' }} </span> <i class="fas fa-thumbtack"></i> @@ -122,9 +124,9 @@ <iav-dynamic-mat-button [matTooltip]="tooltipText" [disabled]="downloadInProgress" - [iav-dynamic-mat-button-style]="mediaBreakPoint < 2 ? 'mat-button' : 'mat-icon-button'"> + [iav-dynamic-mat-button-style]="useSmallIcon ? 'mat-icon-button' : 'mat-button'"> - <span *ngIf="mediaBreakPoint < 2"> + <span *ngIf="!useSmallIcon"> Download Zip </span> <i class="ml-1 fas" [ngClass]="!downloadInProgress? 'fa-download' :'fa-spinner fa-pulse'"></i> @@ -149,11 +151,11 @@ <iav-dynamic-mat-button *ngIf="hasPreview" mat-dialog-close - [iav-dynamic-mat-button-style]="mediaBreakPoint < 2 ? 'mat-button' : 'mat-icon-button'" + [iav-dynamic-mat-button-style]="useSmallIcon ? 'mat-icon-button' : 'mat-button'" [iav-dynamic-mat-button-aria-label]="SHOW_DATASET_PREVIEW_ARIA_LABEL" (click)="showPreviewList(previewFilesListTemplate)"> - <span *ngIf="mediaBreakPoint < 2"> + <span *ngIf="!useSmallIcon"> Preview </span> <i class="ml-1 far fa-eye"></i> diff --git a/src/ui/nehubaContainer/nehubaContainer.template.html b/src/ui/nehubaContainer/nehubaContainer.template.html index 479512f7a..16e175cdd 100644 --- a/src/ui/nehubaContainer/nehubaContainer.template.html +++ b/src/ui/nehubaContainer/nehubaContainer.template.html @@ -355,16 +355,15 @@ <!-- regional features--> <ng-template #regionalFeaturesTmpl> - <data-browser [template]="templateSelected$ | async" + <data-browser [parcellation]="selectedParcellation" [disableVirtualScroll]="true" [regions]="regions"> </data-browser> </ng-template> - <div class="hidden" iav-databrowser-directive - [template]="templateSelected$ | async" - [parcellation]="selectedParcellation" + <div class="hidden" + iav-databrowser-directive [regions]="regions" #iavDbDirective="iavDatabrowserDirective"> </div> @@ -460,19 +459,11 @@ <!-- regional features--> <ng-template #regionalFeaturesTmpl let-expansionPanel="expansionPanel"> - <regional-features *ngIf="expansionPanel.expanded" - [region]="region"> - <data-browser [template]="templateSelected$ | async" - [parcellation]="selectedParcellation" - [disableVirtualScroll]="true" - [regions]="[region]"> - </data-browser> + <regional-features *ngIf="expansionPanel.expanded" [region]="region"> </regional-features> </ng-template> <div class="hidden" iav-databrowser-directive - [template]="templateSelected$ | async" - [parcellation]="selectedParcellation" [regions]="[region]" #iavDbDirective="iavDatabrowserDirective"> </div> diff --git a/src/ui/regionalFeatures/module.ts b/src/ui/regionalFeatures/module.ts index 813cc93fc..0849b2fba 100644 --- a/src/ui/regionalFeatures/module.ts +++ b/src/ui/regionalFeatures/module.ts @@ -1,6 +1,7 @@ import { CommonModule } from "@angular/common"; import { NgModule } from "@angular/core"; import { UtilModule } from "src/util"; +import { DatabrowserModule } from "../databrowserModule"; import { AngularMaterialModule } from "../sharedModules/angularMaterial.module"; import { FeatureExplorer } from "./featureExplorer/featureExplorer.component"; import { RegionalFeatureInteractivity } from "./interactivity.directive"; @@ -14,6 +15,7 @@ import { RegionalFeaturesCmp } from "./regionalFeaturesCmp/regionalFeaturesCmp.c CommonModule, UtilModule, AngularMaterialModule, + DatabrowserModule, ], declarations: [ /** diff --git a/src/ui/regionalFeatures/regionalFeature.service.ts b/src/ui/regionalFeatures/regionalFeature.service.ts index b129bb1e6..e49fd8ff5 100644 --- a/src/ui/regionalFeatures/regionalFeature.service.ts +++ b/src/ui/regionalFeatures/regionalFeature.service.ts @@ -2,7 +2,7 @@ import { HttpClient } from "@angular/common/http"; import { Injectable, OnDestroy } from "@angular/core"; import { PureContantService } from "src/util"; import { getIdFromFullId } from 'common/util' -import { forkJoin, Subscription } from "rxjs"; +import { forkJoin, Subject, Subscription } from "rxjs"; import { switchMap } from "rxjs/operators"; import { IHasId } from "src/util/interfaces"; import { select, Store } from "@ngrx/store"; @@ -119,4 +119,6 @@ export class RegionalFeaturesService implements OnDestroy{ }) ) } + + showDatafeatureInfo$ = new Subject<{ fullId: string } | { name: string, description: string }>() } diff --git a/src/ui/regionalFeatures/regionalFeaturesCmp/regionalFeaturesCmp.component.ts b/src/ui/regionalFeatures/regionalFeaturesCmp/regionalFeaturesCmp.component.ts index 2cf5ec0e7..b5da906cb 100644 --- a/src/ui/regionalFeatures/regionalFeaturesCmp/regionalFeaturesCmp.component.ts +++ b/src/ui/regionalFeatures/regionalFeaturesCmp/regionalFeaturesCmp.component.ts @@ -1,4 +1,8 @@ -import { Component, OnChanges, SimpleChanges } from "@angular/core"; +import { Component, OnChanges, OnDestroy, SimpleChanges, ViewChild } from "@angular/core"; +import { MatSidenav } from "@angular/material/sidenav"; +import { Observable, Subscription } from "rxjs"; +import { shareReplay } from "rxjs/operators"; +import { OVERWRITE_SHOW_DATASET_DIALOG_TOKEN, TOverwriteShowDatasetDialog } from "src/util/interfaces"; import { RegionalFeaturesService } from "../regionalFeature.service"; import { RegionFeatureBase } from "../regionFeature.base"; @@ -8,21 +12,52 @@ import { RegionFeatureBase } from "../regionFeature.base"; styleUrls: [ './regionalFeaturesCmp.style.css' ], + providers: [ + { + provide: OVERWRITE_SHOW_DATASET_DIALOG_TOKEN, + useFactory: (regionalFeatureService: RegionalFeaturesService) => { + return function overwriteShowDatasetDialog( arg ){ + regionalFeatureService.showDatafeatureInfo$.next(arg) + } as TOverwriteShowDatasetDialog + }, + deps: [ + RegionalFeaturesService + ] + } + ] }) -export class RegionalFeaturesCmp extends RegionFeatureBase implements OnChanges{ +export class RegionalFeaturesCmp extends RegionFeatureBase implements OnDestroy, OnChanges{ - ngOnChanges(changes: SimpleChanges){ - super.ngOnChanges(changes) - } + @ViewChild('sideNav', { read: MatSidenav }) + private sideNav: MatSidenav constructor( regionalFeatureService: RegionalFeaturesService ){ super(regionalFeatureService) + this.showDatafeatureInfo$ = regionalFeatureService.showDatafeatureInfo$.pipe( + shareReplay(1) + ) + this.subscription.push( + this.showDatafeatureInfo$.subscribe( + () => this.sideNav.open() + ) + ) + } + + ngOnChanges(changes: SimpleChanges){ + super.ngOnChanges(changes) } + private subscription: Subscription[] = [] + ngOnDestroy(){ + while (this.subscription.length > 0) this.subscription.pop().unsubscribe() + } + + public showingRegionFeatureId: string public showingRegionFeatureIsLoading = false + public showDatafeatureInfo$: Observable<{fullId: string}|{name: string, description: string}> } diff --git a/src/ui/regionalFeatures/regionalFeaturesCmp/regionalFeaturesCmp.template.html b/src/ui/regionalFeatures/regionalFeaturesCmp/regionalFeaturesCmp.template.html index 0cac5e159..7f7d5772a 100644 --- a/src/ui/regionalFeatures/regionalFeaturesCmp/regionalFeaturesCmp.template.html +++ b/src/ui/regionalFeatures/regionalFeaturesCmp/regionalFeaturesCmp.template.html @@ -1,76 +1,54 @@ -<mat-tab-group *ngIf="!isLoading; else loadingTmpl"> - <mat-tab *ngFor="let featureType of features | mapToProperty : 'type' | getUniquePipe"> - <ng-template mat-tab-label> - {{ featureType }} - </ng-template> - - <!-- lazy loading feature content --> - <ng-template matTabContent> - - <!-- selector --> - <div> - <ng-container *ngTemplateOutlet="selectorTmpl; context: { - label: 'Dataset', - options: features | filterRegionalFeaturesBytype : featureType - }"> - </ng-container> - - </div> - - <!-- content --> - <ng-template [ngIf]="showingRegionFeatureId"> - <ng-container *ngTemplateOutlet="featureContentTmpl; context: { - region: region, - feature: features | findRegionFeaturebyId : showingRegionFeatureId - }"> - </ng-container> - </ng-template> - </ng-template> - </mat-tab> - - <!-- transcluded content --> - <mat-tab> - <ng-template mat-tab-label> - Other - </ng-template> - - <!-- lazy loading transcluded content --> - <ng-template matTabContent> - <ng-content></ng-content> - </ng-template> - </mat-tab> - -</mat-tab-group> - -<!-- feature selector --> -<ng-template #selectorTmpl - let-label="label" - let-options="options"> - - <mat-form-field> - <mat-label> - {{ label }} - </mat-label> - <mat-select [(value)]="showingRegionFeatureId"> - <mat-option *ngFor="let option of options" - [value]="option['@id']"> - {{ option.name }} - </mat-option> - </mat-select> - </mat-form-field> -</ng-template> - -<!-- feature content template --> -<ng-template #featureContentTmpl - let-region="region" - let-feature="feature"> - <feature-explorer - [region]="region" - [feature]="feature"> - </feature-explorer> -</ng-template> - -<!-- loading tmpl --> -<ng-template #loadingTmpl> - <div class="spinnerAnimationCircle"></div> -</ng-template> +<mat-sidenav-container> + <mat-sidenav #sideNav class="w-100"> + <button mat-button + (click)="sideNav.close()"> + <i class="fas fa-chevron-left"></i> + <span> + Back + </span> + </button> + + <!-- TODO fix single dataset view bug + does not change render content unless rerender --> + <mat-tab-group *ngIf="sideNav.opened"> + + <mat-tab> + <ng-template mat-tab-label> + Overview + </ng-template> + + <ng-template matTabContent> + <single-dataset-view *ngIf="showDatafeatureInfo$ | async as featureEl" + class="m-2 d-inline-block" + [fullId]="featureEl.fullId" + [name]="featureEl.name" + [description]="featureEl.description" + [useSmallIcon]="true"> + </single-dataset-view> + </ng-template> + </mat-tab> + + <mat-tab *ngFor="let feature of features"> + <ng-template mat-tab-label> + {{ feature.type }} + </ng-template> + <ng-template matTabContent> + <feature-explorer + [region]="region" + [feature]="feature"> + </feature-explorer> + </ng-template> + </mat-tab> + </mat-tab-group> + + </mat-sidenav> + + <mat-sidenav-content> + <mat-card class="p-0"> + <data-browser + [disableVirtualScroll]="true" + [regions]="[region]"> + </data-browser> + </mat-card> + </mat-sidenav-content> +</mat-sidenav-container> diff --git a/src/util/interfaces.ts b/src/util/interfaces.ts index d6c414718..4c7613093 100644 --- a/src/util/interfaces.ts +++ b/src/util/interfaces.ts @@ -2,6 +2,13 @@ * Only the most common interfaces should reside here */ +import { InjectionToken } from "@angular/core" + export interface IHasId{ ['@id']: string } + + +export type TOverwriteShowDatasetDialog = (dataset: { fullId: string } | { name: string, description: string }) => void + +export const OVERWRITE_SHOW_DATASET_DIALOG_TOKEN = new InjectionToken<TOverwriteShowDatasetDialog>('OVERWRITE_SHOW_DATASET_DIALOG_TOKEN') -- GitLab