diff --git a/docs/releases/v2.14.0.md b/docs/releases/v2.14.0.md index 0339fb1b060df2270fc83d5ef8cc26bead46987b..4c561f6b408a2ff8638c89edb9ae15906d6434bd 100644 --- a/docs/releases/v2.14.0.md +++ b/docs/releases/v2.14.0.md @@ -4,6 +4,7 @@ - added `[p]` and `[n]` as keyboard shortcut to navigate previous/next slice - experimental support for other versions of regions +- show template and parcellation info even if the chip are hidden ## Behind the scenes diff --git a/src/atlasComponents/sapi/schemaV3.ts b/src/atlasComponents/sapi/schemaV3.ts index 53b4c245caa70ddc2171bb3f75d5e4ec9dab88f2..2e26c5065a18418c31060d387a6c7b5e8404f2a6 100644 --- a/src/atlasComponents/sapi/schemaV3.ts +++ b/src/atlasComponents/sapi/schemaV3.ts @@ -550,6 +550,8 @@ export interface components { * @description Term or code used to identify the version of something. */ versionIdentifier: string + /** Datasets */ + datasets?: (components["schemas"]["EbrainsDatasetModel"])[] } /** * CoordinatePointModel diff --git a/src/atlasComponents/sapi/translateV3.ts b/src/atlasComponents/sapi/translateV3.ts index 6d08f219997b32eefa77edf5f571a3a66fb2e0b5..3f9de1ba5c77746caa6bed3b706c2ff60a389a2f 100644 --- a/src/atlasComponents/sapi/translateV3.ts +++ b/src/atlasComponents/sapi/translateV3.ts @@ -166,14 +166,19 @@ class TranslateV3 { return this.#templateMap.get(template.id) } async translateTemplate(template:PathReturn<"/spaces/{space_id}">): Promise<SxplrTemplate> { + + const ds = await Promise.all((template.datasets || []).map(ds => this.translateDs(ds))) + const { ...rest } = ds[0] || {} this.#templateMap.set(template["@id"], template) - const tmpl = { + const tmpl: SxplrTemplate = { id: template["@id"], name: template.fullName, shortName: template.shortName, - type: "SxplrTemplate" as const + type: "SxplrTemplate" as const, + ...rest } + this.#sxplrTmplMap.set(tmpl.id, tmpl) return tmpl } diff --git a/src/atlasComponents/sapiViews/core/parcellation/index.ts b/src/atlasComponents/sapiViews/core/parcellation/index.ts index 78616fb9ba32c12462740f59fcc315a93e0da45e..74925f1e2f3c377bc3a76898b59cd953e9224c4d 100644 --- a/src/atlasComponents/sapiViews/core/parcellation/index.ts +++ b/src/atlasComponents/sapiViews/core/parcellation/index.ts @@ -2,5 +2,5 @@ export { SapiViewsCoreParcellationModule } from "./module" export { FilterGroupedParcellationPipe } from "./filterGroupedParcellations.pipe" export { FilterUnsupportedParcPipe } from "./filterUnsupportedParc.pipe" export { GroupedParcellation } from "./groupedParcellation" -export { ParcellationDoiPipe } from "./parcellationDoi.pipe" +export { ParcTmplDoiPipe } from "./parcTmplDoi.pipe" export { ParcellationGroupSelectedPipe } from "./parcellationGroupSelected.pipe" diff --git a/src/atlasComponents/sapiViews/core/parcellation/module.ts b/src/atlasComponents/sapiViews/core/parcellation/module.ts index 9fb7e0066b8d71826f96e61a38e88ad5e3157a6c..5ed46747deddf9527977dfe78793cc5c43eaaba3 100644 --- a/src/atlasComponents/sapiViews/core/parcellation/module.ts +++ b/src/atlasComponents/sapiViews/core/parcellation/module.ts @@ -8,7 +8,7 @@ import { UtilModule } from "src/util"; import { SapiViewsUtilModule } from "../../util"; import { FilterGroupedParcellationPipe } from "./filterGroupedParcellations.pipe"; import { FilterUnsupportedParcPipe } from "./filterUnsupportedParc.pipe"; -import { ParcellationDoiPipe } from "./parcellationDoi.pipe"; +import { ParcTmplDoiPipe } from "./parcTmplDoi.pipe"; import { ParcellationVisibilityService } from "./parcellationVis.service"; import { ParcellationGroupSelectedPipe } from "./parcellationGroupSelected.pipe"; import { IsGroupedParcellation } from "./isGroupedParcellation.pipe"; @@ -31,7 +31,7 @@ import { ParcellationVisEffect } from "./parcellationVis.effect"; declarations: [ FilterGroupedParcellationPipe, FilterUnsupportedParcPipe, - ParcellationDoiPipe, + ParcTmplDoiPipe, ParcellationGroupSelectedPipe, IsGroupedParcellation, ], @@ -39,7 +39,7 @@ import { ParcellationVisEffect } from "./parcellationVis.effect"; FilterGroupedParcellationPipe, FilterUnsupportedParcPipe, ParcellationGroupSelectedPipe, - ParcellationDoiPipe, + ParcTmplDoiPipe, IsGroupedParcellation, ], providers: [ diff --git a/src/atlasComponents/sapiViews/core/parcellation/parcTmplDoi.pipe.ts b/src/atlasComponents/sapiViews/core/parcellation/parcTmplDoi.pipe.ts new file mode 100644 index 0000000000000000000000000000000000000000..a2306403ff5b5f3191f36ed07d98c10f86e77107 --- /dev/null +++ b/src/atlasComponents/sapiViews/core/parcellation/parcTmplDoi.pipe.ts @@ -0,0 +1,13 @@ +import { Pipe, PipeTransform } from "@angular/core"; +import { SxplrParcellation, SxplrTemplate } from "src/atlasComponents/sapi/sxplrTypes"; + +@Pipe({ + name: 'parcTmplDoiPipe', + pure: true +}) + +export class ParcTmplDoiPipe implements PipeTransform { + public transform(_parc: SxplrParcellation|SxplrTemplate): string[] { + return (_parc.link || []).map(v => v.href) + } +} diff --git a/src/atlasComponents/sapiViews/core/parcellation/parcellationDoi.pipe.ts b/src/atlasComponents/sapiViews/core/parcellation/parcellationDoi.pipe.ts deleted file mode 100644 index dad016dc1eb4e383a42bd4054162783c77b4ea6d..0000000000000000000000000000000000000000 --- a/src/atlasComponents/sapiViews/core/parcellation/parcellationDoi.pipe.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Pipe, PipeTransform } from "@angular/core"; -import { SxplrParcellation } from "src/atlasComponents/sapi/sxplrTypes"; - -@Pipe({ - name: 'parcellationDoiPipe', - pure: true -}) - -export class ParcellationDoiPipe implements PipeTransform { - public transform(_parc: SxplrParcellation): string[] { - return (_parc.link || []).map(v => v.href) - } -} diff --git a/src/atlasComponents/sapiViews/core/region/region/rich/region.rich.template.html b/src/atlasComponents/sapiViews/core/region/region/rich/region.rich.template.html index b48e7804e752ec61a567d11c2a415a8ab9f6f62e..0f5e7250100b65dd0e7f46350a90966f88121190 100644 --- a/src/atlasComponents/sapiViews/core/region/region/rich/region.rich.template.html +++ b/src/atlasComponents/sapiViews/core/region/region/rich/region.rich.template.html @@ -43,7 +43,7 @@ [sxplr-dialog-data]="{ title: parcellation.name, descMd: parcellation.desc, - actions: parcellation | parcellationDoiPipe + actions: parcellation | parcTmplDoiPipe }"> <mat-icon mat-list-icon fontSet="fas" fontIcon="fa-brain"></mat-icon> <div mat-line class="overview-content">{{ parcellation.name }}</div> diff --git a/src/atlasComponents/sapiViews/core/rich/ATPSelector/module.ts b/src/atlasComponents/sapiViews/core/rich/ATPSelector/module.ts index acd1fbb94fdf5ec6dc63888ee0de4523c03450eb..c0269e9f13b9ea5f73defe76b79ff8b190ef69c1 100644 --- a/src/atlasComponents/sapiViews/core/rich/ATPSelector/module.ts +++ b/src/atlasComponents/sapiViews/core/rich/ATPSelector/module.ts @@ -13,6 +13,7 @@ import { WrapperATPSelector } from "./wrapper/wrapper.component"; import { SAPIModule } from "src/atlasComponents/sapi/module"; import { MatTooltipModule } from "@angular/material/tooltip"; import { QuickTourModule } from "src/ui/quickTour"; +import { ExperimentalModule } from "src/experimental/experimental.module"; @NgModule({ imports: [ @@ -28,6 +29,7 @@ import { QuickTourModule } from "src/ui/quickTour"; SAPIModule, SapiViewsCoreParcellationModule, QuickTourModule, + ExperimentalModule, ], declarations: [ PureATPSelector, diff --git a/src/atlasComponents/sapiViews/core/rich/ATPSelector/pureDumb/pureATPSelector.components.ts b/src/atlasComponents/sapiViews/core/rich/ATPSelector/pureDumb/pureATPSelector.components.ts index a11e3301d53b422a9830a471ef59e9757568e82f..2a169d93a49517e37c1bd59c6d069ed3aafce31e 100644 --- a/src/atlasComponents/sapiViews/core/rich/ATPSelector/pureDumb/pureATPSelector.components.ts +++ b/src/atlasComponents/sapiViews/core/rich/ATPSelector/pureDumb/pureATPSelector.components.ts @@ -1,4 +1,4 @@ -import { AfterViewInit, ChangeDetectionStrategy, Component, EventEmitter, HostBinding, Input, OnChanges, OnDestroy, Output, QueryList, SimpleChanges, ViewChildren } from "@angular/core"; +import { AfterViewInit, ChangeDetectionStrategy, Component, EventEmitter, HostBinding, Input, OnDestroy, Output, QueryList, ViewChildren } from "@angular/core"; import { BehaviorSubject, Subscription, combineLatest, concat, merge, of } from "rxjs"; import { map, switchMap } from "rxjs/operators"; import { SxplrAtlas, SxplrParcellation, SxplrTemplate } from "src/atlasComponents/sapi/sxplrTypes"; @@ -43,28 +43,35 @@ const pipe = new FilterGroupedParcellationPipe() changeDetection: ChangeDetectionStrategy.OnPush, }) -export class PureATPSelector implements OnChanges, AfterViewInit, OnDestroy{ +export class PureATPSelector implements AfterViewInit, OnDestroy{ #subscriptions: Subscription[] = [] @Input('sxplr-pure-atp-selector-color-palette') colorPalette: string[] = darkThemePalette + #selectedATP$ = new BehaviorSubject<ATP>(null) @Input(`sxplr-pure-atp-selector-selected-atp`) - public selectedATP: ATP + set selectedATP(val: ATP){ + this.#selectedATP$.next(val) + } public selectedIds: string[] = [] @Input(`sxplr-pure-atp-selector-atlases`) public allAtlases: SxplrAtlas[] = [] + #availableTemplates$ = new BehaviorSubject<SxplrTemplate[]>([]) @Input(`sxplr-pure-atp-selector-templates`) - public availableTemplates: SxplrTemplate[] = [] + set availableTemplates(val: SxplrTemplate[]){ + this.#availableTemplates$.next(val) + } + #parcellations$ = new BehaviorSubject<SxplrParcellation[]>([]) @Input(`sxplr-pure-atp-selector-parcellations`) - public parcellations: SxplrParcellation[] = [] - - public parcAndGroup: (GroupedParcellation|SxplrParcellation)[] = [] + set parcellations(val: SxplrParcellation[]){ + this.#parcellations$.next(val) + } @Input('sxplr-pure-atp-selector-is-busy') public isBusy: boolean = false @@ -93,27 +100,34 @@ export class PureATPSelector implements OnChanges, AfterViewInit, OnDestroy{ this.selectLeafEmitter.emit(atp) } - ngOnChanges(changes: SimpleChanges): void { - if (changes.selectedATP) { - if (!changes.selectedATP.currentValue) { - this.selectedIds = [] - } else { - const { atlas, parcellation, template } = changes.selectedATP.currentValue as ATP - this.selectedIds = [atlas?.id, parcellation?.id, template?.id].filter(v => !!v) + view$ = combineLatest([ + this.#selectedATP$, + this.#parcellations$, + this.#availableTemplates$, + ]).pipe( + map(([{ atlas, parcellation, template }, parcellations, availableTemplates]) => { + const parcAndGroup = [ + ...pipe.transform(parcellations || [], true), + ...pipe.transform(parcellations || [], false), + ] + const selectedIds = [atlas?.id, parcellation?.id, template?.id].filter(v => !!v) + + const hideParcChip = parcAndGroup.length <= 1 + const hideTmplChip = availableTemplates?.length <= 1 + + return { + atlas, + parcellation, + template, + parcAndGroup, + parcellations, + selectedIds, + hideParcChip, + hideTmplChip, + availableTemplates: availableTemplates || [], } - } - - if (changes.parcellations) { - if (!changes.parcellations.currentValue) { - this.parcAndGroup = [] - } else { - this.parcAndGroup = [ - ...pipe.transform(changes.parcellations.currentValue, true), - ...pipe.transform(changes.parcellations.currentValue, false), - ] - } - } - } + }) + ) ngAfterViewInit(): void { this.#subscriptions.push( diff --git a/src/atlasComponents/sapiViews/core/rich/ATPSelector/pureDumb/pureATPSelector.template.html b/src/atlasComponents/sapiViews/core/rich/ATPSelector/pureDumb/pureATPSelector.template.html index 9aaf8f9645e8c84970f89add116dd4e3d8a45ef0..d1a85f1b66634c68db3922aa160bcbb0092bcb46 100644 --- a/src/atlasComponents/sapiViews/core/rich/ATPSelector/pureDumb/pureATPSelector.template.html +++ b/src/atlasComponents/sapiViews/core/rich/ATPSelector/pureDumb/pureATPSelector.template.html @@ -1,8 +1,36 @@ -<ng-template [ngIf]="selectedATP" let-ATP> +<ng-template [ngIf]="view$ | async" let-view> + + <!-- fallback for parcellation info icon --> + <ng-template [ngIf]="view.hideParcChip"> + <button mat-icon-button + sxplr-dialog + [sxplr-dialog-size]="null" + [sxplr-dialog-data]="{ + title: view.parcellation.name, + descMd: view.parcellation.desc, + actions: view.parcellation | parcTmplDoiPipe + }"> + <i class="fas fa-info"></i> + </button> + </ng-template> + + <!-- fallback for space info icon --> + <ng-template [ngIf]="view.hideTmplChip"> + <button mat-icon-button + sxplr-dialog + [sxplr-dialog-size]="null" + [sxplr-dialog-data]="{ + title: view.template.name, + descMd: view.template.desc, + actions: view.template | parcTmplDoiPipe + }"> + <i class="fas fa-info"></i> + </button> + </ng-template> <!-- parcellation smart chip --> - <sxplr-smart-chip *ngIf="ATP.parcellation && parcAndGroup && parcAndGroup.length > 1" - [items]="parcAndGroup || []" + <sxplr-smart-chip *ngIf="!view.hideParcChip" + [items]="view.parcAndGroup || []" [color]="colorPalette[2]" [getChildren]="getChildren" (itemClicked)="selectLeaf({ parcellation: $event })" @@ -15,7 +43,7 @@ </span> <span class="sxplr-ml-1 text-muted"> - ({{ parcellations.length }}) + ({{ view.parcellations.length }}) </span> </ng-template> @@ -27,9 +55,9 @@ sxplr-dialog [sxplr-dialog-size]="null" [sxplr-dialog-data]="{ - title: ATP.parcellation.name, - descMd: ATP.parcellation.desc, - actions: ATP.parcellation | parcellationDoiPipe + title: view.parcellation.name, + descMd: view.parcellation.desc, + actions: view.parcellation | parcTmplDoiPipe }"> <i class="fas fa-info"></i> </button> @@ -42,7 +70,7 @@ <ng-template sxplrSmartChipContent> <span class="chip-text"> - {{ ATP.parcellation.shortName }} + {{ view.parcellation.shortName }} </span> </ng-template> @@ -51,7 +79,7 @@ <ng-container *ngTemplateOutlet="optionTmpl; context: { $implicit: parc, suffixText: (parc | isGroupedParcellation) && '(' + parc.parcellations.length + ')' , - overridePrefixIconTmpl: (parc | parcellationGroupSelected : ATP.parcellation) + overridePrefixIconTmpl: (parc | parcellationGroupSelected : view.parcellation) ? halfSelectedTmpl : null }"> @@ -60,8 +88,8 @@ </sxplr-smart-chip> <!-- space smart chip --> - <sxplr-smart-chip *ngIf="ATP.template && availableTemplates && availableTemplates.length > 1" - [items]="availableTemplates || []" + <sxplr-smart-chip *ngIf="!view.hideTmplChip" + [items]="view.availableTemplates || []" [color]="colorPalette[1]" (itemClicked)="selectLeaf({ template: $event })" [elevation]="4" @@ -73,14 +101,14 @@ </span> <span class="sxplr-ml-1 text-muted"> - ({{ availableTemplates.length }}) + ({{ view.availableTemplates.length }}) </span> </ng-template> <ng-template sxplrSmartChipContent> <span class="chip-text"> - {{ ATP.template.shortName }} + {{ view.template.shortName }} </span> </ng-template> <ng-template sxplrSmartChipMenu let-space> @@ -89,7 +117,7 @@ </sxplr-smart-chip> <!-- atlas smart chip --> - <sxplr-smart-chip *ngIf="ATP.atlas" + <sxplr-smart-chip *ngIf="view.atlas" [items]="allAtlases" [color]="colorPalette[0]" (itemClicked)="selectLeaf({ atlas: $event})" @@ -112,13 +140,54 @@ <ng-template sxplrSmartChipContent> <span class="chip-text"> - {{ ATP.atlas.name }} + {{ view.atlas.name }} </span> </ng-template> <ng-template sxplrSmartChipMenu let-atlas> <ng-container *ngTemplateOutlet="optionTmpl; context: { $implicit: atlas }"></ng-container> </ng-template> </sxplr-smart-chip> + + <!-- option template --> + <ng-template + #optionTmpl + let-item + let-overridePrefixIconTmpl="overridePrefixIconTmpl" + let-overrideSuffixIcon="overrideSuffixIcon" + let-suffixText="suffixText"> + + <!-- prefix --> + <ng-template [ngIf]="overridePrefixIconTmpl" [ngIfElse]="defaultPrefix"> + <ng-template [ngTemplateOutlet]="overridePrefixIconTmpl"></ng-template> + </ng-template> + <ng-template #defaultPrefix> + <ng-template [ngIf]="view.selectedIds" let-selectedIds> + <mat-icon + fontSet="fas" + [fontIcon]="selectedIds.includes(item.id) ? 'fa-circle' : 'fa-none'" + > + </mat-icon> + </ng-template> + </ng-template> + + <!-- button body --> + <span *ngIf="item" class="full-sized-button" + [matTooltip]="item.version?.name || item.name || item.fullName" + [matTooltipPosition]="'above'"> + {{ item.version?.name || item.shortName || item.name || item.fullName }} + <ng-template [ngIf]="suffixText"> + <span class="text-muted"> + {{ suffixText }} + </span> + </ng-template> + </span> + + <!-- suffix --> + <ng-template [ngIf]="overrideSuffixIcon"> + <i [class]="overrideSuffixIcon"></i> + </ng-template> + </ng-template> + </ng-template> <!-- half selected --> @@ -126,48 +195,3 @@ <ng-template #halfSelectedTmpl> <mat-icon fontSet="far" fontIcon="fa-circle"></mat-icon> </ng-template> - -<!-- option template --> -<ng-template - #optionTmpl - let-item - let-overridePrefixIconTmpl="overridePrefixIconTmpl" - let-overrideSuffixIcon="overrideSuffixIcon" - let-suffixText="suffixText"> - - <!-- prefix --> - <ng-template [ngIf]="overridePrefixIconTmpl" [ngIfElse]="defaultPrefix"> - <ng-template [ngTemplateOutlet]="overridePrefixIconTmpl"></ng-template> - </ng-template> - <ng-template #defaultPrefix> - <ng-template [ngIf]="selectedIds" let-selectedIds> - <mat-icon - fontSet="fas" - [fontIcon]="selectedIds.includes(item.id) ? 'fa-circle' : 'fa-none'" - > - </mat-icon> - </ng-template> - </ng-template> - - <!-- button body --> - <span *ngIf="item" class="full-sized-button" - [matTooltip]="item.version?.name || item.name || item.fullName" - [matTooltipPosition]="'above'"> - {{ item.version?.name || item.shortName || item.name || item.fullName }} - <ng-template [ngIf]="suffixText"> - <span class="text-muted"> - {{ suffixText }} - </span> - </ng-template> - </span> - - <!-- suffix --> - <ng-template [ngIf]="overrideSuffixIcon"> - <i [class]="overrideSuffixIcon"></i> - </ng-template> -</ng-template> - - -<ng-template #isBusyTmpl> - -</ng-template> \ No newline at end of file