@colijnit/corecomponents_v12 262.1.10 → 262.1.12

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.
@@ -7190,12 +7190,15 @@ class InputTextComponent extends BaseInputComponent {
7190
7190
  rightIconData;
7191
7191
  selectOnFocus = false;
7192
7192
  emptyPlace = false;
7193
- firstDayOfWeek = '1'; // default is sunday ioneJS
7193
+ firstDayOfWeek = '1'; // Monday by default
7194
7194
  noStyle = false;
7195
7195
  hideArrowButtons = false;
7196
7196
  get isDate() {
7197
7197
  return this.type === "date";
7198
7198
  }
7199
+ get hasValue() {
7200
+ return super.hasValue || (this.placeholder !== '' && this.placeholder !== null && this.placeholder !== undefined);
7201
+ }
7199
7202
  leftIconClick = new EventEmitter();
7200
7203
  leftIconMouseDown = new EventEmitter();
7201
7204
  leftIconMouseUp = new EventEmitter();
@@ -7266,6 +7269,15 @@ class InputTextComponent extends BaseInputComponent {
7266
7269
  this.elementRef = elementRef;
7267
7270
  super._markAsOnPush();
7268
7271
  }
7272
+ ngAfterViewInit() {
7273
+ // label was not correctly being used, so this is a legacy issue fix
7274
+ // if you want to use a placeholder (as a placeholder is meant to be used) then you need to set the label as well
7275
+ if (!this.label) {
7276
+ this.label = this.placeholder;
7277
+ this.placeholder = '';
7278
+ }
7279
+ super.ngAfterViewInit();
7280
+ }
7269
7281
  handleLeftIconClick(event) {
7270
7282
  event.preventDefault();
7271
7283
  event.stopPropagation();
@@ -7402,18 +7414,19 @@ class InputTextComponent extends BaseInputComponent {
7402
7414
  // Internal default first day of week
7403
7415
  let weekdayOffset = parseInt(this.firstDayOfWeek, 10);
7404
7416
  if (isNaN(weekdayOffset) || weekdayOffset < 1 || weekdayOffset > 7) {
7405
- weekdayOffset = 2;
7417
+ weekdayOffset = 1; // Monday
7406
7418
  }
7407
- const jsWeekdayIndex = (weekdayOffset - 1) % 7;
7419
+ // 1 = Monday, 2 = Tuesday, 3 = Wednesday, ..., 7 = Sunday
7420
+ const jsWeekdayIndex = weekdayOffset - 1;
7408
7421
  const jan4 = new Date(year, 0, 4);
7409
7422
  const jan4Day = jan4.getDay();
7410
7423
  const diffToMonday = (jan4Day + 6) % 7;
7411
7424
  const firstWeekMonday = new Date(jan4);
7412
- firstWeekMonday.setDate(jan4.getDate() - diffToMonday); // Monday of week 1
7425
+ firstWeekMonday.setDate(jan4.getDate() - diffToMonday);
7413
7426
  const baseWeekStart = new Date(firstWeekMonday);
7414
- baseWeekStart.setDate(baseWeekStart.getDate() + (week - 1) * 7);
7415
- const targetDate = new Date(baseWeekStart);
7416
- targetDate.setDate(baseWeekStart.getDate() + jsWeekdayIndex);
7427
+ baseWeekStart.setDate(firstWeekMonday.getDate() + (week - 1) * 7);
7428
+ let targetDate = new Date(baseWeekStart);
7429
+ targetDate.setDate(targetDate.getDate() + jsWeekdayIndex);
7417
7430
  if (targetDate < today) {
7418
7431
  year += 1;
7419
7432
  const jan4Next = new Date(year, 0, 4);
@@ -7422,14 +7435,19 @@ class InputTextComponent extends BaseInputComponent {
7422
7435
  const firstWeekMondayNext = new Date(jan4Next);
7423
7436
  firstWeekMondayNext.setDate(jan4Next.getDate() - diffToMondayNext);
7424
7437
  const baseWeekStartNext = new Date(firstWeekMondayNext);
7425
- baseWeekStartNext.setDate(baseWeekStartNext.getDate() + (week - 1) * 7);
7426
- targetDate.setFullYear(year);
7427
- targetDate.setMonth(baseWeekStartNext.getMonth());
7428
- targetDate.setDate(baseWeekStartNext.getDate() + jsWeekdayIndex);
7438
+ baseWeekStartNext.setDate(firstWeekMondayNext.getDate() + (week - 1) * 7);
7439
+ targetDate = new Date(baseWeekStartNext);
7440
+ targetDate.setDate(targetDate.getDate() + jsWeekdayIndex);
7429
7441
  }
7430
- this.model = targetDate.toISOString().substring(0, 10);
7442
+ this.model = this.formatDateLocal(targetDate);
7431
7443
  this.modelChange.emit(this.model);
7432
7444
  }
7445
+ formatDateLocal(date) {
7446
+ const year = date.getFullYear();
7447
+ const month = String(date.getMonth() + 1).padStart(2, '0');
7448
+ const day = String(date.getDate()).padStart(2, '0');
7449
+ return `${year}-${month}-${day}`;
7450
+ }
7433
7451
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: InputTextComponent, deps: [{ token: FormComponent, optional: true }, { token: i0.ChangeDetectorRef }, { token: OverlayService }, { token: FormInputUserModelChangeListenerService }, { token: NgZoneWrapperService }, { token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component });
7434
7452
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: InputTextComponent, isStandalone: false, selector: "co-input-text", inputs: { useContent: "useContent", placeholder: "placeholder", align: "align", type: "type", formatPipe: "formatPipe", min: "min", max: "max", pattern: "pattern", digitsOnly: "digitsOnly", excludePlusMinus: "excludePlusMinus", showClearButton: "showClearButton", keyDownWhiteList: "keyDownWhiteList", showPlaceholderOnFocus: "showPlaceholderOnFocus", leftIcon: "leftIcon", rightIcon: "rightIcon", leftIconData: "leftIconData", rightIconData: "rightIconData", selectOnFocus: "selectOnFocus", emptyPlace: "emptyPlace", firstDayOfWeek: "firstDayOfWeek", noStyle: "noStyle", hideArrowButtons: "hideArrowButtons", model: "model" }, outputs: { leftIconClick: "leftIconClick", leftIconMouseDown: "leftIconMouseDown", leftIconMouseUp: "leftIconMouseUp", rightIconClick: "rightIconClick", rightIconMouseDown: "rightIconMouseDown", rightIconMouseUp: "rightIconMouseUp", clearIconClick: "clearIconClick", isFocused: "isFocused" }, host: { listeners: { "document:pointerup": "handleDocumentPointerUp($event)", "document:pointercancel": "handleDocumentPointerUp($event)" }, properties: { "class.no-style": "this.noStyle", "class.hide-arrows": "this.hideArrowButtons", "class.isDate": "this.isDate", "class.co-input-text": "this.showClass", "class.has-left-icon": "this.hasLeftIcon", "class.has-right-icon": "this.hasRightIcon", "class.has-own-label": "this.hasOwnLabel" } }, providers: [
7435
7453
  OverlayService, {
@@ -7452,7 +7470,7 @@ class InputTextComponent extends BaseInputComponent {
7452
7470
  <div class="input-wrapper">
7453
7471
  @if (showPlaceholderOnFocus || (!showPlaceholderOnFocus && !hasValue && !focused)) {
7454
7472
  <label
7455
- [textContent]="placeholder"></label>
7473
+ [textContent]="label"></label>
7456
7474
  }
7457
7475
  @if (!focused && !useContent && formatPipe) {
7458
7476
  <span class="input-text-formatted"
@@ -7466,7 +7484,7 @@ class InputTextComponent extends BaseInputComponent {
7466
7484
  [class.input-input-hidden]="useContent"
7467
7485
  [ngClass]="align"
7468
7486
  [type]="isWeekInputMode ? 'text' : (isFocusedOnDate || (hasValue && emptyPlace)) ? 'date' : (digitsOnly ? 'number' : (type === 'date' ? 'text' : type))"
7469
- [placeholder]="type === 'date' && !isFocusedOnDate ? '' : ''"
7487
+ [placeholder]="type === 'date' && !isFocusedOnDate ? '' : placeholder"
7470
7488
  [pattern]="type === 'date' ? pattern : undefined"
7471
7489
  [ngModel]="model"
7472
7490
  [min]="(type === 'number' || type === 'date') && this.min ? this.min : undefined"
@@ -7528,7 +7546,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
7528
7546
  <div class="input-wrapper">
7529
7547
  @if (showPlaceholderOnFocus || (!showPlaceholderOnFocus && !hasValue && !focused)) {
7530
7548
  <label
7531
- [textContent]="placeholder"></label>
7549
+ [textContent]="label"></label>
7532
7550
  }
7533
7551
  @if (!focused && !useContent && formatPipe) {
7534
7552
  <span class="input-text-formatted"
@@ -7542,7 +7560,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
7542
7560
  [class.input-input-hidden]="useContent"
7543
7561
  [ngClass]="align"
7544
7562
  [type]="isWeekInputMode ? 'text' : (isFocusedOnDate || (hasValue && emptyPlace)) ? 'date' : (digitsOnly ? 'number' : (type === 'date' ? 'text' : type))"
7545
- [placeholder]="type === 'date' && !isFocusedOnDate ? '' : ''"
7563
+ [placeholder]="type === 'date' && !isFocusedOnDate ? '' : placeholder"
7546
7564
  [pattern]="type === 'date' ? pattern : undefined"
7547
7565
  [ngModel]="model"
7548
7566
  [min]="(type === 'number' || type === 'date') && this.min ? this.min : undefined"
@@ -13596,12 +13614,38 @@ class LovUtils {
13596
13614
  .filter(val => val != null && val !== '')
13597
13615
  .join(' - ');
13598
13616
  }
13617
+ static getHighlightParts(value, term) {
13618
+ const text = value ?? '';
13619
+ if (!text || !term) {
13620
+ return [{ text: text, match: false }];
13621
+ }
13622
+ const regex = new RegExp(LovUtils._escapeRegExp(term), 'gi');
13623
+ const parts = [];
13624
+ let lastIndex = 0;
13625
+ let match;
13626
+ while ((match = regex.exec(text)) !== null) {
13627
+ if (match.index > lastIndex) {
13628
+ parts.push({ text: text.slice(lastIndex, match.index), match: false });
13629
+ }
13630
+ parts.push({ text: match[0], match: true });
13631
+ lastIndex = match.index + match[0].length;
13632
+ if (match.index === regex.lastIndex) {
13633
+ regex.lastIndex++;
13634
+ }
13635
+ }
13636
+ if (lastIndex < text.length) {
13637
+ parts.push({ text: text.slice(lastIndex), match: false });
13638
+ }
13639
+ return parts;
13640
+ }
13641
+ static _escapeRegExp(value) {
13642
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
13643
+ }
13599
13644
  }
13600
13645
 
13601
13646
  class ListOfValuesPopupComponent {
13602
13647
  iconCacheService;
13603
13648
  _elementRef;
13604
- lovUtils = LovUtils;
13605
13649
  set lovItems(children) {
13606
13650
  this._lovItems = children.toArray();
13607
13651
  this._scrollIntoView();
@@ -13719,7 +13763,11 @@ class ListOfValuesPopupComponent {
13719
13763
  }
13720
13764
  }
13721
13765
  selectModelByViewModel(viewModel, closePopup = true) {
13722
- this.model = this.viewModelsMain.find(vm => vm === viewModel).model;
13766
+ const found = this.viewModelsMain.find(vm => vm === viewModel);
13767
+ if (!found) {
13768
+ return;
13769
+ }
13770
+ this.model = found.model;
13723
13771
  this.modelChange.emit(this.model);
13724
13772
  this._scrollIntoView();
13725
13773
  if (closePopup) {
@@ -13752,6 +13800,9 @@ class ListOfValuesPopupComponent {
13752
13800
  this.modelChange.emit(this.model);
13753
13801
  }
13754
13802
  selectNextOption(back = false) {
13803
+ if (this.viewModels.length === 0) {
13804
+ return;
13805
+ }
13755
13806
  let nextModel;
13756
13807
  if (!this.highLightModel) {
13757
13808
  nextModel = this.viewModels[back ? this.viewModels.length - 1 : 0];
@@ -13827,6 +13878,9 @@ class ListOfValuesPopupComponent {
13827
13878
  }
13828
13879
  });
13829
13880
  });
13881
+ this.viewModels.forEach((vm) => {
13882
+ vm.highlightParts = LovUtils.getHighlightParts(LovUtils.getDisplayValue(vm.model, this.displayField), this.searchTerm);
13883
+ });
13830
13884
  }
13831
13885
  _scrollIntoView() {
13832
13886
  const activeIndex = this.viewModels.findIndex(vmm => vmm === this.highLightModel);
@@ -13888,18 +13942,30 @@ class ListOfValuesPopupComponent {
13888
13942
  <co-icon class="input-text-left-icon" [iconData]="iconCacheService.getIcon(viewModel.model[optionIcon])">
13889
13943
  </co-icon>
13890
13944
  }
13891
- <span class="lov-options-text" [textContent]="lovUtils.getDisplayValue(viewModel.model, displayField)"></span>
13945
+ <ng-container [ngTemplateOutlet]="optionText" [ngTemplateOutletContext]="{parts: viewModel.highlightParts}"></ng-container>
13892
13946
  }
13893
13947
  @if (multiselect) {
13894
13948
  <co-input-checkbox [model]="viewModel.checked"
13895
13949
  (modelChange)="selectViewModel(viewModel, false)"></co-input-checkbox>
13896
- <span class="lov-options-text" [textContent]="lovUtils.getDisplayValue(viewModel.model, displayField)"></span>
13950
+ <ng-container [ngTemplateOutlet]="optionText" [ngTemplateOutletContext]="{parts: viewModel.highlightParts}"></ng-container>
13897
13951
  }
13898
13952
  </li>
13899
13953
  }
13900
13954
  </ul>
13901
13955
  </div>
13902
- `, isInline: true, dependencies: [{ kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: InputCheckboxComponent, selector: "co-input-checkbox", inputs: ["cssClass", "clickableLabel"], outputs: ["modelChange"] }, { kind: "directive", type: OverlayDirective, selector: "[overlay]", inputs: ["overlay", "view", "inline", "keepInView", "inheritWidth", "rightAlign", "fullSize"] }, { kind: "directive", type: ClickOutsideDirective, selector: "[clickOutside]", inputs: ["clickOutside", "alwaysTrigger"], outputs: ["clickOutside"], exportAs: ["clickOutside"] }, { kind: "component", type: IconComponent, selector: "co-icon", inputs: ["icon", "iconData"] }, { kind: "component", type: InputSearchComponent, selector: "co-input-search", inputs: ["placeholder", "handleKeydown", "useLeftIcon", "useRightIcon", "leftIconData", "rightIconData", "centerLabel"], outputs: ["search", "isFocused", "leftIconClick", "rightIconClick"] }, { kind: "pipe", type: CoreLocalizePipe, name: "coreLocalize" }], encapsulation: i0.ViewEncapsulation.None });
13956
+
13957
+ <ng-template #optionText let-parts="parts">
13958
+ <span class="lov-options-text">
13959
+ @for (part of parts; track $index) {
13960
+ @if (part.match) {
13961
+ <mark class="lov-highlight" [textContent]="part.text"></mark>
13962
+ } @else {
13963
+ <span [textContent]="part.text"></span>
13964
+ }
13965
+ }
13966
+ </span>
13967
+ </ng-template>
13968
+ `, isInline: true, dependencies: [{ kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: InputCheckboxComponent, selector: "co-input-checkbox", inputs: ["cssClass", "clickableLabel"], outputs: ["modelChange"] }, { kind: "directive", type: OverlayDirective, selector: "[overlay]", inputs: ["overlay", "view", "inline", "keepInView", "inheritWidth", "rightAlign", "fullSize"] }, { kind: "directive", type: ClickOutsideDirective, selector: "[clickOutside]", inputs: ["clickOutside", "alwaysTrigger"], outputs: ["clickOutside"], exportAs: ["clickOutside"] }, { kind: "component", type: IconComponent, selector: "co-icon", inputs: ["icon", "iconData"] }, { kind: "component", type: InputSearchComponent, selector: "co-input-search", inputs: ["placeholder", "handleKeydown", "useLeftIcon", "useRightIcon", "leftIconData", "rightIconData", "centerLabel"], outputs: ["search", "isFocused", "leftIconClick", "rightIconClick"] }, { kind: "pipe", type: CoreLocalizePipe, name: "coreLocalize" }], encapsulation: i0.ViewEncapsulation.None });
13903
13969
  }
13904
13970
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: ListOfValuesPopupComponent, decorators: [{
13905
13971
  type: Component,
@@ -13939,17 +14005,29 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
13939
14005
  <co-icon class="input-text-left-icon" [iconData]="iconCacheService.getIcon(viewModel.model[optionIcon])">
13940
14006
  </co-icon>
13941
14007
  }
13942
- <span class="lov-options-text" [textContent]="lovUtils.getDisplayValue(viewModel.model, displayField)"></span>
14008
+ <ng-container [ngTemplateOutlet]="optionText" [ngTemplateOutletContext]="{parts: viewModel.highlightParts}"></ng-container>
13943
14009
  }
13944
14010
  @if (multiselect) {
13945
14011
  <co-input-checkbox [model]="viewModel.checked"
13946
14012
  (modelChange)="selectViewModel(viewModel, false)"></co-input-checkbox>
13947
- <span class="lov-options-text" [textContent]="lovUtils.getDisplayValue(viewModel.model, displayField)"></span>
14013
+ <ng-container [ngTemplateOutlet]="optionText" [ngTemplateOutletContext]="{parts: viewModel.highlightParts}"></ng-container>
13948
14014
  }
13949
14015
  </li>
13950
14016
  }
13951
14017
  </ul>
13952
14018
  </div>
14019
+
14020
+ <ng-template #optionText let-parts="parts">
14021
+ <span class="lov-options-text">
14022
+ @for (part of parts; track $index) {
14023
+ @if (part.match) {
14024
+ <mark class="lov-highlight" [textContent]="part.text"></mark>
14025
+ } @else {
14026
+ <span [textContent]="part.text"></span>
14027
+ }
14028
+ }
14029
+ </span>
14030
+ </ng-template>
13953
14031
  `,
13954
14032
  encapsulation: ViewEncapsulation.None,
13955
14033
  standalone: false
@@ -14047,6 +14125,7 @@ class ListOfValuesComponent extends BaseInputComponent {
14047
14125
  isLoading = false;
14048
14126
  _collection = [];
14049
14127
  debounceTimeout;
14128
+ _filterRequestId = 0;
14050
14129
  _lovPopupComponentRef;
14051
14130
  constructor(formComponent, iconCacheService, changeDetector, overlayService, formUserChangeListener, ngZoneWrapper, elementRef) {
14052
14131
  super(changeDetector, overlayService, formUserChangeListener, ngZoneWrapper, elementRef);
@@ -14063,6 +14142,25 @@ class ListOfValuesComponent extends BaseInputComponent {
14063
14142
  super.ngOnInit();
14064
14143
  this._setSelectedModel();
14065
14144
  }
14145
+ // Value shown in the input: the search text while fetching (largeCollection), otherwise the
14146
+ // selected model(s). Lets a single <co-input-text> serve both modes without a template split.
14147
+ get inputModel() {
14148
+ if (this.largeCollection) {
14149
+ return this.filterText;
14150
+ }
14151
+ if (this.multiselect) {
14152
+ return (!this.selectedModels || this.selectedModels.length === 0) ? null : this.selectedModels;
14153
+ }
14154
+ return this.selectedModel;
14155
+ }
14156
+ handleModelChange(model) {
14157
+ if (this.largeCollection) {
14158
+ this.onModelChange(model);
14159
+ }
14160
+ else {
14161
+ this.handleInputModelChange(model);
14162
+ }
14163
+ }
14066
14164
  handleInputModelChange(model) {
14067
14165
  if (this._lovPopupComponentRef) {
14068
14166
  this._lovPopupComponentRef.instance.searchTerm = model;
@@ -14077,26 +14175,33 @@ class ListOfValuesComponent extends BaseInputComponent {
14077
14175
  }
14078
14176
  onModelChange(model) {
14079
14177
  this.isLoading = true;
14178
+ // Mark this keystroke as the latest request immediately, so an older in-flight
14179
+ // request that resolves within the debounce window is still discarded.
14180
+ const requestId = ++this._filterRequestId;
14080
14181
  clearTimeout(this.debounceTimeout);
14081
14182
  this.debounceTimeout = setTimeout(() => {
14082
- this.applyFilter(model);
14183
+ this.applyFilter(model, requestId);
14083
14184
  }, 300);
14084
14185
  }
14085
- async applyFilter(text) {
14186
+ async applyFilter(text, requestId = ++this._filterRequestId) {
14187
+ await new Promise(resolve => setTimeout(resolve, 300));
14188
+ if (requestId !== this._filterRequestId) {
14189
+ return;
14190
+ }
14086
14191
  if (text?.length < 3) {
14087
- await new Promise(resolve => setTimeout(resolve, 300));
14088
14192
  this.collection = undefined;
14193
+ this.filterText = text;
14194
+ this.closePopup();
14195
+ this.isLoading = false;
14196
+ this.changeDetector.detectChanges();
14197
+ return;
14089
14198
  }
14090
- else {
14091
- await new Promise(resolve => setTimeout(resolve, 300));
14092
- this.collection = await this.collectionLoadFn(text);
14199
+ const loaded = await this.collectionLoadFn(text);
14200
+ if (requestId !== this._filterRequestId) {
14201
+ return;
14093
14202
  }
14203
+ this.collection = loaded;
14094
14204
  this.filterText = text;
14095
- if (!this.collection) {
14096
- this.changeDetector.detectChanges();
14097
- this.isLoading = false;
14098
- return [];
14099
- }
14100
14205
  this.filteredCollection = this.collection?.filter((item) => {
14101
14206
  if (this.collectionLoadFnProp && this.collectionLoadFnProp.length > 0) {
14102
14207
  return item[this.collectionLoadFnProp] && item[this.collectionLoadFnProp].toLowerCase().includes(text.toLowerCase());
@@ -14105,12 +14210,19 @@ class ListOfValuesComponent extends BaseInputComponent {
14105
14210
  return true;
14106
14211
  }
14107
14212
  });
14108
- (this.filteredCollection?.length > 0 && this.filterText?.length > 2) ? this.openPopup() : this.closePopup();
14213
+ if (this.filteredCollection?.length > 0 && this.filterText?.length > 2) {
14214
+ this.openPopup(); // no-op when already open
14215
+ if (this._lovPopupComponentRef) {
14216
+ // Refresh the open popup in place; no re-create, so no flicker.
14217
+ this._lovPopupComponentRef.instance.collection = this.collection;
14218
+ this._lovPopupComponentRef.instance.searchTerm = text;
14219
+ }
14220
+ }
14221
+ else {
14222
+ this.closePopup();
14223
+ }
14109
14224
  this.isLoading = false;
14110
14225
  this.changeDetector.detectChanges();
14111
- if (this._lovPopupComponentRef) {
14112
- this._lovPopupComponentRef.instance.searchTerm = text;
14113
- }
14114
14226
  }
14115
14227
  handleInputKeyDown(event) {
14116
14228
  if (event) {
@@ -14155,6 +14267,11 @@ class ListOfValuesComponent extends BaseInputComponent {
14155
14267
  if (this.readonly) {
14156
14268
  return;
14157
14269
  }
14270
+ // Idempotent: keep the existing popup so continued typing refreshes it in place
14271
+ // instead of re-creating it (which would replay the open animation / flicker).
14272
+ if (this._lovPopupComponentRef) {
14273
+ return;
14274
+ }
14158
14275
  this.isSelectOpen = true;
14159
14276
  this._lovPopupComponentRef = this.overlayService.createComponent(ListOfValuesPopupComponent, {
14160
14277
  parentForOverlay: this.elementRef,
@@ -14213,6 +14330,11 @@ class ListOfValuesComponent extends BaseInputComponent {
14213
14330
  this.focused = false;
14214
14331
  }
14215
14332
  checkModel() {
14333
+ if (this.largeCollection) {
14334
+ // The input holds the search text (not a to-be-resolved value) and selection happens via the
14335
+ // popup, so blur must not try to match/clear the model here.
14336
+ return;
14337
+ }
14216
14338
  if (!this.multiselect && this.selectedModel && this.collection) {
14217
14339
  const model = this.collection.find(c => LovUtils.getDisplayValue(c, this.displayField) === this.selectedModel);
14218
14340
  if (model) {
@@ -14245,6 +14367,7 @@ class ListOfValuesComponent extends BaseInputComponent {
14245
14367
  }
14246
14368
  else {
14247
14369
  this.selectedModel = '';
14370
+ this.filterText = '';
14248
14371
  }
14249
14372
  }
14250
14373
  }
@@ -14256,59 +14379,46 @@ class ListOfValuesComponent extends BaseInputComponent {
14256
14379
  useExisting: forwardRef(() => ListOfValuesComponent)
14257
14380
  }
14258
14381
  ], viewQueries: [{ propertyName: "parentForOverlay", first: true, predicate: ["parentForOverlay"], descendants: true, read: ElementRef }], usesInheritance: true, ngImport: i0, template: `
14259
- @if (!largeCollection) {
14260
- <co-input-text
14261
- aria-haspopup="listbox"
14262
- [attr.aria-expanded]="isSelectOpen"
14263
- aria-controls="lov-popup"
14264
- role="combobox"
14265
- class="no-focus-line"
14266
- overlayParent
14267
- #parentForOverlay="overlayParent"
14268
- type="text"
14269
- [id]="label"
14270
- [model]="multiselect ? ((!selectedModels || selectedModels.length === 0) ? null : selectedModels ) : selectedModel"
14271
- [placeholder]="label"
14272
- [myFormInputInstance]="this"
14273
- [readonly]="readonly"
14274
- [disabled]="disabled"
14275
- [required]="required"
14276
- [noClickFocus]="false"
14277
- [leftIconData]=" model && model[optionIcon] ? iconCacheService.getIcon(model[optionIcon]) : leftIconData"
14278
- [rightIcon]="isSelectOpen ? icons.ChevronUpRegular : icons.ChevronDownRegular"
14279
- [showClearButton]="true"
14280
- [useContent]="multiselect"
14281
- [customHeight]="multiselect"
14282
- [keepFocussed]="keepFocussed"
14283
- (modelChange)="handleInputModelChange($event)"
14284
- (click)="openPopup()"
14285
- (rightIconClick)="toggleSelect()"
14286
- (keydown)="handleInputKeyDown($event)"
14287
- (clearIconClick)="clearModel($event)"
14288
- (blur)="checkModel()">
14289
- @if (multiselect && showChips) {
14290
- <div class="multiselect-chips-wrapper">
14291
- @for (chip of model; track chip) {
14292
- <div class="chips">
14293
- <span class="chips-description" [textContent]="lovUtils.getDisplayValue(chip, displayField)"></span>
14294
- <co-icon class="remove-chip-icon" [icon]="icons.CrossSkinny" (click)="removeOptionFromModel(chip)"></co-icon>
14295
- </div>
14296
- }
14297
- </div>
14298
- }
14299
- </co-input-text>
14300
- }
14301
-
14302
- @if (largeCollection) {
14303
- <co-input-text
14304
- [model]="filterText"
14305
- [placeholder]="label"
14306
- [required]="required"
14307
- [disabled]="disabled"
14308
- [readonly]="readonly"
14309
- (modelChange)="onModelChange($event)">
14310
- </co-input-text>
14311
- }
14382
+ <co-input-text
14383
+ aria-haspopup="listbox"
14384
+ [attr.aria-expanded]="isSelectOpen"
14385
+ aria-controls="lov-popup"
14386
+ role="combobox"
14387
+ class="no-focus-line"
14388
+ overlayParent
14389
+ #parentForOverlay="overlayParent"
14390
+ type="text"
14391
+ [id]="label"
14392
+ [model]="inputModel"
14393
+ [placeholder]="label"
14394
+ [myFormInputInstance]="this"
14395
+ [readonly]="readonly"
14396
+ [disabled]="disabled"
14397
+ [required]="required"
14398
+ [noClickFocus]="false"
14399
+ [leftIconData]=" model && model[optionIcon] ? iconCacheService.getIcon(model[optionIcon]) : leftIconData"
14400
+ [rightIcon]="isSelectOpen ? icons.ChevronUpRegular : icons.ChevronDownRegular"
14401
+ [showClearButton]="true"
14402
+ [useContent]="multiselect"
14403
+ [customHeight]="multiselect"
14404
+ [keepFocussed]="keepFocussed"
14405
+ (modelChange)="handleModelChange($event)"
14406
+ (click)="openPopup()"
14407
+ (rightIconClick)="toggleSelect()"
14408
+ (keydown)="handleInputKeyDown($event)"
14409
+ (clearIconClick)="clearModel($event)"
14410
+ (blur)="checkModel()">
14411
+ @if (multiselect && showChips) {
14412
+ <div class="multiselect-chips-wrapper">
14413
+ @for (chip of model; track chip) {
14414
+ <div class="chips">
14415
+ <span class="chips-description" [textContent]="lovUtils.getDisplayValue(chip, displayField)"></span>
14416
+ <co-icon class="remove-chip-icon" [icon]="icons.CrossSkinny" (click)="removeOptionFromModel(chip)"></co-icon>
14417
+ </div>
14418
+ }
14419
+ </div>
14420
+ }
14421
+ </co-input-text>
14312
14422
  @if (isLoading) {
14313
14423
  <div class="filter-loader"><span></span></div>
14314
14424
  }
@@ -14320,59 +14430,46 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
14320
14430
  args: [{
14321
14431
  selector: 'co-list-of-values',
14322
14432
  template: `
14323
- @if (!largeCollection) {
14324
- <co-input-text
14325
- aria-haspopup="listbox"
14326
- [attr.aria-expanded]="isSelectOpen"
14327
- aria-controls="lov-popup"
14328
- role="combobox"
14329
- class="no-focus-line"
14330
- overlayParent
14331
- #parentForOverlay="overlayParent"
14332
- type="text"
14333
- [id]="label"
14334
- [model]="multiselect ? ((!selectedModels || selectedModels.length === 0) ? null : selectedModels ) : selectedModel"
14335
- [placeholder]="label"
14336
- [myFormInputInstance]="this"
14337
- [readonly]="readonly"
14338
- [disabled]="disabled"
14339
- [required]="required"
14340
- [noClickFocus]="false"
14341
- [leftIconData]=" model && model[optionIcon] ? iconCacheService.getIcon(model[optionIcon]) : leftIconData"
14342
- [rightIcon]="isSelectOpen ? icons.ChevronUpRegular : icons.ChevronDownRegular"
14343
- [showClearButton]="true"
14344
- [useContent]="multiselect"
14345
- [customHeight]="multiselect"
14346
- [keepFocussed]="keepFocussed"
14347
- (modelChange)="handleInputModelChange($event)"
14348
- (click)="openPopup()"
14349
- (rightIconClick)="toggleSelect()"
14350
- (keydown)="handleInputKeyDown($event)"
14351
- (clearIconClick)="clearModel($event)"
14352
- (blur)="checkModel()">
14353
- @if (multiselect && showChips) {
14354
- <div class="multiselect-chips-wrapper">
14355
- @for (chip of model; track chip) {
14356
- <div class="chips">
14357
- <span class="chips-description" [textContent]="lovUtils.getDisplayValue(chip, displayField)"></span>
14358
- <co-icon class="remove-chip-icon" [icon]="icons.CrossSkinny" (click)="removeOptionFromModel(chip)"></co-icon>
14359
- </div>
14360
- }
14361
- </div>
14362
- }
14363
- </co-input-text>
14364
- }
14365
-
14366
- @if (largeCollection) {
14367
- <co-input-text
14368
- [model]="filterText"
14369
- [placeholder]="label"
14370
- [required]="required"
14371
- [disabled]="disabled"
14372
- [readonly]="readonly"
14373
- (modelChange)="onModelChange($event)">
14374
- </co-input-text>
14375
- }
14433
+ <co-input-text
14434
+ aria-haspopup="listbox"
14435
+ [attr.aria-expanded]="isSelectOpen"
14436
+ aria-controls="lov-popup"
14437
+ role="combobox"
14438
+ class="no-focus-line"
14439
+ overlayParent
14440
+ #parentForOverlay="overlayParent"
14441
+ type="text"
14442
+ [id]="label"
14443
+ [model]="inputModel"
14444
+ [placeholder]="label"
14445
+ [myFormInputInstance]="this"
14446
+ [readonly]="readonly"
14447
+ [disabled]="disabled"
14448
+ [required]="required"
14449
+ [noClickFocus]="false"
14450
+ [leftIconData]=" model && model[optionIcon] ? iconCacheService.getIcon(model[optionIcon]) : leftIconData"
14451
+ [rightIcon]="isSelectOpen ? icons.ChevronUpRegular : icons.ChevronDownRegular"
14452
+ [showClearButton]="true"
14453
+ [useContent]="multiselect"
14454
+ [customHeight]="multiselect"
14455
+ [keepFocussed]="keepFocussed"
14456
+ (modelChange)="handleModelChange($event)"
14457
+ (click)="openPopup()"
14458
+ (rightIconClick)="toggleSelect()"
14459
+ (keydown)="handleInputKeyDown($event)"
14460
+ (clearIconClick)="clearModel($event)"
14461
+ (blur)="checkModel()">
14462
+ @if (multiselect && showChips) {
14463
+ <div class="multiselect-chips-wrapper">
14464
+ @for (chip of model; track chip) {
14465
+ <div class="chips">
14466
+ <span class="chips-description" [textContent]="lovUtils.getDisplayValue(chip, displayField)"></span>
14467
+ <co-icon class="remove-chip-icon" [icon]="icons.CrossSkinny" (click)="removeOptionFromModel(chip)"></co-icon>
14468
+ </div>
14469
+ }
14470
+ </div>
14471
+ }
14472
+ </co-input-text>
14376
14473
  @if (isLoading) {
14377
14474
  <div class="filter-loader"><span></span></div>
14378
14475
  }