Skip to content
Snippets Groups Projects
Commit 809426fc authored by Xiao Gui's avatar Xiao Gui
Browse files

feat: rework dataset browser

chore: dialog in condensed region select
parent 6dbe8841
No related branches found
No related tags found
No related merge requests found
......@@ -46,6 +46,22 @@ export class UseEffects implements OnDestroy{
})
)
this.onDeselectRegionsWithId$ = this.actions$.pipe(
ofType(ACTION_TYPES.DESELECT_REGIONS_WITH_ID),
map(action => {
const { deselecRegionIds } = action as any
return deselecRegionIds
}),
withLatestFrom(this.regionsSelected$),
map(([ deselecRegionIds, alreadySelectedRegions ]) => {
const deselectSet = new Set(deselecRegionIds)
return {
type: SELECT_REGIONS,
selectRegions: alreadySelectedRegions.filter(({ ngId, labelIndex }) => !deselectSet.has(generateLabelIndexId({ ngId, labelIndex })))
}
})
)
this.addToSelectedRegions$ = this.actions$.pipe(
ofType(ADD_TO_REGIONS_SELECTION_WITH_IDS),
map(action => {
......@@ -104,6 +120,9 @@ export class UseEffects implements OnDestroy{
@Effect()
onDeselectRegions: Observable<any>
@Effect()
onDeselectRegionsWithId$: Observable<any>
private convertRegionIdsToRegion = ([selectRegionIds, parcellation]) => {
const { ngId: defaultNgId } = parcellation
return (<any[]>selectRegionIds)
......@@ -215,4 +234,10 @@ export const compareRegions: (r1: any,r2: any) => boolean = (r1, r2) => {
return r1.ngId === r2.ngId
&& r1.labelIndex === r2.labelIndex
&& r1.name === r2.name
}
\ No newline at end of file
}
const ACTION_TYPES = {
DESELECT_REGIONS_WITH_ID: 'DESELECT_REGIONS_WITH_ID'
}
export const VIEWER_STATE_ACTION_TYPES = ACTION_TYPES
\ No newline at end of file
......@@ -26,15 +26,20 @@
<ng-container *ngIf="showDataset">
<!-- selected regions container -->
<mat-card *ngIf="viewerStateController.regionsSelected$ | async as regionsSelected"
<mat-card *ngIf="false && viewerStateController.regionsSelected$ | async as regionsSelected"
[ngClass]="{'h-117px flex-grow-1': regionsSelected.length > 1, 'flex-grow-0': regionsSelected.length < 2}"
class="flex-shrink-0 mb-1 pe-all">
<!-- show when no region is selected -->
<mat-card-subtitle *ngIf="regionsSelected.length === 0">
In the current view
</mat-card-subtitle>
<mat-card-content *ngIf="regionsSelected.length === 0">
<div class="pt-2 pb-2 d-flex flex-row align-items-center flex-nowrap">
<i *ngIf="false" class="fas fa-brain font-2x mr-2"></i>
<span class="font-weight-bold">
In this parcellation atlas
</span>
</div>
</mat-card-content>
<!-- show when regions are selected -->
<mat-card-content *ngIf="regionsSelected.length > 0" class="h-100">
......@@ -91,10 +96,19 @@
[parcellation]="viewerStateController.parcellationSelected$ | async"
[regions]="viewerStateController.regionsSelected$ | async"
(dataentriesUpdated)="availableDatasets = $event.length">
<mat-card-subtitle card-header>
Related datasets
</mat-card-subtitle>
<ng-container card-content='prepend'>
<ng-container *ngTemplateOutlet="selectedRegionsTmpl">
</ng-container>
<mat-divider class="mt-2 mb-4 position-relative"></mat-divider>
<mat-card-subtitle card-header>
Related datasets
</mat-card-subtitle>
</ng-container>
<!-- footer content -->
<div class="d-flex flex-row justify-content-center" card-footer>
......@@ -129,4 +143,72 @@
Hide
</button>
</mat-dialog-actions>
</ng-template>
<!-- selected regions container -->
<ng-template #selectedRegionsTmpl>
<ng-container *ngIf="viewerStateController.regionsSelected$ | async as regionsSelected">
<div [ngClass]="{'h-117px flex-grow-1': regionsSelected.length > 1, 'flex-grow-0': regionsSelected.length < 2}"
class="flex-shrink-0 mb-1 pe-all d-flex flex-column">
<!-- show when no region is selected -->
<div *ngIf="regionsSelected.length === 0"
class="pt-2 pb-2 d-flex flex-row align-items-center flex-nowrap">
<i *ngIf="false" class="fas fa-brain font-2x mr-2"></i>
<span class="font-weight-bold">
In this parcellation atlas
</span>
</div>
<!-- show when regions are selected -->
<div *ngIf="regionsSelected.length > 0" class="h-100">
<!-- single region -->
<ng-template [ngIf]="regionsSelected.length === 1" [ngIfElse]="multiRegionTemplate">
<!-- selected brain region -->
<div class="pt-2 pb-2 d-flex flex-row align-items-center flex-nowrap">
<i class="fas fa-brain font-2x mr-2"></i>
<span class="font-weight-bold">
{{ regionsSelected[0].name }}
</span>
<button (click)="removeRegion(regionsSelected[0])" mat-icon-button>
<i class="fas fa-trash"></i>
</button>
</div>
</ng-template>
<!-- multi region -->
<ng-template #multiRegionTemplate>
<cdk-virtual-scroll-viewport class="h-100" itemSize="78">
<div *cdkVirtualFor="let region of regionsSelected; trackBy: trackByFn ; let index = index"
class="region-wrapper d-flex flex-column" >
<!-- divider if index !== 0 -->
<mat-divider class="flex-grow-0 flex-shrink-0" *ngIf="index !== 0"></mat-divider>
<!-- selected brain region -->
<div class="flex-grow-1 flex-shrink-1 pt-2 pb-2 d-flex flex-row align-items-center flex-nowrap">
<i class="flex-grow-0 flex-shrink-0 fas fa-brain font-2x mr-2"></i>
<span class="flex-grow-1 flex-shrink-1 font-weight-bold">
{{ region.name }}
</span>
<button mat-icon-button
class="flex-grow-0 flex-shrink-0"
(click)="removeRegion(region)" >
<i class="fas fa-trash"></i>
</button>
</div>
</div>
</cdk-virtual-scroll-viewport>
</ng-template>
</div>
</div>
</ng-container>
</ng-template>
\ No newline at end of file
import { Component, EventEmitter, Output, ViewChild, ElementRef, TemplateRef, Input } from "@angular/core";
import { Store, select } from "@ngrx/store";
import { Observable } from "rxjs";
import { map, distinctUntilChanged, startWith, withLatestFrom, debounceTime, shareReplay, take, filter } from "rxjs/operators";
import { Observable, BehaviorSubject } from "rxjs";
import { map, distinctUntilChanged, startWith, withLatestFrom, debounceTime, shareReplay, take, filter, tap } from "rxjs/operators";
import { getMultiNgIdsRegionsLabelIndexMap, generateLabelIndexId } from "src/services/stateStore.service";
import { FormControl } from "@angular/forms";
import { MatAutocompleteSelectedEvent, MatDialog } from "@angular/material";
import { ADD_TO_REGIONS_SELECTION_WITH_IDS, SELECT_REGIONS } from "src/services/state/viewerState.store";
import { ADD_TO_REGIONS_SELECTION_WITH_IDS, SELECT_REGIONS, CHANGE_NAVIGATION } from "src/services/state/viewerState.store";
import { VIEWERSTATE_CONTROLLER_ACTION_TYPES } from "../viewerState.base";
import { AtlasViewerConstantsServices } from "src/atlasViewer/atlasViewer.constantService.service";
import { VIEWER_STATE_ACTION_TYPES } from "src/services/effect/effect";
const filterRegionBasedOnText = searchTerm => region => region.name.toLowerCase().includes(searchTerm.toLowerCase())
......@@ -29,6 +30,11 @@ export class RegionTextSearchAutocomplete{
public useMobileUI$: Observable<boolean>
private focusedRegionId$: BehaviorSubject<string> = new BehaviorSubject(null)
public focusedRegion$: Observable<any>
public selectedRegionLabelIndexSet: Set<string> = new Set()
constructor(
private store$: Store<any>,
private dialog: MatDialog,
......@@ -59,13 +65,21 @@ export class RegionTextSearchAutocomplete{
}
}
return returnArray
}),
shareReplay(1)
)
this.focusedRegion$ = this.focusedRegionId$.pipe(
withLatestFrom(this.regionsWithLabelIndex$),
map(([ id, regions ]) => {
if (!id) return null
return regions.find(({ labelIndexId }) => labelIndexId === id)
})
)
)
this.autocompleteList$ = this.formControl.valueChanges.pipe(
startWith(''),
debounceTime(200),
filter(string => string.length > 0),
withLatestFrom(this.regionsWithLabelIndex$.pipe(
startWith([])
)),
......@@ -76,6 +90,10 @@ export class RegionTextSearchAutocomplete{
this.regionsSelected$ = viewerState$.pipe(
select('regionsSelected'),
distinctUntilChanged(),
tap(regions => {
const arrLabelIndexId = regions.map(({ ngId, labelIndex }) => generateLabelIndexId({ ngId, labelIndex }))
this.selectedRegionLabelIndexSet = new Set(arrLabelIndexId)
}),
shareReplay(1)
)
......@@ -86,15 +104,40 @@ export class RegionTextSearchAutocomplete{
)
}
public optionSelected(ev: MatAutocompleteSelectedEvent){
const id = ev.option.value
public toggleRegionWithId(id: string, removeFlag=false){
if (removeFlag) {
this.store$.dispatch({
type: VIEWER_STATE_ACTION_TYPES.DESELECT_REGIONS_WITH_ID,
deselecRegionIds: [id]
})
} else {
this.store$.dispatch({
type: ADD_TO_REGIONS_SELECTION_WITH_IDS,
selectRegionIds : [id]
})
}
}
public navigateTo(position){
this.store$.dispatch({
type: ADD_TO_REGIONS_SELECTION_WITH_IDS,
selectRegionIds : [id]
type: CHANGE_NAVIGATION,
navigation: {
position,
animation: {}
}
})
}
public optionSelected(ev: MatAutocompleteSelectedEvent){
const id = ev.option.value
this.autoTrigger.nativeElement.value = ''
this.autoTrigger.nativeElement.focus()
this.focusedRegionId$.next(id)
}
public openRegionFocusDialog(tmpl:TemplateRef<any>){
this.dialog.open(tmpl).afterClosed().subscribe(() => {
this.autoTrigger.nativeElement.focus()
})
}
private regionsWithLabelIndex$: Observable<any[]>
......
......@@ -14,7 +14,7 @@
<mat-autocomplete
(opened)="focused = true"
(closed)="focused = false"
(optionSelected)="optionSelected($event)"
(optionSelected)="optionSelected($event); openRegionFocusDialog(regionFocusDialog)"
autoActiveFirstOption
#auto="matAutocomplete">
<mat-option
......@@ -36,6 +36,41 @@
</button>
</div>
<ng-template #regionFocusDialog>
<ng-container *ngIf="focusedRegion$ | async as focusedRegion; else noRegionSelected">
<div mat-dialog-title>
{{ focusedRegion.name }}
</div>
<div class="justify-content-end" mat-dialog-actions>
<button mat-flat-button
*ngIf="focusedRegion.position"
(click)="navigateTo(focusedRegion.position)"
mat-dialog-close
class="ml-1"
color="primary">
Navigate
</button>
<button mat-flat-button
*ngIf="focusedRegion.labelIndexId"
mat-dialog-close
(click)="toggleRegionWithId(focusedRegion.labelIndexId, selectedRegionLabelIndexSet.has(focusedRegion.labelIndexId))"
class="ml-1"
[color]="selectedRegionLabelIndexSet.has(focusedRegion.labelIndexId) ? 'warn' : 'primary'">
{{ selectedRegionLabelIndexSet.has(focusedRegion.labelIndexId) ? 'Remove from selection' : 'Add to selection' }}
</button>
<button mat-button
[mat-dialog-close]="null"
class="ml-1">
Dismiss
</button>
</div>
</ng-container>
<ng-template #noRegionSelected>
No region selected.
</ng-template>
</ng-template>
<ng-template #regionHierarchyDialog>
<div class="h-100 d-flex flex-column">
<mat-dialog-content class="flex-grow-1 flex-shrink-1">
......
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment