libs/common/frame/src/services/sidebar.service.ts
Collect and maintain a list of templates candidates marked by the directive [pgrSidebar] somewhere in the application.
The templates can be accessed through templateList$.
The application can request to change the visibility of the sidebar directly (setVisibility)
and indirectly by setting the content location (hideContent/moveContentToSidebar/moveContentToOriginalLocation).
All these methods interacts with other factors like the channel (expert vs retail),
the current active layout (stacked or not), the presence of a header in the sidebar configuration
and an optional conditional visibleIf expression.
The resulting combined status is the sidebar visibility, accessible through the observable show$.
Methods |
Accessors |
constructor()
|
| hideContent |
hideContent()
|
|
Returns :
void
|
| init |
init()
|
|
Returns :
void
|
| moveContentToOriginalLocation |
moveContentToOriginalLocation()
|
|
Returns :
void
|
| moveContentToSidebar |
moveContentToSidebar()
|
|
Returns :
void
|
| registerTemplate | ||||||
registerTemplate(template: TemplateRef<>)
|
||||||
|
Parameters :
Returns :
void
|
| setVisibility | ||||||
setVisibility(value: boolean)
|
||||||
|
Parameters :
Returns :
void
|
| templateList$ |
gettemplateList$()
|
| show$ |
getshow$()
|
|
Observable that emits whether the sidebar component should be shown or not. |
| shouldContentRenderInOriginalLocation$ |
getshouldContentRenderInOriginalLocation$()
|
|
Observable that emits whether sidebar marked content should be rendered in its original location or not. |
| sidebarConfig$ |
getsidebarConfig$()
|
|
Observable that emits the sidebar configuration for the current page.
Returns :
Observable<SidebarConfiguration>
|
import { CHANNEL, CHANNEL_TOKEN, TalyPageDataService } from '@allianz/taly-core';
import { SidebarConfiguration } from '@allianz/taly-core';
import { Injectable, TemplateRef, inject } from '@angular/core';
import { BehaviorSubject, Observable, combineLatest, of, switchMap } from 'rxjs';
import { distinctUntilChanged, map, skip, tap } from 'rxjs/operators';
import { TalyFrameLayoutService } from './frame-layout.service';
import { TalyFrameTopAreaService } from './taly-frame-top-area.service';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
export type SidebarTemplateSet = Set<TemplateRef<unknown>>;
enum ContentLocation {
SIDEBAR,
ORIGINAL,
NULL
}
/**
* Collect and maintain a list of templates candidates marked by the directive `[pgrSidebar]` somewhere in the application.
* The templates can be accessed through `templateList$`.
*
* The application can request to change the visibility of the sidebar directly (`setVisibility`)
* and indirectly by setting the content location (`hideContent`/`moveContentToSidebar`/`moveContentToOriginalLocation`).
* All these methods interacts with other factors like the channel (`expert` vs `retail`),
* the current active layout (`stacked` or not), the presence of a header in the sidebar configuration
* and an optional conditional `visibleIf` expression.
* The resulting combined status is the sidebar visibility, accessible through the observable `show$`.
*/
@Injectable()
export class TalyFrameSidebarService {
private _talyPageDataService = inject(TalyPageDataService);
private _frameLayout = inject(TalyFrameLayoutService);
private _channel = inject<CHANNEL>(CHANNEL_TOKEN);
private _topAreaService = inject(TalyFrameTopAreaService);
private _registeredTemplates: SidebarTemplateSet = new Set();
private _templatesListSubject = new BehaviorSubject<SidebarTemplateSet>(
this._registeredTemplates
);
private _showContentInSidebar = false;
private _showContentInSidebarSubject = new BehaviorSubject(this._showContentInSidebar);
private _hideContentSubject$ = new BehaviorSubject<boolean>(false);
private _conditionalVisibilitySubject$ = new BehaviorSubject<Observable<boolean | undefined>>(
of(undefined)
);
private _contentLocation$!: Observable<ContentLocation>;
constructor() {
this.init();
}
init() {
this._talyPageDataService.pageData$
.pipe(
skip(1), // skip the first as we subscribe to a behavior subject
tap(() => this._pageChanged())
)
.subscribe();
const isExpert: boolean = this._channel === CHANNEL.EXPERT;
if (!isExpert) {
this._contentLocation$ = new BehaviorSubject(ContentLocation.ORIGINAL).asObservable();
return;
}
const isStacked$ = this._frameLayout.isStackedLayoutObservable;
const isTooNarrow$ = this._frameLayout.hideSidebarBreakpoint$;
const conditionalVisibility$ = this._conditionalVisibilitySubject$.pipe(
switchMap((observable) => observable)
);
conditionalVisibility$
.pipe(
tap((conditionallyVisible) => {
if (conditionallyVisible === true) {
this._hideContentSubject$.next(false);
this.moveContentToSidebar();
}
if (conditionallyVisible === false) {
this.moveContentToOriginalLocation();
}
}),
takeUntilDestroyed()
)
.subscribe();
this.sidebarConfig$
.pipe(
tap((config) => {
if (config?.visibleIf) {
this._setConditionalVisibility(config.visibleIf);
}
}),
takeUntilDestroyed()
)
.subscribe();
this._contentLocation$ = combineLatest([
isStacked$,
isTooNarrow$,
this._showContentInSidebarSubject,
this.sidebarConfig$,
conditionalVisibility$,
this._hideContentSubject$
]).pipe(
map(
([
isStacked,
isTooNarrow,
showInSidebar,
sidebarConfig,
conditionallyVisible,
hideContent
]) => {
if (conditionallyVisible === false || hideContent) {
return ContentLocation.NULL;
}
const hasHeader = !!sidebarConfig?.header;
const layoutAllowsSidebar = hasHeader || (!isStacked && !isTooNarrow);
const shouldShowInSidebar = showInSidebar && layoutAllowsSidebar;
return shouldShowInSidebar ? ContentLocation.SIDEBAR : ContentLocation.ORIGINAL;
}
),
distinctUntilChanged()
);
}
registerTemplate(template: TemplateRef<unknown>) {
this._registeredTemplates.add(template);
this._update();
}
get templateList$() {
return this._templatesListSubject.asObservable();
}
/**
* Observable that emits whether the sidebar component should be shown or not.
*/
get show$() {
return this._contentLocation$.pipe(map((location) => location === ContentLocation.SIDEBAR));
}
/**
* Observable that emits whether sidebar marked content should be rendered in its original location or not.
*/
get shouldContentRenderInOriginalLocation$() {
return this._contentLocation$.pipe(map((location) => location === ContentLocation.ORIGINAL));
}
/**
* Observable that emits the sidebar configuration for the current page.
*/
get sidebarConfig$(): Observable<SidebarConfiguration> {
return this._talyPageDataService.pageData$.pipe(
map(
(pageData) =>
(pageData.sidebar as SidebarConfiguration) || {
appearance: 'light',
position: 'static'
}
)
);
}
setVisibility(value: boolean) {
if (value) {
this.moveContentToSidebar();
} else {
this.moveContentToOriginalLocation();
}
}
hideContent() {
this._hideContentSubject$.next(true);
}
moveContentToOriginalLocation() {
if (this._showContentInSidebar === true) {
this._showContentInSidebar = false;
this._showContentInSidebarSubject.next(this._showContentInSidebar);
}
}
moveContentToSidebar() {
if (this._showContentInSidebar === false) {
this._showContentInSidebar = true;
this._showContentInSidebarSubject.next(this._showContentInSidebar);
}
}
private _setConditionalVisibility(visibleIf: string) {
const conditionalVis$ = this._topAreaService.getSidebarVisibility$(visibleIf);
this._conditionalVisibilitySubject$.next(conditionalVis$);
}
private _pageChanged() {
this._registeredTemplates.clear();
this._update();
}
private _update() {
this._templatesListSubject.next(this._registeredTemplates);
}
}