diff --git a/docs/releases/v2.14.0.md b/docs/releases/v2.14.0.md index d0997980b9a98b72034cf4f1f2c6485a1bd0d220..fd75241e279355715372abcb3858f84288d6d804 100644 --- a/docs/releases/v2.14.0.md +++ b/docs/releases/v2.14.0.md @@ -4,11 +4,17 @@ - added `[p]` and `[n]` as keyboard shortcut to navigate previous/next slice - show template and parcellation info even if the chip are hidden +- added button to directly copy navigation coordinate - experimental support for other versions of regions - experimental support for drag/drop pointcloud `.json` files - experimental support for `deepzoom://` source format +## Bugfix + +- fixed keyframe mode not activating on second attempt + ## Behind the scenes -- Minor refactoring -- Update to Angular 15 +- minor refactoring +- update to Angular 15 +- migrated to Angular material 15 diff --git a/src/atlasComponents/sapiViews/core/region/region/rich/region.rich.component.ts b/src/atlasComponents/sapiViews/core/region/region/rich/region.rich.component.ts index d2ba51f51e67d9d123835f04816a5b2ec8c560a9..0b5f6a52f16259c468a8afb953b7df4f7d8ab003 100644 --- a/src/atlasComponents/sapiViews/core/region/region/rich/region.rich.component.ts +++ b/src/atlasComponents/sapiViews/core/region/region/rich/region.rich.component.ts @@ -6,7 +6,7 @@ import { ARIA_LABELS, CONST } from 'common/constants' import { Feature, SxplrParcellation, SxplrRegion } from "src/atlasComponents/sapi/sxplrTypes"; import { SAPI } from "src/atlasComponents/sapi/sapi.service"; import { environment } from "src/environments/environment"; -import { catchError, map, shareReplay, switchMap } from "rxjs/operators"; +import { catchError, map, scan, shareReplay, switchMap } from "rxjs/operators"; import { PathReturn } from "src/atlasComponents/sapi/typeV3"; import { DecisionCollapse } from "src/atlasComponents/sapi/decisionCollapse.service"; @@ -45,6 +45,15 @@ export class SapiViewsCoreRegionRegionRich extends SapiViewsCoreRegionRegionBase } activePanelTitles$: Observable<string[]> = new Subject() + + #fetching$ = new Subject<Record<string, boolean>>() + busy$ = this.#fetching$.pipe( + scan((acc, curr) => ({ ...acc, ...curr })), + map(fetchingItems => { + const busyFlags = Object.values(fetchingItems) + return busyFlags.some(flag => flag) + }) + ) private regionalMaps$ = this.ATPR$.pipe( switchMap(({ parcellation, template, region }) => diff --git a/src/atlasComponents/sapiViews/core/region/region/rich/region.rich.style.css b/src/atlasComponents/sapiViews/core/region/region/rich/region.rich.style.css index 8405a0f949e72f3f0c69d806fc93d7df52756519..8e3dd7d35e225d23691c95e6e39eb3bb3e840387 100644 --- a/src/atlasComponents/sapiViews/core/region/region/rich/region.rich.style.css +++ b/src/atlasComponents/sapiViews/core/region/region/rich/region.rich.style.css @@ -1,8 +1,3 @@ -mat-card-header -{ - padding: 16px; -} - .feature-list-container { max-height: 16rem; @@ -41,3 +36,9 @@ readmore-component width: 16rem; margin: 1rem 0.5rem; } + +table[mat-table] * +{ + white-space: nowrap; + text-overflow: ellipsis; +} diff --git a/src/atlasComponents/sapiViews/core/region/region/rich/region.rich.template.html b/src/atlasComponents/sapiViews/core/region/region/rich/region.rich.template.html index 774d96389fcc72e1d6d18501994bfff2becb29f6..ea3c2390666cbf7c373a97896d72ddc0a1b118c9 100644 --- a/src/atlasComponents/sapiViews/core/region/region/rich/region.rich.template.html +++ b/src/atlasComponents/sapiViews/core/region/region/rich/region.rich.template.html @@ -4,13 +4,13 @@ <ng-template [ngIf]="region"> - <mat-card class="mat-elevation-z4"> - <mat-card-header - [style.backgroundColor]="regionRgbString" - [ngClass]="{ - 'darktheme': regionDarkmode === true, - 'lighttheme': regionDarkmode === false - }"> + <mat-card class="mat-elevation-z4" + [style.backgroundColor]="regionRgbString" + [ngClass]="{ + 'darktheme': regionDarkmode === true, + 'lighttheme': regionDarkmode === false + }"> + <mat-card-header> <mat-card-subtitle> <ng-template [ngTemplateOutlet]="headerTmpl"></ng-template> @@ -27,6 +27,7 @@ </mat-card-title> </mat-card-header> + <mat-card-content></mat-card-content> </mat-card> @@ -48,14 +49,14 @@ actions: parcellation | parcTmplDoiPipe }"> <mat-icon matListItemIcon fontSet="fas" fontIcon="fa-brain"></mat-icon> - <div mat-line class="overview-content">{{ parcellation.name }}</div> + <div matListItemTitle>{{ parcellation.name }}</div> </button> <!-- region position (if eixsts) --> <ng-template [ngIf]="regionPosition"> <button mat-list-item (click)="navigateTo(regionPosition)"> <mat-icon matListItemIcon fontSet="fas" fontIcon="fa-map-marker"></mat-icon> - <div mat-line class="overview-content">Centroid: {{ regionPosition | numbers | addUnitAndJoin : 'mm' }}</div> + <div matListItemTitle>Centroid: {{ regionPosition | numbers | addUnitAndJoin : 'mm' }}</div> </button> </ng-template> @@ -63,13 +64,11 @@ <ng-template ngFor [ngForOf]="dois$ | async" let-doi> <a mat-list-item [href]="doi" target="_blank" class="no-hover"> <mat-icon matListItemIcon fontSet="ai" fontIcon="ai-doi"></mat-icon> - <div mat-line>{{ doi }}</div> + <div matListItemTitle>{{ doi }}</div> </a> </ng-template> - <ng-template sxplrExperimentalFlag [experimental]="true" - #relatedRegionsExport="sxplrExperimentalFlag" - [ngIf]="relatedRegionsExport.show$ | async"> + <ng-template sxplrExperimentalFlag [experimental]="true"> <!-- related regions --> <ng-template [ngIf]="relatedRegions$ | async | dedupRelatedRegionPipe" let-relatedRegions> @@ -79,7 +78,7 @@ [sxplr-dialog]="relatedRegionsTmpl" sxplr-dialog-size="auto"> <mat-icon matListItemIcon fontSet="fas" fontIcon="fa-link"></mat-icon> - <div mat-line class="overview-content">Related Regions ({{ relatedRegions.length }})</div> + <div matListItemTitle>Related Regions ({{ relatedRegions.length }})</div> </button> <!-- dialog when user clicks related regions --> @@ -144,6 +143,13 @@ </ng-template> + <!-- if busy, show spinner --> + <ng-template [ngIf]="busy$ | async"> + <mat-list-item> + <mat-spinner [diameter]="24"></mat-spinner> + </mat-list-item> + </ng-template> + </ng-template> </mat-action-list> @@ -178,9 +184,7 @@ </sxplr-feature-entry> - <ng-template sxplrExperimentalFlag [experimental]="true" - #exmptRelatedFeat="sxplrExperimentalFlag" - [ngIf]="exmptRelatedFeat.show$ | async"> + <ng-template sxplrExperimentalFlag [experimental]="true"> <!-- related region features --> <ng-template [ngIf]="relatedRegions$ | async | dedupRelatedRegionPipe" let-relatedRegions> @@ -194,15 +198,19 @@ <div class="h-carousel-item-container"> <mat-card *ngFor="let relatedRegion of relatedRegions" class="h-carousel-item mat-elevation-z4"> - <mat-card-subtitle> - {{ relatedRegion.qualification | translateQualificationPipe }} - </mat-card-subtitle> - <mat-card-title> - {{ relatedRegion.region.name }} - </mat-card-title> - <mat-card-subtitle> - {{ relatedRegion.parcellation.name}} - </mat-card-subtitle> + + <mat-card-header> + <mat-card-subtitle> + {{ relatedRegion.qualification | translateQualificationPipe }} + </mat-card-subtitle> + <mat-card-title> + {{ relatedRegion.region.name }} + </mat-card-title> + <mat-card-subtitle> + {{ relatedRegion.parcellation.name}} + </mat-card-subtitle> + </mat-card-header> + <mat-card-actions> <button mat-button [sxplr-dialog]="relatedRegFeatTmpl" diff --git a/src/atlasComponents/sapiViews/core/rich/regionsListSearch/regionListSearch.template.html b/src/atlasComponents/sapiViews/core/rich/regionsListSearch/regionListSearch.template.html index 9e5f46b03519f1c4a49f753078de079cc4fef821..fd9edae6072eb4e5ae89cfeb590cf19e3865a111 100644 --- a/src/atlasComponents/sapiViews/core/rich/regionsListSearch/regionListSearch.template.html +++ b/src/atlasComponents/sapiViews/core/rich/regionsListSearch/regionListSearch.template.html @@ -2,6 +2,9 @@ class="sxplr-w-100" subscriptSizing="dynamic" floatLabel="auto"> + <div matPrefix> + <ng-content select="[search-input-prefix]"></ng-content> + </div> <input placeholder="Search for regions" [value]="currentSearch" diff --git a/src/atlasComponents/userAnnotations/annotationList/annotationList.template.html b/src/atlasComponents/userAnnotations/annotationList/annotationList.template.html index d90110fa3f66f2a6483787462e5432bc4704986f..0a2e8a5bd83ebf8631aee73f3e3c52990a4da190 100644 --- a/src/atlasComponents/userAnnotations/annotationList/annotationList.template.html +++ b/src/atlasComponents/userAnnotations/annotationList/annotationList.template.html @@ -2,56 +2,59 @@ <div class="sidenav-cover-header-container bg-50-grey-20"> <!-- title --> - <mat-card-title> - My Annotations - </mat-card-title> - - <!-- actions --> - <mat-card-subtitle> - <!-- import --> - <ng-template #importMessageTmpl> - Please select a annotation file to import. - </ng-template> - - <button mat-icon-button - (file-input-directive)="handleImportEvent($event)" - [file-input-directive-title]="ARIA_LABELS.USER_ANNOTATION_IMPORT" - [file-input-directive-text]="true" - [file-input-directive-file]="true" - [file-input-directive-message]="importMessageTmpl" - [attr.aria-label]="ARIA_LABELS.USER_ANNOTATION_IMPORT" - [matTooltip]="ARIA_LABELS.USER_ANNOTATION_IMPORT"> - <i class="fas fa-folder-open"></i> - </button> - - <!-- export --> - <button mat-icon-button - [zip-files-output]="filesExport$" - zip-files-output-zip-filename="exported_annotations.zip" - [attr.aria-label]="ARIA_LABELS.USER_ANNOTATION_EXPORT" - [matTooltip]="ARIA_LABELS.USER_ANNOTATION_EXPORT" - [disabled]="!(manAnnExists$ | async)"> - <i class="fas fa-download"></i> - </button> - - <!-- share --> - <button mat-icon-button - [matTooltip]="ARIA_LABELS.SHARE_CUSTOM_URL" - (click)="openDialog(saneUrlTmpl)" - iav-state-aggregator - #stateAggregator="iavStateAggregator"> - <i class="fas fa-share-square"></i> - </button> - - <!-- delete all annotations --> - <button mat-icon-button - color="warn" - (click)="deleteAllAnnotation()" - [matTooltip]="ARIA_LABELS.BULK_DELETE_ANNOTATIONS" - [disabled]="!(manAnnExists$ | async)"> - <i class="fas fa-trash"></i> - </button> - </mat-card-subtitle> + <mat-card-header> + + <mat-card-title> + My Annotations + </mat-card-title> + + <!-- actions --> + <mat-card-subtitle> + <!-- import --> + <ng-template #importMessageTmpl> + Please select a annotation file to import. + </ng-template> + + <button mat-icon-button + (file-input-directive)="handleImportEvent($event)" + [file-input-directive-title]="ARIA_LABELS.USER_ANNOTATION_IMPORT" + [file-input-directive-text]="true" + [file-input-directive-file]="true" + [file-input-directive-message]="importMessageTmpl" + [attr.aria-label]="ARIA_LABELS.USER_ANNOTATION_IMPORT" + [matTooltip]="ARIA_LABELS.USER_ANNOTATION_IMPORT"> + <i class="fas fa-folder-open"></i> + </button> + + <!-- export --> + <button mat-icon-button + [zip-files-output]="filesExport$" + zip-files-output-zip-filename="exported_annotations.zip" + [attr.aria-label]="ARIA_LABELS.USER_ANNOTATION_EXPORT" + [matTooltip]="ARIA_LABELS.USER_ANNOTATION_EXPORT" + [disabled]="!(manAnnExists$ | async)"> + <i class="fas fa-download"></i> + </button> + + <!-- share --> + <button mat-icon-button + [matTooltip]="ARIA_LABELS.SHARE_CUSTOM_URL" + (click)="openDialog(saneUrlTmpl)" + iav-state-aggregator + #stateAggregator="iavStateAggregator"> + <i class="fas fa-share-square"></i> + </button> + + <!-- delete all annotations --> + <button mat-icon-button + color="warn" + (click)="deleteAllAnnotation()" + [matTooltip]="ARIA_LABELS.BULK_DELETE_ANNOTATIONS" + [disabled]="!(manAnnExists$ | async)"> + <i class="fas fa-trash"></i> + </button> + </mat-card-subtitle> + </mat-card-header> </div> <!-- content --> diff --git a/src/atlasComponents/userAnnotations/annotationMode/annotationMode.component.ts b/src/atlasComponents/userAnnotations/annotationMode/annotationMode.component.ts index af64520817da2fa0476e6556be23b31a6ce8be74..322d59bd25739beaf102a3870ffe014366758222 100644 --- a/src/atlasComponents/userAnnotations/annotationMode/annotationMode.component.ts +++ b/src/atlasComponents/userAnnotations/annotationMode/annotationMode.component.ts @@ -28,6 +28,12 @@ export class AnnotationMode implements OnDestroy{ @Optional() @Inject(CLICK_INTERCEPTOR_INJECTOR) clickInterceptor: ClickInterceptor, @Optional() @Inject(CONTEXT_MENU_ITEM_INJECTOR) ctxMenuInterceptor: TContextMenu<TContextMenuReg<TContextArg<'nehuba' | 'threeSurfer'>>> ) { + + /** + * reverse the order, since we are column reverse to achieve the tab effect + */ + this.moduleAnnotationTypes.reverse() + const stopClickProp = () => false if (clickInterceptor) { const { register, deregister } = clickInterceptor diff --git a/src/atlasComponents/userAnnotations/annotationMode/annotationMode.style.css b/src/atlasComponents/userAnnotations/annotationMode/annotationMode.style.css index c525622e4f3c7081ec4f3a6a65de4fed4c367c45..fb6d946459d19f4ed511d1821fe8eac0249b3b50 100644 --- a/src/atlasComponents/userAnnotations/annotationMode/annotationMode.style.css +++ b/src/atlasComponents/userAnnotations/annotationMode/annotationMode.style.css @@ -1,13 +1,23 @@ -.tab-toggle-container +.tool-btn-container { - margin-left: -3rem; - padding-top: 0; - padding-bottom: 0; + display: flex; + flex-direction: column-reverse; } -.tab-toggle +button { - margin: 0.25rem -1rem 0.25rem 0rem; - padding-right: 0; - padding-left: 1.5rem; -} \ No newline at end of file + pointer-events: all; + + margin-left: -1rem; + margin-right: 1rem; + margin-top: -0.2rem; + + padding-right: 0.75rem; + text-align: right; +} + +.leave-me-alone +{ + margin-top: 0.5rem; + margin-bottom: 0.5rem; +} diff --git a/src/atlasComponents/userAnnotations/annotationMode/annotationMode.template.html b/src/atlasComponents/userAnnotations/annotationMode/annotationMode.template.html index 7542cdba29fd4102f5e03c801468cbc69fb77b05..12e058c821e644fef4830322ae7d6048944eb6f0 100644 --- a/src/atlasComponents/userAnnotations/annotationMode/annotationMode.template.html +++ b/src/atlasComponents/userAnnotations/annotationMode/annotationMode.template.html @@ -1,26 +1,25 @@ -<mat-card class="d-inline-flex flex-column pe-all tab-toggle-container" +<div class="tool-btn-container" [iav-key-listener]="[{ type: 'keydown', key: 'Escape', target: 'document', capture: true }]" (iav-key-event)="deselectTools()"> <button - *ngFor="let moduleAnnotType of moduleAnnotationTypes" - mat-button - class="tab-toggle" - (click)="moduleAnnotType.onClick()" - [color]="(moduleAnnotType.instance.toolSelected$ | async) ? 'primary' : 'basic'" - type="button"> - <i [class]="moduleAnnotType.instance.iconClass"></i> - </button> - - <mat-divider class="d-block"></mat-divider> - - <button - mat-button + mat-raised-button + class="leave-me-alone annotation-tool-btn" annotation-switch annotation-switch-mode="off" - class="tab-toggle" [matTooltip]="ARIA_LABELS.EXIT_ANNOTATION_MODE" color="warn"> <i class="fas fa-times"></i> </button> -</mat-card> \ No newline at end of file + + <button + *ngFor="let moduleAnnotType of moduleAnnotationTypes" + mat-raised-button + class="annotation-tool-btn" + (click)="moduleAnnotType.onClick()" + [color]="(moduleAnnotType.instance.toolSelected$ | async) ? 'primary' : null" + type="button"> + <i [class]="moduleAnnotType.instance.iconClass"></i> + </button> + +</div> diff --git a/src/atlasComponents/userAnnotations/tools/textareaCopyExport/textareaCopyExport.template.html b/src/atlasComponents/userAnnotations/tools/textareaCopyExport/textareaCopyExport.template.html index a82f199522da1981e81a01e28358937febec850e..65fe0d11264cf82005dcf795ab3fc19239fa83fb 100644 --- a/src/atlasComponents/userAnnotations/tools/textareaCopyExport/textareaCopyExport.template.html +++ b/src/atlasComponents/userAnnotations/tools/textareaCopyExport/textareaCopyExport.template.html @@ -14,8 +14,7 @@ iav-stop="click" aria-label="Copy to clipboard" matTooltip="Copy to clipboard." - (click)="copyToClipboard(exportTarget.value)" - color="basic"> + (click)="copyToClipboard(exportTarget.value)"> <i class="fas fa-copy"></i> </button> <button mat-icon-button diff --git a/src/contextMenuModule/service.ts b/src/contextMenuModule/service.ts index 7cba30e61e0b576feef3c63a7493c78652c23ad1..d28cdd5245c376bd2713b1a0829a7f3d0c0bf2df 100644 --- a/src/contextMenuModule/service.ts +++ b/src/contextMenuModule/service.ts @@ -117,7 +117,7 @@ export class ContextMenuService<T> extends RegDeregController<CtxMenuInterArg<T> this.context$.next(state) } deepMerge(pState: Partial<T>) { - const newState: T = structuredClone(this.context || {}) + const newState: T = structuredClone(this.context || {} as T) this.context$.next( mutateDeepMerge(newState, pState) ) diff --git a/src/experimental/experimental-flag.directive.ts b/src/experimental/experimental-flag.directive.ts index 9574005d8bc0b1a58a845871bdd980b74ec64b44..789d8359a67c7dc9674b35b8eb089d9c05dc8849 100644 --- a/src/experimental/experimental-flag.directive.ts +++ b/src/experimental/experimental-flag.directive.ts @@ -1,15 +1,21 @@ -import { Directive, Input } from '@angular/core'; +import { NgIf } from '@angular/common'; +import { ChangeDetectorRef, Directive, Input, inject } from '@angular/core'; import { Store, select } from '@ngrx/store'; import { BehaviorSubject, combineLatest } from 'rxjs'; -import { debounceTime, filter, map } from 'rxjs/operators'; +import { debounceTime, filter, map, takeUntil } from 'rxjs/operators'; import { MainState, userPreference } from 'src/state'; +import { DestroyDirective } from 'src/util/directives/destroy.directive'; @Directive({ selector: '[sxplrExperimentalFlag]', - exportAs: 'sxplrExperimentalFlag' + exportAs: 'sxplrExperimentalFlag', + hostDirectives: [NgIf, DestroyDirective] }) export class ExperimentalFlagDirective { + private readonly ngIf = inject(NgIf) + private readonly destroyed$ = inject(DestroyDirective).destroyed$ + @Input() set deprecated(deprecated: boolean){ this.#inputs.next({ @@ -57,7 +63,15 @@ export class ExperimentalFlagDirective { ) constructor( - private store: Store<MainState> - ) { } + private store: Store<MainState>, + private cdr: ChangeDetectorRef, + ) { + this.show$.pipe( + takeUntil(this.destroyed$) + ).subscribe(flag => { + this.ngIf.ngIf = flag + this.cdr.detectChanges() + }) + } } diff --git a/src/extra_styles.css b/src/extra_styles.css index 427f8e8e96c096ddd2ab33c7c76826dcf177676b..795f053415d65c0e6ff85df9bf6030c14adcad3f 100644 --- a/src/extra_styles.css +++ b/src/extra_styles.css @@ -882,7 +882,7 @@ how-to-cite img margin-top: 1rem; } -.overview-content +.annotation-tool-btn .mdc-button__label { - white-space: normal!important; + transform: translateX(0.7rem); } diff --git a/src/features/feature-view/feature-view.component.html b/src/features/feature-view/feature-view.component.html index 26a02e48c3a15673db3878ebba79fc0748d795c0..908265a5fb8c501257ae61bfb4bceac37e2fdabb 100644 --- a/src/features/feature-view/feature-view.component.html +++ b/src/features/feature-view/feature-view.component.html @@ -15,27 +15,31 @@ <mat-card *ngIf="feature" class="mat-elevation-z4 sxplr-z-4 header-card"> - <ng-template [ngTemplateOutlet]="headerTmpl"></ng-template> - - <mat-card-subtitle> - <ng-template [ngIf]="feature.category"> - <span class="sxplr-m-a sxplr-pr-1"> - <ng-template [ngIf]="feature.category !== 'Unknown category'" [ngIfElse]="fallbackTmpl"> - {{ feature.category }} feature - </ng-template> - <ng-template #fallbackTmpl> - Other feature - </ng-template> - </span> - </ng-template> - </mat-card-subtitle> - - <mat-card-title> - <div class="feature-title"> - {{ feature.name }} - </div> - </mat-card-title> - + <mat-card-header> + <mat-card-subtitle> + <ng-template [ngTemplateOutlet]="headerTmpl"></ng-template> + </mat-card-subtitle> + + <mat-card-subtitle> + <ng-template [ngIf]="feature.category"> + <span class="sxplr-m-a sxplr-pr-1"> + <ng-template [ngIf]="feature.category !== 'Unknown category'" [ngIfElse]="fallbackTmpl"> + {{ feature.category }} feature + </ng-template> + <ng-template #fallbackTmpl> + Other feature + </ng-template> + </span> + </ng-template> + </mat-card-subtitle> + + <mat-card-title> + <div class="feature-title"> + {{ feature.name }} + </div> + </mat-card-title> + </mat-card-header> + <mat-card-content></mat-card-content> </mat-card> <mat-tab-group> @@ -46,8 +50,8 @@ <ng-template [ngIf]="warnings$ | async" let-warnings> <ng-template ngFor [ngForOf]="warnings" let-warning> <button mat-list-item> - <mat-icon mat-list-icon fontSet="fas" fontIcon="fa-map-marker"></mat-icon> - <div mat-line class="overview-content">{{ warning }}</div> + <mat-icon matListItemIcon fontSet="fas" fontIcon="fa-map-marker"></mat-icon> + <div matListItemTitle>{{ warning }}</div> </button> </ng-template> </ng-template> @@ -55,23 +59,23 @@ <!-- doi --> <ng-template ngFor [ngForOf]="feature.link" let-url> <a [href]="url.href" mat-list-item target="_blank" class="no-hover"> - <mat-icon mat-list-icon fontSet="ai" fontIcon="ai-doi"></mat-icon> - <div mat-line>{{ url.text || url.href }}</div> + <mat-icon matListItemIcon fontSet="ai" fontIcon="ai-doi"></mat-icon> + <div matListItemTitle>{{ url.text || url.href }}</div> </a> </ng-template> <!-- additional links --> <ng-template ngFor [ngForOf]="additionalLinks$ | async" let-url> <a [href]="url" mat-list-item target="_blank" class="no-hover"> - <mat-icon mat-list-icon fontSet="ai" fontIcon="ai-doi"></mat-icon> - <div mat-line>{{ url }}</div> + <mat-icon matListItemIcon fontSet="ai" fontIcon="ai-doi"></mat-icon> + <div matListItemTitle>{{ url }}</div> </a> </ng-template> <ng-template [ngIf]="downloadLink$ | async" let-downloadLink> <a [href]="downloadLink" mat-list-item target="_blank" class="no-hover"> - <mat-icon mat-list-icon fontSet="fas" fontIcon="fa-download"></mat-icon> - <div mat-line>Download</div> + <mat-icon matListItemIcon fontSet="fas" fontIcon="fa-download"></mat-icon> + <div matListItemTitle>Download</div> </a> </ng-template> diff --git a/src/features/feature-view/feature-view.component.scss b/src/features/feature-view/feature-view.component.scss index 02064737f27bc462d44c78afed57c88a044a8e34..8fb4cc58653121eaccf3d8a8a7aeda84f8633ec2 100644 --- a/src/features/feature-view/feature-view.component.scss +++ b/src/features/feature-view/feature-view.component.scss @@ -1,6 +1,6 @@ .feature-title { - max-height: 8rem; + max-height: 12rem; overflow-x: hidden; overflow-y: auto; } diff --git a/src/keyframesModule/keyframe.directive.ts b/src/keyframesModule/keyframe.directive.ts index 50708e85062ba075edf28ca9a41506d492694419..cde65ec7dbd91302906a28c875e4cb5ee6a99cb3 100644 --- a/src/keyframesModule/keyframe.directive.ts +++ b/src/keyframesModule/keyframe.directive.ts @@ -1,42 +1,61 @@ -import { Directive, HostListener, Input } from "@angular/core"; -import { KeyFrameService } from "./service"; +import { Directive, HostListener, Input, inject } from "@angular/core"; +import { Store, select } from "@ngrx/store"; +import { DestroyDirective } from "src/util/directives/destroy.directive"; +import { atlasSelection } from "src/state"; +import { takeUntil } from "rxjs/operators"; +import { ViewerMode } from "src/state/atlasSelection/const"; @Directive({ - selector: '[key-frame-play-now]' + selector: '[key-frame-play-now]', + hostDirectives: [DestroyDirective] }) export class KeyFrameDirective{ + + #viewerMode: ViewerMode + #onDestroy$ = inject(DestroyDirective).destroyed$ + @HostListener('click') onClick(){ - if (this._mode === 'on') { - this.svc.startKeyFrameSession() + if (this.mode === 'on') { + if (this.#viewerMode !== "key frame") { + this.store.dispatch( + atlasSelection.actions.setViewerMode({ + viewerMode: "key frame" + }) + ) + } return } - if (this._mode === 'off') { - this.svc.endKeyFrameSession() + if (this.mode === 'off') { + if (this.#viewerMode === "key frame") { + this.store.dispatch( + atlasSelection.actions.setViewerMode({ + viewerMode: null + }) + ) + } return } - if (this.svc.inSession) { - this.svc.endKeyFrameSession() - } else { - this.svc.startKeyFrameSession() - } + + this.store.dispatch( + atlasSelection.actions.setViewerMode({ + viewerMode: this.#viewerMode === "key frame" + ? null + : "key frame" + }) + ) } - private _mode: 'toggle' | 'off' | 'on' = 'on' @Input('key-frame-play-now') - set mode(val: string){ - if (val === 'off') { - this._mode = val - return - } - if (val === 'toggle') { - this._mode = val - return - } - this._mode = 'on' - } + mode: 'toggle' | 'off' | 'on' | '' = 'on' - constructor(private svc: KeyFrameService){ + constructor(private store: Store){ + this.store.pipe( + select(atlasSelection.selectors.viewerMode), + takeUntil(this.#onDestroy$), + ).subscribe(viewerMode => { + this.#viewerMode = viewerMode + }) } } diff --git a/src/keyframesModule/keyframeCtrl/keyframeCtrl.component.ts b/src/keyframesModule/keyframeCtrl/keyframeCtrl.component.ts index 986db4e1989b57c6322293df46dfe9b63e58c457..24536f82c6e8546d5944b71aa84bf522023ea956 100644 --- a/src/keyframesModule/keyframeCtrl/keyframeCtrl.component.ts +++ b/src/keyframesModule/keyframeCtrl/keyframeCtrl.component.ts @@ -1,10 +1,12 @@ import { CdkDragDrop, moveItemInArray } from "@angular/cdk/drag-drop"; -import { Component, OnDestroy, Optional } from "@angular/core"; -import { Subscription } from "rxjs"; +import { Component, Optional, inject } from "@angular/core"; import { getUuid } from "src/util/fn"; import { timedValues } from "src/util/generator"; import { AUTO_ROTATE, TAutoRotatePayload, ViewerInternalStateSvc } from "src/viewerModule/viewerInternalState.service"; import { MatSnackBar } from 'src/sharedModules/angularMaterial.exports' +import { FormControl, FormGroup } from "@angular/forms"; +import { DestroyDirective } from "src/util/directives/destroy.directive"; +import { debounceTime, filter, takeUntil } from "rxjs/operators"; type TStoredState = { name: string @@ -18,22 +20,37 @@ type TStoredState = { templateUrl: './keyframeCtrl.template.html', styleUrls: [ './keyframeCtrl.style.css' - ] + ], + hostDirectives: [DestroyDirective] }) -export class KeyFrameCtrlCmp implements OnDestroy { +export class KeyFrameCtrlCmp { + + #onDestroy$ = inject(DestroyDirective).destroyed$ public loopFlag = false public linearFlag = false public currState: any - private currViewerType: string + public currViewerType: string public internalStates: TStoredState[] = [] - private subs: Subscription[] = [] + autoRotateFormGrp = new FormGroup({ + autorotate: new FormControl<boolean>(false), + autoRotateSpeed: new FormControl<number>({ + value: 2, + disabled: true, + }), + autorotateReverse: new FormControl<boolean>({ + value: false, + disabled: true, + }) + }) - ngOnDestroy(){ - while(this.subs.length) this.subs.pop().unsubscribe() - } + frameFormGrp = new FormGroup({ + loop: new FormControl<boolean>(false), + linearCamera: new FormGroup<boolean>(false), + steps: new FormGroup({}) + }) constructor( private snackbar: MatSnackBar, @@ -44,13 +61,39 @@ export class KeyFrameCtrlCmp implements OnDestroy { return } - this.subs.push( - viewerInternalSvc.viewerInternalState$.pipe( - ).subscribe(state => { - this.currState = state.payload - this.currViewerType = state.viewerType - }) - ) + viewerInternalSvc.viewerInternalState$.pipe( + takeUntil(this.#onDestroy$), + filter(v => !!v), + ).subscribe(state => { + this.currState = state.payload + this.currViewerType = state.viewerType + }) + + this.autoRotateFormGrp.controls.autorotate.valueChanges.pipe( + takeUntil(this.#onDestroy$) + ).subscribe(value => { + const { autoRotateSpeed, autorotateReverse } = this.autoRotateFormGrp.controls + if (value) { + autoRotateSpeed.enable() + autorotateReverse.enable() + } else { + autoRotateSpeed.disable() + autorotateReverse.disable() + } + }) + + this.autoRotateFormGrp.valueChanges.pipe( + debounceTime(160), + takeUntil(this.#onDestroy$), + ).subscribe({ + next: values => { + const { autoRotateSpeed, autorotate, autorotateReverse } = values + this.#setAutoRotate(autorotate, autoRotateSpeed, autorotateReverse) + }, + complete: () => { + this.#setAutoRotate(false, 0, false) + } + }) } addKeyFrame(){ @@ -65,41 +108,15 @@ export class KeyFrameCtrlCmp implements OnDestroy { ] } - private _autoRotateSpeed = 2 - get autoRotateSpeed(){ - return this._autoRotateSpeed - } - set autoRotateSpeed(val: number){ - this._autoRotateSpeed = val - this.updateAutoRotate() - } - - private _autoRotateReverse = false - get autoRotateReverse(){ - return this._autoRotateReverse - } - set autoRotateReverse(val: boolean){ - this._autoRotateReverse = val - this.updateAutoRotate() - } - - private _autoRotateFlag = false - get autoRotateFlag(){ - return this._autoRotateFlag - } - set autoRotateFlag(val: boolean){ - this._autoRotateFlag = val - this.updateAutoRotate() - } - - private updateAutoRotate(){ + #setAutoRotate(play: boolean, speed: number, reverse: boolean) { + this.viewerInternalSvc.applyInternalState<TAutoRotatePayload>({ "@id": getUuid(), "@type": 'TViewerInternalStateEmitterEvent', payload: { - play: this._autoRotateFlag, - speed: this._autoRotateSpeed, - reverse: this._autoRotateReverse + play, + speed, + reverse, }, viewerType: AUTO_ROTATE }) diff --git a/src/keyframesModule/keyframeCtrl/keyframeCtrl.template.html b/src/keyframesModule/keyframeCtrl/keyframeCtrl.template.html index d1b9382a39ac5656727b8779233b88a9a3c3faca..9017f0511325bdcbbb8cdea223d5e0b55cfd921b 100644 --- a/src/keyframesModule/keyframeCtrl/keyframeCtrl.template.html +++ b/src/keyframesModule/keyframeCtrl/keyframeCtrl.template.html @@ -5,81 +5,85 @@ <div *ngIf="currState; else noCurrStateTmpl" class="controller-container m-4"> - <mat-slide-toggle [(ngModel)]="autoRotateFlag">Auto Rotate</mat-slide-toggle> + <form [formGroup]="autoRotateFormGrp"> + <mat-slide-toggle formControlName="autorotate">Auto Rotate</mat-slide-toggle> - <div> - <mat-slider - min="1" - max="10" - step="0.2" - [disabled]="!autoRotateFlag" - [(ngModel)]="autoRotateSpeed"> - </mat-slider> - <span>Speed</span> - </div> - - <mat-slide-toggle - [(ngModel)]="autoRotateReverse" - [disabled]="!autoRotateFlag"> - Reverse - </mat-slide-toggle> - - <mat-divider class="m-2"></mat-divider> + <div> + <mat-slider + min="1" + max="10" + step="0.2"> + <input matSliderThumb formControlName="autoRotateSpeed"> + </mat-slider> + <span>Speed</span> + </div> + + <mat-slide-toggle formControlName="autorotateReverse"> + Reverse + </mat-slide-toggle> - <mat-slide-toggle [(ngModel)]="loopFlag">Loop</mat-slide-toggle> - <mat-slide-toggle [(ngModel)]="linearFlag">Linear Camera</mat-slide-toggle> + </form> - <button mat-button - (click)="togglePlay()"> - <ng-template [ngIf]="isPlaying" [ngIfElse]="isNotPlayingTmpl"> - <i class="fas fa-stop"></i> - Stop - </ng-template> - <ng-template #isNotPlayingTmpl> - <i class="fas fa-play"></i> - Play - </ng-template> - </button> + <form *ngIf="currViewerType === 'ThreeSurfer'" [formGroup]="frameFormGrp"> - <mat-divider class="m-2"></mat-divider> + <mat-divider class="m-2"></mat-divider> - <button - (click)="addKeyFrame()" - matTooltip="Add key frame. Shortcut: [a]" - mat-button - color="primary"> - <i class="fas fa-plus"></i> - Add Key Frame - </button> -</div> - -<mat-list cdkDropList (cdkDropListDropped)="drop($event)"> - <mat-list-item *ngFor="let state of internalStates" cdkDrag> - <button mat-icon-button - cdkDragHandle> - <i class="fas fa-grip-vertical"></i> + <mat-slide-toggle formControlName="loop">Loop</mat-slide-toggle> + <mat-slide-toggle formControlName="linearCamera">Linear Camera</mat-slide-toggle> + + <button mat-button + (click)="togglePlay()"> + <ng-template [ngIf]="isPlaying" [ngIfElse]="isNotPlayingTmpl"> + <i class="fas fa-stop"></i> + Stop + </ng-template> + <ng-template #isNotPlayingTmpl> + <i class="fas fa-play"></i> + Play + </ng-template> </button> - - <mat-form-field> - <mat-label>name</mat-label> - <input type="text" matInput [(ngModel)]="state.name"> - </mat-form-field> - - <mat-form-field class="duration-container"> - <mat-label>ms</mat-label> - <input type="number" matInput [(ngModel)]="state.duration"> - </mat-form-field> - - <button mat-icon-button - (click)="gotoFrame(state)"> - <i class="fas fa-map-marker-alt"></i> - </button> - - <button mat-icon-button - color="warn" - (click)="removeFrame(state)"> - <i class="fas fa-trash"></i> + + <mat-divider class="m-2"></mat-divider> + + <button + (click)="addKeyFrame()" + matTooltip="Add key frame. Shortcut: [a]" + mat-button + color="primary"> + <i class="fas fa-plus"></i> + Add Key Frame </button> - </mat-list-item> -</mat-list> \ No newline at end of file + <mat-list cdkDropList (cdkDropListDropped)="drop($event)"> + <mat-list-item *ngFor="let state of internalStates" cdkDrag> + <button mat-icon-button + cdkDragHandle> + <i class="fas fa-grip-vertical"></i> + </button> + + <!-- <mat-form-field> + <mat-label>name</mat-label> + <input type="text" matInput [(ngModel)]="state.name"> + </mat-form-field> + + <mat-form-field class="duration-container"> + <mat-label>ms</mat-label> + <input type="number" matInput [(ngModel)]="state.duration"> + </mat-form-field> + --> + <button mat-icon-button + (click)="gotoFrame(state)"> + <i class="fas fa-map-marker-alt"></i> + </button> + + <button mat-icon-button + color="warn" + (click)="removeFrame(state)"> + <i class="fas fa-trash"></i> + </button> + + </mat-list-item> + </mat-list> + </form> + +</div> diff --git a/src/keyframesModule/module.ts b/src/keyframesModule/module.ts index 0042fdbf4a8dd60efc0f048897950236fba2e3b8..0caf4dc9f4057c4d93e49387b684c9bb91863061 100644 --- a/src/keyframesModule/module.ts +++ b/src/keyframesModule/module.ts @@ -1,18 +1,17 @@ import { CommonModule } from "@angular/common"; import { NgModule } from "@angular/core"; -import { FormsModule } from "@angular/forms"; +import { ReactiveFormsModule } from "@angular/forms"; import { ComponentsModule } from "src/components"; import { AngularMaterialModule } from "src/sharedModules"; import { KeyFrameDirective } from "./keyframe.directive"; import { KeyFrameCtrlCmp } from "./keyframeCtrl/keyframeCtrl.component"; -import { KeyFrameService } from "./service"; @NgModule({ imports: [ CommonModule, AngularMaterialModule, ComponentsModule, - FormsModule, + ReactiveFormsModule, ], declarations: [ KeyFrameCtrlCmp, @@ -21,9 +20,6 @@ import { KeyFrameService } from "./service"; exports: [ KeyFrameCtrlCmp, KeyFrameDirective, - ], - providers: [ - KeyFrameService, ] }) diff --git a/src/keyframesModule/service.ts b/src/keyframesModule/service.ts deleted file mode 100644 index 98575c6b3fc1f6de5439791eaf01d0609c580d92..0000000000000000000000000000000000000000 --- a/src/keyframesModule/service.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { Injectable, OnDestroy } from "@angular/core"; -import { Store } from "@ngrx/store"; -import { BehaviorSubject, Subscription } from "rxjs"; -import { distinctUntilChanged } from "rxjs/operators"; -import { actions } from "src/state/atlasSelection"; - -@Injectable() -export class KeyFrameService implements OnDestroy { - - inSession$ = new BehaviorSubject(false) - private subs: Subscription[] = [] - private _inSession = false - get inSession(){ - return this._inSession - } - set inSession(val){ - this._inSession = val - this.inSession$.next(val) - } - - ngOnDestroy(){ - while(this.subs.length) this.subs.pop().unsubscribe() - } - - startKeyFrameSession(){ - this.inSession = true - } - endKeyFrameSession(){ - this.inSession = false - } - - constructor( - // private dialog: MatDialog, - private store: Store<any> - ){ - this.inSession$.pipe( - distinctUntilChanged() - ).subscribe(flag => { - - // TODO enable side bar when ready - this.store.dispatch( - actions.setViewerMode({ - viewerMode: flag - ? "key frame" - : null - }) - ) - }) - } -} \ No newline at end of file diff --git a/src/mouseoverModule/mouseOverCvt.pipe.ts b/src/mouseoverModule/mouseOverCvt.pipe.ts index 410f16478ac2acd31ab24498a4a22bb6be177ad1..fabd1d4899843579d939eb3ca94162b4ab85cc2e 100644 --- a/src/mouseoverModule/mouseOverCvt.pipe.ts +++ b/src/mouseoverModule/mouseOverCvt.pipe.ts @@ -9,7 +9,8 @@ function render<T extends keyof TOnHoverObj>(key: T, value: TOnHoverObj[T]){ return { icon: { fontSet: 'fas', - fontIcon: 'fa-brain' + fontIcon: 'fa-brain', + cls: 'fas fa-brain', }, text: seg?.name || "Unknown" } @@ -20,7 +21,8 @@ function render<T extends keyof TOnHoverObj>(key: T, value: TOnHoverObj[T]){ return [{ icon: { fontSet: 'fas', - fontIcon: 'fa-database' + fontIcon: 'fa-database', + cls: 'fas fa-database' }, text: name }] @@ -35,7 +37,8 @@ function render<T extends keyof TOnHoverObj>(key: T, value: TOnHoverObj[T]){ return [{ icon: { fontSet: 'fas', - fontIcon + fontIcon, + cls: `fas ${fontIcon}`, }, text: name || `Unnamed ${annotationType}` }] @@ -45,6 +48,7 @@ function render<T extends keyof TOnHoverObj>(key: T, value: TOnHoverObj[T]){ icon: { fontSet: 'fas', fontIcon: 'fa-file', + cls: 'fas fa-file' }, text: `Unknown hovered object` }] @@ -56,6 +60,7 @@ type TCvtOutput = { icon: { fontSet: string fontIcon: string + cls: string } text: string } diff --git a/src/overwrite.scss b/src/overwrite.scss index b4a9c686cf9575ce033aef5d83081f561cd711d5..4afc47b813371506ea02d6ed4327de72818e90b6 100644 --- a/src/overwrite.scss +++ b/src/overwrite.scss @@ -296,9 +296,10 @@ button.#{$llb} { width: 100%; - > .mat-button-wrapper + > .mdc-button__label { display: flex; + width: 100%; > .#{$llb}-icon { diff --git a/src/theme.scss b/src/theme.scss index 5a9d897e85e7e0043bab788a54592e6f5b3c72ec..6ca83466812bcde49e6bf12df7cf9d2357fa4169 100644 --- a/src/theme.scss +++ b/src/theme.scss @@ -188,3 +188,8 @@ a[mat-list-item] flex: 0 0 16.67%; } } + +[matListItemTitle] +{ + text-wrap: nowrap; +} diff --git a/src/ui/topMenu/topMenuCmp/topMenu.template.html b/src/ui/topMenu/topMenuCmp/topMenu.template.html index 1490cb3305e94efccb442a3ac7cf4235849b0af6..1a36b61c62ce1aab472b466f16964c316e6d8210 100644 --- a/src/ui/topMenu/topMenuCmp/topMenu.template.html +++ b/src/ui/topMenu/topMenuCmp/topMenu.template.html @@ -145,7 +145,7 @@ matTooltip="Toggle experimental flag"> <iav-dynamic-mat-button [iav-dynamic-mat-button-style]="matBtnStyle" - [iav-dynamic-mat-button-color]="matBtnColor" + iav-dynamic-mat-button-color="warn" iav-dynamic-mat-button-aria-label="Toggle experimental features"> <ng-template [ngIf]="currentState"> @@ -153,10 +153,7 @@ </ng-template> <ng-template [ngIf]="!currentState"> - <span class="fa-stack"> - <i class="fas fa-flask fa-stack-1x"></i> - <i class="fas fa-ban fa-stack-2x" style="color:Tomato"></i> - </span> + <i class="fas fa-wifi"></i> </ng-template> </iav-dynamic-mat-button> </div> diff --git a/src/util/directives/destroy.directive.ts b/src/util/directives/destroy.directive.ts new file mode 100644 index 0000000000000000000000000000000000000000..2bcb2d0cbb17e9c298f1c92771ca4c5dd64aef45 --- /dev/null +++ b/src/util/directives/destroy.directive.ts @@ -0,0 +1,13 @@ +import { Directive, OnDestroy } from "@angular/core"; +import { Subject } from "rxjs"; + +@Directive({ + standalone: true +}) +export class DestroyDirective implements OnDestroy{ + public destroyed$ = new Subject<null>() + ngOnDestroy(): void { + this.destroyed$.next() + this.destroyed$.complete() + } +} diff --git a/src/util/side-panel/side-panel.component.html b/src/util/side-panel/side-panel.component.html index 667876ee42702f5714ec80f84e3bb2e9dfd20a12..9462d087ad75fbabc73d27a5872028f918e07154 100644 --- a/src/util/side-panel/side-panel.component.html +++ b/src/util/side-panel/side-panel.component.html @@ -2,16 +2,17 @@ <ng-content select="[header]"></ng-content> </ng-template> -<mat-card class="mat-elevation-z4"> - <div +<mat-card class="mat-elevation-z4 foo"> + <mat-card-header [style.backgroundColor]="cardColor" - class="vanishing-border" [ngClass]="{ 'darktheme': darktheme === true, 'lighttheme': darktheme === false }"> - <ng-template [ngTemplateOutlet]="headerTmpl"></ng-template> + <mat-card-subtitle> + <ng-template [ngTemplateOutlet]="headerTmpl"></ng-template> + </mat-card-subtitle> <mat-card-title class="sxplr-custom-cmp text"> <ng-content select="[title]"></ng-content> @@ -20,7 +21,7 @@ <mat-card-subtitle> <ng-content select="[subtitle]"></ng-content> </mat-card-subtitle> - </div> + </mat-card-header> </mat-card> <ng-content></ng-content> diff --git a/src/util/side-panel/side-panel.component.scss b/src/util/side-panel/side-panel.component.scss index 44bed767cbc98d3cf2ecdc694422842c758e9177..29761d226924c9b315134c663f715e686b97bbc0 100644 --- a/src/util/side-panel/side-panel.component.scss +++ b/src/util/side-panel/side-panel.component.scss @@ -1,5 +1,4 @@ -.vanishing-border +mat-card-header { padding: 16px; - margin: -16px!important; -} +} \ No newline at end of file diff --git a/src/viewerModule/nehuba/layoutOverlay/nehuba.layoutOverlay/nehuba.layoutOverlay.template.html b/src/viewerModule/nehuba/layoutOverlay/nehuba.layoutOverlay/nehuba.layoutOverlay.template.html index 18968039f924b3625f13254ff2b2d6e0bf346f61..d9036f6746dc6a90c782647de4eec524c3199f8b 100644 --- a/src/viewerModule/nehuba/layoutOverlay/nehuba.layoutOverlay/nehuba.layoutOverlay.template.html +++ b/src/viewerModule/nehuba/layoutOverlay/nehuba.layoutOverlay/nehuba.layoutOverlay.template.html @@ -131,10 +131,7 @@ <!-- viewer ctrl --> <mat-menu #viewerCtrlMenu> - <viewer-ctrl-component - class="d-block m-2 ml-3 mr-3 sxplr-custom-cmp" - (click)="$event.stopPropagation()"> - </viewer-ctrl-component> + <viewer-ctrl-component (click)="$event.stopPropagation()"></viewer-ctrl-component> </mat-menu> <ng-template #sliceViewArrow> diff --git a/src/viewerModule/nehuba/statusCard/statusCard.component.ts b/src/viewerModule/nehuba/statusCard/statusCard.component.ts index 12e15afb36ba801c9945e7a16bf041389ecc20c2..423d69a9eac52ba9b93f1f2025cd461dabd66075 100644 --- a/src/viewerModule/nehuba/statusCard/statusCard.component.ts +++ b/src/viewerModule/nehuba/statusCard/statusCard.component.ts @@ -12,7 +12,7 @@ import { LoggingService } from "src/logging"; import { NehubaViewerUnit } from "../nehubaViewer/nehubaViewer.component"; import { Observable, Subscription, of, combineLatest } from "rxjs"; import { map, filter, startWith, throttleTime } from "rxjs/operators"; -import { MatBottomSheet, MatDialog } from "src/sharedModules/angularMaterial.exports" +import { Clipboard, MatBottomSheet, MatDialog, MatSnackBar } from "src/sharedModules/angularMaterial.exports" import { ARIA_LABELS, QUICKTOUR_DESC } from 'common/constants' import { UntypedFormControl } from "@angular/forms"; @@ -60,11 +60,14 @@ export class StatusCardComponent implements OnInit, OnChanges{ public SHARE_BTN_ARIA_LABEL = ARIA_LABELS.SHARE_BTN public SHOW_FULL_STATUS_PANEL_ARIA_LABEL = ARIA_LABELS.SHOW_FULL_STATUS_PANEL public HIDE_FULL_STATUS_PANEL_ARIA_LABEL = ARIA_LABELS.HIDE_FULL_STATUS_PANEL + public COPY_NAVIGATION_STRING = "Copy navigation coordinates to clipboard" constructor( private store$: Store<any>, private log: LoggingService, private bottomSheet: MatBottomSheet, private dialog: MatDialog, + private clipboard: Clipboard, + private snackbar: MatSnackBar, @Optional() @Inject(NEHUBA_INSTANCE_INJTKN) nehubaViewer$: Observable<NehubaViewerUnit> ) { @@ -201,4 +204,11 @@ export class StatusCardComponent implements OnInit, OnChanges{ ariaLabel }) } + + copyString(value: string){ + this.clipboard.copy(value) + this.snackbar.open(`Copied to clipboard!`, null, { + duration: 1000 + }) + } } diff --git a/src/viewerModule/nehuba/statusCard/statusCard.style.css b/src/viewerModule/nehuba/statusCard/statusCard.style.css index 4da5942081877f8c7722e3d549d3cdbd77965a88..a731c42cd30010291fafbd631db8deb9de5257ee 100644 --- a/src/viewerModule/nehuba/statusCard/statusCard.style.css +++ b/src/viewerModule/nehuba/statusCard/statusCard.style.css @@ -14,12 +14,7 @@ small[onHoverSegment] margin-left:2em; } -.share-btn -{ - right: 0; -} - .expandedContainer { - width: 20rem; + width: 26rem; } diff --git a/src/viewerModule/nehuba/statusCard/statusCard.template.html b/src/viewerModule/nehuba/statusCard/statusCard.template.html index d4daef878ea439f321f40fe5ebb06f6124e70867..4d6ca08573c522ac291a9d0cb219449a8d65cf95 100644 --- a/src/viewerModule/nehuba/statusCard/statusCard.template.html +++ b/src/viewerModule/nehuba/statusCard/statusCard.template.html @@ -3,7 +3,7 @@ [quick-tour-order]="quickTourData.order" #statusCardQT="quickTour"> <mat-card *ngIf="showFull; else showMin" - class="expandedContainer sxplr-p-2 sxplr-pt-1"> + class="expandedContainer"> <mat-card-content> @@ -75,17 +75,23 @@ [value]="navVal$ | async" #navInput="matInput"> - </mat-form-field> + <button mat-icon-button + iav-stop="click" + matSuffix + [attr.aria-label]="COPY_NAVIGATION_STRING" + (click)="copyString(navInput.value)"> + <i class="fas fa-copy"></i> + </button> - <div class="w-0 position-relative"> - <button + <button mat-icon-button + iav-stop="click" + matSuffix sxplr-share-view - [attr.aria-label]="SHARE_BTN_ARIA_LABEL" - mat-icon-button - class="position-absolute share-btn"> + [attr.aria-label]="SHARE_BTN_ARIA_LABEL"> <i class="fas fa-share-square"></i> </button> - </div> + </mat-form-field> + </div> <!-- cursor pos --> @@ -115,7 +121,15 @@ {{ navVal$ | async }} </span> - <mat-divider [vertical]="true"></mat-divider> + <ng-template [ngIf]="navVal$ | async" let-navVal> + + <button mat-icon-button + [attr.aria-label]="COPY_NAVIGATION_STRING" + (click)="copyString(navVal)"> + <i class="fas fa-copy"></i> + </button> + + </ng-template> <button mat-icon-button [attr.aria-label]="SHOW_FULL_STATUS_PANEL_ARIA_LABEL" diff --git a/src/viewerModule/nehuba/viewerCtrl/snapPerspectiveOrientation/snapPerspectiveOrientation.template.html b/src/viewerModule/nehuba/viewerCtrl/snapPerspectiveOrientation/snapPerspectiveOrientation.template.html index 3f2d7015b58a1e285dc958131b67877346d18d78..45c02cdfbfe6ab483fb587c0674e491c5a14cfd7 100644 --- a/src/viewerModule/nehuba/viewerCtrl/snapPerspectiveOrientation/snapPerspectiveOrientation.template.html +++ b/src/viewerModule/nehuba/viewerCtrl/snapPerspectiveOrientation/snapPerspectiveOrientation.template.html @@ -1,7 +1,3 @@ -<h3 class="text mat-h3"> - Snap perspective orientation -</h3> - <ng-container *ngTemplateOutlet="btnTmpl; context: { $implicit: EnumClassicalView.CORONAL }"> </ng-container> <ng-container *ngTemplateOutlet="btnTmpl; context: { $implicit: EnumClassicalView.SAGITTAL }"> diff --git a/src/viewerModule/nehuba/viewerCtrl/viewerCtrlCmp/viewerCtrlCmp.style.css b/src/viewerModule/nehuba/viewerCtrl/viewerCtrlCmp/viewerCtrlCmp.style.css index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..d55498969c3cb84f8cc07e976af87876f7c2b880 100644 --- a/src/viewerModule/nehuba/viewerCtrl/viewerCtrlCmp/viewerCtrlCmp.style.css +++ b/src/viewerModule/nehuba/viewerCtrl/viewerCtrlCmp/viewerCtrlCmp.style.css @@ -0,0 +1,4 @@ +mat-divider +{ + margin-top: 1rem; +} diff --git a/src/viewerModule/nehuba/viewerCtrl/viewerCtrlCmp/viewerCtrlCmp.template.html b/src/viewerModule/nehuba/viewerCtrl/viewerCtrlCmp/viewerCtrlCmp.template.html index 3145dbb6413f40d4db827001dd59f887c346d09d..3c76aae196955a032e50140a355054fb60498160 100644 --- a/src/viewerModule/nehuba/viewerCtrl/viewerCtrlCmp/viewerCtrlCmp.template.html +++ b/src/viewerModule/nehuba/viewerCtrl/viewerCtrlCmp/viewerCtrlCmp.template.html @@ -1,42 +1,60 @@ -<h3 class="text mat-h3"> - Perspective View -</h3> +<mat-card> + <mat-card-header> + <mat-card-title> -<mat-slide-toggle [(ngModel)]="removeOctantFlag" - [aria-label]="ARIA_LABELS.TOGGLE_FRONTAL_OCTANT" - name="remove-frontal-octant"> - <span class="text"> - Remove frontal octant - </span> - <i [matTooltip]="CONST.REMOVE_FRONTAL_OCTANT_HELPER_TEXT" class="fas fa-question"></i> -</mat-slide-toggle> + Perspective View + </mat-card-title> + </mat-card-header> -<!-- aux mesh controller --> -<ng-container *ngIf="auxMeshes$ | async as auxMeshes"> - <ng-template [ngIf]="auxMeshes.length > 0"> - <mat-divider class="mt-1 mb-1"></mat-divider> - - <h3 class="text mat-h3"> - <span> - Toggle auxiliary meshes + <mat-card-content> + <mat-slide-toggle [(ngModel)]="removeOctantFlag" + [aria-label]="ARIA_LABELS.TOGGLE_FRONTAL_OCTANT" + name="remove-frontal-octant"> + <span class="text"> + Remove frontal octant </span> - <i [matTooltip]="CONST.AUXMESH_DESC" class="fas fa-question"></i> - </h3> + <i [matTooltip]="CONST.REMOVE_FRONTAL_OCTANT_HELPER_TEXT" class="fas fa-question"></i> + </mat-slide-toggle> + </mat-card-content> + + <!-- aux mesh controller --> + <ng-container *ngIf="auxMeshes$ | async as auxMeshes"> - <form [formGroup]="auxMeshFormGroup"> - <mat-slide-toggle *ngFor="let auxMesh of auxMeshes; trackBy: trackByAtId" - [formControlName]="auxMesh['@id']" - class="d-block" - [name]="'toggle-aux-mesh-' + auxMesh['@id']"> - <span class="text"> - {{ auxMesh.displayName || auxMesh.name }} + <mat-divider></mat-divider> + + <mat-card-header> + <mat-card-title> + <span> + Toggle auxiliary meshes </span> - </mat-slide-toggle> - </form> - </ng-template> -</ng-container> + </mat-card-title> + </mat-card-header> + <mat-card-content> + <form [formGroup]="auxMeshFormGroup"> + <mat-slide-toggle *ngFor="let auxMesh of auxMeshes; trackBy: trackByAtId" + [formControlName]="auxMesh['@id']" + class="d-block" + [name]="'toggle-aux-mesh-' + auxMesh['@id']"> + <span class="text"> + {{ auxMesh.displayName || auxMesh.name }} + </span> + <i [matTooltip]="CONST.AUXMESH_DESC" class="fas fa-question"></i> + </mat-slide-toggle> + </form> + </mat-card-content> + </ng-container> -<mat-divider class="mt-1 mb-1"></mat-divider> -<snap-perspective-orientation-cmp></snap-perspective-orientation-cmp> + <mat-divider></mat-divider> + <mat-card-header> + <mat-card-title> + <span> + Snap perspective orientation + </span> + </mat-card-title> + </mat-card-header> + <mat-card-content> + <snap-perspective-orientation-cmp></snap-perspective-orientation-cmp> + </mat-card-content> +</mat-card> diff --git a/src/viewerModule/viewerCmp/viewerCmp.style.css b/src/viewerModule/viewerCmp/viewerCmp.style.css index cc90a9f067f0655747632b66d4895cec23d2b3a4..f1ba346a219f1b1b460a0c44c23004d0dae57bf9 100644 --- a/src/viewerModule/viewerCmp/viewerCmp.style.css +++ b/src/viewerModule/viewerCmp/viewerCmp.style.css @@ -22,7 +22,7 @@ .tab-toggle { - margin-left: -2rem; + margin-left: -1rem; padding-right: 0.75rem; margin-right: 1rem; text-align: right; @@ -75,8 +75,6 @@ mat-drawer .min-tray-explr-btn { width: 100%; - padding-left: 0.5rem; - padding-right: 0.5rem; margin-top: -1rem; } @@ -98,13 +96,13 @@ logo-container opacity: 0.2; } -mat-list[dense].contextual-block +mat-list.contextual-block { display: inline-block; background-color:rgba(200,200,200,0.8); } -:host-context([darktheme="true"]) mat-list[dense].contextual-block +:host-context([darktheme="true"]) mat-list.contextual-block { background-color : rgba(30,30,30,0.8); } @@ -154,20 +152,26 @@ sxplr-smart-chip [mat-icon-button] margin-right: -1.5rem; } -.loadingText +sxplr-sapiviews-core-region-region-list-item { - height: 1rem; + display: flex; + align-items: center; } -sxplr-overlay-ui +.context-menu-container { - position: absolute; - width: 100%; - height: 100%; + margin: 0 -16px; } -sxplr-sapiviews-core-region-region-list-item +.centered { display: flex; + justify-content: center; align-items: center; } + +.leave-me-alone +{ + margin-top: 0.5rem; + margin-bottom: 0.5rem; +} diff --git a/src/viewerModule/viewerCmp/viewerCmp.template.html b/src/viewerModule/viewerCmp/viewerCmp.template.html index 62d496a668f9b65da7aad03ac5d140ff2559ada4..e31ad27551e54d3d5d4d7ff1bfdc599d9edc869a 100644 --- a/src/viewerModule/viewerCmp/viewerCmp.template.html +++ b/src/viewerModule/viewerCmp/viewerCmp.template.html @@ -9,36 +9,24 @@ <logo-container></logo-container> </div> - <div *ngIf="(media.mediaBreakPoint$ | async) < 2" floatingMouseContextualContainerDirective> + <div *ngIf="(media.mediaBreakPoint$ | async) < 2" + floatingMouseContextualContainerDirective + iav-mouse-hover + #iavMouseHoverContextualBlock="iavMouseHover"> - <div class="h-0" - iav-mouse-hover - #iavMouseHoverContextualBlock="iavMouseHover"> - </div> - <mat-list dense class="contextual-block"> + <mat-list class="contextual-block"> <mat-list-item *ngFor="let cvtOutput of iavMouseHoverContextualBlock.currentOnHoverObs$ | async | mouseoverCvt" class="h-auto"> - - <mat-icon - [fontSet]="cvtOutput.icon.fontSet" - [fontIcon]="cvtOutput.icon.fontIcon" - mat-list-icon> - </mat-icon> - - <div matLine>{{ cvtOutput.text }}</div> - + <span class="centered" matListItemIcon [class]="cvtOutput.icon.cls"></span> + <span matListItemTitle>{{ cvtOutput.text }}</span> </mat-list-item> - </mat-list> - <!-- TODO Potentially implementing plugin contextual info --> </div> </div> </div> -<!-- <sxplr-overlay-ui></sxplr-overlay-ui> --> - <!-- master draw container --> <ng-template [ngIf]="view$ | async" let-view> @@ -243,7 +231,7 @@ <ng-template [ngIf]="view$ | async" let-view> <!-- if no selected regions, show spatial search --> - <div *ngIf="(view.selectedRegions || []).length === 0" class="sxplr-p-1 w-100"> + <div *ngIf="(view.selectedRegions || []).length === 0" class="sxplr-mt-1 w-100"> <ng-template [ngTemplateOutlet]="spatialFeatureListTmpl" [ngTemplateOutletContext]="{ @@ -279,7 +267,7 @@ }" [style.backgroundColor]="sapiRegion.regionRgbString"> <span class="text sxplr-custom-cmp"> - Explore + Explore Foo </span> </button> </div> @@ -289,44 +277,40 @@ <!-- tab to minimize mini tray --> - <div class="tab-toggle-container"> - - <div [ngClass]="(minTrayVisSwitch.switchState$ | async) ? '' : 'd-none'"> + <div class="tab-toggle-container" [ngClass]="(minTrayVisSwitch.switchState$ | async) ? '' : 'd-none'"> - <ng-template [ngTemplateOutlet]="tabTmpl_defaultTmpl" [ngTemplateOutletContext]="{ - fontIcon: 'fas fa-chevron-left', - matColor: null, - click: minTrayVisSwitch.toggle.bind(minTrayVisSwitch) - }"> - </ng-template> - </div> - - <div [ngClass]="(minTrayVisSwitch.switchState$ | async) ? 'd-none' : ''"> + <ng-template [ngTemplateOutlet]="tabTmpl_defaultTmpl" [ngTemplateOutletContext]="{ + fontIcon: 'fas fa-chevron-left', + matColor: null, + click: minTrayVisSwitch.toggle.bind(minTrayVisSwitch) + }"> + </ng-template> + </div> + + <div class="tab-toggle-container" [ngClass]="(minTrayVisSwitch.switchState$ | async) ? 'd-none' : ''"> - <ng-template [ngIf]="voiFeatureEntryCmp && (voiFeatureEntryCmp.totals$ | async)" - [ngIfElse]="noBadgeTmpl" - let-totals> + <ng-template [ngIf]="voiFeatureEntryCmp && (voiFeatureEntryCmp.totals$ | async)" + [ngIfElse]="noBadgeTmpl" + let-totals> - <ng-template [ngTemplateOutlet]="tabTmpl_defaultTmpl" [ngTemplateOutletContext]="{ - fontIcon: 'fas fa-search', - matColor: 'primary', - click: minTrayVisSwitch.toggle.bind(minTrayVisSwitch), - badge: totals - }"> - </ng-template> + <ng-template [ngTemplateOutlet]="tabTmpl_defaultTmpl" [ngTemplateOutletContext]="{ + fontIcon: 'fas fa-search', + matColor: 'primary', + click: minTrayVisSwitch.toggle.bind(minTrayVisSwitch), + badge: totals + }"> </ng-template> + </ng-template> - <ng-template #noBadgeTmpl> + <ng-template #noBadgeTmpl> - <ng-template [ngTemplateOutlet]="tabTmpl_defaultTmpl" [ngTemplateOutletContext]="{ - fontIcon: 'fas fa-search', - matColor: 'primary', - click: minTrayVisSwitch.toggle.bind(minTrayVisSwitch) - }"> - </ng-template> + <ng-template [ngTemplateOutlet]="tabTmpl_defaultTmpl" [ngTemplateOutletContext]="{ + fontIcon: 'fas fa-search', + matColor: 'primary', + click: minTrayVisSwitch.toggle.bind(minTrayVisSwitch) + }"> </ng-template> - </div> - + </ng-template> </div> </ng-template> @@ -370,13 +354,13 @@ <!-- status panel for (for nehuba viewer) --> <iav-cmp-viewer-nehuba-status *ngIf="(useViewer$ | async) === 'nehuba'" - class="pe-all mt-2 muted-7 d-inline-block v-align-top"> + class="pe-all sxplr-mt-1 muted-7 d-inline-block v-align-top"> </iav-cmp-viewer-nehuba-status> <button mat-icon-button sxplr-share-view *ngIf="(useViewer$ | async) === 'threeSurfer'" - class="pe-all mt-2 muted-7 d-inline-block v-align-top"> + class="pe-all mt-1 muted-7 d-inline-block v-align-top"> <i class="fas fa-share-square"></i> </button> </ng-template> @@ -401,7 +385,7 @@ }"> </ng-container> - <annotating-tools-panel class="z-index-10 d-block" + <annotating-tools-panel class="z-index-10 leave-me-alone" #toolPanel="annoToolsPanel"> </annotating-tools-panel> </ng-template> @@ -437,14 +421,22 @@ <!-- special mode top right --> <ng-template #specialTopRightTmpl let-mode="mode"> <mat-card class="mat-card-sm pe-all m-4"> - <span> - {{ mode }} - </span> - <button mat-icon-button - color="warn" - (click)="exitSpecialViewMode()"> - <i class="fas fa-times"></i> - </button> + <mat-card-header> + <mat-card-subtitle> + <span> + {{ mode }} + </span> + + <button mat-icon-button + color="warn" + (click)="exitSpecialViewMode()"> + <i class="fas fa-times"></i> + </button> + </mat-card-subtitle> + </mat-card-header> + <mat-card-content> + + </mat-card-content> </mat-card> </ng-template> @@ -542,50 +534,48 @@ <!-- auto complete search box --> <ng-template #autocompleteTmpl let-showTour="showTour"> - <ng-template [ngIf]="view$ | async" let-view> - - <div class="ml-2 mr-2 pe-all auto-complete-container"> - - <sxplr-sapiviews-core-rich-regionlistsearch - class="mat-elevation-z4" - [sxplr-sapiviews-core-rich-regionlistsearch-regions]="allAvailableRegions$ | async" - [sxplr-sapiviews-core-rich-regionlistsearch-mapped-region-names]="view.labelMappedRegionNames" - [sxplr-sapiviews-core-rich-regionlistsearch-current-search]="view.selectedRegions.length === 1 ? view.selectedRegions[0].name : null" - (sxplr-sapiviews-core-rich-regionlistsearch-region-select)="selectRoi($event)" - (sxplr-sapiviews-core-rich-regionlistsearch-region-toggle)="toggleRoi($event)"> - <ng-template regionTemplate let-region> - <div class="sxplr-d-flex"> - <button - mat-icon-button - class="sxplr-mt-a sxplr-mb-a"> - <i [ngClass]="(view.selectedRegions | includes : region) ? 'fa-circle' : 'fa-none'" class="fas"></i> - </button> - - <sxplr-sapiviews-core-region-region-list-item - [sxplr-sapiviews-core-region-region]="region"> - </sxplr-sapiviews-core-region-region-list-item> - </div> - </ng-template> - <button mat-icon-button - search-input-suffix - *ngIf="view.selectedRegions.length > 0" - (click)="clearRoi()"> - <i class="fas fa-times"></i> - </button> - </sxplr-sapiviews-core-rich-regionlistsearch> - + <div *ngIf="view$ | async; let view" + class="pe-all auto-complete-container"> + <sxplr-sapiviews-core-rich-regionlistsearch + class="mat-elevation-z4" + [sxplr-sapiviews-core-rich-regionlistsearch-regions]="allAvailableRegions$ | async" + [sxplr-sapiviews-core-rich-regionlistsearch-mapped-region-names]="view.labelMappedRegionNames" + [sxplr-sapiviews-core-rich-regionlistsearch-current-search]="view.selectedRegions.length === 1 ? view.selectedRegions[0].name : null" + (sxplr-sapiviews-core-rich-regionlistsearch-region-select)="selectRoi($event)" + (sxplr-sapiviews-core-rich-regionlistsearch-region-toggle)="toggleRoi($event)"> + <ng-template regionTemplate let-region> + <div class="sxplr-d-flex"> + <button + mat-icon-button + class="sxplr-mt-a sxplr-mb-a"> + <i [ngClass]="(view.selectedRegions | includes : region) ? 'fa-circle' : 'fa-none'" class="fas"></i> + </button> + + <sxplr-sapiviews-core-region-region-list-item + [sxplr-sapiviews-core-region-region]="region"> + </sxplr-sapiviews-core-region-region-list-item> + </div> + </ng-template> + <button mat-icon-button + search-input-suffix + *ngIf="view.selectedRegions.length > 0" + (click)="clearRoi()"> + <i class="fas fa-times"></i> + </button> <button mat-icon-button color="primary" + search-input-prefix + iav-stop="click" [sxplr-dialog]="regionHierarchyTmpl" sxplr-dialog-size="xl"> <i class="fas fa-sitemap"></i> </button> - - <div class="w-100 h-100 position-absolute sxplr-pe-none" *ngIf="showTour"> - </div> - + + </sxplr-sapiviews-core-rich-regionlistsearch> + + <div class="w-100 h-100 position-absolute sxplr-pe-none" *ngIf="showTour"> </div> - </ng-template> + </div> </ng-template> <!-- template for rendering tab --> @@ -600,7 +590,6 @@ <ng-template [ngIf]="isOpen" [ngIfElse]="tabTmpl_closedTmpl"> <ng-template [ngTemplateOutlet]="tabTmpl_defaultTmpl" [ngTemplateOutletContext]="{ - matColor: 'basic', fontIcon: 'fa-chevron-left', click: click, badge: badge @@ -843,8 +832,7 @@ <button mat-raised-button class="mat-elevation-z8" [attr.aria-label]="ARIA_LABELS.COLLAPSE" - (click)="collapse()" - color="basic"> + (click)="collapse()"> <i class="fas fa-chevron-up"></i> <span> collapse @@ -864,84 +852,70 @@ <!-- context menu template --> <ng-template #viewerCtxMenuTmpl let-tmplRefs="tmplRefs"> - <mat-card class="sxplr-p-0 d-flex flex-column" + <mat-card [iav-key-listener]="[{type: 'keydown', target: 'document', capture: true, key: 'Esc'}]" (iav-key-event)="disposeCtxMenu()" (iav-outsideClick)="disposeCtxMenu()"> - <mat-card-content *ngFor="let tmplRef of tmplRefs" - class="m-0" - [ngStyle]="{order: tmplRef.order || 0}"> - <mat-divider></mat-divider> - - <!-- template provided --> - <ng-template [ngIf]="tmplRef.tmpl" - [ngIfElse]="fallbackTmpl" - [ngTemplateOutlet]="tmplRef.tmpl" - [ngTemplateOutletContext]="{$implicit: tmplRef.data}"> - </ng-template> - - <!-- template not provided --> - <ng-template #fallbackTmpl> - {{ tmplRef.data.message || 'test' }} - </ng-template> + + <mat-card-content class="d-flex flex-column"> + <div *ngFor="let tmplRef of tmplRefs" + [ngStyle]="{ order: tmplRef.order || 0 }" + class="context-menu-container"> + + <ng-template [ngIf]="tmplRef.tmpl" + [ngIfElse]="fallbackTmpl" + [ngTemplateOutlet]="tmplRef.tmpl" + [ngTemplateOutletContext]="{ $implicit: tmplRef.data }"> + </ng-template> + + <ng-template #fallbackTmpl> + {{ tmplRef.data.message || 'test' }} + </ng-template> + </div> - <mat-divider></mat-divider> </mat-card-content> + </mat-card> </ng-template> <ng-template #lastViewedPointTmpl let-data> - <div class="text-muted sxplr-m-2"> - Last selected spatial object - </div> - <button mat-button class="sxplr-list-like-button" - (click)="selectPoint(data, data.template)"> - + <mat-action-list class="sxplr-p-0"> + <mat-divider></mat-divider> + <div mat-subheader>Last selected spatial object</div> + <ng-template [ngIf]="data?.point"> + + <button mat-list-item (click)="selectPoint(data, data.template)"> + <span matListItemIcon> + <i class="fas fa-history"></i> + </span> - <div class="sxplr-list-like-button-icon"> - <i class="fas fa-history"></i> - </div> + <!-- <span matListItemLine class="text-muted">Last selected spatial object</span> --> + <span matListItemLine>{{ data.point | nmToMm | numbers | addUnitAndJoin : '' }} (mm)</span> + <span matListItemLine class="text-muted">Point</span> + <span matListItemLine class="text-muted">{{ data.template.name }}</span> + + </button> + </ng-template> - <div class="sxplr-list-like-button-body"> + <ng-template [ngIf]="data?.face && data?.vertices"> - <ng-template [ngIf]="data?.point"> - <span class="sxplr-list-like-button-body-line"> - {{ data.point | nmToMm | numbers | addUnitAndJoin : '' }} (mm) - </span> - <span class="sxplr-list-like-button-body-line text-muted"> - Point + <button mat-list-item (click)="selectPoint(data, data.template)"> + <span matListItemIcon> + <i class="fas fa-history"></i> </span> - </ng-template> - - <ng-template [ngIf]="data.face && data.vertices"> - - <span class="sxplr-list-like-button-body-line"> + + <span matListItemLine> Face Index: {{ data.face }}, Vertices Index: {{ data.vertices | addUnitAndJoin : '' }} </span> - <span class="sxplr-list-like-button-body-line text-muted"> + <span matListItemLine class="text-muted"> Mesh Face </span> - </ng-template> - - <span class="sxplr-list-like-button-body-line text-muted"> - <span> - {{ data.template.name }} - </span> - - <ng-template [ngIf]="templateSelected$ | async" let-templateSelected> - <ng-template [ngIf]="templateSelected.id !== data.template.id"> - <mat-icon fontSet="fas" fontIcon="fa-exclamation-triangle" - color="warn" - matTooltip="Different template spaces detected."> - </mat-icon> - </ng-template> - </ng-template> + <span matListItemLine class="text-muted">{{ data.template.name }}</span> - </span> - </div> + </button> + </ng-template> + </mat-action-list> - <!-- {{ data.point | json }} --> - </button> </ng-template> <!-- viewer status ctx menu --> @@ -953,58 +927,52 @@ <!-- volumetric i.e. nehuba --> <ng-container *ngSwitchCase="'nehuba'"> - <button mat-button class="sxplr-list-like-button" - (click)="selectPoint({ point: context.payload.mouse.real }, data.metadata.template)"> - - <div class="sxplr-list-like-button-icon"> - <i class="fas fa-map"></i> - </div> - - <div class="sxplr-list-like-button-body"> - - <span class="sxplr-list-like-button-body-line"> + <mat-action-list class="sxplr-p-0"> + <button mat-list-item + (click)="selectPoint({ point: context.payload.mouse.real }, data.metadata.template)"> + + <span matListItemIcon> + <i class="fas fa-map"></i> + </span> + + <span matListItemLine> {{ context.payload.mouse.real | nmToMm | numbers | addUnitAndJoin : '' }} (mm) </span> - <span class="sxplr-list-like-button-body-line text-muted"> + <span matListItemLine class="text-muted"> Point </span> - <span class="sxplr-list-like-button-body-line text-muted"> + <span matListItemLine class="text-muted"> {{ data.metadata.template.name }} </span> - </div> + </button> + </mat-action-list> - </button> </ng-container> <ng-container *ngSwitchCase="'threeSurfer'"> <ng-template [ngIf]="context.payload?.faceIndex" let-faceIndex> <ng-template [ngIf]="context.payload?.vertexIndices" let-vertexIndices> - <button mat-button class="sxplr-list-like-button" - (click)="selectPoint({ face: faceIndex, vertices: vertexIndices }, data.metadata.template)" - disabled> - - <div class="sxplr-list-like-button-icon"> - <i class="fas fa-map"></i> - </div> - - <div class="sxplr-list-like-button-body"> - - <span class="sxplr-list-like-button-body-line"> + <mat-action-list class="sxplr-p-0"> + <button mat-list-item + (click)="selectPoint({ face: faceIndex, vertices: vertexIndices }, data.metadata.template)" + disabled> + + <span matListItemIcon> + <i class="fas fa-map"></i> + </span> + + <span matListItemLine> Face Index: {{ faceIndex }}, Vertices Index: {{ vertexIndices | addUnitAndJoin : '' }} </span> - <span class="sxplr-list-like-button-body-line text-muted"> - Mesh Face + <span matListItemLine class="text-muted"> + Mesh Face (Not selectable for now) </span> - <span class="sxplr-list-like-button-body-line text-muted"> + <span matListItemLine class="text-muted"> {{ data.metadata.template.name }} </span> - <i class="sxplr-list-like-button-body-line text-muted"> - (Not selectable at the moment) - </i> - </div> - - </button> + </button> + </mat-action-list> </ng-template> </ng-template> @@ -1022,30 +990,22 @@ <!-- viewer state hover ctx menu --> <ng-template #viewerStatusRegionCtxMenu let-data> <!-- hovered ROIs --> - <ng-template ngFor [ngForOf]="data.metadata.hoveredRegions" - let-region - let-first="first"> - - <mat-divider class="top-0" *ngIf="!first"></mat-divider> + <ng-template ngFor [ngForOf]="data.metadata.hoveredRegions" let-region> - <button mat-button - (click)="$event.ctrlKey ? toggleRoi(region) : selectRoi(region)" - class="sxplr-list-like-button"> - - <div class="sxplr-list-like-button-icon"> - <i class="fas fa-brain"></i> - </div> - - <div class="sxplr-list-like-button-body"> - - <span class="sxplr-list-like-button-body-line"> + <mat-action-list class="sxplr-p-0"> + <button mat-list-item (click)="$event.ctrlKey ? toggleRoi(region) : selectRoi(region)"> + <span matListItemIcon> + <i class="fas fa-brain"></i> + </span> + + <span matListItemLine> {{ region.name }} </span> - <span class="sxplr-list-like-button-body-line text-muted"> + <span matListItemLine class="text-muted"> Brain region </span> - </div> - </button> + </button> + </mat-action-list> </ng-template> </ng-template> @@ -1108,7 +1068,7 @@ <!-- spatial search tmpls --> <ng-template #spatialFeatureListTmpl let-view="view"> - <mat-card class="sxplr-pe-all" + <mat-card class="sxplr-pe-all overflow-hidden" [ngClass]="{ 'sxplr-d-none': !(voiSwitch.switchState$ | async) || (voiFeatureEntryCmp.totals$ | async) === 0 }"> @@ -1131,15 +1091,6 @@ </mat-card-subtitle> </mat-card-header> - <div class="loadingText"> - - <ng-template [ngIf]="voiFeatureEntryCmp.busy$ | async"> - <spinner-cmp class="sxplr-d-inline-block"></spinner-cmp> - <span> - Loading Wireframes ... - </span> - </ng-template> - </div> </mat-card> <sxplr-feature-entry @@ -1154,7 +1105,9 @@ <mat-card [ngClass]="{ 'sxplr-d-none': (voiFeatureEntryCmp.totals$ | async) > 0 }"> - No spatial features found. + <mat-card-content> + No spatial features found. + </mat-card-content> </mat-card> <button mat-raised-button @@ -1183,6 +1136,13 @@ </ng-template> </button> + <div> + <mat-progress-bar + mode="indeterminate" + *ngIf="(voiFeatureEntryCmp.busy$ | async)"> + </mat-progress-bar> + </div> + <!-- TODO voiBbox directive is used to draw outlines for VOI this has been temporarily disabled, since datasource is paginated and how bounding boxes are drawn needs to be reconsidered -->