@clr/angular 18.0.0-beta.1 → 18.0.0-beta.11

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 (132) hide show
  1. package/fesm2022/clr-angular-accordion.mjs +1 -1
  2. package/fesm2022/clr-angular-accordion.mjs.map +1 -1
  3. package/fesm2022/clr-angular-button.mjs +6 -6
  4. package/fesm2022/clr-angular-button.mjs.map +1 -1
  5. package/fesm2022/clr-angular-data-datagrid.mjs +45 -35
  6. package/fesm2022/clr-angular-data-datagrid.mjs.map +1 -1
  7. package/fesm2022/clr-angular-data-tree-view.mjs +3 -3
  8. package/fesm2022/clr-angular-data-tree-view.mjs.map +1 -1
  9. package/fesm2022/clr-angular-data.mjs +11 -9116
  10. package/fesm2022/clr-angular-data.mjs.map +1 -1
  11. package/fesm2022/clr-angular-emphasis-alert.mjs +2 -2
  12. package/fesm2022/clr-angular-emphasis-alert.mjs.map +1 -1
  13. package/fesm2022/clr-angular-emphasis.mjs +12 -778
  14. package/fesm2022/clr-angular-emphasis.mjs.map +1 -1
  15. package/fesm2022/clr-angular-forms-checkbox.mjs +13 -44
  16. package/fesm2022/clr-angular-forms-checkbox.mjs.map +1 -1
  17. package/fesm2022/clr-angular-forms-combobox.mjs +420 -90
  18. package/fesm2022/clr-angular-forms-combobox.mjs.map +1 -1
  19. package/fesm2022/clr-angular-forms-common.mjs +62 -57
  20. package/fesm2022/clr-angular-forms-common.mjs.map +1 -1
  21. package/fesm2022/clr-angular-forms-datalist.mjs +2 -14
  22. package/fesm2022/clr-angular-forms-datalist.mjs.map +1 -1
  23. package/fesm2022/clr-angular-forms-datepicker.mjs +20 -28
  24. package/fesm2022/clr-angular-forms-datepicker.mjs.map +1 -1
  25. package/fesm2022/clr-angular-forms-file-input.mjs +78 -62
  26. package/fesm2022/clr-angular-forms-file-input.mjs.map +1 -1
  27. package/fesm2022/clr-angular-forms-input.mjs +4 -18
  28. package/fesm2022/clr-angular-forms-input.mjs.map +1 -1
  29. package/fesm2022/clr-angular-forms-number-input.mjs +2 -14
  30. package/fesm2022/clr-angular-forms-number-input.mjs.map +1 -1
  31. package/fesm2022/clr-angular-forms-password.mjs +2 -14
  32. package/fesm2022/clr-angular-forms-password.mjs.map +1 -1
  33. package/fesm2022/clr-angular-forms-radio.mjs +13 -44
  34. package/fesm2022/clr-angular-forms-radio.mjs.map +1 -1
  35. package/fesm2022/clr-angular-forms-range.mjs +3 -16
  36. package/fesm2022/clr-angular-forms-range.mjs.map +1 -1
  37. package/fesm2022/clr-angular-forms-select.mjs +3 -16
  38. package/fesm2022/clr-angular-forms-select.mjs.map +1 -1
  39. package/fesm2022/clr-angular-forms-textarea.mjs +4 -17
  40. package/fesm2022/clr-angular-forms-textarea.mjs.map +1 -1
  41. package/fesm2022/clr-angular-forms.mjs +68 -8560
  42. package/fesm2022/clr-angular-forms.mjs.map +1 -1
  43. package/fesm2022/clr-angular-icon.mjs +10 -0
  44. package/fesm2022/clr-angular-icon.mjs.map +1 -1
  45. package/fesm2022/clr-angular-layout-nav.mjs +27 -25
  46. package/fesm2022/clr-angular-layout-nav.mjs.map +1 -1
  47. package/fesm2022/clr-angular-layout-tabs.mjs +1 -1
  48. package/fesm2022/clr-angular-layout-tabs.mjs.map +1 -1
  49. package/fesm2022/clr-angular-layout.mjs +15 -2057
  50. package/fesm2022/clr-angular-layout.mjs.map +1 -1
  51. package/fesm2022/clr-angular-modal.mjs +4 -5
  52. package/fesm2022/clr-angular-modal.mjs.map +1 -1
  53. package/fesm2022/clr-angular-popover-common.mjs +151 -85
  54. package/fesm2022/clr-angular-popover-common.mjs.map +1 -1
  55. package/fesm2022/clr-angular-popover-dropdown.mjs +7 -4
  56. package/fesm2022/clr-angular-popover-dropdown.mjs.map +1 -1
  57. package/fesm2022/clr-angular-popover-signpost.mjs +30 -15
  58. package/fesm2022/clr-angular-popover-signpost.mjs.map +1 -1
  59. package/fesm2022/clr-angular-popover-tooltip.mjs +7 -7
  60. package/fesm2022/clr-angular-popover-tooltip.mjs.map +1 -1
  61. package/fesm2022/clr-angular-popover.mjs +12 -2242
  62. package/fesm2022/clr-angular-popover.mjs.map +1 -1
  63. package/fesm2022/clr-angular-stepper.mjs +5 -5
  64. package/fesm2022/clr-angular-stepper.mjs.map +1 -1
  65. package/fesm2022/clr-angular-utils.mjs +20 -485
  66. package/fesm2022/clr-angular-utils.mjs.map +1 -1
  67. package/fesm2022/clr-angular-wizard.mjs +434 -223
  68. package/fesm2022/clr-angular-wizard.mjs.map +1 -1
  69. package/fesm2022/clr-angular.mjs +94 -38740
  70. package/fesm2022/clr-angular.mjs.map +1 -1
  71. package/package.json +10 -5
  72. package/popover/common/README.md +14 -12
  73. package/schematics/collection.json +14 -0
  74. package/schematics/ng-update/index.d.ts +2 -0
  75. package/schematics/ng-update/index.js +69 -0
  76. package/schematics/ng-update/index.js.map +1 -0
  77. package/schematics/ng-update/migrations/css-migration.d.ts +6 -0
  78. package/schematics/ng-update/migrations/css-migration.js +177 -0
  79. package/schematics/ng-update/migrations/css-migration.js.map +1 -0
  80. package/schematics/ng-update/migrations/import-migration.d.ts +4 -0
  81. package/schematics/ng-update/migrations/import-migration.js +187 -0
  82. package/schematics/ng-update/migrations/import-migration.js.map +1 -0
  83. package/schematics/ng-update/migrations/template-migration.d.ts +6 -0
  84. package/schematics/ng-update/migrations/template-migration.js +261 -0
  85. package/schematics/ng-update/migrations/template-migration.js.map +1 -0
  86. package/schematics/ng-update/replacements/css-replacements.d.ts +17 -0
  87. package/schematics/ng-update/replacements/css-replacements.js +74 -0
  88. package/schematics/ng-update/replacements/css-replacements.js.map +1 -0
  89. package/schematics/ng-update/replacements/import-replacements.d.ts +13 -0
  90. package/schematics/ng-update/replacements/import-replacements.js +108 -0
  91. package/schematics/ng-update/replacements/import-replacements.js.map +1 -0
  92. package/schematics/ng-update/replacements/symbol-replacements.d.ts +12 -0
  93. package/schematics/ng-update/replacements/symbol-replacements.js +67 -0
  94. package/schematics/ng-update/replacements/symbol-replacements.js.map +1 -0
  95. package/schematics/ng-update/replacements/template-replacements.d.ts +19 -0
  96. package/schematics/ng-update/replacements/template-replacements.js +57 -0
  97. package/schematics/ng-update/replacements/template-replacements.js.map +1 -0
  98. package/schematics/ng-update/tests/test-helpers.d.ts +6 -0
  99. package/schematics/ng-update/tests/test-helpers.js +34 -0
  100. package/schematics/ng-update/tests/test-helpers.js.map +1 -0
  101. package/schematics/ng-update/utils/file-visitor.d.ts +8 -0
  102. package/schematics/ng-update/utils/file-visitor.js +44 -0
  103. package/schematics/ng-update/utils/file-visitor.js.map +1 -0
  104. package/schematics/ng-update/utils/regexp-utils.d.ts +16 -0
  105. package/schematics/ng-update/utils/regexp-utils.js +34 -0
  106. package/schematics/ng-update/utils/regexp-utils.js.map +1 -0
  107. package/schematics/vitest.config.d.ts +2 -0
  108. package/schematics/vitest.config.js +17 -0
  109. package/schematics/vitest.config.js.map +1 -0
  110. package/types/clr-angular-accordion.d.ts +1 -1
  111. package/types/clr-angular-button.d.ts +1 -1
  112. package/types/clr-angular-data-datagrid.d.ts +23 -33
  113. package/types/clr-angular-data-tree-view.d.ts +10 -1
  114. package/types/clr-angular-data.d.ts +4 -2136
  115. package/types/clr-angular-emphasis.d.ts +4 -220
  116. package/types/clr-angular-forms-combobox.d.ts +64 -11
  117. package/types/clr-angular-forms-common.d.ts +9 -9
  118. package/types/clr-angular-forms-datepicker.d.ts +8 -1
  119. package/types/clr-angular-forms-file-input.d.ts +6 -3
  120. package/types/clr-angular-forms.d.ts +26 -2062
  121. package/types/clr-angular-layout-nav.d.ts +6 -2
  122. package/types/clr-angular-layout-tabs.d.ts +1 -1
  123. package/types/clr-angular-layout.d.ts +6 -463
  124. package/types/clr-angular-popover-common.d.ts +38 -13
  125. package/types/clr-angular-popover-dropdown.d.ts +2 -1
  126. package/types/clr-angular-popover-signpost.d.ts +15 -7
  127. package/types/clr-angular-popover-tooltip.d.ts +1 -1
  128. package/types/clr-angular-popover.d.ts +7 -559
  129. package/types/clr-angular-stepper.d.ts +1 -1
  130. package/types/clr-angular-utils.d.ts +13 -175
  131. package/types/clr-angular-wizard.d.ts +92 -35
  132. package/types/clr-angular.d.ts +22 -10238
