diff --git a/src/atlasViewer/atlasViewer.constantService.service.ts b/src/atlasViewer/atlasViewer.constantService.service.ts index aaf0da3b864b943630a0060323de6b1c799beaf9..c1dfbfb16f18af6e7367f8501402c53c55c7b656 100644 --- a/src/atlasViewer/atlasViewer.constantService.service.ts +++ b/src/atlasViewer/atlasViewer.constantService.service.ts @@ -15,6 +15,8 @@ export class AtlasViewerConstantsServices{ public ngLandmarkLayerName = 'spatial landmark layer' public ngUserLandmarkLayerName = 'user landmark layer' + public citationToastDuration = 7000 + /** * optimized for nehubaConfig.layout.useNehubaPerspective.fixedZoomPerspectiveSlices * sliceZoom diff --git a/src/components/components.module.ts b/src/components/components.module.ts index 8e3c9a58851ee1a52b87b2ad4ca841d6a5313832..7f7f4f19cbede9efcc7f03dc13061878d19086d2 100644 --- a/src/components/components.module.ts +++ b/src/components/components.module.ts @@ -23,6 +23,7 @@ import { HighlightPipe } from './flatTree/highlight.pipe'; import { FitlerRowsByVisibilityPipe } from './flatTree/filterRowsByVisibility.pipe'; import { AppendSiblingFlagPipe } from './flatTree/appendSiblingFlag.pipe'; import { ClusteringPipe } from './flatTree/clustering.pipe'; +import { TimerComponent } from './timer/timer.component'; @NgModule({ @@ -41,6 +42,7 @@ import { ClusteringPipe } from './flatTree/clustering.pipe'; PaginationComponent, ToastComponent, FlatTreeComponent, + TimerComponent, /* directive */ HoverableBlockDirective, @@ -68,6 +70,7 @@ import { ClusteringPipe } from './flatTree/clustering.pipe'; PaginationComponent, ToastComponent, FlatTreeComponent, + TimerComponent, SearchResultPaginationPipe, TreeSearchPipe, diff --git a/src/components/timer/timer.component.ts b/src/components/timer/timer.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..4c5e6b9715268e68144482a4da9287a57fdf5dc8 --- /dev/null +++ b/src/components/timer/timer.component.ts @@ -0,0 +1,52 @@ +import { Component, OnInit, Input, OnDestroy, EventEmitter, Output, HostBinding } from "@angular/core"; +import { timedValues } from "../../util/generator" + +@Component({ + selector: 'timer-component', + templateUrl: './timer.template.html', + styleUrls: [ + './timer.style.css' + ] +}) + +export class TimerComponent implements OnInit, OnDestroy{ + @Input() private timeout:number = 500 + @Input() private pause:boolean = false + @Output() timerEnd : EventEmitter<boolean> = new EventEmitter() + + private generator : IterableIterator<any> = null + public progress:number = 0 + private baseProgress:number = 0 + + private rafCbId:number + private rafCb = () => { + if(this.pause){ + this.generator = null + this.baseProgress = this.progress + }else{ + if(this.generator === null){ + this.generator = timedValues(this.timeout * (1 - this.baseProgress), 'linear') + }else{ + const next = this.generator.next() + this.progress = this.baseProgress + (1 - this.baseProgress) * next.value + if(next.done){ + this.timerEnd.emit(true) + return + } + } + } + this.rafCbId = requestAnimationFrame(this.rafCb) + } + + get transform(){ + return `translateX(${this.progress * 100}%)` + } + + ngOnInit(){ + this.rafCbId = requestAnimationFrame(this.rafCb) + } + + ngOnDestroy(){ + if(this.rafCbId) cancelAnimationFrame(this.rafCbId) + } +} \ No newline at end of file diff --git a/src/components/timer/timer.style.css b/src/components/timer/timer.style.css new file mode 100644 index 0000000000000000000000000000000000000000..ad5611e32d1c2256c7d887058c0227373c6cbeea --- /dev/null +++ b/src/components/timer/timer.style.css @@ -0,0 +1,14 @@ +:host +{ + display: block; + width: 100%; + height: 100%; + overflow: hidden; +} + +div +{ + background-color:rgba(128, 128, 128, 0.2); + height:100%; + width:100%; +} \ No newline at end of file diff --git a/src/components/timer/timer.template.html b/src/components/timer/timer.template.html new file mode 100644 index 0000000000000000000000000000000000000000..6e13c9405bd33ffe268229f6cbaae57d6af45ece --- /dev/null +++ b/src/components/timer/timer.template.html @@ -0,0 +1,2 @@ +<div [style.transform] = "transform"> +</div> \ No newline at end of file diff --git a/src/components/toast/toast.component.ts b/src/components/toast/toast.component.ts index a2d4599f706b2dfbc21a4e6698f103d075e27ba2..c1836b689cde0dd748c32a381eb6d319bbaa742e 100644 --- a/src/components/toast/toast.component.ts +++ b/src/components/toast/toast.component.ts @@ -1,4 +1,4 @@ -import { Component, Input, ViewContainerRef, ViewChild, Output, EventEmitter, HostBinding, ElementRef, ChangeDetectionStrategy, OnInit } from "@angular/core"; +import { Component, Input, ViewContainerRef, ViewChild, Output, EventEmitter, HostBinding, ElementRef, ChangeDetectionStrategy, OnInit, HostListener, NgZone } from "@angular/core"; import { toastAnimation } from "./toast.animation"; @Component({ @@ -7,34 +7,27 @@ import { toastAnimation } from "./toast.animation"; styleUrls : ['./toast.style.css'], animations : [ toastAnimation - ], - changeDetection: ChangeDetectionStrategy.OnPush + ] }) -export class ToastComponent implements OnInit{ +export class ToastComponent{ @Input() message : string @Input() timeout : number = 0 @Input() dismissable : boolean = true @Output() dismissed : EventEmitter<boolean> = new EventEmitter() - private timeoutId : any + public progress: number = 0 @HostBinding('@exists') exists : boolean = true @ViewChild('messageContainer',{read:ViewContainerRef}) messageContainer : ViewContainerRef - - ngOnInit(){ - if(this.timeout > 0) this.timeoutId = setTimeout(() => this.dismissed.emit(false), this.timeout) - } dismiss(event:MouseEvent){ event.preventDefault() event.stopPropagation() - if(this.timeoutId) clearTimeout(this.timeoutId) - this.dismissed.emit(true) } } \ No newline at end of file diff --git a/src/components/toast/toast.style.css b/src/components/toast/toast.style.css index d3e835e4630d5a15ac03ee3d7224781ec459e17e..50624d5c59826c29247dcbfa9f6286229993120a 100644 --- a/src/components/toast/toast.style.css +++ b/src/components/toast/toast.style.css @@ -10,7 +10,7 @@ div[container] { display : inline-block; align-items: center; - padding : 0.3em 1em; + padding : 0.3em 1em 0em 1em; pointer-events: all; } @@ -35,4 +35,10 @@ div[message], div[close] { display:inline-block; +} +timer-component +{ + margin: 0 -1em; + height:0.5em; + width: calc(100% + 2em); } \ No newline at end of file diff --git a/src/components/toast/toast.template.html b/src/components/toast/toast.template.html index cf5619bd2344029c1a01eef343e22ff78d658607..17e173c70db4a6228c917cff8e5db70d2961c097 100644 --- a/src/components/toast/toast.template.html +++ b/src/components/toast/toast.template.html @@ -1,4 +1,4 @@ -<div container> +<div (mouseenter) = "hover = true" (mouseleave)="hover = false" container> <div message> <ng-template #messageContainer> @@ -10,4 +10,6 @@ <div (click) = "dismiss($event)" *ngIf = "dismissable" close> <i class = "glyphicon glyphicon-remove"></i> </div> + <timer-component (timerEnd)="dismissed.emit(false)" [pause] = "hover" [timeout] = "timeout" timer> + </timer-component> </div> \ No newline at end of file diff --git a/src/ui/banner/banner.component.ts b/src/ui/banner/banner.component.ts index 654748941103461cfec72ae6eb6ff0f9d2936adf..1bec07a845f9eeed4f1cc25a44949e1c2753ef60 100644 --- a/src/ui/banner/banner.component.ts +++ b/src/ui/banner/banner.component.ts @@ -1,81 +1,108 @@ -import { Component, OnDestroy, ChangeDetectionStrategy, HostListener, ViewChild, ElementRef } from "@angular/core"; +import { Component, OnDestroy, ChangeDetectionStrategy, HostListener, 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 } from "rxjs/operators"; +import { map, filter, debounceTime, buffer, distinctUntilChanged } from "rxjs/operators"; import { FilterNameBySearch } from "../../util/pipes/filterNameBySearch.pipe"; import { regionAnimation } from "./regionPopover.animation"; +import { AtlasViewerConstantsServices } from "../../atlasViewer/atlasViewer.constantService.service" @Component({ - selector : 'atlas-banner', - templateUrl : './banner.template.html', - styleUrls : [ + selector: 'atlas-banner', + templateUrl: './banner.template.html', + styleUrls: [ `./banner.style.css` ], - animations:[ + animations: [ regionAnimation ], - changeDetection : ChangeDetectionStrategy.OnPush + changeDetection: ChangeDetectionStrategy.OnPush }) -export class AtlasBanner implements OnDestroy{ +export class AtlasBanner implements OnDestroy, OnInit { - public loadedTemplates$ : Observable<any[]> - public newViewer$ : Observable<any> - public selectedParcellation$ : Observable<any> - public selectedRegions$ : Observable<any[]> + public loadedTemplates$: Observable<any[]> + public newViewer$: Observable<any> + public selectedParcellation$: Observable<any> + public selectedRegions$: Observable<any[]> - private regionsLabelIndexMap : Map<number,any> = new Map() + 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]} + public selectedTemplate: any + public selectedParcellation: any + public selectedRegions: any[] = [] + private navigation: { position: [number, number, number] } = { position: [0, 0, 0] } - private subscriptions : Subscription[] = [] + private subscriptions: Subscription[] = [] - searchTerm : string = '' + @ViewChild('templateCitationAnchor', { read: ElementRef }) templateCitationAnchor: ElementRef + @ViewChild('parcellationCitationAnchor', { read: ElementRef }) parcellationCitationAnchor: ElementRef - constructor(private store:Store<ViewerStateInterface>){ + searchTerm: string = '' + + constructor( + private store: Store<ViewerStateInterface>, + private constantService: AtlasViewerConstantsServices + ) { this.loadedTemplates$ = this.store.pipe( select('viewerState'), safeFilter('fetchedTemplates'), - map(state=>state.fetchedTemplates)) + map(state => state.fetchedTemplates)) this.newViewer$ = this.store.pipe( select('viewerState'), - filter(state=>isDefined(state) && isDefined(state.templateSelected)), - filter(state=> - !isDefined(this.selectedTemplate) || - state.templateSelected.name !== this.selectedTemplate.name) + filter(state => isDefined(state) && isDefined(state.templateSelected)), + distinctUntilChanged((o, n) => o.templateSelected.name === n.templateSelected.name) ) this.selectedParcellation$ = this.store.pipe( select('viewerState'), safeFilter('parcellationSelected'), - map(state=>state.parcellationSelected) + map(state => state.parcellationSelected) ) this.selectedRegions$ = merge( this.store.pipe( select('viewerState'), - filter(state=>isDefined(state)&&isDefined(state.regionsSelected)), - map(state=>state.regionsSelected) + filter(state => isDefined(state) && isDefined(state.regionsSelected)), + map(state => state.regionsSelected) ) - ) + ) + } + + ngOnInit() { this.subscriptions.push( - this.newViewer$.subscribe((state)=>{ - + this.newViewer$.subscribe((state) => { this.selectedTemplate = state.templateSelected const selectedParcellation = state.parcellationSelected ? state.parcellationSelected : this.selectedTemplate.parcellations[0] this.handleParcellationChange(selectedParcellation) }) ) + + this.subscriptions.push( + this.newViewer$.pipe( + debounceTime(250) + ).subscribe(() => { + if (this.templateCitationAnchor) + this.templateCitationAnchor.nativeElement.click() + }) + ) + this.subscriptions.push( this.selectedParcellation$.subscribe((this.handleParcellationChange).bind(this)) ) + this.subscriptions.push( - this.selectedRegions$.subscribe((ev)=>{ + this.selectedParcellation$.pipe( + debounceTime(250) + ).subscribe(() => { + if (this.parcellationCitationAnchor) + this.parcellationCitationAnchor.nativeElement.click() + }) + ) + + this.subscriptions.push( + this.selectedRegions$.subscribe((ev) => { this.selectedRegions = ev }) ) @@ -87,51 +114,52 @@ export class AtlasBanner implements OnDestroy{ debounceTime(200) ) ) - ).subscribe(arr=>arr.length > 1 ? this.doubleClick(arr[0]) : this.singleClick(arr[0])) + ).subscribe(arr => arr.length > 1 ? this.doubleClick(arr[0]) : this.singleClick(arr[0])) ) - this.subscriptions.push( this.store.pipe( select('viewerState'), safeFilter('navigation'), - map(obj=>obj.navigation) - ).subscribe((navigation:any)=>this.navigation = navigation) + map(obj => obj.navigation) + ).subscribe((navigation: any) => this.navigation = navigation) ) } - - ngOnDestroy(){ - this.subscriptions.forEach(s=>s.unsubscribe()) + ngOnDestroy() { + this.subscriptions.forEach(s => s.unsubscribe()) } - handleParcellationChange(parcellation){ + handleParcellationChange(parcellation) { this.selectedParcellation = parcellation this.regionsLabelIndexMap = getLabelIndexMap(parcellation.regions) } - selectTemplate(template:any){ - if(this.selectedTemplate === template){ + selectTemplate(template: any) { + if (this.selectedTemplate === template) { return } this.store.dispatch({ - type : NEWVIEWER, - selectTemplate : template, - selectParcellation : template.parcellations[0] + type: NEWVIEWER, + selectTemplate: template, + selectParcellation: template.parcellations[0] }) } - selectParcellation(parcellation:any){ + selectParcellation(parcellation: any) { + if (this.selectedParcellation === parcellation) { + return + } this.store.dispatch({ - type : SELECT_PARCELLATION, - selectParcellation : parcellation + type: SELECT_PARCELLATION, + selectParcellation: parcellation }) } /* double click navigate to the interested area */ - private doubleClick(obj:any){ - if( !(obj && obj.inputItem && (obj.inputItem.position || obj.inputItem.POIs)) ){ + private doubleClick(obj: any) { + if (!(obj && obj.inputItem && (obj.inputItem.position || obj.inputItem.POIs))) { return } @@ -142,10 +170,10 @@ export class AtlasBanner implements OnDestroy{ : null this.store.dispatch({ - type : CHANGE_NAVIGATION, - navigation : { - position : newPos, - animation : { + type: CHANGE_NAVIGATION, + navigation: { + position: newPos, + animation: { /* empty object is enough to be truthy */ } }, @@ -153,99 +181,107 @@ export class AtlasBanner implements OnDestroy{ } /* single click selects/deselects region(s) */ - private singleClick(obj:any){ + 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))) + 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))) + 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() + 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) + changeSearchTerm(event: any) { + if (event.target.value === this.searchTerm) return this.searchTerm = event.target.value } - @ViewChild('searchRegionPopover', {read:ElementRef}) inputRegionPopover : ElementRef + @ViewChild('searchRegionPopover', { read: ElementRef }) inputRegionPopover: ElementRef public showRegionTree: boolean - @HostListener('document:click',['$event']) - closeRegion(event:MouseEvent){ + @HostListener('document:click', ['$event']) + closeRegion(event: MouseEvent) { /* region popover may not always be rendered */ - if(!this.inputRegionPopover) + if (!this.inputRegionPopover) return - + /* FF < 62 does not implement event.srcElement so use event.originalTarget to polyfill for FF */ - - const contains = event.srcElement + + const contains = event.srcElement ? this.inputRegionPopover.nativeElement.contains(event.srcElement) : this.inputRegionPopover.nativeElement.contains((event as any).originalTarget) - if(contains) + if (contains) this.showRegionTree = true else this.showRegionTree = false } - handleClickRegion(obj:any){ + handleClickRegion(obj: any) { obj.event.stopPropagation() this.handleRegionTreeClickSubject.next(obj) } - displayActiveTemplate(template:any){ + displayActiveTemplate(template: any) { return `<small>Template</small> <small class = "mute-text">${template ? '(' + template.name + ')' : ''}</small> <span class = "caret"></span>` } - displayActiveParcellation(parcellation:any){ + displayActiveParcellation(parcellation: any) { 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 === '' ? + 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>`) + 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>` + 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){ + getChildren(item: any) { return item.children } - filterTreeBySearch(node:any):boolean{ - return this.filterNameBySearchPipe.transform([node.name],this.searchTerm) + filterTreeBySearch(node: any): boolean { + return this.filterNameBySearchPipe.transform([node.name], this.searchTerm) } - clearRegions(event:Event){ + clearRegions(event: Event) { event.stopPropagation() event.preventDefault() this.store.dispatch({ - type : SELECT_REGIONS, - selectRegions : [] + type: SELECT_REGIONS, + selectRegions: [] }) } filterNameBySearchPipe = new FilterNameBySearch() - get aggregatedRegionTree(){ + get aggregatedRegionTree() { return { - name : this.selectedParcellation.name, - children : this.selectedParcellation.regions + name: this.selectedParcellation.name, + children: this.selectedParcellation.regions } } + + citationExists(obj: any) { + return obj && obj.properties && obj.properties.publications && obj.properties.publications.length > 0 + } + + get toastDuration() { + return this.constantService.citationToastDuration + } } diff --git a/src/ui/banner/banner.style.css b/src/ui/banner/banner.style.css index 3cb4c6ce41ae221e17d7a7cffc01cf5ec892e9d3..14fd168502aa0ae4561f419b8bb2926b778d591f 100644 --- a/src/ui/banner/banner.style.css +++ b/src/ui/banner/banner.style.css @@ -10,8 +10,8 @@ dropdown-component, :host > div[searchRegionPopover] { - margin-left:1em; - margin-bottom: 0.4em; + margin:auto 0 auto 1em; + vertical-align: middle; } div[treeHeader] @@ -90,3 +90,16 @@ input[type="text"] box-shadow: inset 0 4px 6px 0 rgba(0,0,0,0.2); color:rgba(255,255,255,0.8); } + +i +{ + font-size: 150%; + margin:auto 0; +} + +citations-component +{ + width: 500px; + max-width: 100%; + display:block; +} \ No newline at end of file diff --git a/src/ui/banner/banner.template.html b/src/ui/banner/banner.template.html index 44201ed9468310b7212ac134a50e2e737667a376..4a0805af51d0b33aa781f019123beb3bbeb34e1e 100644 --- a/src/ui/banner/banner.template.html +++ b/src/ui/banner/banner.template.html @@ -6,6 +6,24 @@ </dropdown-component> +<i + *ngIf = "citationExists(selectedTemplate)" + [toastLength] = "toastDuration" + [showToast] = "citation" + class="glyphicon glyphicon-info-sign" + #templateCitationAnchor> + + <ng-template #citation> + <h4> + Citations for {{ selectedTemplate ? selectedTemplate.name : '' }} + </h4> + <citations-component + [properties] = "selectedTemplate.properties"> + </citations-component> + </ng-template> + +</i> + <dropdown-component *ngIf = "selectedTemplate" (itemSelected) = "selectParcellation($event)" @@ -14,6 +32,22 @@ [inputArray] = "selectedTemplate.parcellations"> </dropdown-component> +<i + *ngIf = "citationExists(selectedParcellation)" + [toastLength] = "toastDuration" + [showToast] = "citation" + class="glyphicon glyphicon-info-sign" + #parcellationCitationAnchor> + <ng-template #citation> + <h4> + Citations for {{ selectedParcellation ? selectedParcellation.name : '' }} + </h4> + <citations-component + [properties] = "selectedParcellation.properties"> + </citations-component> + </ng-template> +</i> + <div *ngIf = "selectedTemplate" #searchRegionPopover diff --git a/src/ui/nehubaContainer/nehubaContainer.style.css b/src/ui/nehubaContainer/nehubaContainer.style.css index 73d82c86d42674388cae498fb89fa009fb967829..2e3528179021066ffa9ea544fc427164394836d1 100644 --- a/src/ui/nehubaContainer/nehubaContainer.style.css +++ b/src/ui/nehubaContainer/nehubaContainer.style.css @@ -138,12 +138,6 @@ div.loadingIndicator div.spinnerAnimationCircle color:rgba(255,255,255,0.8); } -[desktopTemplateCitation] -{ - padding: 0.6em 0 0 0.6em; - font-size:200%; -} - div[mobileObliqueCtrl] { font-size: 200%; @@ -178,10 +172,3 @@ div[mobileObliqueGuide] flex-direction: column; align-items: center; } - -template-parcellation-citation-container -{ - width: 500px; - max-width: 100%; - display:block; -} \ No newline at end of file diff --git a/src/ui/nehubaContainer/nehubaContainer.template.html b/src/ui/nehubaContainer/nehubaContainer.template.html index 57b4f9eb785907ae756562628144f1922ad977e0..2d484c3c77e5e5eefdbd75f0d5a66546225ac77c 100644 --- a/src/ui/nehubaContainer/nehubaContainer.template.html +++ b/src/ui/nehubaContainer/nehubaContainer.template.html @@ -91,14 +91,6 @@ <!-- TODO export status card to its own container --> <div statusCard> - <div desktopTemplateCitation *ngIf = "!isMobile"> - <i - [toastLength] = "7000" - [showToast] = - "templateAtlasCitations" - class="glyphicon glyphicon-info-sign"></i> - </div> - <hr *ngIf = "showCitation && !isMobile" /> <div linksContainer> @@ -151,11 +143,3 @@ <i class="glyphicon glyphicon-globe"></i> </div> </mobile-overlay> - -<ng-template #templateAtlasCitations> - <h4> - Citations for {{ selectedTemplate ? selectedTemplate.name : '' }} and {{ selectedParcellation ? selectedParcellation.name : '' }} - </h4> - <template-parcellation-citation-container> - </template-parcellation-citation-container> -</ng-template> \ No newline at end of file diff --git a/src/util/directives/showToast.directive.ts b/src/util/directives/showToast.directive.ts index 0062b5737168d0142e7de75752a9389e1ba992a6..20642c146955b0785c97dd14202efe644b47bed1 100644 --- a/src/util/directives/showToast.directive.ts +++ b/src/util/directives/showToast.directive.ts @@ -28,10 +28,12 @@ export class ShowToastDirective{ return this._toastLength } + private dismissHandler : () => void + @HostListener('click', ['$event.target']) click(ev:MouseEvent){ - - this.toastService.showToast(this.showToast, { + if(this.dismissHandler) this.dismissHandler() + this.dismissHandler = this.toastService.showToast(this.showToast, { dismissable: true, timeout: this.toastLength }) diff --git a/src/util/generator.ts b/src/util/generator.ts index 6409a61fe65c1b3a8b81e9c6d3bd096cba860c09..90992f8fd51ad8dcc6bbc5202aab4e4826efffcc 100644 --- a/src/util/generator.ts +++ b/src/util/generator.ts @@ -1,4 +1,4 @@ -export function* timedValues(sec:number = 500,mode:string = 'linear'){ +export function* timedValues(ms:number = 500,mode:string = 'linear'){ const startTime = Date.now() const getValue = (fraction) =>{ @@ -8,8 +8,8 @@ export function* timedValues(sec:number = 500,mode:string = 'linear'){ return fraction < 1 ? fraction : 1 } } - while((Date.now() - startTime) < sec){ - yield getValue( (Date.now() - startTime) / sec ) + while((Date.now() - startTime) < ms){ + yield getValue( (Date.now() - startTime) / ms ) } return 1 } \ No newline at end of file