diff --git a/deploy/datasets/query.js b/deploy/datasets/query.js index eb5571d757efd013f5372f0fd50961c147eed5ee..fc97e10e70a590a3fdcb982ddfc893915c9fa140 100644 --- a/deploy/datasets/query.js +++ b/deploy/datasets/query.js @@ -4,26 +4,31 @@ const URL = require('url') const path = require('path') const archiver = require('archiver') const { getPreviewFile, hasPreview } = require('./supplements/previewFile') -const { init: kgQueryUtilInit, getUserKGRequestParam, filterDatasets } = require('./util') +const { constants, init: kgQueryUtilInit, getUserKGRequestParam, filterDatasets } = require('./util') let cachedData = null let otherQueryResult = null -const KG_ROOT = process.env.KG_ROOT || `https://kg.humanbrainproject.eu` -const KG_PATH = process.env.KG_PATH || `/query/minds/core/dataset/v1.0.0/interactiveViewerKgQuery-v0_3` +const { KG_ROOT, KG_SEARCH_VOCAB } = constants + +const KG_DATASET_SEARCH_QUERY_NAME = process.env.KG_DATASET_SEARCH_QUERY_NAME || 'interactiveViewerKgQuery-v0_3' +const KG_DATASET_SEARCH_PATH = process.env.KG_DATASET_SEARCH_PATH || '/minds/core/dataset/v1.0.0' + +const kgDatasetSearchFullString = `${KG_DATASET_SEARCH_PATH}/${KG_DATASET_SEARCH_QUERY_NAME}` + const KG_PARAM = { size: process.env.KG_SEARCH_SIZE || '1000', - vocab: process.env.KG_SEARCH_VOCAB || 'https://schema.hbp.eu/myQuery/' + vocab: KG_SEARCH_VOCAB } -const KG_QUERY_DATASETS_URL = new URL.URL(`${KG_ROOT}${KG_PATH}/instances`) +const KG_QUERY_DATASETS_URL = new URL.URL(`${KG_ROOT}${kgDatasetSearchFullString}/instances`) for (let key in KG_PARAM) { KG_QUERY_DATASETS_URL.searchParams.set(key, KG_PARAM[key]) } const getKgQuerySingleDatasetUrl = ({ kgId }) => { const _newUrl = new URL.URL(KG_QUERY_DATASETS_URL) - _newUrl.pathname = `${KG_PATH}/instances/${kgId}` + _newUrl.pathname = `${_newUrl.pathname}/${kgId}` return _newUrl } diff --git a/deploy/datasets/spatialQuery.js b/deploy/datasets/spatialQuery.js index 5561e6098a7f2859ddc7a7ad5edef3c215a8bf9c..11eec98d36a90bb53439f4be2f10b0fd6be95eff 100644 --- a/deploy/datasets/spatialQuery.js +++ b/deploy/datasets/spatialQuery.js @@ -1,12 +1,24 @@ const url = require('url') const request = require('request') -const { getUserKGRequestParam } = require('./util') +const { getUserKGRequestParam, constants } = require('./util') const { transformWaxholmV2NmToVoxel, transformWaxholmV2VoxelToNm } = require('./spatialXform/waxholmRat') /** * TODO change to URL constructor to improve readability */ -const spatialQuery = 'https://kg.humanbrainproject.eu/query/neuroglancer/seeg/coordinate/v1.0.0/spatialWithCoordinatesNG/instances?vocab=https%3A%2F%2Fschema.hbp.eu%2FmyQuery%2F' + +const { KG_ROOT, KG_SEARCH_VOCAB } = constants + +const kgParams = { + vocab: KG_SEARCH_VOCAB +} + +const KG_SPATIAL_DATASET_SEARCH_QUERY_NAME = process.env.KG_SPATIAL_DATASET_SEARCH_QUERY_NAME || 'iav-spatial-query-v2' +const KG_SPATIAL_DATASET_SEARCH_PATH = process.env.KG_SPATIAL_DATASET_SEARCH_PATH || '/neuroglancer/seeg/coordinate/v1.0.0' + +const kgSpatialDatasetSearchFullString = `${KG_SPATIAL_DATASET_SEARCH_PATH}/${KG_SPATIAL_DATASET_SEARCH_QUERY_NAME}` + +const kgQuerySpatialDatasetUrl = new url.URL(`${KG_ROOT}${kgSpatialDatasetSearchFullString}/instances`) const defaultXform = (coord) => coord @@ -51,30 +63,29 @@ const fetchSpatialDataFromKg = async ({ templateName, queryGeometry, queryArg, u const { releasedOnly, option } = await getUserKGRequestParam({ user }) const _ = getSpatialSearcParam({ templateName, queryGeometry, queryArg }) - const search = new url.URLSearchParams() + const _url = new url.URL(kgQuerySpatialDatasetUrl) + + for (const key in kgParams){ + _url.searchParams.append(key, kgParams[key]) + } for (let key in _) { - search.append(key, _[key]) + _url.searchParams.append(key, _[key]) } - if (releasedOnly) search.append('databaseScope', 'RELEASED') - - const _url = `${spatialQuery}&${search.toString()}` + if (releasedOnly) _url.searchParams.append('databaseScope', 'RELEASED') + return await new Promise((resolve, reject) => { request(_url, option, (err, resp, body) => { - if (err) - return reject(err) + if (err) return reject(err) if (resp.statusCode >= 400) { return reject(resp.statusCode) } const json = JSON.parse(body) const { voxelToNm } = getXformFn(templateName) - - const _ = json.results.map(({ name, coordinates, dataset}) => { + const _ = json.results.map(({ coordinates, ...rest}) => { return { - name, - templateSpace: templateName, - dataset: dataset.map(ds => ds = {name: ds.name, description: ds.description, externalLink: 'https://kg.ebrains.eu/instances/Dataset/' + ds.identifier, embargoStatus: ds['embargo_status']}), + ...rest, geometry: { type: 'point', space: 'real', diff --git a/deploy/datasets/util.js b/deploy/datasets/util.js index bda610f0bdb7f4e6ec89146dc2c9e1333f7d4f10..98fb212d307361f9b1d85c35072009aac378bd6c 100644 --- a/deploy/datasets/util.js +++ b/deploy/datasets/util.js @@ -308,6 +308,9 @@ const retry = async (fn, { timeout = defaultConfig.timeout, retries = defaultCon throw new Error(`fn failed ${retries} times. Aborting.`) } +const KG_ROOT = process.env.KG_ROOT || `https://kg.humanbrainproject.eu/query` +const KG_SEARCH_VOCAB = process.env.KG_SEARCH_VOCAB || 'https://schema.hbp.eu/myQuery/' + module.exports = { init, getUserKGRequestParam, @@ -328,5 +331,9 @@ module.exports = { allen2015Set, allen2017Set } + }, + constants: { + KG_ROOT, + KG_SEARCH_VOCAB } } \ No newline at end of file diff --git a/src/atlasViewer/atlasViewer.component.ts b/src/atlasViewer/atlasViewer.component.ts index afff14d6d590c696f586ada3844a670fe477d775..3dcb365b3f578d52eb83be5b462fabdc23dfe487 100644 --- a/src/atlasViewer/atlasViewer.component.ts +++ b/src/atlasViewer/atlasViewer.component.ts @@ -41,7 +41,7 @@ import { FixedMouseContextualContainerDirective } from "src/util/directives/Fixe import { getViewer, isSame } from "src/util/fn"; import { NehubaContainer } from "../ui/nehubaContainer/nehubaContainer.component"; import { colorAnimation } from "./atlasViewer.animation" -import {SingleDatasetView} from "src/ui/databrowserModule/singleDataset/detailedView/singleDataset.component"; +import { MouseHoverDirective } from "src/util/directives/mouseOver.directive"; /** * TODO @@ -72,6 +72,7 @@ export class AtlasViewer implements OnDestroy, OnInit, AfterViewInit { @ViewChild(NehubaContainer) public nehubaContainer: NehubaContainer @ViewChild(FixedMouseContextualContainerDirective) public rClContextualMenu: FixedMouseContextualContainerDirective + @ViewChild(MouseHoverDirective) private mouseOverNehuba: MouseHoverDirective @ViewChild('mobileMenuTabs') public mobileMenuTabs: TabsetComponent @@ -122,11 +123,9 @@ export class AtlasViewer implements OnDestroy, OnInit, AfterViewInit { private pluginRegionSelectionEnabled$: Observable<boolean> private pluginRegionSelectionEnabled: boolean = false - private persistentStateNotifierTemplate$: Observable<string> - // private pluginRegionSelectionEnabled: boolean = false + public persistentStateNotifierTemplate$: Observable<string> private hoveringRegions = [] - private hoveringLandmark: any public presentDatasetDialogRef: MatDialogRef<any> constructor( @@ -214,6 +213,7 @@ export class AtlasViewer implements OnDestroy, OnInit, AfterViewInit { distinctUntilChanged(isSame), ) + // TODO deprecate this.dedicatedView$ = this.store.pipe( select('viewerState'), filter(state => isDefined(state) && typeof state.dedicatedView !== 'undefined'), @@ -221,48 +221,18 @@ export class AtlasViewer implements OnDestroy, OnInit, AfterViewInit { distinctUntilChanged(), ) - this.onhoverLandmark$ = combineLatest( - this.store.pipe( - select('uiState'), - map(state => state.mouseOverLandmark), - ), - this.store.pipe( - select('dataStore'), - safeFilter('fetchedSpatialData'), - map(state => state.fetchedSpatialData), - ), - ).pipe( - map(([landmark, spatialDatas]) => { - if (landmark === null) { - return landmark - } - const idx = Number(landmark.replace('label=', '')) - if (isNaN(idx)) { - this.log.warn(`Landmark index could not be parsed as a number: ${landmark}`) - return { - landmarkName: idx, - } - } else { - return { - landmarkName: spatialDatas[idx].name, - landmarkDataset: spatialDatas[idx].dataset - } - } - }), - ) - // TODO temporary hack. even though the front octant is hidden, it seems if a mesh is present, hover will select the said mesh - this.onhoverSegments$ = combineLatest( - this.store.pipe( - select('uiState'), - select('mouseOverSegments'), - filter(v => !!v), - distinctUntilChanged((o, n) => o.length === n.length && n.every(segment => o.find(oSegment => oSegment.layer.name === segment.layer.name && oSegment.segment === segment.segment) ) ), - /* cannot filter by state, as the template expects a default value, or it will throw ExpressionChangedAfterItHasBeenCheckedError */ + this.onhoverSegments$ = this.store.pipe( + select('uiState'), + select('mouseOverSegments'), + filter(v => !!v), + distinctUntilChanged((o, n) => o.length === n.length && n.every(segment => o.find(oSegment => oSegment.layer.name === segment.layer.name && oSegment.segment === segment.segment) ) ), + /* cannot filter by state, as the template expects a default value, or it will throw ExpressionChangedAfterItHasBeenCheckedError */ - ), - this.onhoverLandmark$, ).pipe( + withLatestFrom( + this.onhoverLandmark$ || of(null) + ), map(([segments, onhoverLandmark]) => onhoverLandmark ? null : segments ), map(segments => { if (!segments) { return null } @@ -307,9 +277,6 @@ export class AtlasViewer implements OnDestroy, OnInit, AfterViewInit { this.subscriptions.push( this.onhoverSegments$.subscribe(hr => { this.hoveringRegions = hr - }), - this.onhoverLandmark$.subscribe(hl => { - this.hoveringLandmark = hl }) ) } @@ -417,6 +384,10 @@ export class AtlasViewer implements OnDestroy, OnInit, AfterViewInit { this.rd.appendChild(document.head, prefecthMainBundle) } + this.onhoverLandmark$ = this.mouseOverNehuba.currentOnHoverObs$.pipe( + select('landmark') + ) + /** * Show Cookie disclaimer if not yet agreed */ @@ -451,8 +422,10 @@ export class AtlasViewer implements OnDestroy, OnInit, AfterViewInit { ) this.onhoverLandmarkForFixed$ = this.rClContextualMenu.onShow.pipe( - withLatestFrom(this.onhoverLandmark$), - map(([_flag, onhoverLandmark]) => onhoverLandmark || []), + withLatestFrom( + this.onhoverLandmark$ || of(null) + ), + map(([_flag, onhoverLandmark]) => onhoverLandmark) ) } @@ -461,29 +434,14 @@ export class AtlasViewer implements OnDestroy, OnInit, AfterViewInit { } public mouseClickNehuba(event) { - // if (this.mouseUpLeftPosition === event.pageX && this.mouseUpTopPosition === event.pageY) {} - if (this.hoveringLandmark) { - const hoveringLandmark = this.hoveringLandmark - this.hoveringLandmark = null - //ToDo it should be ported into different component with ability to view detailed dataset files - this.presentDatasetDialogRef = this.matDialog.open(SingleDatasetView, { - data: { - name: hoveringLandmark.landmarkDataset[0].name, - title: hoveringLandmark.landmarkName, - description: hoveringLandmark.landmarkDataset[0].description, - kgExternalLink: hoveringLandmark.landmarkDataset[0].externalLink, - underEmbargo: hoveringLandmark.landmarkDataset[0].embargoStatus[0].name === 'Embargoed'? true : false - }, - }) - this.presentDatasetDialogRef.afterClosed().subscribe(() => { - this.presentDatasetDialogRef = null - }) - } + if (!this.rClContextualMenu) { return } this.rClContextualMenu.mousePos = [ event.clientX, event.clientY, ] + + // TODO what if user is hovering a landmark? if (!this.pluginRegionSelectionEnabled) { this.rClContextualMenu.show() } else { diff --git a/src/atlasViewer/atlasViewer.style.css b/src/atlasViewer/atlasViewer.style.css index 1782ffbc74ce60ba5ae6cfe459947a26df9cf47b..3a18311b9864ca06db18b3f15e58e74534222216 100644 --- a/src/atlasViewer/atlasViewer.style.css +++ b/src/atlasViewer/atlasViewer.style.css @@ -65,6 +65,7 @@ mat-sidenav { margin: 40px 0 0 5px; } +landmark-ui, region-menu { display:inline-block; diff --git a/src/atlasViewer/atlasViewer.template.html b/src/atlasViewer/atlasViewer.template.html index 12a0603d04770be9df84b590500ffc4b72b771ea..1ccb126033a3c341f49d8fdba202cc43a9cf5a1f 100644 --- a/src/atlasViewer/atlasViewer.template.html +++ b/src/atlasViewer/atlasViewer.template.html @@ -42,12 +42,14 @@ <ng-template #viewerBody> <div class="atlas-container" (drag-drop)="localFileService.handleFileDrop($event)"> - <ui-nehuba-container iav-mouse-hover #iavMouseHoverEl="iavMouseHover" - [currentOnHoverObs$]="iavMouseHoverEl.currentOnHoverObs$" - [currentOnHover]="iavMouseHoverEl.currentOnHoverObs$ | async" - iav-captureClickListenerDirective - (iav-captureClickListenerDirective-onMousedown)="mouseDownNehuba($event)" - (iav-captureClickListenerDirective-onClick)="mouseClickNehuba($event)"> + <ui-nehuba-container + iav-mouse-hover + #iavMouseHoverEl="iavMouseHover" + [currentOnHoverObs$]="iavMouseHoverEl.currentOnHoverObs$" + [currentOnHover]="iavMouseHoverEl.currentOnHoverObs$ | async" + iav-captureClickListenerDirective + (iav-captureClickListenerDirective-onMousedown)="mouseDownNehuba($event)" + (iav-captureClickListenerDirective-onClick)="mouseClickNehuba($event)"> </ui-nehuba-container> <div class="z-index-10 position-absolute pe-none w-100 h-100"> @@ -118,18 +120,20 @@ <div *ngIf="!ismobile && !presentDatasetDialogRef" class="d-inline-block" iav-mouse-hover - #iavMouseHoverConetxtualBlock="iavMouseHover" + #iavMouseHoverContextualBlock="iavMouseHover" contextualBlock> <ng-container - *ngFor="let labelText of iavMouseHoverConetxtualBlock.currentOnHoverObs$ | async | mouseOverTextPipe : selectedParcellation"> + *ngFor="let labelText of iavMouseHoverContextualBlock.currentOnHoverObs$ | async | mouseOverTextPipe : selectedParcellation"> <mat-list dense> <mat-list-item class="h-auto"> - <mat-icon [fontSet]="(labelText.label | mouseOverIconPipe).fontSet" - [fontIcon]="(labelText.label | mouseOverIconPipe).fontIcon" mat-list-icon> + <mat-icon + [fontSet]="(labelText.label | mouseOverIconPipe).fontSet" + [fontIcon]="(labelText.label | mouseOverIconPipe).fontIcon" + mat-list-icon> </mat-icon> @@ -148,23 +152,36 @@ </div> <div fixedMouseContextualContainerDirective> + + <!-- on click segment menu --> <ng-container *ngIf="(onhoverSegmentsForFixed$ | async) as onHoverSegments"> <ng-container *ngFor="let onHoverRegion of onHoverSegments; let first = first"> <!-- ToDo it should change - we should get information about connectivity existence from API--> <div class="d-flex flex-column"> <region-menu - class="pe-all" - [region]="onHoverRegion" - [isSelected]="selectedRegions$ | async | includes : onHoverRegion : compareFn" - [hasConnectivity]="selectedParcellation - && selectedParcellation.hasAdditionalViewMode - && selectedParcellation.hasAdditionalViewMode.includes('connectivity')" - (closeRegionMenu)="rClContextualMenu.hide()"> + class="pe-all" + [region]="onHoverRegion" + [isSelected]="selectedRegions$ | async | includes : onHoverRegion : compareFn" + [hasConnectivity]="selectedParcellation + && selectedParcellation.hasAdditionalViewMode + && selectedParcellation.hasAdditionalViewMode.includes('connectivity')" + (closeRegionMenu)="rClContextualMenu.hide()"> </region-menu> </div> </ng-container> </ng-container> + + <!-- on click landmark menu --> + <ng-container *ngIf="onhoverLandmarkForFixed$ | async as onHoverLandmark"> + + <landmark-ui + class="pe-all" + [name]="onHoverLandmark.name" + [datasets]="onHoverLandmark.dataset"> + + </landmark-ui> + </ng-container> </div> </layout-floating-container> diff --git a/src/ui/landmarkUI/landmarkUI.component.ts b/src/ui/landmarkUI/landmarkUI.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..26656fd8a4f3caea7afdc75c5c4e7c6e5522a5d0 --- /dev/null +++ b/src/ui/landmarkUI/landmarkUI.component.ts @@ -0,0 +1,16 @@ +import { Component, Input } from "@angular/core"; +import { IDataEntry } from "src/services/stateStore.service"; + +@Component({ + selector: 'landmark-ui', + templateUrl: './landmarkUI.template.html', + styleUrls: [ + './landmarkUI.style.css' + ] +}) + +export class LandmarkUIComponent{ + @Input() name: string + @Input() fullId: string + @Input() datasets: Partial<IDataEntry>[] +} \ No newline at end of file diff --git a/src/ui/landmarkUI/landmarkUI.style.css b/src/ui/landmarkUI/landmarkUI.style.css new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/ui/landmarkUI/landmarkUI.template.html b/src/ui/landmarkUI/landmarkUI.template.html new file mode 100644 index 0000000000000000000000000000000000000000..2b6c66d6cf722c22bdbe226265885b861944d96d --- /dev/null +++ b/src/ui/landmarkUI/landmarkUI.template.html @@ -0,0 +1,35 @@ +<mat-card> + <mat-card-title> + + {{ name }} + </mat-card-title> + <mat-card-subtitle> + + <i class="fas fa-map-marker-alt"></i> + <span> + spatial landmark + </span> + </mat-card-subtitle> + <mat-card-content> + + <hr class="text-muted"> + <!-- associated datasets --> + <div> + <small class="text-muted"> + Associated datasets + </small> + <div> + <ng-container *ngFor="let dataset of datasets"> + + <single-dataset-list-view + *ngIf="dataset.fullId | getKgSchemaIdFromFullIdPipe as kgSchemaId" + [ripple]="true" + [kgSchema]="kgSchemaId[0]" + [kgId]="kgSchemaId[1]"> + + </single-dataset-list-view> + </ng-container> + </div> + </div> + </mat-card-content> +</mat-card> diff --git a/src/ui/nehubaContainer/nehubaContainer.component.ts b/src/ui/nehubaContainer/nehubaContainer.component.ts index 20545327a97a6dd71b15e0c7ae42ea160dc80fc8..c7044a37de988e49151c742f066fc07947f7b571 100644 --- a/src/ui/nehubaContainer/nehubaContainer.component.ts +++ b/src/ui/nehubaContainer/nehubaContainer.component.ts @@ -1054,7 +1054,7 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy { this.nehubaViewerSubscriptions.push( this.nehubaViewer.mouseoverLandmarkEmitter.pipe( - throttleTime(100), + distinctUntilChanged() ).subscribe(label => { this.store.dispatch({ type : MOUSE_OVER_LANDMARK, diff --git a/src/ui/nehubaContainer/nehubaViewer/nehubaViewer.component.ts b/src/ui/nehubaContainer/nehubaViewer/nehubaViewer.component.ts index ad488c9ea3fa810464f6ed72d0c6f3c57f0a3f65..6a00f64a10677e360fae1f97976f80295c89d83c 100644 --- a/src/ui/nehubaContainer/nehubaViewer/nehubaViewer.component.ts +++ b/src/ui/nehubaContainer/nehubaViewer/nehubaViewer.component.ts @@ -775,6 +775,7 @@ export class NehubaViewerUnit implements OnInit, OnDestroy { }) }) + // TODO bug: mouseoverlandmarkemitter does not emit empty for VTK layer when user mouse click this.ondestroySubscriptions.push( this.nehubaViewer.mouseOver.layer .filter(obj => obj.layer.name === this.constantService.ngLandmarkLayerName) diff --git a/src/ui/parcellationRegion/regionMenu/regionMenu.style.css b/src/ui/parcellationRegion/regionMenu/regionMenu.style.css index c9f10ea89767c6d94d967b6486d350e0f13f1910..e9a48a372933a13d21d572ccdc4d8cbf1120fa6b 100644 --- a/src/ui/parcellationRegion/regionMenu/regionMenu.style.css +++ b/src/ui/parcellationRegion/regionMenu/regionMenu.style.css @@ -8,13 +8,3 @@ max-height: 310px; transition: max-height 0.25s ease-in; } - -[fixedMouseContextualContainerDirective] -{ - width: 15rem; -} - -[fixedMouseContextualContainerDirective] div[body] -{ - overflow: hidden; -} diff --git a/src/ui/parcellationRegion/regionMenu/regionMenu.template.html b/src/ui/parcellationRegion/regionMenu/regionMenu.template.html index 1d7e78a1ad5484f51b35145ac258fcc73f278e04..2f908b6f0457e232aa732a86108b2293390e1ccb 100644 --- a/src/ui/parcellationRegion/regionMenu/regionMenu.template.html +++ b/src/ui/parcellationRegion/regionMenu/regionMenu.template.html @@ -1,6 +1,12 @@ <mat-card> - <mat-card-subtitle> + <mat-card-title> {{ region.name }} + </mat-card-title> + <mat-card-subtitle> + <i class="fas fa-brain"></i> + <span> + Brain region + </span> </mat-card-subtitle> <mat-card-content> {{ region.description }} diff --git a/src/ui/ui.module.ts b/src/ui/ui.module.ts index 0411b1d707fadbebe8d4a5cd05d120d837890a14..69ae3d8cb2c4b659ffaef4d0e61ece1dd1c90054 100644 --- a/src/ui/ui.module.ts +++ b/src/ui/ui.module.ts @@ -78,6 +78,7 @@ import {ConnectivityBrowserComponent} from "src/ui/connectivityBrowser/connectiv import { RegionMenuComponent } from 'src/ui/parcellationRegion/regionMenu/regionMenu.component' import { RegionListSimpleViewComponent } from "./parcellationRegion/regionListSimpleView/regionListSimpleView.component"; import { SimpleRegionComponent } from "./parcellationRegion/regionSimple/regionSimple.component"; +import { LandmarkUIComponent } from "./landmarkUI/landmarkUI.component"; @NgModule({ imports : [ @@ -129,6 +130,7 @@ import { SimpleRegionComponent } from "./parcellationRegion/regionSimple/regionS ConnectivityBrowserComponent, SimpleRegionComponent, RegionListSimpleViewComponent, + LandmarkUIComponent, /* pipes */ GroupDatasetByRegion, @@ -192,6 +194,7 @@ import { SimpleRegionComponent } from "./parcellationRegion/regionSimple/regionS ViewerStateMini, RegionMenuComponent, FixedMouseContextualContainerDirective, + LandmarkUIComponent ], schemas: [ CUSTOM_ELEMENTS_SCHEMA, diff --git a/src/util/directives/mouseOver.directive.spec.ts b/src/util/directives/mouseOver.directive.spec.ts index 3a7a26b9d20ee44cedc4fc80f1db579070308456..613d78ee825f93026770c06378d28e1b9464de9c 100644 --- a/src/util/directives/mouseOver.directive.spec.ts +++ b/src/util/directives/mouseOver.directive.spec.ts @@ -4,7 +4,7 @@ import { scan, skip, take } from 'rxjs/operators'; import { temporalPositveScanFn } from './mouseOver.directive' const segmentsPositive = { segments: [{ hello: 'world' }] } as {segments: any} -const segmentsNegative = { segments: null } +const segmentsNegative = { segments: [] } const userLandmarkPostive = { userLandmark: true } const userLandmarkNegative = { userLandmark: null } diff --git a/src/util/directives/mouseOver.directive.ts b/src/util/directives/mouseOver.directive.ts index dd395b25d8859e51b1df971b57e16440e2b023a5..c3609e1b173a174e1cbf8b7f39e8f153e4c4ab72 100644 --- a/src/util/directives/mouseOver.directive.ts +++ b/src/util/directives/mouseOver.directive.ts @@ -21,7 +21,12 @@ import { getNgIdLabelIndexFromId, IavRootStoreInterface } from "src/services/sta export const temporalPositveScanFn = (acc: Array<{segments: any, landmark: any, userLandmark: any}>, curr: {segments: any, landmark: any, userLandmark: any}) => { const keys = Object.keys(curr) - const isPositive = keys.some(key => !!curr[key]) + + // empty array is truthy + const isPositive = keys.some(key => Array.isArray(curr[key]) + ? curr[key].length > 0 + : !!curr[key] + ) return isPositive ? [curr, ...(acc.filter(item => !keys.some(key => !!item[key])))] as Array<{segments?: any, landmark?: any, userLandmark?: any}> @@ -43,15 +48,18 @@ export class MouseHoverDirective { private log: LoggingService, ) { + // TODO consider moving these into a single obs serviced by a DI service + // can potentially net better performance + const onHoverUserLandmark$ = this.store$.pipe( select('uiState'), - map(state => state.mouseOverUserLandmark), + select('mouseOverUserLandmark'), ) const onHoverLandmark$ = combineLatest( this.store$.pipe( select('uiState'), - map(state => state.mouseOverLandmark), + select('mouseOverLandmark'), ), this.store$.pipe( select('dataStore'),