@@ -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 i4 from '@clr/angular/icon';
10
- import { ClarityIcons, exclamationCircleIcon, checkCircleIcon, angleIcon, windowCloseIcon, ClrIcon } from '@clr/angular/icon';
11
- import * as i4$1 from '@clr/angular/popover/common';
12
- import { POPOVER_HOST_ANCHOR, ClrPopoverPosition, ClrPopoverType, ClrPopoverHostDirective, ÇlrClrPopoverModuleNext as _lrClrPopoverModuleNext } from '@clr/angular/popover/common';
9
+ import * as i9 from '@clr/angular/icon';
10
+ import { ClarityIcons, successStandardIcon, errorStandardIcon, angleIcon, windowCloseIcon, ClrIcon } from '@clr/angular/icon';
11
+ import * as i4 from '@clr/angular/popover/common';
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';
16
- import { ArrowKeyDirection, normalizeKey, Keys, customFocusableItemProvider, uniqueIdFactory, ClrLoadingState, IF_ACTIVE_ID, LoadingListener, IF_ACTIVE_ID_PROVIDER, FOCUS_SERVICE_PROVIDER, ClrConditionalModule, ClrKeyFocusModule } from '@clr/angular/utils';
15
+ import * as i3 from '@clr/angular/utils';
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.
@@ -63,12 +63,6 @@ class ClrComboboxContainer extends ClrAbstractContainer {
63
63
  }
64
64
  <div class="clr-control-container" [ngClass]="controlClass()" #controlContainer>
65
65
  <ng-content select="clr-combobox"></ng-content>
