diff --git a/Dockerfile b/Dockerfile index 67876d05bf57addb564b5e6c48622c12719ef1ab..87199619f483ddfe45793a012c87171032ece2b7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,14 +1,16 @@ FROM node:10 as builder ARG BACKEND_URL -ENV BACKEND_URL=$BACKEND_URL +ENV BACKEND_URL=${BACKEND_URL} + +ARG USE_LOGO +ENV USE_LOGO=${USE_LOGO:-hbp} COPY . /iv WORKDIR /iv -ENV VERSION=devNext - -RUN apt update && apt upgrade -y && apt install brotli +ARG VERSION +ENV VERSION=${VERSION:-devNext} RUN npm i RUN npm run build-aot @@ -26,8 +28,6 @@ RUN for f in $(find . -type f); do gzip < $f > $f.gz && brotli < $f > $f.br; don # prod container FROM node:10-alpine -ARG PORT -ENV PORT=$PORT ENV NODE_ENV=production RUN apk --no-cache add ca-certificates @@ -45,6 +45,4 @@ COPY --from=compressor /iv ./public COPY --from=compressor /iv/res/json ./res RUN npm i -EXPOSE $PORT - ENTRYPOINT [ "node", "server.js" ] \ No newline at end of file diff --git a/src/atlasViewer/atlasViewer.component.ts b/src/atlasViewer/atlasViewer.component.ts index cae9d027e425bcbd6a49af168f50f2d34225eb0a..7efaac5ea76db2741b8264a8910a1ca50a1abf05 100644 --- a/src/atlasViewer/atlasViewer.component.ts +++ b/src/atlasViewer/atlasViewer.component.ts @@ -6,8 +6,7 @@ import { OnInit, TemplateRef, AfterViewInit, - Renderer2, - ElementRef + Renderer2 } from "@angular/core"; import { Store, select, ActionsSubject } from "@ngrx/store"; import { @@ -15,10 +14,9 @@ import { isDefined, FETCHED_SPATIAL_DATA, UPDATE_SPATIAL_DATA, - safeFilter, - CHANGE_NAVIGATION, generateLabelIndexId + safeFilter } from "../services/stateStore.service"; -import {Observable, Subscription, combineLatest, interval, merge, of, Observer} from "rxjs"; +import {Observable, Subscription, combineLatest, interval, merge, of } from "rxjs"; import { map, filter, @@ -26,8 +24,6 @@ import { delay, concatMap, withLatestFrom, - switchMapTo, - takeUntil, take, tap, mapTo } from "rxjs/operators"; import { AtlasViewerDataService } from "./atlasViewer.dataService.service"; import { WidgetServices } from "./widgetUnit/widgetService.service"; @@ -43,15 +39,14 @@ import { AGREE_COOKIE, AGREE_KG_TOS, SHOW_KG_TOS, SHOW_BOTTOM_SHEET } from "src/ import { TabsetComponent } from "ngx-bootstrap/tabs"; import { LocalFileService } from "src/services/localFile.service"; import { MatDialog, MatDialogRef, MatSnackBar, MatSnackBarRef, MatBottomSheet, MatBottomSheetRef } from "@angular/material"; -import {ADD_TO_REGIONS_SELECTION_WITH_IDS} from "src/services/state/viewerState.store"; -import {VIEWER_STATE_ACTION_TYPES} from "src/services/effect/effect"; -import {RegionMenuComponent} from "src/ui/regionToolsMenu/regionMenu.component"; + /** * TODO * check against auxlillary mesh indicies, to only filter out aux indicies */ const filterFn = (segment) => typeof segment.segment !== 'string' +const compareFn = (it, item) => it.name === item.name @Component({ selector: 'atlas-viewer', @@ -65,6 +60,8 @@ const filterFn = (segment) => typeof segment.segment !== 'string' }) export class AtlasViewer implements OnDestroy, OnInit, AfterViewInit { + + public compareFn = compareFn @ViewChild('cookieAgreementComponent', {read: TemplateRef}) cookieAgreementComponent : TemplateRef<any> @ViewChild('kgToS', {read: TemplateRef}) kgTosComponent: TemplateRef<any> diff --git a/src/atlasViewer/atlasViewer.style.css b/src/atlasViewer/atlasViewer.style.css index aca0ca06ef6f9579c7a615829f43f407a62e598d..28beaa4f06234794e2478a7593e09d5c76652beb 100644 --- a/src/atlasViewer/atlasViewer.style.css +++ b/src/atlasViewer/atlasViewer.style.css @@ -43,12 +43,7 @@ layout-floating-container [fixedMouseContextualContainerDirective] { - width: 15rem; -} - -[fixedMouseContextualContainerDirective] div[body] -{ - overflow: hidden; + max-width: 20vw; } div[imageContainer] diff --git a/src/atlasViewer/atlasViewer.template.html b/src/atlasViewer/atlasViewer.template.html index 470838c7d62aafb645f27e09668c165065aafd90..c55b4fe3b5166971a78f4f89785e97905d040087 100644 --- a/src/atlasViewer/atlasViewer.template.html +++ b/src/atlasViewer/atlasViewer.template.html @@ -42,8 +42,8 @@ [currentOnHoverObs$]="iavMouseHoverEl.currentOnHoverObs$" [currentOnHover]="iavMouseHoverEl.currentOnHoverObs$ | async" iav-captureClickListenerDirective - (mouseDownEmitter)="mouseDownNehuba($event)" - (mapClicked)="mouseUpNehuba($event)"> + (iav-captureClickListenerDirective-onMousedown)="mouseDownNehuba($event)" + (iav-captureClickListenerDirective-onClick)="mouseUpNehuba($event)"> </ui-nehuba-container> <div class="z-index-10 position-absolute pe-none w-100 h-100"> @@ -129,23 +129,19 @@ <!-- TODO Potentially implementing plugin contextual info --> </div> - <panel-component class="shadow p-0 m-0" fixedMouseContextualContainerDirective> - <div body class="pe-all" *ngIf="(onhoverSegmentsForFixed$ | async) as onHoverSegments"> - <mat-card *ngIf="onHoverSegments.length > 0 && regionToolsMenuVisible" class="d-flex flex-column p-0"> - <div *ngFor="let onHoverRegion of onHoverSegments; let first = first" - class="border-dark rounded"> - <mat-divider *ngIf="!first"></mat-divider> + <div fixedMouseContextualContainerDirective> + <ng-container *ngIf="(onhoverSegmentsForFixed$ | async) as onHoverSegments"> + <ng-container *ngFor="let onHoverRegion of onHoverSegments; let first = first"> - <region-menu #regionToolsMenu - [selectedRegions$]="selectedRegions$" - [region]="onHoverRegion"> - </region-menu> + <region-menu + class="pe-all" + [region]="onHoverRegion" + [isSelected]="selectedRegions$ | async | includes : onHoverRegion : compareFn"> + </region-menu> - </div> - </mat-card> - </div> - - </panel-component> + </ng-container> + </ng-container> + </div> </layout-floating-container> diff --git a/src/res/css/extra_styles.css b/src/res/css/extra_styles.css index 774f1adebd5fe25aee189b0b48c594e0234ff8af..d831b31366539901331ac379181b5c977dc95540 100644 --- a/src/res/css/extra_styles.css +++ b/src/res/css/extra_styles.css @@ -414,6 +414,11 @@ markdown-dom pre code overflow-x:hidden!important; } +.overflow-y-hidden +{ + overflow-y:hidden!important; +} + .muted { opacity : 0.5!important; @@ -626,11 +631,6 @@ mat-icon[fontset="far"] min-height: 2rem; } -.min-h-8 -{ - min-height: 4rem; -} - .w-30vw { width: 30vw!important; diff --git a/src/ui/parcellationRegion/region.base.ts b/src/ui/parcellationRegion/region.base.ts new file mode 100644 index 0000000000000000000000000000000000000000..fadac02aaf1343b99d07b684183e1f623db0a57e --- /dev/null +++ b/src/ui/parcellationRegion/region.base.ts @@ -0,0 +1,32 @@ +import { Store } from "@ngrx/store"; +import { Input } from "@angular/core"; +import { VIEWERSTATE_CONTROLLER_ACTION_TYPES } from "../viewerStateController/viewerState.base"; + +export class RegionBase{ + + @Input() + public region: any + + @Input() + public isSelected: boolean = false + + constructor(private store$: Store<any>){ + + } + + navigateToRegion(){ + const { region } = this + this.store$.dispatch({ + type: VIEWERSTATE_CONTROLLER_ACTION_TYPES.NAVIGATETO_REGION, + payload: { region } + }) + } + + toggleRegionSelected(){ + const { region } = this + this.store$.dispatch({ + type: VIEWERSTATE_CONTROLLER_ACTION_TYPES.TOGGLE_REGION_SELECT, + payload: { region } + }) + } +} \ No newline at end of file diff --git a/src/ui/parcellationRegion/regionListSimpleView/regionListSimpleView.component.ts b/src/ui/parcellationRegion/regionListSimpleView/regionListSimpleView.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..6a4dd6923e5d4493da6abd0d6b4ad898defa429b --- /dev/null +++ b/src/ui/parcellationRegion/regionListSimpleView/regionListSimpleView.component.ts @@ -0,0 +1,25 @@ +import { Component, Input } from "@angular/core"; + +import { RegionBase } from '../region.base' +import { Store } from "@ngrx/store"; + +@Component({ + selector: 'region-list-simple-view', + templateUrl: './regionListSimpleView.template.html', + styleUrls: [ + './regionListSimpleView.style.css' + ] +}) + +export class RegionListSimpleViewComponent extends RegionBase{ + + @Input() + showBrainIcon: boolean = false + + @Input() + showDesc: boolean = false + + constructor(store$: Store<any>){ + super(store$) + } +} \ No newline at end of file diff --git a/src/ui/parcellationRegion/regionListSimpleView/regionListSimpleView.style.css b/src/ui/parcellationRegion/regionListSimpleView/regionListSimpleView.style.css new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/ui/parcellationRegion/regionListSimpleView/regionListSimpleView.template.html b/src/ui/parcellationRegion/regionListSimpleView/regionListSimpleView.template.html new file mode 100644 index 0000000000000000000000000000000000000000..12b4c2e6a25cae2923d90d596980edf5baa36b1e --- /dev/null +++ b/src/ui/parcellationRegion/regionListSimpleView/regionListSimpleView.template.html @@ -0,0 +1,29 @@ + +<!-- selected brain region --> +<div class="flex-grow-1 flex-shrink-1 pt-2 pb-2 d-flex flex-row align-items-center flex-nowrap"> + <i *ngIf="showBrainIcon" class="flex-grow-0 flex-shrink-0 fas fa-brain mr-2"></i> + + <small class="flex-grow-1 flex-shrink-1 "> + {{ region.name }} + </small> + + <button mat-icon-button + *ngIf="region.position" + class="flex-grow-0 flex-shrink-0" + (click)="navigateToRegion()" > + <i *ngIf="isSelected" class="fas fa-map-marked-alt"></i> + </button> + + <button mat-icon-button + class="flex-grow-0 flex-shrink-0" + (click)="toggleRegionSelected()" > + <i *ngIf="isSelected" class="fas fa-trash"></i> + <i *ngIf="!isSelected" class="fas fa-plus"></i> + </button> +</div> + +<mat-divider *ngIf="showDesc && region.description"></mat-divider> + +<small *ngIf="showDesc && region.description"> + {{ region.description }} +</small> \ No newline at end of file diff --git a/src/ui/parcellationRegion/regionMenu/regionMenu.component.ts b/src/ui/parcellationRegion/regionMenu/regionMenu.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..3d5ef2255891d4a15380df8902ccf4180fddd057 --- /dev/null +++ b/src/ui/parcellationRegion/regionMenu/regionMenu.component.ts @@ -0,0 +1,16 @@ +import { Component } from "@angular/core"; +import { Store } from "@ngrx/store"; + +import { RegionBase } from '../region.base' + +@Component({ + selector: 'region-menu', + templateUrl: './regionMenu.template.html', + styleUrls: ['./regionMenu.style.css'] +}) +export class RegionMenuComponent extends RegionBase { + +constructor(store$: Store<any>) { + super(store$) + } +} \ No newline at end of file diff --git a/src/ui/parcellationRegion/regionMenu/regionMenu.style.css b/src/ui/parcellationRegion/regionMenu/regionMenu.style.css new file mode 100644 index 0000000000000000000000000000000000000000..c9f10ea89767c6d94d967b6486d350e0f13f1910 --- /dev/null +++ b/src/ui/parcellationRegion/regionMenu/regionMenu.style.css @@ -0,0 +1,20 @@ +.regionDescriptionTextClass +{ + max-height:100px; + transition: max-height 0.15s ease-out; +} +.regionDescriptionTextOpened +{ + max-height: 310px; + transition: max-height 0.25s ease-in; +} + +[fixedMouseContextualContainerDirective] +{ + width: 15rem; +} + +[fixedMouseContextualContainerDirective] div[body] +{ + overflow: hidden; +} diff --git a/src/ui/parcellationRegion/regionMenu/regionMenu.template.html b/src/ui/parcellationRegion/regionMenu/regionMenu.template.html new file mode 100644 index 0000000000000000000000000000000000000000..f0716128fb236519d9e025a0f8d099928bb85fa1 --- /dev/null +++ b/src/ui/parcellationRegion/regionMenu/regionMenu.template.html @@ -0,0 +1,24 @@ +<mat-card> + <mat-card-subtitle> + {{ region.name }} + </mat-card-subtitle> + <mat-card-content> + {{ region.description }} + </mat-card-content> + <mat-card-actions class="d-flex flex-row"> + <button mat-button + (click)="toggleRegionSelected()" + [color]="isSelected ? 'primary': 'basic'"> + <i class="far" [ngClass]="{'fa-check-square': isSelected, 'fa-square': !isSelected}"></i> + <span> + Selected + </span> + </button> + <button mat-button (click)="navigateToRegion()"> + <i class="fas fa-map-marked-alt"></i> + <span> + Navigate + </span> + </button> + </mat-card-actions> +</mat-card> \ No newline at end of file diff --git a/src/ui/parcellationRegion/regionSimple/regionSimple.component.ts b/src/ui/parcellationRegion/regionSimple/regionSimple.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..05ea0b1e41539cca1bf08a9b1c5bc589771d79e1 --- /dev/null +++ b/src/ui/parcellationRegion/regionSimple/regionSimple.component.ts @@ -0,0 +1,18 @@ +import { Component } from '@angular/core' + +import { RegionBase } from '../region.base' +import { Store } from '@ngrx/store' + +@Component({ + selector: 'simple-region', + templateUrl: './regionSimple.template.html', + styleUrls: [ + './regionSimple.style.css' + ] +}) + +export class SimpleRegionComponent extends RegionBase{ + constructor(store$: Store<any>){ + super(store$) + } +} \ No newline at end of file diff --git a/src/ui/parcellationRegion/regionSimple/regionSimple.style.css b/src/ui/parcellationRegion/regionSimple/regionSimple.style.css new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/ui/parcellationRegion/regionSimple/regionSimple.template.html b/src/ui/parcellationRegion/regionSimple/regionSimple.template.html new file mode 100644 index 0000000000000000000000000000000000000000..eb262ae8f54ded633057a80e9bbb224bf4e145a9 --- /dev/null +++ b/src/ui/parcellationRegion/regionSimple/regionSimple.template.html @@ -0,0 +1,28 @@ +<div class="d-flex flex-row"> + + <small class="text-truncate flex-shrink-1 flex-grow-1"> + {{ region.name }} + </small> + + <div class="flex-grow-0 flex-shrink-0 d-flex flex-row"> + + <!-- if has position defined --> + <button *ngIf="region.position" + iav-stop="click" + (click)="navigateToRegion()" + mat-icon-button> + <i class="fas fa-map-marked-alt"></i> + </button> + + <!-- region selected --> + <button mat-icon-button + iav-stop="click" + (click)="toggleRegionSelected()" + [color]="isSelected ? 'primary' : 'basic'"> + <i class="far" + [ngClass]="{'fa-check-square': isSelected, 'fa-square': !isSelected}"> + </i> + </button> + </div> + +</div> \ No newline at end of file diff --git a/src/ui/pluginBanner/pluginBanner.template.html b/src/ui/pluginBanner/pluginBanner.template.html index d349c6f7cc8d4b3b46ac7a8d6517fa119faebc19..63a7079df80735543eb9b00b66db41c1e42131c7 100644 --- a/src/ui/pluginBanner/pluginBanner.template.html +++ b/src/ui/pluginBanner/pluginBanner.template.html @@ -1,8 +1,9 @@ <mat-action-list> - <button mat-list-item + <button mat-menu-item *ngFor="let plugin of pluginServices.fetchedPluginManifests" + [matTooltip]="plugin.displayName ? plugin.displayName : plugin.name" (click)="clickPlugin(plugin)"> - + <mat-icon fontSet="fas" fontIcon="fa-cube"></mat-icon> {{ plugin.displayName ? plugin.displayName : plugin.name }} </button> </mat-action-list> diff --git a/src/ui/regionToolsMenu/regionMenu.component.ts b/src/ui/regionToolsMenu/regionMenu.component.ts deleted file mode 100644 index 3a64bf7d8451e4e3f778855d2e8c00e86f7fe477..0000000000000000000000000000000000000000 --- a/src/ui/regionToolsMenu/regionMenu.component.ts +++ /dev/null @@ -1,52 +0,0 @@ -import {AfterViewInit, Component, Input, ViewChild} from "@angular/core"; -import {Observable} from "rxjs"; -import {map, withLatestFrom} from "rxjs/operators"; -import {FixedMouseContextualContainerDirective} from "src/util/directives/FixedMouseContextualContainerDirective.directive"; -import {VIEWER_STATE_ACTION_TYPES} from "src/services/effect/effect"; -import {CHANGE_NAVIGATION, generateLabelIndexId} from "src/services/stateStore.service"; -import {ADD_TO_REGIONS_SELECTION_WITH_IDS} from "src/services/state/viewerState.store"; -import {Store} from "@ngrx/store"; - -@Component({ - selector: 'region-menu', - templateUrl: './regionMenu.template.html', - styleUrls: ['./regionMenu.style.css'] -}) -export class RegionMenuComponent { - @Input() selectedRegions$: any - @Input() region: any - - regionToolsMenuVisible = false - collapsedRegionDescription = false - - constructor(private store$: Store<any>) {} - - - toggleRegionWithId(ngId, labelIndex, removeFlag: any){ - if (removeFlag) { - this.store$.dispatch({ - type: VIEWER_STATE_ACTION_TYPES.DESELECT_REGIONS_WITH_ID, - deselecRegionIds: [generateLabelIndexId({ ngId, labelIndex })] - }) - } else { - this.store$.dispatch({ - type: ADD_TO_REGIONS_SELECTION_WITH_IDS, - selectRegionIds : [generateLabelIndexId({ ngId, labelIndex })] - }) - } - } - - regionIsSelected(selectedRegions, ngId, labelIndex) { - return selectedRegions.map(sr => generateLabelIndexId({ ngId: sr.ngId, labelIndex: sr.labelIndex })).includes(generateLabelIndexId({ ngId, labelIndex })) - } - - navigateTo(position){ - this.store$.dispatch({ - type: CHANGE_NAVIGATION, - navigation: { - position, - animation: {} - } - }) - } -} \ No newline at end of file diff --git a/src/ui/regionToolsMenu/regionMenu.style.css b/src/ui/regionToolsMenu/regionMenu.style.css deleted file mode 100644 index fc202344466f16355a9dd257690a17f7e56c151c..0000000000000000000000000000000000000000 --- a/src/ui/regionToolsMenu/regionMenu.style.css +++ /dev/null @@ -1,19 +0,0 @@ -.regionDescriptionTextClass{ - max-height:100px; - transition: max-height 0.15s ease-out; -} -.regionDescriptionTextOpened { - max-height: 310px; - transition: max-height 0.25s ease-in; -} - -[fixedMouseContextualContainerDirective] -{ - width: 15rem; -} - -[fixedMouseContextualContainerDirective] div[body] -{ - overflow: hidden; -} - diff --git a/src/ui/regionToolsMenu/regionMenu.template.html b/src/ui/regionToolsMenu/regionMenu.template.html deleted file mode 100644 index eeaed9f0a9dbaba6a91e09f4a4e50937b7638301..0000000000000000000000000000000000000000 --- a/src/ui/regionToolsMenu/regionMenu.template.html +++ /dev/null @@ -1,50 +0,0 @@ -<div class="text-nowrap text-truncate mt-2 ml-2 mr-2 overflow-hidden" [matTooltip]="region.name" - matTooltipShowDelay="1000">{{region.name}}</div> -<!-- ToDo implement it with Descriptions--> -<!-- <div > <!–*ngIf="!region.description && !region.description.length"–>--> -<!-- <div class="row m-2 position-relative overflow-hidden"--> -<!-- [class.regionDescriptionTextOpened] = "collapsedRegionDescription"--> -<!-- [class.overflow-y-auto] = "collapsedRegionDescription"--> -<!-- [class.regionDescriptionTextClass] = "!collapsedRegionDescription"--> -<!-- [class.linear-gradient-fade] = "(+regionDescriptionText.scrollHeight > +regionDescriptionText.clientHeight) && !collapsedRegionDescription"--> -<!-- #regionDescriptionText>--> -<!-- {{region.description}}--> -<!-- </div>--> -<!-- <div (click)="collapsedRegionDescription = true"--> -<!-- *ngIf="+regionDescriptionText.scrollHeight > +regionDescriptionText.clientHeight && !collapsedRegionDescription"--> -<!-- class="w-100 d-flex justify-content-center align-items-center mt-n2 cursorPointer"><i--> -<!-- class="fas fa-angle-down m-1"></i> Read More--> -<!-- </div>--> -<!-- <div (click)="collapsedRegionDescription = false"--> -<!-- *ngIf="collapsedRegionDescription"--> -<!-- class="w-100 d-flex justify-content-center align-items-center mt-n2 cursorPointer"><i--> -<!-- class="fas fa-angle-up m-1"></i> Collapse--> -<!-- </div>--> -<!-- </div>--> -<div class="d-flex align-items-center justify-content-between"> - <button mat-button - style="font-size: 12px" - class="p-0 w-100" - *ngIf="(selectedRegions$ | async) as selectedRegions" - (click)="toggleRegionWithId(region.ngId, region.labelIndex, regionIsSelected(selectedRegions, region.ngId, region.labelIndex)); regionToolsMenuVisible = false; collapsedRegionDescription = false"> - <span class="fa-stack fa-1x"> - <i class="fas fa-hand-pointer fa-stack-1x"></i> - <i class="fas fa-slash fa-stack-1x fa-inverse" - *ngIf="regionIsSelected(selectedRegions, region.ngId, region.labelIndex)"></i> - </span> - <span [innerText]="regionIsSelected(selectedRegions, region.ngId, region.labelIndex)? 'Deselect' : 'Select'"></span> - </button> - <button mat-button - style="font-size: 12px" - class="p-0 w-100" - (click)="navigateTo(region.position); regionToolsMenuVisible = false; collapsedRegionDescription = false"> - <i class="fas fa-crosshairs"></i> Navigate - </button> - - <!-- ToDo Change other buttons font size to 10 if this button is available and remove w-100 classes--> - <!-- <button mat-button--> - <!-- style="font-size: 10px"--> - <!-- class="p-0 mr-1">--> - <!-- <i class="fab fa-connectdevelop"></i> Connectivity--> - <!-- </button>--> -</div> \ No newline at end of file diff --git a/src/ui/searchSideNav/searchSideNav.component.ts b/src/ui/searchSideNav/searchSideNav.component.ts index 4d12f4cc8665955fadc4e23026cd14048ffc8a62..28441c05010e963b3a9a2432926f25c5e8847a5d 100644 --- a/src/ui/searchSideNav/searchSideNav.component.ts +++ b/src/ui/searchSideNav/searchSideNav.component.ts @@ -5,9 +5,9 @@ import { LayerBrowser } from "../layerbrowser/layerbrowser.component"; import { Observable, Subscription } from "rxjs"; import { Store, select } from "@ngrx/store"; import { map, startWith, scan, filter, mapTo } from "rxjs/operators"; -import { VIEWERSTATE_CONTROLLER_ACTION_TYPES } from "../viewerStateController/viewerState.base"; import { trackRegionBy } from '../viewerStateController/regionHierachy/regionHierarchy.component' import { AtlasViewerConstantsServices } from "src/atlasViewer/atlasViewer.constantService.service"; +import { SELECT_REGIONS } from "src/services/stateStore.service"; @Component({ selector: 'search-side-nav', @@ -93,10 +93,10 @@ export class SearchSideNav implements OnInit, OnDestroy { }) } - removeRegion(region: any){ + public deselectAllRegions(){ this.store$.dispatch({ - type: VIEWERSTATE_CONTROLLER_ACTION_TYPES.SINGLE_CLICK_ON_REGIONHIERARCHY, - payload: { region } + type: SELECT_REGIONS, + selectRegions: [] }) } diff --git a/src/ui/searchSideNav/searchSideNav.template.html b/src/ui/searchSideNav/searchSideNav.template.html index 1230a72321a950f562ca9a3ea778a930b0316ae7..4f1402c684e8cfc058f76b54e85d1d2cdc3ed076 100644 --- a/src/ui/searchSideNav/searchSideNav.template.html +++ b/src/ui/searchSideNav/searchSideNav.template.html @@ -83,65 +83,85 @@ </div> <!-- show when regions are selected --> - <div *ngIf="regionsSelected.length > 0" class="h-100"> - - <!-- single region --> - <ng-template [ngIf]="regionsSelected.length === 1" [ngIfElse]="multiRegionTemplate"> - - <small class="text-muted"> - Region selected - </small> - - <!-- selected brain region --> - <div class="pt-2 pb-2 d-flex flex-row align-items-center flex-nowrap"> - <i class="fas fa-brain mr-2"></i> - - <small> - {{ regionsSelected[0].name }} - </small> - - <button (click)="removeRegion(regionsSelected[0])" mat-icon-button> - <i class="fas fa-trash"></i> - </button> - </div> - </ng-template> - - <!-- multi region --> - <ng-template #multiRegionTemplate> - <div class="h-100 d-flex flex-column"> - <small class="d-block text-muted flex-shrink-0 flex-grow-0"> - {{ regionsSelected.length }} regions selected - </small> - <cdk-virtual-scroll-viewport - class="flex-grow-1 flex-shrink-1" - itemSize="55" - maxBufferPx="600" - minBufferPx="300"> - <div *cdkVirtualFor="let region of regionsSelected; trackBy: trackByFn ; let index = index" - class="region-wrapper d-flex flex-column" > - <!-- divider if index !== 0 --> - <mat-divider class="flex-grow-0 flex-shrink-0" *ngIf="index !== 0"></mat-divider> - - <!-- selected brain region --> - <div class="flex-grow-1 flex-shrink-1 pt-2 pb-2 d-flex flex-row align-items-center flex-nowrap"> - <i class="flex-grow-0 flex-shrink-0 fas fa-brain mr-2"></i> - - <small class="flex-grow-1 flex-shrink-1 "> - {{ region.name }} - </small> - - <button mat-icon-button - class="flex-grow-0 flex-shrink-0" - (click)="removeRegion(region)" > - <i class="fas fa-trash"></i> - </button> - </div> - </div> - </cdk-virtual-scroll-viewport> - </div> - </ng-template> - + <div *ngIf="regionsSelected.length > 0" class="h-100 d-flex flex-column"> + + <!-- header --> + <div class="flex-grow-0 flex-shrink-0"> + <ng-container *ngTemplateOutlet="header"> + </ng-container> + </div> + + <!-- body (region descriptor or multi region list) --> + <div class="flex-grow-1 flex-shrink-1"> + <ng-container *ngTemplateOutlet="body"> + </ng-container> + </div> </div> </div> + + <ng-template #header> + <div class="d-flex flex-row align-items-center"> + <small *ngIf="regionsSelected.length === 1" class="text-muted position-relative"> + Region selected + </small> + + <small *ngIf="regionsSelected.length > 1" class="text-muted position-relative"> + {{ regionsSelected.length }} regions selected + </small> + + <div class="position-relative d-flex align-items-center"> + <button mat-icon-button + class="position-absolute" + (click)="deselectAllRegions()" + matTooltip="Clear all regions" + color="primary"> + <i class="fas fa-times-circle"></i> + </button> + </div> + </div> + </ng-template> + + <ng-template #body> + <!-- single region --> + <ng-template [ngIf]="regionsSelected.length === 1" [ngIfElse]="multiRegionTemplate"> + + <!-- selected brain region --> + <region-list-simple-view + class="position-relative" + [showBrainIcon]="true" + [region]="regionsSelected[0]" + [isSelected]="true" + [showDesc]="true"> + + </region-list-simple-view> + </ng-template> + + <!-- multi region --> + <ng-template #multiRegionTemplate> + <div class="h-100 d-flex flex-column"> + + <cdk-virtual-scroll-viewport + class="flex-grow-1 flex-shrink-1" + itemSize="55" + maxBufferPx="600" + minBufferPx="300"> + <div *cdkVirtualFor="let region of regionsSelected; trackBy: trackByFn ; let first = first" + class="region-wrapper d-flex flex-column" > + <!-- divider if not first --> + <mat-divider class="flex-grow-0 flex-shrink-0" *ngIf="!first"></mat-divider> + + <!-- selected brain region --> + <region-list-simple-view + class="d-block w-100 h-100" + [showBrainIcon]="true" + [region]="region" + [isSelected]="true"> + + </region-list-simple-view> + </div> + </cdk-virtual-scroll-viewport> + </div> + </ng-template> + </ng-template> </ng-container> -</ng-template> \ No newline at end of file +</ng-template> diff --git a/src/ui/signinBanner/signinBanner.components.ts b/src/ui/signinBanner/signinBanner.components.ts index 7d7c624e4fdccc188d20d46e188e0b300d6a69a4..862e09043dddd3c63355bd0d9c638db6dc04076c 100644 --- a/src/ui/signinBanner/signinBanner.components.ts +++ b/src/ui/signinBanner/signinBanner.components.ts @@ -26,6 +26,9 @@ export class SigninBanner{ public userBtnTooltip$: Observable<string> public favDataEntries$: Observable<DataEntry[]> + public pluginTooltipText: string = `Plugins and Tools` + public screenshotTooltipText: string = 'Take screenshot' + constructor( private store$: Store<any>, private authService: AuthService, diff --git a/src/ui/signinBanner/signinBanner.template.html b/src/ui/signinBanner/signinBanner.template.html index 5b066d36399f9484f4c288fd184897df859f4f7a..c05a5a4552629d791d01c2715b8781b4e3a1f805 100644 --- a/src/ui/signinBanner/signinBanner.template.html +++ b/src/ui/signinBanner/signinBanner.template.html @@ -2,19 +2,6 @@ [iav-key-listener]="keyListenerConfig" (iav-key-event)="openTmplWithDialog(helpComponent)"> - <!-- Take screenshot --> - <div class="btnWrapper" *ngIf="parcellationIsSelected"> - <button mat-icon-button - (click)="takeScreenshotElement.takingScreenshot = true; - takeScreenshotElement.previewingScreenshot = false; - takeScreenshotElement.croppedCanvas = null; - takeScreenshotElement.loadingScreenshot = null;" - matTooltip="Take screenshot" - color="primary"> - <i class="fas fa-camera"></i> - </button> - </div> - <!-- pinned datasets --> <div class="btnWrapper"> @@ -30,13 +17,24 @@ </button> </div> + <!-- plugin and tools --> + <div class="btnWrapper"> + <button mat-icon-button + matTooltipPosition="below" + [matTooltip]="pluginTooltipText" + [matMenuTriggerFor]="pluginDropdownMenu" + color="primary"> + <i class="fas fa-th"></i> + </button> + </div> + <!-- signin --> <div class="btnWrapper"> <button [matTooltip]="userBtnTooltip$ | async" matTooltipPosition="below" mat-icon-button - [matMenuTriggerFor]="dropdownMenu" + [matMenuTriggerFor]="userDropdownMenu" color="primary"> <ng-template [ngIf]="user$ | async" [ngIfElse]="notLoggedInTmpl" let-user="ngIf"> {{ (user && user.name || 'Unnamed User').slice(0,1) }} @@ -49,8 +47,21 @@ </div> </div> -<!-- drop downmenu --> -<mat-menu #dropdownMenu> +<!-- plugin dropdownmenu --> +<mat-menu #pluginDropdownMenu> + <button mat-menu-item + [disabled]="!parcellationIsSelected" + (click)="takeScreenshotElement.startScreenshot()" + [matTooltip]="screenshotTooltipText"> + <mat-icon fontSet="fas" fontIcon="fa-camera"> + </mat-icon> + Screenshot + </button> + <plugin-banner></plugin-banner> +</mat-menu> + +<!-- user dropdownmenu --> +<mat-menu #userDropdownMenu> <mat-card> <mat-card-content> <signin-modal> diff --git a/src/ui/takeScreenshot/takeScreenshot.component.ts b/src/ui/takeScreenshot/takeScreenshot.component.ts index e070de583a2b513f8481f5b14d4f5e91c1c04b3e..947113a4c97e030d159d04b29e3b34530c12d48a 100644 --- a/src/ui/takeScreenshot/takeScreenshot.component.ts +++ b/src/ui/takeScreenshot/takeScreenshot.component.ts @@ -60,6 +60,12 @@ export class TakeScreenshotComponent implements OnInit { } } + startScreenshot(){ + this.takingScreenshot = true + this.previewingScreenshot = false + this.croppedCanvas = null + this.loadingScreenshot = null + } move = (e) => { if (this.mouseIsDown) { diff --git a/src/ui/ui.module.ts b/src/ui/ui.module.ts index 0cbb08e0902e243e0c6ba0b0c5e8c6fab344baca..e902fbd55d692a24c0aa60db91724efabe0e5f1b 100644 --- a/src/ui/ui.module.ts +++ b/src/ui/ui.module.ts @@ -72,9 +72,12 @@ import { CurrentlySelectedRegions } from './viewerStateController/regionsListVie import { RegionTextSearchAutocomplete } from "./viewerStateController/regionSearch/regionSearch.component"; import { RegionsListView } from "./viewerStateController/regionsListView/simpleRegionsListView/regionListView.component"; import {TakeScreenshotComponent} from "src/ui/takeScreenshot/takeScreenshot.component"; -import {RegionMenuComponent} from "src/ui/regionToolsMenu/regionMenu.component"; import {FixedMouseContextualContainerDirective} from "src/util/directives/FixedMouseContextualContainerDirective.directive"; +import { RegionMenuComponent } from 'src/ui/parcellationRegion/regionMenu/regionMenu.component' +import { SimpleRegionComponent } from "./parcellationRegion/regionSimple/regionSimple.component"; +import { RegionListSimpleViewComponent } from "./parcellationRegion/regionListSimpleView/regionListSimpleView.component"; + @NgModule({ imports : [ HttpClientModule, @@ -122,6 +125,8 @@ import {FixedMouseContextualContainerDirective} from "src/util/directives/FixedM RegionsListView, TakeScreenshotComponent, RegionMenuComponent, + SimpleRegionComponent, + RegionListSimpleViewComponent, /* pipes */ GroupDatasetByRegion, diff --git a/src/ui/viewerStateController/regionHierachy/regionHierarchy.style.css b/src/ui/viewerStateController/regionHierachy/regionHierarchy.style.css index 121dea1c590f60f7f835b717aadebcc8cfed38a2..6c0b05f0ebfe29bb0c3afef808d17fa7895000e0 100644 --- a/src/ui/viewerStateController/regionHierachy/regionHierarchy.style.css +++ b/src/ui/viewerStateController/regionHierachy/regionHierarchy.style.css @@ -12,9 +12,9 @@ div[treeContainer] background-color:rgba(12,12,12,0.8); */ } -.flex-basis-20-pc +.flex-basis-33-pc { - flex-basis: 20%; + flex-basis: 33%; } .flex-basis-auto @@ -22,11 +22,6 @@ div[treeContainer] flex-basis: auto; } -[hideScrollbarcontainer] -{ - overflow:hidden; -} - input[type="text"] { border:none; @@ -53,3 +48,27 @@ input[type="text"] { flex: 0 0 auto; } + +.horizontal-mode .cdk-viewport-wrapper +{ + display: flex; + flex-direction: row; + flex-wrap: nowrap; + + height:4rem; +} + +.horizontal-mode .cdk-viewport-wrapper region-list-simple-view +{ + width: 200px; +} + +.cdk-viewport-wrapper region-list-simple-view +{ + width: 100%; +} + +.cdk-virtual-scroll-viewport-container.horizontal-mode +{ + height: 6rem; +} \ No newline at end of file diff --git a/src/ui/viewerStateController/regionHierachy/regionHierarchy.template.html b/src/ui/viewerStateController/regionHierachy/regionHierarchy.template.html index 0202c9ffe5a5a9d270d97a705c548f178199d20a..b749d61bdb845654ae3af4e5ab120f9ad978a1c6 100644 --- a/src/ui/viewerStateController/regionHierachy/regionHierarchy.template.html +++ b/src/ui/viewerStateController/regionHierachy/regionHierarchy.template.html @@ -11,15 +11,29 @@ </mat-form-field> <ng-template #noRegionSelected> - No region selected + <small class="text-muted"> + No region selected + </small> </ng-template> <ng-template #regionSelectedText> - <span class="text-muted"> - <ng-template [ngIf]="selectedRegions.length > 0" [ngIfElse]="noRegionSelected"> - {{ (selectedRegions | filterRowsByVisbilityPipe : null : filterTreeBySearch).length }} / {{ selectedRegions.length }} - </ng-template> - </span> + <ng-template [ngIf]="selectedRegions.length > 0" [ngIfElse]="noRegionSelected"> + <div class="d-flex flex-row align-items-center"> + <small class="text-muted"> + {{ (selectedRegions | filterRowsByVisbilityPipe : null : filterTreeBySearch).length }} regions selected ({{ selectedRegions.length }} visible) + </small> + + <div class="position-relative d-flex align-items-center"> + <button mat-icon-button + class="position-absolute" + (click)="clearRegions($event)" + matTooltip="Clear all regions" + color="primary"> + <i class="fas fa-times-circle"></i> + </button> + </div> + </div> + </ng-template> </ng-template> <div @@ -28,40 +42,47 @@ <!-- selected regions --> <div - [ngClass]="{'flex-basis-20-pc': !useMobileUI, 'flex-basis-auto': useMobileUI}" + [ngClass]="{'flex-basis-33-pc': !useMobileUI, 'flex-basis-auto': useMobileUI}" class="d-flex flex-column flex-grow-0 flex-shrink-0"> - <div class="flex-grow-0 flex-shrink-0 d-flex flex-row align-items-center"> - - <button mat-button - *ngIf="selectedRegions.length > 0" - (click)="clearRegions($event)"> - clear all - </button> - - <span class="m-1"> - <ng-container *ngTemplateOutlet="regionSelectedText"> - </ng-container> - </span> + <div class="flex-grow-0 flex-shrink-0 d-flex flex-row align-items-center p-1"> + <ng-container *ngTemplateOutlet="regionSelectedText"> + </ng-container> </div> <mat-divider></mat-divider> <div *ngIf="(selectedRegions | filterRowsByVisbilityPipe : null : filterTreeBySearch).length > 0" - class="mt-2 min-h-8 flex-grow-1 flex-shrink-1" - hideScrollbarcontainer> - <regions-list-view class="d-block h-100" - (gotoRegion)="gotoRegion($event)" - (deselectRegion)="deselectRegion($event)" - [horizontal]="useMobileUI" - [regionsSelected]="selectedRegions | filterRowsByVisbilityPipe : null : filterTreeBySearch"> + class="mt-2 flex-grow-1 flex-shrink-1 overflow-hidden cdk-virtual-scroll-viewport-container" + [ngClass]="{'horizontal-mode': useMobileUI}"> + <cdk-virtual-scroll-viewport + [orientation]="useMobileUI ? 'horizontal' : 'vertical'" + class="w-100 h-100 d-block" + [itemSize]="useMobileUI ? 200 : 32"> + + <!-- required without resorting to viewencapsulation.None or global stylesheet --> + <div class="cdk-viewport-wrapper"> + <ng-container *cdkVirtualFor="let region of selectedRegions | filterRowsByVisbilityPipe : null : filterTreeBySearch; let first = first"> + <mat-divider + class="m-2" + *ngIf="!first" + [vertical]="useMobileUI"> + </mat-divider> + <region-list-simple-view + class="position-relative d-inline-block" + [region]="region" + [isSelected]="true"> + + </region-list-simple-view> + </ng-container> + </div> - </regions-list-view> + </cdk-virtual-scroll-viewport> </div> </div> <!-- region tree --> - <div class="flex-grow-1 flex-shrink-1" hideScrollbarContainer> + <div class="flex-grow-1 flex-shrink-1 overflow-hidden"> <div class="d-flex flex-column h-100" treeContainer diff --git a/src/ui/viewerStateController/regionSearch/regionSearch.component.ts b/src/ui/viewerStateController/regionSearch/regionSearch.component.ts index fb0e259472c064cb64e322fa436f584d60a3acf7..64f545be481e9de3cfed1c692ae09a7f79fcd7fb 100644 --- a/src/ui/viewerStateController/regionSearch/regionSearch.component.ts +++ b/src/ui/viewerStateController/regionSearch/regionSearch.component.ts @@ -1,6 +1,6 @@ import { Component, EventEmitter, Output, ViewChild, ElementRef, TemplateRef, Input, ChangeDetectionStrategy } from "@angular/core"; import { Store, select } from "@ngrx/store"; -import { Observable } from "rxjs"; +import { Observable, combineLatest } from "rxjs"; import { map, distinctUntilChanged, startWith, withLatestFrom, debounceTime, shareReplay, take, tap } from "rxjs/operators"; import { getMultiNgIdsRegionsLabelIndexMap, generateLabelIndexId } from "src/services/stateStore.service"; import { FormControl } from "@angular/forms"; @@ -11,6 +11,7 @@ import { AtlasViewerConstantsServices } from "src/atlasViewer/atlasViewer.consta import { VIEWER_STATE_ACTION_TYPES } from "src/services/effect/effect"; const filterRegionBasedOnText = searchTerm => region => region.name.toLowerCase().includes(searchTerm.toLowerCase()) +const compareFn = (it, item) => it.name === item.name @Component({ selector: 'region-text-search-autocomplete', @@ -23,6 +24,8 @@ const filterRegionBasedOnText = searchTerm => region => region.name.toLowerCase( export class RegionTextSearchAutocomplete{ + public compareFn = compareFn + @Input() public showBadge: boolean = false @Input() public showAutoComplete: boolean = true @@ -67,13 +70,16 @@ export class RegionTextSearchAutocomplete{ shareReplay(1) ) - this.autocompleteList$ = this.formControl.valueChanges.pipe( - startWith(''), - distinctUntilChanged(), - debounceTime(200), - withLatestFrom(this.regionsWithLabelIndex$.pipe( + this.autocompleteList$ = combineLatest( + this.formControl.valueChanges.pipe( + startWith(''), + distinctUntilChanged(), + debounceTime(200), + ), + this.regionsWithLabelIndex$.pipe( startWith([]) - )), + ) + ).pipe( map(([searchTerm, regionsWithLabelIndex]) => regionsWithLabelIndex.filter(filterRegionBasedOnText(searchTerm))), map(arr => arr.slice(0, 5)) ) diff --git a/src/ui/viewerStateController/regionSearch/regionSearch.template.html b/src/ui/viewerStateController/regionSearch/regionSearch.template.html index 59e9a2060832fbb49b661fd14827580bd0da1044..db6ec9747c3573769c1a3dfbaf36869a3694cc7e 100644 --- a/src/ui/viewerStateController/regionSearch/regionSearch.template.html +++ b/src/ui/viewerStateController/regionSearch/regionSearch.template.html @@ -23,34 +23,14 @@ *ngFor="let region of autocompleteList$ | async" [value]="region.labelIndexId"> - <div class="d-flex flex-row"> + <simple-region + #simpleRegion + (click)="simpleRegion.toggleRegionSelected()" + iav-stop="click" + [region]="region" + [isSelected]="regionsSelected$ | async | includes : region : compareFn"> - <small class="text-truncate flex-shrink-1 flex-grow-1"> - {{ region.name }} - </small> - - <div class="flex-grow-0 flex-shrink-0 d-flex flex-row"> - - <!-- if has position defined --> - <button *ngIf="region.position" - iav-stop="click" - (click)="navigateTo(region.position)" - mat-icon-button> - <i class="fas fa-map-marked-alt"></i> - </button> - - <!-- region selected --> - <button mat-icon-button - iav-stop="click" - (click)="toggleRegionWithId(region.labelIndexId, selectedRegionLabelIndexSet.has(region.labelIndexId))" - [color]="selectedRegionLabelIndexSet.has(region.labelIndexId) ? 'primary' : 'basic'"> - <i class="far" - [ngClass]="{'fa-check-square': selectedRegionLabelIndexSet.has(region.labelIndexId), 'fa-square': !selectedRegionLabelIndexSet.has(region.labelIndexId)}"> - </i> - </button> - </div> - - </div> + </simple-region> </mat-option> </mat-autocomplete> </form> diff --git a/src/ui/viewerStateController/viewerState.base.ts b/src/ui/viewerStateController/viewerState.base.ts index 957be297a44f2f030fa0c0e84d7a697043239b2f..3920443e06b17c7fc258f4495cea78270a5a8b88 100644 --- a/src/ui/viewerStateController/viewerState.base.ts +++ b/src/ui/viewerStateController/viewerState.base.ts @@ -210,6 +210,8 @@ const ACTION_TYPES = { SELECT_TEMPLATE_WITH_NAME: 'SELECT_TEMPLATE_WITH_NAME', SELECT_PARCELLATION_WITH_NAME: 'SELECT_PARCELLATION_WITH_NAME', + TOGGLE_REGION_SELECT: 'TOGGLE_REGION_SELECT', + NAVIGATETO_REGION: 'NAVIGATETO_REGION' } export const VIEWERSTATE_CONTROLLER_ACTION_TYPES = ACTION_TYPES diff --git a/src/ui/viewerStateController/viewerState.useEffect.ts b/src/ui/viewerStateController/viewerState.useEffect.ts index 4dc0831faf978faf05d2ea2a3152a9714c2541bf..ea0ae792e66588a4b44898eba1aa6bc1eac8fdf4 100644 --- a/src/ui/viewerStateController/viewerState.useEffect.ts +++ b/src/ui/viewerStateController/viewerState.useEffect.ts @@ -18,8 +18,6 @@ export class ViewerStateControllerUseEffect implements OnInit, OnDestroy{ private selectedRegions$: Observable<any[]> - @Effect() - singleClickOnHierarchy$: Observable<any> @Effect() selectTemplateWithName$: Observable<any> @@ -27,9 +25,24 @@ export class ViewerStateControllerUseEffect implements OnInit, OnDestroy{ @Effect() selectParcellationWithName$: Observable<any> + /** + * Determines how single click on region hierarchy will affect view + */ + @Effect() + singleClickOnHierarchy$: Observable<any> + + /** + * Determines how double click on region hierarchy will effect view + */ @Effect() doubleClickOnHierarchy$: Observable<any> + @Effect() + toggleRegionSelection$: Observable<any> + + @Effect() + navigateToRegion$: Observable<any> + constructor( private actions$: Actions, private store$: Store<any>, @@ -122,6 +135,16 @@ export class ViewerStateControllerUseEffect implements OnInit, OnDestroy{ this.doubleClickOnHierarchy$ = this.actions$.pipe( ofType(VIEWERSTATE_CONTROLLER_ACTION_TYPES.DOUBLE_CLICK_ON_REGIONHIERARCHY), + map(action => { + return { + ...action, + type: VIEWERSTATE_CONTROLLER_ACTION_TYPES.NAVIGATETO_REGION + } + }) + ) + + this.navigateToRegion$ = this.actions$.pipe( + ofType(VIEWERSTATE_CONTROLLER_ACTION_TYPES.NAVIGATETO_REGION), map(action => { const { payload = {} } = action as ViewerStateAction const { region } = payload @@ -156,6 +179,16 @@ export class ViewerStateControllerUseEffect implements OnInit, OnDestroy{ this.singleClickOnHierarchy$ = this.actions$.pipe( ofType(VIEWERSTATE_CONTROLLER_ACTION_TYPES.SINGLE_CLICK_ON_REGIONHIERARCHY), + map(action => { + return { + ...action, + type: VIEWERSTATE_CONTROLLER_ACTION_TYPES.TOGGLE_REGION_SELECT + } + }) + ) + + this.toggleRegionSelection$ = this.actions$.pipe( + ofType(VIEWERSTATE_CONTROLLER_ACTION_TYPES.TOGGLE_REGION_SELECT), withLatestFrom(this.selectedRegions$), map(([action, regionsSelected]) => { diff --git a/src/util/directives/captureClickListener.directive.ts b/src/util/directives/captureClickListener.directive.ts index bd92d61fe6cf9944e5142078b964c4e31044cd55..5489eaf2131ecd96db11aa480c56ae814fd04c4c 100644 --- a/src/util/directives/captureClickListener.directive.ts +++ b/src/util/directives/captureClickListener.directive.ts @@ -1,7 +1,6 @@ import {Directive, ElementRef, EventEmitter, Input, OnDestroy, OnInit, Output} from "@angular/core"; import {Observable, Observer, Subscription} from "rxjs"; import {switchMapTo, takeUntil} from "rxjs/operators"; -import {RegionMenuComponent} from "src/ui/regionToolsMenu/regionMenu.component"; @Directive({ selector: '[iav-captureClickListenerDirective]' @@ -10,13 +9,13 @@ import {RegionMenuComponent} from "src/ui/regionToolsMenu/regionMenu.component"; export class CaptureClickListenerDirective implements OnInit, OnDestroy { private subscriptions: Subscription[] = [] - @Output() mapClicked: EventEmitter<any> = new EventEmitter() - @Output() mouseDownEmitter: EventEmitter<any> = new EventEmitter() + @Output('iav-captureClickListenerDirective-onClick') mapClicked: EventEmitter<any> = new EventEmitter() + @Output('iav-captureClickListenerDirective-onMousedown') mouseDownEmitter: EventEmitter<any> = new EventEmitter() constructor(private el: ElementRef){} - ngOnInit(): void { + ngOnInit(){ // Listen click Events const mouseDownObs$ = new Observable((observer: Observer<any>) => { @@ -45,7 +44,7 @@ export class CaptureClickListenerDirective implements OnInit, OnDestroy { ) } - ngOnDestroy(): void { + ngOnDestroy(){ this.subscriptions.forEach(s=> s.unsubscribe()) } diff --git a/src/util/pipes/includes.pipe.ts b/src/util/pipes/includes.pipe.ts new file mode 100644 index 0000000000000000000000000000000000000000..54e01a5c4bd691ee704eab17895d2f48f0566edb --- /dev/null +++ b/src/util/pipes/includes.pipe.ts @@ -0,0 +1,15 @@ +import { PipeTransform, Pipe } from "@angular/core"; + +const defaultCompareFn = (item: any, comparator:any):boolean => item === comparator + +@Pipe({ + name: 'includes' +}) + +export class IncludesPipe implements PipeTransform{ + public transform(array: any[], item: any, compareFn=defaultCompareFn):boolean{ + if (!array) return false + if (!(array instanceof Array)) return false + return array.some(it => compareFn(it, item)) + } +} \ No newline at end of file diff --git a/src/util/util.module.ts b/src/util/util.module.ts index 229608e356073e3160be72dbbf29a12bf8592a87..c5cfb55540b81d802a192fb9c799fcc5ebdbf8a3 100644 --- a/src/util/util.module.ts +++ b/src/util/util.module.ts @@ -5,6 +5,7 @@ import { StopPropagationDirective } from "./directives/stopPropagation.directive import { DelayEventDirective } from "./directives/delayEvent.directive"; import { MouseHoverDirective, MouseOverTextPipe, MouseOverIconPipe } from "./directives/mouseOver.directive"; import { KeyListner } from "./directives/keyDownListener.directive"; +import { IncludesPipe } from "./pipes/includes.pipe"; @NgModule({ declarations: [ @@ -15,7 +16,8 @@ import { KeyListner } from "./directives/keyDownListener.directive"; MouseHoverDirective, MouseOverTextPipe, MouseOverIconPipe, - KeyListner + KeyListner, + IncludesPipe ], exports: [ FilterNullPipe, @@ -25,7 +27,8 @@ import { KeyListner } from "./directives/keyDownListener.directive"; MouseHoverDirective, MouseOverTextPipe, MouseOverIconPipe, - KeyListner + KeyListner, + IncludesPipe ] })