diff --git a/src/atlasComponents/userAnnotations/filterAnnotationBySpace.pipe.ts b/src/atlasComponents/userAnnotations/filterAnnotationBySpace.pipe.ts
index 69506ed32..fb6500281 100644
--- a/src/atlasComponents/userAnnotations/filterAnnotationBySpace.pipe.ts
+++ b/src/atlasComponents/userAnnotations/filterAnnotationBySpace.pipe.ts
@@ -1,13 +1,20 @@
import { Pipe, PipeTransform } from "@angular/core";
import { IAnnotationGeometry } from "./tools/type";
+type TOpts = {
+ reverse?: boolean
+}
+
@Pipe({
name: 'filterAnnotationsBySpace',
pure: true
})
export class FilterAnnotationsBySpace implements PipeTransform{
- public transform(annotations: IAnnotationGeometry[], space: { '@id': string }): IAnnotationGeometry[]{
- return annotations.filter(ann => ann.space["@id"] === space["@id"])
+ public transform(annotations: IAnnotationGeometry[], space: { '@id': string }, opts?: TOpts): IAnnotationGeometry[]{
+ const { reverse = false } = opts || {}
+ return reverse
+ ? annotations.filter(ann => ann.space["@id"] !== space["@id"])
+ : annotations.filter(ann => ann.space["@id"] === space["@id"])
}
}
\ No newline at end of file
diff --git a/src/atlasComponents/userAnnotations/tools/service.ts b/src/atlasComponents/userAnnotations/tools/service.ts
index 24c26c5a2..952dbe916 100644
--- a/src/atlasComponents/userAnnotations/tools/service.ts
+++ b/src/atlasComponents/userAnnotations/tools/service.ts
@@ -103,6 +103,20 @@ export class ModularUserAnnotationToolService implements OnDestroy{
scanCollapse(),
shareReplay(1),
)
+
+ public otherSpaceManagedAnnotations$ = combineLatest([
+ this.selectedTmpl$,
+ this.managedAnnotations$
+ ]).pipe(
+ map(([tmpl, annts]) => {
+ return this.filterAnnotationBySpacePipe.transform(
+ annts,
+ tmpl,
+ { reverse: true }
+ )
+ })
+ )
+
public spaceFilteredManagedAnnotations$ = combineLatest([
this.selectedTmpl$,
this.managedAnnotations$
diff --git a/src/components/confirmDialog/confirmDialog.component.ts b/src/components/confirmDialog/confirmDialog.component.ts
index 5c3d9eb02..ec2e20c9f 100644
--- a/src/components/confirmDialog/confirmDialog.component.ts
+++ b/src/components/confirmDialog/confirmDialog.component.ts
@@ -25,12 +25,15 @@ export class ConfirmDialogComponent {
@Input()
public markdown: string
+ public hideActionBar = false
+
constructor(@Inject(MAT_DIALOG_DATA) data: any) {
- const { title = null, message = null, markdown, okBtnText, cancelBtnText} = data || {}
+ const { title = null, message = null, markdown, okBtnText, cancelBtnText, hideActionBar} = data || {}
if (title) this.title = title
if (message) this.message = message
if (markdown) this.markdown = markdown
if (okBtnText) this.okBtnText = okBtnText
if (cancelBtnText) this.cancelBtnText = cancelBtnText
+ if (hideActionBar) this.hideActionBar = hideActionBar
}
}
diff --git a/src/components/confirmDialog/confirmDialog.template.html b/src/components/confirmDialog/confirmDialog.template.html
index 401261f1a..f5f054946 100644
--- a/src/components/confirmDialog/confirmDialog.template.html
+++ b/src/components/confirmDialog/confirmDialog.template.html
@@ -17,7 +17,7 @@
-
+
diff --git a/src/services/dialogService.service.ts b/src/services/dialogService.service.ts
index c202eda58..4426ef8c7 100644
--- a/src/services/dialogService.service.ts
+++ b/src/services/dialogService.service.ts
@@ -3,6 +3,10 @@ import { ConfirmDialogComponent } from "src/components/confirmDialog/confirmDial
import { DialogComponent } from "src/components/dialog/dialog.component";
import {MatDialog, MatDialogRef} from "@angular/material/dialog";
+type TCancellable = {
+ abort: () => void
+}
+
@Injectable({
providedIn: 'root',
})
@@ -16,13 +20,26 @@ export class DialogService {
}
+ public blockUserInteraction(config: Partial): TCancellable {
+ const dialogRef = this.dialog.open(ConfirmDialogComponent, {
+ data: {
+ ...config,
+ hideActionBar: true
+ },
+ hasBackdrop: true,
+ disableClose: true
+ })
+ const abort = () => dialogRef.close()
+ return { abort }
+ }
+
public getUserConfirm(config: Partial = {}): Promise {
this.confirmDialogRef = this.dialog.open(ConfirmDialogComponent, {
data: config,
})
return new Promise((resolve, reject) => this.confirmDialogRef.afterClosed()
.subscribe(val => {
- if (val) { resolve() } else { reject('User cancelled') }
+ if (val) { resolve('') } else { reject('User cancelled') }
},
reject,
() => this.confirmDialogRef = null))
diff --git a/src/services/state/ngViewerState.store.ts b/src/services/state/ngViewerState.store.ts
index 694a315fb..f0ad5000d 100644
--- a/src/services/state/ngViewerState.store.ts
+++ b/src/services/state/ngViewerState.store.ts
@@ -9,7 +9,7 @@ import { HttpClient } from '@angular/common/http';
import { INgLayerInterface, ngViewerActionAddNgLayer, ngViewerActionRemoveNgLayer, ngViewerActionSetPerspOctantRemoval } from './ngViewerState.store.helper'
import { PureContantService } from 'src/util';
import { PANELS } from './ngViewerState.store.helper'
-import { ngViewerActionToggleMax, ngViewerActionClearView, ngViewerActionSetPanelOrder, ngViewerActionSwitchPanelMode, ngViewerActionForceShowSegment, ngViewerActionNehubaReady } from './ngViewerState/actions';
+import { ngViewerActionToggleMax, ngViewerActionClearView, ngViewerActionSetPanelOrder, ngViewerActionSwitchPanelMode, ngViewerActionForceShowSegment, ngViewerActionNehubaReady, ngViewerActionCycleViews } from './ngViewerState/actions';
import { generalApplyState } from '../stateStore.helper';
import { ngViewerSelectorPanelMode, ngViewerSelectorPanelOrder } from './ngViewerState/selectors';
import { uiActionSnackbarMessage } from './uiState/actions';
@@ -172,9 +172,6 @@ export class NgViewerUseEffect implements OnDestroy {
@Effect()
public cycleViews$: Observable
- @Effect()
- public spacebarListener$: Observable
-
@Effect()
public removeAllNonBaseLayers$: Observable
@@ -255,7 +252,7 @@ export class NgViewerUseEffect implements OnDestroy {
)
this.cycleViews$ = this.actions.pipe(
- ofType(ACTION_TYPES.CYCLE_VIEWS),
+ ofType(ngViewerActionCycleViews.type),
withLatestFrom(this.panelOrder$),
map(([_, panelOrder]) => {
return ngViewerActionSetPanelOrder({
@@ -351,15 +348,6 @@ export class NgViewerUseEffect implements OnDestroy {
})),
)
- this.spacebarListener$ = fromEvent(document.body, 'keydown', { capture: true }).pipe(
- filter((ev: KeyboardEvent) => ev.key === ' ' && (ev.target as HTMLElement).classList.contains('neuroglancer-panel')),
- withLatestFrom(this.panelMode$),
- filter(([_ , panelMode]) => panelMode === PANELS.SINGLE_PANEL),
- mapTo({
- type: ACTION_TYPES.CYCLE_VIEWS,
- }),
- )
-
/**
* simplify with layer browser
*/
@@ -427,7 +415,6 @@ export class NgViewerUseEffect implements OnDestroy {
export { INgLayerInterface }
const ACTION_TYPES = {
- CYCLE_VIEWS: 'CYCLE_VIEWS',
REMOVE_ALL_NONBASE_LAYERS: `REMOVE_ALL_NONBASE_LAYERS`,
}
diff --git a/src/services/state/ngViewerState/actions.ts b/src/services/state/ngViewerState/actions.ts
index 7f864defa..c504a085a 100644
--- a/src/services/state/ngViewerState/actions.ts
+++ b/src/services/state/ngViewerState/actions.ts
@@ -66,3 +66,7 @@ export const ngViewerActionClearView = createAction(
`[ngViewerAction] clearView`,
props<{ payload: { [key: string]: boolean }}>()
)
+
+export const ngViewerActionCycleViews = createAction(
+ `[ngViewerAction] cycleView`
+)
\ No newline at end of file
diff --git a/src/util/directives/keyDownListener.directive.spec.ts b/src/util/directives/keyDownListener.directive.spec.ts
new file mode 100644
index 000000000..7be542f0d
--- /dev/null
+++ b/src/util/directives/keyDownListener.directive.spec.ts
@@ -0,0 +1,102 @@
+import { DOCUMENT } from "@angular/common"
+import { Component } from "@angular/core"
+import { ComponentFixture, fakeAsync, TestBed, tick } from "@angular/core/testing"
+import { By } from "@angular/platform-browser"
+import { KeyListenerConfig, KeyListner } from "./keyDownListener.directive"
+
+@Component({
+ template: ``
+})
+
+class DummyCmp{
+ public keyConfig: KeyListenerConfig[]=[{
+ type: 'keydown',
+ key: 'a',
+ },{
+ type: 'keyup',
+ key: 'a',
+ },{
+ type: 'keydown',
+ key: 'd',
+ target: 'document',
+ capture: true
+ },{
+ type: 'keydown',
+ key: 'e',
+ target: 'document'
+ }]
+
+ // will get spied on
+ public listener(event: any){
+ console.log('lister called')
+ }
+}
+
+const inputId = `text-input`
+describe('KeyListner', () => {
+ beforeEach(async () => {
+ TestBed.configureTestingModule({
+ imports: [],
+ declarations: [
+ KeyListner,
+ DummyCmp
+ ],
+ }).overrideComponent(DummyCmp, {
+ set: {
+ template: `
+
+
+ `
+ }
+ })
+
+ await TestBed.compileComponents()
+ })
+
+ it('> creates component just fine', () => {
+ const fixture = TestBed.createComponent(DummyCmp)
+ expect(fixture).toBeTruthy()
+ })
+ it('> Directive is created', () => {
+ const fixture = TestBed.createComponent(DummyCmp)
+ const keyListenerDirective = fixture.debugElement.query(By.directive(KeyListner))
+ expect(keyListenerDirective).toBeTruthy()
+ })
+
+ describe('> directive working as intended', () => {
+ let eventListSpy: jasmine.Spy
+ let fixture: ComponentFixture
+ beforeEach(() => {
+ fixture = TestBed.createComponent(DummyCmp)
+ eventListSpy = spyOn(fixture.componentInstance, 'listener')
+ fixture.detectChanges()
+ })
+ describe('> if dispatch element was host element', () => {
+ it('> should trigger event', () => {
+ const newKeybEv = new KeyboardEvent('keydown', {
+ key: 'd'
+ })
+ const nativeEl = fixture.nativeElement as HTMLElement
+ nativeEl.dispatchEvent(newKeybEv)
+
+ expect(eventListSpy).toHaveBeenCalled()
+ })
+ })
+ describe('> if dispatch element was input', () => {
+ it('> should not trigger event listener', () => {
+ const newKeybEv = new KeyboardEvent('keydown', {
+ key: 'd'
+ })
+ const nativeEl = fixture.debugElement.query(By.css(`#${inputId}`)).nativeElement as HTMLElement
+ nativeEl.dispatchEvent(newKeybEv)
+
+ expect(eventListSpy).not.toHaveBeenCalled()
+ })
+ })
+ })
+})
diff --git a/src/util/directives/keyDownListener.directive.ts b/src/util/directives/keyDownListener.directive.ts
index f3fc055d1..e400fcea1 100644
--- a/src/util/directives/keyDownListener.directive.ts
+++ b/src/util/directives/keyDownListener.directive.ts
@@ -100,7 +100,7 @@ export interface KeyListenerConfig {
key: string
target?: 'document'
capture?: boolean
- stop: boolean
+ stop?: boolean
// fromEvent seems to be a passive listener, wheather or not { passive: false } flag is set or not
// so preventDefault cannot be called anyway
}
diff --git a/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.component.ts b/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.component.ts
index c071875bb..4a0b345d3 100644
--- a/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.component.ts
+++ b/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.component.ts
@@ -1,7 +1,7 @@
import { AfterViewInit, Component, ElementRef, EventEmitter, Inject, Input, OnChanges, OnDestroy, Optional, Output, SimpleChanges, ViewChild } from "@angular/core";
import { select, Store } from "@ngrx/store";
import { asyncScheduler, combineLatest, fromEvent, merge, Observable, of, Subject } from "rxjs";
-import { ngViewerActionToggleMax } from "src/services/state/ngViewerState/actions";
+import { ngViewerActionCycleViews, ngViewerActionToggleMax } from "src/services/state/ngViewerState/actions";
import { ClickInterceptor, CLICK_INTERCEPTOR_INJECTOR } from "src/util";
import { uiStateMouseOverSegmentsSelector } from "src/services/state/uiState/selectors";
import { debounceTime, distinctUntilChanged, filter, map, mapTo, scan, shareReplay, startWith, switchMap, switchMapTo, take, tap, throttleTime } from "rxjs/operators";
@@ -70,6 +70,8 @@ export class NehubaGlueCmp implements IViewer<'nehuba'>, OnChanges, OnDestroy, A
public ARIA_LABELS = ARIA_LABELS
public IDS = IDS
+ private currentPanelMode: PANELS
+
@ViewChild(NehubaViewerContainerDirective, { static: true })
public nehubaContainerDirective: NehubaViewerContainerDirective
@@ -325,6 +327,9 @@ export class NehubaGlueCmp implements IViewer<'nehuba'>, OnChanges, OnDestroy, A
]).pipe(
switchMap(this.waitForNehuba.bind(this))
).subscribe(([mode, panelOrder]) => {
+
+ this.currentPanelMode = mode
+
const viewPanels = panelOrder.split('').map(v => Number(v)).map(idx => this.viewPanels[idx]) as [HTMLElement, HTMLElement, HTMLElement, HTMLElement]
/**
@@ -636,6 +641,13 @@ export class NehubaGlueCmp implements IViewer<'nehuba'>, OnChanges, OnDestroy, A
this.onDestroyCb.push(() => navSub.unsubscribe())
}
+ handleCycleViewEvent(){
+ if (this.currentPanelMode !== PANELS.SINGLE_PANEL) return
+ this.store$.dispatch(
+ ngViewerActionCycleViews()
+ )
+ }
+
handleViewerLoadedEvent(flag: boolean) {
this.viewerEvent.emit({
type: EnumViewerEvt.VIEWERLOADED,
diff --git a/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.template.html b/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.template.html
index d79c49a59..158dd9a41 100644
--- a/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.template.html
+++ b/src/viewerModule/nehuba/nehubaViewerGlue/nehubaViewerGlue.template.html
@@ -10,6 +10,8 @@
iav-nehuba-viewer-container
#iavContainer="iavNehubaViewerContainer"
iav-mouse-hover
+ [iav-key-listener]="[{ type: 'keydown', key: ' ', target: 'document', capture: true }]"
+ (iav-key-event)="handleCycleViewEvent()"
(iavNehubaViewerContainerViewerLoading)="handleViewerLoadedEvent($event)">
diff --git a/src/viewerModule/viewerCmp/viewerCmp.template.html b/src/viewerModule/viewerCmp/viewerCmp.template.html
index 20997801b..ca92a5a01 100644
--- a/src/viewerModule/viewerCmp/viewerCmp.template.html
+++ b/src/viewerModule/viewerCmp/viewerCmp.template.html
@@ -16,7 +16,7 @@
[autoFocus]="false"
[disableClose]="true"
class="p-0 pe-all col-10 col-sm-10 col-md-5 col-lg-4 col-xl-3 col-xxl-2">
-