File

libs/common/frame/src/services/sidebar.service.ts

Description

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$.

Index

Methods
Accessors

Constructor

constructor()

Methods

hideContent
hideContent()
Returns : void
init
init()
Returns : void
moveContentToOriginalLocation
moveContentToOriginalLocation()
Returns : void
moveContentToSidebar
moveContentToSidebar()
Returns : void
registerTemplate
registerTemplate(template: TemplateRef<>)
Parameters :
Name Type Optional
template TemplateRef<> No
Returns : void
setVisibility
setVisibility(value: boolean)
Parameters :
Name Type Optional
value boolean No
Returns : void

Accessors

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.

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);
  }
}

results matching ""

    No results matching ""