@siemens/element-ng 49.16.1 → 49.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { input, model, output, viewChild, computed, ChangeDetectionStrategy, Component, signal, Directive, inject, LOCALE_ID, DestroyRef, untracked, Injector, afterNextRender, booleanAttribute, ElementRef, viewChildren, NgModule } from '@angular/core';
2
+ import { input, model, output, viewChild, inject, Injector, afterNextRender, computed, ChangeDetectionStrategy, Component, signal, Directive, LOCALE_ID, DestroyRef, untracked, booleanAttribute, ElementRef, viewChildren, NgModule } from '@angular/core';
3
3
  import { takeUntilDestroyed, toObservable } from '@angular/core/rxjs-interop';
4
4
  import { elementCancel, elementSearch } from '@siemens/element-icons';
5
5
  import { defaultConnectedOverlayScrollStrategyFactory, isRTL } from '@siemens/element-ng/common';
@@ -22,6 +22,12 @@ import { CdkMonitorFocus } from '@angular/cdk/a11y';
22
22
  const toOptionCriteria = (values) => values?.map(v => typeof v === 'string'
23
23
  ? { value: v }
24
24
  : { label: v.label, value: v.value, iconClass: v.iconClass }) ?? [];
25
+ const findOption = (value, options, preferLabel = false) => {
26
+ const normalizedValue = value.toLocaleLowerCase();
27
+ const labelMatch = options.find(option => option.translatedLabel.toLocaleLowerCase() === normalizedValue);
28
+ const valueMatch = options.find(option => option.value === value);
29
+ return preferLabel ? (labelMatch ?? valueMatch) : (valueMatch ?? labelMatch);
30
+ };
25
31
  /*
26
32
  * Update selected state the matching is based on value since plain
27
33
  * string options will automatically fill the value attribute with the
@@ -96,9 +102,12 @@ class SiFilteredSearchInputComponent {
96
102
  /** Emits when Enter is pressed in the search input. */
97
103
  enterSubmit = output();
98
104
  inputElement = viewChild.required('freeTextInputElement');
105
+ typeahead = viewChild.required(SiTypeaheadDirective);
106
+ injector = inject(Injector);
99
107
  /** Public method to focus the input element */
100
108
  focus() {
101
109
  this.inputElement().nativeElement.focus();
110
+ afterNextRender(() => this.typeahead().updateOverlayPosition(), { injector: this.injector });
102
111
  }
