diff --git a/src/atlasComponents/sapiViews/core/rich/ATPSelector/pureDumb/pureATPSelector.components.ts b/src/atlasComponents/sapiViews/core/rich/ATPSelector/pureDumb/pureATPSelector.components.ts index ef1976937906642f2d5ca1cbf1daea9a89d8d30a..04b217ef5cd952c9e21229e07dd6ec038fb9a984 100644 --- a/src/atlasComponents/sapiViews/core/rich/ATPSelector/pureDumb/pureATPSelector.components.ts +++ b/src/atlasComponents/sapiViews/core/rich/ATPSelector/pureDumb/pureATPSelector.components.ts @@ -1,6 +1,9 @@ -import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnChanges, Output, SimpleChanges } from "@angular/core"; +import { AfterViewInit, ChangeDetectionStrategy, Component, EventEmitter, HostBinding, Input, OnChanges, OnDestroy, Output, QueryList, SimpleChanges, ViewChildren } from "@angular/core"; +import { BehaviorSubject, Subscription, combineLatest, concat, merge, of } from "rxjs"; +import { map, switchMap } from "rxjs/operators"; import { SxplrAtlas, SxplrParcellation, SxplrTemplate } from "src/atlasComponents/sapi/sxplrTypes"; import { FilterGroupedParcellationPipe, GroupedParcellation } from "src/atlasComponents/sapiViews/core/parcellation"; +import { SmartChip } from "src/components/smartChip"; export const darkThemePalette = [ "#141414", @@ -40,7 +43,9 @@ const pipe = new FilterGroupedParcellationPipe() changeDetection: ChangeDetectionStrategy.OnPush, }) -export class PureATPSelector implements OnChanges{ +export class PureATPSelector implements OnChanges, AfterViewInit, OnDestroy{ + + #subscriptions: Subscription[] = [] @Input('sxplr-pure-atp-selector-color-palette') colorPalette: string[] = darkThemePalette @@ -67,6 +72,15 @@ export class PureATPSelector implements OnChanges{ @Output('sxplr-pure-atp-selector-on-select') selectLeafEmitter = new EventEmitter<Partial<ATP>>() + @ViewChildren(SmartChip) + smartChips: QueryList<SmartChip<object>> + + #menuOpen$ = new BehaviorSubject<{ some: boolean, all: boolean, none: boolean }>({ some: false, all: false, none: false }) + menuOpen$ = this.#menuOpen$.asObservable() + + @HostBinding('attr.data-menu-open') + menuOpen: 'some'|'all'|'none' = null + getChildren(parc: GroupedParcellation|SxplrParcellation){ return (parc as GroupedParcellation).parcellations || [] } @@ -98,4 +112,55 @@ export class PureATPSelector implements OnChanges{ } } } + + ngAfterViewInit(): void { + this.#subscriptions.push( + concat( + of(null), + this.smartChips.changes, + ).pipe( + switchMap(() => + combineLatest( + Array.from(this.smartChips).map(chip => + concat( + of(false), + merge( + chip.menuOpened.pipe( + map(() => true) + ), + chip.menuClosed.pipe( + map(() => false) + ) + ) + ) + ) + ) + ), + ).subscribe(arr => { + const newVal = { + some: arr.some(val => val), + all: arr.every(val => val), + none: arr.every(val => !val), + } + this.#menuOpen$.next(newVal) + + this.menuOpen = null + if (newVal.none) { + this.menuOpen = 'none' + } + if (newVal.all) { + this.menuOpen = 'all' + } + if (newVal.some) { + this.menuOpen = 'some' + } + }) + ) + } + + ngOnDestroy(): void { + while (this.#subscriptions.length > 0) { + this.#subscriptions.pop().unsubscribe() + } + } } diff --git a/src/atlasComponents/sapiViews/core/rich/ATPSelector/pureDumb/pureATPSelector.style.scss b/src/atlasComponents/sapiViews/core/rich/ATPSelector/pureDumb/pureATPSelector.style.scss index cc3249fac80760e354fd17db88b32a77cdc92c25..3bd40c2661a16e9eea6f01d9826ba55961e74e85 100644 --- a/src/atlasComponents/sapiViews/core/rich/ATPSelector/pureDumb/pureATPSelector.style.scss +++ b/src/atlasComponents/sapiViews/core/rich/ATPSelector/pureDumb/pureATPSelector.style.scss @@ -1,3 +1,17 @@ +:host-context([experimental="true"]) :host +{ + opacity: 0.5; + transition: opacity ease-in-out 160ms; + + &:hover, + &[data-menu-open="some"], + &[data-menu-open="all"] + { + opacity: 1; + } +} + + :host { display: inline-flex; diff --git a/src/atlasComponents/sapiViews/core/rich/ATPSelector/pureDumb/pureATPSelector.template.html b/src/atlasComponents/sapiViews/core/rich/ATPSelector/pureDumb/pureATPSelector.template.html index f8ed4de44d6360bcd5b76090d5d914ba04d012c1..9aaf8f9645e8c84970f89add116dd4e3d8a45ef0 100644 --- a/src/atlasComponents/sapiViews/core/rich/ATPSelector/pureDumb/pureATPSelector.template.html +++ b/src/atlasComponents/sapiViews/core/rich/ATPSelector/pureDumb/pureATPSelector.template.html @@ -19,14 +19,9 @@ </span> </ng-template> - <ng-template sxplrSmartChipContent> - <span class="chip-text"> - {{ ATP.parcellation.shortName }} - </span> - <ng-content select="[parcellation-chip-suffix]"> - </ng-content> - - <button iav-stop="mousedown click" + <ng-template sxplrSmartChipAction> + <button + iav-stop="mousedown click" class="icons" mat-icon-button sxplr-dialog @@ -39,6 +34,18 @@ <i class="fas fa-info"></i> </button> </ng-template> + + <ng-template sxplrSmartChipAction> + <ng-content select="[parcellation-chip-suffix]"> + </ng-content> + </ng-template> + + <ng-template sxplrSmartChipContent> + <span class="chip-text"> + {{ ATP.parcellation.shortName }} + </span> + + </ng-template> <ng-template sxplrSmartChipMenu let-parc> <ng-container *ngTemplateOutlet="optionTmpl; context: { diff --git a/src/components/smartChip/component/smartChip.component.ts b/src/components/smartChip/component/smartChip.component.ts index 99eed11983dd317c32c5f4875e93f640caf8bf95..a6dac2d1a45acfc848f6baa89af2ca06ef23b233 100644 --- a/src/components/smartChip/component/smartChip.component.ts +++ b/src/components/smartChip/component/smartChip.component.ts @@ -1,8 +1,9 @@ -import { ChangeDetectionStrategy, Component, ContentChild, EventEmitter, HostBinding, Input, OnChanges, Output, SimpleChanges } from "@angular/core"; +import { ChangeDetectionStrategy, Component, ContentChild, ContentChildren, EventEmitter, HostBinding, Input, OnChanges, Output, QueryList, SimpleChanges } from "@angular/core"; import { SmartChipContent } from "../smartChip.content.directive" import { SmartChipMenu } from "../smartChip.menu.directive"; import { rgbToHsl, hexToRgb } from 'common/util' import { SmartChipHeader } from "../smartChip.header.directive"; +import { SmartChipAction } from "../smartChip.action.directive"; const cssColorToHsl = (input: string) => { if (/rgb/i.test(input)) { @@ -66,6 +67,14 @@ export class SmartChip<T extends object> implements OnChanges{ @Output('itemClicked') itemClicked = new EventEmitter<T>() + @Output('menuOpened') + menuOpened = new EventEmitter() + @Output('menuClosed') + menuClosed = new EventEmitter() + + @ContentChildren(SmartChipAction) + actionTmpls: QueryList<SmartChipAction> + @ContentChild(SmartChipContent) contentTmpl: SmartChipContent diff --git a/src/components/smartChip/component/smartChip.style.scss b/src/components/smartChip/component/smartChip.style.scss index f6d54f0079aab5db18090676a8200bee03083390..df88b7eed1ed5827254cc9e947b766c508e6db52 100644 --- a/src/components/smartChip/component/smartChip.style.scss +++ b/src/components/smartChip/component/smartChip.style.scss @@ -6,42 +6,65 @@ position: relative; } -:host-context([experimental=true]) :host -{ - min-height: 44px; -} -:host-context([experimental=true]) .smart-chip +:host-context([experimental=true]) { - display: inline-flex; - flex-direction: column!important; - border-radius: 22px; - align-items: center; - justify-content: start; -} + :host + { + min-height: 44px; + } -:host-context([experimental=true]) .header -{ - font-size: 80%; - margin-top:-0.5rem; - margin-bottom:0.3rem; - flex-basis: 1rem; -} + .smart-chip + { + display: inline-flex; + flex-direction: row; + border-radius: 22px; + justify-content: center; -:host-context([experimental=true]) .body -{ - width: 100%; - flex: 1 1 0; - display: flex; - align-items: start; -} -:host-context([experimental=true]) .body>.body-content-wrapper -{ - height: 1px; - width: 100%; - overflow: visible; - display: flex; - align-items: center; + >.text + { + height: 0px; + overflow: visible; + display: inline-flex; + flex-direction: column; + justify-content: center; + } + + >.icons + { + height: 0px; + overflow: visible; + display: inline-flex; + flex-direction: row; + align-items: center; + } + } + + .header + { + + font-size: 80%; + margin-top:-0.5rem; + margin-bottom:0.3rem; + flex-basis: 1rem; + } + + .body + { + width: 100%; + flex: 1 1 0; + display: flex; + align-items: start; + + >.body-content-wrapper + { + height: 1px; + width: 100%; + overflow: visible; + display: flex; + align-items: center; + } + } } .smart-chip diff --git a/src/components/smartChip/component/smartChip.template.html b/src/components/smartChip/component/smartChip.template.html index 659c02630e73bbfde5668378329fa026100b17a2..e057e15013e0a3275e8a3f0b1bdadb761ddb116b 100644 --- a/src/components/smartChip/component/smartChip.template.html +++ b/src/components/smartChip/component/smartChip.template.html @@ -6,24 +6,35 @@ <div [style.background-color]="color" [matMenuTriggerFor]="noMenuFlag ? null : mainMenu" + (menuOpened)="menuOpened.emit()" + (menuClosed)="menuClosed.emit()" matRipple [matRippleDisabled]="noMenuFlag" [ngClass]="smartChipClass" - class="mat-body smart-chip body sxplr-custom-cmp text"> + class="mat-body smart-chip sxplr-custom-cmp text"> - <!-- header component --> + <!-- text component --> - <ng-template [ngIf]="headerTmpl?.templateRef" let-tmpl> - <div class="mat-body sxplr-custom-cmp text header"> - <ng-template [ngTemplateOutlet]="tmpl"> - </ng-template> + <div class="text"> + <ng-template [ngIf]="headerTmpl?.templateRef" let-tmpl> + <div class="mat-body sxplr-custom-cmp text header"> + <ng-template [ngTemplateOutlet]="tmpl"> + </ng-template> + </div> + </ng-template> + <div class="body"> + <div class="body-content-wrapper"> + <ng-template [ngTemplateOutlet]="contentTmpl?.templateRef || fallbackContentTmpl"> + </ng-template> + </div> </div> - </ng-template> - <div class="body"> - <div class="body-content-wrapper"> - <ng-template [ngTemplateOutlet]="contentTmpl?.templateRef || fallbackContentTmpl"> + </div> + + <div class="icons"> + <ng-template ngFor [ngForOf]="actionTmpls" let-actionTmpl> + <ng-template [ngTemplateOutlet]="actionTmpl.templateRef"> </ng-template> - </div> + </ng-template> </div> </div> diff --git a/src/components/smartChip/module.ts b/src/components/smartChip/module.ts index e5c9616adcf1a4b4d4a9e4a2f5eba573a00d743d..58c8a293054afab02430a3104d4765e57c6db582 100644 --- a/src/components/smartChip/module.ts +++ b/src/components/smartChip/module.ts @@ -9,6 +9,7 @@ import { SmartChipContent } from "./smartChip.content.directive"; import { SmartChipHeader } from "./smartChip.header.directive"; import { SmartChipMenu } from "./smartChip.menu.directive"; import { ExperimentalModule } from "src/experimental/experimental.module"; +import { SmartChipAction } from "./smartChip.action.directive"; @NgModule({ imports: [ @@ -22,6 +23,7 @@ import { ExperimentalModule } from "src/experimental/experimental.module"; SmartChipMenu, SmartChipContent, SmartChipHeader, + SmartChipAction, SmartChip, HasSubMenuPipe, ], @@ -29,6 +31,7 @@ import { ExperimentalModule } from "src/experimental/experimental.module"; SmartChipMenu, SmartChipContent, SmartChipHeader, + SmartChipAction, SmartChip, ] }) diff --git a/src/components/smartChip/smartChip.action.directive.ts b/src/components/smartChip/smartChip.action.directive.ts new file mode 100644 index 0000000000000000000000000000000000000000..01196552070c7aeb450821c007d5a95460a0b5e6 --- /dev/null +++ b/src/components/smartChip/smartChip.action.directive.ts @@ -0,0 +1,9 @@ +import { Directive, Inject, TemplateRef } from "@angular/core"; + +@Directive({ + selector: `ng-template[sxplrSmartChipAction]` +}) + +export class SmartChipAction { + constructor(@Inject(TemplateRef) public templateRef: TemplateRef<unknown>){} +} diff --git a/src/features/guards.ts b/src/features/guards.ts index fefbc7a0bbc14d1a5baf042ec6283de30d6516ed..eff1ea272787e87cee62e2fc5e10305880ea9452 100644 --- a/src/features/guards.ts +++ b/src/features/guards.ts @@ -8,11 +8,6 @@ export function isVoiData(feature: unknown): feature is VoiFeature { return !!feature['bbox'] } -export function notQuiteRight(feature: unknown): string[] { - if (feature['name'].includes("Cellular level 3D reconstructed volumes at 1µm resolution")) { - return [ - "This volume is currently not displayed correctly. We are working to restore the functionality." - ] - } +export function notQuiteRight(_feature: unknown): string[] { return [] } diff --git a/src/util/util.module.ts b/src/util/util.module.ts index d40b143f716b382721cd8620cfd3760021088786..53355b1f568ec0e9d46a895649654160ae1f1dc6 100644 --- a/src/util/util.module.ts +++ b/src/util/util.module.ts @@ -77,7 +77,7 @@ import { PrettyPresentPipe } from './pretty-present.pipe'; IncludesPipe, SidePanelComponent, DfToDsPipe, - PrettyPresentPipe + PrettyPresentPipe, ] }) diff --git a/src/viewerModule/viewerCmp/viewerCmp.template.html b/src/viewerModule/viewerCmp/viewerCmp.template.html index cd5c8600e2e5681301ee98111cd67b158d67d5cb..b39b4115cd180ae805fa29ccd67177dbf53fac99 100644 --- a/src/viewerModule/viewerCmp/viewerCmp.template.html +++ b/src/viewerModule/viewerCmp/viewerCmp.template.html @@ -293,14 +293,28 @@ #minTray="sxplrExperimentalFlag" [ngIf]="minTray.show$ | async"> <div class="tab-toggle-container"> + + <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> - <ng-template [ngTemplateOutlet]="tabTmpl_defaultTmpl" [ngTemplateOutletContext]="{ - fontIcon: (minTrayVisSwitch.switchState$ | async) ? 'fas fa-chevron-left' : 'fas fa-search', - matColor: (minTrayVisSwitch.switchState$ | async) ? null : 'primary', - click: minTrayVisSwitch.toggle.bind(minTrayVisSwitch) - }"> + <div [ngClass]="(minTrayVisSwitch.switchState$ | async) ? 'd-none' : ''"> + + <ng-template [ngTemplateOutlet]="tabTmpl_defaultTmpl" [ngTemplateOutletContext]="{ + fontIcon: 'fas fa-search', + matColor: 'primary', + click: minTrayVisSwitch.toggle.bind(minTrayVisSwitch), + badge: voiFeatureEntryCmp.totals$ | async + }"> + </ng-template> + </div> - </ng-template> </div> </ng-template>