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 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 | 1x 5x 5x 5x 5x 5x 5x 5x 4x 2x 2x 2x 1x 1x 4x 4x 5x 5x 5x 7x 7x 10x 4x 10x 7x 7x 3x 2x 3x 4x 4x 16x 16x 11x 5x 6x 6x 6x 6x | /** * Copyright (c) Siemens 2016 - 2025 * SPDX-License-Identifier: MIT */ import { Directive, input, OnDestroy, signal } from '@angular/core'; import { of, Subject, switchMap } from 'rxjs'; import { debounceTime, takeUntil } from 'rxjs/operators'; import { SelectItem, SelectOption } from '../si-select.types'; import { SelectOptionSource } from './si-select-option.source'; import { SI_SELECT_OPTIONS_STRATEGY, SiSelectOptionsStrategy } from './si-select-options-strategy'; @Directive({ // eslint-disable-next-line @angular-eslint/directive-selector selector: 'si-select[optionSource]', providers: [{ provide: SI_SELECT_OPTIONS_STRATEGY, useExisting: SiSelectLazyOptionsDirective }] }) export class SiSelectLazyOptionsDirective<T> implements SiSelectOptionsStrategy<T>, OnDestroy { /** * {@inheritDoc SiSelectOptionsStrategy#loading} * @defaultValue false */ readonly loading = signal(false); /** * {@inheritDoc SiSelectOptionsStrategy#rows} * @defaultValue [] */ readonly rows = signal<SelectItem<T>[]>([]); /** * {@inheritDoc SiSelectOptionsStrategy#selectedRows} * @defaultValue [] */ readonly selectedRows = signal<SelectOption<T>[]>([]); readonly optionSource = input.required<SelectOptionSource<T>>(); private valueChange = new Subject<void>(); private filterChange = new Subject<string | undefined>(); constructor() { this.filterChange .pipe( debounceTime(100), switchMap(filterInput => { if (filterInput) { return this.optionSource().getOptionsForSearch!(filterInput); } else { const optionSource = this.optionSource(); if (optionSource.getAllOptions) { return optionSource.getAllOptions(); } else { return of(this.selectedRows()); } } }) ) .subscribe(rows => { this.loading.set(false); this.rows.set(rows); }); } ngOnDestroy(): void { this.valueChange.next(); this.valueChange.complete(); this.filterChange.complete(); } /** {@inheritDoc SiSelectOptionsStrategy.onValueChange} */ onValueChange(value: T[]): void { this.valueChange.next(); // To prevent flickering, we need to check if we already got all the requested options. // This should always be the case if the user selects one const knownSelectedOptions = value .map( valueEntry => this.selectedRows().find(selected => this.valueEqual(selected.value, valueEntry)) ?? (this.rows().find( selected => selected.type === 'option' && this.valueEqual(selected.value, valueEntry) ) as SelectOption<T>) ) .filter(v => !!v); const optionSource = this.optionSource(); if (knownSelectedOptions.length === value.length) { if (optionSource.compareOptions) { knownSelectedOptions.sort(optionSource.compareOptions); } this.selectedRows.set(knownSelectedOptions); } else { // We don't have all options, so we need to fetch them. optionSource .getOptionsForValues(value) .pipe(takeUntil(this.valueChange)) .subscribe(selectedOptions => this.selectedRows.set(selectedOptions)); } } /** {@inheritDoc SiSelectOptionsStrategy.onFilter} */ onFilter(filterInput?: string): void { this.filterChange.next(filterInput); this.loading.set(true); } private valueEqual(a?: T, b?: T): boolean { if (a === b) { return true; } Iif (!a || !b) { return false; } const optionSource = this.optionSource(); Iif (optionSource.valuesEqual) { return optionSource.valuesEqual(a, b); } return false; } } |