103
112
  typeaheadCreateOption = computed(() => this.freeTextCriterion() && !this.maxCriteriaReached() && this.allowFreeText()
104
113
  ? this.searchForFreeTextLabel()
@@ -141,14 +150,14 @@ class SiFilteredSearchInputComponent {
141
150
  this.searchValue.set('');
142
151
  }
143
152
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: SiFilteredSearchInputComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
144
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.0.8", type: SiFilteredSearchInputComponent, isStandalone: true, selector: "si-filtered-search-input", inputs: { dataSource: { classPropertyName: "dataSource", publicName: "dataSource", isSignal: true, isRequired: true, transformFunction: null }, typeaheadOptionsLimit: { classPropertyName: "typeaheadOptionsLimit", publicName: "typeaheadOptionsLimit", isSignal: true, isRequired: true, transformFunction: null }, optionsInScrollableView: { classPropertyName: "optionsInScrollableView", publicName: "optionsInScrollableView", isSignal: true, isRequired: true, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: true, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: true, transformFunction: null }, searchLabel: { classPropertyName: "searchLabel", publicName: "searchLabel", isSignal: true, isRequired: true, transformFunction: null }, scrollStrategyFactory: { classPropertyName: "scrollStrategyFactory", publicName: "scrollStrategyFactory", isSignal: true, isRequired: true, transformFunction: null }, searchValue: { classPropertyName: "searchValue", publicName: "searchValue", isSignal: true, isRequired: true, transformFunction: null }, freeTextCriterion: { classPropertyName: "freeTextCriterion", publicName: "freeTextCriterion", isSignal: true, isRequired: true, transformFunction: null }, maxCriteriaReached: { classPropertyName: "maxCriteriaReached", publicName: "maxCriteriaReached", isSignal: true, isRequired: true, transformFunction: null }, allowFreeText: { classPropertyName: "allowFreeText", publicName: "allowFreeText", isSignal: true, isRequired: true, transformFunction: null }, searchForFreeTextLabel: { classPropertyName: "searchForFreeTextLabel", publicName: "searchForFreeTextLabel", isSignal: true, isRequired: true, transformFunction: null }, disableSelectionByColonAndSemicolon: { classPropertyName: "disableSelectionByColonAndSemicolon", publicName: "disableSelectionByColonAndSemicolon", isSignal: true, isRequired: true, transformFunction: null }, onlySelectValue: { classPropertyName: "onlySelectValue", publicName: "onlySelectValue", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { searchValue: "searchValueChange", createCriterion: "createCriterion", createCriterionByName: "createCriterionByName", backspaceOverflow: "backspaceOverflow", createFreeTextPill: "createFreeTextPill", inputFocus: "inputFocus", enterSubmit: "enterSubmit" }, host: { classAttribute: "w-100" }, viewQueries: [{ propertyName: "inputElement", first: true, predicate: ["freeTextInputElement"], descendants: true, isSignal: true }], ngImport: i0, template: "<input\n #freeTextInputElement\n type=\"text\"\n class=\"value-input ps-2 me-2 w-100\"\n typeaheadOptionField=\"translatedLabel\"\n typeaheadClearValueOnSelect\n [siTypeahead]=\"dataSource()\"\n [typeaheadScrollStrategy]=\"typeaheadScrollStrategy()\"\n [typeaheadMinLength]=\"0\"\n [typeaheadOptionsLimit]=\"typeaheadOptionsLimit()\"\n [typeaheadScrollable]=\"true\"\n [typeaheadOptionsInScrollableView]=\"optionsInScrollableView()\"\n [typeaheadAutoSelectIndex]=\"searchValue() ? 0 : -1\"\n [typeaheadCreateOption]=\"typeaheadCreateOption()\"\n [disabled]=\"disabled()\"\n [placeholder]=\"placeholder()\"\n [attr.aria-label]=\"searchLabel() | translate\"\n [value]=\"searchValue()\"\n (focus)=\"inputFocus.emit()\"\n (input)=\"freeTextInputHandler($event)\"\n (blur)=\"freeTextBlurHandler()\"\n (keydown.backspace)=\"freeTextBackspaceHandler($event)\"\n (keydown.enter)=\"enterSubmit.emit()\"\n (typeaheadOnSelect)=\"typeaheadOnSelectCriterionHandler($event.option)\"\n (typeaheadOnCreateOption)=\"createFreeTextPillHandler($event)\"\n/>\n", styles: ["input{background:transparent;border:0;padding-block:0}input:focus{min-inline-size:20ch}input:hover::placeholder{opacity:1}:host-context(si-filtered-search-value + :host) input::placeholder{color:transparent}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: SiTypeaheadDirective, selector: "[siTypeahead]", inputs: ["siTypeahead", "typeaheadProcess", "typeaheadScrollable", "typeaheadOptionsInScrollableView", "typeaheadOptionsLimit", "typeaheadScrollableAdditionalHeight", "typeaheadAutoSelectIndex", "typeaheadCloseOnEsc", "typeaheadClearValueOnSelect", "typeaheadWaitMs", "typeaheadMinLength", "typeaheadOptionField", "typeaheadMultiSelect", "typeaheadTokenize", "typeaheadMatchAllTokens", "typeaheadItemTemplate", "typeaheadSkipSortingMatches", "typeaheadAutocompleteListLabel", "typeaheadFullWidth", "typeaheadScrollStrategy", "typeaheadCreateOption"], outputs: ["typeaheadOnInput", "typeaheadOnSelect", "typeaheadOnFullMatch", "typeaheadOpenChange", "typeaheadOnCreateOption"], exportAs: ["si-typeahead"] }, { kind: "pipe", type: SiTranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
153
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.0.8", type: SiFilteredSearchInputComponent, isStandalone: true, selector: "si-filtered-search-input", inputs: { dataSource: { classPropertyName: "dataSource", publicName: "dataSource", isSignal: true, isRequired: true, transformFunction: null }, typeaheadOptionsLimit: { classPropertyName: "typeaheadOptionsLimit", publicName: "typeaheadOptionsLimit", isSignal: true, isRequired: true, transformFunction: null }, optionsInScrollableView: { classPropertyName: "optionsInScrollableView", publicName: "optionsInScrollableView", isSignal: true, isRequired: true, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: true, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: true, transformFunction: null }, searchLabel: { classPropertyName: "searchLabel", publicName: "searchLabel", isSignal: true, isRequired: true, transformFunction: null }, scrollStrategyFactory: { classPropertyName: "scrollStrategyFactory", publicName: "scrollStrategyFactory", isSignal: true, isRequired: true, transformFunction: null }, searchValue: { classPropertyName: "searchValue", publicName: "searchValue", isSignal: true, isRequired: true, transformFunction: null }, freeTextCriterion: { classPropertyName: "freeTextCriterion", publicName: "freeTextCriterion", isSignal: true, isRequired: true, transformFunction: null }, maxCriteriaReached: { classPropertyName: "maxCriteriaReached", publicName: "maxCriteriaReached", isSignal: true, isRequired: true, transformFunction: null }, allowFreeText: { classPropertyName: "allowFreeText", publicName: "allowFreeText", isSignal: true, isRequired: true, transformFunction: null }, searchForFreeTextLabel: { classPropertyName: "searchForFreeTextLabel", publicName: "searchForFreeTextLabel", isSignal: true, isRequired: true, transformFunction: null }, disableSelectionByColonAndSemicolon: { classPropertyName: "disableSelectionByColonAndSemicolon", publicName: "disableSelectionByColonAndSemicolon", isSignal: true, isRequired: true, transformFunction: null }, onlySelectValue: { classPropertyName: "onlySelectValue", publicName: "onlySelectValue", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { searchValue: "searchValueChange", createCriterion: "createCriterion", createCriterionByName: "createCriterionByName", backspaceOverflow: "backspaceOverflow", createFreeTextPill: "createFreeTextPill", inputFocus: "inputFocus", enterSubmit: "enterSubmit" }, host: { classAttribute: "w-100" }, viewQueries: [{ propertyName: "inputElement", first: true, predicate: ["freeTextInputElement"], descendants: true, isSignal: true }, { propertyName: "typeahead", first: true, predicate: SiTypeaheadDirective, descendants: true, isSignal: true }], ngImport: i0, template: "<input\n #freeTextInputElement\n type=\"text\"\n class=\"value-input ps-2 me-2 w-100\"\n typeaheadOptionField=\"translatedLabel\"\n typeaheadClearValueOnSelect\n [siTypeahead]=\"dataSource()\"\n [typeaheadScrollStrategy]=\"typeaheadScrollStrategy()\"\n [typeaheadMinLength]=\"0\"\n [typeaheadOptionsLimit]=\"typeaheadOptionsLimit()\"\n [typeaheadScrollable]=\"true\"\n [typeaheadOptionsInScrollableView]=\"optionsInScrollableView()\"\n [typeaheadAutoSelectIndex]=\"searchValue() ? 0 : -1\"\n [typeaheadCreateOption]=\"typeaheadCreateOption()\"\n [disabled]=\"disabled()\"\n [placeholder]=\"placeholder()\"\n [attr.aria-label]=\"searchLabel() | translate\"\n [value]=\"searchValue()\"\n (focus)=\"inputFocus.emit()\"\n (input)=\"freeTextInputHandler($event)\"\n (blur)=\"freeTextBlurHandler()\"\n (keydown.backspace)=\"freeTextBackspaceHandler($event)\"\n (keydown.enter)=\"enterSubmit.emit()\"\n (typeaheadOnSelect)=\"typeaheadOnSelectCriterionHandler($event.option)\"\n (typeaheadOnCreateOption)=\"createFreeTextPillHandler($event)\"\n/>\n", styles: ["input{background:transparent;border:0;padding-block:0}input:focus{min-inline-size:20ch}input:hover::placeholder{opacity:1}:host-context(si-filtered-search-value + :host) input::placeholder{color:transparent}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: SiTypeaheadDirective, selector: "[siTypeahead]", inputs: ["siTypeahead", "typeaheadProcess", "typeaheadScrollable", "typeaheadOptionsInScrollableView", "typeaheadOptionsLimit", "typeaheadScrollableAdditionalHeight", "typeaheadAutoSelectIndex", "typeaheadCloseOnEsc", "typeaheadClearValueOnSelect", "typeaheadWaitMs", "typeaheadMinLength", "typeaheadOptionField", "typeaheadMultiSelect", "typeaheadTokenize", "typeaheadMatchAllTokens", "typeaheadItemTemplate", "typeaheadSkipSortingMatches", "typeaheadAutocompleteListLabel", "typeaheadFullWidth", "typeaheadScrollStrategy", "typeaheadCreateOption"], outputs: ["typeaheadOnInput", "typeaheadOnSelect", "typeaheadOnFullMatch", "typeaheadOpenChange", "typeaheadOnCreateOption"], exportAs: ["si-typeahead"] }, { kind: "pipe", type: SiTranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
145
154
  }
