diff --git a/.travis.yml b/.travis.yml index 8bb9b983c6dc51d942b8e050a0145afed95d32c5..a147c22ea7030e03630dd1c3129aa1561ed65763 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,7 +15,7 @@ script: skip jobs: include: - - stage: Unit tests and Lint + - stage: Unit tests, Lint & test build if: | type = push AND \ branch NOT IN (master, staging, dev) @@ -25,6 +25,7 @@ jobs: script: - npm run lint - npm test + - npm run build-aot env: - NODE_ENV=test diff --git a/src/atlasViewer/atlasViewer.component.ts b/src/atlasViewer/atlasViewer.component.ts index bf86e43754d1dcc99da494abe3bfaf97454c96e9..11d244e402c9b8683d5a6ac39115456a24e0d180 100644 --- a/src/atlasViewer/atlasViewer.component.ts +++ b/src/atlasViewer/atlasViewer.component.ts @@ -10,10 +10,11 @@ import { ElementRef, Inject, Optional, + InjectionToken, } from "@angular/core"; import { Store, select, ActionsSubject } from "@ngrx/store"; import { Observable, Subscription, combineLatest, interval, merge, of, timer, fromEvent } from "rxjs"; -import { map, filter, distinctUntilChanged, delay, withLatestFrom, switchMapTo, take, startWith, shareReplay } from "rxjs/operators"; +import { map, filter, distinctUntilChanged, delay, withLatestFrom, switchMapTo, take, startWith } from "rxjs/operators"; import { LayoutMainSide } from "../layouts/mainside/mainside.component"; import { @@ -26,11 +27,6 @@ import { WidgetServices } from "src/widget"; import { LocalFileService } from "src/services/localFile.service"; import { AGREE_COOKIE, AGREE_KG_TOS, SHOW_KG_TOS } from "src/services/state/uiState.store"; -import { - CLOSE_SIDE_PANEL, - OPEN_SIDE_PANEL, -} from "src/services/state/uiState.store"; -import { FixedMouseContextualContainerDirective } from "src/util/directives/FixedMouseContextualContainerDirective.directive"; import { isSame } from "src/util/fn"; import { NehubaContainer } from "../ui/nehubaContainer/nehubaContainer.component"; import { colorAnimation } from "./atlasViewer.animation" @@ -39,7 +35,7 @@ import {MatSnackBar, MatSnackBarRef} from "@angular/material/snack-bar"; import {MatDialog, MatDialogRef} from "@angular/material/dialog"; import { ARIA_LABELS } from 'common/constants' -export const NEHUBA_CLICK_OVERRIDE = 'NEHUBA_CLICK_OVERRIDE' +export const NEHUBA_CLICK_OVERRIDE: InjectionToken<(next: () => void) => void> = new InjectionToken('NEHUBA_CLICK_OVERRIDE') import { MIN_REQ_EXPLAINER } from 'src/util/constants' import { SlServiceService } from "src/spotlight/sl-service.service"; diff --git a/src/atlasViewer/atlasViewer.template.html b/src/atlasViewer/atlasViewer.template.html index 825d80b5884f58fcfd456aba152f40e3857fbf21..9343eb3c2ae1f486c48c26deecff3acae4f6d5aa 100644 --- a/src/atlasViewer/atlasViewer.template.html +++ b/src/atlasViewer/atlasViewer.template.html @@ -46,7 +46,7 @@ #media="iavMediaQuery"> <!-- prevent default is required so that user do not zoom in on UI or scroll on mobile UI --> <ui-nehuba-container - + class="z-index-10" #uiNehubaContainer="uiNehubaContainer" iav-mouse-hover #iavMouseHoverEl="iavMouseHover" @@ -55,15 +55,21 @@ iav-captureClickListenerDirective [iav-captureClickListenerDirective-captureDocument]="true" (iav-captureClickListenerDirective-onUnmovedClick)="mouseClickDocument($event)"> + + <div ui-nehuba-container-overlay-top-right + class="d-inline-flex flex-row justify-content-end align-items-start z-index-6 position-absolute pe-none w-100 h-100"> + <!-- atlas selector --> + <atlas-dropdown-selector class="pe-all mt-4"> + </atlas-dropdown-selector> + + <signin-banner + class="mt-3 mr-2" + [parcellationIsSelected]="!!selectedParcellation" + [ismobile]="(media.mediaBreakPoint$ | async) > 3"> + </signin-banner> + </div> </ui-nehuba-container> - <div class="d-flex flex-row justify-content-end z-index-10 position-absolute pe-none w-100 h-100"> - <signin-banner - class="mt-3 mr-2" - [parcellationIsSelected]="!!selectedParcellation" - [ismobile]="(media.mediaBreakPoint$ | async) > 3"> - </signin-banner> - </div> <layout-floating-container zIndex="13" diff --git a/src/main.module.ts b/src/main.module.ts index 77f62638e2745e3535f5441bcdf5f23d37dce321..0e86995d887e66cba6ce00957ce908cec51ef3cb 100644 --- a/src/main.module.ts +++ b/src/main.module.ts @@ -26,7 +26,7 @@ import { LocalFileService } from "./services/localFile.service"; import { NgViewerUseEffect } from "./services/state/ngViewerState.store"; import { ViewerStateUseEffect } from "./services/state/viewerState.store"; import { UIService } from "./services/uiService.service"; -import { DatabrowserModule, OVERRIDE_IAV_DATASET_PREVIEW_DATASET_FN } from "src/ui/databrowserModule"; +import { DatabrowserModule, OVERRIDE_IAV_DATASET_PREVIEW_DATASET_FN, DataBrowserFeatureStore } from "src/ui/databrowserModule"; import { DatabrowserService } from "./ui/databrowserModule/databrowser.service"; import { ViewerStateControllerUseEffect } from "./ui/viewerStateController/viewerState.useEffect"; import { DockedContainerDirective } from "./util/directives/dockedContainer.directive"; @@ -76,6 +76,7 @@ export function debug(reducer: ActionReducer<any>): ActionReducer<any> { DragDropModule, UIModule, DatabrowserModule, + DataBrowserFeatureStore, AngularMaterialModule, UtilModule, WidgetModule, diff --git a/src/res/css/extra_styles.css b/src/res/css/extra_styles.css index e84deb2013694739cb3ab390266d45956cf89dee..3c91a3b4665e7f98249b20a0f59f36da62cf3882 100644 --- a/src/res/css/extra_styles.css +++ b/src/res/css/extra_styles.css @@ -484,6 +484,10 @@ markdown-dom pre code cursor: pointer!important; } +.z-index-6 +{ + z-index: 6!important; +} .z-index-10 { z-index: 10!important; diff --git a/src/services/state/ngViewerState.store.helper.ts b/src/services/state/ngViewerState.store.helper.ts index f5f1f70bb183be4b57d11a7f5f5f68acaca0c057..7f7f668bc4df36aee53d812b1984c88f457b89a2 100644 --- a/src/services/state/ngViewerState.store.helper.ts +++ b/src/services/state/ngViewerState.store.helper.ts @@ -1,29 +1,18 @@ // TODO to be merged with ng viewer state after refactor +import { INgLayerInterface, PANELS } from './ngViewerState/constants' -import { createAction, props } from "@ngrx/store"; +export { INgLayerInterface, PANELS } -export interface INgLayerInterface { - name: string // displayName - source: string - mixability: string // base | mixable | nonmixable - annotation?: string // - id?: string // unique identifier - visible?: boolean - shader?: string - transform?: any -} - -export const ngViewerActionAddNgLayer = createAction( - '[ngLayerAction] addNgLayer', - props<{ layer: INgLayerInterface|INgLayerInterface[] }>() -) +import { + ngViewerActionAddNgLayer, + ngViewerActionRemoveNgLayer, + ngViewerActionSetPerspOctantRemoval, + ngViewerActionToggleMax, +} from './ngViewerState/actions' -export const ngViewerActionRemoveNgLayer = createAction( - '[ngLayerAction] removeNgLayer', - props<{ layer: Partial<INgLayerInterface>|Partial<INgLayerInterface>[] }>() -) - -export const ngViewerActionSetPerspOctantRemoval = createAction( - `[ngViewerAction] setPerspectiveOctant`, - props<{ octantRemovalFlag: boolean }>() -) +export { + ngViewerActionAddNgLayer, + ngViewerActionRemoveNgLayer, + ngViewerActionSetPerspOctantRemoval, + ngViewerActionToggleMax, +} diff --git a/src/services/state/ngViewerState.store.ts b/src/services/state/ngViewerState.store.ts index f6808c67506be9b6d62b92e45eb82d22076d33d5..ca436b3eefed79704e05afaf423db86e974c9800 100644 --- a/src/services/state/ngViewerState.store.ts +++ b/src/services/state/ngViewerState.store.ts @@ -9,11 +9,8 @@ import { BACKENDURL, CYCLE_PANEL_MESSAGE } from 'src/util/constants'; import { HttpClient } from '@angular/common/http'; import { INgLayerInterface, ngViewerActionAddNgLayer, ngViewerActionRemoveNgLayer, ngViewerActionSetPerspOctantRemoval } from './ngViewerState.store.helper' import { PureContantService } from 'src/util'; - -export const FOUR_PANEL = 'FOUR_PANEL' -export const V_ONE_THREE = 'V_ONE_THREE' -export const H_ONE_THREE = 'H_ONE_THREE' -export const SINGLE_PANEL = 'SINGLE_PANEL' +import { PANELS } from './ngViewerState.store.helper' +import { ngViewerActionToggleMax } from './ngViewerState/actions'; export function mixNgLayers(oldLayers: INgLayerInterface[], newLayers: INgLayerInterface|INgLayerInterface[]): INgLayerInterface[] { if (newLayers instanceof Array) { @@ -52,7 +49,7 @@ export const defaultState: StateInterface = { layers: [], forceShowSegment: null, nehubaReady: false, - panelMode: FOUR_PANEL, + panelMode: PANELS.FOUR_PANEL, panelOrder: `0123`, octantRemoval: true, @@ -241,7 +238,7 @@ export class NgViewerUseEffect implements OnDestroy { ) const toggleMaxmimise$ = this.actions.pipe( - ofType(ACTION_TYPES.TOGGLE_MAXIMISE), + ofType(ngViewerActionToggleMax.type), shareReplay(1), ) @@ -277,7 +274,7 @@ export class NgViewerUseEffect implements OnDestroy { this.panelMode$, ), ), - filter(([_action, [_panelOrder, panelMode]]) => panelMode !== SINGLE_PANEL), + filter(([_action, [_panelOrder, panelMode]]) => panelMode !== PANELS.SINGLE_PANEL), map(([ action, [ oldPanelOrder ] ]) => { const { payload } = action as ActionInterface const { index = 0 } = payload @@ -307,7 +304,7 @@ export class NgViewerUseEffect implements OnDestroy { panelMode, }, ...acc.slice(0, 1)] }, [] as any[]), - filter(([ { panelMode } ]) => panelMode === SINGLE_PANEL), + filter(([ { panelMode } ]) => panelMode === PANELS.SINGLE_PANEL), map(arr => { const { action, @@ -342,9 +339,9 @@ export class NgViewerUseEffect implements OnDestroy { return { type: ACTION_TYPES.SWITCH_PANEL_MODE, payload: { - panelMode: panelModes[0] === SINGLE_PANEL - ? (panelModes[1] || FOUR_PANEL) - : SINGLE_PANEL, + panelMode: panelModes[0] === PANELS.SINGLE_PANEL + ? (panelModes[1] || PANELS.FOUR_PANEL) + : PANELS.SINGLE_PANEL, }, } }), @@ -356,7 +353,7 @@ export class NgViewerUseEffect implements OnDestroy { ).pipe( filter(([_, useMobileUI]) => !useMobileUI), map(([toggleMaximiseMode, _]) => toggleMaximiseMode), - filter(({ payload }) => payload.panelMode && payload.panelMode === SINGLE_PANEL), + filter(({ payload }) => payload.panelMode && payload.panelMode === PANELS.SINGLE_PANEL), mapTo({ type: SNACKBAR_MESSAGE, snackbarMessage: CYCLE_PANEL_MESSAGE, @@ -366,7 +363,7 @@ export class NgViewerUseEffect implements OnDestroy { this.spacebarListener$ = fromEvent(document.body, 'keydown', { capture: true }).pipe( filter((ev: KeyboardEvent) => ev.key === ' '), withLatestFrom(this.panelMode$), - filter(([_ , panelMode]) => panelMode === SINGLE_PANEL), + filter(([_ , panelMode]) => panelMode === PANELS.SINGLE_PANEL), mapTo({ type: ACTION_TYPES.CYCLE_VIEWS, }), @@ -450,17 +447,16 @@ const ACTION_TYPES = { SWITCH_PANEL_MODE: 'SWITCH_PANEL_MODE', SET_PANEL_ORDER: 'SET_PANEL_ORDER', - TOGGLE_MAXIMISE: 'TOGGLE_MAXIMISE', CYCLE_VIEWS: 'CYCLE_VIEWS', REMOVE_ALL_NONBASE_LAYERS: `REMOVE_ALL_NONBASE_LAYERS`, } export const SUPPORTED_PANEL_MODES = [ - FOUR_PANEL, - H_ONE_THREE, - V_ONE_THREE, - SINGLE_PANEL, + PANELS.FOUR_PANEL, + PANELS.H_ONE_THREE, + PANELS.V_ONE_THREE, + PANELS.SINGLE_PANEL, ] export const NG_VIEWER_ACTION_TYPES = ACTION_TYPES diff --git a/src/services/state/ngViewerState/actions.ts b/src/services/state/ngViewerState/actions.ts new file mode 100644 index 0000000000000000000000000000000000000000..84c2e2b898fd9cbe559ad93a3ec23a349d6599eb --- /dev/null +++ b/src/services/state/ngViewerState/actions.ts @@ -0,0 +1,22 @@ +import { createAction, props } from "@ngrx/store" +import { INgLayerInterface } from './constants' + +export const ngViewerActionAddNgLayer = createAction( + '[ngLayerAction] addNgLayer', + props<{ layer: INgLayerInterface|INgLayerInterface[] }>() +) + +export const ngViewerActionRemoveNgLayer = createAction( + '[ngLayerAction] removeNgLayer', + props<{ layer: Partial<INgLayerInterface>|Partial<INgLayerInterface>[] }>() +) + +export const ngViewerActionSetPerspOctantRemoval = createAction( + `[ngViewerAction] setPerspectiveOctant`, + props<{ octantRemovalFlag: boolean }>() +) + +export const ngViewerActionToggleMax = createAction( + `[ngViewerAction] toggleMax`, + props<{ payload: { index: number } }>() +) \ No newline at end of file diff --git a/src/services/state/ngViewerState/constants.ts b/src/services/state/ngViewerState/constants.ts new file mode 100644 index 0000000000000000000000000000000000000000..3406494f9d6a4a578b54325b1b2869df7f3f3da9 --- /dev/null +++ b/src/services/state/ngViewerState/constants.ts @@ -0,0 +1,17 @@ +export interface INgLayerInterface { + name: string // displayName + source: string + mixability: string // base | mixable | nonmixable + annotation?: string // + id?: string // unique identifier + visible?: boolean + shader?: string + transform?: any +} + +export enum PANELS { + FOUR_PANEL = 'FOUR_PANEL', + V_ONE_THREE = 'V_ONE_THREE', + H_ONE_THREE = 'H_ONE_THREE', + SINGLE_PANEL = 'SINGLE_PANEL', +} diff --git a/src/services/state/viewerState.store.helper.ts b/src/services/state/viewerState.store.helper.ts index 5b3b42b90c439d59298ebd3a592111583de50774..34283ab014a770fa3d9585d919b4b5df1fe6050a 100644 --- a/src/services/state/viewerState.store.helper.ts +++ b/src/services/state/viewerState.store.helper.ts @@ -1,97 +1,54 @@ // TODO merge with viewerstate.store.ts when refactor is done -import { createAction, props, createReducer, on, ActionReducer, createSelector, Store, select } from "@ngrx/store"; +import { createReducer, on, ActionReducer, createSelector, Store, select } from "@ngrx/store"; import { generalApplyState } from "../stateStore.helper"; import { Effect, Actions, ofType } from "@ngrx/effects"; import { Observable } from "rxjs"; import { withLatestFrom, map } from "rxjs/operators"; import { Injectable } from "@angular/core"; -export interface IRegion{ - name: string - [key: string]: string +import { + viewerStateHelperSelectParcellationWithId, + viewerStateNavigateToRegion, + viewerStateRemoveAdditionalLayer, + viewerStateSelectAtlas, + viewerStateSelectParcellation, + viewerStateSelectTemplateWithId, + viewerStateSetConnectivityRegion, + viewerStateSetFetchedAtlases, + viewerStateSetSelectedRegions, + viewerStateSetSelectedRegionsWithIds, + viewerStateToggleLayer, + viewerStateToggleRegionSelect, + viewerStateSelectRegionWithIdDeprecated, + viewerStateDblClickOnViewer, + viewerStateAddUserLandmarks, + viewreStateRemoveUserLandmarks +} from './viewerState/actions' + +export { + viewerStateHelperSelectParcellationWithId, + viewerStateNavigateToRegion, + viewerStateRemoveAdditionalLayer, + viewerStateSelectAtlas, + viewerStateSelectParcellation, + viewerStateSelectTemplateWithId, + viewerStateSetConnectivityRegion, + viewerStateSetFetchedAtlases, + viewerStateSetSelectedRegions, + viewerStateSetSelectedRegionsWithIds, + viewerStateToggleLayer, + viewerStateToggleRegionSelect, + viewerStateSelectRegionWithIdDeprecated, + viewerStateDblClickOnViewer, + viewerStateAddUserLandmarks, + viewreStateRemoveUserLandmarks } -export const viewerStateSetSelectedRegionsWithIds = createAction( - `[viewerState] setSelectedRegionsWithIds`, - props<{ selectRegionIds: string[] }>() -) - -export const viewerStateSetSelectedRegions = createAction( - '[viewerState] setSelectedRegions', - props<{ selectRegions: IRegion[] }>() -) - -export const viewerStateSetConnectivityRegion = createAction( - `[viewerState] setConnectivityRegion`, - props<{ connectivityRegion: any }>() -) - -export const viewerStateNavigateToRegion = createAction( - `[viewerState] navigateToRegion`, - props<{ payload: { region: any } }>() -) - -export const viewerStateToggleRegionSelect = createAction( - `[viewerState] toggleRegionSelect`, - props<{ payload: { region: any } }>() -) - -export const viewerStateSetFetchedAtlases = createAction( - '[viewerState] setFetchedatlases', - props<{ fetchedAtlases: any[] }>() -) - -export const viewerStateSelectAtlas = createAction( - `[viewerState] selectAtlas`, - props<{ atlas: { ['@id']: string } }>() -) - -export const viewerStateHelperSelectParcellationWithId = createAction( - `[viewerStateHelper] selectParcellationWithId`, - props<{ payload: { ['@id']: string } }>() -) +export { + viewerStateAllParcellationsSelector, + viewerStateSelectedRegionsSelector +} from './viewerState/selectors' -export const viewerStateSelectParcellation = createAction( - `[viewerState] selectParcellation`, - props<{ selectParcellation: any }>() -) - -export const viewerStateSelectTemplateWithId = createAction( - `[viewerState] selectTemplateWithId`, - props<{ payload: { ['@id']: string }, config?: { selectParcellation: { ['@id']: string } } }>() -) - -export const viewerStateToggleLayer = createAction( - `[viewerState] toggleLayer`, - props<{ payload: { ['@id']: string } }>() -) - -export const viewerStateRemoveAdditionalLayer = createAction( - `[viewerState] removeAdditionalLayer`, - props<{ payload?: { ['@id']: string } }>() -) - -export const viewerStateSelectedRegionsSelector = createSelector( - state => state['viewerState'], - viewerState => viewerState['regionsSelected'] -) - -export const viewerStateAllParcellationsSelector = createSelector( - state => state['viewerState'], - viewerState => { - return (viewerState['fetchedTemplates'] as any[] || []) - .reduce((acc, curr) => { - const parcelations = (curr['parcellations'] || []).map(p => { - return { - ...p, - useTheme: curr['useTheme'] - } - }) - - return acc.concat( parcelations ) - }, []) - } -) interface IViewerStateHelperStore{ fetchedAtlases: any[] @@ -112,7 +69,7 @@ function handleToggleLayerAction(reducer: ActionReducer<any>): ActionReducer<any const { payload } = action as any const { templateSelected } = (state && state['viewerState']) || {} - const selectParcellation = templateSelected['parcellations'].find(p => p['@id'] === payload['@id']) + const selectParcellation = templateSelected?.parcellations.find(p => p['@id'] === payload['@id']) return reducer(state, viewerStateSelectParcellation({ selectParcellation })) } default: reducer(state, action) @@ -167,7 +124,7 @@ export const viewerStateGetOverlayingAdditionalParcellations = createSelector( const { parcellationSelected } = viewerState const selectedAtlas = selectedAtlasId && fetchedAtlases.find(a => a['@id'] === selectedAtlasId) - const atlasLayer = selectedAtlas['parcellations'].find(p => p['@id'] === (parcellationSelected && parcellationSelected['@id'])) + const atlasLayer = selectedAtlas?.parcellations.find(p => p['@id'] === (parcellationSelected && parcellationSelected['@id'])) const isBaseLayer = atlasLayer && atlasLayer.baseLayer return (!!atlasLayer && !isBaseLayer) ? [{ ...(parcellationSelected || {} ), diff --git a/src/services/state/viewerState.store.ts b/src/services/state/viewerState.store.ts index df1627897c58791568a58cfa95919387d8383198..ef89295968382eec437c368b4fd809bbcc90791e 100644 --- a/src/services/state/viewerState.store.ts +++ b/src/services/state/viewerState.store.ts @@ -246,6 +246,11 @@ export function stateStore(state, action) { return defaultStateStore(state, action) } +import { + viewerStateSelectRegionWithIdDeprecated +} from './viewerState.store.helper' +import { viewerStateDblClickOnViewer, viewerStateAddUserLandmarks, viewreStateRemoveUserLandmarks } from './viewerState/actions'; + export const LOAD_DEDICATED_LAYER = 'LOAD_DEDICATED_LAYER' export const UNLOAD_DEDICATED_LAYER = 'UNLOAD_DEDICATED_LAYER' @@ -258,7 +263,7 @@ export const SELECT_PARCELLATION = `SELECT_PARCELLATION` export const DESELECT_REGIONS = `DESELECT_REGIONS` export const SELECT_REGIONS = `SELECT_REGIONS` -export const SELECT_REGIONS_WITH_ID = `SELECT_REGIONS_WITH_ID` +export const SELECT_REGIONS_WITH_ID = viewerStateSelectRegionWithIdDeprecated.type export const SELECT_LANDMARKS = `SELECT_LANDMARKS` export const DESELECT_LANDMARKS = `DESELECT_LANDMARKS` export const USER_LANDMARKS = `USER_LANDMARKS` @@ -462,12 +467,12 @@ export class ViewerStateUseEffect { } const ACTION_TYPES = { - ADD_USERLANDMARKS: `ADD_USERLANDMARKS`, - REMOVE_USER_LANDMARKS: 'REMOVE_USER_LANDMARKS', + ADD_USERLANDMARKS: viewerStateAddUserLandmarks.type, + REMOVE_USER_LANDMARKS: viewreStateRemoveUserLandmarks.type, MOUSEOVER_USER_LANDMARK_LABEL: 'MOUSEOVER_USER_LANDMARK_LABEL', SINGLE_CLICK_ON_VIEWER: 'SINGLE_CLICK_ON_VIEWER', - DOUBLE_CLICK_ON_VIEWER: 'DOUBLE_CLICK_ON_VIEWER', + DOUBLE_CLICK_ON_VIEWER: viewerStateDblClickOnViewer.type } export const VIEWERSTATE_ACTION_TYPES = ACTION_TYPES diff --git a/src/services/state/viewerState/actions.ts b/src/services/state/viewerState/actions.ts new file mode 100644 index 0000000000000000000000000000000000000000..c0fba6b04f7d0dc4c60b4725443daacd80d9990a --- /dev/null +++ b/src/services/state/viewerState/actions.ts @@ -0,0 +1,82 @@ +import { createAction, props } from "@ngrx/store" +import { IRegion } from './constants' + +export const viewerStateSetSelectedRegionsWithIds = createAction( + `[viewerState] setSelectedRegionsWithIds`, + props<{ selectRegionIds: string[] }>() +) + +export const viewerStateSetSelectedRegions = createAction( + '[viewerState] setSelectedRegions', + props<{ selectRegions: IRegion[] }>() +) + +export const viewerStateSetConnectivityRegion = createAction( + `[viewerState] setConnectivityRegion`, + props<{ connectivityRegion: any }>() +) + +export const viewerStateNavigateToRegion = createAction( + `[viewerState] navigateToRegion`, + props<{ payload: { region: any } }>() +) + +export const viewerStateToggleRegionSelect = createAction( + `[viewerState] toggleRegionSelect`, + props<{ payload: { region: any } }>() +) + +export const viewerStateSetFetchedAtlases = createAction( + '[viewerState] setFetchedatlases', + props<{ fetchedAtlases: any[] }>() +) + +export const viewerStateSelectAtlas = createAction( + `[viewerState] selectAtlas`, + props<{ atlas: { ['@id']: string } }>() +) + +export const viewerStateHelperSelectParcellationWithId = createAction( + `[viewerStateHelper] selectParcellationWithId`, + props<{ payload: { ['@id']: string } }>() +) + +export const viewerStateSelectParcellation = createAction( + `[viewerState] selectParcellation`, + props<{ selectParcellation: any }>() +) + +export const viewerStateSelectTemplateWithId = createAction( + `[viewerState] selectTemplateWithId`, + props<{ payload: { ['@id']: string }, config?: { selectParcellation: { ['@id']: string } } }>() +) + +export const viewerStateToggleLayer = createAction( + `[viewerState] toggleLayer`, + props<{ payload: { ['@id']: string } }>() +) + +export const viewerStateRemoveAdditionalLayer = createAction( + `[viewerState] removeAdditionalLayer`, + props<{ payload?: { ['@id']: string } }>() +) + +export const viewerStateSelectRegionWithIdDeprecated = createAction( + `[viewerState] [deprecated] selectRegionsWithId`, + props<{ selectRegionIds: number[] }>() +) + +export const viewerStateDblClickOnViewer = createAction( + `[viewerState] dblClickOnViewer`, + props<{ payload: { segments: any, landmark: any, userLandmark: any } }>() +) + +export const viewerStateAddUserLandmarks = createAction( + `[viewerState] addUserlandmark,`, + props<{ landmarks: any[] }>() +) + +export const viewreStateRemoveUserLandmarks = createAction( + `[viewerState] removeUserLandmarks`, + props<{ payload: { landmarkIds: string[] } }>() +) diff --git a/src/services/state/viewerState/constants.ts b/src/services/state/viewerState/constants.ts new file mode 100644 index 0000000000000000000000000000000000000000..dbfa8c7800f0cc602fe1b1f942b7088552fe20ce --- /dev/null +++ b/src/services/state/viewerState/constants.ts @@ -0,0 +1,4 @@ +export interface IRegion{ + name: string + [key: string]: string +} diff --git a/src/services/state/viewerState/selectors.ts b/src/services/state/viewerState/selectors.ts new file mode 100644 index 0000000000000000000000000000000000000000..6d32c879ee234c99fc49aadc62c71770776ac675 --- /dev/null +++ b/src/services/state/viewerState/selectors.ts @@ -0,0 +1,23 @@ +import { createSelector } from "@ngrx/store" + +export const viewerStateSelectedRegionsSelector = createSelector( + state => state['viewerState'], + viewerState => viewerState['regionsSelected'] +) + +export const viewerStateAllParcellationsSelector = createSelector( + state => state['viewerState'], + viewerState => { + return (viewerState['fetchedTemplates'] as any[] || []) + .reduce((acc, curr) => { + const parcelations = (curr['parcellations'] || []).map(p => { + return { + ...p, + useTheme: curr['useTheme'] + } + }) + + return acc.concat( parcelations ) + }, []) + } +) \ No newline at end of file diff --git a/src/theme.scss b/src/theme.scss index 6faa41709be5a41dcdedcc732b71380eae884df2..93e185931cb27302296b57a712c8b8c0a9c659a1 100644 --- a/src/theme.scss +++ b/src/theme.scss @@ -6,6 +6,7 @@ $foreground: map-get($theme, foreground); $background: map-get($theme, background); + $background-color: map-get($background, background); $primary: map-get($theme, primary); $accent: map-get($theme, accent); @@ -17,8 +18,14 @@ &[bg], &.bg { - background-color: mat-color($background, background); + background-color: mat-color($background, background) } + + &.darker-bg + { + background-color: $background-color; + } + &[text], &.text { diff --git a/src/ui/atlasLayerSelector/atlasLayerSelector.component.ts b/src/ui/atlasLayerSelector/atlasLayerSelector.component.ts index 355387093cfc6b380083a0d0902c00d482e2684f..abc96e5aac760ffa769539cd5aeabc1f8696a870 100644 --- a/src/ui/atlasLayerSelector/atlasLayerSelector.component.ts +++ b/src/ui/atlasLayerSelector/atlasLayerSelector.component.ts @@ -141,7 +141,7 @@ export class AtlasLayerSelector implements OnInit { ngOnInit(): void { this.subscriptions.push( this.selectedTemplate$.subscribe(st => { - this.selectedTemplatePreviewUrl = st.templateSpaces.find(t => t['@id'] === st['@id']).previewUrl + this.selectedTemplatePreviewUrl = st.templateSpaces?.find(t => t['@id'] === st['@id']).previewUrl this.selectedTemplateSpaceId = st['@id'] }), ) diff --git a/src/ui/databrowserModule/databrowser.module.ts b/src/ui/databrowserModule/databrowser.module.ts index 0463b9b61ac5447782159317ade99c2d59a02f7c..3aabe488fbeddfece162d3325789c9034b9458e0 100644 --- a/src/ui/databrowserModule/databrowser.module.ts +++ b/src/ui/databrowserModule/databrowser.module.ts @@ -30,20 +30,15 @@ import { PreviewComponentWrapper } from "./preview/previewComponentWrapper/previ import { BulkDownloadBtn, TransformDatasetToIdPipe } from "./bulkDownload/bulkDownloadBtn.component"; import { ShowDatasetDialogDirective, IAV_DATASET_SHOW_DATASET_DIALOG_CMP } from "./showDatasetDialog.directive"; import { PreviewDatasetFile, IAV_DATASET_PREVIEW_DATASET_FN, IAV_DATASET_PREVIEW_ACTIVE, TypePreviewDispalyed } from "./singleDataset/datasetPreviews/previewDatasetFile.directive"; -import { StoreModule } from "@ngrx/store"; import { - stateStore, DatasetPreview } from 'src/services/state/dataStore.store' import { OVERRIDE_IAV_DATASET_PREVIEW_DATASET_FN, } from './constants' -import { EffectsModule } from "@ngrx/effects"; -import { DataBrowserUseEffect } from "./databrowser.useEffect"; -export const DATESTORE_FEATURE_KEY = `dataStore` const previewEmitFactory = ( overrideFn: (file: any, dataset: any) => void) => { if (overrideFn) return overrideFn @@ -58,8 +53,6 @@ const previewEmitFactory = ( overrideFn: (file: any, dataset: any) => void) => { FormsModule, UtilModule, AngularMaterialModule, - StoreModule.forFeature(DATESTORE_FEATURE_KEY, stateStore), - EffectsModule.forFeature([ DataBrowserUseEffect ]) ], declarations: [ DataBrowser, diff --git a/src/ui/databrowserModule/index.ts b/src/ui/databrowserModule/index.ts index 2d9cb9048e35870292e7137eb86ac89157bba550..1c914548b729760c8e2b5cac7fc9a61dce2da8c3 100644 --- a/src/ui/databrowserModule/index.ts +++ b/src/ui/databrowserModule/index.ts @@ -1,5 +1,4 @@ export { - DATESTORE_FEATURE_KEY, DatabrowserModule, DatasetPreview, IAV_DATASET_PREVIEW_ACTIVE, @@ -20,3 +19,4 @@ export { } from './constants' export { PreviewComponentWrapper } from './preview/previewComponentWrapper/previewCW.component' +export { DataBrowserFeatureStore, DATESTORE_FEATURE_KEY } from './store.module' \ No newline at end of file diff --git a/src/ui/databrowserModule/store.module.ts b/src/ui/databrowserModule/store.module.ts new file mode 100644 index 0000000000000000000000000000000000000000..eab27f3e99edb2b1b2ca8f1d0800b04bc48c7125 --- /dev/null +++ b/src/ui/databrowserModule/store.module.ts @@ -0,0 +1,16 @@ +import { NgModule } from "@angular/core"; +import { stateStore } from "src/services/state/uiState.store"; +import { DataBrowserUseEffect } from "./databrowser.useEffect"; +import { StoreModule } from "@ngrx/store"; +import { EffectsModule } from "@ngrx/effects"; + +export const DATESTORE_FEATURE_KEY = `dataStore` + +@NgModule({ + imports: [ + StoreModule.forFeature(DATESTORE_FEATURE_KEY, stateStore), + EffectsModule.forFeature([ DataBrowserUseEffect ]) + ] +}) + +export class DataBrowserFeatureStore{} \ No newline at end of file diff --git a/src/ui/nehubaContainer/maximisePanelButton/maximisePanelButton.component.ts b/src/ui/nehubaContainer/maximisePanelButton/maximisePanelButton.component.ts index 6b74bfde7680c71da8cc14b92a14119a78564a92..36b2d9e882bb5a22b939c55902d7ecf84871a012 100644 --- a/src/ui/nehubaContainer/maximisePanelButton/maximisePanelButton.component.ts +++ b/src/ui/nehubaContainer/maximisePanelButton/maximisePanelButton.component.ts @@ -2,7 +2,7 @@ import { Component, Input } from "@angular/core"; import { select, Store } from "@ngrx/store"; import { Observable } from "rxjs"; import { distinctUntilChanged, map } from "rxjs/operators"; -import { SINGLE_PANEL } from "src/services/state/ngViewerState.store"; +import { PANELS } from 'src/services/state/ngViewerState.store.helper' import { ARIA_LABELS } from 'common/constants' const { @@ -46,7 +46,7 @@ export class MaximmisePanelButton { ) this.isMaximised$ = this.panelMode$.pipe( - map(panelMode => panelMode === SINGLE_PANEL), + map(panelMode => panelMode === PANELS.SINGLE_PANEL), ) } } diff --git a/src/ui/nehubaContainer/nehuba.module.ts b/src/ui/nehubaContainer/nehuba.module.ts index 39e0fda1b916635dbb2555ff0b03fc058fd5f5bc..0104d0c011daa059f725a9b218abfac7f3b494cb 100644 --- a/src/ui/nehubaContainer/nehuba.module.ts +++ b/src/ui/nehubaContainer/nehuba.module.ts @@ -1,14 +1,21 @@ import { NgModule } from "@angular/core"; import { NehubaViewerContainerDirective } from './nehubaViewerInterface/nehubaViewerInterface.directive' +import { NehubaViewerUnit } from "./nehubaViewer/nehubaViewer.component"; +import { CommonModule } from "@angular/common"; @NgModule({ imports: [ - + CommonModule ], declarations: [ - NehubaViewerContainerDirective + NehubaViewerContainerDirective, + NehubaViewerUnit ], exports: [ - NehubaViewerContainerDirective + NehubaViewerContainerDirective, + NehubaViewerUnit + ], + entryComponents: [ + NehubaViewerUnit ] }) diff --git a/src/ui/nehubaContainer/nehubaContainer.component.spec.ts b/src/ui/nehubaContainer/nehubaContainer.component.spec.ts index 8724619c41adae065f2fa0a7f865d077ccd5ce37..0ea7d6fee6b34c6aaeadc3deec12952c5561fa9e 100644 --- a/src/ui/nehubaContainer/nehubaContainer.component.spec.ts +++ b/src/ui/nehubaContainer/nehubaContainer.component.spec.ts @@ -1,52 +1,182 @@ -import { TestBed } from "@angular/core/testing" +import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core' +import { TestBed, async } from "@angular/core/testing" import { NehubaContainer } from "./nehubaContainer.component" -import { provideMockStore } from "@ngrx/store/testing" +import { provideMockStore, MockStore } from "@ngrx/store/testing" +import { defaultRootState } from 'src/services/stateStore.service' +import { ComponentsModule } from "src/components" +import { AngularMaterialModule } from "../sharedModules/angularMaterial.module" +import { TouchSideClass } from "./touchSideClass.directive" +import { MaximmisePanelButton } from "./maximisePanelButton/maximisePanelButton.component" +import { LandmarkUnit } from './landmarkUnit/landmarkUnit.component' +import { LayoutModule } from 'src/layouts/layout.module' +import { UtilModule } from "src/util" +import { AtlasLayerSelector } from "../atlasLayerSelector/atlasLayerSelector.component" +import { StatusCardComponent } from './statusCard/statusCard.component' +import { NehubaViewerTouchDirective } from './nehubaViewerInterface/nehubaViewerTouch.directive' +import { MobileOverlay } from "./mobileOverlay/mobileOverlay.component" +import { RegionMenuComponent } from "../parcellationRegion/regionMenu/regionMenu.component" +import { DatabrowserModule } from "../databrowserModule" +import { SplashScreen } from "./splashScreen/splashScreen.component" +import { CurrentLayout } from 'src/ui/config/currentLayout/currentLayout.component' +import { RegionDirective } from 'src/ui/parcellationRegion/region.directive' +import { RegionTextSearchAutocomplete } from "../viewerStateController/regionSearch/regionSearch.component" +import { MobileControlNubStylePipe } from './pipes/mobileControlNubStyle.pipe' +import { ReorderPanelIndexPipe } from './reorderPanelIndex.pipe' +import { SafeStylePipe } from 'src/util/pipes/safeStyle.pipe' +import { AuthModule } from 'src/auth' +import { StateModule } from 'src/state' +import { ReactiveFormsModule, FormsModule } from '@angular/forms' +import { HttpClientModule } from '@angular/common/http' +import { WidgetModule } from 'src/widget' +import { PluginModule } from 'src/atlasViewer/pluginUnit/plugin.module' +import { NehubaModule } from './nehuba.module' +import { CommonModule } from '@angular/common' +import { IMPORT_NEHUBA_INJECT_TOKEN } from './nehubaViewer/nehubaViewer.component' +import { viewerStateHelperStoreName } from 'src/services/state/viewerState.store.helper' -/** - * access defaultState before initialization error - * TODO figure out why - */ +const _bigbrainJson = require('!json-loader!src/res/ext/bigbrain.json') +const _bigbrainNehubaConfigJson = require('!json-loader!src/res/ext/bigbrainNehubaConfig.json') +const bigbrainJson = { + ..._bigbrainJson, + nehubaConfig: _bigbrainNehubaConfigJson +} +const humanAtlas = require('!json-loader!src/res/ext/atlas/atlas_multiLevelHuman.json') +const importNehubaSpy = jasmine.createSpy('importNehubaSpy').and.returnValue(Promise.reject()) describe('> nehubaContainer.component.ts', () => { - // describe('> NehubaContainer', () => { - // beforeEach(() => { - // TestBed.configureTestingModule({ - // imports: [ - - // ], - // declarations: [ - // NehubaContainer - // ], - // providers: [ - // provideMockStore({ - // initialState: {} - // }) - // ] - // }).compileComponents() - // }) - // it('> can be init', () => { - // const fixture = TestBed.createComponent(NehubaContainer) - // }) - // describe('> loading indicator', () => { - // it('> appears on init', () => { - // const fixture = TestBed.createComponent(NehubaContainer) - // fixture.componentInstance.viewerLoaded = true - // fixture.detectChanges() - // const els = fixture.debugElement.queryAll( By.css(`.loadingIndicator`) ) - // expect(els.length).toBe(3) - // }) - // }) - - // describe('> panel control', () => { - // it('> appears on mouse over', () => { - // /** - // * TODO implement mouse over - // */ - // }) - - // it('> on click of zoom btns, calls appropriate fn', () => { - - // }) - // }) - // }) + + describe('> NehubaContainer', () => { + + beforeEach(async(() => { + + TestBed.configureTestingModule({ + imports: [ + PluginModule, + WidgetModule, + ComponentsModule, + AngularMaterialModule, + LayoutModule, + UtilModule, + DatabrowserModule, + NehubaModule, + AuthModule, + StateModule, + FormsModule, + ReactiveFormsModule, + HttpClientModule, + CommonModule + ], + declarations: [ + NehubaContainer, + TouchSideClass, + MaximmisePanelButton, + LandmarkUnit, + AtlasLayerSelector, + StatusCardComponent, + NehubaViewerTouchDirective, + MobileOverlay, + RegionMenuComponent, + SplashScreen, + CurrentLayout, + RegionDirective, + RegionTextSearchAutocomplete, + + // pipes + MobileControlNubStylePipe, + ReorderPanelIndexPipe, + SafeStylePipe + ], + providers: [ + provideMockStore({ initialState: defaultRootState }), + { + provide: IMPORT_NEHUBA_INJECT_TOKEN, + useValue: importNehubaSpy + } + ], + schemas: [ + CUSTOM_ELEMENTS_SCHEMA + ], + }).compileComponents() + + })) + + it('> component can be created', () => { + const fixture = TestBed.createComponent(NehubaContainer) + const el = fixture.debugElement.componentInstance + expect(el).toBeTruthy() + }) + + describe('> on selectedTemplatechange', () => { + + it('> calls importNehubaPr', async () => { + const fixture = TestBed.createComponent(NehubaContainer) + + const mockStore = TestBed.inject(MockStore) + const newState = { + ...defaultRootState, + viewerState: { + ...defaultRootState.viewerState, + fetchedTemplates: [ bigbrainJson ], + templateSelected: bigbrainJson, + parcellationSelected: bigbrainJson.parcellations[0] + }, + [viewerStateHelperStoreName]: { + fetchedAtlases: [ humanAtlas ], + selectedAtlasId: humanAtlas['@id'] + } + } + + mockStore.setState(newState) + fixture.detectChanges() + expect(importNehubaSpy).toHaveBeenCalled() + }) + }) + + describe('> on selectedparcellation change', () => { + it('> should set ngId of nehubaViewer', () => { + + const fixture = TestBed.createComponent(NehubaContainer) + const el = fixture.debugElement.componentInstance as NehubaContainer + const mockStore = TestBed.inject(MockStore) + const newState = { + ...defaultRootState, + viewerState: { + ...defaultRootState.viewerState, + fetchedTemplates: [ bigbrainJson ], + templateSelected: bigbrainJson, + parcellationSelected: bigbrainJson.parcellations[0] + }, + [viewerStateHelperStoreName]: { + fetchedAtlases: [ humanAtlas ], + selectedAtlasId: humanAtlas['@id'] + } + } + + mockStore.setState(newState) + fixture.detectChanges() + + const setSpy = spyOnProperty(el.nehubaViewer, 'ngIds', 'set') + + const newState2 = { + ...defaultRootState, + viewerState: { + ...defaultRootState.viewerState, + fetchedTemplates: [ bigbrainJson ], + templateSelected: bigbrainJson, + parcellationSelected: bigbrainJson.parcellations[1] + }, + [viewerStateHelperStoreName]: { + fetchedAtlases: [ humanAtlas ], + selectedAtlasId: humanAtlas['@id'] + } + } + + mockStore.setState(newState2) + fixture.detectChanges() + + expect(setSpy).toHaveBeenCalled() + + }) + }) + }) }) \ No newline at end of file diff --git a/src/ui/nehubaContainer/nehubaContainer.component.ts b/src/ui/nehubaContainer/nehubaContainer.component.ts index 005befdf3fd5c6de1b4a520bf6da04fd10452a79..7423481ea933ef3837f354fcb743772c2fa3e49e 100644 --- a/src/ui/nehubaContainer/nehubaContainer.component.ts +++ b/src/ui/nehubaContainer/nehubaContainer.component.ts @@ -1,54 +1,89 @@ import { Component, ElementRef, Input, OnChanges, OnDestroy, OnInit, ViewChild, ChangeDetectorRef, Output, EventEmitter } from "@angular/core"; import { select, Store } from "@ngrx/store"; -import { combineLatest, fromEvent, merge, Observable, of, Subscription, timer, asyncScheduler } from "rxjs"; +import { combineLatest, fromEvent, merge, Observable, of, Subscription, timer, asyncScheduler, BehaviorSubject } from "rxjs"; import { pipeFromArray } from "rxjs/internal/util/pipe"; -import { - buffer, - debounceTime, - distinctUntilChanged, - filter, - map, - mapTo, - scan, - shareReplay, - skip, - startWith, - switchMap, - switchMapTo, - take, - tap, - withLatestFrom, - delayWhen, - throttleTime, -} from "rxjs/operators"; +import { buffer, debounceTime, distinctUntilChanged, filter, map, mapTo, scan, shareReplay, skip, startWith, switchMap, switchMapTo, take, tap, withLatestFrom, delayWhen, throttleTime } from "rxjs/operators"; +import { trigger, state, style, animate, transition } from '@angular/animations' +import { MatDrawer } from "@angular/material/sidenav"; + import { LoggingService } from "src/logging"; -import { FOUR_PANEL, H_ONE_THREE, NG_VIEWER_ACTION_TYPES, SINGLE_PANEL, V_ONE_THREE } from "src/services/state/ngViewerState.store"; -import { SELECT_REGIONS_WITH_ID, VIEWERSTATE_ACTION_TYPES } from "src/services/state/viewerState.store"; -import { ADD_NG_LAYER, generateLabelIndexId, getMultiNgIdsRegionsLabelIndexMap, getNgIds, ILandmark, IOtherLandmarkGeometry, IPlaneLandmarkGeometry, IPointLandmarkGeometry, isDefined, MOUSE_OVER_LANDMARK, NgViewerStateInterface, REMOVE_NG_LAYER, safeFilter } from "src/services/stateStore.service"; +import { generateLabelIndexId, getMultiNgIdsRegionsLabelIndexMap, getNgIds, ILandmark, IOtherLandmarkGeometry, IPlaneLandmarkGeometry, IPointLandmarkGeometry, isDefined, MOUSE_OVER_LANDMARK, NgViewerStateInterface } from "src/services/stateStore.service"; import { getExportNehuba, isSame } from "src/util/fn"; import { AtlasViewerAPIServices, IUserLandmark } from "src/atlasViewer/atlasViewer.apiService.service"; import { NehubaViewerUnit } from "./nehubaViewer/nehubaViewer.component"; -import { getFourPanel, getHorizontalOneThree, getSinglePanel, getVerticalOneThree, calculateSliceZoomFactor, scanSliceViewRenderFn as scanFn, isFirstRow, isFirstCell } from "./util"; -import { NehubaViewerContainerDirective } from "./nehubaViewerInterface/nehubaViewerInterface.directive"; -import { ITunableProp } from "./mobileOverlay/mobileOverlay.component"; import { compareLandmarksChanged } from "src/util/constants"; import { PureContantService } from "src/util"; import { ARIA_LABELS, IDS } from 'common/constants' -import { ngViewerActionSetPerspOctantRemoval } from "src/services/state/ngViewerState.store.helper"; +import { ngViewerActionSetPerspOctantRemoval, PANELS, ngViewerActionToggleMax, ngViewerActionAddNgLayer, ngViewerActionRemoveNgLayer } from "src/services/state/ngViewerState.store.helper"; +import { viewerStateSelectRegionWithIdDeprecated, viewerStateAddUserLandmarks, viewreStateRemoveUserLandmarks } from 'src/services/state/viewerState.store.helper' import { SwitchDirective } from "src/util/directives/switch.directive"; -import { - trigger, - state, - style, - animate, - transition, - -} from '@angular/animations' -import { MatDrawer } from "@angular/material/sidenav"; -import { viewerStateSetConnectivityRegion, viewerStateGetOverlayingAdditionalParcellations, viewerStateSetSelectedRegions, viewerStateRemoveAdditionalLayer } from "src/services/state/viewerState.store.helper"; +import { + viewerStateSetConnectivityRegion, + viewerStateGetOverlayingAdditionalParcellations, + viewerStateSetSelectedRegions, + viewerStateRemoveAdditionalLayer, + viewerStateDblClickOnViewer, +} from "src/services/state/viewerState.store.helper"; + +import { getFourPanel, getHorizontalOneThree, getSinglePanel, getVerticalOneThree, calculateSliceZoomFactor, scanSliceViewRenderFn as scanFn, isFirstRow, isFirstCell } from "./util"; +import { NehubaViewerContainerDirective } from "./nehubaViewerInterface/nehubaViewerInterface.directive"; +import { ITunableProp } from "./mobileOverlay/mobileOverlay.component"; const { MESH_LOADING_STATUS } = IDS +const sortByFreshness: (acc: any[], curr: any[]) => any[] = (acc, curr) => { + + const getLayerName = ({layer} = {layer: {}}) => { + const { name } = layer as any + return name + } + + const newEntries = (curr && curr.filter(entry => { + const name = getLayerName(entry) + return acc.map(getLayerName).indexOf(name) < 0 + })) || [] + + const entryChanged: (itemPrevState, newArr) => boolean = (itemPrevState, newArr) => { + const layerName = getLayerName(itemPrevState) + const { segment } = itemPrevState + const foundItem = newArr?.find((_item) => + getLayerName(_item) === layerName) + + if (foundItem) { + const { segment: foundSegment } = foundItem + return segment !== foundSegment + } else { + /** + * if item was not found in the new array, meaning hovering nothing + */ + return segment !== null + } + } + + const getItemFromLayerName = (item, arr) => { + const foundItem = arr?.find(i => getLayerName(i) === getLayerName(item)) + return foundItem + ? foundItem + : { + layer: item.layer, + segment: null, + } + } + + const getReduceExistingLayers = (newArr) => ([changed, unchanged], _curr) => { + const changedFlag = entryChanged(_curr, newArr) + return changedFlag + ? [ changed.concat( getItemFromLayerName(_curr, newArr) ), unchanged ] + : [ changed, unchanged.concat(_curr) ] + } + + /** + * now, for all the previous layers, separate into changed and unchanged layers + */ + const [changed, unchanged] = acc.reduce(getReduceExistingLayers(curr), [[], []]) + return [...newEntries, ...changed, ...unchanged] +} + const { ZOOM_IN, ZOOM_OUT, @@ -137,8 +172,6 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy { private fetchedSpatialDatasets$: Observable<ILandmark[]> private userLandmarks$: Observable<IUserLandmark[]> - public onHoverSegment$: Observable<any> - public nehubaViewerPerspectiveOctantRemoval$: Observable<boolean> @Input() @@ -147,7 +180,24 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy { @Input() private currentOnHoverObs$: Observable<{segments: any, landmark: any, userLandmark: any}> - public onHoverSegments$: Observable<any[]> + public onHoverSegments$: BehaviorSubject<any[]> = new BehaviorSubject([]) + public onHoverSegment$: Observable<any> = this.onHoverSegments$.pipe( + scan(sortByFreshness, []), + /** + * take the first element after sort by freshness + */ + map(arr => arr[0]), + /** + * map to the older interface + */ + filter(v => !!v), + map(({ segment }) => { + return { + labelIndex: (isNaN(segment) && Number(segment.labelIndex)) || null, + foundRegion: (isNaN(segment) && segment) || null, + } + }), + ) public selectedTemplate: any | null private selectedRegionIndexSet: Set<string> = new Set() @@ -158,7 +208,7 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy { public selectedParcellation: any | null - public nehubaViewer: NehubaViewerUnit + public nehubaViewer: NehubaViewerUnit = null private multiNgIdsRegionsLabelIndexMap: Map<string, Map<number, any>> = new Map() private landmarksLabelIndexMap: Map<number, any> = new Map() private landmarksNameMap: Map<string, number> = new Map() @@ -232,9 +282,9 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy { this.selectedParcellation$ = this.store.pipe( select('viewerState'), - safeFilter('parcellationSelected'), - map(state => state.parcellationSelected), + select('parcellationSelected'), distinctUntilChanged(), + filter(v => !!v) ) this.selectedRegions$ = this.store.pipe( @@ -245,8 +295,7 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy { this.selectedLandmarks$ = this.store.pipe( select('viewerState'), - safeFilter('landmarksSelected'), - map(state => state.landmarksSelected), + select('landmarksSelected'), ) this.selectedPtLandmarks$ = this.selectedLandmarks$.pipe( @@ -255,8 +304,7 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy { this.fetchedSpatialDatasets$ = this.store.pipe( select('dataStore'), - safeFilter('fetchedSpatialData'), - map(state => state.fetchedSpatialData), + select('fetchedSpatialData'), distinctUntilChanged(compareLandmarksChanged), debounceTime(300), ) @@ -338,7 +386,7 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy { this.hideSegmentations$ = this.ngLayers$.pipe( map(state => isDefined(state) - ? state.layers.findIndex(l => l.mixability === 'nonmixable') >= 0 + ? state.layers?.findIndex(l => l.mixability === 'nonmixable') >= 0 : false), ) } @@ -369,7 +417,7 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy { return element } - private findPanelIndex = (panel: HTMLElement) => this.viewPanels.findIndex(p => p === panel) + private findPanelIndex = (panel: HTMLElement) => this.viewPanels?.findIndex(p => p === panel) private _exportNehuba: any get exportNehuba() { @@ -428,25 +476,25 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy { if (!viewPanels.every(v => !!v)) { return } switch (mode) { - case H_ONE_THREE: { + case PANELS.H_ONE_THREE: { const element = this.removeExistingPanels() const newEl = getHorizontalOneThree(viewPanels) element.appendChild(newEl) break; } - case V_ONE_THREE: { + case PANELS.V_ONE_THREE: { const element = this.removeExistingPanels() const newEl = getVerticalOneThree(viewPanels) element.appendChild(newEl) break; } - case FOUR_PANEL: { + case PANELS.FOUR_PANEL: { const element = this.removeExistingPanels() const newEl = getFourPanel(viewPanels) element.appendChild(newEl) break; } - case SINGLE_PANEL: { + case PANELS.SINGLE_PANEL: { const element = this.removeExistingPanels() const newEl = getSinglePanel(viewPanels) element.appendChild(newEl) @@ -478,7 +526,7 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy { ).subscribe(([fetchedSpatialData]) => { this.fetchedSpatialData = fetchedSpatialData - if (this.fetchedSpatialData && this.fetchedSpatialData.length > 0) { + if (this.fetchedSpatialData?.length > 0) { this.nehubaViewer.addSpatialSearch3DLandmarks( this.fetchedSpatialData .map(data => data.geometry.type === 'point' @@ -519,15 +567,14 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy { /* on selecting of new template, remove additional nglayers */ const baseLayerNames = Object.keys(this.selectedTemplate.nehubaConfig.dataset.initialNgState.layers) this.ngLayersRegister.layers - .filter(layer => baseLayerNames.findIndex(l => l === layer.name) < 0) + .filter(layer => baseLayerNames?.findIndex(l => l === layer.name) < 0) .map(l => l.name) .forEach(layerName => { - this.store.dispatch({ - type : REMOVE_NG_LAYER, - layer : { - name : layerName, - }, - }) + this.store.dispatch(ngViewerActionRemoveNgLayer({ + layer: { + name: layerName + } + })) }) }), ) @@ -559,7 +606,7 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy { this.selectedTemplate = templateSelected this.createNewNehuba(templateSelected) const foundParcellation = parcellationSelected - && templateSelected.parcellations.find(parcellation => parcellationSelected.name === parcellation.name) + && templateSelected?.parcellations?.find(parcellation => parcellationSelected.name === parcellation.name) this.handleParcellation(foundParcellation || templateSelected.parcellations[0]) const nehubaConfig = templateSelected.nehubaConfig @@ -584,10 +631,9 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy { return layer }) - this.store.dispatch({ - type : ADD_NG_LAYER, - layer : dispatchLayers, - }) + this.store.dispatch(ngViewerActionAddNgLayer({ + layer: dispatchLayers + })) }) ) @@ -609,8 +655,7 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy { this.selectedParcellation$, this.store.pipe( select('viewerState'), - safeFilter('overwrittenColorMap'), - map(state => state.overwrittenColorMap), + select('overwrittenColorMap'), distinctUntilChanged() ) ).pipe( @@ -641,10 +686,10 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy { this.ngLayers$.subscribe(ngLayersInterface => { if (!this.nehubaViewer) { return } - const newLayers = ngLayersInterface.layers.filter(l => this.ngLayersRegister.layers.findIndex(ol => ol.name === l.name) < 0) - const removeLayers = this.ngLayersRegister.layers.filter(l => ngLayersInterface.layers.findIndex(nl => nl.name === l.name) < 0) + const newLayers = ngLayersInterface.layers.filter(l => this.ngLayersRegister.layers?.findIndex(ol => ol.name === l.name) < 0) + const removeLayers = this.ngLayersRegister.layers.filter(l => ngLayersInterface.layers?.findIndex(nl => nl.name === l.name) < 0) - if (newLayers.length > 0) { + if (newLayers?.length > 0) { const newLayersObj: any = {} newLayers.forEach(({ name, source, ...rest }) => newLayersObj[name] = { ...rest, @@ -661,7 +706,7 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy { this.ngLayersRegister.layers = this.ngLayersRegister.layers.concat(newLayers) } - if (removeLayers.length > 0) { + if (removeLayers?.length > 0) { removeLayers.forEach(l => { if (this.nehubaViewer.removeLayer({ name : l.name, @@ -672,6 +717,10 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy { } }), ) + + this.subscriptions.push( + this.selectedParcellation$.subscribe(this.handleParcellation.bind(this)) + ) /* setup init view state */ @@ -684,7 +733,7 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy { ) this.subscriptions.push(this.selectedRegions$.subscribe(sr => { - if (sr.length ===1) this.setConnectivityRegion(sr[0].name) + if (sr?.length === 1) this.setConnectivityRegion(sr[0].name) this.selectedRegions = sr })) @@ -692,7 +741,7 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy { this.subscriptions.push( this.selectedRegions$.subscribe(regions => { this.selectedRegions = regions - if (regions.length > 0) { + if (regions?.length > 0) { this.matDrawerMinor && this.matDrawerMinor.open() this.navSideDrawerMainSwitch && this.navSideDrawerMainSwitch.open() } @@ -709,14 +758,13 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy { debounceTime(200), ), ), - filter(arr => arr.length >= 2), + filter(arr => arr?.length >= 2), ) .subscribe(() => { const { currentOnHover } = this - this.store.dispatch({ - type : VIEWERSTATE_ACTION_TYPES.DOUBLE_CLICK_ON_VIEWER, - payload: { ...currentOnHover }, - }) + this.store.dispatch(viewerStateDblClickOnViewer({ + payload: { ...currentOnHover } + })) }), ) @@ -738,84 +786,11 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy { public showObliqueSelection$: Observable<boolean> public showObliqueRotate$: Observable<boolean> + private currOnHoverObsSub: Subscription public ngOnChanges() { + this.currOnHoverObsSub && this.currOnHoverObsSub.unsubscribe() if (this.currentOnHoverObs$) { - this.onHoverSegments$ = this.currentOnHoverObs$.pipe( - map(({ segments }) => segments), - ) - - const sortByFreshness: (acc: any[], curr: any[]) => any[] = (acc, curr) => { - - const getLayerName = ({layer} = {layer: {}}) => { - const { name } = layer as any - return name - } - - const newEntries = (curr && curr.filter(entry => { - const name = getLayerName(entry) - return acc.map(getLayerName).indexOf(name) < 0 - })) || [] - - const entryChanged: (itemPrevState, newArr) => boolean = (itemPrevState, newArr) => { - const layerName = getLayerName(itemPrevState) - const { segment } = itemPrevState - const foundItem = newArr.find((_item) => - getLayerName(_item) === layerName) - - if (foundItem) { - const { segment: foundSegment } = foundItem - return segment !== foundSegment - } else { - /** - * if item was not found in the new array, meaning hovering nothing - */ - return segment !== null - } - } - - const getItemFromLayerName = (item, arr) => { - const foundItem = arr.find(i => getLayerName(i) === getLayerName(item)) - return foundItem - ? foundItem - : { - layer: item.layer, - segment: null, - } - } - - const getReduceExistingLayers = (newArr) => ([changed, unchanged], _curr) => { - const changedFlag = entryChanged(_curr, newArr) - return changedFlag - ? [ changed.concat( getItemFromLayerName(_curr, newArr) ), unchanged ] - : [ changed, unchanged.concat(_curr) ] - } - - /** - * now, for all the previous layers, separate into changed and unchanged layers - */ - const [changed, unchanged] = acc.reduce(getReduceExistingLayers(curr), [[], []]) - return [...newEntries, ...changed, ...unchanged] - } - - // TODO to be deprected soon - - this.onHoverSegment$ = this.onHoverSegments$.pipe( - scan(sortByFreshness, []), - /** - * take the first element after sort by freshness - */ - map(arr => arr[0]), - /** - * map to the older interface - */ - filter(v => !!v), - map(({ segment }) => { - return { - labelIndex: (isNaN(segment) && Number(segment.labelIndex)) || null, - foundRegion: (isNaN(segment) && segment) || null, - } - }), - ) + this.currOnHoverObsSub = this.currentOnHoverObs$.subscribe(({ segments }) => this.onHoverSegments$.next(segments)) } } @@ -828,12 +803,9 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy { } public toggleMaximiseMinimise(index: number) { - this.store.dispatch({ - type: NG_VIEWER_ACTION_TYPES.TOGGLE_MAXIMISE, - payload: { - index, - }, - }) + this.store.dispatch(ngViewerActionToggleMax({ + payload: { index } + })) } public tunableMobileProperties: ITunableProp[] = [] @@ -968,18 +940,15 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy { if (!landmarks.every(l => l.position.constructor === Array) || !landmarks.every(l => l.position.every(v => !isNaN(v))) || !landmarks.every(l => l.position.length == 3)) { throw new Error('position needs to be a length 3 tuple of numbers ') } - this.store.dispatch({ - type: VIEWERSTATE_ACTION_TYPES.ADD_USERLANDMARKS, - landmarks, - }) + + this.store.dispatch(viewerStateAddUserLandmarks({ + landmarks + })) }, remove3DLandmarks : landmarkIds => { - this.store.dispatch({ - type: VIEWERSTATE_ACTION_TYPES.REMOVE_USER_LANDMARKS, - payload: { - landmarkIds, - }, - }) + this.store.dispatch(viewreStateRemoveUserLandmarks({ + payload: { landmarkIds } + })) }, hideSegment : (_labelIndex) => { /** @@ -1001,16 +970,14 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy { selectRegionIds.push(generateLabelIndexId({ ngId, labelIndex })) }) }) - this.store.dispatch({ - type : SELECT_REGIONS_WITH_ID, - selectRegionIds, - }) + this.store.dispatch(viewerStateSelectRegionWithIdDeprecated({ + selectRegionIds + })) }, hideAllSegments : () => { - this.store.dispatch({ - type : SELECT_REGIONS_WITH_ID, - selectRegions : [], - }) + this.store.dispatch(viewerStateSelectRegionWithIdDeprecated({ + selectRegionIds: [] + })) }, segmentColourMap : new Map(), getLayersSegmentColourMap: () => this.nehubaViewer.multiNgIdColorMap, diff --git a/src/ui/nehubaContainer/nehubaContainer.style.css b/src/ui/nehubaContainer/nehubaContainer.style.css index 2a6899a96696ddb766dbf047e4883f80e7af8b93..7214f1460274b44198e1dd2289fa5d181137a865 100644 --- a/src/ui/nehubaContainer/nehubaContainer.style.css +++ b/src/ui/nehubaContainer/nehubaContainer.style.css @@ -76,13 +76,6 @@ div#scratch-pad bottom:1em; } -.viewer-status-container -{ - position: absolute; - top: 0; - left: 0; -} - /* if not mobile, then show on hover */ .opacity-crossfade diff --git a/src/ui/nehubaContainer/nehubaContainer.template.html b/src/ui/nehubaContainer/nehubaContainer.template.html index 3701d09e2ba695b953aa152492a20c26a845e781..738cc37f24bc9a3c2e1344f615a56a5519b4cb99 100644 --- a/src/ui/nehubaContainer/nehubaContainer.template.html +++ b/src/ui/nehubaContainer/nehubaContainer.template.html @@ -46,7 +46,7 @@ <!-- sidenav-content --> <mat-drawer class="box-shadow-none pe-none bg-none col-10 col-sm-10 col-md-5 col-lg-4 col-xl-3 col-xxl-2" - mode="push" + mode="side" [opened]="sideNavMasterSwitch.switchState" [autoFocus]="false" (closedStart)="sideNavSwitch.switchState && matDrawerMinor.close()" @@ -69,7 +69,19 @@ </mat-drawer> <mat-drawer-content class="visible position-relative"> - <div *ngIf="viewerLoaded" class="viewer-status-container pe-none m-2"> + <!-- top left overlay --> + <div class="position-absolute top-0 left-0 d-inline-block pe-none"> + <ng-content select="[ui-nehuba-container-overlay-top-left]"> + </ng-content> + </div> + + <!-- top right overlay --> + <div class="position-absolute top-0 right-0 d-inline-block pe-none"> + <ng-content select="[ui-nehuba-container-overlay-top-right]"> + </ng-content> + </div> + + <div *ngIf="viewerLoaded" class="position-absolute z-index-6 top-0 left-0 pe-none m-2"> <button mat-raised-button class="pe-all tab-toggle" (click)="sideNavMasterSwitch.toggle()" @@ -77,12 +89,8 @@ <i class="fas" [ngClass]="sideNavMasterSwitch.switchState ? 'fa-chevron-left' : 'fa-chevron-right'"></i> </button> - <!-- atlas selector --> - <atlas-dropdown-selector class="pe-all"> - </atlas-dropdown-selector> - <!-- StatusCard container--> - <div class="muted-7 mt-4-n"> + <div class="muted-7 d-inline"> <ui-status-card class="pe-all" [selectedTemplateName]="selectedTemplate && selectedTemplate.name" @@ -132,6 +140,9 @@ <div *ngIf="templateSelected$ | async" class="viewer-config-container d-flex align-items-end pe-none"> + + <!-- bottom left overlay --> + <!-- Viewer Selector Container--> <atlas-layer-selector #alSelector="atlasLayerSelector" @@ -179,10 +190,10 @@ (click)="alSelector.selectorExpanded = true" [selected]="selected"> - {{ parcel.groupName ? (parcel.groupName + ' - ') : '' }}{{ parcel.name }} + {{ parcel?.groupName ? (parcel?.groupName + ' - ') : '' }}{{ parcel?.name }} <!-- info icon --> - <ng-template [ngIf]="parcel.originDatasets && parcel.originDatasets.length > 0" + <ng-template [ngIf]="parcel?.originDatasets?.length > 0" [ngIfElse]="infoIconBasic"> <mat-icon @@ -193,20 +204,20 @@ iav-dataset-show-dataset-dialog [iav-dataset-show-dataset-dialog-kgid]="ds['kgId']" [iav-dataset-show-dataset-dialog-kgschema]="ds['kgSchema']" - [iav-dataset-show-dataset-dialog-name]="parcel.properties?.name" - [iav-dataset-show-dataset-dialog-description]="parcel.properties?.description"> + [iav-dataset-show-dataset-dialog-name]="parcel?.properties?.name" + [iav-dataset-show-dataset-dialog-description]="parcel?.properties?.description"> </mat-icon> </ng-template> <ng-template #infoIconBasic> - <mat-icon *ngIf="parcel.properties?.name && parcel.properties?.description" + <mat-icon *ngIf="parcel?.properties?.name && parcel?.properties?.description" fontSet="fas" fontIcon="fa-info-circle" iav-stop="click" iav-dataset-show-dataset-dialog - [iav-dataset-show-dataset-dialog-name]="parcel.properties?.name" - [iav-dataset-show-dataset-dialog-description]="parcel.properties?.description"> + [iav-dataset-show-dataset-dialog-name]="parcel.properties.name" + [iav-dataset-show-dataset-dialog-description]="parcel.properties.description"> </mat-icon> </ng-template> @@ -228,10 +239,13 @@ (click)="matDrawerMinor.open() && sideNavMasterSwitch.open()" [region]="r" class="pe-all" - [ngClass]="{'darktheme':regionDirective.rgbDarkmode === true, 'lighttheme': regionDirective.rgbDarkmode === false}" + [ngClass]="{ + 'darktheme':regionDirective.rgbDarkmode === true, + 'lighttheme': regionDirective.rgbDarkmode === false + }" [style.backgroundColor]="regionDirective.rgbString" #regionDirective="iavRegion"> - <span class="iv-custom-comp text"> + <span class="iv-custom-comp text text-truncate d-inline"> {{ r.name }} </span> <mat-icon @@ -303,7 +317,7 @@ <!-- single region template --> <ng-template #singleRegionTmpl let-region="region"> <ng-template #regionDetailTmpl> - <div class="placeholder-region-detail"> + <div class="placeholder-region-detail mat-elevation-z4"> <span class="text-muted"> Select a region by clicking on the viewer or search from above </span> @@ -314,13 +328,13 @@ <ng-container *ngIf="region; else regionDetailTmpl"> <region-menu [region]="region" - class="side-nav-cover"> + class="side-nav-cover mat-elevation-z4"> </region-menu> </ng-container> <!-- regional features --> - <mat-card class="mt-2 side-nav-cover feature-card d-flex flex-column"> - <mat-card-subtitle class="flex-grow-0 flex-shrink-0"> + <mat-card class="mt-3 overflow-hidden mat-elevation-z4 side-nav-cover feature-card d-flex flex-column"> + <mat-card-subtitle class="flex-grow-0 flex-shrink-0 font-weight-bold"> Regional features </mat-card-subtitle> @@ -364,17 +378,28 @@ </mat-card> - <mat-card *ngIf="selectedParcellation && selectedParcellation.hasAdditionalViewMode - && selectedParcellation.hasAdditionalViewMode.includes('connectivity') && selectedRegions?.length" class="mt-2 side-nav-cover d-flex flex-column"> - <mat-card-subtitle class="flex-grow-0 flex-shrink-0"> + <mat-card class="mt-3 overflow-hidden mat-elevation-z4 side-nav-cover feature-card d-flex flex-column"> + <mat-card-subtitle class="flex-grow-0 flex-shrink-0 font-weight-bold"> Connectivity </mat-card-subtitle> - <mat-card-content class="flex-grow-1 flex-shrink-1 w-100"> + <mat-card-content class="flex-grow-1 flex-shrink-1 w-100 feature-card"> <!-- add connectivity component --> + <ng-container *ngIf="region; else cnctvtyPlaceholderTmpl"> - <connectivity-browser class="pe-all flex-grow-5 flex-shrink-1"> - </connectivity-browser> + <hbp-connectivity-matrix-row + [region]="region.name" + theme="dark" + show-export="true" + show-source="true" + show-title="false" + show-toolbar="false" + show-description="false" + show-dataset-name="false" + custom-dataset-selector="true" + loadurl="https://connectivityquery-connectivity.apps-dev.hbp.eu/connectivity" + dataset-url="https://connectivityquery-connectivity.apps-dev.hbp.eu/studies"> + </hbp-connectivity-matrix-row> </ng-container> <ng-template #cnctvtyPlaceholderTmpl> diff --git a/src/ui/nehubaContainer/pipes/mobileControlNubStyle.pipe.ts b/src/ui/nehubaContainer/pipes/mobileControlNubStyle.pipe.ts index 7e2e4e2a4844fa07fd57c6adc6c7a2276b4fadac..1f70f6b658cb27001bab3415c77aa0ec90d5be70 100644 --- a/src/ui/nehubaContainer/pipes/mobileControlNubStyle.pipe.ts +++ b/src/ui/nehubaContainer/pipes/mobileControlNubStyle.pipe.ts @@ -1,5 +1,6 @@ import { Pipe, PipeTransform } from "@angular/core"; -import { FOUR_PANEL, H_ONE_THREE, SINGLE_PANEL, V_ONE_THREE } from "src/services/state/ngViewerState.store"; +import { PANELS } from 'src/services/state/ngViewerState.store.helper' + @Pipe({ name: 'mobileControlNubStylePipe', @@ -8,18 +9,18 @@ import { FOUR_PANEL, H_ONE_THREE, SINGLE_PANEL, V_ONE_THREE } from "src/services export class MobileControlNubStylePipe implements PipeTransform { public transform(panelMode: string): any { switch (panelMode) { - case SINGLE_PANEL: + case PANELS.SINGLE_PANEL: return { top: '80%', left: '95%', } - case V_ONE_THREE: - case H_ONE_THREE: + case PANELS.V_ONE_THREE: + case PANELS.H_ONE_THREE: return { top: '66.66%', left: '66.66%', } - case FOUR_PANEL: + case PANELS.FOUR_PANEL: default: return { top: '50%', diff --git a/src/ui/nehubaContainer/statusCard/statusCard.style.css b/src/ui/nehubaContainer/statusCard/statusCard.style.css index 0a5c8a77bcbc9e710b534584989f56cd7364c304..4da5942081877f8c7722e3d549d3cdbd77965a88 100644 --- a/src/ui/nehubaContainer/statusCard/statusCard.style.css +++ b/src/ui/nehubaContainer/statusCard/statusCard.style.css @@ -17,4 +17,9 @@ small[onHoverSegment] .share-btn { right: 0; -} \ No newline at end of file +} + +.expandedContainer +{ + width: 20rem; +} diff --git a/src/ui/nehubaContainer/statusCard/statusCard.template.html b/src/ui/nehubaContainer/statusCard/statusCard.template.html index b6d1577d5ac57928a8c886ea573bdd57d6c4a6b8..1caa1d8459d8c7ab975659be9a41b6991d473210 100644 --- a/src/ui/nehubaContainer/statusCard/statusCard.template.html +++ b/src/ui/nehubaContainer/statusCard/statusCard.template.html @@ -1,4 +1,4 @@ -<mat-card *ngIf="showFull; else showMin"> +<mat-card class="expandedContainer" *ngIf="showFull; else showMin"> <mat-card-content> <!-- reset --> diff --git a/src/ui/nehubaContainer/util.ts b/src/ui/nehubaContainer/util.ts index edadec62d60a7c10ff954a35b669aa0e90315412..7efb0abcc211bfe381190467509e4d18ecbd2b0b 100644 --- a/src/ui/nehubaContainer/util.ts +++ b/src/ui/nehubaContainer/util.ts @@ -1,4 +1,4 @@ -import { FOUR_PANEL, H_ONE_THREE, SINGLE_PANEL, V_ONE_THREE } from "src/services/state/ngViewerState.store"; +import { PANELS } from 'src/services/state/ngViewerState.store.helper' const flexContCmnCls = ['w-100', 'h-100', 'd-flex', 'justify-content-center', 'align-items-stretch'] @@ -34,28 +34,28 @@ const bottom = true const mapModeIdxClass = new Map() -mapModeIdxClass.set(FOUR_PANEL, new Map([ +mapModeIdxClass.set(PANELS.FOUR_PANEL, new Map([ [0, { top, left }], [1, { top, right }], [2, { bottom, left }], [3, { right, bottom }], ])) -mapModeIdxClass.set(SINGLE_PANEL, new Map([ +mapModeIdxClass.set(PANELS.SINGLE_PANEL, new Map([ [0, { top, left, right, bottom }], [1, {}], [2, {}], [3, {}], ])) -mapModeIdxClass.set(H_ONE_THREE, new Map([ +mapModeIdxClass.set(PANELS.H_ONE_THREE, new Map([ [0, { top, left, bottom }], [1, { top, right }], [2, { right }], [3, { bottom, right }], ])) -mapModeIdxClass.set(V_ONE_THREE, new Map([ +mapModeIdxClass.set(PANELS.V_ONE_THREE, new Map([ [0, { top, left, right }], [1, { bottom, left }], [2, { bottom }], @@ -98,7 +98,7 @@ export const addTouchSideClasses = (panel: HTMLElement, actualOrderIndex: number export const getHorizontalOneThree = (panels: [HTMLElement, HTMLElement, HTMLElement, HTMLElement]) => { washPanels(panels) - panels.forEach((panel, idx) => addTouchSideClasses(panel, idx, H_ONE_THREE)) + panels.forEach((panel, idx) => addTouchSideClasses(panel, idx, PANELS.H_ONE_THREE)) const majorContainer = makeCol(panels[0]) const minorContainer = makeCol(panels[1], panels[2], panels[3]) @@ -112,7 +112,7 @@ export const getHorizontalOneThree = (panels: [HTMLElement, HTMLElement, HTMLEle export const getVerticalOneThree = (panels: [HTMLElement, HTMLElement, HTMLElement, HTMLElement]) => { washPanels(panels) - panels.forEach((panel, idx) => addTouchSideClasses(panel, idx, V_ONE_THREE)) + panels.forEach((panel, idx) => addTouchSideClasses(panel, idx, PANELS.V_ONE_THREE)) const majorContainer = makeRow(panels[0]) const minorContainer = makeRow(panels[1], panels[2], panels[3]) @@ -126,7 +126,7 @@ export const getVerticalOneThree = (panels: [HTMLElement, HTMLElement, HTMLEleme export const getFourPanel = (panels: [HTMLElement, HTMLElement, HTMLElement, HTMLElement]) => { washPanels(panels) - panels.forEach((panel, idx) => addTouchSideClasses(panel, idx, FOUR_PANEL)) + panels.forEach((panel, idx) => addTouchSideClasses(panel, idx, PANELS.FOUR_PANEL)) const majorContainer = makeRow(panels[0], panels[1]) const minorContainer = makeRow(panels[2], panels[3]) @@ -140,7 +140,7 @@ export const getFourPanel = (panels: [HTMLElement, HTMLElement, HTMLElement, HTM export const getSinglePanel = (panels: [HTMLElement, HTMLElement, HTMLElement, HTMLElement]) => { washPanels(panels) - panels.forEach((panel, idx) => addTouchSideClasses(panel, idx, SINGLE_PANEL)) + panels.forEach((panel, idx) => addTouchSideClasses(panel, idx, PANELS.SINGLE_PANEL)) const majorContainer = makeRow(panels[0]) const minorContainer = makeRow(panels[1], panels[2], panels[3]) diff --git a/src/ui/parcellationRegion/region.directive.ts b/src/ui/parcellationRegion/region.directive.ts index 0ffe3894c5d5a324ca858bef31d55ab021cef88c..fb66743eb6ec06a570b9c91223476d44695d1748 100644 --- a/src/ui/parcellationRegion/region.directive.ts +++ b/src/ui/parcellationRegion/region.directive.ts @@ -1,5 +1,6 @@ import { Directive } from "@angular/core"; import { RegionBase } from "./region.base"; +import { Store } from "@ngrx/store"; @Directive({ selector: '[iav-region]', @@ -7,5 +8,7 @@ import { RegionBase } from "./region.base"; }) export class RegionDirective extends RegionBase{ - + constructor(store: Store<any>){ + super(store) + } } diff --git a/src/ui/ui.module.ts b/src/ui/ui.module.ts index 8b538d649542c813f1262c52ff93ce848d6f433a..d185e1440e52470da9825000e092eed1d198125d 100644 --- a/src/ui/ui.module.ts +++ b/src/ui/ui.module.ts @@ -4,7 +4,7 @@ import { ComponentsModule } from "src/components/components.module"; import { FormsModule, ReactiveFormsModule } from "@angular/forms"; import { LayoutModule } from "src/layouts/layout.module"; import { NehubaContainer } from "./nehubaContainer/nehubaContainer.component"; -import { NehubaViewerUnit, IMPORT_NEHUBA_INJECT_TOKEN } from "./nehubaContainer/nehubaViewer/nehubaViewer.component"; +import { IMPORT_NEHUBA_INJECT_TOKEN } from "./nehubaContainer/nehubaViewer/nehubaViewer.component"; import { GetTemplateImageSrcPipe, ImgSrcSetPipe, SplashScreen } from "./nehubaContainer/splashScreen/splashScreen.component"; import { FilterRegionDataEntries } from "src/util/pipes/filterRegionDataEntries.pipe"; @@ -112,7 +112,7 @@ import { RegionDirective } from "./parcellationRegion/region.directive"; ], declarations : [ NehubaContainer, - NehubaViewerUnit, + SplashScreen, LandmarkUnit, PluginBannerUI, @@ -203,7 +203,7 @@ import { RegionDirective } from "./parcellationRegion/region.directive"; entryComponents : [ /* dynamically created components needs to be declared here */ - NehubaViewerUnit, + LayerBrowser, PluginBannerUI, ActionDialog, @@ -214,7 +214,7 @@ import { RegionDirective } from "./parcellationRegion/region.directive"; CitationsContainer, PluginBannerUI, NehubaContainer, - NehubaViewerUnit, + LayerBrowser, LogoContainer, TemplateParcellationCitationsContainer, diff --git a/src/util/constants.ts b/src/util/constants.ts index 91a7cc447b197bc7d90160e9932eca612487d41b..0236f4e41a896222c77208711a82b836d7f18aae 100644 --- a/src/util/constants.ts +++ b/src/util/constants.ts @@ -29,7 +29,7 @@ export const MIN_REQ_EXPLAINER = ` - Unfortunately, Safari and iOS devices currently do not support **webgl2.0**: <https://webkit.org/status/#specification-webgl-2> ` -export const APPEND_SCRIPT_TOKEN = `APPEND_SCRIPT_TOKEN` +export const APPEND_SCRIPT_TOKEN: InjectionToken<(url: string) => Promise<HTMLScriptElement>> = new InjectionToken(`APPEND_SCRIPT_TOKEN`) export const appendScriptFactory = (document: Document) => { return src => new Promise((rs, rj) => { @@ -41,7 +41,7 @@ export const appendScriptFactory = (document: Document) => { }) } -export const REMOVE_SCRIPT_TOKEN = `REMOVE_SCRIPT_TOKEN` +export const REMOVE_SCRIPT_TOKEN: InjectionToken<(el: HTMLScriptElement) => void> = new InjectionToken(`REMOVE_SCRIPT_TOKEN`) export const removeScriptFactory = (document: Document) => { return (srcEl: HTMLScriptElement) => { @@ -70,6 +70,7 @@ export const getHttpHeader: () => HttpHeaders = () => { export const COLORMAP_IS_JET = `// iav-colormap-is-jet` import { EnumColorMapName, mapKeyColorMap } from './colorMaps' +import { InjectionToken } from "@angular/core" export const getShader = ({ colormap = EnumColorMapName.GREYSCALE,