libs/core/frame/src/frame-parts/navigation/navigation-view.component.ts

Implements

AfterViewInit

Metadata

Relationships

Index

Properties
Methods
Inputs
Outputs
Accessors

Constructor

constructor()

Inputs

collapsed
Type : boolean
Default value : false
compact
Type : boolean
Default value : false
horizontal
Type : boolean
Default value : false
isForbidden
Type : boolean
Default value : false
sections
Type : SectionConfig
sectionStates
Type : SectionWithState[]
Default value : []

Outputs

goto
Type : string

Methods

getSectionLabel
getSectionLabel(id: string)
Parameters :
Name Type Optional
id string No
Returns : any
onKeydown
onKeydown($event: KeyboardEvent, : { sectionId: string })
Parameters :
Name Type Optional
$event KeyboardEvent No
{ sectionId: string } No
Returns : void
requestSection
requestSection(: { sectionId: string })
Parameters :
Name Type Optional
{ sectionId: string } No
Returns : void

Properties

focusIndex
Type : number | null
Default value : null
Readonly hasMargin
Type : unknown
Default value : !inject(NO_MARGIN)
Readonly isA1
Type : unknown
Default value : inject(IS_A1)
Readonly items
Type : unknown
Default value : viewChildren(NavigationStepComponent)
Readonly mobileBreadcrumbsText
Type : unknown
Default value : computed(() => this.getMobileBreadcrumbsText())

Accessors

currentSectionIndex
getcurrentSectionIndex()
import { FocusKeyManager } from '@angular/cdk/a11y';
import {
  AfterViewInit,
  ChangeDetectionStrategy,
  Component,
  computed,
  effect,
  inject,
  Injector,
  viewChildren,
  input,
  output
} from '@angular/core';
import { MOBILE_STEP_LABEL_KEY, SectionConfig, SectionWithState } from './model';
import { StepState } from './navigation-step-state';
import { NavigationStepComponent } from './navigation-step.component';
import { pushEvent } from '@allianz/tracking';
import { IS_A1, NO_MARGIN, TalyRuntimeLocalizationService } from '@allianz/taly-core';