146
155
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: SiFilteredSearchInputComponent, decorators: [{
147
156
  type: Component,
148
157
  args: [{ selector: 'si-filtered-search-input', imports: [FormsModule, SiTypeaheadDirective, SiTranslatePipe], changeDetection: ChangeDetectionStrategy.OnPush, host: {
149
158
  class: 'w-100'
150
159
  }, template: "<input\n #freeTextInputElement\n type=\"text\"\n class=\"value-input ps-2 me-2 w-100\"\n typeaheadOptionField=\"translatedLabel\"\n typeaheadClearValueOnSelect\n [siTypeahead]=\"dataSource()\"\n [typeaheadScrollStrategy]=\"typeaheadScrollStrategy()\"\n [typeaheadMinLength]=\"0\"\n [typeaheadOptionsLimit]=\"typeaheadOptionsLimit()\"\n [typeaheadScrollable]=\"true\"\n [typeaheadOptionsInScrollableView]=\"optionsInScrollableView()\"\n [typeaheadAutoSelectIndex]=\"searchValue() ? 0 : -1\"\n [typeaheadCreateOption]=\"typeaheadCreateOption()\"\n [disabled]=\"disabled()\"\n [placeholder]=\"placeholder()\"\n [attr.aria-label]=\"searchLabel() | translate\"\n [value]=\"searchValue()\"\n (focus)=\"inputFocus.emit()\"\n (input)=\"freeTextInputHandler($event)\"\n (blur)=\"freeTextBlurHandler()\"\n (keydown.backspace)=\"freeTextBackspaceHandler($event)\"\n (keydown.enter)=\"enterSubmit.emit()\"\n (typeaheadOnSelect)=\"typeaheadOnSelectCriterionHandler($event.option)\"\n (typeaheadOnCreateOption)=\"createFreeTextPillHandler($event)\"\n/>\n", styles: ["input{background:transparent;border:0;padding-block:0}input:focus{min-inline-size:20ch}input:hover::placeholder{opacity:1}:host-context(si-filtered-search-value + :host) input::placeholder{color:transparent}\n"] }]
151
- }], propDecorators: { dataSource: [{ type: i0.Input, args: [{ isSignal: true, alias: "dataSource", required: true }] }], typeaheadOptionsLimit: [{ type: i0.Input, args: [{ isSignal: true, alias: "typeaheadOptionsLimit", required: true }] }], optionsInScrollableView: [{ type: i0.Input, args: [{ isSignal: true, alias: "optionsInScrollableView", required: true }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: true }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: true }] }], searchLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchLabel", required: true }] }], scrollStrategyFactory: [{ type: i0.Input, args: [{ isSignal: true, alias: "scrollStrategyFactory", required: true }] }], searchValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchValue", required: true }] }, { type: i0.Output, args: ["searchValueChange"] }], freeTextCriterion: [{ type: i0.Input, args: [{ isSignal: true, alias: "freeTextCriterion", required: true }] }], maxCriteriaReached: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxCriteriaReached", required: true }] }], allowFreeText: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowFreeText", required: true }] }], searchForFreeTextLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchForFreeTextLabel", required: true }] }], disableSelectionByColonAndSemicolon: [{ type: i0.Input, args: [{ isSignal: true, alias: "disableSelectionByColonAndSemicolon", required: true }] }], onlySelectValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "onlySelectValue", required: true }] }], createCriterion: [{ type: i0.Output, args: ["createCriterion"] }], createCriterionByName: [{ type: i0.Output, args: ["createCriterionByName"] }], backspaceOverflow: [{ type: i0.Output, args: ["backspaceOverflow"] }], createFreeTextPill: [{ type: i0.Output, args: ["createFreeTextPill"] }], inputFocus: [{ type: i0.Output, args: ["inputFocus"] }], enterSubmit: [{ type: i0.Output, args: ["enterSubmit"] }], inputElement: [{ type: i0.ViewChild, args: ['freeTextInputElement', { isSignal: true }] }] } });
160
+ }], propDecorators: { dataSource: [{ type: i0.Input, args: [{ isSignal: true, alias: "dataSource", required: true }] }], typeaheadOptionsLimit: [{ type: i0.Input, args: [{ isSignal: true, alias: "typeaheadOptionsLimit", required: true }] }], optionsInScrollableView: [{ type: i0.Input, args: [{ isSignal: true, alias: "optionsInScrollableView", required: true }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: true }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: true }] }], searchLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchLabel", required: true }] }], scrollStrategyFactory: [{ type: i0.Input, args: [{ isSignal: true, alias: "scrollStrategyFactory", required: true }] }], searchValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchValue", required: true }] }, { type: i0.Output, args: ["searchValueChange"] }], freeTextCriterion: [{ type: i0.Input, args: [{ isSignal: true, alias: "freeTextCriterion", required: true }] }], maxCriteriaReached: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxCriteriaReached", required: true }] }], allowFreeText: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowFreeText", required: true }] }], searchForFreeTextLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchForFreeTextLabel", required: true }] }], disableSelectionByColonAndSemicolon: [{ type: i0.Input, args: [{ isSignal: true, alias: "disableSelectionByColonAndSemicolon", required: true }] }], onlySelectValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "onlySelectValue", required: true }] }], createCriterion: [{ type: i0.Output, args: ["createCriterion"] }], createCriterionByName: [{ type: i0.Output, args: ["createCriterionByName"] }], backspaceOverflow: [{ type: i0.Output, args: ["backspaceOverflow"] }], createFreeTextPill: [{ type: i0.Output, args: ["createFreeTextPill"] }], inputFocus: [{ type: i0.Output, args: ["inputFocus"] }], enterSubmit: [{ type: i0.Output, args: ["enterSubmit"] }], inputElement: [{ type: i0.ViewChild, args: ['freeTextInputElement', { isSignal: true }] }], typeahead: [{ type: i0.ViewChild, args: [i0.forwardRef(() => SiTypeaheadDirective), { isSignal: true }] }] } });
152
161
 
