@clr/angular 18.0.0-beta.3 → 18.0.0-beta.5
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.
- package/fesm2022/clr-angular-data-datagrid.mjs +8 -2
- package/fesm2022/clr-angular-data-datagrid.mjs.map +1 -1
- package/fesm2022/clr-angular-forms-combobox.mjs +332 -39
- package/fesm2022/clr-angular-forms-combobox.mjs.map +1 -1
- package/fesm2022/clr-angular-layout-nav.mjs +27 -25
- package/fesm2022/clr-angular-layout-nav.mjs.map +1 -1
- package/fesm2022/clr-angular-modal.mjs +2 -3
- package/fesm2022/clr-angular-modal.mjs.map +1 -1
- package/fesm2022/clr-angular-utils.mjs +5 -0
- package/fesm2022/clr-angular-utils.mjs.map +1 -1
- package/fesm2022/clr-angular-wizard.mjs +427 -224
- package/fesm2022/clr-angular-wizard.mjs.map +1 -1
- package/package.json +7 -2
- package/schematics/collection.json +10 -0
- package/schematics/ng-update/index.d.ts +2 -0
- package/schematics/ng-update/index.js +69 -0
- package/schematics/ng-update/index.js.map +1 -0
- package/schematics/ng-update/migrations/css-migration.d.ts +6 -0
- package/schematics/ng-update/migrations/css-migration.js +177 -0
- package/schematics/ng-update/migrations/css-migration.js.map +1 -0
- package/schematics/ng-update/migrations/import-migration.d.ts +4 -0
- package/schematics/ng-update/migrations/import-migration.js +187 -0
- package/schematics/ng-update/migrations/import-migration.js.map +1 -0
- package/schematics/ng-update/migrations/template-migration.d.ts +6 -0
- package/schematics/ng-update/migrations/template-migration.js +203 -0
- package/schematics/ng-update/migrations/template-migration.js.map +1 -0
- package/schematics/ng-update/replacements/css-replacements.d.ts +17 -0
- package/schematics/ng-update/replacements/css-replacements.js +74 -0
- package/schematics/ng-update/replacements/css-replacements.js.map +1 -0
- package/schematics/ng-update/replacements/import-replacements.d.ts +13 -0
- package/schematics/ng-update/replacements/import-replacements.js +101 -0
- package/schematics/ng-update/replacements/import-replacements.js.map +1 -0
- package/schematics/ng-update/replacements/symbol-replacements.d.ts +12 -0
- package/schematics/ng-update/replacements/symbol-replacements.js +68 -0
- package/schematics/ng-update/replacements/symbol-replacements.js.map +1 -0
- package/schematics/ng-update/replacements/template-replacements.d.ts +14 -0
- package/schematics/ng-update/replacements/template-replacements.js +53 -0
- package/schematics/ng-update/replacements/template-replacements.js.map +1 -0
- package/schematics/ng-update/tests/test-helpers.d.ts +6 -0
- package/schematics/ng-update/tests/test-helpers.js +34 -0
- package/schematics/ng-update/tests/test-helpers.js.map +1 -0
- package/schematics/ng-update/utils/file-visitor.d.ts +8 -0
- package/schematics/ng-update/utils/file-visitor.js +44 -0
- package/schematics/ng-update/utils/file-visitor.js.map +1 -0
- package/schematics/ng-update/utils/regexp-utils.d.ts +16 -0
- package/schematics/ng-update/utils/regexp-utils.js +34 -0
- package/schematics/ng-update/utils/regexp-utils.js.map +1 -0
- package/schematics/vitest.config.d.ts +2 -0
- package/schematics/vitest.config.js +17 -0
- package/schematics/vitest.config.js.map +1 -0
- package/types/clr-angular-forms-combobox.d.ts +55 -9
- package/types/clr-angular-layout-nav.d.ts +6 -2
- package/types/clr-angular-utils.d.ts +5 -0
- package/types/clr-angular-wizard.d.ts +89 -35
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
import * as
|
|
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
|
|
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
|
|
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:
|
|
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 (
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
|
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
|
|
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.
|
|
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
|
-
|
|
812
|
-
|
|
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
|
|
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.
|
|
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.
|
|
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 (
|
|
1120
|
-
this.
|
|
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
|
|
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 &&
|
|
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 &&
|
|
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
|
|
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 }
|
|
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
|
-
|
|
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
|