diff --git a/src/atlasViewer/atlasViewer.component.ts b/src/atlasViewer/atlasViewer.component.ts index c6f4628f9c5976923731a17d8dff513d62c4f19f..428e3dd7a5861017ded7f15df2a5d021737710c3 100644 --- a/src/atlasViewer/atlasViewer.component.ts +++ b/src/atlasViewer/atlasViewer.component.ts @@ -19,6 +19,8 @@ import { FixedMouseContextualContainerDirective } from "src/util/directives/Fixe import { DatabrowserService } from "src/ui/databrowserModule/databrowser.service"; import { AGREE_COOKIE, AGREE_KG_TOS, SHOW_KG_TOS } from "src/services/state/uiState.store"; import { TabsetComponent } from "ngx-bootstrap/tabs"; +import { ToastService } from "src/services/toastService.service"; +import { ZipFileDownloadService } from "src/services/zipFileDownload.service"; @Component({ selector: 'atlas-viewer', @@ -45,6 +47,7 @@ export class AtlasViewer implements OnDestroy, OnInit, AfterViewInit { @ViewChild(FixedMouseContextualContainerDirective) rClContextualMenu: FixedMouseContextualContainerDirective @ViewChild('mobileMenuTabs') mobileMenuTabs: TabsetComponent + @ViewChild('publications') publications: TemplateRef<any> /** * required for styling of all child components @@ -85,6 +88,10 @@ export class AtlasViewer implements OnDestroy, OnInit, AfterViewInit { public sidePanelOpen$: Observable<boolean> + handleToast + tPublication + pPublication + get toggleMessage(){ return this.constantsService.toggleMessage } @@ -98,7 +105,9 @@ export class AtlasViewer implements OnDestroy, OnInit, AfterViewInit { public apiService: AtlasViewerAPIServices, private modalService: BsModalService, private databrowserService: DatabrowserService, - private dispatcher$: ActionsSubject + private dispatcher$: ActionsSubject, + private toastService: ToastService, + private zipFileDownloadService: ZipFileDownloadService, ) { this.ngLayerNames$ = this.store.pipe( select('viewerState'), @@ -203,15 +212,72 @@ export class AtlasViewer implements OnDestroy, OnInit, AfterViewInit { distinctUntilChanged(), ) + this.subscriptions.push( - this.selectedParcellation$.subscribe(parcellation => this.selectedParcellation = parcellation) + this.newViewer$.subscribe(template => this.selectedTemplate = template) ) this.subscriptions.push( - this.newViewer$.subscribe(template => this.selectedTemplate = template) + this.selectedParcellation$.subscribe(parcellation => { + this.selectedParcellation = parcellation + + if (this.selectedTemplate && this.selectedParcellation) { + if (this.selectedTemplate['properties'] && this.selectedTemplate['properties']['publications']) { + this.tPublication = this.selectedTemplate['properties']['publications'] + } else { + this.tPublication = null + } + if (this.selectedParcellation['properties'] && this.selectedParcellation['properties']['publications']) { + this.pPublication = this.selectedParcellation['properties']['publications'] + } else { + this.pPublication = null + } + } else { + this.tPublication = null + this.pPublication = null + } + + if (this.tPublication || this.pPublication) { + + if (this.handleToast) { + this.handleToast() + this.handleToast = null + } + this.handleToast = this.toastService.showToast(this.publications, { + timeout: 7000 + }) + + } + }) ) + + } + downloadPublications() { + const filename = this.selectedTemplate.name + ' - ' + this.selectedParcellation.name + '.txt' + let publicationsText = '' + + if (this.tPublication) { + publicationsText += this.selectedTemplate.name + ' Publications:\r\n' + this.tPublication.forEach((tp, i) => { + publicationsText += '\t' + (i+1) + '. ' + tp['citation'] + ' - ' + tp['doi'] + '\r\n' + }); + } + + if (this.pPublication) { + if (this.tPublication) publicationsText += '\r\n\r\n' + publicationsText += this.selectedParcellation.name + ' Publications:\r\n' + this.pPublication.forEach((pp, i) => { + publicationsText += '\t' + (i+1) + '. ' + pp['citation'] + ' - ' + pp['doi'] + '\r\n' + }); + } + + this.zipFileDownloadService.saveTextAsFile(publicationsText, filename) + publicationsText = '' + } + + private selectedParcellation$: Observable<any> private selectedParcellation: any diff --git a/src/atlasViewer/atlasViewer.style.css b/src/atlasViewer/atlasViewer.style.css index f0f1b2e0550826934afbf5e032c96fb8c706714c..e3b805bfa801af142bd6a2eb8c1f3721b11276d5 100644 --- a/src/atlasViewer/atlasViewer.style.css +++ b/src/atlasViewer/atlasViewer.style.css @@ -177,4 +177,16 @@ mat-sidenav { .mpobileMenuTabs { margin: 40px 0 0 5px; +} + +.timerToast { + max-width: 700px; + display: flex; + flex-direction: column; + justify-content: center; +} + +.downloadPublications { + align-self: flex-end; + outline: none; } \ No newline at end of file diff --git a/src/atlasViewer/atlasViewer.template.html b/src/atlasViewer/atlasViewer.template.html index 8b1ca1beeee7b31443254c593099fd53dd19acf0..dddf61d6222aefd4fa8eea60713e0c9f88b69570 100644 --- a/src/atlasViewer/atlasViewer.template.html +++ b/src/atlasViewer/atlasViewer.template.html @@ -257,4 +257,24 @@ </div> </ng-container> </div> +</ng-template> + +<ng-template #publications > + <div *ngIf="tPublication || pPublication" class="timerToast"> + <button mat-mini-fab color="primary" class="downloadPublications" (click)="downloadPublications()">↓</button> + + <div *ngIf="tPublication"> + <p>{{selectedTemplate['name']}} Publication(s)</p> + <div *ngFor="let tp of tPublication"> + <a [href]="tp['doi']" target="_blank">{{tp['citation']}}</a> + </div> + <hr *ngIf="pPublication"> + </div> + <div *ngIf="pPublication"> + <p>{{selectedParcellation['name']}} Publication(s)</p> + <div *ngFor="let pp of pPublication"> + <a [href]="pp['doi']" target="_blank">{{pp['citation']}}</a> + </div> + </div> + </div> </ng-template> \ No newline at end of file diff --git a/src/components/components.module.ts b/src/components/components.module.ts index deeed608e8648a00074b0aae7edc17119613e5b0..d51928617d69ff3089cb7aa07dcfd7da2d37b0f0 100644 --- a/src/components/components.module.ts +++ b/src/components/components.module.ts @@ -27,6 +27,7 @@ import { TimerComponent } from './timer/timer.component'; import { PillComponent } from './pill/pill.component'; import { CommonModule } from '@angular/common'; import { RadioList } from './radiolist/radiolist.component'; +import { AngularMaterialModule } from 'src/ui/sharedModules/angularMaterial.module'; @NgModule({ @@ -35,6 +36,7 @@ import { RadioList } from './radiolist/radiolist.component'; ScrollingModule, FormsModule, BrowserAnimationsModule, + AngularMaterialModule ], declarations : [ /* components */ diff --git a/src/components/radiolist/radiolist.component.ts b/src/components/radiolist/radiolist.component.ts index 486ee98ecb8d2bf3775d5a1e0e2aef22571c9e68..dd0bca365ddef44e390aa7afed23e5bd4e3bffa2 100644 --- a/src/components/radiolist/radiolist.component.ts +++ b/src/components/radiolist/radiolist.component.ts @@ -1,4 +1,6 @@ -import { Component, ChangeDetectionStrategy, Input, Output, EventEmitter } from "@angular/core"; +import { Component, ChangeDetectionStrategy, Input, Output, EventEmitter, OnInit, ViewChild, TemplateRef } from "@angular/core"; +import { ToastService } from "src/services/toastService.service"; +import { ZipFileDownloadService } from "src/services/zipFileDownload.service"; @Component({ selector: 'radio-list', @@ -8,13 +10,13 @@ import { Component, ChangeDetectionStrategy, Input, Output, EventEmitter } from ], styles: [ ` - ul > li.selected > span:before + ul > li.selected > .textSpan:before { content: '\u2022'; width : 1em; display:inline-block; } - ul > li:not(.selected) > span:before + ul > li:not(.selected) > .textSpan:before { content: ' '; width : 1em; @@ -46,4 +48,32 @@ export class RadioList{ @Input() isMobile: boolean @Input() darktheme: boolean + @ViewChild('publicationTemplate') publicationTemplate: TemplateRef<any> + + handleToast + + constructor(private toastService: ToastService, + private zipFileDownloadService: ZipFileDownloadService) {} + + showToast(item) { + if (this.handleToast) { + this.handleToast() + this.handleToast = null + } + this.handleToast = this.toastService.showToast(this.publicationTemplate, { + timeout: 7000 + }) + + } + + + downloadPublications(item) { + const filename = item['name'] + ' publications.txt' + let publicationsText = item['name'] + ' Publications:\r\n' + item['properties']['publications'].forEach((p, i) => { + publicationsText += '\t' + (i+1) + '. ' + p['citation'] + ' - ' + p['doi'] + '\r\n' + }); + this.zipFileDownloadService.saveTextAsFile(publicationsText, filename) + publicationsText = '' + } } \ No newline at end of file diff --git a/src/components/radiolist/radiolist.style.css b/src/components/radiolist/radiolist.style.css index d264f83b0676a602570939980e60c9abc93c2294..6973a4c9701b781ede3ca2e099a499ff464c6209 100644 --- a/src/components/radiolist/radiolist.style.css +++ b/src/components/radiolist/radiolist.style.css @@ -55,3 +55,26 @@ ul,span.dropdown-item-1 white-space: nowrap; border:none; } + +.infoIcon { + margin-right: 5px; + display: inline-block; + border: 1px solid gray; + border-radius: 15px; + width: 24px; + height: 24px; + cursor: pointer; + text-align: center; +} + +.timerToast { + max-width: 700px; + display: flex; + flex-direction: column; + justify-content: center; +} + +.downloadPublications { + align-self: flex-end; + outline: none; +} \ No newline at end of file diff --git a/src/components/radiolist/radiolist.template.html b/src/components/radiolist/radiolist.template.html index f46b731b9636b6835a948e77c427112c8e4dcd58..d0f2d3041f9b5a465794d73acba0fc0944110e46 100644 --- a/src/components/radiolist/radiolist.template.html +++ b/src/components/radiolist/radiolist.template.html @@ -4,13 +4,33 @@ role="menu"> <li *ngFor="let input of inputArray" - [ngClass]="checkSelected(selectedItem, input) ? 'selected' : 'notselected'" - (click)="itemSelected.emit({previous: selectedItem, current: input})" + [ngClass]="checkSelected(selectedItem, input) ? 'selected' : 'notselected'" role="menuitem"> + <span *ngIf="input['properties'] && input['properties']['publications']" class="infoIcon" (click)="showToast(input)"> + i + </span> + <span - class="dropdown-item-1" + (click)="itemSelected.emit({previous: selectedItem, current: input})" + class="dropdown-item-1 textSpan" [innerHTML] = "listDisplay(input)"> </span> + + + + +<ng-template #publicationTemplate> + <div *ngIf="input['properties'] && input['properties']['publications']" class="timerToast"> + <button mat-mini-fab color="primary" class="downloadPublications" (click)="downloadPublications(input)">↓</button> + <p>{{input['name']}} Publication(s)</p> + <div *ngFor="let p of input['properties']['publications']"> + <a [href]="p['doi']" target="_blank">{{p['citation']}}</a> + </div> + </div> +</ng-template> + + + </li> </ul> \ No newline at end of file diff --git a/src/main.module.ts b/src/main.module.ts index 9d59f85ee1dab8eb502711f34a36deb79429c243..169f495052a8fc24d3540d7a173ea15a3e68303b 100644 --- a/src/main.module.ts +++ b/src/main.module.ts @@ -42,6 +42,7 @@ import { TransformOnhoverSegmentPipe } from "./atlasViewer/onhoverSegment.pipe"; import 'hammerjs'; import 'hammer-timejs'; +import { ZipFileDownloadService } from "./services/zipFileDownload.service"; @NgModule({ imports : [ @@ -108,7 +109,8 @@ import 'hammer-timejs'; ToastService, AtlasWorkerService, AuthService, - + ZipFileDownloadService, + /** * TODO * once nehubacontainer is separated into viewer + overlay, migrate to nehubaContainer module diff --git a/src/services/zipFileDownload.service.ts b/src/services/zipFileDownload.service.ts new file mode 100644 index 0000000000000000000000000000000000000000..e1524a68460a4e15dce9ad86c80c2f7fdeb688f7 --- /dev/null +++ b/src/services/zipFileDownload.service.ts @@ -0,0 +1,33 @@ +import { Injectable } from "@angular/core"; + +@Injectable({ providedIn: 'root' }) + +export class ZipFileDownloadService { + saveTextAsFile(data, filename) { + + if (!data) { + console.error('Console.save: No data') + return; + } + if (!filename) filename = 'Publication.json' + + var blob = new Blob([data], { type: 'text/plain' }), + e = document.createEvent('MouseEvents'), + a = document.createElement('a') + // FOR IE: + + if (window.navigator && window.navigator.msSaveOrOpenBlob) { + window.navigator.msSaveOrOpenBlob(blob, filename); + } + else { + var e = document.createEvent('MouseEvents'), + a = document.createElement('a'); + + a.download = filename; + a.href = window.URL.createObjectURL(blob); + a.dataset.downloadurl = ['text/plain', a.download, a.href].join(':'); + e.initEvent('click', true, false); + a.dispatchEvent(e); + } + } +} \ No newline at end of file