From e0fa37bd79f9a8dffd05b3e5b81e921827830140 Mon Sep 17 00:00:00 2001 From: Xiao Gui <xgui3783@gmail.com> Date: Fri, 20 Jul 2018 12:06:57 +0200 Subject: [PATCH] rework components, refactor + improvements on performanc --- package.json | 2 + src/atlasViewerExports/export.html | 9 +- src/atlasViewerExports/export.module.ts | 14 +-- src/atlasViewerExports/main.export.aot.ts | 6 + src/components/components.module.ts | 10 +- .../componentsExample.component.ts | 61 ---------- .../componentsExample.style.css | 11 -- .../componentsExample.template.html | 77 ------------- src/components/panel/panel.component.ts | 19 +++- src/components/panel/panel.template.html | 3 +- src/components/parseAttribute.directive.ts | 71 ++++++++++++ .../parseAttribute.ts} | 0 src/components/tree/tree.animation.ts | 6 +- src/components/tree/tree.component.ts | 105 +++++++++++++++--- src/components/tree/tree.template.html | 18 +-- src/components/tree/treeBase.directive.ts | 54 +++++++++ src/components/tree/treeService.service.ts | 17 +++ src/layouts/mainside/mainside.template.html | 2 +- src/main.ts | 3 +- src/res/css/extra_styles.css | 1 + src/ui/banner/banner.component.ts | 17 ++- src/ui/banner/banner.style.css | 31 ++++++ src/ui/banner/banner.template.html | 9 +- src/ui/databrowser/databrowser.template.html | 7 +- src/ui/fileviewer/fileviewer.style.css | 5 + .../nehubaContainer.component.ts | 68 ++++++------ .../nehubaContainer.template.html | 3 +- .../nehubaViewer/nehubaViewer.component.ts | 4 +- webpack.export.aot.js | 47 ++++++++ webpack.export.js | 1 + webpack.export.min.js | 26 +++++ webpack.ngassets.js | 10 +- webpack.staticassets.js | 16 +++ 33 files changed, 481 insertions(+), 252 deletions(-) create mode 100644 src/atlasViewerExports/main.export.aot.ts delete mode 100644 src/components/componentsExample/componentsExample.component.ts delete mode 100644 src/components/componentsExample/componentsExample.style.css delete mode 100644 src/components/componentsExample/componentsExample.template.html create mode 100644 src/components/parseAttribute.directive.ts rename src/{atlasViewer/widgetUnit/widgetDirective.directive.ts => components/parseAttribute.ts} (100%) create mode 100644 src/components/tree/treeBase.directive.ts create mode 100644 src/components/tree/treeService.service.ts create mode 100644 webpack.export.aot.js create mode 100644 webpack.export.min.js create mode 100644 webpack.staticassets.js diff --git a/package.json b/package.json index 70aef493c..56fd60bec 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,8 @@ "scripts": { "dev-server-export": "webpack-dev-server --config webpack.export.js", "build-export": "webpack --config webpack.export.js", + "build-export-min": "webpack --config webpack.export.min.js", + "build-export-aot": "webpack --config webpack.export.aot.js", "build-aot": "webpack --config webpack.aot.js", "build-min": "webpack --config webpack.prod.js", "build": "webpack --config webpack.dev.js", diff --git a/src/atlasViewerExports/export.html b/src/atlasViewerExports/export.html index 88c7be942..b4a41a088 100644 --- a/src/atlasViewerExports/export.html +++ b/src/atlasViewerExports/export.html @@ -39,7 +39,10 @@ tree.setAttribute('input-item',JSON.stringify(inputItem)) ` const treeSampleBox = document.getElementById('tree-element-sample-box') treeSampleBox.setAttribute('script-input',script) - eval(script); + eval(script) + + const tree2 = document.getElementById('tree-element') + tree2.addEventListener('mouseclicktree',(ev)=>console.warn(ev)) const markdownIncludeString = ` You will also need \`bootstrap 3.3.7\` for formatting and \`custom-elements-es5-adapter\` for es5 shim: @@ -138,7 +141,7 @@ console.log('GOTCHA') </div> <div class = "col-md-3"> <sample-box sample-box-title = "panel component"> -<panel-element body-collapsable = "true" show-footer = "true"> +<panel-element collapse-body = "false" body-collapsable = "true" show-body = "true" show-footer = "true" iv-parse-attribute> <div style = "padding: 0.5em;" heading> panel heading </div> @@ -153,7 +156,7 @@ console.log('GOTCHA') </div> <div class = "col-md-3"> <sample-box sample-box-title = "tree component" id = "tree-element-sample-box"> -<tree-element id = 'tree-element'> +<tree-element children-expanded = "true" id = 'tree-element' treebase> </tree-element> </sample-box> diff --git a/src/atlasViewerExports/export.module.ts b/src/atlasViewerExports/export.module.ts index d17b61f71..2952924ea 100644 --- a/src/atlasViewerExports/export.module.ts +++ b/src/atlasViewerExports/export.module.ts @@ -12,25 +12,23 @@ import { PanelComponent } from "../components/panel/panel.component"; import { HoverableBlockDirective } from "../components/hoverableBlock.directive"; import { TreeComponent } from "../components/tree/tree.component"; import { TreeSearchPipe } from "../util/pipes/treeSearch.pipe"; +import { TreeBaseDirective } from "../components/tree/treeBase.directive"; +import { ParseAttributeDirective } from "../components/parseAttribute.directive"; +import { ComponentsModule } from "../components/components.module"; @NgModule({ imports : [ BrowserModule, BrowserAnimationsModule, FormsModule, + ComponentsModule, BsDropdownModule.forRoot() ], declarations : [ SampleBoxUnit, - ReadmoreComponent, - MarkdownDom, - PanelComponent, - TreeComponent, - - SafeHtmlPipe, - HoverableBlockDirective, - TreeSearchPipe + /* parse element attributes from string to respective datatypes */ + ParseAttributeDirective ], entryComponents : [ SampleBoxUnit, diff --git a/src/atlasViewerExports/main.export.aot.ts b/src/atlasViewerExports/main.export.aot.ts new file mode 100644 index 000000000..754ac3bdb --- /dev/null +++ b/src/atlasViewerExports/main.export.aot.ts @@ -0,0 +1,6 @@ +import 'zone.js' + +import { platformBrowserDynamic } from "@angular/platform-browser-dynamic"; +import { ExportModule } from "./export.module"; + +platformBrowserDynamic().bootstrapModule(ExportModule) \ No newline at end of file diff --git a/src/components/components.module.ts b/src/components/components.module.ts index d85c8b202..0ce465df0 100644 --- a/src/components/components.module.ts +++ b/src/components/components.module.ts @@ -5,7 +5,6 @@ import { BrowserAnimationsModule } from '@angular/platform-browser/animations' import { BsDropdownModule } from 'ngx-bootstrap/dropdown'; import { MarkdownDom } from './markdown/markdown.component'; -import { ComponentsExample } from './componentsExample/componentsExample.component'; import { SafeHtmlPipe } from '../util/pipes/safeHtml.pipe' import { ReadmoreComponent } from './readmoore/readmore.component'; @@ -18,6 +17,8 @@ import { PaginationComponent } from './pagination/pagination.component'; import { SearchResultPaginationPipe } from '../util/pipes/pagination.pipe'; import { ToastComponent } from './toast/toast.component'; import { TreeSearchPipe } from '../util/pipes/treeSearch.pipe'; +import { TreeBaseDirective } from './tree/treeBase.directive'; + @NgModule({ imports : [ @@ -29,7 +30,6 @@ import { TreeSearchPipe } from '../util/pipes/treeSearch.pipe'; declarations : [ /* components */ MarkdownDom, - ComponentsExample, ReadmoreComponent, DropdownComponent, TreeComponent, @@ -39,11 +39,12 @@ import { TreeSearchPipe } from '../util/pipes/treeSearch.pipe'; /* directive */ HoverableBlockDirective, + TreeBaseDirective, /* pipes */ SafeHtmlPipe, SearchResultPaginationPipe, - TreeSearchPipe + TreeSearchPipe, ], exports : [ BrowserAnimationsModule, @@ -60,8 +61,7 @@ import { TreeSearchPipe } from '../util/pipes/treeSearch.pipe'; TreeSearchPipe, HoverableBlockDirective, - - ComponentsExample, + TreeBaseDirective, ] }) diff --git a/src/components/componentsExample/componentsExample.component.ts b/src/components/componentsExample/componentsExample.component.ts deleted file mode 100644 index c37f826da..000000000 --- a/src/components/componentsExample/componentsExample.component.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { Component } from "@angular/core"; - -@Component({ - selector : 'components-exmample', - templateUrl : `./componentsExample.template.html`, - styleUrls : [ - `./componentsExample.style.css` - ] -}) - -export class ComponentsExample{ - markdownInput : string = `Heading ------- - -**bold** - -~~strikeout~~ - -normal paragraph - -[google](https://www.google.com)` - - markdownCode :string = ` -html -\`\`\`html -<markdown-dom id = "markdown-dom1"> -</markdown-dom> -\`\`\` -javascript -\`\`\`javascript -const mdDom = document.getElementById('markdown-dom1') -mdDom.markdown = \` -MD text here ------- -\` -\`\`\` - -` - markdownText : string = ` -mardown2Html -====== -utilising [showdownjs](https://github.com/showdownjs/showdown). Note the [XSS vulnerability](https://github.com/showdownjs/showdown/wiki/Markdown's-XSS-Vulnerability-(and-how-to-mitigate-it)). -` - readmoreInput : string = `Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.` - readmoreSampleCode : string = ` - -html -\`\`\`html -<readmore - collapsedHeight = 45 - show = false> -Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. -</readmore> -\`\`\` -` - readmoreText : string = ` -readmore -====== -hide/show more. Useful for the display of large block of text. -` -} \ No newline at end of file diff --git a/src/components/componentsExample/componentsExample.style.css b/src/components/componentsExample/componentsExample.style.css deleted file mode 100644 index ab40d12a3..000000000 --- a/src/components/componentsExample/componentsExample.style.css +++ /dev/null @@ -1,11 +0,0 @@ -.cell -{ - max-height:20em; -} - -textarea -{ - resize:none; - width:100%; - height:20em; -} diff --git a/src/components/componentsExample/componentsExample.template.html b/src/components/componentsExample/componentsExample.template.html deleted file mode 100644 index 2e0389ba2..000000000 --- a/src/components/componentsExample/componentsExample.template.html +++ /dev/null @@ -1,77 +0,0 @@ -<div class = "container-fluid"> - - <div class = "row"> - <div class = "col-md-12 cell"> - <markdown-dom [markdown] = "markdownText"> - - </markdown-dom> - </div> - </div> - <div class = "row"> - <div class = "col-md-8 cell"> - try a demo: - </div> - <div class = "col-md-4 cell"> - code sample for implementation: - </div> - </div> - <div class = "row"> - <div class = "col-md-4 cell"> - <textarea [(ngModel)] = "markdownInput"> - </textarea> - </div> - - <div class = "col-md-4 cell"> - <div class = "well"> - <markdown-dom - [markdown]="markdownInput"> - </markdown-dom> - </div> - </div> - - <div class = "col-md-4 cell"> - <div class = "well"> - <markdown-dom - [markdown]="markdownCode"> - </markdown-dom> - </div> - </div> - </div> - - <div class = "row"> - <div class = "col-md-12 cell"> - <markdown-dom [markdown] = "readmoreText"> - - </markdown-dom> - </div> - </div> - <div class = "row"> - <div class = "col-md-8 cell"> - try a demo: - </div> - <div class = "col-md-4 cell"> - code sample for implementation: - </div> - </div> - <div class = "row"> - <div class = "col-md-4 cell"> - <textarea [(ngModel)] = "readmoreInput"> - - </textarea> - </div> - <div class = "col-md-4 cell"> - - <readmore> - {{ readmoreInput }} - </readmore> - </div> - <div class = "col-md-4 cell"> - <div class = "well"> - - <markdown-dom [markdown] = "readmoreSampleCode"> - - </markdown-dom> - </div> - </div> - </div> - </div> \ No newline at end of file diff --git a/src/components/panel/panel.component.ts b/src/components/panel/panel.component.ts index e030dc5bc..388989548 100644 --- a/src/components/panel/panel.component.ts +++ b/src/components/panel/panel.component.ts @@ -1,5 +1,6 @@ -import { Component, Input, ViewChild, ElementRef, AfterContentChecked } from "@angular/core"; +import { Component, Input, ViewChild, ElementRef, AfterContentChecked, ChangeDetectionStrategy, ChangeDetectorRef, OnChanges, SimpleChanges, HostBinding, ApplicationRef } from "@angular/core"; import { panelAnimations } from "./panel.animation"; +import { ParseAttributeDirective } from "../parseAttribute.directive"; @Component({ selector : 'panel', @@ -9,10 +10,11 @@ import { panelAnimations } from "./panel.animation"; ], animations : [ panelAnimations - ] + ], + changeDetection:ChangeDetectionStrategy.OnPush }) -export class PanelComponent implements AfterContentChecked{ +export class PanelComponent extends ParseAttributeDirective implements AfterContentChecked{ @Input() showHeading : boolean = true @Input() showBody : boolean = true @@ -28,9 +30,14 @@ export class PanelComponent implements AfterContentChecked{ _fullHeight : number = 0 + constructor(private cdr:ChangeDetectorRef){ + super() + } + ngAfterContentChecked(){ this.fullHeight = (this.efPanelBody ? this.efPanelBody.nativeElement.offsetHeight : 0) + (this.efPanelFooter ? this.efPanelFooter.nativeElement.offsetHeight : 0) + this.cdr.detectChanges() } set fullHeight(num:number){ @@ -45,10 +52,10 @@ export class PanelComponent implements AfterContentChecked{ if(this.bodyCollapsable){ this.collapseBody = !this.collapseBody - this.fullHeight = (this.efPanelBody ? this.efPanelBody.nativeElement.offsetHeight : 0) + - (this.efPanelFooter ? this.efPanelFooter.nativeElement.offsetHeight : 0) + // this.fullHeight = (this.efPanelBody ? this.efPanelBody.nativeElement.offsetHeight : 0) + + // (this.efPanelFooter ? this.efPanelFooter.nativeElement.offsetHeight : 0) } event.stopPropagation() event.preventDefault() } -} \ No newline at end of file +} diff --git a/src/components/panel/panel.template.html b/src/components/panel/panel.template.html index c6bab147f..bbdf5f62b 100644 --- a/src/components/panel/panel.template.html +++ b/src/components/panel/panel.template.html @@ -10,7 +10,8 @@ </ng-content> </div> - <div bodyFooterContainer> + <div + bodyFooterContainer> <div *ngIf = "showBody" class = "panel-body" diff --git a/src/components/parseAttribute.directive.ts b/src/components/parseAttribute.directive.ts new file mode 100644 index 000000000..257686723 --- /dev/null +++ b/src/components/parseAttribute.directive.ts @@ -0,0 +1,71 @@ +import { Directive, OnChanges, SimpleChanges } from "@angular/core"; + +function parseAttribute(arg:any,expectedType:string){ + + // if( + // typeof arg === expectedType || + // arg === undefined || + // arg === null + // ){ + // return arg + // } + // switch (expectedType){ + // case 'object': + // try{ + // const json = JSON.parse(arg) + // return json + // }catch(e){ + // console.warn('parseAttribute error, cannot JSON.parse object') + // return arg + // } + // case 'boolean' : + // return arg === 'true' + // case 'number': + // return isNaN(arg) ? 0 : Number(arg) + + // case 'string': + // default : + // return arg + // } + + /* return if empty string */ + if( + arg === '' || + arg === undefined || + arg === null + ) + return arg + + if(!isNaN(arg)){ + return Number(arg) + } + + if(arg === 'true') + return true + + if(arg === 'false') + return false + + try{ + const json = JSON.parse(arg) + return json + }catch(e){ + // console.warn('parseAttribute, parse JSON, not a json') + /* not a json, continue */ + /* probably print in debug mode */ + } + + return arg +} + +@Directive({ + selector : '[ivparseattribute]', +}) + +export class ParseAttributeDirective implements OnChanges{ + ngOnChanges(simpleChanges:SimpleChanges){ + Object.keys(simpleChanges).forEach(key=>{ + this[key] = parseAttribute(simpleChanges[key].currentValue,typeof simpleChanges[key].previousValue) + }) + } +} diff --git a/src/atlasViewer/widgetUnit/widgetDirective.directive.ts b/src/components/parseAttribute.ts similarity index 100% rename from src/atlasViewer/widgetUnit/widgetDirective.directive.ts rename to src/components/parseAttribute.ts diff --git a/src/components/tree/tree.animation.ts b/src/components/tree/tree.animation.ts index b346e34b6..9cb6afa3c 100644 --- a/src/components/tree/tree.animation.ts +++ b/src/components/tree/tree.animation.ts @@ -4,13 +4,13 @@ import { trigger, state, style, transition, animate } from "@angular/animations" export const treeAnimations = trigger('collapseState',[ state('collapsed', style({ - 'margin-top' : '-{{ fullHeight }}px' + 'margin-top' : '-{{ fullHeight }}px', }), - { params : { fullHeight : 9999 } } + { params : { fullHeight : 0 } } ), state('visible', style({ - 'margin-top' : '0px' + 'margin-top' : '0px', }), { params : { fullHeight : 0 } } ), diff --git a/src/components/tree/tree.component.ts b/src/components/tree/tree.component.ts index 3c19da127..4928c29c5 100644 --- a/src/components/tree/tree.component.ts +++ b/src/components/tree/tree.component.ts @@ -1,5 +1,8 @@ -import { Component, Input, Output, EventEmitter, ViewChildren, QueryList, HostBinding, ChangeDetectionStrategy, OnChanges, AfterContentChecked, ViewChild, ElementRef } from "@angular/core"; +import { Component, Input, Output, EventEmitter, ViewChildren, QueryList, HostBinding, ChangeDetectionStrategy, OnChanges, AfterContentChecked, ViewChild, ElementRef, Optional, OnInit, ChangeDetectorRef, OnDestroy } from "@angular/core"; import { treeAnimations } from "./tree.animation"; +import { TreeService } from "./treeService.service"; +import { Subscription } from "rxjs"; +import { ParseAttributeDirective } from "../parseAttribute.directive"; @Component({ @@ -14,17 +17,13 @@ import { treeAnimations } from "./tree.animation"; changeDetection:ChangeDetectionStrategy.OnPush }) -export class TreeComponent implements OnChanges,AfterContentChecked{ +export class TreeComponent extends ParseAttributeDirective implements OnChanges,OnInit,OnDestroy,AfterContentChecked{ @Input() inputItem : any = { name : 'Untitled', children : [] } - @Input() renderNode : (item:any)=>string = (item)=>item.name - @Input() findChildren : (item:any)=>any[] = (item)=>item.children @Input() childrenExpanded : boolean = true - @Input() searchFilter : (item:any)=>boolean | null = ()=>true - @Output() mouseentertree : EventEmitter<any> = new EventEmitter() @Output() mouseleavetree : EventEmitter<any> = new EventEmitter() @Output() mouseclicktree : EventEmitter<any> = new EventEmitter() @@ -32,16 +31,64 @@ export class TreeComponent implements OnChanges,AfterContentChecked{ @ViewChildren(TreeComponent) treeChildren : QueryList<TreeComponent> @ViewChild('childrenContainer',{ read : ElementRef }) childrenContainer : ElementRef - ngOnChanges(){ - if(typeof this.inputItem === 'string'){ - this.inputItem = JSON.parse(this.inputItem) + constructor( + private cdr : ChangeDetectorRef, + @Optional() public treeService : TreeService ){ + super() + } + + subscriptions : Subscription[] = [] + + ngOnInit(){ + if( this.treeService ){ + this.subscriptions.push( + this.treeService.markForCheck.subscribe(()=>this.cdr.markForCheck()) + ) } } - fullHeight : number = 100 + + ngOnDestroy(){ + this.subscriptions.forEach(s=>s.unsubscribe()) + } + + _fullHeight : number = 9999 + + set fullHeight(num:number){ + this._fullHeight = num + } + + get fullHeight(){ + return this._fullHeight + } ngAfterContentChecked(){ this.fullHeight = this.childrenContainer ? this.childrenContainer.nativeElement.offsetHeight : 0 + this.cdr.detectChanges() + } + + mouseenter(ev:MouseEvent){ + this.treeService.mouseenter.next({ + inputItem : this.inputItem, + node : this, + event : ev + }) + } + + mouseleave(ev:MouseEvent){ + this.treeService.mouseleave.next({ + inputItem : this.inputItem, + node : this, + event : ev + }) + } + + mouseclick(ev:MouseEvent){ + this.treeService.mouseclick.next({ + inputItem : this.inputItem, + node : this, + event : ev + }) } get chevronClass():string{ @@ -66,13 +113,43 @@ export class TreeComponent implements OnChanges,AfterContentChecked{ } get children():any[]{ - return this.findChildren(this.inputItem) + return this.treeService ? + this.treeService.findChildren(this.inputItem) : + this.inputItem.children } @HostBinding('attr.filterHidden') get visibilityOnFilter():boolean{ - return this.searchFilter ? - this.searchFilter(this.inputItem) : - false + return this.treeService ? + this.treeService.searchFilter(this.inputItem) : + true + } + handleMouseEnter(fullObj:any){ + + this.mouseentertree.emit(fullObj) + + if(this.treeService){ + this.treeService.mouseenter.next(fullObj) + } } + + handleMouseLeave(fullObj:any){ + + this.mouseleavetree.emit(fullObj) + + if(this.treeService){ + this.treeService.mouseleave.next(fullObj) + } + } + + handleMouseClick(fullObj:any){ + + this.mouseclicktree.emit(fullObj) + + if(this.treeService){ + this.treeService.mouseclick.next(fullObj) + } + } + + public defaultSearchFilter = ()=>true } \ No newline at end of file diff --git a/src/components/tree/tree.template.html b/src/components/tree/tree.template.html index 9d918d9a8..bc4f3c0b0 100644 --- a/src/components/tree/tree.template.html +++ b/src/components/tree/tree.template.html @@ -8,10 +8,10 @@ glyphicon> </i> <span - (mouseleave)="mouseleavetree.emit({inputItem:inputItem,node:this});handleEv($event)" - (mouseenter)="mouseentertree.emit({inputItem:inputItem,node:this});handleEv($event)" - (click)="mouseclicktree.emit({inputItem:inputItem,node:this});handleEv($event)" - [innerHTML] = "renderNode(inputItem)" + (mouseleave)="handleMouseLeave({inputItem:inputItem,node:this});handleEv($event)" + (mouseenter)="handleMouseEnter({inputItem:inputItem,node:this});handleEv($event)" + (click)="handleMouseClick({inputItem:inputItem,node:this});handleEv($event)" + [innerHTML] = "treeService ? treeService.renderNode(inputItem) : inputItem.name" itemName> </span> </div> @@ -20,15 +20,9 @@ [@collapseState] = "{ value : childrenExpanded ? 'visible' : 'collapsed' , params : { fullHeight : fullHeight }}" #childrenContainer> <tree - *ngFor = "let child of (findChildren(inputItem) | treeSearch : searchFilter : findChildren )" + *ngFor = "let child of ( treeService ? (treeService.findChildren(inputItem) | treeSearch : treeService.searchFilter : treeService.findChildren ) : inputItem.children )" [childrenExpanded] = "childrenExpanded" - [inputItem] = "child" - [renderNode]="renderNode" - [searchFilter]="searchFilter" - [findChildren] = "findChildren" - (mouseentertree)="mouseentertree.emit($event)" - (mouseleavetree)="mouseleavetree.emit($event)" - (mouseclicktree)="mouseclicktree.emit($event)"> + [inputItem] = "child"> </tree> </div> diff --git a/src/components/tree/treeBase.directive.ts b/src/components/tree/treeBase.directive.ts new file mode 100644 index 000000000..20869d5df --- /dev/null +++ b/src/components/tree/treeBase.directive.ts @@ -0,0 +1,54 @@ +import { Directive, Output, EventEmitter, OnDestroy, Input, OnChanges, ChangeDetectorRef } from "@angular/core"; +import { TreeService } from "./treeService.service"; +import { Subscription } from "rxjs"; + +@Directive({ + selector : '[treebase]', + host :{ + 'style' : ` + background-color:red + ` + }, + providers : [ + TreeService + ] +}) + +export class TreeBaseDirective implements OnDestroy, OnChanges{ + @Output() treeNodeClick : EventEmitter<any> = new EventEmitter() + @Output() treeNodeEnter : EventEmitter<any> = new EventEmitter() + @Output() treeNodeLeave : EventEmitter<any> = new EventEmitter() + + @Input() renderNode : (item:any)=>string = (item)=>item.name + @Input() findChildren : (item:any)=>any[] = (item)=>item.children + @Input() searchFilter : (item:any)=>boolean | null = ()=>true + + private subscriptions : Subscription[] = [] + + constructor( + public changeDetectorRef : ChangeDetectorRef, + public treeService : TreeService + ){ + this.subscriptions.push( + this.treeService.mouseclick.subscribe((obj)=>this.treeNodeClick.emit(obj)) + ) + this.subscriptions.push( + this.treeService.mouseenter.subscribe((obj)=>this.treeNodeEnter.emit(obj)) + ) + this.subscriptions.push( + this.treeService.mouseleave.subscribe((obj)=>this.treeNodeLeave.emit(obj)) + ) + } + + ngOnChanges(){ + this.treeService.findChildren = this.findChildren + this.treeService.renderNode = this.renderNode + this.treeService.searchFilter = this.searchFilter + + this.treeService.markForCheck.next(true) + } + + ngOnDestroy(){ + this.subscriptions.forEach(s=>s.unsubscribe()) + } +} \ No newline at end of file diff --git a/src/components/tree/treeService.service.ts b/src/components/tree/treeService.service.ts new file mode 100644 index 000000000..dc659b94e --- /dev/null +++ b/src/components/tree/treeService.service.ts @@ -0,0 +1,17 @@ +import { Injectable } from "@angular/core"; +import { Subject } from "rxjs"; + +@Injectable() +export class TreeService{ + mouseclick : Subject<any> = new Subject() + mouseenter : Subject<any> = new Subject() + mouseleave : Subject<any> = new Subject() + + findChildren : (item:any)=>any[] = (item)=>item.children + searchFilter : (item:any)=>boolean | null = ()=>true + renderNode : (item:any)=>string = (item)=>item.name + + searchTerm : string = `` + + markForCheck : Subject<any> = new Subject() +} \ No newline at end of file diff --git a/src/layouts/mainside/mainside.template.html b/src/layouts/mainside/mainside.template.html index 89609a3b3..c4176df13 100644 --- a/src/layouts/mainside/mainside.template.html +++ b/src/layouts/mainside/mainside.template.html @@ -13,7 +13,7 @@ </i> </div> <div - [@collapseSide] = "{ value : showSide ? 'visible' : 'collapsed' , params : { sideWidth : sideWith } }" + [@collapseSide] = "{ value : showSide ? 'visible' : 'collapsed' , params : { sideWidth : sideWidth } }" (@collapseSide.start) = "animationStart()" (@collapseSide.done) = "animationEnd()" sidecontent> diff --git a/src/main.ts b/src/main.ts index f9f13fe7c..a95e92c71 100644 --- a/src/main.ts +++ b/src/main.ts @@ -6,6 +6,7 @@ import { MainModule } from './main.module'; import 'chart.js/src/chart.js' const requireAll = (r:any) => {r.keys().forEach(r)} -requireAll(require.context('./res/ext',false, /.json$/)) +requireAll(require.context('./res/ext',false, /\.json$/)) +requireAll(require.context('./res/images',true,/\.jpg|\.png/)) platformBrowserDynamic().bootstrapModule(MainModule) \ No newline at end of file diff --git a/src/res/css/extra_styles.css b/src/res/css/extra_styles.css index 09096fe46..2ebd8d949 100644 --- a/src/res/css/extra_styles.css +++ b/src/res/css/extra_styles.css @@ -118,6 +118,7 @@ span.regionNotSelected, span.regionSelected { cursor : default; + user-select: none; } markdown-dom pre code diff --git a/src/ui/banner/banner.component.ts b/src/ui/banner/banner.component.ts index 70ae80f8f..e528e6636 100644 --- a/src/ui/banner/banner.component.ts +++ b/src/ui/banner/banner.component.ts @@ -1,4 +1,4 @@ -import { Component, OnDestroy } from "@angular/core"; +import { Component, OnDestroy, ChangeDetectionStrategy } 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"; @@ -10,7 +10,8 @@ import { FilterNameBySearch } from "../../util/pipes/filterNameBySearch.pipe"; templateUrl : './banner.template.html', styleUrls : [ `./banner.style.css` - ] + ], + changeDetection : ChangeDetectionStrategy.OnPush }) export class AtlasBanner implements OnDestroy{ @@ -171,7 +172,9 @@ export class AtlasBanner implements OnDestroy{ private insertHighlight(name:string,searchTerm:string):string{ const regex = new RegExp(searchTerm,'gi') - return name.replace(regex,(s)=>`<span class = "highlight">${s}</span>`) + return searchTerm === '' ? + name : + name.replace(regex,(s)=>`<span class = "highlight">${s}</span>`) } displayTreeNode(item:any){ @@ -180,11 +183,6 @@ export class AtlasBanner implements OnDestroy{ `<span class = "regionNotSelected">${this.insertHighlight(item.name,this.searchTerm)}</span>` } - /* TODO obsolete? */ - get treeHeaderText():string{ - return '' - } - getChildren(item:any){ return item.children } @@ -203,4 +201,5 @@ export class AtlasBanner implements OnDestroy{ } filterNameBySearchPipe = new FilterNameBySearch() -} \ No newline at end of file +} + diff --git a/src/ui/banner/banner.style.css b/src/ui/banner/banner.style.css index 30f37e29a..d19243eff 100644 --- a/src/ui/banner/banner.style.css +++ b/src/ui/banner/banner.style.css @@ -85,4 +85,35 @@ 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); +} + +:host-context([darktheme="true"]) [hbpLogoContainer] +{ + background-image:url('res/image/HBP_Primary_RGB_WhiteText.png') +} + +:host-context([darktheme="false"]) [hbpLogoContainer] +{ + background-image:url('res/image/HBP_Primary_RGB_BlackText.png') +} + +[hbpLogoContainer] +{ + display:inline-block; + background-size: cover; + pointer-events: none; + height:80px; +} + +[hbpLogoContainer] > img +{ + visibility: hidden; + height:80px; + pointer-events: none; +} + +:host +{ + display:flex; + height:0px; } \ No newline at end of file diff --git a/src/ui/banner/banner.template.html b/src/ui/banner/banner.template.html index 6b0e30b92..45af32561 100644 --- a/src/ui/banner/banner.template.html +++ b/src/ui/banner/banner.template.html @@ -1,3 +1,7 @@ + +<span hbpLogoContainer> + <img src = "res/image/HBP_Primary_RGB_WhiteText.png" /> +</span> <div container> <dropdown (itemSelected) = "selectTemplate($event)" @@ -47,10 +51,11 @@ </div> <tree *ngFor = "let child of (selectedParcellation.regions | treeSearch : filterTreeBySearch.bind(this) : getChildren )" - (mouseclicktree) = "handleClickRegion($event)" + (treeNodeClick) = "handleClickRegion($event)" [renderNode]="(displayTreeNode).bind(this)" [searchFilter]="(filterTreeBySearch).bind(this)" - [inputItem]="child"> + [inputItem]="child" + treebase> </tree> </div> </div> diff --git a/src/ui/databrowser/databrowser.template.html b/src/ui/databrowser/databrowser.template.html index 54aeec21a..92c9353b8 100644 --- a/src/ui/databrowser/databrowser.template.html +++ b/src/ui/databrowser/databrowser.template.html @@ -122,9 +122,10 @@ <tree *ngFor = "let item of searchResult.files | copyProperty : 'filename' : 'path' | pathToNestedChildren" [childrenExpanded] = "false" - [renderNode] = "renderNode" - (mouseclicktree) = "handleTreeNodeClick($event)" - [inputItem] = " item "> + [renderNode] = "renderNode" + (treeNodeClick) = "handleTreeNodeClick($event)" + [inputItem] = " item " + treebase> </tree> </div> diff --git a/src/ui/fileviewer/fileviewer.style.css b/src/ui/fileviewer/fileviewer.style.css index b5791c17b..365b2318b 100644 --- a/src/ui/fileviewer/fileviewer.style.css +++ b/src/ui/fileviewer/fileviewer.style.css @@ -7,4 +7,9 @@ small#captions div[emptyRow] { margin-bottom:-1em; +} + +img +{ + width: 100%; } \ No newline at end of file diff --git a/src/ui/nehubaContainer/nehubaContainer.component.ts b/src/ui/nehubaContainer/nehubaContainer.component.ts index 3eb347cb3..cc6ca1b4e 100644 --- a/src/ui/nehubaContainer/nehubaContainer.component.ts +++ b/src/ui/nehubaContainer/nehubaContainer.component.ts @@ -3,7 +3,7 @@ import { NehubaViewerUnit } from "./nehubaViewer/nehubaViewer.component"; import { Store, select } from "@ngrx/store"; import { ViewerStateInterface, safeFilter, SELECT_REGIONS, getLabelIndexMap, DataEntry, CHANGE_NAVIGATION, isDefined, MOUSE_OVER_SEGMENT } from "../../services/stateStore.service"; import { Observable, Subscription, fromEvent, combineLatest, merge } from "rxjs"; -import { filter,map, take, scan, debounceTime, distinctUntilChanged } from "rxjs/operators"; +import { filter,map, take, scan, debounceTime, distinctUntilChanged, switchMap } from "rxjs/operators"; import * as export_nehuba from 'export_nehuba' import { AtlasViewerAPIServices } from "../../atlasViewer/atlasViewer.apiService.service"; import { timedValues } from "../../util/generator"; @@ -144,6 +144,36 @@ export class NehubaContainer implements OnInit,OnDestroy,AfterViewInit{ /* patch NG */ this.patchNG() + + /* each time a new viewer is initialised, take the first event to get the translation function */ + this.newViewer$.pipe( + switchMap(()=>fromEvent(this.elementRef.nativeElement,'sliceRenderEvent').pipe( + scan((acc:Event[],event:Event)=>{ + const target = (event as Event).target as HTMLElement + const key = target.offsetLeft < 5 && target.offsetTop < 5 ? + 0 : + target.offsetLeft > 5 && target.offsetTop < 5 ? + 1 : + target.offsetLeft < 5 && target.offsetTop > 5 ? + 2 : + target.offsetLeft > 5 && target.offsetTop > 5 ? + 3 : + 4 + + const _ = {} + _[key] = event + return Object.assign({},acc,_) + },[]), + filter(v=>{ + const isdefined = (obj) => typeof obj !== 'undefined' && obj !== null + return (isdefined(v[0]) && isdefined(v[1]) && isdefined(v[2])) + }), + take(1) + ) + ) + ).subscribe((events)=>{ + [0,1,2].forEach(idx=>this.nanometersToOffsetPixelsFn[idx] = (events[idx] as any).detail.nanometersToOffsetPixels) + }) } ngOnInit(){ @@ -222,7 +252,7 @@ export class NehubaContainer implements OnInit,OnDestroy,AfterViewInit{ } ngAfterViewInit(){ - this.getNMToOffsetPixelFn() + } ngOnDestroy(){ @@ -256,34 +286,6 @@ export class NehubaContainer implements OnInit,OnDestroy,AfterViewInit{ spatialData.highlight = false } - private getNMToOffsetPixelFn(){ - - fromEvent(this.elementRef.nativeElement,'sliceRenderEvent').pipe( - scan((acc:Event[],event:Event)=>{ - const target = (event as Event).target as HTMLElement - const key = target.offsetLeft < 5 && target.offsetTop < 5 ? - 0 : - target.offsetLeft > 5 && target.offsetTop < 5 ? - 1 : - target.offsetLeft < 5 && target.offsetTop > 5 ? - 2 : - target.offsetLeft > 5 && target.offsetTop > 5 ? - 3 : - 4 - - const _ = {} - _[key] = event - return Object.assign({},acc,_) - },[]), - filter(v=>{ - const isdefined = (obj) => typeof obj !== 'undefined' && obj !== null - return (isdefined(v[0]) && isdefined(v[1]) && isdefined(v[2])) - }), - take(1) - ).subscribe((events)=> - [0,1,2].forEach(idx=>this.nanometersToOffsetPixelsFn[idx] = (events[idx] as any).detail.nanometersToOffsetPixels)) - } - private patchNG(){ const { LayerManager, UrlHashBinding } = export_nehuba.getNgPatchableObj() @@ -359,6 +361,8 @@ export class NehubaContainer implements OnInit,OnDestroy,AfterViewInit{ this.apiService.interactiveViewer.viewerHandle = null this.viewerLoaded = true + if( this.cr ) + this.cr.destroy() this.container.clear() this.cr = this.container.createComponent(this.nehubaViewerFactory) this.nehubaViewer = this.cr.instance @@ -527,7 +531,7 @@ export class NehubaContainer implements OnInit,OnDestroy,AfterViewInit{ /* set this.oldnavigation to represent the state of the store */ /* animation done, set this.oldNavigation */ - this.oldNavigation = Object.assign({},dest) + this.oldNavigation = Object.assign({},this.oldNavigation,dest) } } requestAnimationFrame(()=>animate()) @@ -649,4 +653,4 @@ export class NehubaContainer implements OnInit,OnDestroy,AfterViewInit{ export const CM_THRESHOLD = `0.05` export const CM_MATLAB_JET = `float r;if( x < 0.7 ){r = 4.0 * x - 1.5;} else {r = -4.0 * x + 4.5;}float g;if (x < 0.5) {g = 4.0 * x - 0.5;} else {g = -4.0 * x + 3.5;}float b;if (x < 0.3) {b = 4.0 * x + 0.5;} else {b = -4.0 * x + 2.5;}float a = 1.0;` -export const getActiveColorMapFragmentMain = ():string=>`void main(){float x = toNormalized(getDataValue());${CM_MATLAB_JET}if(x>${CM_THRESHOLD}){emitRGB(vec3(r,g,b));}else{emitTransparent();}}` +export const getActiveColorMapFragmentMain = ():string=>`void main(){float x = toNormalized(getDataValue());${CM_MATLAB_JET}if(x>${CM_THRESHOLD}){emitRGB(vec3(r,g,b));}else{emitTransparent();}}` \ No newline at end of file diff --git a/src/ui/nehubaContainer/nehubaContainer.template.html b/src/ui/nehubaContainer/nehubaContainer.template.html index 89b48be2b..671068e0b 100644 --- a/src/ui/nehubaContainer/nehubaContainer.template.html +++ b/src/ui/nehubaContainer/nehubaContainer.template.html @@ -78,7 +78,8 @@ </a> </div> - <hr /> + <hr + *ngIf = "showCitation" /> <span class = "btn btn-link" diff --git a/src/ui/nehubaContainer/nehubaViewer/nehubaViewer.component.ts b/src/ui/nehubaContainer/nehubaViewer/nehubaViewer.component.ts index 340949cfa..d47c0da23 100644 --- a/src/ui/nehubaContainer/nehubaViewer/nehubaViewer.component.ts +++ b/src/ui/nehubaContainer/nehubaViewer/nehubaViewer.component.ts @@ -83,7 +83,9 @@ export class NehubaViewerUnit implements AfterViewInit,OnDestroy{ public mouseOverSegment : number | null ngAfterViewInit(){ - this.nehubaViewer = export_nehuba.createNehubaViewer(this.config,console.warn) + this.nehubaViewer = export_nehuba.createNehubaViewer(this.config,(err)=>{ + /* print in debug mode */ + }) if(this.regionsLabelIndexMap){ const managedLayers = this.nehubaViewer.ngviewer.layerManager.managedLayers diff --git a/webpack.export.aot.js b/webpack.export.aot.js new file mode 100644 index 000000000..8421c23bf --- /dev/null +++ b/webpack.export.aot.js @@ -0,0 +1,47 @@ +const common = require('./webpack.common.js') +const path = require('path') +const ngtools = require('@ngtools/webpack') +const HtmlWebpackPlugin = require('html-webpack-plugin') +const AngularCompilerPlugin = ngtools.AngularCompilerPlugin +const ClosureCompilerPlugin = require('webpack-closure-compiler') + +module.exports = { + + entry : './src/main-aot.ts', + output : { + filename : 'main.js', + path : path.resolve(__dirname,'dist/export-aot') + }, + module: { + rules: [ + { + test: /(?:\.ngfactory\.js|\.ngstyle\.js|\.ts)$/, + // test : /\.ts$/, + loader: '@ngtools/webpack', + exclude : /export_nehuba/ + }, + { + test : /\.(html|css)$/, + use : { + loader : 'raw-loader', + } + } + ] + }, + plugins : [ + new HtmlWebpackPlugin({ + template : './src/atlasViewerExports/export.html' + }), + new AngularCompilerPlugin({ + tsConfigPath: 'tsconfig-aot.json', + entryModule: 'src/atlasViewerExports/export.module#ExportModule' + }) + ], + resolve : { + extensions : [ + '.ts', + '.js', + '.json' + ] + } +} \ No newline at end of file diff --git a/webpack.export.js b/webpack.export.js index 1bb833dc1..d6cc210c9 100644 --- a/webpack.export.js +++ b/webpack.export.js @@ -12,6 +12,7 @@ module.exports = merge(common,ngAssets,{ filename : 'export.js', path : path.resolve(__dirname,'dist/export') }, + devtool:'source-map', plugins : [ // new ClosureCompilerPlugin({ // compiler : { diff --git a/webpack.export.min.js b/webpack.export.min.js new file mode 100644 index 000000000..eff53e1cc --- /dev/null +++ b/webpack.export.min.js @@ -0,0 +1,26 @@ +const merge = require('webpack-merge') +const common = require('./webpack.common.js') +const ngAssets = require('./webpack.ngassets') +const path = require('path') +const ClosureCompilerPlugin = require('webpack-closure-compiler') +const HtmlWebpackPlugin = require('html-webpack-plugin') + +module.exports = merge(common,ngAssets,{ + entry : './src/atlasViewerExports/main.export.ts', + output : { + filename : 'export.js', + path : path.resolve(__dirname,'dist/export-min') + }, + plugins : [ + new ClosureCompilerPlugin({ + compiler : { + compilation_level : 'SIMPLE' + }, + concurrency : 4 + }), + + new HtmlWebpackPlugin({ + template : './src/atlasViewerExports/export.html' + }) + ] +}) \ No newline at end of file diff --git a/webpack.ngassets.js b/webpack.ngassets.js index 79f5e00da..c9227ba43 100644 --- a/webpack.ngassets.js +++ b/webpack.ngassets.js @@ -2,7 +2,7 @@ module.exports = { module : { rules : [ { - test : /\.(html|css)$/, + test : /(html|css)$/, exclude : /export\_nehuba|index/, use : { loader : 'file-loader', @@ -10,6 +10,14 @@ module.exports = { name : '[name].[ext]' } } + },{ + test : /(jpg|png)$/, + use : { + loader : 'file-loader', + options : { + name : 'res/image/[name].[ext]' + } + } } ] } diff --git a/webpack.staticassets.js b/webpack.staticassets.js new file mode 100644 index 000000000..c123acfa2 --- /dev/null +++ b/webpack.staticassets.js @@ -0,0 +1,16 @@ +module.exports = { + module : { + rules : [ + { + test : /jpg|png/, + exclude : /export\_nehuba|index/, + use : { + loader : 'file-loader', + options : { + name : '[name].[ext]' + } + } + } + ] + } +} \ No newline at end of file -- GitLab