@clr/angular 18.0.0-beta.3 → 18.0.0-beta.4

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.
Files changed (48) hide show
  1. package/fesm2022/clr-angular-data-datagrid.mjs +8 -2
  2. package/fesm2022/clr-angular-data-datagrid.mjs.map +1 -1
  3. package/fesm2022/clr-angular-forms-combobox.mjs +332 -39
  4. package/fesm2022/clr-angular-forms-combobox.mjs.map +1 -1
  5. package/fesm2022/clr-angular-layout-nav.mjs +27 -25
  6. package/fesm2022/clr-angular-layout-nav.mjs.map +1 -1
  7. package/fesm2022/clr-angular-modal.mjs +2 -3
  8. package/fesm2022/clr-angular-modal.mjs.map +1 -1
  9. package/fesm2022/clr-angular-utils.mjs +5 -0
  10. package/fesm2022/clr-angular-utils.mjs.map +1 -1
  11. package/fesm2022/clr-angular-wizard.mjs +427 -224
  12. package/fesm2022/clr-angular-wizard.mjs.map +1 -1
  13. package/package.json +7 -2
  14. package/schematics/collection.json +10 -0
  15. package/schematics/ng-update/index.d.ts +2 -0
  16. package/schematics/ng-update/index.js +66 -0
  17. package/schematics/ng-update/index.js.map +1 -0
  18. package/schematics/ng-update/migrations/css-migration.d.ts +5 -0
  19. package/schematics/ng-update/migrations/css-migration.js +167 -0
  20. package/schematics/ng-update/migrations/css-migration.js.map +1 -0
  21. package/schematics/ng-update/migrations/import-migration.d.ts +4 -0
  22. package/schematics/ng-update/migrations/import-migration.js +136 -0
  23. package/schematics/ng-update/migrations/import-migration.js.map +1 -0
  24. package/schematics/ng-update/migrations/template-migration.d.ts +5 -0
  25. package/schematics/ng-update/migrations/template-migration.js +175 -0
  26. package/schematics/ng-update/migrations/template-migration.js.map +1 -0
  27. package/schematics/ng-update/replacements/css-replacements.d.ts +17 -0
  28. package/schematics/ng-update/replacements/css-replacements.js +74 -0
  29. package/schematics/ng-update/replacements/css-replacements.js.map +1 -0
  30. package/schematics/ng-update/replacements/import-replacements.d.ts +13 -0
  31. package/schematics/ng-update/replacements/import-replacements.js +101 -0
  32. package/schematics/ng-update/replacements/import-replacements.js.map +1 -0
  33. package/schematics/ng-update/replacements/symbol-replacements.d.ts +12 -0
  34. package/schematics/ng-update/replacements/symbol-replacements.js +68 -0
  35. package/schematics/ng-update/replacements/symbol-replacements.js.map +1 -0
  36. package/schematics/ng-update/replacements/template-replacements.d.ts +14 -0
  37. package/schematics/ng-update/replacements/template-replacements.js +53 -0
  38. package/schematics/ng-update/replacements/template-replacements.js.map +1 -0
  39. package/schematics/ng-update/utils/file-visitor.d.ts +8 -0
  40. package/schematics/ng-update/utils/file-visitor.js +44 -0
  41. package/schematics/ng-update/utils/file-visitor.js.map +1 -0
  42. package/schematics/vitest.config.d.ts +2 -0
  43. package/schematics/vitest.config.js +17 -0
  44. package/schematics/vitest.config.js.map +1 -0
  45. package/types/clr-angular-forms-combobox.d.ts +55 -9
  46. package/types/clr-angular-layout-nav.d.ts +6 -2
  47. package/types/clr-angular-utils.d.ts +5 -0
  48. package/types/clr-angular-wizard.d.ts +89 -35
@@ -1,21 +1,21 @@
1
- import * as i3 from '@angular/common';
1
+ import * as i8 from '@angular/common';
2
2
  import { isPlatformBrowser, NgForOf, CommonModule } from '@angular/common';
3
3
  import * as i0 from '@angular/core';
4
- import { Injectable, ViewChild, Optional, Component, Input, Directive, PLATFORM_ID, Inject, HostListener, HostBinding, DOCUMENT, ContentChildren, EventEmitter, ContentChild, Output, Self, NgModule } from '@angular/core';
4
+ import { Injectable, ViewChild, Optional, Component, Input, Directive, PLATFORM_ID, Inject, HostListener, HostBinding, DOCUMENT, ContentChildren, EventEmitter, booleanAttribute, ContentChild, ViewChildren, Output, Self, Host, NgModule } from '@angular/core';
5
5
  import * as i1$1 from '@angular/forms';
6
6
  import { FormsModule } from '@angular/forms';
7
7
  import * as i1 from '@clr/angular/forms/common';
8
8
  import { ClrAbstractContainer, NgControlService, ControlIdService, ControlClassService, WrappedFormControl, ClrCommonFormsModule } from '@clr/angular/forms/common';
9
- import * as i8 from '@clr/angular/icon';
9
+ import * as i9 from '@clr/angular/icon';
10
10
  import { ClarityIcons, successStandardIcon, errorStandardIcon, angleIcon, windowCloseIcon, ClrIcon } from '@clr/angular/icon';
11
11
  import * as i4 from '@clr/angular/popover/common';
12
12
  import { POPOVER_HOST_ORIGIN, ClrPopoverPosition, ClrPopoverType, ClrPopoverHostDirective, ClrPopoverModuleNext } from '@clr/angular/popover/common';
13
13
  import * as i5 from '@clr/angular/progress/spinner';
14
14
  import { ClrSpinnerModule } from '@clr/angular/progress/spinner';
15
- import * as i3$1 from '@clr/angular/utils';
15
+ import * as i3 from '@clr/angular/utils';
16
16
  import { ArrowKeyDirection, Keys, customFocusableItemProvider, uniqueIdFactory, ClrLoadingState, IF_ACTIVE_ID, LoadingListener, IF_ACTIVE_ID_PROVIDER, FOCUS_SERVICE_PROVIDER, ClrConditionalModule, ClrKeyFocusModule } from '@clr/angular/utils';
17
+ import { BehaviorSubject, ReplaySubject, Subject, debounceTime } from 'rxjs';
17
18
  import { take } from 'rxjs/operators';
18
- import { BehaviorSubject, ReplaySubject } from 'rxjs';
19
19
 
20
20
  /*
21
21
  * Copyright (c) 2016-2026 Broadcom. All Rights Reserved.
@@ -73,7 +73,7 @@ class ClrComboboxContainer extends ClrAbstractContainer {
73
73
  <ng-content select="clr-control-success"></ng-content>
74
74
  }
75
75
  </div>
76
- `, isInline: true, dependencies: [{ kind: "directive", type: i3.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.ClrControlLabel, selector: "label", inputs: ["id", "for"] }] }); }
76
+ `, isInline: true, dependencies: [{ kind: "directive", type: i8.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.ClrControlLabel, selector: "label", inputs: ["id", "for"] }] }); }
77
77
  }
78
78
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ClrComboboxContainer, decorators: [{
79
79
  type: Component,
@@ -133,7 +133,7 @@ class ComboboxModel {
133
133
  */
