All files / select/options si-select-options-strategy.base.ts

100% Statements 26/26
100% Branches 14/14
100% Functions 8/8
100% Lines 24/24

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                          1x                     132x               132x   132x 157x 157x 511x   574x       190x       161x 9x 9x 42x   9x   40x 14x 26x 2x 1x   1x   1x 1x         40x       152x        
/**
 * Copyright (c) Siemens 2016 - 2025
 * SPDX-License-Identifier: MIT
 */
import { computed, Directive, InputSignal, Signal, signal } from '@angular/core';
 
import { SelectItem, SelectOption } from '../si-select.types';
import { SiSelectOptionsStrategy } from './si-select-options-strategy';
 
/**
 * Input options strategy base class, for eagerly fetched options.
 */
@Directive()
export abstract class SiSelectOptionsStrategyBase<T> implements SiSelectOptionsStrategy<T> {
  /**
   * Function to compare two values on equality which is used to match/filter options.
   */
  abstract optionsEqual: InputSignal<(a: T, b: T) => boolean>;
 
  /**
   * Rows that should be shown.
   *
   * @defaultValue []
   */
  readonly rows = signal<SelectItem<T>[]>([]);
  /**
   * All group and option items in the dropdown.
   *
   * @defaultValue []
   */
  abstract readonly allRows: Signal<(SelectItem<T> | SelectOption<T>)[]>;
 
  private readonly value = signal<T[]>([]);
 
  readonly selectedRows = computed(() => {
    const values = this.value();
    return this.allRows()
      .map(row => (row.type === 'group' ? row.options : row))
      .flat()
      .filter(option => values.some(value => this.optionsEqual()(value, option.value)));
  });
 
  onValueChange(value: T[]): void {
    this.value.set(value);
  }
 
  onFilter(filterValue?: string): void {
    if (filterValue) {
      const filterValueLC = filterValue.toLowerCase();
      const checkRow: (row: SelectOption<T>) => boolean = (row: SelectOption<T>) =>
        (row.typeaheadLabel ?? row.label)!.toLowerCase().includes(filterValueLC!);
 
      this.rows.set(
        this.allRows().reduce((rows, row) => {
          if (row.type === 'option' && checkRow(row)) {
            rows.push(row);
          } else if (row.type === 'group') {
            if (row.label!.toLowerCase().includes(filterValueLC!)) {
              rows.push(row);
            } else {
              const options = row.options.filter(checkRow);
 
              if (options.length) {
                rows.push({ ...row, options });
              }
            }
          }
 
          return rows;
        }, [] as SelectItem<T>[])
      );
    } else {
      this.rows.set(this.allRows());
    }
  }
}