All files / typeahead si-typeahead.component.ts

100% Statements 21/21
76.92% Branches 10/13
100% Functions 6/6
100% Lines 20/20

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                                                                                1x 132x 132x 303x         132x   132x       132x     132x         28x               132x 132x       5x 5x 5x 5x 5x 5x 5x       127x             32x      
/**
 * Copyright (c) Siemens 2016 - 2025
 * SPDX-License-Identifier: MIT
 */
import { NgTemplateOutlet } from '@angular/common';
import {
  AfterViewInit,
  ChangeDetectionStrategy,
  Component,
  computed,
  ElementRef,
  HostListener,
  inject,
  viewChild
} from '@angular/core';
import {
  SiAutocompleteDirective,
  SiAutocompleteListboxDirective,
  SiAutocompleteOptionDirective
} from '@siemens/element-ng/autocomplete';
import { SiIconComponent } from '@siemens/element-ng/icon';
import { SiTranslatePipe } from '@siemens/element-translate-ng/translate';
 
import { SiTypeaheadDirective } from './si-typeahead.directive';
import { TypeaheadMatch } from './si-typeahead.model';
 
@Component({
  selector: 'si-typeahead',
  imports: [
    SiAutocompleteListboxDirective,
    SiAutocompleteOptionDirective,
    SiIconComponent,
    NgTemplateOutlet,
    SiTranslatePipe
  ],
  templateUrl: './si-typeahead.component.html',
  styleUrl: './si-typeahead.component.scss',
  changeDetection: ChangeDetectionStrategy.OnPush,
  host: { class: 'w-100' }
})
export class SiTypeaheadComponent implements AfterViewInit {
  protected parent = inject(SiTypeaheadDirective);
  protected readonly matches = computed(() =>
    this.parent.typeaheadOptionsLimit()
      ? this.parent.foundMatches().slice(0, this.parent.typeaheadOptionsLimit())
      : this.parent.foundMatches()
  );
 
  protected readonly multiselect = computed(() => this.parent.typeaheadMultiSelect());
 
  private readonly typeaheadElement = viewChild.required('typeahead', {
    read: ElementRef
  });
 
  protected autocompleteDirective = inject(SiAutocompleteDirective);
 
  ngAfterViewInit(): void {
    this.setHeight(this.typeaheadElement());
  }
 
  @HostListener('mousedown', ['$event'])
  protected onMouseDown(event: Event): void {
    event.preventDefault();
  }
 
  /*
   * Set the height of the element passed to it (typeahead) if there are items displayed,
   * the number of displayed items changed and it is scrollable.
   */
  private setHeight(element: ElementRef): void {
    if (this.matches().length) {
      if (
        this.parent.typeaheadScrollable() &&
        this.parent.typeaheadOptionsInScrollableView() < this.matches().length
      ) {
        const computedStyle = getComputedStyle(element.nativeElement);
        const matchComputedStyle = getComputedStyle(element.nativeElement.firstElementChild);
        const matchHeight = parseFloat(matchComputedStyle.height || '0');
        const paddingTop = parseFloat(computedStyle.paddingTop || '0');
        const paddingBottom = parseFloat(computedStyle.paddingBottom || '');
        const height = this.parent.typeaheadOptionsInScrollableView() * matchHeight;
        element.nativeElement.style.maxBlockSize = `${
          height + paddingTop + paddingBottom + this.parent.typeaheadScrollableAdditionalHeight()
        }px`;
      } else {
        element.nativeElement.style.maxBlockSize = 'auto';
      }
    }
  }
 
  // Gets called when a match is selected by clicking on it.
  protected selectMatch(match: TypeaheadMatch): void {
    this.parent.selectMatch(match);
  }
}