134
134
  class MultiSelectComboboxModel extends ComboboxModel {
135
135
  containsItem(item) {
136
- if (!this.model) {
136
+ if (this.model === null || this.model === undefined) {
137
137
  return false;
138
138
  }
139
139
  return this.model.some(m => this.identityFn(m) === this.identityFn(item));
@@ -218,7 +218,10 @@ class MultiSelectComboboxModel extends ComboboxModel {
218
218
  */
219
219
  class SingleSelectComboboxModel extends ComboboxModel {
220
220
  containsItem(item) {
221
- return this.model !== null && this.identityFn(this.model) === this.identityFn(item);
221
+ if (this.model === null || this.model === undefined) {
222
+ return false;
223
+ }
224
+ return this.identityFn(this.model) === this.identityFn(item);
222
225
  }
223
226
  select(item) {
224
227
  this.model = item;
@@ -305,13 +308,15 @@ class OptionSelectionService {
305
308
  constructor() {
306
309
  this.loading = false;
307
310
  this.editable = false;
308
- this.filtering = true;
311
+ this.showSelectAll = false;
309
312
  // Display all options on first open, even if filter text exists.
310
313
  // https://github.com/vmware-clarity/ng-clarity/issues/386
311
314
  this.showAllOptions = true;
312
315
  this._currentInput = '';
313
316
  this._inputChanged = new BehaviorSubject('');
314
317
  this._selectionChanged = new ReplaySubject(1);
318
+ this._selectAllRequested = new Subject();
319
+ this.editableResolver = (input) => input;
315
320
  this._identityFn = (item) => item;
316
321
  this.inputChanged = this._inputChanged.asObservable();
317
322
  }
@@ -352,6 +357,12 @@ class OptionSelectionService {
352
357
  this.selectionModel.identityFn = this._identityFn;
353
358
  }
354
359
  }
360
+ get selectAllRequested() {
361
+ return this._selectAllRequested.asObservable();
362
+ }
363
+ requestSelectAll() {
364
+ this._selectAllRequested.next();
365
+ }
355
366
  select(item) {
356
367
  if (item === null || item === undefined || this.selectionModel.containsItem(item)) {
357
368
  return;
@@ -371,6 +382,33 @@ class OptionSelectionService {
371
382
  }
372
383
  this._selectionChanged.next(this.selectionModel);
373
384
  }
385
+ selectMany(items) {
386
+ let changed = false;
387
+ for (const item of items) {
388
+ if (!this.selectionModel.containsItem(item)) {
389
+ this.selectionModel.select(item);
390
+ changed = true;
391
+ }
392
+ }
393
+ if (changed) {
394
+ this._selectionChanged.next(this.selectionModel);
395
+ }
396
+ }
397
+ unselectMany(items) {
398
+ if (!this.selectionModel || this.selectionModel.isEmpty()) {
399
+ return;
400
+ }
401
+ let changed = false;
402
+ for (const item of items) {
403
+ if (this.selectionModel.containsItem(item)) {
404
+ this.selectionModel.unselect(item);
405
+ changed = true;
406
+ }
407
+ }
408
+ if (changed) {
409
+ this._selectionChanged.next(this.selectionModel);
410
+ }
411
+ }
374
412
  unselect(item) {
375
413
  if (item === null || item === undefined || !this.selectionModel.containsItem(item)) {
376
414
  return;
@@ -378,6 +416,15 @@ class OptionSelectionService {
378
416
  this.selectionModel.unselect(item);
379
417
  this._selectionChanged.next(this.selectionModel);
380
418
  }
419
+ /**
420
+ * Checks whether all given items are currently selected, using identityFn for comparison.
421
+ */
422
+ containsAll(items) {
423
+ if (!items.length || this.selectionModel.isEmpty()) {
424
+ return false;
425
+ }
426
+ return items.every(item => this.selectionModel.containsItem(item));
427
+ }
381
428
  setSelectionValue(value) {
382
429
  if (!this.selectionModel) {
383
430
  return;
@@ -389,14 +436,6 @@ class OptionSelectionService {
389
436
  this.selectionModel.model = value;
390
437
  this._selectionChanged.next(this.selectionModel);
391
438
  }
392
- parseStringToModel(value) {
393
- if (this.displayField) {
394
- return {
395
- [this.displayField]: value,
396
- };
397
- }
398
- return value;
399
- }
400
439
  valuesEqualByIdentity(current, value) {
401
440
  if (current === value) {
402
441
  return true;
@@ -510,6 +549,9 @@ class ComboboxFocusHandler {
510
549
  addOptionValues(options) {
511
550
  this.optionData = options;
512
551
  }
552
+ focusOption(option) {
553
+ this.pseudoFocus.select(option);
554
+ }
513
555
  handleFocusSubscription() {
514
556
  this.popoverService.openChange.subscribe(open => {
515
557
  if (!open) {
@@ -560,7 +602,12 @@ class ComboboxFocusHandler {
560
602
  case Keys.Enter:
561
603
  if (this.popoverService.open && this.pseudoFocus.model) {
562
604
  if (this.selectionService.multiselectable) {
563
- this.selectionService.toggle(this.pseudoFocus.model.value);
605
+ if (this.pseudoFocus.model.id === SELECT_ALL_ID) {
606
+ this.selectionService.requestSelectAll();
607
+ }
608
+ else {
609
+ this.selectionService.toggle(this.pseudoFocus.model.value);
610
+ }
564
611
  }
565
612
  else {
566
613
  this.selectionService.select(this.pseudoFocus.model.value);
@@ -696,9 +743,10 @@ class ClrOption {
696
743
  // This way we achieve two things:
697
744
  // - do not lose focus
698
745
  // - we're still able to use onBlur for "outside-click" handling
746
+ this.focusHandler.focusOption(this.optionProxy);
699
747
  this.focusHandler.focusInput();
700
748
  }
701
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ClrOption, deps: [{ token: i0.ElementRef }, { token: i3$1.ClrCommonStringsService }, { token: ComboboxFocusHandler }, { token: OptionSelectionService }], target: i0.ɵɵFactoryTarget.Component }); }
749
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ClrOption, deps: [{ token: i0.ElementRef }, { token: i3.ClrCommonStringsService }, { token: ComboboxFocusHandler }, { token: OptionSelectionService }], target: i0.ɵɵFactoryTarget.Component }); }
702
750
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: ClrOption, isStandalone: false, selector: "clr-option", inputs: { optionId: ["id", "optionId"], value: ["clrValue", "value"] }, host: { listeners: { "click": "onClick($event)" }, properties: { "class.clr-combobox-option": "true", "attr.role": "\"option\"", "attr.tabindex": "-1", "attr.id": "optionId", "class.active": "this.selected", "class.clr-focus": "this.focusClass" } }, ngImport: i0, template: `
703
751
  <ng-content></ng-content>
704
752
  @if (selected) {
@@ -725,7 +773,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
725
773
  },
726
774
  standalone: false,
727
775
  }]
728
- }], ctorParameters: () => [{ type: i0.ElementRef }, { type: i3$1.ClrCommonStringsService }, { type: ComboboxFocusHandler }, { type: OptionSelectionService }], propDecorators: { optionId: [{
776
+ }], ctorParameters: () => [{ type: i0.ElementRef }, { type: i3.ClrCommonStringsService }, { type: ComboboxFocusHandler }, { type: OptionSelectionService }], propDecorators: { optionId: [{
729
777
  type: Input,
730
778
  args: ['id']
731
779
  }], value: [{
@@ -749,6 +797,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
749
797
  * The full license information can be found in LICENSE in the root directory of this project.
750
798
  */
751
799
  let nbOptionsComponents = 0;
800
+ const SELECT_ALL_ID = 'select-all-id';
752
801
  class ClrOptions {
753
802
  constructor(optionSelectionService, id, el, commonStrings, focusHandler, popoverService, parentHost, document) {
754
803
  this.optionSelectionService = optionSelectionService;
@@ -767,12 +816,22 @@ class ClrOptions {
767
816
  this.optionsId = 'clr-options-' + nbOptionsComponents++;
768
817
  }
769
818
  }
819
+ set selectAllBtn(value) {
820
+ if (value) {
821
+ this._selectAllOption = new OptionData(SELECT_ALL_ID, null);
822
+ this._selectAllOption.el = value.nativeElement;
823
+ }
824
+ else {
825
+ this._selectAllOption = null;
826
+ }
827
+ this.updateFocusableItems();
828
+ }
770
829
  get items() {
771
830
  return this._items;
772
831
  }
773
832
  set items(items) {
774
833
  this._items = items;
775
- this.focusHandler.addOptionValues(this._items.map(option => option.optionProxy));
834
+ this.updateFocusableItems();
776
835
  }
777
836
  /**
778
837
  * Tests if the list of options is empty, meaning it doesn't contain any items
@@ -786,6 +845,34 @@ class ClrOptions {
786
845
  get noResultsElementId() {
787
846
  return `${this.optionsId}-no-results`;
788
847
  }
848
+ get showSelectAll() {
849
+ return (this.optionSelectionService.showSelectAll &&
850
+ this.optionSelectionService.multiselectable &&
851
+ !this.optionSelectionService.loading &&
852
+ this.items.length > 0);
853
+ }
854
+ get allVisibleSelected() {
855
+ if (!this.items || this.items.length === 0) {
856
+ return false;
857
+ }
858
+ return this.optionSelectionService.containsAll(this.items.map(option => option.value));
859
+ }
860
+ get isSelectAllFocused() {
861
+ return this.focusHandler.pseudoFocus.model?.id === SELECT_ALL_ID;
862
+ }
863
+ toggleSelectAll(event = null) {
864
+ if (event) {
865
+ event.stopPropagation();
866
+ this.focusHandler.focusInput();
867
+ }
868
+ const visibleValues = this.items.map(option => option.value);
869
+ if (this.allVisibleSelected) {
870
+ this.optionSelectionService.unselectMany(visibleValues);
871
+ }
872
+ else {
873
+ this.optionSelectionService.selectMany(visibleValues);
874
+ }
875
+ }
789
876
  ngAfterViewInit() {
790
877
  this.focusHandler.listbox = this.el.nativeElement;
791
878
  this.subscriptions.push(this.items.changes.subscribe(items => {
@@ -797,6 +884,8 @@ class ClrOptions {
797
884
  else {
798
885
  this.focusHandler.pseudoFocus.pop();
799
886
  }
887
+ }), this.optionSelectionService.selectAllRequested.subscribe(() => {
888
+ this.toggleSelectAll();
800
889
  }));
801
890
  }
802
891
  ngOnDestroy() {
@@ -808,8 +897,19 @@ class ClrOptions {
808
897
  loadingStateChange(state) {
809
898
  this.loading = state === ClrLoadingState.LOADING;
810
899
  }
811
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ClrOptions, deps: [{ token: OptionSelectionService }, { token: IF_ACTIVE_ID }, { token: i0.ElementRef }, { token: i3$1.ClrCommonStringsService }, { token: ComboboxFocusHandler }, { token: i4.ClrPopoverService }, { token: POPOVER_HOST_ORIGIN, optional: true }, { token: DOCUMENT }], target: i0.ɵɵFactoryTarget.Component }); }
812
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: ClrOptions, isStandalone: false, selector: "clr-options", inputs: { optionsId: ["id", "optionsId"] }, host: { properties: { "class.clr-combobox-options": "true", "class.clr-combobox-options-hidden": "emptyOptions && editable", "attr.role": "\"listbox\"", "id": "optionsId" } }, providers: [{ provide: LoadingListener, useExisting: ClrOptions }], queries: [{ propertyName: "items", predicate: ClrOption, descendants: true }], ngImport: i0, template: `
900
+ updateFocusableItems() {
901
+ const focusList = [];
902
+ if (this._selectAllOption) {
903
+ focusList.push(this._selectAllOption);
904
+ }
905
+ if (this._items) {
906
+ const itemOptions = this._items.map(option => option.optionProxy);
907
+ focusList.push(...itemOptions);
908
+ }
909
+ this.focusHandler.addOptionValues(focusList);
910
+ }
911
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ClrOptions, deps: [{ token: OptionSelectionService }, { token: IF_ACTIVE_ID }, { token: i0.ElementRef }, { token: i3.ClrCommonStringsService }, { token: ComboboxFocusHandler }, { token: i4.ClrPopoverService }, { token: POPOVER_HOST_ORIGIN, optional: true }, { token: DOCUMENT }], target: i0.ɵɵFactoryTarget.Component }); }
912
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: ClrOptions, isStandalone: false, selector: "clr-options", inputs: { optionsId: ["id", "optionsId"] }, host: { properties: { "class.clr-combobox-options": "true", "class.clr-combobox-options-multi": "optionSelectionService.multiselectable", "class.clr-combobox-options-hidden": "emptyOptions && editable", "attr.role": "\"listbox\"", "id": "optionsId" } }, providers: [{ provide: LoadingListener, useExisting: ClrOptions }], queries: [{ propertyName: "items", predicate: ClrOption, descendants: true }], viewQueries: [{ propertyName: "selectAllBtn", first: true, predicate: ["selectAllBtn"], descendants: true }], ngImport: i0, template: `
813
913
  @if (optionSelectionService.loading) {
814
914
  <div class="clr-combobox-options-loading">
815
915
  <clr-spinner clrInline>
@@ -821,6 +921,21 @@ class ClrOptions {
821
921
  </div>
822
922
  }
823
923
 
924
+ @if (showSelectAll) {
925
+ <div class="clr-combobox-select-all">
926
+ <button
927
+ #selectAllBtn
928
+ type="button"
929
+ tabindex="-1"
930
+ class="btn btn-link clr-combobox-select-all-btn clr-combobox-option"
931
+ [class.clr-focus]="isSelectAllFocused"
932
+ (click)="toggleSelectAll($event)"
933
+ >
934
+ {{ allVisibleSelected ? commonStrings.keys.comboboxUnselectAll : commonStrings.keys.comboboxSelectAll }}
935
+ </button>
936
+ </div>
937
+ }
938
+
824
939
  <!-- Rendered if data set is empty -->
825
940
  @if (emptyOptions) {
826
941
  <div [id]="noResultsElementId" role="option">
@@ -850,6 +965,21 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
850
965
  </div>
851
966
  }
852
967
 
968
+ @if (showSelectAll) {
969
+ <div class="clr-combobox-select-all">
970
+ <button
971
+ #selectAllBtn
972
+ type="button"
973
+ tabindex="-1"
974
+ class="btn btn-link clr-combobox-select-all-btn clr-combobox-option"
975
+ [class.clr-focus]="isSelectAllFocused"
976
+ (click)="toggleSelectAll($event)"
977
+ >
978
+ {{ allVisibleSelected ? commonStrings.keys.comboboxUnselectAll : commonStrings.keys.comboboxSelectAll }}
979
+ </button>
980
+ </div>
981
+ }
982
+
853
983
  <!-- Rendered if data set is empty -->
854
984
  @if (emptyOptions) {
855
985
  <div [id]="noResultsElementId" role="option">
@@ -865,6 +995,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
865
995
  providers: [{ provide: LoadingListener, useExisting: ClrOptions }],
866
996
  host: {
867
997
  '[class.clr-combobox-options]': 'true',
998
+ '[class.clr-combobox-options-multi]': 'optionSelectionService.multiselectable',
868
999
  '[class.clr-combobox-options-hidden]': 'emptyOptions && editable',
869
1000
  '[attr.role]': '"listbox"',
870
1001
  '[id]': 'optionsId',
@@ -874,7 +1005,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
874
1005
  }], ctorParameters: () => [{ type: OptionSelectionService }, { type: undefined, decorators: [{
875
1006
  type: Inject,
876
1007
  args: [IF_ACTIVE_ID]
877
- }] }, { type: i0.ElementRef }, { type: i3$1.ClrCommonStringsService }, { type: ComboboxFocusHandler }, { type: i4.ClrPopoverService }, { type: i0.ElementRef, decorators: [{
1008
+ }] }, { type: i0.ElementRef }, { type: i3.ClrCommonStringsService }, { type: ComboboxFocusHandler }, { type: i4.ClrPopoverService }, { type: i0.ElementRef, decorators: [{
878
1009
  type: Optional
879
1010
  }, {
880
1011
  type: Inject,
@@ -885,6 +1016,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
885
1016
  }] }], propDecorators: { optionsId: [{
886
1017
  type: Input,
887
1018
  args: ['id']
1019
+ }], selectAllBtn: [{
1020
+ type: ViewChild,
1021
+ args: ['selectAllBtn']
888
1022
  }], items: [{
889
1023
  type: ContentChildren,
890
1024
  args: [ClrOption, { descendants: true }]
@@ -897,7 +1031,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
897
1031
  * The full license information can be found in LICENSE in the root directory of this project.
898
1032
  */
899
1033
  class ClrCombobox extends WrappedFormControl {
900
- constructor(vcr, injector, control, renderer, el, optionSelectionService, commonStrings, popoverService, containerService, platformId, focusHandler, cdr) {
1034
+ constructor(vcr, injector, control, renderer, el, optionSelectionService, commonStrings, popoverService, containerService, platformId, focusHandler, cdr, zone, container) {
901
1035
  super(vcr, ClrComboboxContainer, injector, control, renderer, el);
902
1036
  this.control = control;
903
1037
  this.renderer = renderer;
@@ -909,6 +1043,8 @@ class ClrCombobox extends WrappedFormControl {
909
1043
  this.platformId = platformId;
910
1044
  this.focusHandler = focusHandler;
911
1045
  this.cdr = cdr;
1046
+ this.zone = zone;
1047
+ this.container = container;
912
1048
  this.placeholder = '';
913
1049
  this.clrInputChange = new EventEmitter(false);
914
1050
  this.clrOpenChange = this.popoverService.openChange;
@@ -920,6 +1056,11 @@ class ClrCombobox extends WrappedFormControl {
920
1056
  this.popoverPosition = ClrPopoverPosition.BOTTOM_LEFT;
921
1057
  this.index = 1;
922
1058
  this.popoverType = ClrPopoverType.DROPDOWN;
1059
+ this.containerWidth = null;
1060
+ this.selectionExpanded = false;
1061
+ this.shouldCalculate = true;
1062
+ this.isTotalSelection = false;
1063
+ this.containerWidthChange = new Subject();
923
1064
  this._searchText = '';
924
1065
  if (control) {
925
1066
  control.valueAccessor = this;
@@ -927,12 +1068,21 @@ class ClrCombobox extends WrappedFormControl {
927
1068
  // default to SingleSelectComboboxModel, in case the optional input [ClrMulti] isn't used
928
1069
  this.multiSelect = false;
929
1070
  }
1071
+ get showSelectAll() {
1072
+ return this.optionSelectionService.showSelectAll;
1073
+ }
1074
+ set showSelectAll(value) {
1075
+ this.optionSelectionService.showSelectAll = value;
1076
+ }
930
1077
  get editable() {
931
1078
  return this.optionSelectionService.editable;
932
1079
  }
933
1080
  set editable(value) {
934
1081
  this.optionSelectionService.editable = value;
935
1082
  }
1083
+ set editableResolver(value) {
1084
+ this.optionSelectionService.editableResolver = value;
1085
+ }
936
1086
  set identityFn(value) {
937
1087
  this.optionSelectionService.identityFn = value;
938
1088
  }
@@ -997,6 +1147,24 @@ class ClrCombobox extends WrappedFormControl {
997
1147
  get displayField() {
998
1148
  return this.optionSelectionService.displayField;
999
1149
  }
1150
+ get showAllText() {
1151
+ return this.commonStrings.parse(this.commonStrings.keys.comboboxShowAll, {
1152
+ ITEMS: this.multiSelectModel?.length.toString(),
1153
+ });
1154
+ }
1155
+ get allSelectedText() {
1156
+ return this.commonStrings.parse(this.commonStrings.keys.comboboxAllSelected, {
1157
+ ITEMS: this.multiSelectModel?.length.toString(),
1158
+ });
1159
+ }
1160
+ get showIndividualPills() {
1161
+ return !this.isTotalSelection || this.selectionExpanded;
1162
+ }
1163
+ get showTruncationToggle() {
1164
+ return (this.selectionExpanded ||
1165
+ this.isTotalSelection ||
1166
+ (this.calculatedLimit !== null && this.calculatedLimit < this.multiSelectModel.length));
1167
+ }
1000
1168
  get disabled() {
1001
1169
  return this.control?.disabled;
1002
1170
  }
@@ -1013,7 +1181,21 @@ class ClrCombobox extends WrappedFormControl {
1013
1181
  // The text input is the actual element we are wrapping
1014
1182
  // This assignment is needed by the wrapper, so it can set
1015
1183
  // the aria properties on the input element, not on the component.
1184
+ // We calculate on the initial load to prevent flickering
1016
1185
  this.el = this.textbox;
1186
+ if (this.multiSelect && this.multiSelectModel?.length > 0) {
1187
+ this.calculateLimit();
1188
+ }
1189
+ this.initialiseObserver();
1190
+ }
1191
+ ngOnDestroy() {
1192
+ super.ngOnDestroy();
1193
+ this.resizeObserver.disconnect();
1194
+ }
1195
+ clearSelection() {
1196
+ this.focusHandler.focusInput();
1197
+ // Clear the array model directly
1198
+ this.optionSelectionService.setSelectionValue([]);
1017
1199
  }
1018
1200
  onKeyUp(event) {
1019
1201
  // if BACKSPACE in multiselect mode, delete the last pill if text is empty
@@ -1031,7 +1213,7 @@ class ClrCombobox extends WrappedFormControl {
1031
1213
  break;
1032
1214
  case Keys.Enter:
1033
1215
  if (this.editable && this._searchText.length > 0 && this.options.emptyOptions) {
1034
- const parsedInput = this.optionSelectionService.parseStringToModel(this._searchText);
1216
+ const parsedInput = this.optionSelectionService.editableResolver(this._searchText);
1035
1217
  this.control?.control.markAsTouched();
1036
1218
  this.optionSelectionService.select(parsedInput);
1037
1219
  this.searchText = '';
@@ -1072,7 +1254,7 @@ class ClrCombobox extends WrappedFormControl {
1072
1254
  }
1073
1255
  onChange() {
1074
1256
  if (this.editable && !this.multiSelect && this.options.emptyOptions) {
1075
- const parsedInput = this.optionSelectionService.parseStringToModel(this._searchText);
1257
+ const parsedInput = this.optionSelectionService.editableResolver(this._searchText);
1076
1258
  this.optionSelectionService.setSelectionValue(parsedInput);
1077
1259
  }
1078
1260
  }
@@ -1113,13 +1295,103 @@ class ClrCombobox extends WrappedFormControl {
1113
1295
  this.popoverService.toggleWithEvent(event);
1114
1296
  }
1115
1297
  }
1298
+ toggleSelectionExpand() {
1299
+ this.selectionExpanded = !this.selectionExpanded;
1300
+ if (this.selectionExpanded) {
1301
+ this.applyLimit(this.multiSelectModel.length);
1302
+ }
1303
+ else {
1304
+ this.containerWidthChange.next(this.containerWidth);
1305
+ }
1306
+ }
1307
+ initialiseObserver() {
1308
+ const container = this.container ? this.container.el.nativeElement : this.el.nativeElement.parentElement;
1309
+ this.containerWidth = container.offsetWidth;
1310
+ this.resizeObserver = new ResizeObserver(entries => {
1311
+ this.zone.runOutsideAngular(() => {
1312
+ entries.forEach(entry => {
1313
+ const entryWidth = entry.contentRect.width;
1314
+ switch (entry.target) {
1315
+ case container:
1316
+ if (this.containerWidth !== entryWidth) {
1317
+ this.containerWidth = entryWidth;
1318
+ this.containerWidthChange.next(entryWidth);
1319
+ }
1320
+ break;
1321
+ case this.wrapper.nativeElement:
1322
+ this.containerWidthChange.next(null);
1323
+ break;
1324
+ }
1325
+ });
1326
+ });
1327
+ });
1328
+ this.resizeObserver.observe(container);
1329
+ this.resizeObserver.observe(this.wrapper.nativeElement);
1330
+ }
1331
+ calculateLimit() {
1332
+ this.shouldCalculate = true;
1333
+ this.cdr.detectChanges();
1334
+ if (!this.calculationPills || this.calculationPills.length === 0) {
1335
+ this.applyLimit();
1336
+ return;
1337
+ }
1338
+ const pillDimensions = this.calculationPills.map(p => ({
1339
+ top: p.nativeElement.offsetTop,
1340
+ width: p.nativeElement.offsetWidth,
1341
+ left: p.nativeElement.offsetLeft,
1342
+ }));
1343
+ const firstPill = pillDimensions[0];
1344
+ const buttonWidth = this.truncationButton?.nativeElement?.offsetWidth || 100;
1345
+ const textboxWidth = this.textbox.nativeElement.offsetWidth;
1346
+ const expectedWidth = this.containerWidth - textboxWidth - buttonWidth;
1347
+ let fitCount = 0;
1348
+ for (const pill of pillDimensions) {
1349
+ if (pill.top > firstPill.top || pill.left + pill.width > expectedWidth) {
1350
+ break;
1351
+ }
1352
+ fitCount++;
1353
+ }
1354
+ this.applyLimit(fitCount);
1355
+ }
1356
+ applyLimit(limit = undefined) {
1357
+ this.zone.run(() => {
1358
+ this.calculatedLimit = limit === 0 ? 1 : limit;
1359
+ this.shouldCalculate = false;
1360
+ this.cdr.markForCheck();
1361
+ });
1362
+ }
1363
+ updateTotalSelection() {
1364
+ if (!this.multiSelect || !this.multiSelectModel?.length) {
1365
+ this.isTotalSelection = false;
1366
+ return;
1367
+ }
1368
+ // Skip recalculation when items are filtered to zero (e.g. "no results")
1369
+ // to prevent pills from flashing while typing in the search input.
1370
+ if (!this.options?.items?.length) {
1371
+ return;
1372
+ }
1373
+ this.isTotalSelection = this.optionSelectionService.containsAll(this.options.items.map(option => option.value));
1374
+ }
1116
1375
  initializeSubscriptions() {
1117
1376
  this.subscriptions.push(this.optionSelectionService.selectionChanged.subscribe((newSelection) => {
1118
1377
  this.updateInputValue(newSelection);
1119
- if (!this.multiSelect && newSelection && !newSelection.isEmpty()) {
1120
- this.popoverService.open = false;
1378
+ if (newSelection.isEmpty()) {
1379
+ this.selectionExpanded = false;
1380
+ this.isTotalSelection = false;
1381
+ }
1382
+ else {
1383
+ if (!this.multiSelect) {
1384
+ this.popoverService.open = false;
1385
+ }
1386
+ this.updateTotalSelection();
1121
1387
  }
1122
1388
  this.updateControlValue();
1389
+ if (this.selectionExpanded) {
1390
+ this.applyLimit(this.multiSelectModel.length);
1391
+ }
1392
+ else {
1393
+ this.calculateLimit();
1394
+ }
1123
1395
  if (this.multiSelect) {
1124
1396
  setTimeout(() => {
1125
1397
  this.popoverService?.updatePosition();
@@ -1146,6 +1418,10 @@ class ClrCombobox extends WrappedFormControl {
1146
1418
  else {
1147
1419
  this.searchText = this.getDisplayNames(this.optionSelectionService.selectionModel.model)[0] || '';
1148
1420
  }
1421
+ }), this.containerWidthChange.pipe(debounceTime(0)).subscribe(() => {
1422
+ if (!this.selectionExpanded && !this.isTotalSelection) {
1423
+ this.calculateLimit();
1424
+ }
1149
1425
  }));
1150
1426
  }
1151
1427
  updateInputValue(model) {
@@ -1170,14 +1446,14 @@ class ClrCombobox extends WrappedFormControl {
1170
1446
  }
1171
1447
  return [this.optionSelectionService.selectionModel.model];
1172
1448
  }
1173
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ClrCombobox, deps: [{ token: i0.ViewContainerRef }, { token: i0.Injector }, { token: i1$1.NgControl, optional: true, self: true }, { token: i0.Renderer2 }, { token: i0.ElementRef }, { token: OptionSelectionService }, { token: i3$1.ClrCommonStringsService }, { token: i4.ClrPopoverService }, { token: ComboboxContainerService, optional: true }, { token: PLATFORM_ID }, { token: ComboboxFocusHandler }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); }
1174
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: ClrCombobox, isStandalone: false, selector: "clr-combobox", inputs: { placeholder: "placeholder", editable: ["clrEditable", "editable"], identityFn: ["clrComboboxIdentityFn", "identityFn"], multiSelect: ["clrMulti", "multiSelect"] }, outputs: { clrInputChange: "clrInputChange", clrOpenChange: "clrOpenChange", clrSelectionChange: "clrSelectionChange" }, host: { listeners: { "keydown": "onKeyUp($event)" }, properties: { "class.aria-required": "true", "class.clr-combobox": "true", "class.clr-combobox-disabled": "control?.disabled" } }, providers: [
1449
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ClrCombobox, deps: [{ token: i0.ViewContainerRef }, { token: i0.Injector }, { token: i1$1.NgControl, optional: true, self: true }, { token: i0.Renderer2 }, { token: i0.ElementRef }, { token: OptionSelectionService }, { token: i3.ClrCommonStringsService }, { token: i4.ClrPopoverService }, { token: ComboboxContainerService, optional: true }, { token: PLATFORM_ID }, { token: ComboboxFocusHandler }, { token: i0.ChangeDetectorRef }, { token: i0.NgZone }, { token: ClrComboboxContainer, host: true, optional: true }], target: i0.ɵɵFactoryTarget.Component }); }
1450
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: ClrCombobox, isStandalone: false, selector: "clr-combobox", inputs: { placeholder: "placeholder", showSelectAll: ["showSelectAll", "showSelectAll", booleanAttribute], editable: ["clrEditable", "editable"], editableResolver: ["clrEditableResolverFn", "editableResolver"], identityFn: ["clrComboboxIdentityFn", "identityFn"], multiSelect: ["clrMulti", "multiSelect"] }, outputs: { clrInputChange: "clrInputChange", clrOpenChange: "clrOpenChange", clrSelectionChange: "clrSelectionChange" }, host: { listeners: { "keydown": "onKeyUp($event)" }, properties: { "class.aria-required": "true", "class.clr-combobox": "true", "class.clr-combobox-disabled": "control?.disabled" } }, providers: [
1175
1451
  OptionSelectionService,
1176
1452
  { provide: LoadingListener, useExisting: ClrCombobox },
1177
1453
  IF_ACTIVE_ID_PROVIDER,
1178
1454
  FOCUS_SERVICE_PROVIDER,
1179
1455
  COMBOBOX_FOCUS_HANDLER_PROVIDER,
1180
- ], queries: [{ propertyName: "optionSelected", first: true, predicate: ClrOptionSelected, descendants: true }, { propertyName: "options", first: true, predicate: ClrOptions, descendants: true }], viewQueries: [{ propertyName: "textbox", first: true, predicate: ["textboxInput"], descendants: true }, { propertyName: "trigger", first: true, predicate: ["trigger"], descendants: true }], usesInheritance: true, hostDirectives: [{ directive: i4.ClrPopoverHostDirective }], ngImport: i0, template: "<!--\n ~ Copyright (c) 2016-2026 Broadcom. All Rights Reserved.\n ~ The term \"Broadcom\" refers to Broadcom Inc. and/or its subsidiaries.\n ~ This software is released under MIT license.\n ~ The full license information can be found in LICENSE in the root directory of this project.\n -->\n\n<!-- The (click) handler is needed to auto-focus on input field which can not currently occupy the whole\nwidth of the component, after being wrapped to a new line -->\n<div\n class=\"clr-combobox-wrapper\"\n clrPopoverOrigin\n (click)=\"onWrapperClick($event)\"\n [class.multi]=\"multiSelect\"\n [class.invalid]=\"(control?.control.touched && control?.invalid)\"\n [class.disabled]=\"control?.disabled\"\n>\n @if (multiSelect && optionSelectionService.selectionModel.model && multiSelectModel.length > 0) {\n <span\n role=\"grid\"\n clrRovingTabindex\n [clrRovingTabindexDisabled]=\"control?.disabled\"\n clrDirection=\"both\"\n [attr.aria-label]=\"getSelectionAriaLabel()\"\n [attr.aria-disabled]=\"control?.disabled? true: null\"\n class=\"clr-combobox-pills\"\n >\n @for (item of multiSelectModel; track item; let i = $index) {\n <span class=\"label label-combobox-pill\" role=\"row\">\n <span role=\"gridcell\">\n <span class=\"clr-combobox-pill-content\" clrKeyFocusItem>\n @if (optionSelected) {\n <ng-container\n [ngTemplateOutlet]=\"optionSelected.template\"\n [ngTemplateOutletContext]=\"{$implicit: optionSelectionService.selectionModel.model[i]}\"\n ></ng-container>\n }\n </span>\n </span>\n <span role=\"gridcell\">\n <button\n clrKeyFocusItem\n type=\"button\"\n class=\"clr-combobox-remove-btn\"\n [disabled]=\"control?.disabled? true: null\"\n [attr.aria-label]=\"commonStrings.keys.comboboxDelete + ' ' + optionSelectionService.selectionModel.toString(displayField, i)\"\n (click)=\"unselect(item)\"\n >\n <cds-icon shape=\"window-close\" size=\"12\"></cds-icon>\n </button>\n </span>\n </span>\n }\n </span>\n }\n\n <input\n #textboxInput\n type=\"text\"\n role=\"combobox\"\n [id]=\"inputId()\"\n class=\"clr-input clr-combobox-input\"\n [(ngModel)]=\"searchText\"\n (blur)=\"onBlur($event)\"\n (focus)=\"onFocus()\"\n (change)=\"onChange()\"\n [attr.aria-expanded]=\"openState\"\n [attr.aria-owns]=\"ariaOwns\"\n aria-haspopup=\"listbox\"\n aria-autocomplete=\"list\"\n autocomplete=\"off\"\n [attr.aria-invalid]=\"control?.invalid? true: null\"\n [disabled]=\"control?.disabled? true: null\"\n [attr.aria-activedescendant]=\"getActiveDescendant()\"\n [attr.placeholder]=\"placeholder\"\n />\n\n <!-- No click handler, as it uses the handler on the .clr-combobox-wrapper -->\n <button\n #trigger\n type=\"button\"\n class=\"clr-combobox-trigger\"\n tabindex=\"-1\"\n [disabled]=\"control?.disabled || null\"\n [attr.aria-label]=\"commonStrings.keys.comboboxOpen\"\n >\n <cds-icon shape=\"angle\" direction=\"down\"></cds-icon>\n </button>\n\n <div class=\"clr-focus-indicator\" [class.clr-focus]=\"focused\"></div>\n</div>\n\n<!-- Both close handlers are handled manually.\n'outsideClickToClose' has complex handling that's necessary\nto be manual due to the component architecture -->\n<div role=\"dialog\" *clrPopoverContent=\"openState; at popoverPosition; type: popoverType;\">\n <ng-content></ng-content>\n</div>\n", dependencies: [{ kind: "directive", type: i3.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i8.ClrIcon, selector: "clr-icon, cds-icon", inputs: ["shape", "size", "direction", "flip", "solid", "status", "inverse", "badge"] }, { kind: "component", type: i3$1.ClrRovingTabindex, selector: "[clrRovingTabindex]", inputs: ["clrRovingTabindex", "clrRovingTabindexDisabled"] }, { kind: "directive", type: i3$1.ClrKeyFocusItem, selector: "[clrKeyFocusItem]" }, { kind: "directive", type: i4.ClrPopoverOrigin, selector: "[clrPopoverOrigin]" }, { kind: "directive", type: i4.ClrPopoverContent, selector: "[clrPopoverContent]", inputs: ["clrPopoverContent", "clrPopoverContentAt", "clrPopoverContentAvailablePositions", "clrPopoverContentType", "clrPopoverContentOutsideClickToClose", "clrPopoverContentScrollToClose", "clrPopoverContentOrigin"] }] }); }
1456
+ ], queries: [{ propertyName: "optionSelected", first: true, predicate: ClrOptionSelected, descendants: true }, { propertyName: "options", first: true, predicate: ClrOptions, descendants: true }], viewQueries: [{ propertyName: "textbox", first: true, predicate: ["textboxInput"], descendants: true }, { propertyName: "trigger", first: true, predicate: ["trigger"], descendants: true }, { propertyName: "truncationButton", first: true, predicate: ["truncationButton"], descendants: true }, { propertyName: "wrapper", first: true, predicate: ["wrapper"], descendants: true, static: true }, { propertyName: "calculationPills", predicate: ["pill"], descendants: true }], usesInheritance: true, hostDirectives: [{ directive: i4.ClrPopoverHostDirective }], ngImport: i0, template: "<!--\n ~ Copyright (c) 2016-2026 Broadcom. All Rights Reserved.\n ~ The term \"Broadcom\" refers to Broadcom Inc. and/or its subsidiaries.\n ~ This software is released under MIT license.\n ~ The full license information can be found in LICENSE in the root directory of this project.\n -->\n\n<!-- The (click) handler is needed to auto-focus on input field which can not currently occupy the whole\nwidth of the component, after being wrapped to a new line -->\n<div\n #wrapper\n class=\"clr-combobox-wrapper\"\n clrPopoverOrigin\n (click)=\"onWrapperClick($event)\"\n [class.multi]=\"multiSelect\"\n [class.invalid]=\"(control?.control.touched && control?.invalid)\"\n [class.disabled]=\"control?.disabled\"\n>\n @if (multiSelect && multiSelectModel && multiSelectModel.length > 0) {\n <span\n role=\"grid\"\n clrRovingTabindex\n [clrRovingTabindexDisabled]=\"control?.disabled\"\n clrDirection=\"both\"\n [attr.aria-label]=\"getSelectionAriaLabel()\"\n [attr.aria-disabled]=\"control?.disabled? true: null\"\n class=\"clr-combobox-pills\"\n >\n <ng-template #pillTemplate let-item let-i=\"index\">\n <span class=\"label label-combobox-pill\" role=\"row\">\n <span role=\"gridcell\">\n <span class=\"clr-combobox-pill-content\" clrKeyFocusItem>\n @if (optionSelected) {\n <ng-container\n [ngTemplateOutlet]=\"optionSelected.template\"\n [ngTemplateOutletContext]=\"{$implicit: item}\"\n ></ng-container>\n }\n </span>\n </span>\n <span role=\"gridcell\">\n <button\n clrKeyFocusItem\n type=\"button\"\n class=\"clr-combobox-remove-btn\"\n [disabled]=\"control?.disabled ? true : null\"\n [attr.aria-label]=\"commonStrings.keys.comboboxDelete + ' ' + optionSelectionService.selectionModel.toString(displayField, i)\"\n (click)=\"unselect(item)\"\n >\n <cds-icon shape=\"window-close\" size=\"12\"></cds-icon>\n </button>\n </span>\n </span>\n </ng-template>\n\n @if (showSelectAll) { @if (isTotalSelection && !selectionExpanded) {\n <span class=\"label label-combobox-pill\" role=\"row\">\n <span role=\"gridcell\">\n <span class=\"clr-combobox-pill-content\" clrKeyFocusItem>{{ allSelectedText }}</span>\n </span>\n <span role=\"gridcell\">\n <button\n clrKeyFocusItem\n type=\"button\"\n class=\"clr-combobox-remove-btn\"\n (click)=\"clearSelection()\"\n [attr.aria-label]=\"commonStrings.keys.comboboxUnselectAll\"\n >\n <cds-icon shape=\"window-close\" size=\"12\"></cds-icon>\n </button>\n </span>\n </span>\n } @if (showIndividualPills) { @for (item of multiSelectModel | slice:0:calculatedLimit; track item; let i = $index)\n {\n <ng-container\n [ngTemplateOutlet]=\"pillTemplate\"\n [ngTemplateOutletContext]=\"{$implicit: item, index: i}\"\n ></ng-container>\n } } @if (showTruncationToggle) {\n <span role=\"row\" class=\"label label-combobox-pill clr-combobox-truncation-row\">\n <span role=\"gridcell\" class=\"clr-combobox-truncation-cell\">\n <span class=\"clr-combobox-pill-content\">\n <button\n #truncationButton\n clrKeyFocusItem\n type=\"button\"\n class=\"btn btn-link clr-combobox-show-more-btn\"\n (click)=\"toggleSelectionExpand()\"\n [attr.aria-expanded]=\"selectionExpanded\"\n [attr.aria-controls]=\"'expanded-selection-' + id\"\n >\n @if (!selectionExpanded) { {{ showAllText }} } @else { {{ commonStrings.keys.comboboxShowLess }} }\n </button>\n </span>\n </span>\n </span>\n } } @else { @for (item of multiSelectModel; track item; let i = $index) {\n <ng-container\n [ngTemplateOutlet]=\"pillTemplate\"\n [ngTemplateOutletContext]=\"{$implicit: item, index: i}\"\n ></ng-container>\n } }\n </span>\n\n @if (showSelectAll && shouldCalculate) {\n <span class=\"clr-combobox-pills-calculation\" aria-hidden=\"true\">\n @for (item of multiSelectModel; track item) {\n <span class=\"label label-combobox-pill\" #pill>\n <span class=\"clr-combobox-pill-content\">\n @if (optionSelected) {\n <ng-container\n [ngTemplateOutlet]=\"optionSelected.template\"\n [ngTemplateOutletContext]=\"{$implicit: item}\"\n ></ng-container>\n }\n </span>\n <button type=\"button\" class=\"clr-combobox-remove-btn\" tabindex=\"-1\">\n <cds-icon shape=\"window-close\" size=\"12\"></cds-icon>\n </button>\n </span>\n }\n </span>\n } }\n\n <input\n #textboxInput\n type=\"text\"\n role=\"combobox\"\n [id]=\"inputId()\"\n class=\"clr-input clr-combobox-input\"\n [(ngModel)]=\"searchText\"\n (blur)=\"onBlur($event)\"\n (focus)=\"onFocus()\"\n (change)=\"onChange()\"\n [attr.aria-expanded]=\"openState\"\n [attr.aria-owns]=\"ariaOwns\"\n aria-haspopup=\"listbox\"\n aria-autocomplete=\"list\"\n autocomplete=\"off\"\n [attr.aria-invalid]=\"control?.invalid? true: null\"\n [disabled]=\"control?.disabled? true: null\"\n [attr.aria-activedescendant]=\"getActiveDescendant()\"\n [attr.placeholder]=\"placeholder\"\n />\n\n <!-- No click handler, as it uses the handler on the .clr-combobox-wrapper -->\n <button\n #trigger\n type=\"button\"\n class=\"clr-combobox-trigger\"\n tabindex=\"-1\"\n [disabled]=\"control?.disabled || null\"\n [attr.aria-label]=\"commonStrings.keys.comboboxOpen\"\n >\n <cds-icon shape=\"angle\" direction=\"down\"></cds-icon>\n </button>\n\n <div class=\"clr-focus-indicator\" [class.clr-focus]=\"focused\"></div>\n</div>\n\n<!-- Both close handlers are handled manually.\n'outsideClickToClose' has complex handling that's necessary\nto be manual due to the component architecture -->\n<div role=\"dialog\" *clrPopoverContent=\"openState; at popoverPosition; type: popoverType;\">\n <ng-content></ng-content>\n</div>\n", dependencies: [{ kind: "directive", type: i8.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i9.ClrIcon, selector: "clr-icon, cds-icon", inputs: ["shape", "size", "direction", "flip", "solid", "status", "inverse", "badge"] }, { kind: "component", type: i3.ClrRovingTabindex, selector: "[clrRovingTabindex]", inputs: ["clrRovingTabindex", "clrRovingTabindexDisabled"] }, { kind: "directive", type: i3.ClrKeyFocusItem, selector: "[clrKeyFocusItem]" }, { kind: "directive", type: i4.ClrPopoverOrigin, selector: "[clrPopoverOrigin]" }, { kind: "directive", type: i4.ClrPopoverContent, selector: "[clrPopoverContent]", inputs: ["clrPopoverContent", "clrPopoverContentAt", "clrPopoverContentAvailablePositions", "clrPopoverContentType", "clrPopoverContentOutsideClickToClose", "clrPopoverContentScrollToClose", "clrPopoverContentOrigin"] }, { kind: "pipe", type: i8.SlicePipe, name: "slice" }] }); }
1181
1457
  }
1182
1458
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ClrCombobox, decorators: [{
1183
1459
  type: Component,
@@ -1191,17 +1467,21 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
1191
1467
  '[class.aria-required]': 'true',
1192
1468
  '[class.clr-combobox]': 'true',
1193
1469
  '[class.clr-combobox-disabled]': 'control?.disabled',
1194
- }, standalone: false, template: "<!--\n ~ Copyright (c) 2016-2026 Broadcom. All Rights Reserved.\n ~ The term \"Broadcom\" refers to Broadcom Inc. and/or its subsidiaries.\n ~ This software is released under MIT license.\n ~ The full license information can be found in LICENSE in the root directory of this project.\n -->\n\n<!-- The (click) handler is needed to auto-focus on input field which can not currently occupy the whole\nwidth of the component, after being wrapped to a new line -->\n<div\n class=\"clr-combobox-wrapper\"\n clrPopoverOrigin\n (click)=\"onWrapperClick($event)\"\n [class.multi]=\"multiSelect\"\n [class.invalid]=\"(control?.control.touched && control?.invalid)\"\n [class.disabled]=\"control?.disabled\"\n>\n @if (multiSelect && optionSelectionService.selectionModel.model && multiSelectModel.length > 0) {\n <span\n role=\"grid\"\n clrRovingTabindex\n [clrRovingTabindexDisabled]=\"control?.disabled\"\n clrDirection=\"both\"\n [attr.aria-label]=\"getSelectionAriaLabel()\"\n [attr.aria-disabled]=\"control?.disabled? true: null\"\n class=\"clr-combobox-pills\"\n >\n @for (item of multiSelectModel; track item; let i = $index) {\n <span class=\"label label-combobox-pill\" role=\"row\">\n <span role=\"gridcell\">\n <span class=\"clr-combobox-pill-content\" clrKeyFocusItem>\n @if (optionSelected) {\n <ng-container\n [ngTemplateOutlet]=\"optionSelected.template\"\n [ngTemplateOutletContext]=\"{$implicit: optionSelectionService.selectionModel.model[i]}\"\n ></ng-container>\n }\n </span>\n </span>\n <span role=\"gridcell\">\n <button\n clrKeyFocusItem\n type=\"button\"\n class=\"clr-combobox-remove-btn\"\n [disabled]=\"control?.disabled? true: null\"\n [attr.aria-label]=\"commonStrings.keys.comboboxDelete + ' ' + optionSelectionService.selectionModel.toString(displayField, i)\"\n (click)=\"unselect(item)\"\n >\n <cds-icon shape=\"window-close\" size=\"12\"></cds-icon>\n </button>\n </span>\n </span>\n }\n </span>\n }\n\n <input\n #textboxInput\n type=\"text\"\n role=\"combobox\"\n [id]=\"inputId()\"\n class=\"clr-input clr-combobox-input\"\n [(ngModel)]=\"searchText\"\n (blur)=\"onBlur($event)\"\n (focus)=\"onFocus()\"\n (change)=\"onChange()\"\n [attr.aria-expanded]=\"openState\"\n [attr.aria-owns]=\"ariaOwns\"\n aria-haspopup=\"listbox\"\n aria-autocomplete=\"list\"\n autocomplete=\"off\"\n [attr.aria-invalid]=\"control?.invalid? true: null\"\n [disabled]=\"control?.disabled? true: null\"\n [attr.aria-activedescendant]=\"getActiveDescendant()\"\n [attr.placeholder]=\"placeholder\"\n />\n\n <!-- No click handler, as it uses the handler on the .clr-combobox-wrapper -->\n <button\n #trigger\n type=\"button\"\n class=\"clr-combobox-trigger\"\n tabindex=\"-1\"\n [disabled]=\"control?.disabled || null\"\n [attr.aria-label]=\"commonStrings.keys.comboboxOpen\"\n >\n <cds-icon shape=\"angle\" direction=\"down\"></cds-icon>\n </button>\n\n <div class=\"clr-focus-indicator\" [class.clr-focus]=\"focused\"></div>\n</div>\n\n<!-- Both close handlers are handled manually.\n'outsideClickToClose' has complex handling that's necessary\nto be manual due to the component architecture -->\n<div role=\"dialog\" *clrPopoverContent=\"openState; at popoverPosition; type: popoverType;\">\n <ng-content></ng-content>\n</div>\n" }]
1470
+ }, standalone: false, template: "<!--\n ~ Copyright (c) 2016-2026 Broadcom. All Rights Reserved.\n ~ The term \"Broadcom\" refers to Broadcom Inc. and/or its subsidiaries.\n ~ This software is released under MIT license.\n ~ The full license information can be found in LICENSE in the root directory of this project.\n -->\n\n<!-- The (click) handler is needed to auto-focus on input field which can not currently occupy the whole\nwidth of the component, after being wrapped to a new line -->\n<div\n #wrapper\n class=\"clr-combobox-wrapper\"\n clrPopoverOrigin\n (click)=\"onWrapperClick($event)\"\n [class.multi]=\"multiSelect\"\n [class.invalid]=\"(control?.control.touched && control?.invalid)\"\n [class.disabled]=\"control?.disabled\"\n>\n @if (multiSelect && multiSelectModel && multiSelectModel.length > 0) {\n <span\n role=\"grid\"\n clrRovingTabindex\n [clrRovingTabindexDisabled]=\"control?.disabled\"\n clrDirection=\"both\"\n [attr.aria-label]=\"getSelectionAriaLabel()\"\n [attr.aria-disabled]=\"control?.disabled? true: null\"\n class=\"clr-combobox-pills\"\n >\n <ng-template #pillTemplate let-item let-i=\"index\">\n <span class=\"label label-combobox-pill\" role=\"row\">\n <span role=\"gridcell\">\n <span class=\"clr-combobox-pill-content\" clrKeyFocusItem>\n @if (optionSelected) {\n <ng-container\n [ngTemplateOutlet]=\"optionSelected.template\"\n [ngTemplateOutletContext]=\"{$implicit: item}\"\n ></ng-container>\n }\n </span>\n </span>\n <span role=\"gridcell\">\n <button\n clrKeyFocusItem\n type=\"button\"\n class=\"clr-combobox-remove-btn\"\n [disabled]=\"control?.disabled ? true : null\"\n [attr.aria-label]=\"commonStrings.keys.comboboxDelete + ' ' + optionSelectionService.selectionModel.toString(displayField, i)\"\n (click)=\"unselect(item)\"\n >\n <cds-icon shape=\"window-close\" size=\"12\"></cds-icon>\n </button>\n </span>\n </span>\n </ng-template>\n\n @if (showSelectAll) { @if (isTotalSelection && !selectionExpanded) {\n <span class=\"label label-combobox-pill\" role=\"row\">\n <span role=\"gridcell\">\n <span class=\"clr-combobox-pill-content\" clrKeyFocusItem>{{ allSelectedText }}</span>\n </span>\n <span role=\"gridcell\">\n <button\n clrKeyFocusItem\n type=\"button\"\n class=\"clr-combobox-remove-btn\"\n (click)=\"clearSelection()\"\n [attr.aria-label]=\"commonStrings.keys.comboboxUnselectAll\"\n >\n <cds-icon shape=\"window-close\" size=\"12\"></cds-icon>\n </button>\n </span>\n </span>\n } @if (showIndividualPills) { @for (item of multiSelectModel | slice:0:calculatedLimit; track item; let i = $index)\n {\n <ng-container\n [ngTemplateOutlet]=\"pillTemplate\"\n [ngTemplateOutletContext]=\"{$implicit: item, index: i}\"\n ></ng-container>\n } } @if (showTruncationToggle) {\n <span role=\"row\" class=\"label label-combobox-pill clr-combobox-truncation-row\">\n <span role=\"gridcell\" class=\"clr-combobox-truncation-cell\">\n <span class=\"clr-combobox-pill-content\">\n <button\n #truncationButton\n clrKeyFocusItem\n type=\"button\"\n class=\"btn btn-link clr-combobox-show-more-btn\"\n (click)=\"toggleSelectionExpand()\"\n [attr.aria-expanded]=\"selectionExpanded\"\n [attr.aria-controls]=\"'expanded-selection-' + id\"\n >\n @if (!selectionExpanded) { {{ showAllText }} } @else { {{ commonStrings.keys.comboboxShowLess }} }\n </button>\n </span>\n </span>\n </span>\n } } @else { @for (item of multiSelectModel; track item; let i = $index) {\n <ng-container\n [ngTemplateOutlet]=\"pillTemplate\"\n [ngTemplateOutletContext]=\"{$implicit: item, index: i}\"\n ></ng-container>\n } }\n </span>\n\n @if (showSelectAll && shouldCalculate) {\n <span class=\"clr-combobox-pills-calculation\" aria-hidden=\"true\">\n @for (item of multiSelectModel; track item) {\n <span class=\"label label-combobox-pill\" #pill>\n <span class=\"clr-combobox-pill-content\">\n @if (optionSelected) {\n <ng-container\n [ngTemplateOutlet]=\"optionSelected.template\"\n [ngTemplateOutletContext]=\"{$implicit: item}\"\n ></ng-container>\n }\n </span>\n <button type=\"button\" class=\"clr-combobox-remove-btn\" tabindex=\"-1\">\n <cds-icon shape=\"window-close\" size=\"12\"></cds-icon>\n </button>\n </span>\n }\n </span>\n } }\n\n <input\n #textboxInput\n type=\"text\"\n role=\"combobox\"\n [id]=\"inputId()\"\n class=\"clr-input clr-combobox-input\"\n [(ngModel)]=\"searchText\"\n (blur)=\"onBlur($event)\"\n (focus)=\"onFocus()\"\n (change)=\"onChange()\"\n [attr.aria-expanded]=\"openState\"\n [attr.aria-owns]=\"ariaOwns\"\n aria-haspopup=\"listbox\"\n aria-autocomplete=\"list\"\n autocomplete=\"off\"\n [attr.aria-invalid]=\"control?.invalid? true: null\"\n [disabled]=\"control?.disabled? true: null\"\n [attr.aria-activedescendant]=\"getActiveDescendant()\"\n [attr.placeholder]=\"placeholder\"\n />\n\n <!-- No click handler, as it uses the handler on the .clr-combobox-wrapper -->\n <button\n #trigger\n type=\"button\"\n class=\"clr-combobox-trigger\"\n tabindex=\"-1\"\n [disabled]=\"control?.disabled || null\"\n [attr.aria-label]=\"commonStrings.keys.comboboxOpen\"\n >\n <cds-icon shape=\"angle\" direction=\"down\"></cds-icon>\n </button>\n\n <div class=\"clr-focus-indicator\" [class.clr-focus]=\"focused\"></div>\n</div>\n\n<!-- Both close handlers are handled manually.\n'outsideClickToClose' has complex handling that's necessary\nto be manual due to the component architecture -->\n<div role=\"dialog\" *clrPopoverContent=\"openState; at popoverPosition; type: popoverType;\">\n <ng-content></ng-content>\n</div>\n" }]
1195
1471
  }], ctorParameters: () => [{ type: i0.ViewContainerRef }, { type: i0.Injector }, { type: i1$1.NgControl, decorators: [{
1196
1472
  type: Self
1197
1473
  }, {
1198
1474
  type: Optional
1199
- }] }, { type: i0.Renderer2 }, { type: i0.ElementRef }, { type: OptionSelectionService }, { type: i3$1.ClrCommonStringsService }, { type: i4.ClrPopoverService }, { type: ComboboxContainerService, decorators: [{
1475
+ }] }, { type: i0.Renderer2 }, { type: i0.ElementRef }, { type: OptionSelectionService }, { type: i3.ClrCommonStringsService }, { type: i4.ClrPopoverService }, { type: ComboboxContainerService, decorators: [{
1200
1476
  type: Optional
1201
1477
  }] }, { type: undefined, decorators: [{
1202
1478
  type: Inject,
1203
1479
  args: [PLATFORM_ID]
1204
- }] }, { type: ComboboxFocusHandler }, { type: i0.ChangeDetectorRef }], propDecorators: { placeholder: [{
1480
+ }] }, { type: ComboboxFocusHandler }, { type: i0.ChangeDetectorRef }, { type: i0.NgZone }, { type: ClrComboboxContainer, decorators: [{
1481
+ type: Optional
1482
+ }, {
1483
+ type: Host
1484
+ }] }], propDecorators: { placeholder: [{
1205
1485
  type: Input,
1206
1486
  args: ['placeholder']
1207
1487
  }], clrInputChange: [{
@@ -1222,12 +1502,27 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
1222
1502
  }], optionSelected: [{
1223
1503
  type: ContentChild,
1224
1504
  args: [ClrOptionSelected]
1505
+ }], truncationButton: [{
1506
+ type: ViewChild,
1507
+ args: ['truncationButton']
1508
+ }], wrapper: [{
1509
+ type: ViewChild,
1510
+ args: ['wrapper', { static: true }]
1511
+ }], calculationPills: [{
1512
+ type: ViewChildren,
1513
+ args: ['pill']
1225
1514
  }], options: [{
1226
1515
  type: ContentChild,
1227
1516
  args: [ClrOptions]
1517
+ }], showSelectAll: [{
1518
+ type: Input,
1519
+ args: [{ alias: 'showSelectAll', transform: booleanAttribute }]
1228
1520
  }], editable: [{
1229
1521
  type: Input,
1230
1522
  args: ['clrEditable']
1523
+ }], editableResolver: [{
1524
+ type: Input,
1525
+ args: ['clrEditableResolverFn']
1231
1526
  }], identityFn: [{
1232
1527
  type: Input,
1233
1528
  args: ['clrComboboxIdentityFn']
@@ -1256,9 +1551,7 @@ class ClrOptionItems {
1256
1551
  this.iterableProxy = new NgForOf(vcr, template, differs);
1257
1552
  this.subscriptions.push(optionService.inputChanged.subscribe(filter => {
1258
1553
  this.filter = filter;
1259
- if (optionService.filtering) {
1260
- this.updateItems();
1261
- }
1554
+ this.updateItems();
1262
1555
  }));
1263
1556
  }
1264
1557
  set rawItems(items) {
@@ -1474,5 +1767,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
1474
1767
  * Generated bundle index. Do not edit.
1475
1768
  */
1476
1769
 
1477
- export { ClrCombobox, ClrComboboxContainer, ClrComboboxModule, ClrOption, ClrOptionGroup, ClrOptionItems, ClrOptionSelected, ClrOptions };
1770
+ export { ClrCombobox, ClrComboboxContainer, ClrComboboxModule, ClrOption, ClrOptionGroup, ClrOptionItems, ClrOptionSelected, ClrOptions, SELECT_ALL_ID };
1478
1771
  //# sourceMappingURL=clr-angular-forms-combobox.mjs.map