All files / navbar-vertical si-navbar-vertical-group.component.ts

88.23% Statements 15/17
57.14% Branches 4/7
60% Functions 3/5
88.23% Lines 15/17

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                                                                                                1x 11x 11x 11x 11x     11x   11x       11x 18x 1x     17x 9x     8x       11x   1x              
/**
 * Copyright (c) Siemens 2016 - 2025
 * SPDX-License-Identifier: MIT
 */
import { animate, state, style, transition, trigger } from '@angular/animations';
import { CdkTrapFocus } from '@angular/cdk/a11y';
import { Component, computed, HostListener, inject } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { RouterLinkActive } from '@angular/router';
 
import { SiNavbarVerticalGroupTriggerDirective } from './si-navbar-vertical-group-trigger.directive';
import { SiNavbarVerticalItemComponent } from './si-navbar-vertical-item.component';
import { SI_NAVBAR_VERTICAL } from './si-navbar-vertical.provider';
 
// We have to use a component to build animations.
@Component({
  selector: 'si-navbar-vertical-group',
  imports: [CdkTrapFocus],
  template: `<div [cdkTrapFocus]="flyout" [cdkTrapFocusAutoCapture]="flyout">
    <ng-content />
  </div>`,
  styles: `
    :host {
      display: block;
      overflow: hidden;
      position: static;
    }
  `,
  host: {
    role: 'group',
    '[id]': 'groupTrigger.groupId',
    '[attr.aria-labelledby]': 'groupTrigger.id',
    '[class.dropdown-menu]': 'flyout',
    '[@collapse]': 'state() ?? "collapsed"'
  },
  animations: [
    trigger('collapse', [
      state('collapsed', style({ display: 'none' })),
      // Prevents initial animation. See: https://stackoverflow.com/a/50791299
      transition(':enter', []),
      transition('collapsed => expanded', [
        style({ 'display': 'block', 'block-size': '0' }),
        animate('0.5s ease', style({ 'block-size': '*' }))
      ]),
      transition('expanded => collapsed', [animate('0.5s ease', style({ 'block-size': '0' }))])
    ])
  ]
})
export class SiNavbarVerticalGroupComponent {
  protected readonly navbar = inject(SI_NAVBAR_VERTICAL);
  protected readonly groupTrigger = inject(SiNavbarVerticalGroupTriggerDirective);
  readonly groupParent = inject(SiNavbarVerticalItemComponent);
  private readonly routerLinkActive = inject(RouterLinkActive, { optional: true });
 
  // Store initial value, as the mode for an instance never changes.
  protected flyout = this.groupTrigger.flyout();
 
  protected readonly visible = computed(() => {
    return this.flyout || (!this.navbar.collapsed() && this.groupTrigger.expanded());
  });
 
  protected readonly state = computed(() => {
    if (this.flyout) {
      return 'flyout';
    }
 
    if (this.groupTrigger.expanded() && !this.navbar.collapsed()) {
      return 'expanded';
    }
 
    return 'collapsed';
  });
 
  constructor() {
    this.routerLinkActive?.isActiveChange
      .pipe(takeUntilDestroyed())
      .subscribe(active => this.groupTrigger.active.set(active));
  }
 
  @HostListener('keydown.escape') protected close(): void {
    this.groupTrigger.hideFlyout();
  }
}