66
- @if (showInvalid) {
67
- <cds-icon class="clr-validate-icon" shape="exclamation-circle" status="danger" aria-hidden="true"></cds-icon>
68
- }
69
- @if (showValid) {
70
- <cds-icon class="clr-validate-icon" shape="check-circle" status="success" aria-hidden="true"></cds-icon>
71
- }
72
66
  @if (showHelper) {
73
67
  <ng-content select="clr-control-helper"></ng-content>
74
68
  }
@@ -79,7 +73,7 @@ class ClrComboboxContainer extends ClrAbstractContainer {
79
73
  <ng-content select="clr-control-success"></ng-content>
80
74
  }
81
75
  </div>
82
- `, isInline: true, dependencies: [{ kind: "directive", type: i3.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: i4.ClrIcon, selector: "clr-icon, cds-icon", inputs: ["shape", "size", "direction", "flip", "solid", "status", "inverse", "badge"] }, { 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"] }] }); }
83
77
  }
84
78
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ClrComboboxContainer, decorators: [{
85
79
  type: Component,
@@ -92,12 +86,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
92
86
  }
93
87
  <div class="clr-control-container" [ngClass]="controlClass()" #controlContainer>
94
88
  <ng-content select="clr-combobox"></ng-content>
95
- @if (showInvalid) {
96
- <cds-icon class="clr-validate-icon" shape="exclamation-circle" status="danger" aria-hidden="true"></cds-icon>
97
- }
98
- @if (showValid) {
99
- <cds-icon class="clr-validate-icon" shape="check-circle" status="success" aria-hidden="true"></cds-icon>
100
- }
101
89
  @if (showHelper) {
102
90
  <ng-content select="clr-control-helper"></ng-content>
103
91
  }
@@ -131,20 +119,24 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
131
119
  * This software is released under MIT license.
132
120
  * The full license information can be found in LICENSE in the root directory of this project.
133
121
  */
134
- class MultiSelectComboboxModel {
122
+ class ComboboxModel {
123
+ constructor() {
124
+ this.identityFn = (item) => item;
125
+ }
126
+ }
127
+
128
+ /*
129
+ * Copyright (c) 2016-2026 Broadcom. All Rights Reserved.
130
+ * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
131
+ * This software is released under MIT license.
132
+ * The full license information can be found in LICENSE in the root directory of this project.
133
+ */
134
+ class MultiSelectComboboxModel extends ComboboxModel {
135
135
  containsItem(item) {
136
- if (this.model) {
137
- if (this.displayField && typeof item === 'object') {
138
- const includes = this.model.some(modelItem => {
139
- return modelItem[this.displayField] === item[this.displayField];
140
- });
141
- return includes;
142
- }
143
- else {
144
- return this.model.includes(item);
145
- }
136
+ if (this.model === null || this.model === undefined) {
137
+ return false;
146
138
  }
147
- return false;
139
+ return this.model.some(m => this.identityFn(m) === this.identityFn(item));
148
140
  }
149
141
  select(item) {
150
142
  this.addItem(item);
@@ -207,7 +199,7 @@ class MultiSelectComboboxModel {
207
199
  if (this.model === null || this.model === undefined) {
208
200
  return;
209
201
  }
210
- const index = this.model.indexOf(item);
202
+ const index = this.model.findIndex(m => this.identityFn(m) === this.identityFn(item));
211
203
  if (index > -1) {
212
204
  this.model.splice(index, 1);
213
205
  }
@@ -224,9 +216,12 @@ class MultiSelectComboboxModel {
224
216
  * This software is released under MIT license.
225
217
  * The full license information can be found in LICENSE in the root directory of this project.
226
218
  */
227
- class SingleSelectComboboxModel {
219
+ class SingleSelectComboboxModel extends ComboboxModel {
228
220
  containsItem(item) {
229
- return this.model === item;
221
+ if (this.model === null || this.model === undefined) {
222
+ return false;
223
+ }
224
+ return this.identityFn(this.model) === this.identityFn(item);
230
225
  }
231
226
  select(item) {
232
227
  this.model = item;
@@ -313,13 +308,16 @@ class OptionSelectionService {
313
308
  constructor() {
314
309
  this.loading = false;
315
310
  this.editable = false;
316
- this.filtering = true;
311
+ this.showSelectAll = false;
317
312
  // Display all options on first open, even if filter text exists.
318
313
  // https://github.com/vmware-clarity/ng-clarity/issues/386
319
314
  this.showAllOptions = true;
320
315
  this._currentInput = '';
321
316
  this._inputChanged = new BehaviorSubject('');
322
317
  this._selectionChanged = new ReplaySubject(1);
318
+ this._selectAllRequested = new Subject();
319
+ this.editableResolver = (input) => input;
320
+ this._identityFn = (item) => item;
323
321
  this.inputChanged = this._inputChanged.asObservable();
324
322
  }
325
323
  get displayField() {
@@ -350,6 +348,21 @@ class OptionSelectionService {
350
348
  get multiselectable() {
351
349
  return this.selectionModel instanceof MultiSelectComboboxModel;
352
350
  }
351
+ get identityFn() {
352
+ return this._identityFn;
353
+ }
354
+ set identityFn(value) {
355
+ this._identityFn = value || ((item) => item);
356
+ if (this.selectionModel) {
357
+ this.selectionModel.identityFn = this._identityFn;
358
+ }
359
+ }
360
+ get selectAllRequested() {
361
+ return this._selectAllRequested.asObservable();
362
+ }
363
+ requestSelectAll() {
364
+ this._selectAllRequested.next();
365
+ }
353
366
  select(item) {
354
367
  if (item === null || item === undefined || this.selectionModel.containsItem(item)) {
355
368
  return;
@@ -369,6 +382,33 @@ class OptionSelectionService {
369
382
  }
370
383
  this._selectionChanged.next(this.selectionModel);
371
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
+ }
372
412
  unselect(item) {
373
413
  if (item === null || item === undefined || !this.selectionModel.containsItem(item)) {
374
414
  return;
@@ -376,25 +416,58 @@ class OptionSelectionService {
376
416
  this.selectionModel.unselect(item);
377
417
  this._selectionChanged.next(this.selectionModel);
378
418
  }
379
- // TODO: Add support for trackBy and compareFn
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
+ }
380
428
  setSelectionValue(value) {
381
- // NOTE: Currently we assume that no 2 options will have the same value
382
- // but Eudes and I discussed that this is a possibility but we will handle
383
- // this later
384
- // if selection is undefined, or its value hasn't changed, or changing from null <-> undefined, that's not really changing so we return
385
- if (!this.selectionModel || this.selectionModel.model === value || (!this.selectionModel.model && !value)) {
429
+ if (!this.selectionModel) {
430
+ return;
431
+ }
432
+ const current = this.selectionModel.model;
433
+ if (this.valuesEqualByIdentity(current, value)) {
386
434
  return;
387
435
  }
388
436
  this.selectionModel.model = value;
389
437
  this._selectionChanged.next(this.selectionModel);
390
438
  }
391
- parseStringToModel(value) {
392
- if (this.displayField) {
393
- return {
394
- [this.displayField]: value,
395
- };
439
+ valuesEqualByIdentity(current, value) {
440
+ if (current === value) {
441
+ return true;
442
+ }
443
+ // Check if both are null or undefined or empty string.
444
+ if ((current === null || current === undefined || current === '') &&
445
+ (value === null || value === undefined || value === '')) {
446
+ return true;
447
+ }
448
+ // Check if one is null or undefined or empty string and the other is not.
449
+ if (current === null ||
450
+ current === undefined ||
451
+ current === '' ||
452
+ value === null ||
453
+ value === undefined ||
454
+ value === '') {
455
+ return false;
456
+ }
457
+ if (this.multiselectable) {
458
+ const cur = current;
459
+ const val = value;
460
+ if (cur.length !== val.length) {
461
+ return false;
462
+ }
463
+ // We only consider values equal if they are ordered the same way.
464
+ const curIds = cur.map(this._identityFn);
465
+ const valIds = val.map(this._identityFn);
466
+ return curIds.every((id, i) => id === valIds[i]);
467
+ }
468
+ else {
469
+ return this._identityFn(current) === this._identityFn(value);
396
470
  }
397
- return value;
398
471
  }
399
472
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: OptionSelectionService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
400
473
  static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: OptionSelectionService }); }
@@ -476,6 +549,9 @@ class ComboboxFocusHandler {
476
549
  addOptionValues(options) {
477
550
  this.optionData = options;
478
551
  }
552
+ focusOption(option) {
553
+ this.pseudoFocus.select(option);
554
+ }
479
555
  handleFocusSubscription() {
480
556
  this.popoverService.openChange.subscribe(open => {
481
557
  if (!open) {
@@ -520,13 +596,18 @@ class ComboboxFocusHandler {
520
596
  // this service is only interested in keys that may move the focus
521
597
  handleTextInput(event) {
522
598
  let preventDefault = false;
523
- const key = normalizeKey(event.key);
599
+ const key = event.key;
524
600
  if (event) {
525
601
  switch (key) {
526
602
  case Keys.Enter:
527
603
  if (this.popoverService.open && this.pseudoFocus.model) {
528
604
  if (this.selectionService.multiselectable) {
529
- 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
+ }
530
611
  }
531
612
  else {
532
613
  this.selectionService.select(this.pseudoFocus.model.value);
@@ -577,26 +658,20 @@ class ComboboxFocusHandler {
577
658
  this.renderer.listen(el, 'blur', event => {
578
659
  if (this.focusOutOfComponent(event)) {
579
660
  this.popoverService.open = false;
580
- // Workaround for popover close-on-outside-click timing issues in Edge browser
581
- if (this.componentCdRef) {
582
- this.componentCdRef.detectChanges();
583
- }
584
661
  }
585
662
  });
586
663
  }
587
664
  }
588
665
  focusOutOfComponent(event) {
589
- // event.relatedTarget is null in IE11. In that case we use document.activeElement
590
- // which points to the element that becomes active as the blur event occurs on the input.
591
- const target = (event.relatedTarget || document.activeElement);
666
+ const target = event.relatedTarget;
592
667
  return !(this.textInput.contains(target) || this.trigger.contains(target) || this.listbox.contains(target));
593
668
  }
594
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ComboboxFocusHandler, deps: [{ token: i0.RendererFactory2 }, { token: i4$1.ClrPopoverService }, { token: OptionSelectionService }, { token: PLATFORM_ID }], target: i0.ɵɵFactoryTarget.Injectable }); }
669
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ComboboxFocusHandler, deps: [{ token: i0.RendererFactory2 }, { token: i4.ClrPopoverService }, { token: OptionSelectionService }, { token: PLATFORM_ID }], target: i0.ɵɵFactoryTarget.Injectable }); }
595
670
  static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ComboboxFocusHandler }); }
596
671
  }
597
672
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ComboboxFocusHandler, decorators: [{
598
673
  type: Injectable
599
- }], ctorParameters: () => [{ type: i0.RendererFactory2 }, { type: i4$1.ClrPopoverService }, { type: OptionSelectionService }, { type: undefined, decorators: [{
674
+ }], ctorParameters: () => [{ type: i0.RendererFactory2 }, { type: i4.ClrPopoverService }, { type: OptionSelectionService }, { type: undefined, decorators: [{
600
675
  type: Inject,
601
676
  args: [PLATFORM_ID]
602
677
  }] }] });
@@ -668,9 +743,10 @@ class ClrOption {
668
743
  // This way we achieve two things:
669
744
  // - do not lose focus
670
745
  // - we're still able to use onBlur for "outside-click" handling
746
+ this.focusHandler.focusOption(this.optionProxy);
671
747
  this.focusHandler.focusInput();
672
748
  }
673
- 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 }); }
674
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: `
675
751
  <ng-content></ng-content>
