diff --git a/common/constants.js b/common/constants.js index d94ab3a30d5d7c6fbafb2380aadc40a725e28fed..1849b731b946a42438e8a9be05c6233f2f85bf0a 100644 --- a/common/constants.js +++ b/common/constants.js @@ -23,6 +23,7 @@ // overlay/layout specific SELECT_ATLAS: 'Select a different atlas', CONTEXT_MENU: `Viewer context menu`, + TOGGLE_FRONTAL_OCTANT: `Toggle perspective view frontal octant`, ZOOM_IN: 'Zoom in', ZOOM_OUT: 'Zoom out', MAXIMISE_VIEW: 'Maximise this view', diff --git a/src/services/state/ngViewerState.store.ts b/src/services/state/ngViewerState.store.ts index 2f708a826b8479ec41699b9c2f3528fb78763a5f..8a9136dc4c78be7652c60b5d5a4ace514562e5e2 100644 --- a/src/services/state/ngViewerState.store.ts +++ b/src/services/state/ngViewerState.store.ts @@ -12,6 +12,7 @@ import { PureContantService } from 'src/util'; import { PANELS } from './ngViewerState.store.helper' import { ngViewerActionToggleMax, ngViewerActionClearView, ngViewerActionSetPanelOrder, ngViewerActionSwitchPanelMode, ngViewerActionForceShowSegment, ngViewerActionNehubaReady } from './ngViewerState/actions'; import { generalApplyState } from '../stateStore.helper'; +import { ngViewerSelectorPanelMode, ngViewerSelectorPanelOrder } from './ngViewerState/selectors'; export function mixNgLayers(oldLayers: INgLayerInterface[], newLayers: INgLayerInterface|INgLayerInterface[]): INgLayerInterface[] { if (newLayers instanceof Array) { @@ -233,14 +234,12 @@ export class NgViewerUseEffect implements OnDestroy { ) this.panelOrder$ = this.store$.pipe( - select('ngViewerState'), - select('panelOrder'), + select(ngViewerSelectorPanelOrder), distinctUntilChanged(), ) this.panelMode$ = this.store$.pipe( - select('ngViewerState'), - select('panelMode'), + select(ngViewerSelectorPanelMode), distinctUntilChanged(), ) @@ -258,10 +257,10 @@ export class NgViewerUseEffect implements OnDestroy { this.maximiseOrder$ = toggleMaxmimise$.pipe( withLatestFrom( - combineLatest( + combineLatest([ this.panelOrder$, this.panelMode$, - ), + ]), ), filter(([_action, [_panelOrder, panelMode]]) => panelMode !== PANELS.SINGLE_PANEL), map(([ action, [ oldPanelOrder ] ]) => { diff --git a/src/services/state/ngViewerState/selectors.ts b/src/services/state/ngViewerState/selectors.ts index 85b992c44c585e5d75b997ba69f8ea908a935275..ddfa9f306f2af4ec89a14033028ef1c7f9b60615 100644 --- a/src/services/state/ngViewerState/selectors.ts +++ b/src/services/state/ngViewerState/selectors.ts @@ -15,3 +15,18 @@ export const ngViewerSelectorClearView = createSelector( ngViewerSelectorClearViewEntries, keys => keys.length > 0 ) + +export const ngViewerSelectorPanelOrder = createSelector( + state => state['ngViewerState'], + ngViewerState => ngViewerState.panelOrder +) + +export const ngViewerSelectorPanelMode = createSelector( + state => state['ngViewerState'], + ngViewerState => ngViewerState.panelMode +) + +export const ngViewerSelectorOctantRemoval = createSelector( + state => state['ngViewerState'], + ngViewerState => ngViewerState.octantRemoval +) diff --git a/src/ui/config/config.component.ts b/src/ui/config/config.component.ts index 756c15d1a6e80fd4f49a58dc1282642b331bc07f..36f9e9e0d2c44c374078745bad9d1f8fd7386c23 100644 --- a/src/ui/config/config.component.ts +++ b/src/ui/config/config.component.ts @@ -2,7 +2,7 @@ import { Component, OnDestroy, OnInit } from '@angular/core' import { select, Store } from '@ngrx/store'; import { combineLatest, Observable, Subscription } from 'rxjs'; import { debounceTime, distinctUntilChanged, map, startWith } from 'rxjs/operators'; -import { NG_VIEWER_ACTION_TYPES, SUPPORTED_PANEL_MODES } from 'src/services/state/ngViewerState.store'; +import { SUPPORTED_PANEL_MODES } from 'src/services/state/ngViewerState.store'; import { ngViewerActionSetPanelOrder } from 'src/services/state/ngViewerState.store.helper'; import { VIEWER_CONFIG_ACTION_TYPES, StateInterface as ViewerConfiguration } from 'src/services/state/viewerConfig.store' import { IavRootStoreInterface } from 'src/services/stateStore.service'; @@ -11,6 +11,7 @@ import {MatSlideToggleChange} from "@angular/material/slide-toggle"; import {MatSliderChange} from "@angular/material/slider"; import { PureContantService } from 'src/util'; import { ngViewerActionSwitchPanelMode } from 'src/services/state/ngViewerState/actions'; +import { ngViewerSelectorPanelMode, ngViewerSelectorPanelOrder } from 'src/services/state/ngViewerState/selectors'; const GPU_TOOLTIP = `Higher GPU usage can cause crashes on lower end machines` const ANIMATION_TOOLTIP = `Animation can cause slowdowns in lower end machines` @@ -73,14 +74,12 @@ export class ConfigComponent implements OnInit, OnDestroy { ) this.panelMode$ = this.store.pipe( - select('ngViewerState'), - select('panelMode'), + select(ngViewerSelectorPanelMode), startWith(SUPPORTED_PANEL_MODES[0]), ) this.panelOrder$ = this.store.pipe( - select('ngViewerState'), - select('panelOrder'), + select(ngViewerSelectorPanelOrder), ) this.viewerObliqueRotated$ = this.store.pipe( @@ -93,12 +92,12 @@ export class ConfigComponent implements OnInit, OnDestroy { distinctUntilChanged(), ) - this.panelTexts$ = combineLatest( + this.panelTexts$ = combineLatest([ this.panelOrder$.pipe( map(string => string.split('').map(s => Number(s))), ), this.viewerObliqueRotated$, - ).pipe( + ]).pipe( map(([arr, isObliqueRotated]) => arr.map(idx => (isObliqueRotated ? OBLIQUE_ROOT_TEXT_ORDER : ROOT_TEXT_ORDER)[idx]) as [string, string, string, string]), startWith(ROOT_TEXT_ORDER), ) diff --git a/src/ui/config/currentLayout/currentLayout.component.ts b/src/ui/config/currentLayout/currentLayout.component.ts index 40e8e8c4943de2fdb9139dc21ee62de4856e7ed7..313f1cd93467cb08955bd92f2dc728568427bf62 100644 --- a/src/ui/config/currentLayout/currentLayout.component.ts +++ b/src/ui/config/currentLayout/currentLayout.component.ts @@ -3,6 +3,7 @@ import { select, Store } from "@ngrx/store"; import { Observable } from "rxjs"; import { startWith } from "rxjs/operators"; import { SUPPORTED_PANEL_MODES } from "src/services/state/ngViewerState.store"; +import { ngViewerSelectorPanelMode } from "src/services/state/ngViewerState/selectors"; import { IavRootStoreInterface } from "src/services/stateStore.service"; @Component({ @@ -22,8 +23,7 @@ export class CurrentLayout { private store$: Store<IavRootStoreInterface>, ) { this.panelMode$ = this.store$.pipe( - select('ngViewerState'), - select('panelMode'), + select(ngViewerSelectorPanelMode), startWith(SUPPORTED_PANEL_MODES[0]), ) } diff --git a/src/ui/nehubaContainer/maximisePanelButton/maximisePanelButton.component.ts b/src/ui/nehubaContainer/maximisePanelButton/maximisePanelButton.component.ts index 36b2d9e882bb5a22b939c55902d7ecf84871a012..b3e5745f92e2ea7e9dbb2d95cc2298375b3a49b3 100644 --- a/src/ui/nehubaContainer/maximisePanelButton/maximisePanelButton.component.ts +++ b/src/ui/nehubaContainer/maximisePanelButton/maximisePanelButton.component.ts @@ -4,6 +4,7 @@ import { Observable } from "rxjs"; import { distinctUntilChanged, map } from "rxjs/operators"; import { PANELS } from 'src/services/state/ngViewerState.store.helper' import { ARIA_LABELS } from 'common/constants' +import { ngViewerSelectorPanelMode, ngViewerSelectorPanelOrder } from "src/services/state/ngViewerState/selectors"; const { MAXIMISE_VIEW, @@ -34,14 +35,12 @@ export class MaximmisePanelButton { private store$: Store<any>, ) { this.panelMode$ = this.store$.pipe( - select('ngViewerState'), - select('panelMode'), + select(ngViewerSelectorPanelMode), distinctUntilChanged(), ) this.panelOrder$ = this.store$.pipe( - select('ngViewerState'), - select('panelOrder'), + select(ngViewerSelectorPanelOrder), distinctUntilChanged(), ) diff --git a/src/ui/nehubaContainer/nehubaContainer.component.spec.ts b/src/ui/nehubaContainer/nehubaContainer.component.spec.ts index 0d3f098c077e9b26853c1a0fb5177a67d90c89dc..966569fe94484d56131a535dad0d45f0413ea6cc 100644 --- a/src/ui/nehubaContainer/nehubaContainer.component.spec.ts +++ b/src/ui/nehubaContainer/nehubaContainer.component.spec.ts @@ -1,5 +1,5 @@ -import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core' -import { TestBed, async } from "@angular/core/testing" +import { CUSTOM_ELEMENTS_SCHEMA, DebugElement } from '@angular/core' +import { TestBed, async, ComponentFixture, fakeAsync, tick, flush, discardPeriodicTasks } from "@angular/core/testing" import { NehubaContainer } from "./nehubaContainer.component" import { provideMockStore, MockStore } from "@ngrx/store/testing" import { defaultRootState } from 'src/services/stateStore.service' @@ -39,11 +39,16 @@ import { ARIA_LABELS } from 'common/constants' import { NoopAnimationsModule } from '@angular/platform-browser/animations' import { RegionAccordionTooltipTextPipe } from '../util' import { hot } from 'jasmine-marbles' +import { ngViewerSelectorPanelMode, ngViewerSelectorPanelOrder } from 'src/services/state/ngViewerState/selectors' +import { PANELS } from 'src/services/state/ngViewerState/constants' const { TOGGLE_SIDE_PANEL, EXPAND, - COLLAPSE + COLLAPSE, + ZOOM_IN, + ZOOM_OUT, + TOGGLE_FRONTAL_OCTANT } = ARIA_LABELS const _bigbrainJson = require('!json-loader!src/res/ext/bigbrain.json') @@ -420,5 +425,211 @@ describe('> nehubaContainer.component.ts', () => { it('> if something (region features/connectivity) exists, placeh holder text should be hdiden') }) }) + + describe('> panelCtrl', () => { + let fixture: ComponentFixture<NehubaContainer> + const setViewerLoaded = () => { + fixture.componentInstance.viewerLoaded = true + } + const ctrlElementIsVisible = (el: DebugElement) => { + const visible = (el.nativeElement as HTMLElement).getAttribute('data-viewer-controller-visible') + return visible === 'true' + } + beforeEach(() => { + fixture = TestBed.createComponent(NehubaContainer) + }) + it('> on start, all four ctrl panels exists', () => { + fixture.detectChanges() + setViewerLoaded() + fixture.detectChanges() + for (const idx of [0, 1, 2, 3]) { + const el = fixture.debugElement.query( + By.css(`[data-viewer-controller-index="${idx}"]`) + ) + expect(el).toBeTruthy() + } + }) + + it('> on start all four ctrl panels are invisible', () => { + + fixture.detectChanges() + setViewerLoaded() + fixture.detectChanges() + for (const idx of [0, 1, 2, 3]) { + const el = fixture.debugElement.query( + By.css(`[data-viewer-controller-index="${idx}"]`) + ) + expect(ctrlElementIsVisible(el)).toBeFalsy() + } + }) + + describe('> on hover, only the hovered panel have ctrl shown', () => { + + for (const idx of [0, 1, 2, 3]) { + + it(`> on hoveredPanelIndices$ emit ${idx}, the panel index ${idx} ctrl becomes visible`, fakeAsync(() => { + fixture.detectChanges() + const findPanelIndexSpy = spyOn<any>(fixture.componentInstance, 'findPanelIndex').and.callFake(() => { + return idx + }) + setViewerLoaded() + fixture.detectChanges() + const nativeElement = fixture.componentInstance['elementRef'].nativeElement + nativeElement.dispatchEvent(new MouseEvent('mouseover', { bubbles: true })) + + /** + * assert findPanelIndex called with event.target, i.e. native element in thsi case + */ + expect(findPanelIndexSpy).toHaveBeenCalledWith(nativeElement) + tick(200) + fixture.detectChanges() + + /** + * every panel index should be non visible + * only when idx matches, it can be visible + * n.b. this does not test visual visibility (which is controlled by extra-style.css) + * (which is also affected by global [ismobile] configuration) + * + * this merely test the unit logic, and sets the flag appropriately + */ + for (const iterativeIdx of [0, 1, 2, 3]) { + const el = fixture.debugElement.query( + By.css(`[data-viewer-controller-index="${iterativeIdx}"]`) + ) + if (iterativeIdx === idx) { + expect(ctrlElementIsVisible(el)).toBeTruthy() + } else { + expect(ctrlElementIsVisible(el)).toBeFalsy() + } + } + discardPeriodicTasks() + })) + } + + }) + + describe('> on maximise top right slice panel (idx 1)', () => { + beforeEach(() => { + const mockStore = TestBed.inject(MockStore) + mockStore.overrideSelector(ngViewerSelectorPanelMode, PANELS.SINGLE_PANEL) + mockStore.overrideSelector(ngViewerSelectorPanelOrder, '1230') + + fixture.detectChanges() + setViewerLoaded() + fixture.detectChanges() + }) + it('> toggle front octant btn not visible', () => { + + const toggleBtn = fixture.debugElement.query( + By.css(`[cell-i] [aria-label="${TOGGLE_FRONTAL_OCTANT}"]`) + ) + expect(toggleBtn).toBeFalsy() + }) + + it('> zoom in and out btns are visible', () => { + + const zoomInBtn = fixture.debugElement.query( + By.css(`[cell-i] [aria-label="${ZOOM_IN}"]`) + ) + + const zoomOutBtn = fixture.debugElement.query( + By.css(`[cell-i] [aria-label="${ZOOM_OUT}"]`) + ) + + expect(zoomInBtn).toBeTruthy() + expect(zoomOutBtn).toBeTruthy() + }) + + it('> zoom in btn calls fn with right param', () => { + const zoomViewSpy = spyOn(fixture.componentInstance, 'zoomNgView') + + const zoomInBtn = fixture.debugElement.query( + By.css(`[cell-i] [aria-label="${ZOOM_IN}"]`) + ) + zoomInBtn.triggerEventHandler('click', null) + expect(zoomViewSpy).toHaveBeenCalled() + const { args } = zoomViewSpy.calls.first() + expect(args[0]).toEqual(1) + /** + * zoom in < 1 + */ + expect(args[1]).toBeLessThan(1) + }) + it('> zoom out btn calls fn with right param', () => { + const zoomViewSpy = spyOn(fixture.componentInstance, 'zoomNgView') + + const zoomOutBtn = fixture.debugElement.query( + By.css(`[cell-i] [aria-label="${ZOOM_OUT}"]`) + ) + zoomOutBtn.triggerEventHandler('click', null) + expect(zoomViewSpy).toHaveBeenCalled() + const { args } = zoomViewSpy.calls.first() + expect(args[0]).toEqual(1) + /** + * zoom out > 1 + */ + expect(args[1]).toBeGreaterThan(1) + }) + }) + describe('> on maximise perspective panel', () => { + beforeEach(() => { + const mockStore = TestBed.inject(MockStore) + mockStore.overrideSelector(ngViewerSelectorPanelMode, PANELS.SINGLE_PANEL) + mockStore.overrideSelector(ngViewerSelectorPanelOrder, '3012') + + fixture.detectChanges() + setViewerLoaded() + fixture.detectChanges() + }) + it('> toggle octant btn visible and functional', () => { + const setOctantRemovalSpy = spyOn(fixture.componentInstance, 'setOctantRemoval') + + const toggleBtn = fixture.debugElement.query( + By.css(`[cell-i] [aria-label="${TOGGLE_FRONTAL_OCTANT}"]`) + ) + expect(toggleBtn).toBeTruthy() + toggleBtn.nativeElement.dispatchEvent( + new MouseEvent('click', { bubbles: true }) + ) + expect(setOctantRemovalSpy).toHaveBeenCalled() + }) + + it('> zoom in btn visible and functional', () => { + const zoomViewSpy = spyOn(fixture.componentInstance, 'zoomNgView') + + const zoomInBtn = fixture.debugElement.query( + By.css(`[cell-i] [aria-label="${ZOOM_IN}"]`) + ) + expect(zoomInBtn).toBeTruthy() + + zoomInBtn.triggerEventHandler('click', null) + expect(zoomViewSpy).toHaveBeenCalled() + const { args } = zoomViewSpy.calls.first() + expect(args[0]).toEqual(3) + /** + * zoom in < 1 + */ + expect(args[1]).toBeLessThan(1) + }) + it('> zoom out btn visible and functional', () => { + const zoomViewSpy = spyOn(fixture.componentInstance, 'zoomNgView') + + const zoomOutBtn = fixture.debugElement.query( + By.css(`[cell-i] [aria-label="${ZOOM_OUT}"]`) + ) + expect(zoomOutBtn).toBeTruthy() + + zoomOutBtn.triggerEventHandler('click', null) + expect(zoomViewSpy).toHaveBeenCalled() + const { args } = zoomViewSpy.calls.first() + expect(args[0]).toEqual(3) + /** + * zoom in < 1 + */ + expect(args[1]).toBeGreaterThan(1) + }) + + }) + }) }) -}) \ No newline at end of file +}) diff --git a/src/ui/nehubaContainer/nehubaContainer.component.ts b/src/ui/nehubaContainer/nehubaContainer.component.ts index 7f0e469ac660e75d0e8109ca52611ca80884c554..319d2be8b85ceb2140836dc64a09b34e15183640 100644 --- a/src/ui/nehubaContainer/nehubaContainer.component.ts +++ b/src/ui/nehubaContainer/nehubaContainer.component.ts @@ -38,6 +38,7 @@ import { NehubaViewerContainerDirective } from "./nehubaViewerInterface/nehubaVi import { ITunableProp } from "./mobileOverlay/mobileOverlay.component"; import {ConnectivityBrowserComponent} from "src/ui/connectivityBrowser/connectivityBrowser.component"; import { viewerStateMouseOverCustomLandmark } from "src/services/state/viewerState/actions"; +import { ngViewerSelectorOctantRemoval, ngViewerSelectorPanelMode, ngViewerSelectorPanelOrder } from "src/services/state/ngViewerState/selectors"; const { MESH_LOADING_STATUS } = IDS @@ -97,10 +98,11 @@ const sortByFreshness: (acc: any[], curr: any[]) => any[] = (acc, curr) => { const { ZOOM_IN, ZOOM_OUT, + TOGGLE_FRONTAL_OCTANT, TOGGLE_SIDE_PANEL, EXPAND, COLLAPSE, - ADDITIONAL_VOLUME_CONTROL + ADDITIONAL_VOLUME_CONTROL, } = ARIA_LABELS @Component({ @@ -149,6 +151,7 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy { public CONST = CONST public ARIA_LABEL_ZOOM_IN = ZOOM_IN public ARIA_LABEL_ZOOM_OUT = ZOOM_OUT + public ARIA_LABEL_TOGGLE_FRONTAL_OCTANT = TOGGLE_FRONTAL_OCTANT public ARIA_LABEL_TOGGLE_SIDE_PANEL = TOGGLE_SIDE_PANEL public ARIA_LABEL_EXPAND = EXPAND public ARIA_LABEL_COLLAPSE = COLLAPSE @@ -299,20 +302,17 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy { this.useMobileUI$ = this.pureConstantService.useTouchUI$ this.nehubaViewerPerspectiveOctantRemoval$ = this.store.pipe( - select('ngViewerState'), - select('octantRemoval') + select(ngViewerSelectorOctantRemoval), ) this.panelMode$ = this.store.pipe( - select('ngViewerState'), - select('panelMode'), + select(ngViewerSelectorPanelMode), distinctUntilChanged(), shareReplay(1), ) this.panelOrder$ = this.store.pipe( - select('ngViewerState'), - select('panelOrder'), + select(ngViewerSelectorPanelOrder), distinctUntilChanged(), shareReplay(1), ) @@ -322,10 +322,10 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy { select('nehubaReady'), distinctUntilChanged(), filter(v => !!v), - switchMapTo(combineLatest( + switchMapTo(combineLatest([ this.panelMode$, this.panelOrder$, - )), + ])), ) this.selectedLandmarks$ = this.store.pipe( @@ -661,7 +661,7 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy { this.subscriptions.push( - combineLatest( + combineLatest([ this.selectedRegions$.pipe( distinctUntilChanged(), ), @@ -678,7 +678,7 @@ export class NehubaContainer implements OnInit, OnChanges, OnDestroy { select('overwrittenColorMap'), distinctUntilChanged() ) - ).pipe( + ]).pipe( delayWhen(() => timer()) ).subscribe(([regions, hideSegmentFlag, forceShowSegment, selectedParcellation, overwrittenColorMap]) => { if (!this.nehubaViewer) { return } diff --git a/src/ui/nehubaContainer/nehubaContainer.template.html b/src/ui/nehubaContainer/nehubaContainer.template.html index 6aee271815eb369c30ee9b753afaf8abc1355963..364093d9b98024013f18dabf06aec67bd51474f7 100644 --- a/src/ui/nehubaContainer/nehubaContainer.template.html +++ b/src/ui/nehubaContainer/nehubaContainer.template.html @@ -459,7 +459,10 @@ </div> <!-- maximise/minimise button --> - <ng-container *ngTemplateOutlet="panelCtrlTmpl; context: { panelIndex: 3, visible: (panelOrder$ | async | reorderPanelIndexPipe : ( hoveredPanelIndices$ | async )) === 3 }"> + <ng-container *ngTemplateOutlet="panelCtrlTmpl; context: { + panelIndex: panelOrder$ | async | getNthElement : 3 | parseAsNumber, + visible: (panelOrder$ | async | reorderPanelIndexPipe : ( hoveredPanelIndices$ | async )) === 3 + }"> </ng-container> <!-- mesh loading is still weird --> @@ -505,7 +508,10 @@ </nehuba-2dlandmark-unit> <!-- maximise/minimise button --> - <ng-container *ngTemplateOutlet="panelCtrlTmpl; context: { panelIndex: panelIndex, visible: (panelOrder$ | async | reorderPanelIndexPipe : ( hoveredPanelIndices$ | async )) === panelIndex }"> + <ng-container *ngTemplateOutlet="panelCtrlTmpl; context: { + panelIndex: panelOrder$ | async | getNthElement : panelIndex | parseAsNumber, + visible: (panelOrder$ | async | reorderPanelIndexPipe : ( hoveredPanelIndices$ | async )) === panelIndex + }"> </ng-container> <div *ngIf="(sliceViewLoadingMain$ | async)[panelIndex]" class="loadingIndicator"> @@ -523,7 +529,9 @@ let-visible="visible"> <div class="opacity-crossfade always-show-touchdevice pe-all overlay-btn-container" - [ngClass]="{ onHover: visible }"> + [ngClass]="{ onHover: visible }" + [attr.data-viewer-controller-visible]="visible" + [attr.data-viewer-controller-index]="panelIndex"> <!-- perspective specific control --> <ng-container *ngIf="panelIndex === 3"> @@ -559,6 +567,7 @@ <button (click)="setOctantRemoval(!state)" mat-icon-button + [attr.aria-label]="ARIA_LABEL_TOGGLE_FRONTAL_OCTANT" color="primary"> <!-- octant removal is true --> diff --git a/src/ui/nehubaContainer/nehubaViewerInterface/nehubaViewerInterface.directive.ts b/src/ui/nehubaContainer/nehubaViewerInterface/nehubaViewerInterface.directive.ts index 74d8f40197f3b99d1d672888dea30c05ff03028f..c4748e0900f9c6328a42ffbab69e4f0b2f6c446d 100644 --- a/src/ui/nehubaContainer/nehubaViewerInterface/nehubaViewerInterface.directive.ts +++ b/src/ui/nehubaContainer/nehubaViewerInterface/nehubaViewerInterface.directive.ts @@ -13,6 +13,7 @@ import { takeOnePipe } from "../nehubaContainer.component"; import { ngViewerActionNehubaReady } from "src/services/state/ngViewerState/actions"; import { viewerStateMouseOverCustomLandmarkInPerspectiveView } from "src/services/state/viewerState/actions"; import { viewerStateStandAloneVolumes } from "src/services/state/viewerState/selectors"; +import { ngViewerSelectorOctantRemoval } from "src/services/state/ngViewerState/selectors"; const defaultNehubaConfig = { "configName": "", @@ -295,8 +296,7 @@ export class NehubaViewerContainerDirective implements OnInit, OnDestroy{ ) this.nehubaViewerPerspectiveOctantRemoval$ = this.store$.pipe( - select('ngViewerState'), - select('octantRemoval') + select(ngViewerSelectorOctantRemoval), ) } diff --git a/src/ui/nehubaContainer/touchSideClass.directive.ts b/src/ui/nehubaContainer/touchSideClass.directive.ts index f10e11d71845c5f3e61b62ec5140b80079975961..edaa9823ff6af39756e0f51e16c91cee40d91022 100644 --- a/src/ui/nehubaContainer/touchSideClass.directive.ts +++ b/src/ui/nehubaContainer/touchSideClass.directive.ts @@ -2,6 +2,7 @@ import { Directive, ElementRef, Input, OnDestroy, OnInit } from "@angular/core"; import { select, Store } from "@ngrx/store"; import { Observable, Subscription } from "rxjs"; import { distinctUntilChanged, tap } from "rxjs/operators"; +import { ngViewerSelectorPanelMode } from "src/services/state/ngViewerState/selectors"; import { IavRootStoreInterface } from "src/services/stateStore.service"; import { addTouchSideClasses, removeTouchSideClasses } from "./util"; @@ -25,8 +26,7 @@ export class TouchSideClass implements OnDestroy, OnInit { ) { this.panelMode$ = this.store$.pipe( - select('ngViewerState'), - select('panelMode'), + select(ngViewerSelectorPanelMode), distinctUntilChanged(), tap(mode => this.panelMode = mode), ) diff --git a/src/util/pipes/getNthElement.pipe.ts b/src/util/pipes/getNthElement.pipe.ts new file mode 100644 index 0000000000000000000000000000000000000000..d4b27720b903731fa8c5cb0526380030854b7d4d --- /dev/null +++ b/src/util/pipes/getNthElement.pipe.ts @@ -0,0 +1,11 @@ +import { Pipe, PipeTransform } from "@angular/core"; + +@Pipe({ + name: 'getNthElement' +}) +export class GetNthElementPipe<T> implements PipeTransform{ + public transform(array: T[], idx: number): T{ + if (!array[idx]) throw new Error(`[GetNthElementPipe] accessor error`) + return array[idx] + } +} diff --git a/src/util/pipes/parseAsNumber.pipe.ts b/src/util/pipes/parseAsNumber.pipe.ts new file mode 100644 index 0000000000000000000000000000000000000000..40688b6b9847f3fafce2d8c1dddfb64e1355550a --- /dev/null +++ b/src/util/pipes/parseAsNumber.pipe.ts @@ -0,0 +1,12 @@ +import { Pipe, PipeTransform } from "@angular/core"; + +@Pipe({ + name: 'parseAsNumber' +}) + +export class ParseAsNumberPipe implements PipeTransform{ + public transform(input: string | string[]): number | number[]{ + if (input instanceof Array) return input.map(v => Number(v)) + return Number(input) + } +} diff --git a/src/util/util.module.ts b/src/util/util.module.ts index 4c8bf1ce5bc6b597e1bc9229c02f48df74c6d949..f29aa368ea5931808431ac7f5ca87f3761655c04 100644 --- a/src/util/util.module.ts +++ b/src/util/util.module.ts @@ -16,6 +16,8 @@ import { LayoutModule } from "@angular/cdk/layout"; import { MapToPropertyPipe } from "./pipes/mapToProperty.pipe"; import {ClickOutsideDirective} from "src/util/directives/clickOutside.directive"; import { CounterDirective } from "./directives/counter.directive"; +import { GetNthElementPipe } from "./pipes/getNthElement.pipe"; +import { ParseAsNumberPipe } from "./pipes/parseAsNumber.pipe"; @NgModule({ imports:[ @@ -36,7 +38,9 @@ import { CounterDirective } from "./directives/counter.directive"; MediaQueryDirective, MapToPropertyPipe, ClickOutsideDirective, - CounterDirective + CounterDirective, + GetNthElementPipe, + ParseAsNumberPipe, ], exports: [ FilterNullPipe, @@ -53,7 +57,9 @@ import { CounterDirective } from "./directives/counter.directive"; MediaQueryDirective, MapToPropertyPipe, ClickOutsideDirective, - CounterDirective + CounterDirective, + GetNthElementPipe, + ParseAsNumberPipe, ] })