153
162
  /**
154
163
  * Copyright (c) Siemens 2016 - 2026
@@ -313,6 +322,7 @@ class SiFilteredSearchOptionValueBase extends SiFilteredSearchValueBase {
313
322
  /** Whether strict or selection-only value validation is active. */
314
323
  isStrictOrOnlySelectValue = input.required(...(ngDevMode ? [{ debugName: "isStrictOrOnlySelectValue" }] : []));
315
324
  inputChange = new BehaviorSubject('');
325
+ loadedOptions = signal(undefined, ...(ngDevMode ? [{ debugName: "loadedOptions" }] : []));
316
326
  destroyRef = inject(DestroyRef);
317
327
  translateService = injectSiTranslateService();
318
328
  inputType = computed(() => this.definition().validationType === 'integer' || this.definition().validationType === 'float'
@@ -348,7 +358,7 @@ class SiFilteredSearchOptionValueBase extends SiFilteredSearchValueBase {
348
358
  ...option,
349
359
  translatedLabel: translations[option.label] ?? option.label ?? option.value
350
360
  }))));
351
- }));
361
+ }), tap(options => this.loadedOptions.set(options)));
352
362
  }
353
363
  buildOptionValue() {
354
364
  if (this.criterionValue().value?.length) {
@@ -482,15 +492,21 @@ class SiFilteredSearchTypeaheadComponent extends SiFilteredSearchOptionValueBase
482
492
  else {
483
493
  value = newValue;
484
494
  }
485
- this.optionValue.set(undefined);
486
- this.criterionValue.update(v => ({ ...v, value }));
487
- this.inputChange.next(newValue);
495
+ const option = findOption(value, this.loadedOptions() ?? [], true);
496
+ value = option?.value ?? value;
497
+ this.optionValue.set(option);
498
+ if (this.criterionValue().value !== value) {
499
+ this.criterionValue.update(v => ({ ...v, value }));
500
+ this.inputChange.next(newValue);
501
+ }
488
502
  }
489
503
  }
490
504
  valueTypeaheadFullMatch(match) {
491
505
  const option = match.option;
492
506
  this.optionValue.set(option);
493
- this.criterionValue.update(v => ({ ...v, value: option.value }));
507
+ if (this.criterionValue().value !== option.value) {
508
+ this.criterionValue.update(v => ({ ...v, value: option.value }));
509
+ }
494
510
  }
495
511
  valueTypeaheadSelect(match) {
496
512
  this.valueTypeaheadFullMatch(match);