676
752
  @if (selected) {
@@ -697,7 +773,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
697
773
  },
698
774
  standalone: false,
699
775
  }]
700
- }], 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: [{
701
777
  type: Input,
702
778
  args: ['id']
703
779
  }], value: [{
@@ -721,6 +797,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
721
797
  * The full license information can be found in LICENSE in the root directory of this project.
722
798
  */
723
799
  let nbOptionsComponents = 0;
800
+ const SELECT_ALL_ID = 'select-all-id';
724
801
  class ClrOptions {
725
802
  constructor(optionSelectionService, id, el, commonStrings, focusHandler, popoverService, parentHost, document) {
726
803
  this.optionSelectionService = optionSelectionService;
@@ -739,12 +816,22 @@ class ClrOptions {
739
816
  this.optionsId = 'clr-options-' + nbOptionsComponents++;
740
817
  }
741
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
+ }
742
829
  get items() {
743
830
  return this._items;
744
831
  }
745
832
  set items(items) {
746
833
  this._items = items;
747
- this.focusHandler.addOptionValues(this._items.map(option => option.optionProxy));
834
+ this.updateFocusableItems();
748
835
  }
749
836
  /**
750
837
  * Tests if the list of options is empty, meaning it doesn't contain any items
@@ -758,6 +845,34 @@ class ClrOptions {
758
845
  get noResultsElementId() {
759
846
  return `${this.optionsId}-no-results`;
760
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
+ }
761
876
  ngAfterViewInit() {
762
877
  this.focusHandler.listbox = this.el.nativeElement;
763
878
  this.subscriptions.push(this.items.changes.subscribe(items => {
@@ -769,6 +884,8 @@ class ClrOptions {
769
884
  else {
770
885
  this.focusHandler.pseudoFocus.pop();
771
886
  }
887
+ }), this.optionSelectionService.selectAllRequested.subscribe(() => {
888
+ this.toggleSelectAll();
772
889
  }));
773
890
  }
774
891
  ngOnDestroy() {
@@ -780,8 +897,19 @@ class ClrOptions {
780
897
  loadingStateChange(state) {
781
898
  this.loading = state === ClrLoadingState.LOADING;
782
899
  }
783
- 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$1.ClrPopoverService }, { token: POPOVER_HOST_ANCHOR, optional: true }, { token: DOCUMENT }], target: i0.ɵɵFactoryTarget.Component }); }
784
- 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: `
785
913
  @if (optionSelectionService.loading) {
786
914
  <div class="clr-combobox-options-loading">
787
915
  <clr-spinner clrInline>
@@ -793,6 +921,21 @@ class ClrOptions {
793
921
  </div>
794
922
  }
795
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
+
796
939
  <!-- Rendered if data set is empty -->
797
940
  @if (emptyOptions) {
798
941
  <div [id]="noResultsElementId" role="option">
@@ -822,6 +965,21 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
822
965
  </div>
823
966
  }
824
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
+
825
983
  <!-- Rendered if data set is empty -->
826
984
  @if (emptyOptions) {
827
985
  <div [id]="noResultsElementId" role="option">
@@ -837,6 +995,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
837
995
  providers: [{ provide: LoadingListener, useExisting: ClrOptions }],
838
996
  host: {
839
997
  '[class.clr-combobox-options]': 'true',
998
+ '[class.clr-combobox-options-multi]': 'optionSelectionService.multiselectable',
840
999
  '[class.clr-combobox-options-hidden]': 'emptyOptions && editable',
841
1000
  '[attr.role]': '"listbox"',
842
1001
  '[id]': 'optionsId',
@@ -846,17 +1005,20 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
846
1005
  }], ctorParameters: () => [{ type: OptionSelectionService }, { type: undefined, decorators: [{
847
1006
  type: Inject,
848
1007
  args: [IF_ACTIVE_ID]
849
- }] }, { type: i0.ElementRef }, { type: i3$1.ClrCommonStringsService }, { type: ComboboxFocusHandler }, { type: i4$1.ClrPopoverService }, { type: i0.ElementRef, decorators: [{
1008
+ }] }, { type: i0.ElementRef }, { type: i3.ClrCommonStringsService }, { type: ComboboxFocusHandler }, { type: i4.ClrPopoverService }, { type: i0.ElementRef, decorators: [{
850
1009
  type: Optional
851
1010
  }, {
852
1011
  type: Inject,
853
- args: [POPOVER_HOST_ANCHOR]
1012
+ args: [POPOVER_HOST_ORIGIN]
854
1013
  }] }, { type: undefined, decorators: [{
855
1014
  type: Inject,
856
1015
  args: [DOCUMENT]
857
1016
  }] }], propDecorators: { optionsId: [{
858
1017
  type: Input,
859
1018
  args: ['id']
1019
+ }], selectAllBtn: [{
1020
+ type: ViewChild,
1021
+ args: ['selectAllBtn']
860
1022
  }], items: [{
861
1023
  type: ContentChildren,
862
1024
  args: [ClrOption, { descendants: true }]
@@ -869,7 +1031,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
869
1031
  * The full license information can be found in LICENSE in the root directory of this project.
870
1032
  */
871
1033
  class ClrCombobox extends WrappedFormControl {
872
- 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) {
873
1035
  super(vcr, ClrComboboxContainer, injector, control, renderer, el);
874
1036
  this.control = control;
875
1037
  this.renderer = renderer;
@@ -881,6 +1043,8 @@ class ClrCombobox extends WrappedFormControl {
881
1043
  this.platformId = platformId;
882
1044
  this.focusHandler = focusHandler;
883
1045
  this.cdr = cdr;
1046
+ this.zone = zone;
1047
+ this.container = container;
884
1048
  this.placeholder = '';
885
1049
  this.clrInputChange = new EventEmitter(false);
886
1050
  this.clrOpenChange = this.popoverService.openChange;
@@ -892,13 +1056,23 @@ class ClrCombobox extends WrappedFormControl {
892
1056
  this.popoverPosition = ClrPopoverPosition.BOTTOM_LEFT;
893
1057
  this.index = 1;
894
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();
895
1064
  this._searchText = '';
896
1065
  if (control) {
897
1066
  control.valueAccessor = this;
898
1067
  }
899
1068
  // default to SingleSelectComboboxModel, in case the optional input [ClrMulti] isn't used
900
- optionSelectionService.selectionModel = new SingleSelectComboboxModel();
901
- this.updateControlValue();
1069
+ this.multiSelect = false;
1070
+ }
1071
+ get showSelectAll() {
1072
+ return this.optionSelectionService.showSelectAll;
1073
+ }
1074
+ set showSelectAll(value) {
1075
+ this.optionSelectionService.showSelectAll = value;
902
1076
  }
903
1077
  get editable() {
904
1078
  return this.optionSelectionService.editable;
@@ -906,6 +1080,12 @@ class ClrCombobox extends WrappedFormControl {
906
1080
  set editable(value) {
907
1081
  this.optionSelectionService.editable = value;
908
1082
  }
1083
+ set editableResolver(value) {
1084
+ this.optionSelectionService.editableResolver = value;
1085
+ }
1086
+ set identityFn(value) {
1087
+ this.optionSelectionService.identityFn = value;
1088
+ }
909
1089
  get multiSelect() {
910
1090
  return this.optionSelectionService.multiselectable;
911
1091
  }
@@ -918,6 +1098,7 @@ class ClrCombobox extends WrappedFormControl {
918
1098
  // since the initial call to writeValue (caused by [ngModel] input) should happen after this
919
1099
  this.optionSelectionService.selectionModel = new SingleSelectComboboxModel();
920
1100
  }
1101
+ this.optionSelectionService.selectionModel.identityFn = this.optionSelectionService.identityFn;
921
1102
  this.updateControlValue();
922
1103
  }
923
1104
  // Override the id of WrappedFormControl, as we want to move it to the embedded input.
@@ -966,6 +1147,24 @@ class ClrCombobox extends WrappedFormControl {
966
1147
  get displayField() {
967
1148
  return this.optionSelectionService.displayField;
968
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
+ }
969
1168
  get disabled() {
970
1169
  return this.control?.disabled;
971
1170
  }
@@ -977,13 +1176,30 @@ class ClrCombobox extends WrappedFormControl {
977
1176
  }
978
1177
  }
979
1178
  ngAfterViewInit() {
980
- this.focusHandler.componentCdRef = this.cdr;
981
1179
  this.focusHandler.textInput = this.textbox.nativeElement;
982
1180
  this.focusHandler.trigger = this.trigger.nativeElement;
983
1181
  // The text input is the actual element we are wrapping
984
1182
  // This assignment is needed by the wrapper, so it can set
985
1183
  // the aria properties on the input element, not on the component.
1184
+ // We calculate on the initial load to prevent flickering
986
1185
  this.el = this.textbox;
1186
+ if (this.showSelectAll) {
1187
+ if (this.multiSelect && this.multiSelectModel?.length > 0) {
1188
+ this.calculateLimit();
1189
+ }
1190
+ this.initialiseObserver();
1191
+ }
1192
+ }
1193
+ ngOnDestroy() {
1194
+ super.ngOnDestroy();
1195
+ if (this.resizeObserver) {
1196
+ this.resizeObserver.disconnect();
1197
+ }
1198
+ }
1199
+ clearSelection() {
1200
+ this.focusHandler.focusInput();
1201
+ // Clear the array model directly
1202
+ this.optionSelectionService.setSelectionValue([]);
987
1203
  }
988
1204
  onKeyUp(event) {
989
1205
  // if BACKSPACE in multiselect mode, delete the last pill if text is empty
@@ -1001,7 +1217,7 @@ class ClrCombobox extends WrappedFormControl {
1001
1217
  break;
1002
1218
  case Keys.Enter:
1003
1219
  if (this.editable && this._searchText.length > 0 && this.options.emptyOptions) {
1004
- const parsedInput = this.optionSelectionService.parseStringToModel(this._searchText);
1220
+ const parsedInput = this.optionSelectionService.editableResolver(this._searchText);
1005
1221
  this.control?.control.markAsTouched();
1006
1222
  this.optionSelectionService.select(parsedInput);
1007
1223
  this.searchText = '';
@@ -1042,7 +1258,7 @@ class ClrCombobox extends WrappedFormControl {
1042
1258
  }
1043
1259
  onChange() {
1044
1260
  if (this.editable && !this.multiSelect && this.options.emptyOptions) {
1045
- const parsedInput = this.optionSelectionService.parseStringToModel(this._searchText);
1261
+ const parsedInput = this.optionSelectionService.editableResolver(this._searchText);
1046
1262
  this.optionSelectionService.setSelectionValue(parsedInput);
1047
1263
  }
1048
1264
  }
@@ -1083,13 +1299,103 @@ class ClrCombobox extends WrappedFormControl {
1083
1299
  this.popoverService.toggleWithEvent(event);
1084
1300
  }
1085
1301
  }
1302
+ toggleSelectionExpand() {
1303
+ this.selectionExpanded = !this.selectionExpanded;
1304
+ if (this.selectionExpanded) {
1305
+ this.applyLimit(this.multiSelectModel.length);
1306
+ }
1307
+ else {
1308
+ this.containerWidthChange.next(this.containerWidth);
1309
+ }
1310
+ }
1311
+ initialiseObserver() {
1312
+ const container = this.container ? this.container.el.nativeElement : this.el.nativeElement.parentElement;
1313
+ this.containerWidth = container.offsetWidth;
1314
+ this.resizeObserver = new ResizeObserver(entries => {
1315
+ this.zone.runOutsideAngular(() => {
1316
+ entries.forEach(entry => {
1317
+ const entryWidth = entry.contentRect.width;
1318
+ switch (entry.target) {
1319
+ case container:
1320
+ if (this.containerWidth !== entryWidth) {
1321
+ this.containerWidth = entryWidth;
1322
+ this.containerWidthChange.next(entryWidth);
1323
+ }
1324
+ break;
1325
+ case this.wrapper.nativeElement:
1326
+ this.containerWidthChange.next(null);
1327
+ break;
1328
+ }
1329
+ });
1330
+ });
1331
+ });
1332
+ this.resizeObserver.observe(container);
1333
+ this.resizeObserver.observe(this.wrapper.nativeElement);
1334
+ }
1335
+ calculateLimit() {
1336
+ this.shouldCalculate = true;
1337
+ this.cdr.detectChanges();
1338
+ if (!this.calculationPills || this.calculationPills.length === 0) {
1339
+ this.applyLimit();
1340
+ return;
1341
+ }
1342
+ const pillDimensions = this.calculationPills.map(p => ({
1343
+ top: p.nativeElement.offsetTop,
1344
+ width: p.nativeElement.offsetWidth,
1345
+ left: p.nativeElement.offsetLeft,
1346
+ }));
1347
+ const firstPill = pillDimensions[0];
1348
+ const buttonWidth = this.truncationButton?.nativeElement?.offsetWidth || 100;
1349
+ const textboxWidth = this.textbox.nativeElement.offsetWidth;
1350
+ const expectedWidth = this.containerWidth - textboxWidth - buttonWidth;
1351
+ let fitCount = 0;
1352
+ for (const pill of pillDimensions) {
1353
+ if (pill.top > firstPill.top || pill.left + pill.width > expectedWidth) {
1354
+ break;
1355
+ }
1356
+ fitCount++;
1357
+ }
1358
+ this.applyLimit(fitCount);
1359
+ }
1360
+ applyLimit(limit = undefined) {
1361
+ this.zone.run(() => {
1362
+ this.calculatedLimit = limit === 0 ? 1 : limit;
1363
+ this.shouldCalculate = false;
1364
+ this.cdr.markForCheck();
1365
+ });
1366
+ }
1367
+ updateTotalSelection() {
1368
+ if (!this.multiSelect || !this.multiSelectModel?.length) {
1369
+ this.isTotalSelection = false;
1370
+ return;
1371
+ }
1372
+ // Skip recalculation when items are filtered to zero (e.g. "no results")
1373
+ // to prevent pills from flashing while typing in the search input.
1374
+ if (!this.options?.items?.length) {
1375
+ return;
1376
+ }
1377
+ this.isTotalSelection = this.optionSelectionService.containsAll(this.options.items.map(option => option.value));
1378
+ }
1086
1379
  initializeSubscriptions() {
1087
1380
  this.subscriptions.push(this.optionSelectionService.selectionChanged.subscribe((newSelection) => {
1088
1381
  this.updateInputValue(newSelection);
1089
- if (!this.multiSelect && newSelection && !newSelection.isEmpty()) {
1090
- this.popoverService.open = false;
1382
+ if (newSelection.isEmpty()) {
1383
+ this.selectionExpanded = false;
1384
+ this.isTotalSelection = false;
1385
+ }
1386
+ else {
1387
+ if (!this.multiSelect) {
1388
+ this.popoverService.open = false;
1389
+ }
1390
+ this.updateTotalSelection();
1091
1391
  }
1092
1392
  this.updateControlValue();
1393
+ if (this.selectionExpanded) {
1394
+ this.applyLimit(this.multiSelectModel.length);
1395
+ }
1396
+ else {
1397
+ this.calculateLimit();
1398
+ }
1093
1399
  if (this.multiSelect) {
1094
1400
  setTimeout(() => {
1095
1401
  this.popoverService?.updatePosition();
@@ -1116,6 +1422,10 @@ class ClrCombobox extends WrappedFormControl {
1116
1422
  else {
1117
1423
  this.searchText = this.getDisplayNames(this.optionSelectionService.selectionModel.model)[0] || '';
1118
1424
  }
1425
+ }), this.containerWidthChange.pipe(debounceTime(0)).subscribe(() => {
1426
+ if (!this.selectionExpanded && !this.isTotalSelection) {
1427
+ this.calculateLimit();
1428
+ }
1119
1429
  }));
1120
1430
  }
1121
1431
  updateInputValue(model) {
@@ -1140,14 +1450,14 @@ class ClrCombobox extends WrappedFormControl {
1140
1450
  }
1141
1451
  return [this.optionSelectionService.selectionModel.model];
1142
1452
  }
1143
- 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$1.ClrPopoverService }, { token: ComboboxContainerService, optional: true }, { token: PLATFORM_ID }, { token: ComboboxFocusHandler }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); }
1144
- 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"], 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: [
1453
+ 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 }); }
1454
+ 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: [
1145
1455
  OptionSelectionService,
1146
1456
  { provide: LoadingListener, useExisting: ClrCombobox },
1147
1457
  IF_ACTIVE_ID_PROVIDER,
1148
1458
  FOCUS_SERVICE_PROVIDER,
1149
1459
  COMBOBOX_FOCUS_HANDLER_PROVIDER,
1150
- ], 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$1.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 clrPopoverAnchor\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 due to issues in Edge browser.\nAdditionally '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: i4.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$1.ClrPopoverAnchor, selector: "[clrPopoverAnchor]" }, { kind: "directive", type: i4$1.ClrPopoverContent, selector: "[clrPopoverContent]", inputs: ["clrPopoverContent", "clrPopoverContentAt", "clrPopoverContentAvailablePositions", "clrPopoverContentType", "clrPopoverContentOutsideClickToClose", "clrPopoverContentScrollToClose"] }] }); }
1460
+ ], 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" }] }); }
1151
1461
  }
1152
1462
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ClrCombobox, decorators: [{
1153
1463
  type: Component,
@@ -1161,17 +1471,21 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
1161
1471
  '[class.aria-required]': 'true',
1162
1472
  '[class.clr-combobox]': 'true',
1163
1473
  '[class.clr-combobox-disabled]': 'control?.disabled',
1164
- }, 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 clrPopoverAnchor\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 due to issues in Edge browser.\nAdditionally '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" }]
1474
+ }, 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" }]
1165
1475
  }], ctorParameters: () => [{ type: i0.ViewContainerRef }, { type: i0.Injector }, { type: i1$1.NgControl, decorators: [{
1166
1476
  type: Self
1167
1477
  }, {
1168
1478
  type: Optional
1169
- }] }, { type: i0.Renderer2 }, { type: i0.ElementRef }, { type: OptionSelectionService }, { type: i3$1.ClrCommonStringsService }, { type: i4$1.ClrPopoverService }, { type: ComboboxContainerService, decorators: [{
1479
+ }] }, { type: i0.Renderer2 }, { type: i0.ElementRef }, { type: OptionSelectionService }, { type: i3.ClrCommonStringsService }, { type: i4.ClrPopoverService }, { type: ComboboxContainerService, decorators: [{
1170
1480
  type: Optional
1171
1481
  }] }, { type: undefined, decorators: [{
1172
1482
  type: Inject,
1173
1483
  args: [PLATFORM_ID]
1174
- }] }, { type: ComboboxFocusHandler }, { type: i0.ChangeDetectorRef }], propDecorators: { placeholder: [{
1484
+ }] }, { type: ComboboxFocusHandler }, { type: i0.ChangeDetectorRef }, { type: i0.NgZone }, { type: ClrComboboxContainer, decorators: [{
1485
+ type: Optional
1486
+ }, {
1487
+ type: Host
1488
+ }] }], propDecorators: { placeholder: [{
1175
1489
  type: Input,
1176
1490
  args: ['placeholder']
1177
1491
  }], clrInputChange: [{
@@ -1192,12 +1506,30 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
1192
1506
  }], optionSelected: [{
1193
1507
  type: ContentChild,
1194
1508
  args: [ClrOptionSelected]
1509
+ }], truncationButton: [{
1510
+ type: ViewChild,
1511
+ args: ['truncationButton']
1512
+ }], wrapper: [{
1513
+ type: ViewChild,
1514
+ args: ['wrapper', { static: true }]
1515
+ }], calculationPills: [{
1516
+ type: ViewChildren,
1517
+ args: ['pill']
1195
1518
  }], options: [{
1196
1519
  type: ContentChild,
1197
1520
  args: [ClrOptions]
1521
+ }], showSelectAll: [{
1522
+ type: Input,
1523
+ args: [{ alias: 'showSelectAll', transform: booleanAttribute }]
1198
1524
  }], editable: [{
1199
1525
  type: Input,
1200
1526
  args: ['clrEditable']
1527
+ }], editableResolver: [{
1528
+ type: Input,
1529
+ args: ['clrEditableResolverFn']
1530
+ }], identityFn: [{
1531
+ type: Input,
1532
+ args: ['clrComboboxIdentityFn']
1201
1533
  }], multiSelect: [{
1202
1534
  type: Input,
1203
1535
  args: ['clrMulti']
@@ -1223,9 +1555,7 @@ class ClrOptionItems {
1223
1555
  this.iterableProxy = new NgForOf(vcr, template, differs);
1224
1556
  this.subscriptions.push(optionService.inputChanged.subscribe(filter => {
1225
1557
  this.filter = filter;
1226
- if (optionService.filtering) {
1227
- this.updateItems();
1228
- }
1558
+ this.updateItems();
1229
1559
  }));
1230
1560
  }
1231
1561
  set rawItems(items) {
@@ -1359,7 +1689,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
1359
1689
  */
1360
1690
  class ClrComboboxModule {
1361
1691
  constructor() {
1362
- ClarityIcons.addIcons(exclamationCircleIcon, checkCircleIcon, angleIcon, windowCloseIcon);
1692
+ ClarityIcons.addIcons(successStandardIcon, errorStandardIcon, angleIcon, windowCloseIcon);
1363
1693
  }
1364
1694
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ClrComboboxModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
1365
1695
  static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.1.3", ngImport: i0, type: ClrComboboxModule, declarations: [ClrCombobox,
@@ -1374,7 +1704,7 @@ class ClrComboboxModule {
1374
1704
  ClrKeyFocusModule,
1375
1705
  ClrCommonFormsModule,
1376
1706
  ClrConditionalModule,
1377
- _lrClrPopoverModuleNext,
1707
+ ClrPopoverModuleNext,
1378
1708
  ClrSpinnerModule], exports: [ClrCommonFormsModule,
1379
1709
  ClrCombobox,
1380
1710
  ClrComboboxContainer,
@@ -1390,7 +1720,7 @@ class ClrComboboxModule {
1390
1720
  ClrKeyFocusModule,
1391
1721
  ClrCommonFormsModule,
1392
1722
  ClrConditionalModule,
1393
- _lrClrPopoverModuleNext,
1723
+ ClrPopoverModuleNext,
1394
1724
  ClrSpinnerModule, ClrCommonFormsModule,
1395
1725
  ClrConditionalModule] }); }
1396
1726
  }
@@ -1404,7 +1734,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
1404
1734
  ClrKeyFocusModule,
1405
1735
  ClrCommonFormsModule,
1406
1736
  ClrConditionalModule,
1407
- _lrClrPopoverModuleNext,
1737
+ ClrPopoverModuleNext,
1408
1738
  ClrSpinnerModule,
1409
1739
  ],
1410
1740
  declarations: [
@@ -1441,5 +1771,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
1441
1771
  * Generated bundle index. Do not edit.
1442
1772
  */
1443
1773
 
1444
- export { ClrCombobox, ClrComboboxContainer, ClrComboboxModule, ClrOption, ClrOptionGroup, ClrOptionItems, ClrOptionSelected, ClrOptions };
1774
+ export { ClrCombobox, ClrComboboxContainer, ClrComboboxModule, ClrOption, ClrOptionGroup, ClrOptionItems, ClrOptionSelected, ClrOptions, SELECT_ALL_ID };
1445
1775
  //# sourceMappingURL=clr-angular-forms-combobox.mjs.map