All files / application-header si-header-collapsible-actions.component.ts

90.32% Statements 28/31
75% Branches 3/4
87.5% Functions 7/8
90% Lines 27/30

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103                                                                      1x 1x                   13x 13x       13x   13x   13x 13x   13x 13x 13x 17x       13x 13x       5x     5x                   5x 5x 5x 5x 5x   1x 5x         17x 3x 3x 3x        
/**
 * Copyright (c) Siemens 2016 - 2025
 * SPDX-License-Identifier: MIT
 */
import { A11yModule, CdkTrapFocus } from '@angular/cdk/a11y';
import {
  ChangeDetectionStrategy,
  Component,
  ElementRef,
  inject,
  input,
  OnDestroy,
  signal,
  viewChild
} from '@angular/core';
import { SI_HEADER_DROPDOWN_OPTIONS } from '@siemens/element-ng/header-dropdown';
import { addIcons, elementOptionsVertical, SiIconComponent } from '@siemens/element-ng/icon';
import { SiTranslatePipe, t } from '@siemens/element-translate-ng/translate';
import { Subscription } from 'rxjs';
import { skip, takeUntil } from 'rxjs/operators';
 
import { SiApplicationHeaderComponent } from './si-application-header.component';
 
/** Container for actions that should be collapsed in mobile mode. */
@Component({
  selector: 'si-header-collapsible-actions',
  imports: [SiTranslatePipe, A11yModule, SiIconComponent],
  templateUrl: './si-header-collapsible-actions.component.html',
  styles: '.badge-dot::after { inset-inline-end: 4px; }',
  providers: [
    { provide: SI_HEADER_DROPDOWN_OPTIONS, useValue: { disableRootFocusTrapForInlineMode: true } }
  ],
  changeDetection: ChangeDetectionStrategy.OnPush,
  host: { class: 'd-contents' }
})
export class SiHeaderCollapsibleActionsComponent implements OnDestroy {
  private static idCounter = 0;
 
  /**
   * Accessible label of the toggle button if actions are collapsed.
   *
   * @defaultValue
   * ```
   * t(() => $localize`:@@SI_APPLICATION_HEADER.TOGGLE_ACTIONS:Toggle actions`)
   * ```
   */
  readonly mobileToggleLabel = input(
    t(() => $localize`:@@SI_APPLICATION_HEADER.TOGGLE_ACTIONS:Toggle actions`)
  );
 
  /** @internal **/
  readonly mobileExpanded = signal(false);
  /** @internal **/
  readonly badgeCount = signal(0);
 
  protected readonly id = `__si-header-collapsible-actions-${SiHeaderCollapsibleActionsComponent.idCounter++}`;
  protected readonly icons = addIcons({ elementOptionsVertical });
 
  private readonly toggle = viewChild.required<ElementRef<HTMLDivElement>>('toggle');
  private readonly focusTrap = viewChild.required(CdkTrapFocus);
  private header = inject(SiApplicationHeaderComponent);
  private closeMobileSub = this.header.closeMobileMenus.subscribe(() => this.closeMobile());
  private inlineChangeSubscription?: Subscription;
 
  ngOnDestroy(): void {
    this.closeMobileSub.unsubscribe();
    this.inlineChangeSubscription?.unsubscribe();
  }
 
  protected toggleMobileExpanded(): void {
    Iif (this.mobileExpanded()) {
      this.closeMobile();
    } else {
      this.openMobile();
    }
  }
 
  protected escapePressed(): void {
    this.closeMobile();
    this.toggle().nativeElement.focus();
  }
 
  private openMobile(): void {
    if (!this.mobileExpanded()) {
      this.header.closeMobileMenus.next();
      this.header.dropdownOpened();
      this.mobileExpanded.set(true);
      this.inlineChangeSubscription = this.header.inlineDropdown
        .pipe(skip(1), takeUntil(this.header.closeMobileMenus))
        .subscribe(() => this.header.closeMobileMenus.next());
      this.focusTrap().focusTrap.focusFirstTabbableElementWhenReady();
    }
  }
 
  private closeMobile(): void {
    if (this.mobileExpanded()) {
      this.header.dropdownClosed();
      this.mobileExpanded.set(false);
      this.toggle().nativeElement.focus();
    }
  }
}