Skip to content
Snippets Groups Projects
Commit 3c1f01b6 authored by Xiao Gui's avatar Xiao Gui Committed by xgui3783
Browse files

bugfix: fixed how flat tree render dotted guiding line, removed dependency on ngx-popover

parent 99172c1a
No related branches found
No related tags found
No related merge requests found
...@@ -20,8 +20,8 @@ import { FlatTreeComponent } from './flatTree/flatTree.component'; ...@@ -20,8 +20,8 @@ import { FlatTreeComponent } from './flatTree/flatTree.component';
import { FlattenTreePipe } from './flatTree/flattener.pipe'; import { FlattenTreePipe } from './flatTree/flattener.pipe';
import { RenderPipe } from './flatTree/render.pipe'; import { RenderPipe } from './flatTree/render.pipe';
import { HighlightPipe } from './flatTree/highlight.pipe'; import { HighlightPipe } from './flatTree/highlight.pipe';
import { HasVisibleChildrenPipe } from './flatTree/hasVisibleChildren.pipe';
import { FitlerRowsByVisibilityPipe } from './flatTree/filterRowsByVisibility.pipe'; import { FitlerRowsByVisibilityPipe } from './flatTree/filterRowsByVisibility.pipe';
import { AppendSiblingFlagPipe } from './flatTree/appendSiblingFlag.pipe';
@NgModule({ @NgModule({
...@@ -52,8 +52,8 @@ import { FitlerRowsByVisibilityPipe } from './flatTree/filterRowsByVisibility.pi ...@@ -52,8 +52,8 @@ import { FitlerRowsByVisibilityPipe } from './flatTree/filterRowsByVisibility.pi
FlattenTreePipe, FlattenTreePipe,
RenderPipe, RenderPipe,
HighlightPipe, HighlightPipe,
HasVisibleChildrenPipe, FitlerRowsByVisibilityPipe,
FitlerRowsByVisibilityPipe AppendSiblingFlagPipe
], ],
exports : [ exports : [
BrowserAnimationsModule, BrowserAnimationsModule,
......
import { Pipe, PipeTransform } from "@angular/core";
@Pipe({
name : 'appendSiblingFlagPipe'
})
export class AppendSiblingFlagPipe implements PipeTransform{
public transform(objs:any[]):any[]{
const idSet = new Set(objs.map(obj => obj.lvlId))
return objs.map(obj =>
Object.assign({}, obj, {
siblingFlags : obj.lvlId
.split('_')
.reduce((acc,curr) => acc.concat(acc.length === 0 ? curr : acc[acc.length -1].concat(`_${curr}`)), [])
.map(prevIds => this.isLast(prevIds,idSet))
.slice(1)
})
)
}
private isLast(id:string, set:Set<string>):boolean{
return !set.has(id.split('_').slice(0,-1).concat( (Number(id.split('_').reverse()[0]) + 1).toString() ).join('_'))
}
}
\ No newline at end of file
...@@ -18,19 +18,16 @@ export class FlatTreeComponent{ ...@@ -18,19 +18,16 @@ export class FlatTreeComponent{
@Input() childrenExpanded : boolean = true @Input() childrenExpanded : boolean = true
@Output() treeNodeClick : EventEmitter<any> = new EventEmitter() @Output() treeNodeClick : EventEmitter<any> = new EventEmitter()
@Output() treeNodeEnter : EventEmitter<any> = new EventEmitter()
@Output() treeNodeLeave : EventEmitter<any> = new EventEmitter() /* highly non-performant. rerenders each time on mouseover or mouseout */
// @Output() treeNodeEnter : EventEmitter<any> = new EventEmitter()
// @Output() treeNodeLeave : EventEmitter<any> = new EventEmitter()
@Input() renderNode : (item:any)=>string = (item)=>item.name @Input() renderNode : (item:any)=>string = (item)=>item.name
@Input() findChildren : (item:any)=>any[] = (item)=>item.children ? item.children : [] @Input() findChildren : (item:any)=>any[] = (item)=>item.children ? item.children : []
@Input() searchFilter : (item:any)=>boolean | null = ()=>true @Input() searchFilter : (item:any)=>boolean | null = ()=>true
constructor(){
// this.searchedVisibleSet = this.searchFilter()
}
getClass(level:number){ getClass(level:number){
// return `render-node-level-${level}`
return [...Array(level+1)].map((v,idx) => `render-node-level-${idx}`).join(' ') return [...Array(level+1)].map((v,idx) => `render-node-level-${idx}`).join(' ')
} }
...@@ -58,8 +55,4 @@ export class FlatTreeComponent{ ...@@ -58,8 +55,4 @@ export class FlatTreeComponent{
? true ? true
: false : false
} }
filterRows(items:any[], ){
}
} }
\ No newline at end of file
<div <div
*ngFor = "let flattenedItem of (inputItem | flattenTreePipe : findChildren | filterRowsByVisbilityPipe : findChildren : searchFilter)" *ngFor = "let flattenedItem of (inputItem | flattenTreePipe : findChildren | filterRowsByVisbilityPipe : findChildren : searchFilter | appendSiblingFlagPipe)"
[ngClass] = "getClass(flattenedItem.flattenedTreeLevel)" [ngClass] = "getClass(flattenedItem.flattenedTreeLevel)"
[attr.flattenedtreelevel] = "flattenedItem.flattenedTreeLevel" [attr.flattenedtreelevel] = "flattenedItem.flattenedTreeLevel"
[attr.collapsed] = "flattenedItem.collapsed ? flattenedItem.collapsed : false" [attr.collapsed] = "flattenedItem.collapsed ? flattenedItem.collapsed : false"
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
</span> </span>
<span <span
*ngIf = "findChildren(flattenedItem).length > 0; else noChildren" *ngIf = "findChildren(flattenedItem).length > 0; else noChildren"
(click) = "toggleCollapse(flattenedItem)" > (click) = "$event.stopPropagation(); toggleCollapse(flattenedItem)" >
<i [ngClass] = "isCollapsed(flattenedItem) ? 'glyphicon-chevron-right' : 'glyphicon-chevron-down'" class="glyphicon"></i> <i [ngClass] = "isCollapsed(flattenedItem) ? 'glyphicon-chevron-right' : 'glyphicon-chevron-down'" class="glyphicon"></i>
</span> </span>
<ng-template #noChildren> <ng-template #noChildren>
...@@ -26,9 +26,7 @@ ...@@ -26,9 +26,7 @@
</i> </i>
</ng-template> </ng-template>
<span <span
(mouseenter) = "treeNodeEnter.emit(flattenedItem)" (click) = "treeNodeClick.emit({event:$event,inputItem:flattenedItem})"
(mouseleave) = "treeNodeLeave.emit(flattenedItem)"
(click) = "treeNodeClick.emit(flattenedItem)"
class = "render-node-text" class = "render-node-text"
[innerHtml] = "flattenedItem | renderPipe : renderNode "> [innerHtml] = "flattenedItem | renderPipe : renderNode ">
</span> </span>
......
...@@ -6,29 +6,27 @@ import { Pipe, PipeTransform } from "@angular/core"; ...@@ -6,29 +6,27 @@ import { Pipe, PipeTransform } from "@angular/core";
export class FlattenTreePipe implements PipeTransform{ export class FlattenTreePipe implements PipeTransform{
public transform(root:any, findChildren: (root:any) => any[]):any&FlattenedTreeInterface[]{ public transform(root:any, findChildren: (root:any) => any[]):any&FlattenedTreeInterface[]{
return this.recursiveFlatten(root,findChildren,0, '0', []) return this.recursiveFlatten(root,findChildren,0, '0')
} }
private recursiveFlatten(obj, findChildren, flattenedTreeLevel, lvlId, siblingFlags){ private recursiveFlatten(obj, findChildren, flattenedTreeLevel, lvlId){
return [ return [
this.attachLvlAndLvlIdAndSiblingFlag( this.attachLvlAndLvlIdAndSiblingFlag(
obj, obj,
flattenedTreeLevel, flattenedTreeLevel,
lvlId, lvlId
siblingFlags
) )
].concat( ].concat(
...findChildren(obj) ...findChildren(obj)
.map((c,idx,arr) => this.recursiveFlatten(c,findChildren,flattenedTreeLevel + 1, `${lvlId}_${idx}`, siblingFlags.concat( idx === (arr.length - 1)) )) .map((c,idx) => this.recursiveFlatten(c,findChildren,flattenedTreeLevel + 1, `${lvlId}_${idx}` ))
) )
} }
private attachLvlAndLvlIdAndSiblingFlag(obj:any, flattenedTreeLevel:number, lvlId:string, siblingFlags : boolean[]){ private attachLvlAndLvlIdAndSiblingFlag(obj:any, flattenedTreeLevel:number, lvlId:string){
return Object.assign({}, obj,{ return Object.assign({}, obj,{
flattenedTreeLevel, flattenedTreeLevel,
collapsed : typeof obj.collapsed === 'undefined' ? false : true, collapsed : typeof obj.collapsed === 'undefined' ? false : true,
lvlId, lvlId
siblingFlags
}) })
} }
......
import { Pipe, PipeTransform } from "@angular/core";
@Pipe({
name : 'hasVisibleChildrenPipe'
})
export class HasVisibleChildrenPipe implements PipeTransform{
public transform(item:any, getChildren: (item:any)=>any[], filterFn: (item:any)=>boolean){
return filterFn(item) || getChildren(item).some(c => this.transform(c, getChildren, filterFn))
}
}
\ No newline at end of file
import { Component, OnDestroy, ChangeDetectionStrategy } from "@angular/core"; import { Component, OnDestroy, ChangeDetectionStrategy, HostListener, ViewChild, ElementRef } from "@angular/core";
import { Store, select } from "@ngrx/store"; import { Store, select } from "@ngrx/store";
import { ViewerStateInterface, safeFilter, SELECT_PARCELLATION, extractLabelIdx, SELECT_REGIONS, NEWVIEWER, getLabelIndexMap, isDefined, CHANGE_NAVIGATION } from "../../services/stateStore.service"; 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 { Observable, Subscription, merge, Subject } from "rxjs";
...@@ -106,7 +106,6 @@ export class AtlasBanner implements OnDestroy{ ...@@ -106,7 +106,6 @@ export class AtlasBanner implements OnDestroy{
} }
handleParcellationChange(parcellation){ handleParcellationChange(parcellation){
// const selectedParcellation = this.selectedTemplate.parcellations.find(p=>p.ngId === parcellation.ngId)
this.selectedParcellation = parcellation this.selectedParcellation = parcellation
this.regionsLabelIndexMap = getLabelIndexMap(parcellation.regions) this.regionsLabelIndexMap = getLabelIndexMap(parcellation.regions)
} }
...@@ -168,7 +167,29 @@ export class AtlasBanner implements OnDestroy{ ...@@ -168,7 +167,29 @@ export class AtlasBanner implements OnDestroy{
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)
return
this.searchTerm = event.target.value
}
@ViewChild('searchRegionPopover', {read:ElementRef}) inputRegionPopover : ElementRef
public showRegionTree: boolean
@HostListener('document:click',['$event'])
closeRegion(event:MouseEvent){
/* FF does not implement event.srcElement so use event.originalTarget to polyfill for FF */
const contains = 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){ handleClickRegion(obj:any){
obj.event.stopPropagation()
this.handleRegionTreeClickSubject.next(obj) this.handleRegionTreeClickSubject.next(obj)
} }
......
...@@ -59,6 +59,7 @@ div[hideScrollbarcontainer] ...@@ -59,6 +59,7 @@ div[hideScrollbarcontainer]
{ {
width: 20em; width: 20em;
overflow:hidden; overflow:hidden;
margin-top:2px;
} }
div[searchRegionPopover] div[searchRegionPopover]
......
<span hbpLogoContainer> <span hbpLogoContainer>
<img src = "res/image/HBP_Primary_RGB_WhiteText.png" /> <img src = "res/image/HBP_Primary_RGB_WhiteText.png" />
</span> </span>
...@@ -21,40 +20,38 @@ ...@@ -21,40 +20,38 @@
<div <div
*ngIf = "selectedTemplate" *ngIf = "selectedTemplate"
placement = "bottom" #searchRegionPopover
[outsideClick] = "true"
[popover]="searchRegionTemplate"
containerClass = "inputSearchContainer"
triggers = ""
#inputSearchRegionPopover = "bs-popover"
searchRegionPopover> searchRegionPopover>
<input <input
(keydown.esc)="inputSearchRegionPopover.hide();$event.target.blur();searchTerm=''" (keydown.esc) = "showRegionTree = false; $event.target.blur(); searchTerm = ''"
[(ngModel)]="searchTerm" (focus) = "showRegionTree = true"
[value] = "searchTerm"
(input) = "changeSearchTerm($event)"
class = "form-control" class = "form-control"
(click)="inputSearchRegionPopover.show()"
type="text" type="text"
placeholder="Regions"/> placeholder="Regions"/>
</div> <div
</div> [@showState]
*ngIf = "showRegionTree"
hideScrollbarContainer>
<ng-template #searchRegionTemplate> <div treeContainer>
<div [@showState] hideScrollbarContainer> <div treeHeader>
<div treeContainer> <span>{{ selectedRegions.length }} {{ selectedRegions.length > 1 ? 'regions' : 'region' }} selected</span>
<div treeHeader> <span (click) = "clearRegions($event)" *ngIf = "selectedRegions.length > 0" class = "btn btn-link">clear all</span>
<span>{{ selectedRegions.length }} {{ selectedRegions.length > 1 ? 'regions' : 'region' }} selected</span> </div>
<span (click) = "clearRegions($event)" *ngIf = "selectedRegions.length > 0" class = "btn btn-link">clear all</span>
</div>
<flat-tree-component
*ngFor = "let child of selectedParcellation.regions "
(treeNodeClick) = "handleClickRegion({inputItem:$event})"
[inputItem] = "child"
[renderNode] = "displayTreeNode.bind(this)"
[searchFilter] = "filterTreeBySearch.bind(this)">
</flat-tree-component> <flat-tree-component
*ngFor = "let child of selectedParcellation.regions "
(treeNodeClick) = "handleClickRegion($event)"
[inputItem] = "child"
[renderNode] = "displayTreeNode.bind(this)"
[searchFilter] = "filterTreeBySearch.bind(this)">
</flat-tree-component>
</div>
</div> </div>
</div> </div>
</ng-template> </div>
\ No newline at end of file
...@@ -14,9 +14,14 @@ export const regionAnimation = trigger('showState',[ ...@@ -14,9 +14,14 @@ export const regionAnimation = trigger('showState',[
}) })
), ),
transition('* => void', [ transition('* => void', [
animate('2300ms ease-in') animate('230ms ease-in')
]), ]),
transition('void => *',[ transition('void => *',[
animate('230ms ease-out') style({
opacity : '0.0',
}),
animate('420ms ease-in', style({
opacity : '1.0'
}))
]) ])
]) ])
\ No newline at end of file
...@@ -24,7 +24,6 @@ import { LandmarkUnit } from "./nehubaContainer/landmarkUnit/landmarkUnit.compon ...@@ -24,7 +24,6 @@ import { LandmarkUnit } from "./nehubaContainer/landmarkUnit/landmarkUnit.compon
import { SafeStylePipe } from "../util/pipes/safeStyle.pipe"; import { SafeStylePipe } from "../util/pipes/safeStyle.pipe";
import { PluginBannerUI } from "./pluginBanner/pluginBanner.component"; import { PluginBannerUI } from "./pluginBanner/pluginBanner.component";
import { AtlasBanner } from "./banner/banner.component"; import { AtlasBanner } from "./banner/banner.component";
import { PopoverModule } from "ngx-bootstrap/popover";
import { CitationsContainer } from "./citation/citations.component"; import { CitationsContainer } from "./citation/citations.component";
import { LayerBrowser } from "./layerbrowser/layerbrowser.component"; import { LayerBrowser } from "./layerbrowser/layerbrowser.component";
import { TooltipModule } from "ngx-bootstrap/tooltip"; import { TooltipModule } from "ngx-bootstrap/tooltip";
...@@ -42,8 +41,7 @@ import { SortDataEntriesToRegion } from "../util/pipes/sortDataEntriesIntoRegion ...@@ -42,8 +41,7 @@ import { SortDataEntriesToRegion } from "../util/pipes/sortDataEntriesIntoRegion
LayoutModule, LayoutModule,
ComponentsModule, ComponentsModule,
TooltipModule.forRoot(), TooltipModule.forRoot()
PopoverModule.forRoot()
], ],
declarations : [ declarations : [
NehubaContainer, NehubaContainer,
......
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment