diff --git a/src/atlasViewer/atlasViewer.apiService.service.ts b/src/atlasViewer/atlasViewer.apiService.service.ts index c040cde554a6852933ee323bace0d2e409d649ad..69912b85bd23c5061dbca866882aba3d2c616b24 100644 --- a/src/atlasViewer/atlasViewer.apiService.service.ts +++ b/src/atlasViewer/atlasViewer.apiService.service.ts @@ -1,6 +1,6 @@ import { Injectable } from "@angular/core"; import { Store, select } from "@ngrx/store"; -import { ViewerStateInterface, safeFilter, getLabelIndexMap, isDefined } from "../services/stateStore.service"; +import { ViewerStateInterface, safeFilter, getLabelIndexMap, isDefined } from "src/services/stateStore.service"; import { Observable } from "rxjs"; import { map, distinctUntilChanged, filter } from "rxjs/operators"; import { BsModalService } from "ngx-bootstrap/modal"; diff --git a/src/atlasViewer/atlasViewer.dataService.service.ts b/src/atlasViewer/atlasViewer.dataService.service.ts index ae8c41d418bf62f65250aa3629a7e2e8b68046b1..3cc95082cdd71699dd9147928cb7237f60deefbb 100644 --- a/src/atlasViewer/atlasViewer.dataService.service.ts +++ b/src/atlasViewer/atlasViewer.dataService.service.ts @@ -67,8 +67,6 @@ export class AtlasViewerDataService implements OnDestroy{ }) }) )) - - this.init() } /* all units in mm */ @@ -188,55 +186,6 @@ export class AtlasViewerDataService implements OnDestroy{ } - init(){ - - const dispatchData = (arr:DataEntry[][]) =>{ - this.store.dispatch({ - type : FETCHED_DATAENTRIES, - fetchedDataEntries : arr.reduce((acc,curr)=>acc.concat(curr),[]) - }) - } - - const fetchData = (templateName : string, parcellationName: string) => { - dispatchData([]) - const encodedTemplateName = encodeURI(templateName) - const encodedParcellationName = encodeURI(parcellationName) - Promise.all([ - fetch(`${this.constantService.backendUrl}datasets/templateName/${encodedTemplateName}`) - .then(res => res.json()), - fetch(`${this.constantService.backendUrl}datasets/parcellationName/${encodedParcellationName}`) - .then(res => res.json()) - ]) - .then(arr => [...arr[0], ...arr[1]]) - .then(arr => arr.reduce((acc, item) => { - const newMap = new Map(acc) - return newMap.set(item.name, item) - }, new Map())) - .then(map => Array.from(map.values())) - .then(dispatchData) - .catch(console.warn) - } - - this.subscriptions.push( - combineLatest( - this.store.pipe( - select('viewerState'), - safeFilter('templateSelected'), - map(({templateSelected})=>(templateSelected.name)), - distinctUntilChanged() - ), - this.store.pipe( - select('viewerState'), - safeFilter('parcellationSelected'), - map(({parcellationSelected})=>(parcellationSelected.name)), - distinctUntilChanged() - ) - ).pipe( - debounceTime(16) - ).subscribe((param : [string, string] ) => fetchData(param[0], param[1])) - ) - } - ngOnDestroy(){ this.subscriptions.forEach(s=>s.unsubscribe()) } diff --git a/src/atlasViewer/atlasViewer.style.css b/src/atlasViewer/atlasViewer.style.css index 2c8d8178570e50956588776d8ac5e2d7ffbd16f0..a8481399f45b606f64c4a6e1dacd0449ecc15503 100644 --- a/src/atlasViewer/atlasViewer.style.css +++ b/src/atlasViewer/atlasViewer.style.css @@ -35,12 +35,12 @@ div[bannerWrapper] { z-index:5; position:absolute; - top : 1em; - left: 1em; width:100%; display:flex; height:0px; + + justify-content: space-between; } @@ -58,6 +58,17 @@ layout-floating-container overflow:hidden; } +[iconWrapper] +{ + margin-left: 1em; +} + +div[signinWrapper] +{ + display: flex; + flex-direction: row; +} + [toastContainer] { pointer-events: none; diff --git a/src/atlasViewer/atlasViewer.template.html b/src/atlasViewer/atlasViewer.template.html index fbdb2f07935e84a824861b7b6adcc8ff22b0d2ce..97531ecd1017a9a05869b309bca97efb598073f9 100644 --- a/src/atlasViewer/atlasViewer.template.html +++ b/src/atlasViewer/atlasViewer.template.html @@ -1,152 +1,15 @@ <div *ngIf = "meetsRequirement" class = "atlas-container" helpdirective> - <layout-mainside - [showSide] = "false" - [animationFlag] = "false" - (panelAnimationEnd) = "panelAnimationEnd($event)"> - <div maincontent> - <ui-nehuba-container> - </ui-nehuba-container> + <ui-nehuba-container> + </ui-nehuba-container> - <div bannerWrapper> - - <logo-container> - </logo-container> - - <atlas-banner *ngIf = "!isMobile"> - </atlas-banner> - </div> + <div bannerWrapper> + <menu-icons iconWrapper> + </menu-icons> + <div signinWrapper> </div> - <div [ngSwitch] = "sidePanelView$ | async" sidecontent> - <div *ngSwitchCase = "'menuBrowser'"> - <panel-component> - <div heading> - Menu - </div> - <div mobileMenu body> - <atlas-banner> - - </atlas-banner> - <template-parcellation-citation-container mobileTemplateCitation> - - </template-parcellation-citation-container> - </div> - </panel-component> - </div> - - <!-- Data Browser --> - <div *ngSwitchCase = "'dataBrowser'"> - <panel-component [bodyCollapsable] = "true"> - <div heading> - Data Browser - </div> - <data-browser body> - </data-browser> - </panel-component> - </div> - - <!-- Layer Browser --> - <div *ngSwitchCase = "'ngLayer'"> - <panel-component [bodyCollapsable] = "true"> - <div heading> - Layer Browser - </div> - <layer-browser body> - </layer-browser> - </panel-component> - </div> - - <!-- Tools Browser--> - <div *ngSwitchCase = "'toolsBrowser'"> - <panel-component [bodyCollapsable] = "true"> - <div heading> - Tools Browser - </div> - <plugin-banner body> - </plugin-banner> - </panel-component> - </div> - - <!-- Default (shown at the beginning + sliver of time during closing of side bar)--> - <div *ngSwitchDefault> - - </div> - - <!-- Docked Container--> - <div dockedContainer> - <div dockedContainerDirective> - - </div> - </div> - </div> - - <div resizeSliver> - <span - *ngIf = "isMobile" - class = "tabContainer" - (click) = "toggleSidePanel('menuBrowser')" - [ngClass] = "{'active-tab' : (sidePanelView$ | async) === 'menuBrowser'}" > - <i class="glyphicon glyphicon-info-sign"></i> - </span> - - <span - container = "body" - placement = "left" - [tooltip] = "isMobile ? null : !(selectedPOI$ | async) ? '' : (((selectedPOI$ | async).length === 0 ? 'No' : (selectedPOI$ | async).length) + ' selected region' + ((selectedPOI$ | async).length > 1 ? 's' : '')) + ' of interest' " - [ngClass] = "{'active-tab' : (sidePanelView$ | async) === 'dataBrowser'}" - (click) = "toggleSidePanel('dataBrowser')" - class = "tabContainer"> - <span [@newEvent] = "selectedPOI$ | async" class = "highlightContainer"> - </span> - <i class = "glyphicon glyphicon-search"></i> - <span class = "badge" *ngIf = "(selectedPOI$ | async) && (selectedPOI$ | async).length > 0"> - {{ (selectedPOI$ | async).length }} - </span> - </span> - - <span - placement = "left" - [tooltip] = "isMobile ? null : !ngLayers ? '' : ((ngLayers.length === 0 ? 'No' : ngLayers.length) + ' loaded layer' + (ngLayers.length > 1 ? 's' : ''))" - [ngClass] = "{'active-tab' : (sidePanelView$ | async) === 'ngLayer'}" - (click) = "toggleSidePanel('ngLayer')" - class = "tabContainer"> - <span [@newEvent] = "ngLayers" class = "highlightContainer"> - </span> - <i class = "glyphicon glyphicon-list"></i> - <span class = "badge" *ngIf = "ngLayers && ngLayers.length > 0"> - {{ ngLayers.length }} - </span> - </span> - - <span - placement = "left" - (mouseover) = "_toolIconsVisible = true" - (mouseout) = "_toolIconsVisible = false" - [tooltip] = "isMobile ? null : _toolIconDockAll ? 'dock all widgets' : _toolIconFloatAll ? 'float all widgets' : 'Tools Browser'" - [ngClass] = "{'active-tab' : (sidePanelView$ | async) === 'toolsBrowser'}" - (click) = "toggleSidePanel('toolsBrowser')" - class = "tabContainer"> - <i class="glyphicon glyphicon-wrench"></i> - <span *ngIf = "!isMobile" class ="badge"> - <span [class.hidden] = "_toolIconsVisible" > - ... - </span> - <span [class.hidden] = "!_toolIconsVisible"> - <span class = "pointer-events" (click) = "$event.stopPropagation();this.widgetServices.dockAllWidgets()" (mouseover) = "_toolIconDockAll = true" (mouseout) = "_toolIconDockAll = false"> - <i class = "no-pointer-events glyphicon glyphicon-log-in"></i> - </span> - <span class = "mute-text"> - | - </span> - <span class = "pointer-events" (click) = "$event.stopPropagation();this.widgetServices.floatAllWidgets()" (mouseover) = "_toolIconFloatAll = true" (mouseout) = "_toolIconFloatAll = false"> - <i class = "no-pointer-events glyphicon glyphicon-new-window"></i> - </span> - </span> - </span> - </span> - </div> - </layout-mainside> + </div> <layout-floating-container zIndex = "9" diff --git a/src/atlasViewer/widgetUnit/widgetService.service.ts b/src/atlasViewer/widgetUnit/widgetService.service.ts index be784e7cb95c13d75f48f6e22e212cdc03e78d46..0e490d2890c3de7a6b0e0df47619e29fe2f1690f 100644 --- a/src/atlasViewer/widgetUnit/widgetService.service.ts +++ b/src/atlasViewer/widgetUnit/widgetService.service.ts @@ -64,6 +64,7 @@ export class WidgetServices{ _component.instance.exitable = _option.exitable _component.instance.title = _option.title _component.instance.persistency = _option.persistency + _component.instance.titleHTML = _option.titleHTML /* internal properties, used for changing state */ _component.instance.guestComponentRef = guestComponentRef @@ -157,15 +158,10 @@ function getOption(option?:Partial<WidgetOptionsInterface>):WidgetOptionsInterfa exitable : safeGet(option, 'exitable') !== null ? safeGet(option, 'exitable') : true, - state : safeGet(option, 'state') - ? safeGet(option, 'state') - : 'floating', - title : safeGet(option, 'title') - ? safeGet(option, 'title') - : 'Untitled', - persistency : safeGet(option, 'persistency') - ? safeGet(option, 'persistency') - : false + state : safeGet(option, 'state') || 'floating', + title : safeGet(option, 'title') || 'Untitled', + persistency : safeGet(option, 'persistency') || false, + titleHTML: safeGet(option, 'titleHTML') || null } } @@ -173,6 +169,7 @@ export interface WidgetOptionsInterface{ title? : string state? : 'docked' | 'floating' exitable? : boolean - persistency : boolean + persistency? : boolean + titleHTML? : string } diff --git a/src/atlasViewer/widgetUnit/widgetUnit.component.ts b/src/atlasViewer/widgetUnit/widgetUnit.component.ts index 3be619d899d9293eaff612448acc35bebb394663..2ff33338c8318ae06e750b0389852064e6f0806f 100644 --- a/src/atlasViewer/widgetUnit/widgetUnit.component.ts +++ b/src/atlasViewer/widgetUnit/widgetUnit.component.ts @@ -34,8 +34,12 @@ export class WidgetUnit { @Output() clickedEmitter : EventEmitter<WidgetUnit> = new EventEmitter() + @Input() public exitable : boolean = true + @Input() + public titleHTML : string = null + public guestComponentRef : ComponentRef<any> public cf : ComponentRef<WidgetUnit> public widgetServices:WidgetServices @@ -77,6 +81,7 @@ export class WidgetUnit { } exit(event?:Event){ + console.log('exit') if(event){ event.stopPropagation() event.preventDefault() diff --git a/src/atlasViewer/widgetUnit/widgetUnit.template.html b/src/atlasViewer/widgetUnit/widgetUnit.template.html index 9c70830616324ecdf38c10b84761805ff1ebd6d1..3155ee86b5eee06ca5ac412c4f18db2110f12838 100644 --- a/src/atlasViewer/widgetUnit/widgetUnit.template.html +++ b/src/atlasViewer/widgetUnit/widgetUnit.template.html @@ -10,19 +10,26 @@ widgetUnitHeading heading> <div #emptyspan emptyspan>.</div> - <div title>{{ title }}</div> + <div title> + <div *ngIf="!titleHTML"> + {{ title }} + </div> + <div [innerHTML]="titleHTML" *ngIf="titleHTML"> + + </div> + </div> <div icons> <i *ngIf = "state === 'floating'" (click) = "dock($event)" - class = "glyphicon glyphicon-log-in" + class = "fas fa-window-minimize" hoverable></i> <i *ngIf = "state === 'docked'" (click) = "undock($event)" - class = "glyphicon glyphicon-new-window" + class = "fas fa-window-restore" hoverable></i> <i *ngIf = "exitable" (click) = "exit($event)" - class = "glyphicon glyphicon-remove" + class = "fas fa-times" hoverable></i> </div> </div> diff --git a/src/components/components.module.ts b/src/components/components.module.ts index b7bcd03b86b97857e7fb1b007b967a1dc296914a..7f168657153c1dd65dc4b6c9ae0d0aba1f4dd97a 100644 --- a/src/components/components.module.ts +++ b/src/components/components.module.ts @@ -6,7 +6,6 @@ import { MarkdownDom } from './markdown/markdown.component'; import { SafeHtmlPipe } from '../util/pipes/safeHtml.pipe' import { ReadmoreComponent } from './readmoore/readmore.component'; -import { BrowserModule } from '@angular/platform-browser'; import { HoverableBlockDirective } from './hoverableBlock.directive'; import { DropdownComponent } from './dropdown/dropdown.component'; import { TreeComponent } from './tree/tree.component'; @@ -25,11 +24,12 @@ import { AppendSiblingFlagPipe } from './flatTree/appendSiblingFlag.pipe'; import { ClusteringPipe } from './flatTree/clustering.pipe'; import { TimerComponent } from './timer/timer.component'; import { PillComponent } from './pill/pill.component'; +import { CommonModule } from '@angular/common'; @NgModule({ imports : [ - BrowserModule, + CommonModule, FormsModule, BrowserAnimationsModule, ], diff --git a/src/components/flatTree/flatTree.template.html b/src/components/flatTree/flatTree.template.html index de7151e12be34258ebcb2064afe5360d54b25ba4..2e8cd6192a073b2aa551d947370cc32db50cdbce 100644 --- a/src/components/flatTree/flatTree.template.html +++ b/src/components/flatTree/flatTree.template.html @@ -24,10 +24,10 @@ <span *ngIf = "findChildren(flattenedItem).length > 0; else noChildren" (click) = "$event.stopPropagation(); toggleCollapse(flattenedItem)" > - <i [ngClass] = "isCollapsed(flattenedItem) ? 'glyphicon-chevron-right' : 'glyphicon-chevron-down'" class="glyphicon"></i> + <i [ngClass] = "isCollapsed(flattenedItem) ? 'fa-chevron-right' : 'fa-chevron-down'" class="fas"></i> </span> <ng-template #noChildren> - <i class="glyphicon glyphicon-none"> + <i class="fas fa-none"> </i> </ng-template> diff --git a/src/components/pagination/pagination.template.html b/src/components/pagination/pagination.template.html index cb53156b2a5e30acdf284bf2a9bb37c90a3f261b..1d3f14ae11e85731797eb47941ced54ef63bfc62 100644 --- a/src/components/pagination/pagination.template.html +++ b/src/components/pagination/pagination.template.html @@ -6,14 +6,14 @@ (click)="gotoFirst()" > - <i class = "glyphicon glyphicon-fast-backward"></i> + <i class = "fas fa-fast-backward"></i> </div> <div class = "btn btn-default btn-sm" (click)="goto(currentPage - 1)" > - <i class = "glyphicon glyphicon-step-backward"></i> + <i class = "fas fa-step-backward"></i> </div> <div @@ -29,13 +29,13 @@ class = "btn btn-default btn-sm" (click)="goto(currentPage + 1)" > - <i class = "glyphicon glyphicon-step-forward"></i> + <i class = "fas fa-step-forward"></i> </div> <div class = "btn btn-default btn-sm" (click)="gotoLast()" > - <i class = "glyphicon glyphicon-fast-forward"></i> + <i class = "fas fa-fast-forward"></i> </div> </div> diff --git a/src/components/panel/panel.style.css b/src/components/panel/panel.style.css index 8cf87fc19f464e119eabba889b2e4b9e70c968f5..3c72615f1dc704bdc5e0ae7a28f3738e85e75da4 100644 --- a/src/components/panel/panel.style.css +++ b/src/components/panel/panel.style.css @@ -47,7 +47,7 @@ div.panel-body color:rgba(223,240,216 ,0.9); } -:host-context([darktheme="true"]) div.panel-body +:host-context([darktheme="true"]) div.l-card-body { color:rgba(255,255,255,0.9); background-color:rgba(45,45,45,0.8); diff --git a/src/components/panel/panel.template.html b/src/components/panel/panel.template.html index 7f7c1af411784a2153456c412dc9f02429288e5e..48fbb5069aa442f2bf2716299e94571b8e210593 100644 --- a/src/components/panel/panel.template.html +++ b/src/components/panel/panel.template.html @@ -1,35 +1,33 @@ <div *ngIf = "showHeading" class = "panel-heading" - (click) = "toggleCollapseBody($event)" hoverable> - <ng-content select="[heading]"> - </ng-content> - </div> -<!-- While wrapping body and footer in a diffrent div adds a lot of overhead, it enables nice transition effects --> -<div - bodyFooterContainer> - <div - *ngIf = "showBody" - class = "panel-body" - [@collapseState] = "{ value : collapseBody ? 'collapsed' : 'visible', params : { fullHeight : fullHeight } }" - #panelBody> - - <ng-content select="[body]"> - </ng-content> +<div class="l-card"> + <div class="l-card-body"> + <div + *ngIf="showHeading" + class="l-card-title" + (click)="toggleCollapseBody($event)" + hoverable> + <ng-content select="[heading]"> + </ng-content> + </div> + <!-- While wrapping body and footer in a diffrent div adds a lot of overhead, it enables nice transition effects --> + <div + bodyFooterContainer> + <div + *ngIf = "showBody" + class = "l-card-text" + [@collapseState] = "{ value : collapseBody ? 'collapsed' : 'visible', params : { fullHeight : fullHeight } }" + #panelBody> - </div> - <div - *ngIf = "showFooter" - class = "panel-footer" - [@collapseState] = "{ value : collapseBody ? 'collapsed' : 'visible', params : { fullHeight : fullHeight } }" - #panelFooter> - - <ng-content select="[footer]"> - </ng-content> + <ng-content select="[body]"> + </ng-content> + </div> + </div> </div> </div> \ No newline at end of file diff --git a/src/components/pill/pill.style.css b/src/components/pill/pill.style.css index 7fae3ae45d04164d0e1482c3b02564b850a2f20f..7ecc86ab4ae6ec1d6c506dd80ee6eec507a2a64e 100644 --- a/src/components/pill/pill.style.css +++ b/src/components/pill/pill.style.css @@ -15,15 +15,11 @@ .pill-close { flex: 0 0 auto; - width:1.4em; - height: 1.4em; - font-size:0.8em; - padding: 0.2em; - border-radius: 0.7em; box-sizing: border-box; + line-height: 1; margin-left: 0.4em; - margin-right: -0.8em; + margin-right: -0.6em; } :host diff --git a/src/components/pill/pill.template.html b/src/components/pill/pill.template.html index 0900ed2148bdaac8e0de9169dbf4792f5877f4c7..e5b2618e9fc02a0db167df6035d2fa5eabbde4b5 100644 --- a/src/components/pill/pill.template.html +++ b/src/components/pill/pill.template.html @@ -5,11 +5,11 @@ class="pill-title"> {{ title }} </span> - <span + <div [ngStyle]="closeBtnStyle" - class="pill-close" + class="pill-close rounded-circle" (click)="close()" *ngIf="showClose"> - <i class="glyphicon glyphicon-remove"></i> - </span> + <i class="fas fa-times-circle"></i> + </div> </div> \ No newline at end of file diff --git a/src/components/radiolist/radiolist.component.ts b/src/components/radiolist/radiolist.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..319d1625b4cbb2a594b06bf5c5903edd89936481 --- /dev/null +++ b/src/components/radiolist/radiolist.component.ts @@ -0,0 +1,30 @@ +import { Component, ChangeDetectionStrategy } from "@angular/core"; + +@Component({ + selector: 'radio-list', + templateUrl: './radiolist.template.html', + styleUrls: [ + './radiolist.style.css' + ], + styles: [ + ` + ul > li.selected > span:before + { + content: '\u2022'; + width : 1em; + display:inline-block; + } + ul > li:not(.selected) > span:before + { + content: ' '; + width : 1em; + display:inline-block; + } + ` + ], + changeDetection: ChangeDetectionStrategy.OnPush +}) + +export class RadioList{ + +} \ No newline at end of file diff --git a/src/components/radiolist/radiolist.style.css b/src/components/radiolist/radiolist.style.css new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/components/radiolist/radiolist.template.html b/src/components/radiolist/radiolist.template.html new file mode 100644 index 0000000000000000000000000000000000000000..23aaefbdaf72ce199a0b34686dbf3e47d0f52334 --- /dev/null +++ b/src/components/radiolist/radiolist.template.html @@ -0,0 +1,17 @@ + +<ul + class="dropdown-menu" + role="menu" + [@showState] = "openState ? 'show' : 'hide'"> + <li + *ngFor="let input of inputArray" + [ngClass]="selectedItem === input ? 'selected' : 'notselected'" + (click)="itemSelected.emit(input); openState = false" + role="menuitem"> + + <span + class="dropdown-item" + [innerHTML] = "listDisplay(input)"> + </span> + </li> +</ul> \ No newline at end of file diff --git a/src/components/readmoore/readmore.template.html b/src/components/readmoore/readmore.template.html index a54e54bee9f5b799b7575d78c0ab53d344122e2a..11fc3189dd7b9a37cf59506e326c0848c1c1fd53 100644 --- a/src/components/readmoore/readmore.template.html +++ b/src/components/readmoore/readmore.template.html @@ -12,7 +12,7 @@ hoverable> <i - [ngClass] = "show ? 'glyphicon-chevron-up' : 'glyphicon-chevron-down'" - class = "glyphicon"> + [ngClass] = "show ? 'fa-chevron-up' : 'fa-chevron-down'" + class = "fas"> </i> </div> \ No newline at end of file diff --git a/src/components/toast/toast.template.html b/src/components/toast/toast.template.html index 17e173c70db4a6228c917cff8e5db70d2961c097..7cd477e5aee8a9afc1793982f94cf3b51037725d 100644 --- a/src/components/toast/toast.template.html +++ b/src/components/toast/toast.template.html @@ -8,7 +8,7 @@ {{ message }} </div> <div (click) = "dismiss($event)" *ngIf = "dismissable" close> - <i class = "glyphicon glyphicon-remove"></i> + <i class = "fas fa-remove"></i> </div> <timer-component (timerEnd)="dismissed.emit(false)" [pause] = "hover" [timeout] = "timeout" timer> </timer-component> diff --git a/src/components/tree/tree.component.ts b/src/components/tree/tree.component.ts index 4bc3063a2111bbc742cbabb7a2808d702de07288..5ec84da8ecfb4ed956d4ccc95bcb32c383004d02 100644 --- a/src/components/tree/tree.component.ts +++ b/src/components/tree/tree.component.ts @@ -96,10 +96,10 @@ export class TreeComponent extends ParseAttributeDirective implements OnChanges, return this.children ? this.children.length > 0 ? this.childrenExpanded ? - 'glyphicon-chevron-down' : - 'glyphicon-chevron-right' : - 'glyphicon-none' : - 'glyphicon-none' + 'fa-chevron-down' : + 'fa-chevron-right' : + 'fa-none' : + 'fa-none' } public handleEv(event:Event){ diff --git a/src/components/tree/tree.style.css b/src/components/tree/tree.style.css index 1fa2da45772b8c39ceefd2d684ecc91794409bcb..b05982290b10f2674d9868aa9d958274d762979d 100644 --- a/src/components/tree/tree.style.css +++ b/src/components/tree/tree.style.css @@ -9,7 +9,7 @@ div[itemContainer] display:flex; } -div[itemContainer] > [glyphicon] +div[itemContainer] > [fas] { flex: 0 0 1.2em; align-self: center; diff --git a/src/components/tree/tree.template.html b/src/components/tree/tree.template.html index 8e7dff2d794b095bd4eaf712bac498339a5bb96c..10b6318bfe862cd8da5813da78aabd16e3ff6782 100644 --- a/src/components/tree/tree.template.html +++ b/src/components/tree/tree.template.html @@ -4,8 +4,8 @@ <i (click) = "toggleChildrenShow($event)" [ngClass] = "chevronClass" - class = "glyphicon" - glyphicon> + class = "fas" + fas> </i> <span (mouseleave)="handleMouseLeave({inputItem:inputItem,node:this});handleEv($event)" diff --git a/src/index.html b/src/index.html index 55ed7b6156871cd5192c5499c6607793000deddd..3a45da9bca5a8f7038f93470f1089bdfe3d696b6 100644 --- a/src/index.html +++ b/src/index.html @@ -4,7 +4,8 @@ <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> - <link rel = "stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"> + <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous"> + <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.8.1/css/all.css" integrity="sha384-50oBUHEmvpQ+1lW4y57PTFmhCaXp0ML5d60M1M7uH2+nqUivzIebhndOJK28anvf" crossorigin="anonymous"> <link rel = "stylesheet" href = "extra_styles.css"> <link rel = "stylesheet" href = "plugin_styles.css"> <title>Interactive Atlas Viewer</title> @@ -48,4 +49,4 @@ </script> </body> -</html> \ No newline at end of file +</html> diff --git a/src/main.module.ts b/src/main.module.ts index daf70e99d09ceadbb3e6102a95575b6498b06bad..b167b47900fd14bf0cda60bf956efe95db3e7742 100644 --- a/src/main.module.ts +++ b/src/main.module.ts @@ -13,14 +13,13 @@ import { FormsModule } from "@angular/forms"; import { AtlasViewerDataService } from "./atlasViewer/atlasViewer.dataService.service"; import { WidgetUnit } from "./atlasViewer/widgetUnit/widgetUnit.component"; import { WidgetServices } from './atlasViewer/widgetUnit/widgetService.service' -import { GlyphiconTooltipScreenshotDirective,GlyphiconTooltipInfoSignDirective,GlyphiconTooltipLogInDirective,GlyphiconTooltipNewWindowDirective,GlyphiconTooltipQuestionSignDirective,GlyphiconTooltipRemoveDirective,GlyphiconTooltipRemoveSignDirective } from "./util/directives/glyphiconTooltip.directive"; +import { fasTooltipScreenshotDirective,fasTooltipInfoSignDirective,fasTooltipLogInDirective,fasTooltipNewWindowDirective,fasTooltipQuestionSignDirective,fasTooltipRemoveDirective,fasTooltipRemoveSignDirective } from "./util/directives/glyphiconTooltip.directive"; import { TooltipModule } from "ngx-bootstrap/tooltip"; import { ModalModule } from 'ngx-bootstrap/modal' import { ModalUnit } from "./atlasViewer/modalUnit/modalUnit.component"; import { AtlasViewerURLService } from "./atlasViewer/atlasViewer.urlService.service"; import { ToastComponent } from "./components/toast/toast.component"; import { GetFilenameFromPathnamePipe } from "./util/pipes/getFileNameFromPathName.pipe"; -import { FilterNameBySearch } from "./util/pipes/filterNameBySearch.pipe"; import { AtlasViewerAPIServices } from "./atlasViewer/atlasViewer.apiService.service"; import { PluginUnit } from "./atlasViewer/pluginUnit/pluginUnit.component"; import { NewViewerDisctinctViewToLayer } from "./util/pipes/newViewerDistinctViewToLayer.pipe"; @@ -62,13 +61,13 @@ import { ViewerConfiguration } from "./services/state/viewerConfig.store"; PluginUnit, /* directives */ - GlyphiconTooltipScreenshotDirective, - GlyphiconTooltipInfoSignDirective, - GlyphiconTooltipLogInDirective, - GlyphiconTooltipNewWindowDirective, - GlyphiconTooltipQuestionSignDirective, - GlyphiconTooltipRemoveDirective, - GlyphiconTooltipRemoveSignDirective, + fasTooltipScreenshotDirective, + fasTooltipInfoSignDirective, + fasTooltipLogInDirective, + fasTooltipNewWindowDirective, + fasTooltipQuestionSignDirective, + fasTooltipRemoveDirective, + fasTooltipRemoveSignDirective, HelpDirective, ToastContainerDirective, DockedContainerDirective, @@ -80,7 +79,6 @@ import { ViewerConfiguration } from "./services/state/viewerConfig.store"; GetNamesPipe, GetNamePipe, GetFilenameFromPathnamePipe, - FilterNameBySearch, NewViewerDisctinctViewToLayer ], entryComponents : [ diff --git a/src/res/css/extra_styles.css b/src/res/css/extra_styles.css index 5481f94d09177ce2abc642126bf66fddf8195611..917ee3264386a4656e9bdb530ff0b8bd2c2b166d 100644 --- a/src/res/css/extra_styles.css +++ b/src/res/css/extra_styles.css @@ -2,6 +2,7 @@ html { width:100%; height:100%; + font-size:90%; } body { @@ -10,7 +11,7 @@ body margin:0; border:0; - /* required for glyphicon tooltip directives */ + /* required for fas tooltip directives */ overflow:hidden; } div.scale-bar-container @@ -149,7 +150,7 @@ markdown-dom pre code background-color:rgba(150,150,0,0.5); } -.glyphicon-none +.fa-none { width:1em; } diff --git a/src/services/state/dataStore.store.ts b/src/services/state/dataStore.store.ts index 7242028e910685d19fc727a7715e8f0d480b9b50..889f1bbdc9f0ec88fb4d61b7838e010292d9f895 100644 --- a/src/services/state/dataStore.store.ts +++ b/src/services/state/dataStore.store.ts @@ -32,7 +32,14 @@ export const FETCHED_DATAENTRIES = 'FETCHED_DATAENTRIES' export const FETCHED_METADATA = 'FETCHED_METADATA' export const FETCHED_SPATIAL_DATA = `FETCHED_SPATIAL_DATA` +export interface Activity{ + methods: string[] + preparation: string[] + protocols: string[ ] +} + export interface DataEntry{ + activity: Activity[] name: string description: string license: string[] diff --git a/src/services/state/spatialSearchState.store.ts b/src/services/state/spatialSearchState.store.ts index c3a5bd4c7690741be92f1cdea0b5c4d7040c3011..cdc34bf99f82e1a6c4d72f842114a0c2f5e841ec 100644 --- a/src/services/state/spatialSearchState.store.ts +++ b/src/services/state/spatialSearchState.store.ts @@ -10,10 +10,6 @@ export function spatialSearchState(state:SpatialDataStateInterface = initSpatial return Object.assign({},state,{ spatialSearchTotalResults : action.totalResults }) - case UPDATE_SPATIAL_DATA_VISIBLE: - return Object.assign({},state,{ - spatialDataVisible : action.visible - }) default : return state } @@ -22,11 +18,9 @@ export function spatialSearchState(state:SpatialDataStateInterface = initSpatial export interface SpatialDataStateInterface{ spatialSearchPagination : number spatialSearchTotalResults : number - spatialDataVisible : boolean } const initSpatialDataState : SpatialDataStateInterface = { - spatialDataVisible : true, spatialSearchPagination : 0, spatialSearchTotalResults : 0 } @@ -39,4 +33,3 @@ export interface SpatialDataEntries extends Action{ export const SPATIAL_GOTO_PAGE = `SPATIAL_GOTO_PAGE` export const UPDATE_SPATIAL_DATA = `UPDATE_SPATIAL_DATA` -export const UPDATE_SPATIAL_DATA_VISIBLE = `UPDATE_SPATIAL_DATA_VISIBLE ` diff --git a/src/services/stateStore.service.ts b/src/services/stateStore.service.ts index 68eadf1c57d2133d39cd43cf2cc44aa8e02ad449..18c1a547e5f963ab03db7be7f92e79b0ffc37649 100644 --- a/src/services/stateStore.service.ts +++ b/src/services/stateStore.service.ts @@ -6,7 +6,7 @@ export { NgViewerAction, NgViewerStateInterface, ngViewerState, ADD_NG_LAYER, FO export { CHANGE_NAVIGATION, AtlasAction, DESELECT_LANDMARKS, DESELECT_REGIONS, FETCHED_TEMPLATE, NEWVIEWER, SELECT_LANDMARKS, SELECT_PARCELLATION, SELECT_REGIONS, USER_LANDMARKS, ViewerStateInterface, viewerState } from './state/viewerState.store' export { DataEntry, ParcellationRegion, DataStateInterface, DatasetAction, FETCHED_DATAENTRIES, FETCHED_METADATA, FETCHED_SPATIAL_DATA, Landmark, OtherLandmarkGeometry, PlaneLandmarkGeometry, PointLandmarkGeometry, Property, Publication, ReferenceSpace, dataStore, File, FileSupplementData } from './state/dataStore.store' export { CLOSE_SIDE_PANEL, MOUSE_OVER_LANDMARK, MOUSE_OVER_SEGMENT, OPEN_SIDE_PANEL, TOGGLE_SIDE_PANEL, UIAction, UIStateInterface, uiState } from './state/uiState.store' -export { SPATIAL_GOTO_PAGE, SpatialDataEntries, SpatialDataStateInterface, UPDATE_SPATIAL_DATA, UPDATE_SPATIAL_DATA_VISIBLE, spatialSearchState } from './state/spatialSearchState.store' +export { SPATIAL_GOTO_PAGE, SpatialDataEntries, SpatialDataStateInterface, UPDATE_SPATIAL_DATA, spatialSearchState } from './state/spatialSearchState.store' export function safeFilter(key:string){ return filter((state:any)=> diff --git a/src/ui/banner/banner.component.ts b/src/ui/banner/banner.component.ts index f69c931abeb611e3fe2e1c29546013ea5df1749f..1377d02853b7fb5cb4671ef227d0f90a700d9c49 100644 --- a/src/ui/banner/banner.component.ts +++ b/src/ui/banner/banner.component.ts @@ -1,9 +1,8 @@ -import { Component, OnDestroy, ChangeDetectionStrategy, HostListener, ViewChild, ElementRef, OnInit } from "@angular/core"; +import { Component, OnDestroy, ChangeDetectionStrategy, ViewChild, ElementRef, OnInit } from "@angular/core"; import { Store, select } from "@ngrx/store"; -import { ViewerStateInterface, safeFilter, SELECT_PARCELLATION, extractLabelIdx, SELECT_REGIONS, NEWVIEWER, getLabelIndexMap, isDefined, CHANGE_NAVIGATION } from "../../services/stateStore.service"; -import { Observable, Subscription, merge, Subject } from "rxjs"; -import { map, filter, debounceTime, buffer, distinctUntilChanged } from "rxjs/operators"; -import { FilterNameBySearch } from "../../util/pipes/filterNameBySearch.pipe"; +import { ViewerStateInterface, safeFilter, SELECT_PARCELLATION, SELECT_REGIONS, NEWVIEWER, isDefined } from "../../services/stateStore.service"; +import { Observable, Subscription } from "rxjs"; +import { map, filter, debounceTime, distinctUntilChanged } from "rxjs/operators"; import { regionAnimation } from "./regionPopover.animation"; import { AtlasViewerConstantsServices } from "../../atlasViewer/atlasViewer.constantService.service" import { AuthService, User, AuthMethod } from "src/services/auth.service"; @@ -26,13 +25,9 @@ export class AtlasBanner implements OnDestroy, OnInit { public loadedTemplates$: Observable<any[]> public newViewer$: Observable<any> public selectedParcellation$: Observable<any> - public selectedRegions$: Observable<any[]> - - private regionsLabelIndexMap: Map<number, any> = new Map() public selectedTemplate: any public selectedParcellation: any - public selectedRegions: any[] = [] // private navigation: { position: [number, number, number] } = { position: [0, 0, 0] } private subscriptions: Subscription[] = [] @@ -65,13 +60,6 @@ export class AtlasBanner implements OnDestroy, OnInit { distinctUntilChanged((o, n) => o === n) ) - this.selectedRegions$ = merge( - this.store.pipe( - select('viewerState'), - filter(state => isDefined(state) && isDefined(state.regionsSelected)), - map(state => state.regionsSelected) - ) - ) } ngOnInit() { @@ -79,7 +67,6 @@ export class AtlasBanner implements OnDestroy, OnInit { this.newViewer$.subscribe((state) => { this.selectedTemplate = state.templateSelected const selectedParcellation = state.parcellationSelected ? state.parcellationSelected : this.selectedTemplate.parcellations[0] - this.handleParcellationChange(selectedParcellation) }) ) @@ -92,9 +79,6 @@ export class AtlasBanner implements OnDestroy, OnInit { }) ) - this.subscriptions.push( - this.selectedParcellation$.subscribe((this.handleParcellationChange).bind(this)) - ) this.subscriptions.push( this.selectedParcellation$.pipe( @@ -105,21 +89,6 @@ export class AtlasBanner implements OnDestroy, OnInit { }) ) - this.subscriptions.push( - this.selectedRegions$.subscribe((ev) => { - this.selectedRegions = ev - }) - ) - - this.subscriptions.push( - this.handleRegionTreeClickSubject.pipe( - buffer( - this.handleRegionTreeClickSubject.pipe( - debounceTime(200) - ) - ) - ).subscribe(arr => arr.length > 1 ? this.doubleClick(arr[0]) : this.singleClick(arr[0])) - ) // this.subscriptions.push( // this.store.pipe( @@ -134,13 +103,6 @@ export class AtlasBanner implements OnDestroy, OnInit { this.subscriptions.forEach(s => s.unsubscribe()) } - handleParcellationChange(parcellation) { - if (!(parcellation && parcellation.regions)) { - return - } - this.selectedParcellation = parcellation - this.regionsLabelIndexMap = getLabelIndexMap(parcellation.regions) - } get user() : User | null { return this.authService.user @@ -176,77 +138,7 @@ export class AtlasBanner implements OnDestroy, OnInit { }) } - /* double click navigate to the interested area */ - private doubleClick(obj: any) { - if (!(obj && obj.inputItem && (obj.inputItem.position || obj.inputItem.POIs))) { - return - } - - const newPos = obj.inputItem.position - ? obj.inputItem.position - : obj.inputItem.POIs && obj.inputItem.POIs.constructor === Array && obj.inputItem.POIs.length > 0 - ? obj.inputItem.POIs[0] - : null - - this.store.dispatch({ - type: CHANGE_NAVIGATION, - navigation: { - position: newPos, - animation: { - /* empty object is enough to be truthy */ - } - }, - }) - } - - /* single click selects/deselects region(s) */ - private singleClick(obj: any) { - const region = obj.inputItem - const selectedSet = new Set(extractLabelIdx(region)) - const intersection = new Set([...this.selectedRegions.map(r => r.labelIndex)].filter(v => selectedSet.has(v))) - this.store.dispatch({ - type: SELECT_REGIONS, - selectRegions: intersection.size > 0 ? - this.selectedRegions.filter(v => !intersection.has(v.labelIndex)) : - this.selectedRegions.concat([...selectedSet].map(idx => this.regionsLabelIndexMap.get(idx))) - }) - } - - private handleRegionTreeClickSubject: Subject<any> = new Subject() - - /* NB need to bind two way data binding like this. Or else, on searchInput blur, the flat tree will be rebuilt, - resulting in first click to be ignored */ - changeSearchTerm(event: any) { - if (event.target.value === this.searchTerm) - return - this.searchTerm = event.target.value - } - - @ViewChild('searchRegionPopover', { read: ElementRef }) inputRegionPopover: ElementRef - public showRegionTree: boolean - - @HostListener('document:click', ['$event']) - closeRegion(event: MouseEvent) { - - /* region popover may not always be rendered */ - if (!this.inputRegionPopover) - return - - /* FF < 62 does not implement event.srcElement so use event.originalTarget to polyfill for FF */ - - const contains = event.srcElement - ? this.inputRegionPopover.nativeElement.contains(event.srcElement) - : this.inputRegionPopover.nativeElement.contains((event as any).originalTarget) - if (contains) - this.showRegionTree = true - else - this.showRegionTree = false - } - handleClickRegion(obj: any) { - obj.event.stopPropagation() - this.handleRegionTreeClickSubject.next(obj) - } displayActiveTemplate(template: any) { return `<small>Template</small> <small class = "mute-text">${template ? '(' + template.name + ')' : ''}</small> <span class = "caret"></span>` @@ -256,26 +148,12 @@ export class AtlasBanner implements OnDestroy, OnInit { return `<small>Parcellation</small> <small class = "mute-text">${parcellation ? '(' + parcellation.name + ')' : ''}</small> <span class = "caret"></span>` } - private insertHighlight(name: string, searchTerm: string): string { - const regex = new RegExp(searchTerm, 'gi') - return searchTerm === '' ? - name : - name.replace(regex, (s) => `<span class = "highlight">${s}</span>`) - } - displayTreeNode(item: any) { - return typeof item.labelIndex !== 'undefined' && this.selectedRegions.findIndex(re => re.labelIndex === Number(item.labelIndex)) >= 0 ? - `<span class = "regionSelected">${this.insertHighlight(item.name, this.searchTerm)}</span>` : - `<span class = "regionNotSelected">${this.insertHighlight(item.name, this.searchTerm)}</span>` - } getChildren(item: any) { return item.children } - filterTreeBySearch(node: any): boolean { - return this.filterNameBySearchPipe.transform([node.name], this.searchTerm) - } clearRegions(event: Event) { event.stopPropagation() @@ -286,15 +164,6 @@ export class AtlasBanner implements OnDestroy, OnInit { }) } - filterNameBySearchPipe = new FilterNameBySearch() - - get aggregatedRegionTree() { - return { - name: this.selectedParcellation.name, - children: this.selectedParcellation.regions - } - } - citationExists(obj: any) { return obj && obj.properties && obj.properties.publications && obj.properties.publications.length > 0 } diff --git a/src/ui/banner/banner.style.css b/src/ui/banner/banner.style.css index 4a47b703bb5a3c234f7690777d7f1de1468e8344..08955dc9ff4852fbcbef1d5726981256f2374e9c 100644 --- a/src/ui/banner/banner.style.css +++ b/src/ui/banner/banner.style.css @@ -13,83 +13,6 @@ margin-right:0.5em; } -div[treeHeader] -{ - height: 55px; -} - -div[treeHeader] > span -{ - display:inline-block; - padding: 12px 0px; - line-height:2em; - border:none; -} - -div[treeHeader] > span.btn.btn-link -{ - margin-left:2em; -} - -div[treeContainer] -{ - padding:1em; - z-index: 3; - - max-height:40em; - width: calc(100% + 2em); - overflow-y:auto; - overflow-x:hidden; - - - /* color:white; - background-color:rgba(12,12,12,0.8); */ -} - -:host-context([darktheme="false"]) div[treeContainer] -{ - background-color:rgba(240,240,240,0.8); -} - -:host-context([darktheme="true"]) div[treeContainer] -{ - background-color:rgba(30,30,30,0.8); - color:rgba(255,255,255,1.0); -} - -div[hideScrollbarcontainer] -{ - width: 20em; - overflow:hidden; - margin-top:2px; -} - -div[searchRegionPopover] -{ - height:2em; -} - -input[type="text"] -{ - border-radius:0px; - height:2.6em; - width:20em; - border:none; -} - -:host-context([darktheme="false"]) input[type="text"] -{ - background-color:rgba(245,245,245,0.85); - box-shadow: inset 0 4px 6px 0 rgba(5,5,5,0.1); -} - -:host-context([darktheme="true"]) input[type="text"] -{ - background-color:rgba(30,30,30,0.85); - box-shadow: inset 0 4px 6px 0 rgba(0,0,0,0.2); - color:rgba(255,255,255,0.8); -} - i { font-size: 150%; diff --git a/src/ui/banner/banner.template.html b/src/ui/banner/banner.template.html index 92118772e11050a036c37a08081e8efaf12624bf..537d21a879a2e0b3b45eb0fc1aa35532f8447c6d 100644 --- a/src/ui/banner/banner.template.html +++ b/src/ui/banner/banner.template.html @@ -10,7 +10,7 @@ *ngIf="citationExists(selectedTemplate) && !isMobile" [toastLength]="toastDuration" [showToast]="citation" - class="glyphicon glyphicon-info-sign" + class="fas fa-info-sign" #templateCitationAnchor> <ng-template #citation> @@ -36,7 +36,7 @@ *ngIf="citationExists(selectedParcellation) && !isMobile" [toastLength]="toastDuration" [showToast]="citation" - class="glyphicon glyphicon-info-sign" + class="fas fa-info-sign" #parcellationCitationAnchor> <ng-template #citation> <h4> @@ -87,15 +87,15 @@ <!-- help btn --> <div *ngIf="isMobile" class="help-container"> - <i (click)="showHelp()" class="glyphicon glyphicon-question-sign"></i> + <i (click)="showHelp()" class="fas fa-question-circle"></i> </div> -<i *ngIf="!isMobile" (click)="showHelp()" class="glyphicon glyphicon-question-sign"></i> +<i *ngIf="!isMobile" (click)="showHelp()" class="fas fa-question-circle"></i> <!-- config btn --> <div *ngIf="isMobile" class="help-container"> - <i (click)="showConfig()" class="glyphicon glyphicon-cog"></i> + <i (click)="showConfig()" class="fas fa-cog"></i> </div> -<i *ngIf="!isMobile" (click)="showConfig()" class="glyphicon glyphicon-cog"></i> +<i *ngIf="!isMobile" (click)="showConfig()" class="fas fa-cog"></i> <!-- login btn --> <div *ngIf="isMobile" class="help-container"> @@ -105,8 +105,8 @@ [outsideClick]="true" [popover]="loginPopover" [popoverTitle]="user ? 'Hi, ' + user.name : 'Login'" - [ngClass]="user ? 'glyphicon-user' : 'glyphicon-log-in'" - class="glyphicon" + [ngClass]="user ? 'fa-user' : 'fa-sign-in-alt'" + class="fas" containerClass="popoverContainer"></i> </div> <i @@ -116,8 +116,8 @@ [outsideClick]="true" [popover]="loginPopover" [popoverTitle]="user ? 'Hi, ' + user.name : 'Login with'" - [ngClass]="user ? 'glyphicon-user' : 'glyphicon-log-in'" - class="glyphicon" + [ngClass]="user ? 'fa-user' : 'fa-sign-in-alt'" + class="fas" containerClass="popoverContainer"></i> <ng-template #loginPopover> @@ -126,7 +126,7 @@ class="btn btn-sm btn-default" [href]="logoutHref" *ngIf="user"> - <i class="glyphicon glyphicon-log-out"></i> + <i class="fas fa-log-out"></i> Logout </a> <div diff --git a/src/ui/config/config.template.html b/src/ui/config/config.template.html index ab92e4430415a1a7adf3165c8965a1cf927e0194..18f84d0b297f9ef71422960695f032c567faafb1 100644 --- a/src/ui/config/config.template.html +++ b/src/ui/config/config.template.html @@ -1,6 +1,8 @@ <div class="input-group"> - <span class="input-group-addon"> - GPU Limit + <span class="input-group-prepend"> + <span class="input-group-text"> + GPU Limit + </span> </span> <input (wheel)="wheelEvent($event)" @@ -11,18 +13,20 @@ class="form-control" (input)="keydown$.next($event)" [value]="gpuLimit$ | async "> - <div class="input-group-btn"> - <div (click)="setGpuPreset({ value: 100 })" class="btn btn-default"> - 100 + + <div class="input-group-append"> + + <div (click)="setGpuPreset({ value: 100 })" class="btn btn-outline-secondary"> + 100 + </div> + <div (click)="setGpuPreset({ value: 500 })" class="btn btn-outline-secondary"> + 500 + </div> + <div (click)="setGpuPreset({ value: 1000 })" class="btn btn-outline-secondary"> + 1000 + </div> + <span class="input-group-text"> + MB + </span> </div> - <div (click)="setGpuPreset({ value: 500 })" class="btn btn-default"> - 500 - </div> - <div (click)="setGpuPreset({ value: 1000 })" class="btn btn-default"> - 1000 - </div> - </div> - <span class="input-group-addon"> - MB - </span> -</div> \ No newline at end of file + </div> \ No newline at end of file diff --git a/src/ui/databrowser/databrowser.component.ts b/src/ui/databrowser/databrowser.component.ts deleted file mode 100644 index a06d6456d505f4bc586221db66e1caaff07f30f0..0000000000000000000000000000000000000000 --- a/src/ui/databrowser/databrowser.component.ts +++ /dev/null @@ -1,340 +0,0 @@ -import { Component, OnDestroy, ComponentFactoryResolver, ComponentFactory, OnInit, Injector } from "@angular/core"; -import { Store, select } from "@ngrx/store"; -import { DataStateInterface, Property, safeFilter, DataEntry, File, SELECT_REGIONS, getLabelIndexMap, isDefined, SPATIAL_GOTO_PAGE, CHANGE_NAVIGATION, UPDATE_SPATIAL_DATA_VISIBLE, DESELECT_REGIONS, DESELECT_LANDMARKS, SELECT_LANDMARKS } from "../../services/stateStore.service"; -import { map, filter, distinctUntilChanged } from "rxjs/operators"; -import { HasPathProperty } from "../../util/pipes/pathToNestedChildren.pipe"; -import { Observable, Subscription, combineLatest, Subject } from "rxjs"; -import { FileViewer } from "../fileviewer/fileviewer.component"; -import { WidgetServices } from "../../atlasViewer/widgetUnit/widgetService.service"; -import { AtlasViewerConstantsServices } from "../../atlasViewer/atlasViewer.constantService.service"; - -@Component({ - selector : 'data-browser', - templateUrl : './databrowser.template.html', - styleUrls : [ - `./databrowser.style.css` - ] -}) - -export class DataBrowserUI implements OnDestroy,OnInit{ - - private fileViewerComponentFactory : ComponentFactory<FileViewer> - - hitsPerPage : number = 5 - currentPage : number = 0 - - metadataMap : Map<string,Map<string,{properties:Property}>> - dataEntries : DataEntry[] = [] - spatialDataEntries : DataEntry[] = [] - spatialPagination : number = 0 - spatialTotalNo : number = 0 - hideDataTypes : Set<string> = new Set() - - private _spatialDataVisible : boolean = false - - dedicatedViewString : string | null - - private regionsLabelIndexMap : Map<number,any> = new Map() - - public selectedRegions$ : Observable<any[]> - public selectedLandmarks$ : Observable<any[]> - public selectedPOI$ : Observable<any[]> - - private metadataMap$ : Observable<any> - public fetchedDataEntries$ : Observable<any> - private selectParcellation$ : Observable<any> - private dedicatedViewString$ : Observable<string|null> - private spatialDataEntries$ : Observable<any[]> - private spatialPagination$ : Observable<{spatialSearchPagination:number,spatialSearchTotalResults:number}> - - /** - * TODO filter types - */ - public filterApplied$: Observable<any> - private typeVisibility$: Subject<Set<String>> = new Subject() - - private subscriptions : Subscription[] = [] - - get showDataTypes(){ - const availableDatatypes = new Set(this.dataEntries - .map(de => de.formats) - .reduce((acc, item) => acc.concat(item), []) - .filter(type => !this.hideDataTypes.has(type))) - return availableDatatypes - } - - constructor( - private cfr : ComponentFactoryResolver, - private store : Store<DataStateInterface>, - private constantService : AtlasViewerConstantsServices, - private injector : Injector, - private widgetServices : WidgetServices - ){ - - this.fileViewerComponentFactory = this.cfr.resolveComponentFactory(FileViewer) - - this.selectedRegions$ = this.store.pipe( - select('viewerState'), - safeFilter('regionsSelected'), - map(state=>state.regionsSelected) - ) - - this.selectedLandmarks$ = this.store.pipe( - select('viewerState'), - safeFilter('landmarksSelected'), - map(state => state.landmarksSelected) - ) - - this.selectedPOI$ = combineLatest( - this.selectedRegions$, - this.selectedLandmarks$ - ).pipe( - map(results => [...results[0], ...results[1]]) - ) - - this.filterApplied$ = combineLatest( - this.selectedRegions$, - this.typeVisibility$ - ) - - this.metadataMap$ = this.store.pipe( - select('dataStore'), - safeFilter('fetchedMetadataMap'), - map(v=>v.fetchedMetadataMap) - ) - - this.fetchedDataEntries$ = this.store.pipe( - select('dataStore'), - safeFilter('fetchedDataEntries'), - map(v=>v.fetchedDataEntries) - ) - - this.selectParcellation$ = this.store.pipe( - select('viewerState'), - safeFilter('parcellationSelected'), - map(state=>state.parcellationSelected), - distinctUntilChanged((p1,p2)=>p1.name === p2.name) - ) - - - this.dedicatedViewString$ = this.store.pipe( - select('viewerState'), - filter(state=> typeof state !== 'undefined' && state !== null), - map(state=>state.dedicatedView) - ) - - this.spatialDataEntries$ = this.store.pipe( - select('dataStore'), - safeFilter('fetchedSpatialData'), - map(state=>state.fetchedSpatialData) - ) - - this.spatialPagination$ = store.pipe( - select('spatialSearchState'), - filter(state=> isDefined(state) && - isDefined(state.spatialSearchPagination) && - isDefined(state.spatialSearchTotalResults) - ), - distinctUntilChanged((s1,s2)=> - s1.spatialSearchPagination === s2.spatialSearchPagination && - s1.spatialSearchTotalResults === s2.spatialSearchTotalResults && - s1.spatialDataVisible === s2.spatialDataVisible), - ) - } - - ngOnInit(){ - - this.subscriptions.push(this.metadataMap$.subscribe(map=>(this.metadataMap = map))) - - this.subscriptions.push(this.fetchedDataEntries$.subscribe(arr=>{ - console.log('arr', arr) - this.dataEntries = arr - })) - - this.subscriptions.push(this.selectParcellation$.subscribe(parcellation=> - this.handleParcellationSelection(parcellation.regions))) - - this.subscriptions.push( - this.dedicatedViewString$.subscribe(dvs=>this.dedicatedViewString = dvs)) - - this.subscriptions.push( - this.spatialDataEntries$.subscribe(this.handleSpatialDataEntries.bind(this))) - - this.subscriptions.push( - this.spatialPagination$.subscribe(this.handleSpatialPaginationChange.bind(this)) - ) - - this.subscriptions.push( - this.filterApplied$.subscribe(() => this.currentPage = 0) - ) - } - - ngOnDestroy(){ - this.subscriptions.forEach(s=>s.unsubscribe()) - } - - /* spatial search functionalities */ - - spatialPaginationChange(pagenum:number){ - this.store.dispatch({ - type : SPATIAL_GOTO_PAGE, - pageNo : pagenum - }) - } - - get spatialPaginationHitsPerPage(){ - return this.constantService.spatialResultsPerPage - } - - deselectRegion(region:any){ - this.store.dispatch({ - type: DESELECT_REGIONS, - deselectRegions: [region] - }) - } - - toggleSpatialDataVisible(){ - this.store.dispatch({ - type : UPDATE_SPATIAL_DATA_VISIBLE, - visible : !this._spatialDataVisible - }) - } - - get spatialDataVisible(){ - return this._spatialDataVisible - } - - // TODO deprecated? rethink how to implement displaying of spatial landmarks - handleSpatialPaginationChange(state){ - // if(isDefined (state.spatialSearchPagination) ) - // this.spatialPagination = state.spatialSearchPagination - - if(isDefined(state.spatialSearchTotalResults)) - this.spatialTotalNo = state.spatialSearchTotalResults - - if(isDefined(state.spatialDataVisible)) - this._spatialDataVisible = state.spatialDataVisible - - // if(this._spatialDataVisible === false) - // return - - // if(this.spatialPagination === this.spatialSearchObj.pageNo) - // return - - // console.log('pagination change') - // this.spatialSearchObj.pageNo = this.spatialPagination - // this.atlasviewerDataService.spatialSearch(this.spatialSearchObj) - } - - handleSpatialDataEntries(datas){ - this.spatialDataEntries = datas - } - - /* non-spatial data functionalities */ - - regions : any[] = [] - - handleParcellationSelection(regions:any[]){ - this.regionsLabelIndexMap = getLabelIndexMap(regions) - this.regions = Array.from(this.regionsLabelIndexMap.values()) - } - - paginationChange(pageNum:number){ - this.currentPage = pageNum - } - - serchResultFilesIsArray(files:any){ - return typeof files !== 'undefined' && - files !== null && - files.constructor === Array - } - - renderNode(file:HasPathProperty&File){ - return `${file.name ? file.name : file.path}` - } - - dataWindowRegistry: Set<string> = new Set() - - handleFlatTreeNodeClick(payload:{dataset:DataEntry, file:File}){ - const { dataset, file } = payload - if(dataset.formats.findIndex(format => format.toLowerCase() === 'nifti' ) >= 0){ - - // TODO use KG id in future - if(this.dataWindowRegistry.has(file.name)){ - /* already open, will not open again */ - return - } - /* not yet open, add the name to registry */ - this.dataWindowRegistry.add(file.name) - - const component = this.fileViewerComponentFactory.create(this.injector) - component.instance.searchResultFile = file - const compref = this.widgetServices.addNewWidget(component,{title:file.name,exitable:true,state:'floating'}) - - /* on destroy, removes name from registry */ - compref.onDestroy(() => this.dataWindowRegistry.delete(file.name)) - }else{ - /** no mime type */ - } - } - - clearAllPOIs(){ - this.store.dispatch({ - type : SELECT_REGIONS, - selectRegions : [] - }) - this.store.dispatch({ - type : SELECT_LANDMARKS, - landmarks : [] - }) - } - - typeVisible(type:string){ - return !this.hideDataTypes.has(type) - } - - toggleTypeVisibility(type:string){ - this.hideDataTypes.has(type) ? - this.hideDataTypes.delete(type) : - this.hideDataTypes.add(type) - - /* Somehow necessary, or else Angular will not mark for change */ - this.hideDataTypes = new Set( - [...this.hideDataTypes] - ) - - this.typeVisibility$.next(new Set([...this.hideDataTypes])) - } - - gothere(event:MouseEvent,position:any){ - event.stopPropagation() - event.preventDefault() - - this.store.dispatch({ - type : CHANGE_NAVIGATION, - navigation : { - position : position, - animation : { - - } - } - }) - } - - removePOI(event:MouseEvent, region:any){ - event.stopPropagation() - event.preventDefault() - - if(region.spatialLandmark){ - this.store.dispatch({ - type : DESELECT_LANDMARKS, - deselectLandmarks : [region] - }) - }else{ - this.store.dispatch({ - type : DESELECT_REGIONS, - deselectRegions : [region] - }) - } - } -} diff --git a/src/ui/databrowser/databrowser.template.html b/src/ui/databrowser/databrowser.template.html deleted file mode 100644 index c03af707c259dd6d9d83ac971355e334422384ea..0000000000000000000000000000000000000000 --- a/src/ui/databrowser/databrowser.template.html +++ /dev/null @@ -1,156 +0,0 @@ -<!-- Filter --> -<div filterBlock> - <div> - Filter {{ dataEntries.length }} - </div> - <div - *ngFor = "let type of dataEntries | getPropMapPipe : 'formats' | flatmapArrayPipe | getUniquePipe " - class = "clickable" - (click)="toggleTypeVisibility(type)"> - <small> - <i - *ngIf = "typeVisible(type)" - class = "glyphicon glyphicon-check"> - </i> - <i - *ngIf = "!typeVisible(type)" - class = "glyphicon glyphicon-unchecked"> - </i> - </small> - <small> - {{ type }} - </small> - </div> - <div - class = "clickable" - (click) = "toggleSpatialDataVisible()"> - <small> - <i *ngIf = "spatialDataVisible" class = "glyphicon glyphicon-check"></i> - <i *ngIf = "!spatialDataVisible" class = "glyphicon glyphicon-unchecked"></i> - </small> - <small> - Spatial Search - </small> - </div> - - <pill-component - [containerStyle]="{backgroundColor:'rgba(128,128,128,0.2)'}" - [closeBtnStyle]="{backgroundColor:'rgba(128,128,128,0.5)'}" - (closeClicked)="deselectRegion(region)" - [title]="region.name" - *ngFor="let region of selectedRegions$ | async"> - </pill-component> -</div> - -<!-- Data --> - -<!-- spatial search --> -<div *ngIf = "false && spatialDataVisible"> - <panel-component [collapseBody] = "true" [bodyCollapsable] = "true"> - <div heading> - <span> - Spatial Search Data - </span> - <span class = "mute-text"> - ({{ spatialTotalNo }}) - </span> - </div> - <div body> - <div *ngFor = "let data of spatialDataEntries" spatialSearchCell> - <div *ngIf = "data.name"> - {{ data.name }} - </div> - <div *ngIf = "data.properties"> - {{ data.properties.description }} - </div> - </div> - - <pagination-component - *ngIf = "spatialDataEntries.length > 0" - [hitsPerPage]="spatialPaginationHitsPerPage" - [total] = "spatialTotalNo" - [currentPage]="spatialPagination" - (paginationChange)="spatialPaginationChange($event)"> - - </pagination-component> - </div> - </panel-component> -</div> - -<!-- other data --> -<pagination-component - (paginationChange)="currentPage = $event" - [hitsPerPage]="hitsPerPage" - [total]="(fetchedDataEntries$ | async | filterDataEntriesByType : showDataTypes | filterDataEntriesByRegion : (selectedRegions$ | async)).length" - [currentPage]="currentPage"> -</pagination-component> -<dataset-viewer - *ngFor="let dataset of fetchedDataEntries$ | async | filterDataEntriesByType : showDataTypes | filterDataEntriesByRegion : (selectedRegions$ | async) | searchResultPagination : currentPage : hitsPerPage" - (launchFileViewer) = "handleFlatTreeNodeClick($event)" - [dataset] = "dataset"> - -</dataset-viewer> -<div - *ngIf = "false && (selectedPOI$ | async) && (selectedPOI$ | async).length > 0" - regionsContainer> - - <div - *ngFor = "let data of (selectedRegions$ | async | sortDataEntriesToRegion : dataEntries).concat( selectedLandmarks$ | async | spatialLandmarksToDataBrowserItemPipe ) | searchResultPagination : currentPage : hitsPerPage " - regionContainer> - <panel-component - [collapseBody] = "true" - [bodyCollapsable] = "true"> - <div [ngClass] = "(data.searchResults | filterDataEntriesByType : showDataTypes).length === 0 ? 'noResultClass' : ''" - displayflex - heading> - <div maintext> - <span> - {{ data.region ? data.region.name : 'Not associated with any region' }} - </span> - <span class = "mute-text"> - ({{ (data.searchResults | filterDataEntriesByType : hideDataTypes).length }}) - </span> - </div> - <div propertyicons> - <i *ngFor = "let poi of data.region.POIs" - (click) = "gothere($event,poi)" - class = "glyphicon glyphicon-screenshot" > - </i> - <i *ngIf = "data.region && data.region.position" - (click) = "gothere($event,data.region.position)" - class = "glyphicon glyphicon-screenshot" > - </i> - <i (click) = "removePOI($event,data.region)" - class = "glyphicon glyphicon-remove-sign"> - </i> - </div> - </div> - <div body> - - <dataset-viewer - (launchFileViewer) = "handleFlatTreeNodeClick($event)" - [dataset] = "searchResult" - *ngFor = "let searchResult of data.searchResults"> - - </dataset-viewer> - </div> - </panel-component> - </div> - - <!-- pagination --> - <pagination-component - *ngIf = "(selectedPOI$ | async).length > 0 && (selectedPOI$ | async).length > hitsPerPage" - [hitsPerPage]="hitsPerPage" - [total] = "(selectedPOI$ | async).length" - [currentPage]="currentPage" - (paginationChange)="paginationChange($event)"> - - </pagination-component> - -</div> - -<ng-template #noSelectedRegion> - <div noSelectedRegion> - Select a region / spatial landmark to get started - </div> -</ng-template> diff --git a/src/util/pipes/copyProperty.pipe.spec.ts b/src/ui/databrowserModule/copyProperty.pipe.spec.ts similarity index 100% rename from src/util/pipes/copyProperty.pipe.spec.ts rename to src/ui/databrowserModule/copyProperty.pipe.spec.ts diff --git a/src/util/pipes/copyProperty.pipe.ts b/src/ui/databrowserModule/copyProperty.pipe.ts similarity index 100% rename from src/util/pipes/copyProperty.pipe.ts rename to src/ui/databrowserModule/copyProperty.pipe.ts diff --git a/src/ui/databrowserModule/databrowser.module.ts b/src/ui/databrowserModule/databrowser.module.ts new file mode 100644 index 0000000000000000000000000000000000000000..4f10475c54a3d075875a078d279e20d0ba793924 --- /dev/null +++ b/src/ui/databrowserModule/databrowser.module.ts @@ -0,0 +1,53 @@ +import { NgModule } from "@angular/core"; +import { CommonModule } from "@angular/common"; +import { DataBrowser } from "./databrowser/databrowser.component"; +import { DatasetViewerComponent } from "./datasetViewer/datasetViewer.component"; +import { ComponentsModule } from "src/components/components.module"; +import { ModalityPicker } from "./modalityPicker/modalityPicker.component"; +import { RegionHierarchy } from "./regionHierachy/regionHierarchy.component"; +import { FilterNameBySearch } from "./filterNameBySearch.pipe"; +import { FormsModule } from "@angular/forms"; +import { DatabrowserService } from "./databrowser.service"; +import { PathToNestedChildren } from "./pathToNestedChildren.pipe"; +import { CopyPropertyPipe } from "./copyProperty.pipe"; +import { FilterDataEntriesbyMethods } from "./filterDataEntriesByMethods.pipe"; +import { FilterDataEntriesByRegion } from "./filterDataEntriesByRegion.pipe"; + +@NgModule({ + imports:[ + CommonModule, + ComponentsModule, + FormsModule + ], + declarations: [ + DataBrowser, + DatasetViewerComponent, + ModalityPicker, + RegionHierarchy, + + /** + * pipes + */ + FilterNameBySearch, + PathToNestedChildren, + CopyPropertyPipe, + FilterDataEntriesbyMethods, + FilterDataEntriesByRegion + ], + exports:[ + DataBrowser + ], + entryComponents:[ + DataBrowser + ], + providers:[ + DatabrowserService + ], + /** + * shouldn't need bootstrap, so no need for browser module + */ +}) + +export class DatabrowserModule{ + +} \ No newline at end of file diff --git a/src/ui/databrowserModule/databrowser.service.ts b/src/ui/databrowserModule/databrowser.service.ts new file mode 100644 index 0000000000000000000000000000000000000000..f30fd3bfc557277101e499b66c934bdfcf3645e3 --- /dev/null +++ b/src/ui/databrowserModule/databrowser.service.ts @@ -0,0 +1,162 @@ +import { Injectable, ComponentRef, OnDestroy } from "@angular/core"; +import { Store, select } from "@ngrx/store"; +import { ViewerConfiguration } from "src/services/state/viewerConfig.store"; +import { SELECT_REGIONS, extractLabelIdx, CHANGE_NAVIGATION, DataEntry, File, safeFilter, isDefined, getLabelIndexMap, FETCHED_DATAENTRIES } from "src/services/stateStore.service"; +import { WidgetServices } from "src/atlasViewer/widgetUnit/widgetService.service"; +import { map, distinctUntilChanged, filter, debounceTime } from "rxjs/operators"; +import { Subscription, combineLatest, Observable } from "rxjs"; +import { AtlasViewerConstantsServices } from "src/atlasViewer/atlasViewer.constantService.service"; + +export function temporaryFilterDataentryName(name: string):string{ + return /autoradiography/.test(name) + ? 'autoradiography' + : name +} + +@Injectable() +export class DatabrowserService implements OnDestroy{ + + private subscriptions: Subscription[] = [] + + public selectedParcellation: any + public selectedRegions: any[] = [] + public regionsLabelIndexMap: Map<number, any> = new Map() + + public fetchedDataEntries$: Observable<DataEntry[]> + + constructor( + private constantService: AtlasViewerConstantsServices, + private store: Store<ViewerConfiguration>, + private widgetService: WidgetServices + ){ + /** + * This service is provided on init. Angular does not provide + * lazy loading of module unless for routing + */ + this.subscriptions.push( + this.store.pipe( + select('viewerState'), + safeFilter('parcellationSelected'), + map(state => state.parcellationSelected), + distinctUntilChanged() + ).subscribe(p => { + this.selectedParcellation = p + this.regionsLabelIndexMap = getLabelIndexMap(this.selectedParcellation.regions) + }) + ) + + this.fetchedDataEntries$ = store.pipe( + select('dataStore'), + safeFilter('fetchedDataEntries'), + map(v => v.fetchedDataEntries) + ) + + this.subscriptions.push( + this.store.pipe( + select('viewerState'), + filter(state => isDefined(state) && isDefined(state.regionsSelected)), + map(state => state.regionsSelected) + ).subscribe(r => this.selectedRegions = r) + ) + + this.subscriptions.push( + combineLatest( + this.store.pipe( + select('viewerState'), + safeFilter('templateSelected'), + map(({templateSelected})=>(templateSelected.name)), + distinctUntilChanged() + ), + this.store.pipe( + select('viewerState'), + safeFilter('parcellationSelected'), + map(({parcellationSelected})=>(parcellationSelected.name)), + distinctUntilChanged() + ) + ).pipe( + debounceTime(16) + ).subscribe((param : [string, string] ) => this.fetchData(param[0], param[1])) + ) + } + + ngOnDestroy(){ + console.log('destory') + this.subscriptions.forEach(s => s.unsubscribe()) + } + + public updateRegionSelection(regions: any[]) { + this.store.dispatch({ + type: SELECT_REGIONS, + selectRegions: regions + }) + } + + public singleClickRegion(region) { + const selectedSet = new Set(extractLabelIdx(region)) + const intersection = new Set([...this.selectedRegions.map(r => r.labelIndex)].filter(v => selectedSet.has(v))) + this.store.dispatch({ + type: SELECT_REGIONS, + selectRegions: intersection.size > 0 + ? this.selectedRegions.filter(v => !intersection.has(v.labelIndex)) + : this.selectedRegions.concat([...selectedSet].map(idx => this.regionsLabelIndexMap.get(idx))) + }) + } + + public doubleClickRegion(region) { + if (!region.POIs && region.position) + return + + const newPos = region.position || region.POIs && region.POIs.constructor === Array && region.POIs[0] + + this.store.dispatch({ + type: CHANGE_NAVIGATION, + navigation: { + position: newPos, + animation: { + /* empty object is enough to be truthy */ + } + }, + }) + } + + public attachFileViewer(comp:ComponentRef<any>, file:File) { + return this.widgetService.addNewWidget(comp, { + title: file.name, + exitable: true, + state: 'floating' + }) + } + + private dispatchData(arr:DataEntry[][]){ + this.store.dispatch({ + type : FETCHED_DATAENTRIES, + fetchedDataEntries : arr.reduce((acc,curr)=>acc.concat(curr),[]) + }) + } + + private fetchData(templateName: string, parcellationName: string){ + this.dispatchData([]) + const encodedTemplateName = encodeURI(templateName) + const encodedParcellationName = encodeURI(parcellationName) + /** + * TODO instead of using promise.all, use stepwise fetching and + * dispatching of dataentries + */ + Promise.all([ + fetch(`${this.constantService.backendUrl}datasets/templateName/${encodedTemplateName}`) + .then(res => res.json()), + fetch(`${this.constantService.backendUrl}datasets/parcellationName/${encodedParcellationName}`) + .then(res => res.json()) + ]) + .then(arr => [...arr[0], ...arr[1]]) + .then(arr => arr.reduce((acc, item) => { + const newMap = new Map(acc) + return newMap.set(item.name, item) + }, new Map())) + .then(map => Array.from(map.values())) + .then(this.dispatchData.bind(this)) + .catch(console.warn) + } + + public temporaryFilterDataentryName = temporaryFilterDataentryName +} \ No newline at end of file diff --git a/src/ui/databrowserModule/databrowser/databrowser.component.ts b/src/ui/databrowserModule/databrowser/databrowser.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..bff1a0f505b3950c5de23b56037262380ab909cb --- /dev/null +++ b/src/ui/databrowserModule/databrowser/databrowser.component.ts @@ -0,0 +1,113 @@ +import { Component, OnDestroy, OnInit, Input, Injector, ViewChild } from "@angular/core"; +import { DataEntry, File } from "src/services/stateStore.service"; +import { Subscription } from "rxjs"; +import { ComponentFactory, ComponentRef } from "@angular/core/src/render3"; +import { FileViewer } from "src/ui/fileviewer/fileviewer.component"; +import { DatabrowserService } from "../databrowser.service"; +import { ModalityPicker } from "../modalityPicker/modalityPicker.component"; + +@Component({ + selector : 'data-browser', + templateUrl : './databrowser.template.html', + styleUrls : [ + `./databrowser.style.css` + ] +}) + +export class DataBrowser implements OnDestroy,OnInit{ + + public currentPage: number = 0 + public hitsPerPage: number = 5 + + public dataEntries: DataEntry[] = [] + + get selectedRegions(){ + return this.dbService.selectedRegions + } + + /** + * TODO + * viewport + * user defined filter + * etc + */ + public gemoetryFilter: any + + constructor( + private injector: Injector, + private dbService: DatabrowserService + ){ + + } + + /** + * TODO filter types + */ + public modalityFilter: string[] = [] + private subscriptions : Subscription[] = [] + + @ViewChild(ModalityPicker) + modalityPicker: ModalityPicker + + ngOnInit(){ + + /** + * TODO fix + */ + // this.subscriptions.push( + // this.filterApplied$.subscribe(() => this.currentPage = 0) + // ) + } + + ngOnDestroy(){ + this.subscriptions.forEach(s=>s.unsubscribe()) + } + + deselectRegion(region:any){ + /** + * when user clicks x on region selector + */ + + this.dbService.updateRegionSelection( + this.selectedRegions.filter(r => r.name !== region.name) + ) + } + + uncheckModality(modality:string){ + this.modalityPicker.toggleModality({name: modality}) + } + + /** + * TODO + * work around for now. + * service does not have injector, and thus cannot create componentt + */ + private fileViewerComponentFactory: ComponentFactory<FileViewer> + private dataWindowRegistry: Set<string> = new Set() + launchFile({dataset, file}:{dataset:DataEntry, file:File}){ + + if(dataset.formats.findIndex(format => format.toLowerCase() === 'nifti' ) >= 0){ + + // TODO use KG id in future + if(this.dataWindowRegistry.has(file.name)){ + /* already open, will not open again */ + return + } + /* not yet open, add the name to registry */ + this.dataWindowRegistry.add(file.name) + + const component = this.fileViewerComponentFactory.create(this.injector) + component.instance.searchResultFile = file + // const compref = this.dbService.attachFileViewer(component, file) + + /* on destroy, removes name from registry */ + // compref.onDestroy(() => this.dataWindowRegistry.delete(file.name)) + }else{ + /** no mime type */ + } + } +} + +export interface DataEntryFilter{ + filter: (dataentries:DataEntry[]) => DataEntry[] +} diff --git a/src/ui/databrowser/databrowser.style.css b/src/ui/databrowserModule/databrowser/databrowser.style.css similarity index 86% rename from src/ui/databrowser/databrowser.style.css rename to src/ui/databrowserModule/databrowser/databrowser.style.css index 64e569802b2976ce09edb48878e094b9eaa4cbc3..38659ec582170eaab13cb4b61abf9a8eae33b980 100644 --- a/src/ui/databrowser/databrowser.style.css +++ b/src/ui/databrowserModule/databrowser/databrowser.style.css @@ -63,7 +63,7 @@ div[databrowserheader] white-space: nowrap; } -div[filterBlock] +:host > div { padding : 0.5em 1em; } @@ -74,7 +74,7 @@ div[filterBlock] > * overflow: hidden; } -div.clickable:hover +.clickable:hover { color:#dbb556; cursor:default; @@ -119,8 +119,7 @@ div[noSelectedRegion] dataset-viewer { display: block; - margin: 1em; - padding: 1em; + padding: 0.3em 1em; background-color: rgba(128, 128, 128, 0.2); } @@ -133,4 +132,23 @@ div[regionsContainer] div[regionContainer] { margin-top:0.5em; +} + +modality-picker +{ + font-size: 90%; + display:inline-block; +} + +.filterWrapper +{ + white-space:normal; + max-height: 15em; + overflow-y:auto; + overflow-x:hidden; +} + +region-hierarchy +{ + display:inline-block; } \ No newline at end of file diff --git a/src/ui/databrowserModule/databrowser/databrowser.template.html b/src/ui/databrowserModule/databrowser/databrowser.template.html new file mode 100644 index 0000000000000000000000000000000000000000..6a5695ac99fa126f6b35556f4e60f7b369fd88d4 --- /dev/null +++ b/src/ui/databrowserModule/databrowser/databrowser.template.html @@ -0,0 +1,75 @@ + +<!-- modality picker --> +<div> + <i (click)="modalityReadmore.show = !modalityReadmore.show" class="clickable"> + Filter by Modality <small *ngIf="modalityFilter.length > 0" class="text-muted">({{ modalityFilter.length }})</small> + </i> + <readmore-component + #modalityReadmore + [collapsedHeight]="0"> + <div class="filterWrapper"> + <modality-picker (modalityFilterEmitter)="modalityFilter = $event"> + + </modality-picker> + </div> + </readmore-component> + <div *ngIf="!modalityReadmore.show"> + <pill-component + [containerStyle]="{backgroundColor:'rgba(128,128,128,0.2)'}" + [closeBtnStyle]="{backgroundColor:'rgba(128,128,128,0.5)'}" + (closeClicked)="uncheckModality(modality)" + [title]="modality" + *ngFor="let modality of modalityFilter"> + + </pill-component> + </div> +</div> + +<!-- region hierarchy --> +<div> + <i (click)="regionHierarchy.focusInput($event)" class="clickable"> + Filter by parcellation region <small *ngIf="selectedRegions.length > 0" class="text-muted">({{ selectedRegions.length }})</small> + </i> + <region-hierarchy #regionHierarchy> + </region-hierarchy> + <readmore-component + [hidden]="selectedRegions.length === 0" + #selectedRegionReadmore + [collapsedHeight]="45"> + <div class="filterWrapper"> + <pill-component + [containerStyle]="{backgroundColor:'rgba(128,128,128,0.2)'}" + [closeBtnStyle]="{backgroundColor:'rgba(128,128,128,0.5)'}" + (closeClicked)="deselectRegion(region)" + [title]="region.name" + *ngFor="let region of selectedRegions"> + </pill-component> + </div> + </readmore-component> +</div> + +<!-- Data Entries --> +<div> + <i *ngIf="dbService.fetchedDataEntries$ | async"> + {{ (dbService.fetchedDataEntries$ | async).length }} total results. <span *ngIf="selectedRegions.length + modalityFilter.length > 0 "> {{ (dbService.fetchedDataEntries$ | async | filterDataEntriesByMethods : modalityFilter | filterDataEntriesByRegion : selectedRegions).length }} filtered results.</span> + </i> + <i *ngIf="!(dbService.fetchedDataEntries$ | async)"> + No results to show. + </i> +</div> +<div *ngIf="dbService.fetchedDataEntries$ | async"> + <dataset-viewer + class="mt-1" + *ngFor="let dataset of dbService.fetchedDataEntries$ | async | filterDataEntriesByMethods : modalityFilter | filterDataEntriesByRegion : selectedRegions | searchResultPagination : currentPage : hitsPerPage" + (launchFileViewer) = "launchFile($event)" + [dataset] = "dataset"> + </dataset-viewer> +</div> + +<pagination-component + *ngIf="dbService.fetchedDataEntries$ | async" + (paginationChange)="currentPage = $event" + [hitsPerPage]="hitsPerPage" + [total]="(dbService.fetchedDataEntries$ | async | filterDataEntriesByMethods : modalityFilter | filterDataEntriesByRegion : selectedRegions).length" + [currentPage]="currentPage"> +</pagination-component> \ No newline at end of file diff --git a/src/ui/datasetViewer/datasetViewer.component.ts b/src/ui/databrowserModule/datasetViewer/datasetViewer.component.ts similarity index 71% rename from src/ui/datasetViewer/datasetViewer.component.ts rename to src/ui/databrowserModule/datasetViewer/datasetViewer.component.ts index cbd5b087aa54d053da76d65fe553a711d92f80d5..4ad17eded8a6ca7422228ad3d0ace7556b959ac1 100644 --- a/src/ui/datasetViewer/datasetViewer.component.ts +++ b/src/ui/databrowserModule/datasetViewer/datasetViewer.component.ts @@ -1,5 +1,5 @@ import { Component, Input, Output, EventEmitter } from "@angular/core"; -import { DataEntry } from "../../services/stateStore.service"; +import { DataEntry } from "src/services/stateStore.service"; @Component({ selector : 'dataset-viewer', @@ -31,4 +31,14 @@ export class DatasetViewerComponent{ ? obj.name : obj.path } + + get methods(): string[]{ + return this.dataset.activity.reduce((acc, act) => { + return acc.concat(act.methods) + }, []) + } + + get kgReference(): string[] { + return this.dataset.kgReference.map(ref => `https://doi.org/${ref}`) + } } \ No newline at end of file diff --git a/src/ui/datasetViewer/datasetViewer.style.css b/src/ui/databrowserModule/datasetViewer/datasetViewer.style.css similarity index 100% rename from src/ui/datasetViewer/datasetViewer.style.css rename to src/ui/databrowserModule/datasetViewer/datasetViewer.style.css diff --git a/src/ui/databrowserModule/datasetViewer/datasetViewer.template.html b/src/ui/databrowserModule/datasetViewer/datasetViewer.template.html new file mode 100644 index 0000000000000000000000000000000000000000..1dcfe9ebb59ab17670aae44994fdf8ea7423e762 --- /dev/null +++ b/src/ui/databrowserModule/datasetViewer/datasetViewer.template.html @@ -0,0 +1,38 @@ +<div *ngIf = "dataset; else defaultDisplay"> + + <div title> + {{ dataset.name }} + </div> + + <a + *ngFor="let kgr of kgReference" + target="_blank" + [href]="kgr"> + open in knowledge graph + </a> + <!-- <kg-entry-viewer [dataset]="dataset"> + + </kg-entry-viewer> --> + + <div *ngIf="false"> + + <hr /> + <h5> + Preview Dataset + </h5> + <flat-tree-component + #flatTreeNode + *ngFor = "let item of dataset.files | copyProperty : 'filename' : 'path' | pathToNestedChildren" + [childrenExpanded] = "false" + [renderNode] = "renderNode" + [inputItem] = " item " + (treeNodeClick)= "previewFileClick($event, flatTreeNode)"> + + </flat-tree-component> + <hr /> + </div> +</div> + +<ng-template #defaultDisplay> + Nothing to display ... +</ng-template> diff --git a/src/ui/databrowserModule/filterDataEntriesByMethods.pipe.ts b/src/ui/databrowserModule/filterDataEntriesByMethods.pipe.ts new file mode 100644 index 0000000000000000000000000000000000000000..7c3d190dce76779c43e643b2c862dfed15bcf68e --- /dev/null +++ b/src/ui/databrowserModule/filterDataEntriesByMethods.pipe.ts @@ -0,0 +1,17 @@ +import { PipeTransform, Pipe } from "@angular/core"; +import { DataEntry } from "../../services/stateStore.service"; +import { temporaryFilterDataentryName } from './databrowser.service' + +@Pipe({ + name : 'filterDataEntriesByMethods' +}) + +export class FilterDataEntriesbyMethods implements PipeTransform{ + public transform(dataEntries:DataEntry[],showDataMethods:string[]):DataEntry[]{ + return showDataMethods.length > 0 + ? dataEntries.filter(dataEntry => { + return dataEntry.activity.some(a => a.methods.some(m => showDataMethods.findIndex(dm => dm === temporaryFilterDataentryName(m)) >= 0)) + }) + : dataEntries + } +} \ No newline at end of file diff --git a/src/util/pipes/filterDataEntriesByRegion.pipe.ts b/src/ui/databrowserModule/filterDataEntriesByRegion.pipe.ts similarity index 100% rename from src/util/pipes/filterDataEntriesByRegion.pipe.ts rename to src/ui/databrowserModule/filterDataEntriesByRegion.pipe.ts diff --git a/src/util/pipes/filterNameBySearch.pipe.ts b/src/ui/databrowserModule/filterNameBySearch.pipe.ts similarity index 100% rename from src/util/pipes/filterNameBySearch.pipe.ts rename to src/ui/databrowserModule/filterNameBySearch.pipe.ts diff --git a/src/ui/databrowserModule/modalityPicker/modalityPicker.component.ts b/src/ui/databrowserModule/modalityPicker/modalityPicker.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..a12ad5325c15d7a8aac56581ac54778933792109 --- /dev/null +++ b/src/ui/databrowserModule/modalityPicker/modalityPicker.component.ts @@ -0,0 +1,92 @@ +import { Component, OnInit, OnDestroy, EventEmitter, Input, Output } from "@angular/core"; +import { Observable, Subscription } from "rxjs"; +import { DataEntry } from "src/services/stateStore.service"; +import { DatabrowserService } from "../databrowser.service"; + +@Component({ + selector: 'modality-picker', + templateUrl: './modalityPicker.template.html', + styleUrls: [ + './modalityPicker.style.css' + ] +}) + +export class ModalityPicker implements OnInit, OnDestroy{ + + private subscrptions: Subscription[] = [] + + public modalities$: Observable<string> + public modalityVisibility: Set<string> = new Set() + public countedDataM: CountedDataModality[] = [] + + @Output() + public modalityFilterEmitter: EventEmitter<string[]> = new EventEmitter() + + constructor( + private dbService:DatabrowserService + ){ + + } + + filter(dataentries:DataEntry[]) { + return this.modalityVisibility.size === 0 + ? dataentries + : dataentries.filter(de => de.activity.some(a => a.methods.some(m => this.modalityVisibility.has(this.dbService.temporaryFilterDataentryName(m))))) + } + + ngOnInit(){ + this.subscrptions.push( + this.dbService.fetchedDataEntries$.subscribe(de => + this.countedDataM = this.getModalityFromDE(de)) + ) + } + + ngOnDestroy(){ + this.subscrptions.forEach(s => s.unsubscribe()) + } + + toggleModality(modality: Partial<CountedDataModality>){ + const dm = this.countedDataM.find(dm => dm.name === modality.name) + if (dm) { + dm.visible = !dm.visible + } + this.modalityFilterEmitter.emit( + this.countedDataM.filter(dm => dm.visible).map(dm => dm.name) + ) + } + + reduceDataentry(accumulator:{name:string, occurance:number}[], dataentry: DataEntry) { + const methods = dataentry.activity + .map(a => a.methods) + .reduce((acc, item) => acc.concat(item), []) + .map(this.dbService.temporaryFilterDataentryName) + + const newDE = Array.from(new Set(methods)) + .filter(m => !accumulator.some(a => a.name === m)) + + return accumulator.map(({name, occurance, ...rest}) => { + return { + ...rest, + name, + occurance: methods.some(m => m === name) + ? occurance + 1 + : occurance + } + }).concat(newDE.map(name => { + return { + name, + occurance: 1 + } + })) + } + + getModalityFromDE(dataentries:DataEntry[]):CountedDataModality[] { + return dataentries.reduce((acc, de) => this.reduceDataentry(acc, de), []) + } +} + +interface CountedDataModality{ + name: string + occurance: number + visible: boolean +} \ No newline at end of file diff --git a/src/ui/databrowserModule/modalityPicker/modalityPicker.style.css b/src/ui/databrowserModule/modalityPicker/modalityPicker.style.css new file mode 100644 index 0000000000000000000000000000000000000000..4205d6e7236657f7efe9cc237da8951f5b04d1f4 --- /dev/null +++ b/src/ui/databrowserModule/modalityPicker/modalityPicker.style.css @@ -0,0 +1,10 @@ +div +{ + white-space: nowrap; +} + +.clickable:hover +{ + color:#dbb556; + cursor:default; +} \ No newline at end of file diff --git a/src/ui/databrowserModule/modalityPicker/modalityPicker.template.html b/src/ui/databrowserModule/modalityPicker/modalityPicker.template.html new file mode 100644 index 0000000000000000000000000000000000000000..f5bb0d906f4833dc8a40145466f808fbd144d612 --- /dev/null +++ b/src/ui/databrowserModule/modalityPicker/modalityPicker.template.html @@ -0,0 +1,9 @@ +<div + *ngFor="let datamodality of countedDataM" + (click)="toggleModality(datamodality)" + class="clickable"> + <i [ngClass]="datamodality.visible ? 'far fa-check-square' : 'text-muted far fa-square'"> + + </i> + {{ datamodality.name }} <span class="text-muted">({{ datamodality.occurance }})</span> +</div> \ No newline at end of file diff --git a/src/util/pipes/pathToNestedChildren.pipe.spec.ts b/src/ui/databrowserModule/pathToNestedChildren.pipe.spec.ts similarity index 100% rename from src/util/pipes/pathToNestedChildren.pipe.spec.ts rename to src/ui/databrowserModule/pathToNestedChildren.pipe.spec.ts diff --git a/src/util/pipes/pathToNestedChildren.pipe.ts b/src/ui/databrowserModule/pathToNestedChildren.pipe.ts similarity index 100% rename from src/util/pipes/pathToNestedChildren.pipe.ts rename to src/ui/databrowserModule/pathToNestedChildren.pipe.ts diff --git a/src/ui/databrowserModule/regionHierachy/regionHierarchy.component.ts b/src/ui/databrowserModule/regionHierachy/regionHierarchy.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..c6e25c5823d3d506ee3c8d9f700a5e7b4353cd65 --- /dev/null +++ b/src/ui/databrowserModule/regionHierachy/regionHierarchy.component.ts @@ -0,0 +1,157 @@ +import { Component, ElementRef, ViewChild, HostListener, OnInit, ChangeDetectionStrategy, ChangeDetectorRef, Input } from "@angular/core"; +import { Subscription, Subject } from "rxjs"; +import { buffer, debounceTime } from "rxjs/operators"; +import { FilterNameBySearch } from "../filterNameBySearch.pipe"; +import { DatabrowserService } from "../databrowser.service"; + +@Component({ + selector: 'region-hierarchy', + templateUrl: './regionHierarchy.template.html', + styleUrls: [ + './regionHierarchy.style.css' + ], + changeDetection: ChangeDetectionStrategy.OnPush +}) + +export class RegionHierarchy implements OnInit{ + + + @Input() + public selectedRegions: any[] = [] + + public showRegionTree: boolean = false + public searchTerm: string = '' + private subscriptions: Subscription[] = [] + + @ViewChild('searchRegionPopover', { read: ElementRef }) + private searchRegionPopover: ElementRef + + @ViewChild('searchTermInput', {read: ElementRef}) + private searchTermInput: ElementRef + + @HostListener('document:click', ['$event']) + closeRegion(event: MouseEvent) { + if (!this.searchRegionPopover) + return + const contains = this.searchRegionPopover.nativeElement.contains(event.target) + this.showRegionTree = contains + if (!this.showRegionTree) + this.searchTerm = '' + } + + get selectedParcellation(){ + return this.dbService.selectedParcellation + } + + get regionsLabelIndexMap() { + return this.dbService.regionsLabelIndexMap + } + + constructor( + private cdr:ChangeDetectorRef, + private dbService: DatabrowserService + ){ + } + + ngOnInit(){ + this.subscriptions.push( + this.handleRegionTreeClickSubject.pipe( + buffer( + this.handleRegionTreeClickSubject.pipe( + debounceTime(200) + ) + ) + ).subscribe(arr => arr.length > 1 ? this.doubleClick(arr[0]) : this.singleClick(arr[0])) + ) + } + + getInputPlaceholder(parcellation:any) { + if (parcellation) + return `Search region in ${parcellation.name}` + else + return `Start by selecting a template and a parcellation.` + } + + escape(event:KeyboardEvent){ + this.showRegionTree = false + this.searchTerm = ''; + (event.target as HTMLInputElement).blur() + + } + + focusInput(event?:MouseEvent){ + if (event) { + /** + * need to stop propagation, or @closeRegion will be triggered + */ + event.stopPropagation() + } + this.searchTermInput.nativeElement.focus() + this.showRegionTree = true + } + + /* NB need to bind two way data binding like this. Or else, on searchInput blur, the flat tree will be rebuilt, + resulting in first click to be ignored */ + + changeSearchTerm(event: any) { + if (event.target.value === this.searchTerm) + return + this.searchTerm = event.target.value + /** + * TODO maybe introduce debounce + */ + this.cdr.markForCheck() + } + + private handleRegionTreeClickSubject: Subject<any> = new Subject() + + handleClickRegion(obj: any) { + obj.event.stopPropagation() + this.handleRegionTreeClickSubject.next(obj) + } + + /* double click navigate to the interested area */ + private doubleClick(obj: any) { + if (!obj) + return + const { inputItem : region } = obj + if (!region) + return + this.dbService.doubleClickRegion(region) + } + + /* single click selects/deselects region(s) */ + private singleClick(obj: any) { + this.dbService.singleClickRegion(obj.inputItem) + + /** + * TODO may no longer be needed + */ + this.cdr.markForCheck() + } + + private insertHighlight(name: string, searchTerm: string): string { + const regex = new RegExp(searchTerm, 'gi') + return searchTerm === '' ? + name : + name.replace(regex, (s) => `<span class = "highlight">${s}</span>`) + } + + displayTreeNode(item: any) { + return typeof item.labelIndex !== 'undefined' && this.selectedRegions.findIndex(re => re.labelIndex === Number(item.labelIndex)) >= 0 ? + `<span class = "regionSelected">${this.insertHighlight(item.name, this.searchTerm)}</span>` : + `<span class = "regionNotSelected">${this.insertHighlight(item.name, this.searchTerm)}</span>` + } + + filterNameBySearchPipe = new FilterNameBySearch() + filterTreeBySearch(node: any): boolean { + return this.filterNameBySearchPipe.transform([node.name], this.searchTerm) + } + + get aggregatedRegionTree() { + return { + name: this.selectedParcellation.name, + children: this.selectedParcellation.regions + } + } +} \ No newline at end of file diff --git a/src/ui/databrowserModule/regionHierachy/regionHierarchy.style.css b/src/ui/databrowserModule/regionHierachy/regionHierarchy.style.css new file mode 100644 index 0000000000000000000000000000000000000000..0bda773ba8e8333f71aa969f5856ae6b39679f0f --- /dev/null +++ b/src/ui/databrowserModule/regionHierachy/regionHierarchy.style.css @@ -0,0 +1,82 @@ + +div[treeHeader] +{ + height: 55px; +} + +div[treeHeader] > span +{ + display:inline-block; + padding: 12px 0px; + line-height:2em; + border:none; +} + +div[treeHeader] > span.btn.btn-link +{ + margin-left:2em; +} + +div[treeContainer] +{ + padding:1em; + z-index: 3; + + max-height:20em; + width: calc(100% + 2em); + overflow-y:auto; + overflow-x:hidden; + + + /* color:white; + background-color:rgba(12,12,12,0.8); */ +} + +:host-context([darktheme="false"]) div[treeContainer] +{ + background-color:rgba(240,240,240,0.8); +} + +:host-context([darktheme="true"]) div[treeContainer] +{ + background-color:rgba(30,30,30,0.8); + color:rgba(255,255,255,1.0); +} + +div[hideScrollbarcontainer] +{ + width: 20em; + overflow:hidden; + margin-top:2px; +} + +div[searchRegionPopover] +{ + height:2em; +} + +input[type="text"] +{ + border-radius:0px; + height:2.6em; + width:20em; + border:none; +} + +:host-context([darktheme="false"]) input[type="text"] +{ + background-color:rgba(245,245,245,0.85); + box-shadow: inset 0 4px 6px 0 rgba(5,5,5,0.1); +} + +:host-context([darktheme="true"]) input[type="text"] +{ + background-color:rgba(30,30,30,0.85); + box-shadow: inset 0 4px 6px 0 rgba(0,0,0,0.2); + color:rgba(255,255,255,0.8); +} + +.regionSearch +{ + font-size: 80%; +} \ No newline at end of file diff --git a/src/ui/databrowserModule/regionHierachy/regionHierarchy.template.html b/src/ui/databrowserModule/regionHierachy/regionHierarchy.template.html new file mode 100644 index 0000000000000000000000000000000000000000..3e62d4cf28d6dd885c2579fdd6e2496ab219cb25 --- /dev/null +++ b/src/ui/databrowserModule/regionHierachy/regionHierarchy.template.html @@ -0,0 +1,35 @@ +<div + #searchRegionPopover + searchRegionPopover> + <input + #searchTermInput + tabindex="0" + (keydown.esc)="escape($event)" + (focus)="showRegionTree = true" + [value]="searchTerm" + (input)="changeSearchTerm($event)" + class="form-control regionSearch" + type="text" + [placeholder]="getInputPlaceholder(selectedParcellation)"/> + + <div + *ngIf="showRegionTree" + hideScrollbarContainer> + + <div treeContainer #treeContainer> + <div *ngIf="false" treeHeader> + <span>{{ selectedRegions.length }} {{ selectedRegions.length > 1 ? 'regions' : 'region' }} selected</span> + <span (click)="clearRegions($event)" *ngIf="selectedRegions.length > 0" class="btn btn-link">clear all</span> + </div> + + <flat-tree-component + [flatTreeViewPort]="treeContainer" + (treeNodeClick)="handleClickRegion($event)" + [inputItem]="aggregatedRegionTree" + [renderNode]="displayTreeNode.bind(this)" + [searchFilter]="filterTreeBySearch.bind(this)"> + + </flat-tree-component> + </div> + </div> +</div> \ No newline at end of file diff --git a/src/ui/datasetViewer/datasetViewer.template.html b/src/ui/datasetViewer/datasetViewer.template.html deleted file mode 100644 index 829c6ceee8a7725e876ba7a2906b06aa1d0b998f..0000000000000000000000000000000000000000 --- a/src/ui/datasetViewer/datasetViewer.template.html +++ /dev/null @@ -1,39 +0,0 @@ -<div *ngIf = "dataset; else defaultDisplay"> - - <pill-component - class="dataset-pill" - [title]="format" - [showClose]="false" - *ngFor="let format of dataset.formats"> - - </pill-component> - - <h4 title> - {{ dataset.name }} - </h4> - <kg-entry-viewer [dataset]="dataset"> - - </kg-entry-viewer> - - <div *ngIf="false"> - - <hr /> - <h5> - Preview Dataset - </h5> - <flat-tree-component - #flatTreeNode - *ngFor = "let item of dataset.files | copyProperty : 'filename' : 'path' | pathToNestedChildren" - [childrenExpanded] = "false" - [renderNode] = "renderNode" - [inputItem] = " item " - (treeNodeClick)= "previewFileClick($event, flatTreeNode)"> - - </flat-tree-component> - <hr /> - </div> -</div> - -<ng-template #defaultDisplay> - Nothing to display ... -</ng-template> diff --git a/src/ui/kgEntryViewer/kgentry.template.html b/src/ui/kgEntryViewer/kgentry.template.html index 5986ebf305f5708d3f0a02f4c0600f2d14eedff9..42e95d0156320d1e8e703bcd09364ea95121bc72 100644 --- a/src/ui/kgEntryViewer/kgentry.template.html +++ b/src/ui/kgEntryViewer/kgentry.template.html @@ -39,7 +39,7 @@ <div *ngFor="let publication of dataset.publications"> <a target="_blank" link [href]="'https://doi.org/' + publication.doi"> {{ publication.cite }} - <i class = "glyphicon glyphicon-new-window"></i> + <i class = "fas fa-new-window"></i> </a> </div> </div> @@ -67,7 +67,7 @@ <div *ngFor="let file of dataset.files"> <a link target="_blank" [href]="file.absolutePath"> {{ file.name }} - <i class="glyphicon glyphicon-download-alt"></i> + <i class="fas fa-download-alt"></i> </a> </div> </div> @@ -78,7 +78,7 @@ [href]="'https://doi.org/' + ref" target="_blank"> Open in Knowledge Graph - <i class = "glyphicon glyphicon-new-window"></i> + <i class = "fas fa-new-window"></i> </a> </div> \ No newline at end of file diff --git a/src/ui/layerbrowser/layerbrowser.style.css b/src/ui/layerbrowser/layerbrowser.style.css index 2babcd853d582a36bed00a1710af4bf7529bb445..d9b0b2bfbd3eab6d6de2f1f694e7326ff76b5e1a 100644 --- a/src/ui/layerbrowser/layerbrowser.style.css +++ b/src/ui/layerbrowser/layerbrowser.style.css @@ -33,12 +33,12 @@ div[body] align-items: center; } -.glyphicon.blue +.fas.blue { color: #337ab7; } -.glyphicon.red +.fas.red { color: red; } diff --git a/src/ui/layerbrowser/layerbrowser.template.html b/src/ui/layerbrowser/layerbrowser.template.html index b88d2e18b1a748c40821d5b89ee58ec590704a00..652d01cf70fcd1e456248d215b9e89896a28d720 100644 --- a/src/ui/layerbrowser/layerbrowser.template.html +++ b/src/ui/layerbrowser/layerbrowser.template.html @@ -8,8 +8,8 @@ placement = "bottom" [tooltip] = "checkLocked(ngLayer) ? 'base layer cannot be hidden' : 'toggle visibility'" (click) = "checkLocked(ngLayer) ? null : toggleVisibility(ngLayer)" - class = "glyphicon" - [ngClass] = "checkLocked(ngLayer) ? 'glyphicon-lock muted' :ngLayer.visible ? 'glyphicon-eye-open' : 'glyphicon-eye-close'"> + class = "fas" + [ngClass] = "checkLocked(ngLayer) ? 'fa-lock muted' :ngLayer.visible ? 'fa-eye-open' : 'fa-eye-close'"> </i> </div> @@ -19,9 +19,9 @@ placement = "bottom" [tooltip] = "ngLayer.type === 'segmentation' ? segmentationTooltip() : 'only segmentation layer can hide/show segments'" (click) = "forceSegment.hide();toggleForceShowSegment(ngLayer)" - class = "glyphicon" + class = "fas" #forceSegment = "bs-tooltip" - [ngClass] = "ngLayer.type === 'segmentation' ? ('glyphicon-th-large ' + segmentationAdditionalClass) : 'glyphicon-lock muted' "> + [ngClass] = "ngLayer.type === 'segmentation' ? ('fa-th-large ' + segmentationAdditionalClass) : 'fa-lock muted' "> </i> </div> @@ -31,8 +31,8 @@ container = "body" placement = "bottom" [tooltip] = "checkLocked(ngLayer) ? 'base layers cannot be removed' : 'remove layer'" - class = "glyphicon" - [ngClass] = "checkLocked(ngLayer) ? 'glyphicon-lock muted' : 'glyphicon-remove-circle'"> + class = "fas" + [ngClass] = "checkLocked(ngLayer) ? 'fa-lock muted' : 'fa-remove-circle'"> </i> </div> diff --git a/src/ui/menuicons/menuicons.component.ts b/src/ui/menuicons/menuicons.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..4d31100bf9c8720b7dae2863b72e357b9135f3c8 --- /dev/null +++ b/src/ui/menuicons/menuicons.component.ts @@ -0,0 +1,138 @@ +import { Component, ViewChild, TemplateRef, ComponentRef, Injector, ComponentFactory, ComponentFactoryResolver } from "@angular/core"; + +import { WidgetServices } from "src/atlasViewer/widgetUnit/widgetService.service"; +import { WidgetUnit } from "src/atlasViewer/widgetUnit/widgetUnit.component"; +import { LayerBrowser } from "src/ui/layerbrowser/layerbrowser.component"; +import { DataBrowser } from "src/ui/databrowserModule/databrowser/databrowser.component"; +import { FileViewer } from "../fileviewer/fileviewer.component"; + +@Component({ + selector: 'menu-icons', + templateUrl: './menuicons.template.html', + styleUrls: [ + './menuicons.style.css' + ] +}) + +export class MenuIconsBar{ + + /** + * databrowser + */ + dbcf: ComponentFactory<DataBrowser> + dataBrowser: ComponentRef<DataBrowser> = null + dbWidget: ComponentRef<WidgetUnit> = null + + /** + * file viewer + */ + fcf: ComponentFactory<FileViewer> + + /** + * layerBrowser + */ + lbcf: ComponentFactory<LayerBrowser> + layerBrowser: ComponentRef<LayerBrowser> = null + lbWidget: ComponentRef<WidgetUnit> = null + + constructor( + private widgetServices:WidgetServices, + private injector:Injector, + cfr: ComponentFactoryResolver + ){ + this.dbcf = cfr.resolveComponentFactory(DataBrowser) + this.lbcf = cfr.resolveComponentFactory(LayerBrowser) + this.fcf = cfr.resolveComponentFactory(FileViewer) + } + public clickSearch(event: MouseEvent){ + if (this.dbWidget) { + this.dbWidget.destroy() + this.dbWidget = null + return + } + this.dataBrowser = this.dbcf.create(this.injector) + this.dbWidget = this.widgetServices.addNewWidget(this.dataBrowser, { + exitable: true, + persistency: true, + state: 'floating', + title: this.dataBrowserTitle, + titleHTML: `<i class="fas fa-search"></i> ${this.dataBrowserTitle}` + }) + + + const dataWindowRegistry: Set<string> = new Set() + + // const sub = this.dataBrowser.instance.launchFile.subscribe(payload => { + // const { dataset, file } = payload + // if(dataset.formats.findIndex(format => format.toLowerCase() === 'nifti' ) >= 0){ + + // // TODO use KG id in future + // if(dataWindowRegistry.has(file.name)){ + // /* already open, will not open again */ + // return + // } + // /* not yet open, add the name to registry */ + // dataWindowRegistry.add(file.name) + + // const component = this.fcf.create(this.injector) + // component.instance.searchResultFile = file + // const compref = this.widgetServices.addNewWidget(component,{title:file.name,exitable:true,state:'floating'}) + + // /* on destroy, removes name from registry */ + // compref.onDestroy(() => dataWindowRegistry.delete(file.name)) + // }else{ + // /** no mime type */ + // } + // }) + + this.dbWidget.onDestroy(() => { + // sub.unsubscribe() + this.dataBrowser = null + this.dbWidget = null + }) + + const el = event.currentTarget as HTMLElement + const top = el.offsetTop + const left = el.offsetLeft + 50 + this.dbWidget.instance.position = [left, top] + } + + public clickLayer(event: MouseEvent){ + + if (this.lbWidget) { + this.lbWidget.destroy() + this.lbWidget = null + return + } + this.layerBrowser = this.lbcf.create(this.injector) + this.lbWidget = this.widgetServices.addNewWidget(this.layerBrowser, { + exitable: true, + persistency: true, + state: 'floating', + title: 'Layer Browser', + titleHTML: '<i class="fas fa-layer-group"></i> Layer Browser' + }) + + this.lbWidget.onDestroy(() => { + this.layerBrowser = null + this.lbWidget = null + }) + + const el = event.currentTarget as HTMLElement + const top = el.offsetTop + const left = el.offsetLeft + 50 + this.lbWidget.instance.position = [left, top] + } + + get databrowserIsShowing() { + return this.dataBrowser !== null + } + + get layerbrowserIsShowing() { + return this.layerBrowser !== null + } + + get dataBrowserTitle() { + return `Browse` + } +} \ No newline at end of file diff --git a/src/ui/menuicons/menuicons.style.css b/src/ui/menuicons/menuicons.style.css new file mode 100644 index 0000000000000000000000000000000000000000..8a1663369fecdd67c4c7c70ee73774316f49e430 --- /dev/null +++ b/src/ui/menuicons/menuicons.style.css @@ -0,0 +1,23 @@ +:host +{ + display: flex; + flex-direction: column; + align-items: flex-start; +} + +:host > * +{ + margin-top: 1em; + display:inline-block; +} + +:host >>> .tooltip-inner +{ + background-color: rgba(128, 128, 128, 0.5); +} + +:host >>> .tooltip.right .tooltip-arrow::before, +:host >>> .tooltip.right .tooltip-arrow +{ + border-right-color: rgba(128, 128, 128, 0.5); +} \ No newline at end of file diff --git a/src/ui/menuicons/menuicons.template.html b/src/ui/menuicons/menuicons.template.html new file mode 100644 index 0000000000000000000000000000000000000000..8f40b4e913de40da340d571b4e0e587afa09f110 --- /dev/null +++ b/src/ui/menuicons/menuicons.template.html @@ -0,0 +1,35 @@ +<logo-container> +</logo-container> +<div *ngIf="false" class="btn btn-sm btn-secondary rounded-circle"> + <i class="fas fa-brain"> + + </i> +</div> +<div + [tooltip]="dataBrowserTitle" + placement="right" + (click)="clickSearch($event)" + [ngClass]="databrowserIsShowing ? 'btn-primary' : 'btn-secondary'" + class="btn btn-sm rounded-circle"> + <i class="fas fa-search"> + + </i> +</div> +<div + tooltip="Layer" + placement="right" + (click)="clickLayer($event)" + [ngClass]="layerbrowserIsShowing ? 'btn-primary' : 'btn-secondary'" + class="btn btn-sm rounded-circle"> + <i class="fas fa-layer-group"> + + </i> +</div> +<div + tooltip="Plugins" + placement="right" + class="btn btn-sm btn-secondary rounded-circle"> + <i class="fas fa-tools"> + + </i> +</div> diff --git a/src/ui/nehubaContainer/landmarkUnit/landmarkUnit.component.ts b/src/ui/nehubaContainer/landmarkUnit/landmarkUnit.component.ts index 7ee2e92976f0a0ee3d40ccc235b96a0fbde81e08..77c4992fda331347cd90be8d5e8af289118a743e 100644 --- a/src/ui/nehubaContainer/landmarkUnit/landmarkUnit.component.ts +++ b/src/ui/nehubaContainer/landmarkUnit/landmarkUnit.component.ts @@ -18,13 +18,13 @@ export class LandmarkUnit implements OnChanges{ @Input() highlight : boolean = false @Input() flatProjection : boolean = false - @Input() glyphiconClass : string = 'glyphicon-map-marker' + @Input() fasClass : string = 'fa-map-marker' @HostBinding('style.transform') transform : string = `translate(${this.positionX}px, ${this.positionY}px)` get className() { - return `glyphicon ${this.glyphiconClass}` + return `fas ${this.fasClass}` } styleNode(){ return({ diff --git a/src/ui/nehubaContainer/landmarkUnit/landmarkUnit.style.css b/src/ui/nehubaContainer/landmarkUnit/landmarkUnit.style.css index 1846dd902ecf97e188ab0c165ac62056a19eae3d..6a09196fe028c971d28eb6f22ac3e87d385dafb7 100644 --- a/src/ui/nehubaContainer/landmarkUnit/landmarkUnit.style.css +++ b/src/ui/nehubaContainer/landmarkUnit/landmarkUnit.style.css @@ -79,7 +79,7 @@ div[landmarkContainer] top:0px; } -[nodeView] > .glyphicon +[nodeView] > .fas { margin-left:-0.5em; margin-top:-1em; diff --git a/src/ui/nehubaContainer/nehubaContainer.style.css b/src/ui/nehubaContainer/nehubaContainer.style.css index dcfd8e4ad7daaefdfef763c40c77f2a63bda0cc4..54f5cf5826289b6ad72cf45796d269481c174fa3 100644 --- a/src/ui/nehubaContainer/nehubaContainer.style.css +++ b/src/ui/nehubaContainer/nehubaContainer.style.css @@ -44,7 +44,8 @@ div[statusCard] background-color:rgba(230,230,230,0.8); } -:host-context([darktheme=true]) div[statusCard] +:host-context([darktheme=true]) div[statusCard], +input { background-color:rgba(20,20,20,0.8); color : rgba(250,250,250,0.8); diff --git a/src/ui/nehubaContainer/nehubaContainer.template.html b/src/ui/nehubaContainer/nehubaContainer.template.html index 3334c087fa64293a2acbfd78fc9bd8994e67ff94..82b5510ac80d8850e9b4be3246a5ffdd3603c21a 100644 --- a/src/ui/nehubaContainer/nehubaContainer.template.html +++ b/src/ui/nehubaContainer/nehubaContainer.template.html @@ -15,7 +15,7 @@ (mouseenter)="handleMouseEnterLandmark(spatialData)" (mouseleave)="handleMouseLeaveLandmark(spatialData)" [highlight]="spatialData.highlight ? spatialData.highlight : false" - [glyphiconClass]="spatialData.type === 'userLandmark' ? 'glyphicon-chevron-down' : 'glyphicon-map-marker'" + [fasClass]="spatialData.type === 'userLandmark' ? 'fa-chevron-down' : 'fa-map-marker'" [positionX]="getPositionX(0,spatialData)" [positionY]="getPositionY(0,spatialData)" [positionZ]="getPositionZ(0,spatialData)"> @@ -37,7 +37,7 @@ (mouseenter)="handleMouseEnterLandmark(spatialData)" (mouseleave)="handleMouseLeaveLandmark(spatialData)" [highlight]="spatialData.highlight ? spatialData.highlight : false" - [glyphiconClass]="spatialData.type === 'userLandmark' ? 'glyphicon-chevron-down' : 'glyphicon-map-marker'" + [fasClass]="spatialData.type === 'userLandmark' ? 'fa-chevron-down' : 'fa-map-marker'" [positionX]="getPositionX(1,spatialData)" [positionY]="getPositionY(1,spatialData)" [positionZ]="getPositionZ(1,spatialData)"> @@ -59,7 +59,7 @@ (mouseenter)="handleMouseEnterLandmark(spatialData)" (mouseleave)="handleMouseLeaveLandmark(spatialData)" [highlight]="spatialData.highlight ? spatialData.highlight : false" - [glyphiconClass]="spatialData.type === 'userLandmark' ? 'glyphicon-chevron-down' : 'glyphicon-map-marker'" + [fasClass]="spatialData.type === 'userLandmark' ? 'fa-chevron-down' : 'fa-map-marker'" [positionX]="getPositionX(2,spatialData)" [positionY]="getPositionY(2,spatialData)" [positionZ]="getPositionZ(2,spatialData)"> @@ -160,13 +160,13 @@ (deltaValue)="handleMobileOverlayEvent($event)"> <div mobileObliqueGuide guide> <div> - <i class="glyphicon glyphicon-resize-vertical"></i> oblique mode + <i class="fas fa-resize-vertical"></i> oblique mode </div> <div> - <i class="glyphicon glyphicon-resize-horizontal"></i> rotate slice + <i class="fas fa-resize-horizontal"></i> rotate slice </div> </div> <div mobileObliqueCtrl initiator> - <i class="glyphicon glyphicon-globe"></i> + <i class="fas fa-globe"></i> </div> </mobile-overlay> diff --git a/src/ui/ui.module.ts b/src/ui/ui.module.ts index b8b80de7449073522851a8957519c99c7473060f..7b99648240d219d6af4e82429acd417c171efc81 100644 --- a/src/ui/ui.module.ts +++ b/src/ui/ui.module.ts @@ -7,20 +7,18 @@ import { NehubaContainer } from "./nehubaContainer/nehubaContainer.component"; import { SplashScreen } from "./nehubaContainer/splashScreen/splashScreen.component"; import { LayoutModule } from "../layouts/layout.module"; import { FormsModule } from "@angular/forms"; -import { DataBrowserUI } from "./databrowser/databrowser.component"; + import { GroupDatasetByRegion } from "../util/pipes/groupDataEntriesByRegion.pipe"; import { filterRegionDataEntries } from "../util/pipes/filterRegionDataEntries.pipe"; import { FileViewer } from "./fileviewer/fileviewer.component"; +import { MenuIconsBar } from './menuicons/menuicons.component' import { ChartsModule } from 'ng2-charts' import { RadarChart } from "./fileviewer/radar/radar.chart.component"; import { LineChart } from "./fileviewer/line/line.chart.component"; -import { PathToNestedChildren } from "../util/pipes/pathToNestedChildren.pipe"; -import { CopyPropertyPipe } from "../util/pipes/copyProperty.pipe"; import { GetPropMapPipe } from "../util/pipes/getPropMap.pipe"; import { GetUniquePipe } from "src/util/pipes/getUnique.pipe"; -import { FilterDataEntriesbyType } from "../util/pipes/filterDataEntriesByType.pipe"; import { DedicatedViewer } from "./fileviewer/dedicated/dedicated.component"; import { LandmarkUnit } from "./nehubaContainer/landmarkUnit/landmarkUnit.component"; import { SafeStylePipe } from "../util/pipes/safeStyle.pipe"; @@ -33,7 +31,7 @@ import { KgEntryViewer } from "./kgEntryViewer/kgentry.component"; import { SubjectViewer } from "./kgEntryViewer/subjectViewer/subjectViewer.component"; import { GetLayerNameFromDatasets } from "../util/pipes/getLayerNamePipe.pipe"; import { SortDataEntriesToRegion } from "../util/pipes/sortDataEntriesIntoRegion.pipe"; -import { DatasetViewerComponent } from "./datasetViewer/datasetViewer.component"; + import { SpatialLandmarksToDataBrowserItemPipe } from "../util/pipes/spatialLandmarksToDatabrowserItem.pipe"; import { DownloadDirective } from "../util/directives/download.directive"; import { LogoContainer } from "./logoContainer/logoContainer.component"; @@ -44,8 +42,8 @@ import { ShowToastDirective } from "../util/directives/showToast.directive"; import { HelpComponent } from "./help/help.component"; import { ConfigComponent } from './config/config.component' import { FlatmapArrayPipe } from "src/util/pipes/flatMapArray.pipe"; -import { FilterDataEntriesByRegion } from "src/util/pipes/filterDataEntriesByRegion.pipe"; import { PopoverModule } from 'ngx-bootstrap/popover' +import { DatabrowserModule } from "./databrowserModule/databrowser.module"; @NgModule({ @@ -55,6 +53,7 @@ import { PopoverModule } from 'ngx-bootstrap/popover' BrowserModule, LayoutModule, ComponentsModule, + DatabrowserModule, PopoverModule.forRoot(), TooltipModule.forRoot() @@ -63,7 +62,6 @@ import { PopoverModule } from 'ngx-bootstrap/popover' NehubaContainer, NehubaViewerUnit, SplashScreen, - DataBrowserUI, FileViewer, RadarChart, LineChart, @@ -75,23 +73,19 @@ import { PopoverModule } from 'ngx-bootstrap/popover' LayerBrowser, KgEntryViewer, SubjectViewer, - DatasetViewerComponent, LogoContainer, TemplateParcellationCitationsContainer, MobileOverlay, HelpComponent, ConfigComponent, + MenuIconsBar, /* pipes */ GroupDatasetByRegion, filterRegionDataEntries, - PathToNestedChildren, - CopyPropertyPipe, GetPropMapPipe, GetUniquePipe, FlatmapArrayPipe, - FilterDataEntriesbyType, - FilterDataEntriesByRegion, SafeStylePipe, GetLayerNameFromDatasets, SortDataEntriesToRegion, @@ -107,8 +101,7 @@ import { PopoverModule } from 'ngx-bootstrap/popover' /* dynamically created components needs to be declared here */ NehubaViewerUnit, FileViewer, - DataBrowserUI, - DatasetViewerComponent + LayerBrowser, ], exports : [ SubjectViewer, @@ -118,15 +111,14 @@ import { PopoverModule } from 'ngx-bootstrap/popover' PluginBannerUI, NehubaContainer, NehubaViewerUnit, - DataBrowserUI, LayerBrowser, FileViewer, LogoContainer, - DatasetViewerComponent, TemplateParcellationCitationsContainer, MobileOverlay, HelpComponent, - ConfigComponent + ConfigComponent, + MenuIconsBar ] }) diff --git a/src/util/directives/download.directive.ts b/src/util/directives/download.directive.ts index 4f2e6b317f7a4fc54716959abb3e79885fc3c54c..650c8108b4f32fa657c01cb9a445ebf212129fb0 100644 --- a/src/util/directives/download.directive.ts +++ b/src/util/directives/download.directive.ts @@ -10,8 +10,8 @@ export class DownloadDirective{ constructor(public el:ElementRef, public rd2:Renderer2){ this.downloadIcon = rd2.createElement('i') - rd2.addClass(this.downloadIcon, 'glyphicon') - rd2.addClass(this.downloadIcon, 'glyphicon-download-alt') + rd2.addClass(this.downloadIcon, 'fas') + rd2.addClass(this.downloadIcon, 'fa-download-alt') } ngAfterViewInit(){ diff --git a/src/util/directives/glyphiconTooltip.directive.ts b/src/util/directives/glyphiconTooltip.directive.ts index bda7412a118051ca85363a794d81a6806d295277..38544204a3295fc19ca075266350b53482ee6309 100644 --- a/src/util/directives/glyphiconTooltip.directive.ts +++ b/src/util/directives/glyphiconTooltip.directive.ts @@ -6,10 +6,10 @@ import { TooltipDirective } from 'ngx-bootstrap/tooltip' import { ComponentLoaderFactory } from 'ngx-bootstrap/component-loader'; @Directive({ - selector : '.glyphicon.glyphicon-screenshot' + selector : '.fas.fa-screenshot' }) -export class GlyphiconTooltipScreenshotDirective extends TooltipDirective{ +export class fasTooltipScreenshotDirective extends TooltipDirective{ constructor( public viewContainerRef:ViewContainerRef, public rd : Renderer2, @@ -28,10 +28,10 @@ export class GlyphiconTooltipScreenshotDirective extends TooltipDirective{ } @Directive({ - selector : '.glyphicon.glyphicon-remove-sign' + selector : '.fas.fa-remove-sign' }) -export class GlyphiconTooltipRemoveSignDirective extends TooltipDirective{ +export class fasTooltipRemoveSignDirective extends TooltipDirective{ constructor( public viewContainerRef:ViewContainerRef, public rd : Renderer2, @@ -50,10 +50,10 @@ export class GlyphiconTooltipRemoveSignDirective extends TooltipDirective{ } @Directive({ - selector : '.glyphicon.glyphicon-remove' + selector : '.fas.fa-remove' }) -export class GlyphiconTooltipRemoveDirective extends TooltipDirective{ +export class fasTooltipRemoveDirective extends TooltipDirective{ constructor( public viewContainerRef:ViewContainerRef, public rd : Renderer2, @@ -72,10 +72,10 @@ export class GlyphiconTooltipRemoveDirective extends TooltipDirective{ } @Directive({ - selector : '.glyphicon.glyphicon-new-window' + selector : '.fas.fa-new-window' }) -export class GlyphiconTooltipNewWindowDirective extends TooltipDirective{ +export class fasTooltipNewWindowDirective extends TooltipDirective{ constructor( public viewContainerRef:ViewContainerRef, public rd : Renderer2, @@ -94,10 +94,10 @@ export class GlyphiconTooltipNewWindowDirective extends TooltipDirective{ } @Directive({ - selector : '.glyphicon.glyphicon-log-in' + selector : '.fas.fa-log-in' }) -export class GlyphiconTooltipLogInDirective extends TooltipDirective{ +export class fasTooltipLogInDirective extends TooltipDirective{ constructor( public viewContainerRef:ViewContainerRef, public rd : Renderer2, @@ -115,10 +115,10 @@ export class GlyphiconTooltipLogInDirective extends TooltipDirective{ } } @Directive({ - selector : '.glyphicon.glyphicon-question-sign' + selector : '.fas.fa-question-circle' }) -export class GlyphiconTooltipQuestionSignDirective extends TooltipDirective{ +export class fasTooltipQuestionSignDirective extends TooltipDirective{ constructor( public viewContainerRef:ViewContainerRef, public rd : Renderer2, @@ -136,10 +136,10 @@ export class GlyphiconTooltipQuestionSignDirective extends TooltipDirective{ } } @Directive({ - selector : '.glyphicon.glyphicon-info-sign' + selector : '.fas.fa-info-sign' }) -export class GlyphiconTooltipInfoSignDirective extends TooltipDirective{ +export class fasTooltipInfoSignDirective extends TooltipDirective{ constructor( public viewContainerRef:ViewContainerRef, public rd : Renderer2, diff --git a/src/util/pipes/filterDataEntriesByType.pipe.ts b/src/util/pipes/filterDataEntriesByType.pipe.ts deleted file mode 100644 index 0e61d49a51239d0fb625d63c15724bdfd4023141..0000000000000000000000000000000000000000 --- a/src/util/pipes/filterDataEntriesByType.pipe.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { PipeTransform, Pipe } from "@angular/core"; -import { DataEntry } from "../../services/stateStore.service"; - - -@Pipe({ - name : 'filterDataEntriesByType' -}) - -export class FilterDataEntriesbyType implements PipeTransform{ - public transform(dataEntries:DataEntry[],showDataType:Set<string>):DataEntry[]{ - return showDataType.size > 0 - ? dataEntries.filter(dataEntry=>dataEntry.formats.some(format => showDataType.has(format))) - : dataEntries - } -} \ No newline at end of file