@Component({
  selector: 'frame-navigation-view',
  templateUrl: './navigation-view.component.html',
  styleUrls: ['./navigation-view.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
  host: {
    '[class.is-horizontal]': 'horizontal()',
    '[class.is-a1]': 'isA1',
    '[class.has-margin]': 'hasMargin'
  },
  imports: [NavigationStepComponent]
})
export class NavigationViewComponent implements AfterViewInit {
  readonly horizontal = input(false);
  readonly collapsed = input(false);
  readonly compact = input(false);
  readonly sectionStates = input<SectionWithState[]>([]);
  readonly sections = input<SectionConfig>();
  readonly isForbidden = input(false);
  readonly goto = output<string>();

  private talyRuntimeLocalizationService = inject(TalyRuntimeLocalizationService);
  private injector = inject(Injector);
  readonly isA1 = inject(IS_A1);
  readonly hasMargin = !inject(NO_MARGIN);
  private readonly currentState = computed(() => this.getCurrentState());
  private previousState: SectionWithState | undefined;
  readonly mobileBreadcrumbsText = computed(() => this.getMobileBreadcrumbsText());
  focusIndex: number | null = null;

  readonly items = viewChildren(NavigationStepComponent);

  private keyManager!: FocusKeyManager<NavigationStepComponent>;

  constructor() {
    effect(() => {
      const previousState = this.previousState;
      const currentState = this.currentState();

      // this.keyManager is only assigned in ngAfterViewInit; guard defensively since
      // this effect's first run relative to that lifecycle hook is not guaranteed
      if (this.keyManager) {
        this.keyManager.updateActiveItem(this.currentSectionIndex);
      }

      if (this.hasCurrentStepChanged(previousState)) {
        this.trackNavigationEvent();
      }

      this.previousState = currentState;
    });
  }

  onKeydown($event: KeyboardEvent, { sectionId }: { sectionId: string }) {
    const key = $event.key;
    if (key === 'Enter') {
      this.requestSection({ sectionId });
      $event.preventDefault();
    } else {
      this.keyManager.onKeydown($event);
      this.focusIndex = this.getFocusIndex();
    }
  }

  ngAfterViewInit() {
    this.keyManager = new FocusKeyManager(this.items, this.injector)
      .withWrap()
      .skipPredicate((item) => item.isPending)
      .withHorizontalOrientation('ltr')
      .withVerticalOrientation();
  }

  getSectionLabel(id: string) {
    const section = this.sections()?.get(id);
    return section?.label;
  }

  private getCurrentState(): SectionWithState | undefined {
    return this.sectionStates().find((item) => item.state === StepState.CURRENT);
  }

  private getFocusIndex() {
    return this.keyManager.activeItemIndex;
  }

  get currentSectionIndex() {
    const currentState = this.currentState();
    return currentState ? this.sectionStates().indexOf(currentState) : -1;
  }

  requestSection({ sectionId }: { sectionId: string }) {
    this.goto.emit(sectionId);
  }

  private trackNavigationEvent() {
    const currentState = this.currentState();
    if (!currentState) return;

    const currentIndex = this.currentSectionIndex;
    const totalSteps = this.sectionStates().length;

    const baseEventName = 'navigation';
    pushEvent(baseEventName, {
      currentStep: currentIndex + 1,
      totalSteps,
      stepId: currentState.sectionId,
      stepLabel: this.getSectionLabel(currentState.sectionId)
    });
  }

  private hasCurrentStepChanged(previousState: SectionWithState | undefined): boolean {
    return previousState?.sectionId !== this.currentState()?.sectionId;
  }

  private getMobileBreadcrumbsText(): string | undefined {
    const currentState = this.currentState();
    let text: string | undefined;
    if (currentState) {
      const configuredLabel =
        String(this.talyRuntimeLocalizationService.getTranslation(MOBILE_STEP_LABEL_KEY)()) ||
        $localize`Step `;
      text =
        `${configuredLabel.trim()} ` +
        `${this.currentSectionIndex + 1}/${this.sectionStates().length}` +
        $localize`:@@frame.navigation.step-separator::` +
        ` ${this.getSectionLabel(currentState.sectionId)}`;
    }
    return text;
  }
}
@if (compact()) { @if (mobileBreadcrumbsText()) {
<div class="compact">{{ mobileBreadcrumbsText() }}</div>
} } @if (!compact()) {
<div class="navigation-steps" role="tablist">
  @for (section of sectionStates(); track section.sectionId; let i = $index) {
  <frame-navigation-step
    (click)="requestSection(section)"
    (keydown)="onKeydown($event, section)"
    [tabindex]="focusIndex === i ? 0 : -1"
    [state]="section.state"
    [label]="getSectionLabel(section.sectionId)"
    [hideLabel]="collapsed()"
    [horizontal]="horizontal()"
    [isForbidden]="isForbidden()"
    [labelId]="'frame-navigation-step-' + i"
  >
  </frame-navigation-step>
  }
</div>
}

./navigation-view.component.scss

:host {
  display: block;
}

:host(.is-horizontal) {
  .navigation-steps {
    display: flex;
  }
  .dots,
  .labels {
    display: flex;
  }
}

:host(:not(.is-a1)) {
  .compact {
    text-align: center;
  }
}

:host-context(.is-a1.has-margin) {
  max-width: 100%;
  width: var(--grid-max-width);
  margin-inline: auto;
}

.compact {
  font-size: var(--progress-indicator-mobile-font-size);
  line-height: var(--progress-indicator-mobile-line-height);
}
Legend
Html element
Component
Html element with directive

results matching ""

    No results matching ""