diff --git a/.github/workflows/deploy-helm.yml b/.github/workflows/deploy-helm.yml index fe8c4c1449066a3911b34c182508b7ef6a9f4aab..09b6a70f7f9fb4d374b3ea73d8b9506452ca1e7c 100644 --- a/.github/workflows/deploy-helm.yml +++ b/.github/workflows/deploy-helm.yml @@ -70,6 +70,7 @@ jobs: --set image.tag=${{ inputs.IMAGE_TAG }} \ --set podLabels.image-digest=${{ inputs.IMAGE_DIGEST }} \ --set envObj.HOST_PATHNAME=/viewer-staging \ + --set envObj.OVERWRITE_API_ENDPOINT="https://siibra-api-rc.apps.tc.humanbrainproject.eu/v3_0" \ ${{ inputs.DEPLOYMENT_NAME }} .helm/siibra-explorer/ rm $kubecfg_path @@ -91,6 +92,7 @@ jobs: --reuse-values \ --set image.tag=${{ inputs.IMAGE_TAG }} \ --set podLabels.image-digest=${{ inputs.IMAGE_DIGEST }} \ + --set envObj.OVERWRITE_API_ENDPOINT="https://siibra-api-rc.apps.tc.humanbrainproject.eu/v3_0" \ ${{ inputs.DEPLOYMENT_NAME }} .helm/siibra-explorer/ \ || helm --kubeconfig=$kubecfg_path \ install\ diff --git a/backend/app/config.py b/backend/app/config.py index 42ddbaace8377c189451d9a8dd61448ee5a8bf52..54cac5d1355aa6eac921bbf3a6564be81533b39b 100644 --- a/backend/app/config.py +++ b/backend/app/config.py @@ -8,8 +8,6 @@ OVERWRITE_API_ENDPOINT = os.getenv("OVERWRITE_API_ENDPOINT") OVERWRITE_SPATIAL_ENDPOINT = os.getenv("OVERWRITE_SPATIAL_ENDPOINT") -EXPERIMENTAL_FLAG = os.getenv("EXPERIMENTAL_FLAG") - LOCAL_CDN = os.getenv("LOCAL_CDN") HBP_CLIENTID_V2 = os.getenv("HBP_CLIENTID_V2", "no hbp id") diff --git a/backend/app/const.py b/backend/app/const.py index 071545deeafd17bb32b4e7ed1b89660f04483491..75a3094acc21460b1c293f19a27f86b10769a790 100644 --- a/backend/app/const.py +++ b/backend/app/const.py @@ -22,4 +22,3 @@ OVERWRITE_SAPI_ENDPOINT_ATTR = "x-sapi-base-url" OVERWRITE_SPATIAL_BACKEND_ATTR = "x-spatial-backend-url" -OVERWRITE_EXPERIMENTAL_FLAG_ATTR = "x-experimental-flag" diff --git a/backend/app/index_html.py b/backend/app/index_html.py index c054d76ca6c23eae8c06446d5d009c05c2e526f6..339a69efaf7bccd99bb9164ce70d5f4c8520df73 100644 --- a/backend/app/index_html.py +++ b/backend/app/index_html.py @@ -2,8 +2,8 @@ from fastapi import APIRouter, Request from pathlib import Path from fastapi.responses import Response from typing import Dict -from .const import ERROR_KEY, DATA_ERROR_ATTR, OVERWRITE_SAPI_ENDPOINT_ATTR, COOKIE_KWARGS, OVERWRITE_SPATIAL_BACKEND_ATTR, OVERWRITE_EXPERIMENTAL_FLAG_ATTR -from .config import PATH_TO_PUBLIC, OVERWRITE_API_ENDPOINT, OVERWRITE_SPATIAL_ENDPOINT, EXPERIMENTAL_FLAG +from .const import ERROR_KEY, DATA_ERROR_ATTR, OVERWRITE_SAPI_ENDPOINT_ATTR, COOKIE_KWARGS, OVERWRITE_SPATIAL_BACKEND_ATTR +from .config import PATH_TO_PUBLIC, OVERWRITE_API_ENDPOINT, OVERWRITE_SPATIAL_ENDPOINT path_to_index = Path(PATH_TO_PUBLIC) / "index.html" index_html: str = None @@ -35,9 +35,6 @@ async def get_index_html(request: Request): if OVERWRITE_SPATIAL_ENDPOINT: attributes_to_append[OVERWRITE_SPATIAL_BACKEND_ATTR] = OVERWRITE_SPATIAL_ENDPOINT - if EXPERIMENTAL_FLAG: - attributes_to_append[OVERWRITE_EXPERIMENTAL_FLAG_ATTR] = EXPERIMENTAL_FLAG - attr_string = " ".join([f'{key}="{_monkey_sanitize(value)}"' for key, value in attributes_to_append.items()]) resp_string = index_html.replace("<atlas-viewer>", f"<atlas-viewer {attr_string}>") diff --git a/common/constants.js b/common/constants.js index 0e9f6b3126d80bd061363c7c02fcc9c1e1034275..4ddf697531624b02c7cb8934dd2a9d2cb348859c 100644 --- a/common/constants.js +++ b/common/constants.js @@ -151,7 +151,6 @@ If you do not accept the Terms & Conditions you are not permitted to access or u OVERWRITE_SAPI_ENDPOINT_ATTR: `x-sapi-base-url`, OVERWRITE_SPATIAL_BACKEND_ATTR: `x-spatial-backend-url`, - OVERWRITE_EXPERIMENTAL_FLAG_ATTR: `x-experimental-flag`, DATA_ERROR_ATTR: `data-error`, } diff --git a/docs/releases/v2.14.10.md b/docs/releases/v2.14.10.md new file mode 100644 index 0000000000000000000000000000000000000000..4b9bccc5bd0430b8238718aa8d6f4747b22b7537 --- /dev/null +++ b/docs/releases/v2.14.10.md @@ -0,0 +1,12 @@ +# v2.14.10 + +## Bugfix + +- Fix deployment scripts +- Fix region update to updated siibra-api endpoint + +## Behind the scenes + +- Re-enable siibra-api warning mismatch warning +- Adapt to siibra-api v0.3.18 point assignment rhetoric +- Enable cheat code, removing obsolete experimental flags diff --git a/mkdocs.yml b/mkdocs.yml index e234072162630b9f6bba602d5360eff08166180d..e867e6f3ead12d0a4d60fcb63c98b7df7b506b5d 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -45,6 +45,7 @@ nav: - Differential gene expression analysis: "advanced/differential_gene_expression_analysis.md" - Release notes: + - v2.14.10: 'releases/v2.14.10.md' - v2.14.9: 'releases/v2.14.9.md' - v2.14.8: 'releases/v2.14.8.md' - v2.14.7: 'releases/v2.14.7.md' diff --git a/package.json b/package.json index 0d8728b4facb97c0a093a25cbaed83ad44525130..3a703dce6e29b924eacec330213646e06da29971 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "siibra-explorer", - "version": "2.14.9", + "version": "2.14.10", "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/sapi/sapi.service.ts b/src/atlasComponents/sapi/sapi.service.ts index 0d35f90f5885aada182158af33c0fd0df16950d4..f191d042fd3e1782c7c0fd0b0af4bbe1e94f5944 100644 --- a/src/atlasComponents/sapi/sapi.service.ts +++ b/src/atlasComponents/sapi/sapi.service.ts @@ -20,7 +20,7 @@ export const useViewer = { } as const export const SIIBRA_API_VERSION_HEADER_KEY='x-siibra-api-version' -export const EXPECTED_SIIBRA_API_VERSION = '0.3.16' +export const EXPECTED_SIIBRA_API_VERSION = '0.3.18' type PaginatedResponse<T> = { items: T[] @@ -286,11 +286,10 @@ export class SAPI{ tap(() => { const respVersion = SAPI.API_VERSION if (respVersion !== EXPECTED_SIIBRA_API_VERSION) { - // TODO temporarily disable snackbar. Enable once siibra-api version stabilises console.log(`Expecting ${EXPECTED_SIIBRA_API_VERSION}, got ${respVersion}. Some functionalities may not work as expected.`) - // this.snackbar.open(`Expecting ${EXPECTED_SIIBRA_API_VERSION}, got ${respVersion}. Some functionalities may not work as expected.`, 'Dismiss', { - // duration: 5000 - // }) + this.snackbar.open(`Expecting ${EXPECTED_SIIBRA_API_VERSION}, got ${respVersion}. Some functionalities may not work as expected.`, 'Dismiss', { + duration: 5000 + }) } }), shareReplay(1), diff --git a/src/atlasComponents/sapi/translateV3.ts b/src/atlasComponents/sapi/translateV3.ts index ff48d7125bd162b1a573f09ab9a4a15ea9b5f431..9c6f8c5c82dc542333ad2b8c7e64f6fd4ea09d69 100644 --- a/src/atlasComponents/sapi/translateV3.ts +++ b/src/atlasComponents/sapi/translateV3.ts @@ -272,7 +272,7 @@ class TranslateV3 { id: region["@id"], name: region.name, color: hexToRgb(region.hasAnnotation?.displayColor) as [number, number, number], - parentIds: region.hasParent.map( v => v["@id"] ), + parentIds: (region.hasParent || []).map( v => v["@id"] ), type: "SxplrRegion", centroid: bestViewPoint ? { @@ -771,3 +771,34 @@ class TranslateV3 { } export const translateV3Entities = new TranslateV3() + + +// TODO +// >= 0.3.18 siibra-api /maps endpoint populates *both* full region name as well as short names +// This is a side effect of mixing both versions of siibra-python. +// and expected to end at >= 0.4. By then, restore this warning for debugging purposes + +const REMOVE_FROM_NAMES = [ + "hemisphere", + " -", + "-brain", + "both", + "Both", +] +const REPLACE_IN_NAME = { + "ctx-lh-": "left ", + "ctx-rh-": "right ", +} + +export function translateRegionName(fullRegionName: string): string { + let returnName = fullRegionName + for (const rm of REMOVE_FROM_NAMES) { + returnName = returnName.replace(rm , "") + } + for (const key in REPLACE_IN_NAME){ + returnName = returnName.replace(key, REPLACE_IN_NAME[key]) + } + return returnName.trim() +} + +// end TODO 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 b77d7965588f208b3ee80173e4804be2e170a479..31b58dd51a1f8f6be84527b55da266a4f76480e5 100644 --- a/src/atlasComponents/sapiViews/volumes/point-assignment/point-assignment.component.html +++ b/src/atlasComponents/sapiViews/volumes/point-assignment/point-assignment.component.html @@ -74,7 +74,7 @@ <!-- {{ element | json }} --> <button mat-button (click)="selectRegion(element['region'], $event)" class="ws-no-wrap"> - {{ element['region'].name }} + {{ element['region'] }} </button> </td> </ng-container> @@ -83,7 +83,7 @@ map value </th> <td mat-cell *matCellDef="let element"> - {{ element['map value'] | prettyPresent }} + {{ element['map_value'] | prettyPresent }} </td> </ng-container> @@ -108,7 +108,7 @@ <td mat-cell *matCellDef="let element"> <button mat-button (click)="selectRegion(element['region'], $event)" class="ws-no-wrap"> - {{ element['region'].name }} + {{ element['region'] }} </button> </td> </ng-container> 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 e7eec1fe091a40eb5fc4706da66b364527bf1469..b901c539eba4807a967d934d9425158a66ecda73 100644 --- a/src/atlasComponents/sapiViews/volumes/point-assignment/point-assignment.component.ts +++ b/src/atlasComponents/sapiViews/volumes/point-assignment/point-assignment.component.ts @@ -3,8 +3,8 @@ import { Clipboard, MatDialog, MatDialogRef, MatSnackBar } from 'src/sharedModul import { BehaviorSubject, EMPTY, Observable, Subscription, combineLatest, concat, of } from 'rxjs'; import { catchError, map, shareReplay, switchMap, tap } from 'rxjs/operators'; 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 { SxplrParcellation, SxplrTemplate } from 'src/atlasComponents/sapi/sxplrTypes'; +import { translateRegionName } from 'src/atlasComponents/sapi/translateV3'; import { PathReturn } from 'src/atlasComponents/sapi/typeV3'; import { TFace, TSandsPoint } from 'src/util/types'; import { TZipFileConfig } from "src/zipFilesOutput/type" @@ -56,7 +56,7 @@ export class PointAssignmentComponent implements OnDestroy { } @Output() - clickOnRegion = new EventEmitter<{ target: SxplrRegion, event: MouseEvent }>() + clickOnRegionName = new EventEmitter<{ target: string, event: MouseEvent }>() df$: Observable<PathReturn<"/map/assign">> = combineLatest([ this.point$, @@ -131,9 +131,8 @@ export class PointAssignmentComponent implements OnDestroy { ngOnDestroy(): void { while (this.#sub.length > 0) this.#sub.pop().unsubscribe() } - async selectRegion(region: PathReturn<"/regions/{region_id}">, event: MouseEvent){ - const sxplrReg = await translateV3Entities.translateRegion(region) - this.clickOnRegion.emit({ target: sxplrReg, event }) + selectRegion(regionName: string, event: MouseEvent){ + this.clickOnRegionName.emit({ target: translateRegionName(regionName), event }) if (this.#dialogRef) { this.#dialogRef.close() } diff --git a/src/experimental/experimental.module.ts b/src/experimental/experimental.module.ts new file mode 100644 index 0000000000000000000000000000000000000000..d7682ef0da1d078b9979b5611a47e2189e7eadf4 --- /dev/null +++ b/src/experimental/experimental.module.ts @@ -0,0 +1,73 @@ +import { DOCUMENT } from "@angular/common"; +import { APP_INITIALIZER, InjectionToken, NgModule } from "@angular/core"; +import { BehaviorSubject, fromEvent, Observable } from "rxjs"; +import { filter, scan, take } from "rxjs/operators"; + +const CODE_DICT = { + ArrowUp: "ArrowUp", + ArrowDown: "ArrowDown", + ArrowLeft: "ArrowLeft", + ArrowRight: "ArrowRight", + b: "b", + a: "a", +} as const + +const CODE = [ + CODE_DICT.ArrowUp, + CODE_DICT.ArrowUp, + CODE_DICT.ArrowDown, + CODE_DICT.ArrowDown, + CODE_DICT.ArrowLeft, + CODE_DICT.ArrowRight, + CODE_DICT.ArrowLeft, + CODE_DICT.ArrowRight, + CODE_DICT.b, + CODE_DICT.a, +] + +function isKCode(input: string): input is (keyof typeof CODE_DICT) { + return input in CODE_DICT +} + +function isNextKCode(current: (keyof typeof CODE_DICT)[], input: keyof typeof CODE_DICT): boolean { + return CODE[current.length] === input +} + +export const SHOW_EXPERIMENTAL_TOKEN = new InjectionToken<Observable<boolean>>("SHOW_EXPERIMENTAL_TOKEN") + +const showXmptToggle = new BehaviorSubject<boolean>(false) + +@NgModule({ + providers: [ + { + provide: APP_INITIALIZER, + useFactory: (document: Document) => { + fromEvent(document, "keydown", { capture: true }).pipe( + scan((acc, curr: KeyboardEvent) => { + const key = curr.key + if (!isKCode(key)) { + return [] + } + if (!isNextKCode(acc, key)) { + return [] + } + return [...acc, key] + }, [] as (keyof typeof CODE_DICT)[]), + filter(code => code.length === CODE.length), + take(1) + ).subscribe(() => { + showXmptToggle.next(true) + }) + return () => Promise.resolve() + }, + multi: true, + deps: [DOCUMENT] + }, + { + provide: SHOW_EXPERIMENTAL_TOKEN, + useValue: showXmptToggle.asObservable() + } + ] +}) + +export class KCodeModule{} diff --git a/src/main.module.ts b/src/main.module.ts index aedbee438fac95a83a983c2704b2a7cee7bdb00d..2c59072d7fe0b9382192388795bdb1e927cfbd17 100644 --- a/src/main.module.ts +++ b/src/main.module.ts @@ -40,7 +40,6 @@ import { atlasSelection, RootStoreModule, getStoreEffects, - userPreference, } from "./state" import { DARKTHEME } from './util/injectionTokens'; import { map } from 'rxjs/operators'; @@ -54,6 +53,7 @@ import { ViewerCommonEffects } from './viewerModule'; import { environment } from './environments/environment'; import { SAPI } from './atlasComponents/sapi'; import { GET_ATTR_TOKEN, GetAttr } from './util/constants'; +import { KCodeModule } from "./experimental/experimental.module" @NgModule({ imports: [ @@ -85,6 +85,7 @@ import { GET_ATTR_TOKEN, GetAttr } from './util/constants'; ]), RootStoreModule, HttpClientModule, + KCodeModule, ], declarations: [ AtlasViewer, @@ -176,17 +177,12 @@ import { GET_ATTR_TOKEN, GetAttr } from './util/constants'; }, { provide: APP_INITIALIZER, - useFactory: (authSvc: AuthService, store: Store) => { - window['setExperimentalFlag'] = (flag: boolean) => { - store.dispatch(userPreference.actions.setShowExperimental({ - flag - })) - } + useFactory: (authSvc: AuthService) => { authSvc.authReloadState() return () => Promise.resolve() }, multi: true, - deps: [ AuthService, Store ] + deps: [ AuthService ] }, { provide: GET_ATTR_TOKEN, diff --git a/src/ui/topMenu/topMenuCmp/topMenu.components.ts b/src/ui/topMenu/topMenuCmp/topMenu.components.ts index 4adf087feaf37161a32e0a426fcc0ffa0b39ab92..1239ed800044b8d71cda955965ab3b8d93be01c2 100644 --- a/src/ui/topMenu/topMenuCmp/topMenu.components.ts +++ b/src/ui/topMenu/topMenuCmp/topMenu.components.ts @@ -3,9 +3,10 @@ import { Component, Inject, Input, + Optional, TemplateRef, } from "@angular/core"; -import { Observable } from "rxjs"; +import { Observable, of } from "rxjs"; import { map } from "rxjs/operators"; import { AuthService } from "src/auth"; import { MatBottomSheet, MatDialog, MatDialogConfig, MatDialogRef } from 'src/sharedModules/angularMaterial.exports' @@ -15,7 +16,7 @@ import { TypeMatBtnColor, TypeMatBtnStyle } from "src/components/dynamicMaterial import { select, Store } from "@ngrx/store"; import { userPreference } from "src/state"; import { environment } from "src/environments/environment" -import { GET_ATTR_TOKEN, GetAttr } from "src/util/constants"; +import { SHOW_EXPERIMENTAL_TOKEN } from "src/experimental/experimental.module"; @Component({ selector: 'top-menu-cmp', @@ -28,7 +29,10 @@ import { GET_ATTR_TOKEN, GetAttr } from "src/util/constants"; export class TopMenuCmp { - public showExperimentalToggle = environment.EXPERIMENTAL_FEATURE_FLAG + public showExptToggle$ = environment.EXPERIMENTAL_FEATURE_FLAG + ? of (true) + : (this.showXplrToggle$ || of(false)) + setExperimentalFlag(flag: boolean){ this.store.dispatch( userPreference.actions.setShowExperimental({ @@ -86,15 +90,10 @@ export class TopMenuCmp { private authService: AuthService, private dialog: MatDialog, public bottomSheet: MatBottomSheet, - @Inject(GET_ATTR_TOKEN) getAttr: GetAttr + @Optional() @Inject(SHOW_EXPERIMENTAL_TOKEN) private showXplrToggle$: Observable<boolean>, ) { this.user$ = this.authService.user$ - const experimentalFlag = getAttr(CONST.OVERWRITE_EXPERIMENTAL_FLAG_ATTR) - if (experimentalFlag) { - this.showExperimentalToggle = !!experimentalFlag - } - this.userBtnTooltip$ = this.user$.pipe( map(user => user ? `Logged in as ${(user && user.name) ? user.name : 'Unknown name'}` diff --git a/src/ui/topMenu/topMenuCmp/topMenu.template.html b/src/ui/topMenu/topMenuCmp/topMenu.template.html index 43d695b912ebc5d1299788cc044c52925d8d8ff1..3334ea58e4d0bdc7621689acb666d7ff5e36bbd7 100644 --- a/src/ui/topMenu/topMenuCmp/topMenu.template.html +++ b/src/ui/topMenu/topMenuCmp/topMenu.template.html @@ -159,7 +159,7 @@ </div> </ng-template> - <ng-template [ngIf]="showExperimentalToggle"> + <ng-template [ngIf]="showExptToggle$ | async"> <ng-template [ngTemplateOutlet]="toggleBtnTmpl" [ngTemplateOutletContext]="{$implicit: experimentalFlag$ | async}"> </ng-template> diff --git a/src/viewerModule/nehuba/base.service/base.service.ts b/src/viewerModule/nehuba/base.service/base.service.ts index a6bfe046ab51e81f2d84db4930e9747d22f0388d..5f825da7c02c845fa26e4526a40cbde8d0988239 100644 --- a/src/viewerModule/nehuba/base.service/base.service.ts +++ b/src/viewerModule/nehuba/base.service/base.service.ts @@ -49,7 +49,12 @@ export class BaseService { for (const { name, label } of region) { const actualRegion = regionmap.get(name) || (() => { - console.log(`region with name ${name} cannot be found. Viewer may not behave properly`) + // TODO + // >= 0.3.18 siibra-api /maps endpoint populates *both* full region name as well as short names + // This is a side effect of mixing both versions of siibra-python. + // and expected to end at >= 0.4. By then, restore this warning for debugging purposes + + // console.log(`region with name ${name} cannot be found. Viewer may not behave properly`) return { name, id: '', parentIds: [], type: 'SxplrRegion' } })() const ngId = getParcNgId(atlas, template, parcellation, actualRegion) diff --git a/src/viewerModule/viewerCmp/viewerCmp.component.ts b/src/viewerModule/viewerCmp/viewerCmp.component.ts index ac84f5c6e75e32956314bcb8537a7e3391edcbf1..35639ce121190f73f8531b5e3c86da16639dac34 100644 --- a/src/viewerModule/viewerCmp/viewerCmp.component.ts +++ b/src/viewerModule/viewerCmp/viewerCmp.component.ts @@ -1,7 +1,7 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, TemplateRef, ViewChild, inject } from "@angular/core"; import { select, Store } from "@ngrx/store"; import { BehaviorSubject, combineLatest, Observable, of } from "rxjs"; -import { debounceTime, distinctUntilChanged, map, shareReplay, switchMap, takeUntil } from "rxjs/operators"; +import { debounceTime, distinctUntilChanged, map, shareReplay, switchMap, take, takeUntil } from "rxjs/operators"; import { CONST, ARIA_LABELS, QUICKTOUR_DESC } from 'common/constants' import { animate, state, style, transition, trigger } from "@angular/animations"; import { IQuickTourData } from "src/ui/quickTour"; @@ -16,6 +16,7 @@ import { EntryComponent } from "src/features/entry/entry.component"; import { TFace, TSandsPoint, getCoord } from "src/util/types"; import { wait } from "src/util/fn"; import { DestroyDirective } from "src/util/directives/destroy.directive"; +import { generalActionError } from "src/state/actions"; interface HasName { name: string @@ -542,6 +543,28 @@ export class ViewerCmp { this.controlHalfNav(false) } + async handleClickOnRegionName(regionName: string, event: MouseEvent){ + const regions = await this.store$.pipe( + select(atlasSelection.selectors.selectedParcAllRegions), + take(1) + ).toPromise() + + const foundRegion = regions.find(r => r.name === regionName) + if (!foundRegion) { + this.store$.dispatch( + generalActionError({ + message: `Region with name ${regionName} not found.` + }) + ) + return + } + if (event.ctrlKey) { + this.toggleRoi(foundRegion) + } else { + this.selectRoi(foundRegion) + } + } + nameEql(a: HasName, b: HasName){ return a.name === b.name } diff --git a/src/viewerModule/viewerCmp/viewerCmp.template.html b/src/viewerModule/viewerCmp/viewerCmp.template.html index b837c843a4ef310b2af85cc27c52f8ccbd5763b1..fcd679e41f36335023dd561b6d6a98c1b9d67ff4 100644 --- a/src/viewerModule/viewerCmp/viewerCmp.template.html +++ b/src/viewerModule/viewerCmp/viewerCmp.template.html @@ -861,7 +861,7 @@ [point]="view.selectedPoint" [template]="view.selectedTemplate" [parcellation]="view.selectedParcellation" - (clickOnRegion)="$event.event.ctrlKey ? toggleRoi($event.target) : selectRoi($event.target)"> + (clickOnRegionName)="handleClickOnRegionName($event.target, $event.event)"> </sxplr-point-assignment> </ng-template>