diff --git a/src/components/fabSpeedDial/fabSpeedDial.module.ts b/src/components/fabSpeedDial/fabSpeedDial.module.ts new file mode 100644 index 0000000000000000000000000000000000000000..4800d906fd216aea4518d40f7a77eaebda5e5944 --- /dev/null +++ b/src/components/fabSpeedDial/fabSpeedDial.module.ts @@ -0,0 +1,23 @@ +import { NgModule } from "@angular/core"; +import { FabSpeedDialChild } from "./fabSpeedDialChild.directive"; +import { FabSpeedDialContainer } from "./fabSpeedDialContainer.directive"; +import { FabSpeedDialTrigger } from "./fabSpeedDialTrigger.directive"; +import { CommonModule } from "@angular/common"; + +@NgModule({ + imports: [ + CommonModule + ], + declarations: [ + FabSpeedDialChild, + FabSpeedDialContainer, + FabSpeedDialTrigger + ], + exports: [ + FabSpeedDialChild, + FabSpeedDialContainer, + FabSpeedDialTrigger + ] +}) + +export class FabSpeedDialModule{} diff --git a/src/components/fabSpeedDial/fabSpeedDial.service.ts b/src/components/fabSpeedDial/fabSpeedDial.service.ts new file mode 100644 index 0000000000000000000000000000000000000000..6c7d8b69c54dd35a441c26cbb3ca5fb3dbb2809d --- /dev/null +++ b/src/components/fabSpeedDial/fabSpeedDial.service.ts @@ -0,0 +1,43 @@ +import { Injectable, OnDestroy } from "@angular/core"; +import { BehaviorSubject, Subscription } from "rxjs"; + +export enum SCALE_ORIGIN { + LEFT = 'left', + RIGHT = 'right', + TOP = 'top', + BOTTOM = 'bottom', + CENTER = 'center', +} + +@Injectable() +export class FabSpeedDialService implements OnDestroy{ + public openState$ = new BehaviorSubject(false) + public scaleOrigin$: BehaviorSubject<SCALE_ORIGIN> = new BehaviorSubject(SCALE_ORIGIN.CENTER) + private s: Subscription[] = [] + + public isOpen: boolean = false + + constructor(){ + this.s.push( + this.openState$.subscribe(flag => this.isOpen = flag) + ) + } + + ngOnDestroy(){ + while(this.s.length > 0) { + this.s.pop().unsubscribe() + } + } + + toggle(){ + this.openState$.next(!this.isOpen) + } + + close(){ + this.openState$.next(false) + } + + open(){ + this.openState$.next(true) + } +} \ No newline at end of file diff --git a/src/components/fabSpeedDial/fabSpeedDialChild.directive.ts b/src/components/fabSpeedDial/fabSpeedDialChild.directive.ts new file mode 100644 index 0000000000000000000000000000000000000000..e668558243597abb140382965d5eaca659fa8386 --- /dev/null +++ b/src/components/fabSpeedDial/fabSpeedDialChild.directive.ts @@ -0,0 +1,62 @@ +import { Directive, Input, HostBinding, OnDestroy, OnChanges } from "@angular/core"; +import { Subscription } from "rxjs"; +import { FabSpeedDialService } from "./fabSpeedDial.service"; + +const NORMAL = 150 +const MAX_CHANGE = 70 +const INDEX_FACTOR = 5 + +@Directive({ + selector: '[iav-fab-speed-dial-child]', + exportAs: 'iavFabSpeedDialChild' +}) + +export class FabSpeedDialChild implements OnDestroy, OnChanges{ + + private s: Subscription[] = [] + + constructor(private fabService: FabSpeedDialService){ + this.s.push( + this.fabService.openState$.subscribe(flag => { + this.quickDialOpening(flag) + }), + this.fabService.scaleOrigin$.subscribe(origin => { + this.transformOrigin = origin + }) + ) + } + ngOnDestroy(){ + while(this.s.length > 0) { + this.s.pop().unsubscribe() + } + } + + @Input('iav-fab-speed-dial-child-index') + public index: number = 0 + + ngOnChanges(){ + + // const transitionTimer = this.fabService.isOpen ? (NORMAL + delta) : (NORMAL - delta ) + // console.log(this.index, delta, transitionTimer) + // this.transitionProp = `all ${transitionTimer}ms ${this.fabService.isOpen ? 'ease-in' : 'ease-out'}` + } + + quickDialOpening(flag){ + const _index = Number(this.index) + const delta = Math.atan(_index * INDEX_FACTOR) / (Math.PI / 2) * MAX_CHANGE + this.transitionDelay = flag ? `${delta}ms` : `${MAX_CHANGE - delta}ms` + this.transformProp = `scale(${flag ? 1 : 0})` + } + + @HostBinding('style.transitionDelay') + transitionDelay = '0' + + @HostBinding('style.transition') + transitionProp = `all ${NORMAL}ms linear` + + @HostBinding('style.transform') + transformProp = `scale(0)` + + @HostBinding('style.transformOrigin') + transformOrigin = 'center' +} diff --git a/src/components/fabSpeedDial/fabSpeedDialContainer.directive.ts b/src/components/fabSpeedDial/fabSpeedDialContainer.directive.ts new file mode 100644 index 0000000000000000000000000000000000000000..5f3be7d09c8b466b561e3805e658067b6570e548 --- /dev/null +++ b/src/components/fabSpeedDial/fabSpeedDialContainer.directive.ts @@ -0,0 +1,66 @@ +import { Directive, OnDestroy, Output, EventEmitter, Input, OnChanges, SimpleChanges, HostListener, ElementRef } from "@angular/core"; +import { FabSpeedDialService } from "./fabSpeedDial.service"; +import { Subscription } from "rxjs"; +import { SCALE_ORIGIN } from './fabSpeedDial.service' + +@Directive({ + selector: '[iav-fab-spped-dial-container]', + exportAs: 'iavFabSpeedDialContainer', + providers: [ + FabSpeedDialService + ] +}) + +export class FabSpeedDialContainer implements OnDestroy, OnChanges{ + + private s: Subscription[] = [] + + public isOpen = false + + @Input('iav-fab-speed-dial-scale-origin') + scaleOrigin = 'center' + + private validOriginValues = Object.keys(SCALE_ORIGIN).map(key => SCALE_ORIGIN[key]) + + @Output() + openStateChanged: EventEmitter<boolean> = new EventEmitter() + + constructor( + private fabSDService: FabSpeedDialService, + private el: ElementRef + ){ + + this.s.push( + this.fabSDService.openState$.subscribe(flag => { + this.isOpen = flag + this.openStateChanged.emit(this.isOpen) + }) + ) + } + + ngOnChanges(changes: SimpleChanges) { + if (this.validOriginValues.includes(changes.scaleOrigin.currentValue)) { + this.fabSDService.scaleOrigin$.next( + changes.scaleOrigin.currentValue + ) + } + } + + ngOnDestroy(){ + while(this.s.length > 0){ + this.s.pop().unsubscribe() + } + } + + toggle(){ + this.fabSDService.toggle() + } + + close(){ + this.fabSDService.close() + } + + open(){ + this.fabSDService.open() + } +} diff --git a/src/components/fabSpeedDial/fabSpeedDialTrigger.directive.ts b/src/components/fabSpeedDial/fabSpeedDialTrigger.directive.ts new file mode 100644 index 0000000000000000000000000000000000000000..0fcaf50f872ed006cf205de677bb65548808b340 --- /dev/null +++ b/src/components/fabSpeedDial/fabSpeedDialTrigger.directive.ts @@ -0,0 +1,17 @@ +import { Directive, Input, HostListener } from "@angular/core"; +import { FabSpeedDialService } from "./fabSpeedDial.service"; + +@Directive({ + selector: '[iav-fab-speed-dial-trigger]', + exportAs: 'iavFabSpeedDialTrigger' +}) + +export class FabSpeedDialTrigger{ + + constructor(private fabService: FabSpeedDialService ){} + + @HostListener('click') + triggerClicked(){ + this.fabService.toggle() + } +} diff --git a/src/components/fabSpeedDial/index.ts b/src/components/fabSpeedDial/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..7ef7972478b4669d36793f9c3d66e8a98c3209ba --- /dev/null +++ b/src/components/fabSpeedDial/index.ts @@ -0,0 +1,4 @@ +export { FabSpeedDialModule } from './fabSpeedDial.module' +export { FabSpeedDialChild } from './fabSpeedDialChild.directive' +export { FabSpeedDialContainer } from './fabSpeedDialContainer.directive' +export { FabSpeedDialTrigger } from './fabSpeedDialTrigger.directive' diff --git a/src/res/css/extra_styles.css b/src/res/css/extra_styles.css index fe7d4d4e320b24ee0149726a15a29781d31d646b..e2f9b3b5685fb32f7d90f768427095b3f9aee649 100644 --- a/src/res/css/extra_styles.css +++ b/src/res/css/extra_styles.css @@ -699,6 +699,11 @@ kg-dataset-previewer > img margin-right: -1rem!important; } +.mr-8 +{ + margin-right: 4rem!important; +} + .mat-card-sm > mat-dialog-container { padding-left: 16px!important; diff --git a/src/res/ext/colin.json b/src/res/ext/colin.json index d80629a3b3dedf68cf0958d94ce55353ac0cbde0..85fa1d0ae6787f34f79ce8e0369f16eb27611e65 100644 --- a/src/res/ext/colin.json +++ b/src/res/ext/colin.json @@ -3176,6 +3176,13 @@ 132, 147 ], + "originDatasets": [ + { + "kgSchema": "minds/core/dataset/v1.0.0", + "kgId": "5c669b77-c981-424a-858d-fe9f527dbc07", + "filename": "Area hOc1 (V1, 17, CalcS) [v2.4, Colin 27, left hemisphere]" + } + ], "labelIndex": 8, "ngId": "jubrain colin v18 left", "children": [], @@ -3193,6 +3200,13 @@ 132, 147 ], + "originDatasets": [ + { + "kgSchema": "minds/core/dataset/v1.0.0", + "kgId": "5c669b77-c981-424a-858d-fe9f527dbc07", + "filename": "Area hOc1 (V1, 17, CalcS) [v2.4, Colin 27, right hemisphere]" + } + ], "ngId": "jubrain colin v18 right", "labelIndex": 8, "children": [], diff --git a/src/ui/databrowserModule/databrowser.module.ts b/src/ui/databrowserModule/databrowser.module.ts index c0dd1fd10c3ba6bb94dd8bb61426c9fc09472407..a364832671cbdf3909c06bdd0161069744c7882d 100644 --- a/src/ui/databrowserModule/databrowser.module.ts +++ b/src/ui/databrowserModule/databrowser.module.ts @@ -28,6 +28,8 @@ import { PreviewFileVisibleInSelectedReferenceTemplatePipe } from "./util/previe import { DatasetPreviewList, UnavailableTooltip } from "./singleDataset/datasetPreviews/datasetPreviewsList/datasetPreviewList.component"; import { PreviewComponentWrapper } from "./preview/previewComponentWrapper/previewCW.component"; import { BulkDownloadBtn, TransformDatasetToIdPipe } from "./bulkDownload/bulkDownloadBtn.component"; +import { ShowDatasetDialogDirective } from "./showDatasetDialog.directive"; +import { PreviewDatasetFile } from "./singleDataset/datasetPreviews/previewDatasetFile.directive"; @NgModule({ imports: [ @@ -47,6 +49,12 @@ import { BulkDownloadBtn, TransformDatasetToIdPipe } from "./bulkDownload/bulkDo PreviewComponentWrapper, BulkDownloadBtn, + /** + * Directives + */ + ShowDatasetDialogDirective, + PreviewDatasetFile, + /** * pipes */ @@ -76,6 +84,8 @@ import { BulkDownloadBtn, TransformDatasetToIdPipe } from "./bulkDownload/bulkDo GetKgSchemaIdFromFullIdPipe, BulkDownloadBtn, TransformDatasetToIdPipe, + ShowDatasetDialogDirective, + PreviewDatasetFile, ], entryComponents: [ DataBrowser, diff --git a/src/ui/databrowserModule/databrowser.useEffect.ts b/src/ui/databrowserModule/databrowser.useEffect.ts index add27a5bf5e2947a0ebc7bb25764cd7d20bcd6e8..b760603e5a4e349b4138c4f0b49de52589869c9b 100644 --- a/src/ui/databrowserModule/databrowser.useEffect.ts +++ b/src/ui/databrowserModule/databrowser.useEffect.ts @@ -2,7 +2,7 @@ import { Injectable, OnDestroy } from "@angular/core"; import { Actions, Effect, ofType } from "@ngrx/effects"; import { select, Store } from "@ngrx/store"; import { from, merge, Observable, of, Subscription, forkJoin, combineLatest } from "rxjs"; -import { filter, map, scan, switchMap, withLatestFrom, mapTo, shareReplay, startWith, distinctUntilChanged } from "rxjs/operators"; +import { filter, map, scan, switchMap, withLatestFrom, mapTo, shareReplay, startWith, distinctUntilChanged, concatMap } from "rxjs/operators"; import { LoggingService } from "src/logging"; import { DATASETS_ACTIONS_TYPES, IDataEntry, ViewerPreviewFile } from "src/services/state/dataStore.store"; import { IavRootStoreInterface, ADD_NG_LAYER, CHANGE_NAVIGATION } from "src/services/stateStore.service"; @@ -55,6 +55,8 @@ export class DataBrowserUseEffect implements OnDestroy { private http: HttpClient ) { + // TODO this is almost definitely wrong + // possibily causing https://github.com/HumanBrainProject/interactive-viewer/issues/502 this.subscriptions.push( this.store$.pipe( select('dataStore'), @@ -129,12 +131,24 @@ export class DataBrowserUseEffect implements OnDestroy { this.previewDatasetFile$ = actions$.pipe( ofType(DATASETS_ACTIONS_TYPES.PREVIEW_DATASET), - map(actionBody => { + concatMap(actionBody => { + const { payload = {} } = actionBody as any - const { file = null } = payload as { file: ViewerPreviewFile, dataset: IDataEntry } - return file + const { file = null, dataset } = payload as { file: ViewerPreviewFile, dataset: IDataEntry } + const { fullId } = dataset + + const { filename, ...rest } = file + if (Object.keys(rest).length === 0) { + const re = /\/([a-f0-9-]+)$/.exec(fullId) + if (!re) return of(null) + const url = `${DATASET_PREVIEW_URL}/${re[0]}/${encodeURIComponent(filename)}` + return this.http.get<ViewerPreviewFile>(url) + } else { + return of(file) + } }), - shareReplay(1) + shareReplay(1), + distinctUntilChanged() ) this.navigateToPreviewPosition$ = this.previewDatasetFile$.pipe( diff --git a/src/ui/databrowserModule/kgSingleDatasetService.service.ts b/src/ui/databrowserModule/kgSingleDatasetService.service.ts index 82d26d4089f0cb200e8925243a36d9bd086e13b2..d6180e3e4853f7a973af78a2fd464cefd5474446 100644 --- a/src/ui/databrowserModule/kgSingleDatasetService.service.ts +++ b/src/ui/databrowserModule/kgSingleDatasetService.service.ts @@ -7,7 +7,6 @@ import { AtlasViewerConstantsServices } from "src/atlasViewer/atlasViewer.consta import { IDataEntry, ViewerPreviewFile, DATASETS_ACTIONS_TYPES } from "src/services/state/dataStore.store"; import { SHOW_BOTTOM_SHEET } from "src/services/state/uiState.store"; import { IavRootStoreInterface, REMOVE_NG_LAYER } from "src/services/stateStore.service"; -import { GetKgSchemaIdFromFullIdPipe } from "./util/getKgSchemaIdFromFullId.pipe"; @Injectable({ providedIn: 'root' }) export class KgSingleDatasetService implements OnDestroy { @@ -76,7 +75,7 @@ export class KgSingleDatasetService implements OnDestroy { }) } - public previewFile(file: ViewerPreviewFile, dataset: IDataEntry) { + public previewFile(file: Partial<ViewerPreviewFile>, dataset: Partial<IDataEntry>) { this.store$.dispatch({ type: DATASETS_ACTIONS_TYPES.PREVIEW_DATASET, payload: { diff --git a/src/ui/databrowserModule/showDatasetDialog.directive.ts b/src/ui/databrowserModule/showDatasetDialog.directive.ts new file mode 100644 index 0000000000000000000000000000000000000000..cb5ee84b7282dae8f293aeff158e2b9d5442fa32 --- /dev/null +++ b/src/ui/databrowserModule/showDatasetDialog.directive.ts @@ -0,0 +1,43 @@ +import { Directive, Input, HostListener } from "@angular/core"; +import { MatDialog } from "@angular/material/dialog"; +import { MatSnackBar } from "@angular/material/snack-bar"; +import { SingleDatasetView } from "./singleDataset/detailedView/singleDataset.component"; + +@Directive({ + selector: '[iav-dataset-show-dataset-dialog]', + exportAs: 'iavDatasetShowDatasetDialog' +}) + +export class ShowDatasetDialogDirective{ + + @Input() + kgSchema: string = 'minds/core/dataset/v1.0.0' + + @Input('iav-dataset-show-dataset-dialog-kgid') + kgId: string + + @Input('iav-dataset-show-dataset-dialog-fullid') + fullId: string + + constructor( + private matDialog: MatDialog, + private snackbar: MatSnackBar, + ){ } + + @HostListener('click') + onClick(){ + + if (this.fullId || (this.kgSchema && this.kgId)) { + + this.matDialog.open(SingleDatasetView, { + autoFocus: false, + data: { + fullId: this.fullId || `${this.kgSchema}/${this.kgId}` + } + }) + + } else { + this.snackbar.open(`Cannot show dataset. Neither fullId nor kgId provided.`) + } + } +} diff --git a/src/ui/databrowserModule/singleDataset/datasetPreviews/datasetPreviewsList/datasetPreviewList.component.ts b/src/ui/databrowserModule/singleDataset/datasetPreviews/datasetPreviewsList/datasetPreviewList.component.ts index 7fcdb4ae1b2419b9e664f476654973ee0ffeae9b..fd98090e22bda2d0a23da2a1bb3d66006329d723 100644 --- a/src/ui/databrowserModule/singleDataset/datasetPreviews/datasetPreviewsList/datasetPreviewList.component.ts +++ b/src/ui/databrowserModule/singleDataset/datasetPreviews/datasetPreviewsList/datasetPreviewList.component.ts @@ -12,8 +12,6 @@ import { DS_PREVIEW_URL } from 'src/util/constants' export class DatasetPreviewList{ - @Output() public previewingFile: EventEmitter<ViewerPreviewFile> = new EventEmitter() - public datasetPreviewList: any[] = [] public loadingDatasetPreviewList: boolean = false public selectedTemplateSpace$: Observable<any> @@ -42,10 +40,6 @@ export class DatasetPreviewList{ this.cdr.markForCheck() } - public handlePreviewFile(file: ViewerPreviewFile) { - - this.previewingFile.emit(file) - } } @Pipe({ diff --git a/src/ui/databrowserModule/singleDataset/datasetPreviews/datasetPreviewsList/datasetPreviewList.template.html b/src/ui/databrowserModule/singleDataset/datasetPreviews/datasetPreviewsList/datasetPreviewList.template.html index 25afd1d18921e98c59b6f1c41ecd2449cfeafc6d..3859c93453d7782a3affafbdf80d0cfb872a226e 100644 --- a/src/ui/databrowserModule/singleDataset/datasetPreviews/datasetPreviewsList/datasetPreviewList.template.html +++ b/src/ui/databrowserModule/singleDataset/datasetPreviews/datasetPreviewsList/datasetPreviewList.template.html @@ -16,8 +16,12 @@ <ng-container *ngFor="let file of datasetPreviewList"> <!-- preview available --> - <ng-template [ngIf]="(selectedTemplateSpace$ | async | previewFileVisibleInSelectedReferenceTemplatePipe : file)" [ngIfElse]="notAvailalbePreview" > - <mat-list-item (click)=" handlePreviewFile(file)"> + <ng-template [ngIf]="(selectedTemplateSpace$ | async | previewFileVisibleInSelectedReferenceTemplatePipe : file)" + [ngIfElse]="notAvailalbePreview" > + <mat-list-item + [iav-dataset-preview-dataset-file-kgid]="kgId" + [iav-dataset-preview-dataset-file]="file"> + <mat-icon [fontSet]="(file | previewFileIconPipe).fontSet" [fontIcon]="(file | previewFileIconPipe).fontIcon" diff --git a/src/ui/databrowserModule/singleDataset/datasetPreviews/previewDatasetFile.directive.ts b/src/ui/databrowserModule/singleDataset/datasetPreviews/previewDatasetFile.directive.ts new file mode 100644 index 0000000000000000000000000000000000000000..784f68658ffa24f12f2c50f09d78bdd87e793a25 --- /dev/null +++ b/src/ui/databrowserModule/singleDataset/datasetPreviews/previewDatasetFile.directive.ts @@ -0,0 +1,61 @@ +import { Directive, Input, HostListener } from "@angular/core"; +import { KgSingleDatasetService } from "../singleDataset.base"; +import { MatSnackBar } from "@angular/material/snack-bar"; +import { ViewerPreviewFile, IDataEntry } from 'src/services/state/dataStore.store' + +@Directive({ + selector: '[iav-dataset-preview-dataset-file]', + exportAs: 'iavDatasetPreviewDatasetFile' +}) + +export class PreviewDatasetFile{ + @Input('iav-dataset-preview-dataset-file') + file: ViewerPreviewFile + + @Input('iav-dataset-preview-dataset-file-filename') + filename: string + + @Input('iav-dataset-preview-dataset-file-dataset') + dataset: IDataEntry + + @Input('iav-dataset-preview-dataset-file-kgid') + kgId: string + + @Input('iav-dataset-preview-dataset-file-kgschema') + kgSchema: string = `minds/core/dataset/v1.0.0` + + @Input('iav-dataset-preview-dataset-file-fullid') + fullId: string + + constructor( + private kgSingleDSService: KgSingleDatasetService, + private snackbar: MatSnackBar, + ){ + } + + private getFile(): Partial<ViewerPreviewFile>{ + if (!this.file && !this.filename) { + return null + } + return this.file || { + filename: this.filename + } + } + + private getDataset(): Partial<IDataEntry>{ + return this.dataset || { + fullId: this.fullId || `${this.kgSchema}/${this.kgId}` + } + } + + @HostListener('click') + onClick(){ + const file = this.getFile() + const dataset = this.getDataset() + if (!file) { + this.snackbar.open(`Cannot preview dataset file. Neither file nor filename are defined.`) + return + } + this.kgSingleDSService.previewFile(file, dataset) + } +} diff --git a/src/ui/databrowserModule/singleDataset/detailedView/singleDataset.template.html b/src/ui/databrowserModule/singleDataset/detailedView/singleDataset.template.html index 993958bec3737b6311782c62075a22da6c962afc..88dc0002bf7cdb6a11837f944a623cdb12115b75 100644 --- a/src/ui/databrowserModule/singleDataset/detailedView/singleDataset.template.html +++ b/src/ui/databrowserModule/singleDataset/detailedView/singleDataset.template.html @@ -131,8 +131,7 @@ <ng-template #previewFilesListTemplate> <dataset-preview-list - [kgId]="kgId" - (previewingFile)="handlePreviewFile($event)"> + [kgId]="kgId"> </dataset-preview-list> </ng-template> diff --git a/src/ui/databrowserModule/singleDataset/listView/singleDatasetListView.component.ts b/src/ui/databrowserModule/singleDataset/listView/singleDatasetListView.component.ts index be8e778626e3f6d81aaa408e0a6c1c4b0a1e94e8..664c3a1deea97e18e9075fda54f77243bae9fb3d 100644 --- a/src/ui/databrowserModule/singleDataset/listView/singleDatasetListView.component.ts +++ b/src/ui/databrowserModule/singleDataset/listView/singleDatasetListView.component.ts @@ -3,8 +3,6 @@ import { Component, ChangeDetectionStrategy, ChangeDetectorRef } from "@angular/ DatabrowserService, KgSingleDatasetService, } from "../singleDataset.base"; -import { SingleDatasetView } from "../detailedView/singleDataset.component"; -import {MatDialog} from "@angular/material/dialog"; import {MatSnackBar} from "@angular/material/snack-bar"; @Component({ @@ -22,18 +20,8 @@ export class SingleDatasetListView extends SingleDatasetBase { _dbService: DatabrowserService, singleDatasetService: KgSingleDatasetService, cdr: ChangeDetectorRef, - private dialog: MatDialog, snackBar: MatSnackBar, ) { super(_dbService, singleDatasetService, cdr, snackBar) } - - public showDetailInfo() { - this.dialog.open(SingleDatasetView, { - autoFocus: false, - data: { - fullId: this.fullId - }, - }) - } } diff --git a/src/ui/databrowserModule/singleDataset/listView/singleDatasetListView.template.html b/src/ui/databrowserModule/singleDataset/listView/singleDatasetListView.template.html index b8256849438468a91c3b92ff37ffaca0a81faae0..445d21d5f44aca22f69d34ec1597fd4ed6777d38 100644 --- a/src/ui/databrowserModule/singleDataset/listView/singleDatasetListView.template.html +++ b/src/ui/databrowserModule/singleDataset/listView/singleDatasetListView.template.html @@ -1,5 +1,6 @@ <div mat-ripple - (click)="ripple ? showDetailInfo() : null" + iav-dataset-show-dataset-dialog + [iav-dataset-show-dataset-dialog-fullid]="fullId" [iav-stop]="ripple ? 'click' : null" [matRippleDisabled]="!ripple" class="d-flex flex-row flex-nowrap align-items-center h-100"> @@ -66,7 +67,8 @@ <button mat-menu-item *ngIf="true" class="no-focus" - (click)="showDetailInfo()"> + [iav-dataset-show-dataset-dialog-fullid]="fullId" + iav-dataset-show-dataset-dialog> <mat-icon fontSet="fas" fontIcon="fa-info"></mat-icon> Detail </button> @@ -108,8 +110,7 @@ <ng-template #previewFilesListTemplate> <dataset-preview-list - [kgId]="kgId" - (previewingFile)="handlePreviewFile($event)"> + [kgId]="kgId"> </dataset-preview-list> </ng-template> diff --git a/src/ui/databrowserModule/singleDataset/singleDataset.base.ts b/src/ui/databrowserModule/singleDataset/singleDataset.base.ts index dfb7f94a34a6bb746d7049611aae6bdd31c67e92..0caa4d51c49573510fe0a937b730145647b031f4 100644 --- a/src/ui/databrowserModule/singleDataset/singleDataset.base.ts +++ b/src/ui/databrowserModule/singleDataset/singleDataset.base.ts @@ -212,10 +212,6 @@ export class SingleDatasetBase implements OnInit, OnChanges { this.singleDatasetService.showPreviewList(templateRef) } - public handlePreviewFile(file: ViewerPreviewFile) { - this.singleDatasetService.previewFile(file, this.dataset) - } - public undoableRemoveFav() { this.snackBar.open(`Unpinned dataset: ${this.name}`, 'Undo', { duration: 5000, diff --git a/src/ui/parcellationRegion/regionMenu/regionMenu.style.css b/src/ui/parcellationRegion/regionMenu/regionMenu.style.css index e9a48a372933a13d21d572ccdc4d8cbf1120fa6b..c56194bd3b83a1f354678b862eb6f669c727a0ac 100644 --- a/src/ui/parcellationRegion/regionMenu/regionMenu.style.css +++ b/src/ui/parcellationRegion/regionMenu/regionMenu.style.css @@ -1,10 +1,6 @@ -.regionDescriptionTextClass +.speed-dial { - max-height:100px; - transition: max-height 0.15s ease-out; -} -.regionDescriptionTextOpened -{ - max-height: 310px; - transition: max-height 0.25s ease-in; -} + right: 0; + bottom: 0; + height: 0; +} \ No newline at end of file diff --git a/src/ui/parcellationRegion/regionMenu/regionMenu.template.html b/src/ui/parcellationRegion/regionMenu/regionMenu.template.html index b078e810dfbe977a5427559bcd29ba9391715333..e7ea153fc7ca64dacf4c69247f0557a0e6ce61fc 100644 --- a/src/ui/parcellationRegion/regionMenu/regionMenu.template.html +++ b/src/ui/parcellationRegion/regionMenu/regionMenu.template.html @@ -1,6 +1,66 @@ <mat-card> <mat-card-title> - {{ region.name }} + <div class="position-relative"> + <span class="mr-8"> + {{ region.name }} + </span> + + <!-- only show speed dial if originDatasets exists and are greater than 0 --> + <div *ngIf="((region && region.originDatasets) || []).length > 0" + class="speed-dial position-absolute d-flex flex-row-reverse align-items-center" + (iav-onclick-outside)="fab.close()" + #fab="iavFabSpeedDialContainer" + iav-fab-speed-dial-scale-origin="right" + iav-fab-spped-dial-container> + + <!-- main btn --> + <button mat-fab class="ml-3" + iav-fab-speed-dial-trigger + [color]="fab.isOpen ? 'default' : 'accent'"> + + <!-- TODO add test to ensure this does not happen --> + <!-- nb pe-none is essential for iav-onclick-outside directive to work properly --> + <ng-container *ngIf="fab.isOpen; else closedTmpl"> + <i class="pe-none fas fa-times"></i> + </ng-container> + <ng-template #closedTmpl> + <i class="pe-none fas fa-ellipsis-v"></i> + </ng-template> + </button> + + <!-- action btns --> + <!-- originDatasets --> + <ng-container *ngFor="let originDataset of (region.originDatasets || []); let index = index"> + + <!-- preview file --> + <button *ngIf="originDataset.kgSchema && originDataset.kgId && originDataset.filename" + mat-mini-fab + iav-dataset-preview-dataset-file + [iav-dataset-preview-dataset-file-kgid]="originDataset.kgId" + [iav-dataset-preview-dataset-file-filename]="originDataset.filename" + class="m-1" + [iav-fab-speed-dial-child-index]="index * 2" + matTooltip="Preview file" + iav-fab-speed-dial-child> + <i class="far fa-eye"></i> + </button> + + <!-- show dataset --> + <button *ngIf="originDataset.kgSchema && originDataset.kgId" + mat-mini-fab + class="m-1" + (click)="fab.close()" + matTooltip="More detail on dataset" + [iav-dataset-show-dataset-dialog-kgid]="originDataset.kgId" + iav-dataset-show-dataset-dialog + [iav-fab-speed-dial-child-index]="index * 2 + 1" + iav-fab-speed-dial-child> + <i class="fas fa-info"></i> + </button> + </ng-container> + + </div> + </div> </mat-card-title> <mat-card-subtitle> <i class="fas fa-brain"></i> diff --git a/src/ui/ui.module.ts b/src/ui/ui.module.ts index 3b57eea7d5521bd184a142ec66ad4edc7d7a0f39..86a9375fb6550bb74d0fb1db4557dc29bb2a0e27 100644 --- a/src/ui/ui.module.ts +++ b/src/ui/ui.module.ts @@ -84,6 +84,7 @@ import { LayerDetailComponent } from "./layerbrowser/layerDetail/layerDetail.com import { ShareModule } from "src/share"; import { StateModule } from "src/state"; import { AuthModule } from "src/auth"; +import { FabSpeedDialModule } from "src/components/fabSpeedDial"; @NgModule({ imports : [ @@ -100,6 +101,7 @@ import { AuthModule } from "src/auth"; ShareModule, StateModule, AuthModule, + FabSpeedDialModule, ], declarations : [ NehubaContainer, diff --git a/src/util/directives/elementOutClick.directive.ts b/src/util/directives/elementOutClick.directive.ts index a690c8d6524aff6e43154f2078a90a384afb160f..93f3b1d64a66ac2c3a400e30c36fed474cbe26b4 100644 --- a/src/util/directives/elementOutClick.directive.ts +++ b/src/util/directives/elementOutClick.directive.ts @@ -1,19 +1,22 @@ import {Directive, ElementRef, EventEmitter, HostListener, Output} from "@angular/core"; @Directive({ - selector: '[elementOutClick]', + selector: '[iav-onclick-outside]', }) export class ElementOutClickDirective { constructor(private elRef: ElementRef) { } - @Output() public outsideClick = new EventEmitter() + @Output('iav-onclick-outside') + public outsideClick = new EventEmitter() - @HostListener('document:click', ['$event', '$event.target']) - public onclick(event: MouseEvent, targetElement: HTMLElement): void { - if (!targetElement) { - return - } - - this.outsideClick.emit(!this.elRef.nativeElement.contains(targetElement)) + @HostListener('document:click', ['$event', '$event.target']) + public onclick(event: MouseEvent, targetElement: HTMLElement): void { + if (!targetElement) { + return + } + if (this.elRef.nativeElement.contains(targetElement)) { + return } + this.outsideClick.emit(event) + } }