diff --git a/docs/releases/v2.11.1.md b/docs/releases/v2.11.1.md index 4a9e90acec8afb13aeba860f999ad9ddbadd381a..cd89866c729578f4e11a82420342cec75c9d1b34 100644 --- a/docs/releases/v2.11.1.md +++ b/docs/releases/v2.11.1.md @@ -1,5 +1,16 @@ # v2.11.1 +## Feature + +- Allow point assignment result to be sorted +- Allow point assignment result to be downloaded as csv + +## Bugfixes + +- Fixed point assignment full table not showing + ## Behind the scenes - Bump siibra-api version dependency. Remove guard for feature type query restrictions +- Removed unused components +- Tweaked context menu, showing on hover effects \ No newline at end of file diff --git a/package.json b/package.json index 593c06d72221ece38464f61b65e29828639ff594..9719e8ac3d423f69b3e79bc3c266675522175327 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "siibra-explorer", - "version": "2.11.0", + "version": "2.11.1", "description": "siibra-explorer - explore brain atlases. Based on humanbrainproject/nehuba & google/neuroglancer. Built with angular", "scripts": { "lint": "eslint src --ext .ts", diff --git a/src/atlasComponents/sapiViews/volumes/point-assignment/point-assignment.component.html b/src/atlasComponents/sapiViews/volumes/point-assignment/point-assignment.component.html index 8fc0aa7b4411e39a46e1d30f200f45907eb8d1ee..57a9cf3d5031587bc37f7ac36e115319b396f500 100644 --- a/src/atlasComponents/sapiViews/volumes/point-assignment/point-assignment.component.html +++ b/src/atlasComponents/sapiViews/volumes/point-assignment/point-assignment.component.html @@ -1,7 +1,7 @@ <div class="sxplr-m-2" *ngIf="busy$ | async"> <spinner-cmp class="sxplr-d-inline-block"></spinner-cmp> <span> - Loading assignment ... + Performing probabilistic assignment ... </span> </div> @@ -13,9 +13,14 @@ </button> <!-- simple table --> - <table mat-table [dataSource]="df | dfToDs" class="sxplr-w-100"> + <table mat-table [dataSource]="df | dfToDs : simpleTblSort" + matSort + class="sxplr-w-100" + #simpleTblSort="matSort" + matSortActive="map value" + matSortDirection="desc"> <ng-container matColumnDef="region"> - <th mat-header-cell *matHeaderCellDef> + <th mat-header-cell *matHeaderCellDef mat-sort-header> region </th> <td mat-cell *matCellDef="let element"> @@ -25,12 +30,12 @@ </button> </td> </ng-container> - <ng-container matColumnDef="intersection over union"> - <th mat-header-cell *matHeaderCellDef> - intersection over union + <ng-container matColumnDef="map value"> + <th mat-header-cell *matHeaderCellDef mat-sort-header> + map value </th> <td mat-cell *matCellDef="let element"> - {{ element['intersection over union'] | prettyPresent }} + {{ element['map value'] | prettyPresent }} </td> </ng-container> @@ -42,10 +47,14 @@ <ng-template #datatableTmpl> <h2 mat-dialog-title>Assignment</h2> <mat-dialog-content> - <table mat-table [dataSource]="df$ | async | dfToDs"> + <table mat-table [dataSource]="df$ | async | dfToDs : comphTableSort" + matSort + #comphTableSort="matSort" + matSortActive="map value" + matSortDirection="desc"> <ng-container *ngFor="let column of columns$ | async" [matColumnDef]="column"> - <th mat-header-cell *matHeaderCellDef> + <th mat-header-cell *matHeaderCellDef mat-sort-header> {{ column }} </th> <td mat-cell *matCellDef="let element"> @@ -58,6 +67,14 @@ </table> </mat-dialog-content> <mat-dialog-actions align="center"> + <button mat-raised-button color="primary" + [zip-files-output]="zipfileConfig$ | async" + zip-files-output-zip-filename="pointassignment.zip"> + <i class="fas fa-download"></i> + <span> + Download CSV + </span> + </button> <button mat-button mat-dialog-close>Close</button> - </mat-dialog-actions> + </mat-dialog-actions> </ng-template> diff --git a/src/atlasComponents/sapiViews/volumes/point-assignment/point-assignment.component.ts b/src/atlasComponents/sapiViews/volumes/point-assignment/point-assignment.component.ts index 2bf281e7816015f0607b4f77122373d7097705c5..46377dac685aab4db567a7efc318d032a8af305d 100644 --- a/src/atlasComponents/sapiViews/volumes/point-assignment/point-assignment.component.ts +++ b/src/atlasComponents/sapiViews/volumes/point-assignment/point-assignment.component.ts @@ -1,12 +1,14 @@ -import { Component, Input, OnDestroy, Output, TemplateRef, EventEmitter } from '@angular/core'; +import { Component, Input, OnDestroy, Output, TemplateRef, EventEmitter, ViewChild, AfterViewInit } from '@angular/core'; import { MatDialog } from '@angular/material/dialog'; -import { BehaviorSubject, EMPTY, Subscription, combineLatest, concat, of } from 'rxjs'; +import { BehaviorSubject, EMPTY, Observable, Subscription, combineLatest, concat, of } from 'rxjs'; import { map, shareReplay, switchMap, tap } from 'rxjs/operators'; -import { SAPI } from 'src/atlasComponents/sapi/sapi.service'; +import { SAPI, EXPECTED_SIIBRA_API_VERSION } from 'src/atlasComponents/sapi/sapi.service'; import { SxplrParcellation, SxplrRegion, SxplrTemplate } from 'src/atlasComponents/sapi/sxplrTypes'; import { translateV3Entities } from 'src/atlasComponents/sapi/translateV3'; import { PathReturn } from 'src/atlasComponents/sapi/typeV3'; import { TSandsPoint } from 'src/util/types'; +import { TZipFileConfig } from "src/zipFilesOutput/type" +import { environment } from "src/environments/environment" @Component({ selector: 'sxplr-point-assignment', @@ -17,7 +19,7 @@ export class PointAssignmentComponent implements OnDestroy { SIMPLE_COLUMNS = [ "region", - "intersection over union", + "map value", ] #busy$ = new BehaviorSubject(false) @@ -44,7 +46,7 @@ export class PointAssignmentComponent implements OnDestroy { @Output() clickOnRegion = new EventEmitter<{ target: SxplrRegion, event: MouseEvent }>() - df$ = combineLatest([ + df$: Observable<PathReturn<"/map/assign">> = combineLatest([ this.#point, this.#parcellation, this.#template, @@ -66,15 +68,14 @@ export class PointAssignmentComponent implements OnDestroy { parcellation_id: parcellation.id, point: point.coordinates.map(v => `${v.value/1e6}mm`).join(','), space_id: template.id, - sigma_mm: 3.0 + sigma_mm: 0 } }).pipe( tap(() => this.#busy$.next(false)), ) - ).pipe( - shareReplay(1), ) - }) + }), + shareReplay(1), ) columns$ = this.df$.pipe( @@ -95,4 +96,76 @@ export class PointAssignmentComponent implements OnDestroy { const sxplrReg = await translateV3Entities.translateRegion(region) this.clickOnRegion.emit({ target: sxplrReg, event }) } + + zipfileConfig$: Observable<TZipFileConfig[]> = combineLatest([ + this.#point, + this.#parcellation, + this.#template, + this.df$ + ]).pipe( + map(([ pt, parc, tmpl, df ]) => { + return [{ + filename: 'README.md', + filecontent: generateReadMe(pt, parc, tmpl) + }, { + filename: 'pointassignment.csv', + filecontent: generateCsv(df) + }] as TZipFileConfig[] + }) + ) +} + +function generateReadMe(pt: TSandsPoint, parc: SxplrParcellation, tmpl: SxplrTemplate){ + return `# Point assignment exporter + +Exported by siibra-explorer verison \`${environment.VERSION}\` hash: \`${environment.GIT_HASH}\`. + +On: ${new Date().toString()} + +Data retrieved through siibra-api version \`${EXPECTED_SIIBRA_API_VERSION}\` + +Retrieval parameters: + +Point +- coord: ${pt.coordinates.map(v => v.value).join(',')} mm + +Parcellation +- name: ${parc.name || parc.shortName} +- id: ${parc.id} + +Space +- name: ${tmpl.name || tmpl.shortName} +- id: ${tmpl.id} +` +} + +function escapeFactory(chars: string[] = []){ + const search = new RegExp(`[${chars.join('').replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}]`, 'g') + return function escape(s: string) { + return s.replace(search, s => `\\${s}`) + } +} + +const escapeDoubleQuotes = escapeFactory(['"']) + +function processRow(v: unknown[]): string{ + const returnValue: string[] = [] + for (const item of v) { + + // region + if (typeof item === "object" && item?.['@type'] === "siibra-0.4/region") { + returnValue.push(item['name']) + continue + } + + returnValue.push(JSON.stringify(item)) + } + return returnValue.map(escapeDoubleQuotes).map(v => `"${v}"`).join(",") +} + +function generateCsv(df: PathReturn<"/map/assign">) { + return [ + df.columns.map(escapeDoubleQuotes).map(v => `"${v}"`).join(","), + ...df.data.map(processRow) + ].join("\n") } diff --git a/src/atlasComponents/sapiViews/volumes/volumes.module.ts b/src/atlasComponents/sapiViews/volumes/volumes.module.ts index eee2503b61afec1b5533c4d265ce178669ee492c..1a5a32d0f5cf07a536f3e559c29ad835ca1abb6f 100644 --- a/src/atlasComponents/sapiViews/volumes/volumes.module.ts +++ b/src/atlasComponents/sapiViews/volumes/volumes.module.ts @@ -6,6 +6,8 @@ import { UtilModule } from 'src/util'; import { SpinnerModule } from 'src/components/spinner'; import { MatDialogModule } from '@angular/material/dialog'; import { MatButtonModule } from '@angular/material/button'; +import { MatSortModule } from '@angular/material/sort'; +import { ZipFilesOutputModule } from 'src/zipFilesOutput/module'; @@ -20,6 +22,8 @@ import { MatButtonModule } from '@angular/material/button'; SpinnerModule, MatDialogModule, MatButtonModule, + MatSortModule, + ZipFilesOutputModule, ], exports: [ PointAssignmentComponent diff --git a/src/overwrite.scss b/src/overwrite.scss index 9fd0bb0b88e2fc3039a03040dfa659a2ec341cf8..5b17076e66aca6d569e24b3e6f5d0b6ab4c65f32 100644 --- a/src/overwrite.scss +++ b/src/overwrite.scss @@ -289,3 +289,38 @@ a[mat-raised-button] { pointer-events: none!important; } + +// list like button +$llb: "#{$nsp}-list-like-button"; +button.#{$llb} +{ + width: 100%; + + > .mat-button-wrapper + { + display: flex; + + > .#{$llb}-icon + { + width: 2rem; + flex: 0 0 auto; + margin: auto; + font-size: 150%; + } + + > .#{$llb}-body + { + flex: 1 1 0px; + display: flex; + flex-direction: column; + margin:1rem; + + .#{$llb}-body-line + { + flex: 0 0 0px; + text-align: start; + line-height: 1.5rem; + } + } + } +} diff --git a/src/ui/ui.module.ts b/src/ui/ui.module.ts index a657e32cc68916a0b63a485f226dbdd7a05e948e..1d9c79d562f70331b06637468450d964b6d733b4 100644 --- a/src/ui/ui.module.ts +++ b/src/ui/ui.module.ts @@ -10,7 +10,6 @@ import { DownloadDirective } from "../util/directives/download.directive"; import { MobileOverlay } from "./nehubaContainer/mobileOverlay/mobileOverlay.component"; import { HumanReadableFileSizePipe } from "src/util/pipes/humanReadableFileSize.pipe"; import { ReorderPanelIndexPipe } from "./nehubaContainer/reorderPanelIndex.pipe"; -import { FixedMouseContextualContainerDirective } from "src/util/directives/FixedMouseContextualContainerDirective.directive"; import { ShareModule } from "src/share"; import { AuthModule } from "src/auth"; import { ActionDialog } from "./actionDialog/actionDialog.component"; @@ -41,7 +40,6 @@ import { HANDLE_SCREENSHOT_PROMISE, TypeHandleScrnShotPromise } from "../screens ReorderPanelIndexPipe, /* directive */ DownloadDirective, - FixedMouseContextualContainerDirective, ], providers: [ { @@ -104,7 +102,6 @@ import { HANDLE_SCREENSHOT_PROMISE, TypeHandleScrnShotPromise } from "../screens // NehubaContainer, MobileOverlay, // StatusCardComponent, - FixedMouseContextualContainerDirective, ] }) diff --git a/src/util/df-to-ds.pipe.ts b/src/util/df-to-ds.pipe.ts index 612b8b48220cb6463458d21bd63f5f5c2eb60e7b..f50be028d82a3522df9277be89ef75b59accd33d 100644 --- a/src/util/df-to-ds.pipe.ts +++ b/src/util/df-to-ds.pipe.ts @@ -1,5 +1,7 @@ import { CdkTableDataSourceInput } from '@angular/cdk/table'; import { Pipe, PipeTransform } from '@angular/core'; +import { MatSort } from '@angular/material/sort'; +import { MatTableDataSource } from '@angular/material/table'; import { components } from "src/atlasComponents/sapi/schemaV3" type DF = components["schemas"]["DataFrameModel"] @@ -19,11 +21,11 @@ function isDf(val: object): val is DF { }) export class DfToDsPipe implements PipeTransform { - transform(df: object): CdkTableDataSourceInput<unknown> { + transform(df: object, sort: MatSort): CdkTableDataSourceInput<unknown> { if (!isDf(df)) { return null } - return df.data.map((arr, idx) => { + const v = df.data.map((arr, idx) => { const val = df.index[idx] as any const returnVal: Record<string, string|number|number[]> = { index: val, @@ -37,6 +39,9 @@ export class DfToDsPipe implements PipeTransform { }) return returnVal }) + const ds = new MatTableDataSource(v) + ds.sort = sort + return ds } } diff --git a/src/util/directives/FixedMouseContextualContainerDirective.directive.spec.ts b/src/util/directives/FixedMouseContextualContainerDirective.directive.spec.ts deleted file mode 100644 index 0cf11aeb387d08a01a89f616e7095cc716fb63c5..0000000000000000000000000000000000000000 --- a/src/util/directives/FixedMouseContextualContainerDirective.directive.spec.ts +++ /dev/null @@ -1,131 +0,0 @@ -import { Component, ViewChild } from "@angular/core"; -import { TestBed } from "@angular/core/testing"; -import { FixedMouseContextualContainerDirective } from "./FixedMouseContextualContainerDirective.directive"; -import { By } from "@angular/platform-browser"; - -@Component({ - template: '' -}) - -class TestCmp{ - @ViewChild(FixedMouseContextualContainerDirective) directive: FixedMouseContextualContainerDirective -} - -describe('FixedMouseContextualContainerDirective', () => { - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [ - - ], - declarations: [ - TestCmp, - FixedMouseContextualContainerDirective - ] - }) - }) - - it('> can instantiate directive properly', () => { - TestBed.overrideComponent(TestCmp, { - set: { - template: ` - <div fixedMouseContextualContainerDirective> - </div> - ` - } - }).compileComponents() - - const fixture = TestBed.createComponent(TestCmp) - fixture.detectChanges() - const directive = fixture.debugElement.query( By.directive(FixedMouseContextualContainerDirective) ) - expect(directive).toBeTruthy() - - expect(fixture.componentInstance.directive).toBeTruthy() - }) - - describe('> hides if no content', () => { - it('> on #show, if content exists, isShown will be true', () => { - - TestBed.overrideComponent(TestCmp, { - set: { - template: ` - <div fixedMouseContextualContainerDirective> - <span>Hello World</span> - </div> - ` - } - }).compileComponents() - - const fixture = TestBed.createComponent(TestCmp) - fixture.detectChanges() - - const cmp = fixture.componentInstance - cmp.directive.show() - fixture.detectChanges() - expect(cmp.directive.isShown).toBeTrue() - }) - - it('> on #show, if only comment exists, isShown will be false', () => { - - TestBed.overrideComponent(TestCmp, { - set: { - template: ` - <div fixedMouseContextualContainerDirective> - <!-- hello world --> - </div> - ` - } - }).compileComponents() - - const fixture = TestBed.createComponent(TestCmp) - fixture.detectChanges() - - const cmp = fixture.componentInstance - cmp.directive.show() - fixture.detectChanges() - expect(cmp.directive.isShown).toBeFalse() - }) - - it('> on #show, if only text exists, isShown will be false', () => { - - TestBed.overrideComponent(TestCmp, { - set: { - template: ` - <div fixedMouseContextualContainerDirective> - hello world - </div> - ` - } - }).compileComponents() - - const fixture = TestBed.createComponent(TestCmp) - fixture.detectChanges() - - const cmp = fixture.componentInstance - cmp.directive.show() - fixture.detectChanges() - expect(cmp.directive.isShown).toBeFalse() - }) - - it('> on #show, if nothing exists, isShown will be false', () => { - - TestBed.overrideComponent(TestCmp, { - set: { - template: ` - <div fixedMouseContextualContainerDirective> - </div> - ` - } - }).compileComponents() - - const fixture = TestBed.createComponent(TestCmp) - fixture.detectChanges() - - const cmp = fixture.componentInstance - cmp.directive.show() - fixture.detectChanges() - expect(cmp.directive.isShown).toBeFalse() - }) - }) - - // TODO complete tests for FixedMouseContextualContainerDirective -}) \ No newline at end of file diff --git a/src/util/directives/FixedMouseContextualContainerDirective.directive.ts b/src/util/directives/FixedMouseContextualContainerDirective.directive.ts deleted file mode 100644 index d7fbf0905efa08166e8f864f45cf375a97950102..0000000000000000000000000000000000000000 --- a/src/util/directives/FixedMouseContextualContainerDirective.directive.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { Directive, ElementRef, EventEmitter, HostBinding, Input, Output, AfterContentChecked, ChangeDetectorRef, AfterViewInit } from "@angular/core"; - -@Directive({ - selector: '[fixedMouseContextualContainerDirective]', - exportAs: 'iavFixedMouseCtxContainer' -}) - -export class FixedMouseContextualContainerDirective implements AfterContentChecked { - - private defaultPos: [number, number] = [-1e3, -1e3] - public isShown: boolean = false - - @Input() - public mousePos: [number, number] = this.defaultPos - - @Output() - public onShow: EventEmitter<null> = new EventEmitter() - - @Output() - public onHide: EventEmitter<null> = new EventEmitter() - - constructor( - public el: ElementRef, - private cdr: ChangeDetectorRef, - ) { - } - - public recalculatePosition(){ - const clientWidth = this.el.nativeElement.clientWidth - const clientHeight = this.el.nativeElement.clientHeight - - const windowInnerWidth = window.innerWidth - const windowInnerHeight = window.innerHeight - if (windowInnerHeight - this.mousePos[1] < clientHeight) { - this.mousePos[1] = windowInnerHeight - clientHeight - } - - if ((windowInnerWidth - this.mousePos[0]) < clientWidth) { - this.mousePos[0] = windowInnerWidth - clientWidth - } - - this.transform = `translate(${this.mousePos.map(v => v.toString() + 'px').join(', ')})` - } - - ngAfterContentChecked(){ - if (this.el.nativeElement.childElementCount === 0) { - this.hide() - } - this.recalculatePosition() - this.cdr.markForCheck() - } - - public show() { - this.styleDisplay = 'inline-block' - this.isShown = true - this.onShow.emit() - } - - public hide() { - this.transform = `translate(${this.defaultPos.map(v => v.toString() + 'px').join(', ')})` - this.styleDisplay = 'none' - this.isShown = false - this.onHide.emit() - } - - @HostBinding('style.display') - public styleDisplay = `none` - - @HostBinding('style.transform') - public transform = `translate(${this.mousePos.map(v => v.toString() + 'px').join(', ')})` - -} diff --git a/src/viewerModule/viewerCmp/viewerCmp.template.html b/src/viewerModule/viewerCmp/viewerCmp.template.html index 0c80076b1593496a3f94cfb7892f312d192cad22..2ec0d95591e688c67eecc4ce5604cb70975182b0 100644 --- a/src/viewerModule/viewerCmp/viewerCmp.template.html +++ b/src/viewerModule/viewerCmp/viewerCmp.template.html @@ -905,62 +905,69 @@ <ng-template #viewerStatusCtxMenu let-data> <ng-template [ngIf]="data.context" let-context> - <mat-list> + <!-- ref space & position --> + <ng-container [ngSwitch]="context.viewerType"> - <!-- ref space & position --> - <ng-container [ngSwitch]="context.viewerType"> - - <!-- volumetric i.e. nehuba --> - <ng-container *ngSwitchCase="'nehuba'"> - <mat-list-item mat-ripple - (click)="selectPoint({ point: context.payload.mouse.real }, data.metadata.template)"> - <div mat-list-icon> - <i class="fas fa-map"></i> - </div> - - <div mat-line> + <!-- volumetric i.e. nehuba --> + <ng-container *ngSwitchCase="'nehuba'"> + <button mat-button class="sxplr-list-like-button" + (click)="selectPoint({ point: context.payload.mouse.real }, data.metadata.template)"> + + <div class="sxplr-list-like-button-icon"> + <i class="fas fa-map"></i> + </div> + + <div class="sxplr-list-like-button-body"> + + <span class="sxplr-list-like-button-body-line"> {{ context.payload.mouse.real | nmToMm | numbers | addUnitAndJoin : '' }} (mm) - </div> - <div mat-line class="text-muted"> + </span> + <span class="sxplr-list-like-button-body-line text-muted"> Point - </div> - <div mat-line class="text-muted"> + </span> + <span class="sxplr-list-like-button-body-line text-muted"> {{ data.metadata.template.name }} - </div> - </mat-list-item> - </ng-container> - - <ng-container *ngSwitchCase="'threeSurfer'"> - - <ng-template [ngIf]="context.payload?.faceIndex" let-faceIndex> - <ng-template [ngIf]="context.payload?.vertexIndices" let-vertexIndices> - <mat-list-item mat-ripple - (click)="selectPoint({ face: faceIndex, vertices: vertexIndices }, data.metadata.template)"> - - <div mat-list-icon> - <i class="fas fa-map"></i> - </div> - - <div mat-line> + </span> + </div> + + </button> + </ng-container> + + <ng-container *ngSwitchCase="'threeSurfer'"> + + <ng-template [ngIf]="context.payload?.faceIndex" let-faceIndex> + <ng-template [ngIf]="context.payload?.vertexIndices" let-vertexIndices> + <button mat-button class="sxplr-list-like-button" + (click)="selectPoint({ face: faceIndex, vertices: vertexIndices }, data.metadata.template)"> + + <div class="sxplr-list-like-button-icon"> + <i class="fas fa-map"></i> + </div> + + <div class="sxplr-list-like-button-body"> + + <span class="sxplr-list-like-button-body-line"> Face Index: {{ faceIndex }}, Vertices Index: {{ vertexIndices | addUnitAndJoin : '' }} - </div> - <div mat-line class="text-muted"> + </span> + <span class="sxplr-list-like-button-body-line text-muted"> Mesh Face - </div> - <div mat-line class="text-muted"> + </span> + <span class="sxplr-list-like-button-body-line text-muted"> {{ data.metadata.template.name }} - </div> - </mat-list-item> - </ng-template> + </span> + </div> + + </button> + </ng-template> - - </ng-container> - - <ng-container *ngSwitchDefault> - DEFAULT - </ng-container> + </ng-template> + + </ng-container> + + <ng-container *ngSwitchDefault> + DEFAULT </ng-container> - </mat-list> + </ng-container> </ng-template> </ng-template> @@ -968,41 +975,32 @@ <!-- viewer state hover ctx menu --> <ng-template #viewerStatusRegionCtxMenu let-data> <!-- hovered ROIs --> - <mat-list> - <ng-template ngFor [ngForOf]="data.metadata.hoveredRegions" - let-region - let-first="first"> + <ng-template ngFor [ngForOf]="data.metadata.hoveredRegions" + let-region + let-first="first"> - <mat-divider class="top-0" *ngIf="!first"></mat-divider> + <mat-divider class="top-0" *ngIf="!first"></mat-divider> - <mat-list-item mat-ripple - class="cursor-default" - (click)="$event.ctrlKey ? toggleRoi(region) : selectRoi(region)"> + <button mat-button + (click)="$event.ctrlKey ? toggleRoi(region) : selectRoi(region)" + class="sxplr-list-like-button"> + + <div class="sxplr-list-like-button-icon"> + <i class="fas fa-brain"></i> + </div> - <div mat-list-icon> - <i class="fas fa-brain"></i> - </div> + <div class="sxplr-list-like-button-body"> - <span mat-line> + <span class="sxplr-list-like-button-body-line"> {{ region.name }} </span> - <span mat-line class="text-muted"> - <span> - Brain region - </span> + <span class="sxplr-list-like-button-body-line text-muted"> + Brain region </span> - - <!-- lookup region --> - <!-- <button mat-icon-button - (click)="selectRoi(region)" - ctx-menu-dismiss> - <i class="fas fa-search"></i> - </button> --> - </mat-list-item> - + </div> + </button> - </ng-template> - </mat-list> + </ng-template> </ng-template> <!-- feature tmpls -->