@rossigee/clarity-angular 18.2.1-fixed → 18.2.1
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-accordion.mjs +355 -0
- package/fesm2022/clr-angular-accordion.mjs.map +1 -0
- package/fesm2022/clr-angular-button.mjs +713 -0
- package/fesm2022/clr-angular-button.mjs.map +1 -0
- package/fesm2022/clr-angular-collapsible-panel.mjs +201 -0
- package/fesm2022/clr-angular-collapsible-panel.mjs.map +1 -0
- package/fesm2022/clr-angular-data-datagrid.mjs +7635 -0
- package/fesm2022/clr-angular-data-datagrid.mjs.map +1 -0
- package/fesm2022/clr-angular-data-stack-view.mjs +442 -0
- package/fesm2022/clr-angular-data-stack-view.mjs.map +1 -0
- package/fesm2022/clr-angular-data-tree-view.mjs +1106 -0
- package/fesm2022/clr-angular-data-tree-view.mjs.map +1 -0
- package/fesm2022/clr-angular-data.mjs +40 -0
- package/fesm2022/clr-angular-data.mjs.map +1 -0
- package/fesm2022/clr-angular-emphasis-alert.mjs +624 -0
- package/fesm2022/clr-angular-emphasis-alert.mjs.map +1 -0
- package/fesm2022/clr-angular-emphasis-badge.mjs +69 -0
- package/fesm2022/clr-angular-emphasis-badge.mjs.map +1 -0
- package/fesm2022/clr-angular-emphasis-common.mjs +25 -0
- package/fesm2022/clr-angular-emphasis-common.mjs.map +1 -0
- package/fesm2022/clr-angular-emphasis-label.mjs +105 -0
- package/fesm2022/clr-angular-emphasis-label.mjs.map +1 -0
- package/fesm2022/clr-angular-emphasis.mjs +41 -0
- package/fesm2022/clr-angular-emphasis.mjs.map +1 -0
- package/fesm2022/clr-angular-forms-checkbox.mjs +270 -0
- package/fesm2022/clr-angular-forms-checkbox.mjs.map +1 -0
- package/fesm2022/clr-angular-forms-combobox.mjs +1775 -0
- package/fesm2022/clr-angular-forms-combobox.mjs.map +1 -0
- package/fesm2022/clr-angular-forms-common.mjs +1251 -0
- package/fesm2022/clr-angular-forms-common.mjs.map +1 -0
- package/fesm2022/clr-angular-forms-datalist.mjs +263 -0
- package/fesm2022/clr-angular-forms-datalist.mjs.map +1 -0
- package/fesm2022/clr-angular-forms-datepicker.mjs +3274 -0
- package/fesm2022/clr-angular-forms-datepicker.mjs.map +1 -0
- package/fesm2022/clr-angular-forms-file-input.mjs +826 -0
- package/fesm2022/clr-angular-forms-file-input.mjs.map +1 -0
- package/fesm2022/clr-angular-forms-input.mjs +153 -0
- package/fesm2022/clr-angular-forms-input.mjs.map +1 -0
- package/fesm2022/clr-angular-forms-number-input.mjs +236 -0
- package/fesm2022/clr-angular-forms-number-input.mjs.map +1 -0
- package/fesm2022/clr-angular-forms-password.mjs +233 -0
- package/fesm2022/clr-angular-forms-password.mjs.map +1 -0
- package/fesm2022/clr-angular-forms-radio.mjs +231 -0
- package/fesm2022/clr-angular-forms-radio.mjs.map +1 -0
- package/fesm2022/clr-angular-forms-range.mjs +186 -0
- package/fesm2022/clr-angular-forms-range.mjs.map +1 -0
- package/fesm2022/clr-angular-forms-select.mjs +153 -0
- package/fesm2022/clr-angular-forms-select.mjs.map +1 -0
- package/fesm2022/clr-angular-forms-textarea.mjs +136 -0
- package/fesm2022/clr-angular-forms-textarea.mjs.map +1 -0
- package/fesm2022/clr-angular-forms.mjs +100 -0
- package/fesm2022/clr-angular-forms.mjs.map +1 -0
- package/fesm2022/clr-angular-icon.mjs +7397 -0
- package/fesm2022/clr-angular-icon.mjs.map +1 -0
- package/fesm2022/clr-angular-layout-breadcrumbs.mjs +120 -0
- package/fesm2022/clr-angular-layout-breadcrumbs.mjs.map +1 -0
- package/fesm2022/clr-angular-layout-main-container.mjs +100 -0
- package/fesm2022/clr-angular-layout-main-container.mjs.map +1 -0
- package/fesm2022/clr-angular-layout-nav.mjs +582 -0
- package/fesm2022/clr-angular-layout-nav.mjs.map +1 -0
- package/fesm2022/clr-angular-layout-tabs.mjs +807 -0
- package/fesm2022/clr-angular-layout-tabs.mjs.map +1 -0
- package/fesm2022/clr-angular-layout-vertical-nav.mjs +507 -0
- package/fesm2022/clr-angular-layout-vertical-nav.mjs.map +1 -0
- package/fesm2022/clr-angular-layout.mjs +44 -0
- package/fesm2022/clr-angular-layout.mjs.map +1 -0
- package/fesm2022/clr-angular-modal.mjs +617 -0
- package/fesm2022/clr-angular-modal.mjs.map +1 -0
- package/fesm2022/clr-angular-popover-common.mjs +1082 -0
- package/fesm2022/clr-angular-popover-common.mjs.map +1 -0
- package/fesm2022/clr-angular-popover-dropdown.mjs +492 -0
- package/fesm2022/clr-angular-popover-dropdown.mjs.map +1 -0
- package/fesm2022/clr-angular-popover-signpost.mjs +494 -0
- package/fesm2022/clr-angular-popover-signpost.mjs.map +1 -0
- package/fesm2022/clr-angular-popover-tooltip.mjs +293 -0
- package/fesm2022/clr-angular-popover-tooltip.mjs.map +1 -0
- package/fesm2022/clr-angular-popover.mjs +41 -0
- package/fesm2022/clr-angular-popover.mjs.map +1 -0
- package/fesm2022/clr-angular-progress-progress-bars.mjs +217 -0
- package/fesm2022/clr-angular-progress-progress-bars.mjs.map +1 -0
- package/fesm2022/clr-angular-progress-spinner.mjs +132 -0
- package/fesm2022/clr-angular-progress-spinner.mjs.map +1 -0
- package/fesm2022/clr-angular-stepper.mjs +694 -0
- package/fesm2022/clr-angular-stepper.mjs.map +1 -0
- package/fesm2022/clr-angular-timeline.mjs +316 -0
- package/fesm2022/clr-angular-timeline.mjs.map +1 -0
- package/fesm2022/clr-angular-utils-conditional.mjs +351 -0
- package/fesm2022/clr-angular-utils-conditional.mjs.map +1 -0
- package/fesm2022/clr-angular-utils-loading.mjs +107 -0
- package/fesm2022/clr-angular-utils-loading.mjs.map +1 -0
- package/fesm2022/clr-angular-utils.mjs +2079 -0
- package/fesm2022/clr-angular-utils.mjs.map +1 -0
- package/fesm2022/clr-angular-wizard.mjs +3074 -0
- package/fesm2022/clr-angular-wizard.mjs.map +1 -0
- package/fesm2022/{rossigee-clarity-angular.mjs → clr-angular.mjs} +2 -2
- package/fesm2022/clr-angular.mjs.map +1 -0
- package/package.json +105 -103
- 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 +261 -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 +108 -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 +67 -0
- package/schematics/ng-update/replacements/symbol-replacements.js.map +1 -0
- package/schematics/ng-update/replacements/template-replacements.d.ts +19 -0
- package/schematics/ng-update/replacements/template-replacements.js +57 -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-accordion.d.ts +100 -0
- package/types/clr-angular-button.d.ts +169 -0
- package/types/clr-angular-collapsible-panel.d.ts +73 -0
- package/types/clr-angular-data-datagrid.d.ts +1843 -0
- package/types/clr-angular-data-stack-view.d.ts +87 -0
- package/types/clr-angular-data-tree-view.d.ts +229 -0
- package/types/clr-angular-data.d.ts +15 -0
- package/types/clr-angular-emphasis-alert.d.ts +175 -0
- package/types/clr-angular-emphasis-badge.d.ts +25 -0
- package/types/clr-angular-emphasis-common.d.ts +6 -0
- package/types/clr-angular-emphasis-label.d.ts +29 -0
- package/types/clr-angular-emphasis.d.ts +15 -0
- package/types/clr-angular-forms-checkbox.d.ts +69 -0
- package/types/clr-angular-forms-combobox.d.ts +353 -0
- package/types/clr-angular-forms-common.d.ts +339 -0
- package/types/clr-angular-forms-datalist.d.ts +59 -0
- package/types/clr-angular-forms-datepicker.d.ts +986 -0
- package/types/clr-angular-forms-file-input.d.ts +193 -0
- package/types/clr-angular-forms-input.d.ts +29 -0
- package/types/clr-angular-forms-number-input.d.ts +43 -0
- package/types/clr-angular-forms-password.d.ts +52 -0
- package/types/clr-angular-forms-radio.d.ts +50 -0
- package/types/clr-angular-forms-range.d.ts +37 -0
- package/types/clr-angular-forms-select.d.ts +36 -0
- package/types/clr-angular-forms-textarea.d.ts +29 -0
- package/types/clr-angular-forms.d.ts +36 -0
- package/types/clr-angular-icon.d.ts +1498 -0
- package/types/clr-angular-layout-breadcrumbs.d.ts +45 -0
- package/types/clr-angular-layout-main-container.d.ts +28 -0
- package/types/clr-angular-layout-nav.d.ts +142 -0
- package/types/clr-angular-layout-tabs.d.ts +158 -0
- package/types/clr-angular-layout-vertical-nav.d.ts +128 -0
- package/types/clr-angular-layout.d.ts +19 -0
- package/types/clr-angular-modal.d.ts +160 -0
- package/types/clr-angular-popover-common.d.ts +254 -0
- package/types/clr-angular-popover-dropdown.d.ts +123 -0
- package/types/clr-angular-popover-signpost.d.ts +157 -0
- package/types/clr-angular-popover-tooltip.d.ts +83 -0
- package/types/clr-angular-popover.d.ts +16 -0
- package/types/clr-angular-progress-progress-bars.d.ts +57 -0
- package/types/clr-angular-progress-spinner.d.ts +44 -0
- package/types/clr-angular-stepper.d.ts +179 -0
- package/types/clr-angular-timeline.d.ts +86 -0
- package/types/clr-angular-utils-conditional.d.ts +132 -0
- package/types/clr-angular-utils-loading.d.ts +38 -0
- package/types/clr-angular-utils.d.ts +913 -0
- package/types/clr-angular-wizard.d.ts +1508 -0
- package/fesm2022/rossigee-clarity-angular.mjs.map +0 -1
- /package/types/{rossigee-clarity-angular.d.ts → clr-angular.d.ts} +0 -0
|
@@ -0,0 +1,1775 @@
|
|
|
1
|
+
import * as i8 from '@angular/common';
|
|
2
|
+
import { isPlatformBrowser, NgForOf, CommonModule } from '@angular/common';
|
|
3
|
+
import * as i0 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
|
+
import * as i1$1 from '@angular/forms';
|
|
6
|
+
import { FormsModule } from '@angular/forms';
|
|
7
|
+
import * as i1 from '@clr/angular/forms/common';
|
|
8
|
+
import { ClrAbstractContainer, NgControlService, ControlIdService, ControlClassService, WrappedFormControl, ClrCommonFormsModule } from '@clr/angular/forms/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
|
+
import * as i5 from '@clr/angular/progress/spinner';
|
|
14
|
+
import { ClrSpinnerModule } from '@clr/angular/progress/spinner';
|
|
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';
|
|
18
|
+
import { take } from 'rxjs/operators';
|
|
19
|
+
|
|
20
|
+
/*
|
|
21
|
+
* Copyright (c) 2016-2026 Broadcom. All Rights Reserved.
|
|
22
|
+
* The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
|
|
23
|
+
* This software is released under MIT license.
|
|
24
|
+
* The full license information can be found in LICENSE in the root directory of this project.
|
|
25
|
+
*/
|
|
26
|
+
class ComboboxContainerService {
|
|
27
|
+
constructor() {
|
|
28
|
+
this.labelOffset = 0;
|
|
29
|
+
}
|
|
30
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ComboboxContainerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
31
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ComboboxContainerService }); }
|
|
32
|
+
}
|
|
33
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ComboboxContainerService, decorators: [{
|
|
34
|
+
type: Injectable
|
|
35
|
+
}] });
|
|
36
|
+
|
|
37
|
+
/*
|
|
38
|
+
* Copyright (c) 2016-2026 Broadcom. All Rights Reserved.
|
|
39
|
+
* The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
|
|
40
|
+
* This software is released under MIT license.
|
|
41
|
+
* The full license information can be found in LICENSE in the root directory of this project.
|
|
42
|
+
*/
|
|
43
|
+
class ClrComboboxContainer extends ClrAbstractContainer {
|
|
44
|
+
constructor(layoutService, controlClassService, ngControlService, containerService, el) {
|
|
45
|
+
super(layoutService, controlClassService, ngControlService);
|
|
46
|
+
this.containerService = containerService;
|
|
47
|
+
this.el = el;
|
|
48
|
+
}
|
|
49
|
+
ngAfterContentInit() {
|
|
50
|
+
if (this.label) {
|
|
51
|
+
this.containerService.labelText = this.label.labelText;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
ngAfterViewInit() {
|
|
55
|
+
this.containerService.labelOffset =
|
|
56
|
+
this.controlContainer.nativeElement.offsetHeight - this.el.nativeElement.offsetHeight;
|
|
57
|
+
}
|
|
58
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ClrComboboxContainer, deps: [{ token: i1.LayoutService, optional: true }, { token: i1.ControlClassService }, { token: i1.NgControlService }, { token: ComboboxContainerService }, { token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
59
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: ClrComboboxContainer, isStandalone: false, selector: "clr-combobox-container", host: { properties: { "class.clr-form-control": "true", "class.clr-combobox-form-control": "true", "class.clr-form-control-disabled": "control?.disabled", "class.clr-row": "addGrid()" } }, providers: [NgControlService, ControlIdService, ControlClassService, ComboboxContainerService], viewQueries: [{ propertyName: "controlContainer", first: true, predicate: ["controlContainer"], descendants: true }], usesInheritance: true, ngImport: i0, template: `
|
|
60
|
+
<ng-content select="label"></ng-content>
|
|
61
|
+
@if (!label && addGrid()) {
|
|
62
|
+
<label></label>
|
|
63
|
+
}
|
|
64
|
+
<div class="clr-control-container" [ngClass]="controlClass()" #controlContainer>
|
|
65
|
+
<ng-content select="clr-combobox"></ng-content>
|
|
66
|
+
@if (showHelper) {
|
|
67
|
+
<ng-content select="clr-control-helper"></ng-content>
|
|
68
|
+
}
|
|
69
|
+
@if (showInvalid) {
|
|
70
|
+
<ng-content select="clr-control-error"></ng-content>
|
|
71
|
+
}
|
|
72
|
+
@if (showValid) {
|
|
73
|
+
<ng-content select="clr-control-success"></ng-content>
|
|
74
|
+
}
|
|
75
|
+
</div>
|
|
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
|
+
}
|
|
78
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ClrComboboxContainer, decorators: [{
|
|
79
|
+
type: Component,
|
|
80
|
+
args: [{
|
|
81
|
+
selector: 'clr-combobox-container',
|
|
82
|
+
template: `
|
|
83
|
+
<ng-content select="label"></ng-content>
|
|
84
|
+
@if (!label && addGrid()) {
|
|
85
|
+
<label></label>
|
|
86
|
+
}
|
|
87
|
+
<div class="clr-control-container" [ngClass]="controlClass()" #controlContainer>
|
|
88
|
+
<ng-content select="clr-combobox"></ng-content>
|
|
89
|
+
@if (showHelper) {
|
|
90
|
+
<ng-content select="clr-control-helper"></ng-content>
|
|
91
|
+
}
|
|
92
|
+
@if (showInvalid) {
|
|
93
|
+
<ng-content select="clr-control-error"></ng-content>
|
|
94
|
+
}
|
|
95
|
+
@if (showValid) {
|
|
96
|
+
<ng-content select="clr-control-success"></ng-content>
|
|
97
|
+
}
|
|
98
|
+
</div>
|
|
99
|
+
`,
|
|
100
|
+
host: {
|
|
101
|
+
'[class.clr-form-control]': 'true',
|
|
102
|
+
'[class.clr-combobox-form-control]': 'true',
|
|
103
|
+
'[class.clr-form-control-disabled]': 'control?.disabled',
|
|
104
|
+
'[class.clr-row]': 'addGrid()',
|
|
105
|
+
},
|
|
106
|
+
providers: [NgControlService, ControlIdService, ControlClassService, ComboboxContainerService],
|
|
107
|
+
standalone: false,
|
|
108
|
+
}]
|
|
109
|
+
}], ctorParameters: () => [{ type: i1.LayoutService, decorators: [{
|
|
110
|
+
type: Optional
|
|
111
|
+
}] }, { type: i1.ControlClassService }, { type: i1.NgControlService }, { type: ComboboxContainerService }, { type: i0.ElementRef }], propDecorators: { controlContainer: [{
|
|
112
|
+
type: ViewChild,
|
|
113
|
+
args: ['controlContainer']
|
|
114
|
+
}] } });
|
|
115
|
+
|
|
116
|
+
/*
|
|
117
|
+
* Copyright (c) 2016-2026 Broadcom. All Rights Reserved.
|
|
118
|
+
* The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
|
|
119
|
+
* This software is released under MIT license.
|
|
120
|
+
* The full license information can be found in LICENSE in the root directory of this project.
|
|
121
|
+
*/
|
|
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
|
+
containsItem(item) {
|
|
136
|
+
if (this.model === null || this.model === undefined) {
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
return this.model.some(m => this.identityFn(m) === this.identityFn(item));
|
|
140
|
+
}
|
|
141
|
+
select(item) {
|
|
142
|
+
this.addItem(item);
|
|
143
|
+
}
|
|
144
|
+
unselect(item) {
|
|
145
|
+
this.removeItem(item);
|
|
146
|
+
}
|
|
147
|
+
isEmpty() {
|
|
148
|
+
return !(this.model && this.model.length > 0);
|
|
149
|
+
}
|
|
150
|
+
pop() {
|
|
151
|
+
let item;
|
|
152
|
+
if (this.model && this.model.length > 0) {
|
|
153
|
+
item = this.model[this.model.length - 1];
|
|
154
|
+
this.removeItem(item);
|
|
155
|
+
}
|
|
156
|
+
return item;
|
|
157
|
+
}
|
|
158
|
+
toString(displayField, index = -1) {
|
|
159
|
+
let displayString = '';
|
|
160
|
+
if (this.model) {
|
|
161
|
+
// If the model is array, we can use a specific item from it, to retrieve the display value.
|
|
162
|
+
if (index > -1) {
|
|
163
|
+
if (this.model[index]) {
|
|
164
|
+
// If we have a defined display field, we'll use it's value as display value
|
|
165
|
+
if (displayField && this.model[index][displayField]) {
|
|
166
|
+
displayString += this.model[index][displayField];
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
// If we don't have a defined display field, we'll use the toString representation of the
|
|
170
|
+
// item as display value.
|
|
171
|
+
displayString += this.model[index].toString();
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
this.model.forEach((model) => {
|
|
177
|
+
// If we have a defined display field, we'll use it's value as display value
|
|
178
|
+
if (displayField && model[displayField]) {
|
|
179
|
+
displayString += model[displayField];
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
// If we don't have a defined display field, we'll use the toString representation of the
|
|
183
|
+
// model as display value.
|
|
184
|
+
displayString += model.toString();
|
|
185
|
+
}
|
|
186
|
+
displayString += ' ';
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return displayString.trim();
|
|
191
|
+
}
|
|
192
|
+
addItem(item) {
|
|
193
|
+
if (!this.containsItem(item)) {
|
|
194
|
+
this.model = this.model || [];
|
|
195
|
+
this.model.push(item);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
removeItem(item) {
|
|
199
|
+
if (this.model === null || this.model === undefined) {
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
const index = this.model.findIndex(m => this.identityFn(m) === this.identityFn(item));
|
|
203
|
+
if (index > -1) {
|
|
204
|
+
this.model.splice(index, 1);
|
|
205
|
+
}
|
|
206
|
+
// we intentionally set the model to null for form validation
|
|
207
|
+
if (this.model.length === 0) {
|
|
208
|
+
this.model = null;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/*
|
|
214
|
+
* Copyright (c) 2016-2026 Broadcom. All Rights Reserved.
|
|
215
|
+
* The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
|
|
216
|
+
* This software is released under MIT license.
|
|
217
|
+
* The full license information can be found in LICENSE in the root directory of this project.
|
|
218
|
+
*/
|
|
219
|
+
class SingleSelectComboboxModel extends ComboboxModel {
|
|
220
|
+
containsItem(item) {
|
|
221
|
+
if (this.model === null || this.model === undefined) {
|
|
222
|
+
return false;
|
|
223
|
+
}
|
|
224
|
+
return this.identityFn(this.model) === this.identityFn(item);
|
|
225
|
+
}
|
|
226
|
+
select(item) {
|
|
227
|
+
this.model = item;
|
|
228
|
+
}
|
|
229
|
+
unselect(item) {
|
|
230
|
+
if (this.containsItem(item)) {
|
|
231
|
+
this.model = null;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
isEmpty() {
|
|
235
|
+
return !this.model;
|
|
236
|
+
}
|
|
237
|
+
pop() {
|
|
238
|
+
const item = this.model;
|
|
239
|
+
this.model = null;
|
|
240
|
+
return item;
|
|
241
|
+
}
|
|
242
|
+
toString(displayField) {
|
|
243
|
+
if (!this.model) {
|
|
244
|
+
return '';
|
|
245
|
+
}
|
|
246
|
+
if (displayField && this.model[displayField]) {
|
|
247
|
+
return this.model[displayField];
|
|
248
|
+
}
|
|
249
|
+
else {
|
|
250
|
+
return this.model.toString();
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/*
|
|
256
|
+
* Copyright (c) 2016-2026 Broadcom. All Rights Reserved.
|
|
257
|
+
* The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
|
|
258
|
+
* This software is released under MIT license.
|
|
259
|
+
* The full license information can be found in LICENSE in the root directory of this project.
|
|
260
|
+
*/
|
|
261
|
+
class ClrOptionSelected {
|
|
262
|
+
constructor(template) {
|
|
263
|
+
this.template = template;
|
|
264
|
+
}
|
|
265
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ClrOptionSelected, deps: [{ token: i0.TemplateRef }], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
266
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.1.3", type: ClrOptionSelected, isStandalone: false, selector: "[clrOptionSelected]", inputs: { selected: ["clrOptionSelected", "selected"] }, ngImport: i0 }); }
|
|
267
|
+
}
|
|
268
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ClrOptionSelected, decorators: [{
|
|
269
|
+
type: Directive,
|
|
270
|
+
args: [{
|
|
271
|
+
selector: '[clrOptionSelected]',
|
|
272
|
+
standalone: false,
|
|
273
|
+
}]
|
|
274
|
+
}], ctorParameters: () => [{ type: i0.TemplateRef }], propDecorators: { selected: [{
|
|
275
|
+
type: Input,
|
|
276
|
+
args: ['clrOptionSelected']
|
|
277
|
+
}] } });
|
|
278
|
+
|
|
279
|
+
/*
|
|
280
|
+
* Copyright (c) 2016-2026 Broadcom. All Rights Reserved.
|
|
281
|
+
* The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
|
|
282
|
+
* This software is released under MIT license.
|
|
283
|
+
* The full license information can be found in LICENSE in the root directory of this project.
|
|
284
|
+
*/
|
|
285
|
+
class PseudoFocusModel extends SingleSelectComboboxModel {
|
|
286
|
+
constructor() {
|
|
287
|
+
super(...arguments);
|
|
288
|
+
this._focusChanged = new BehaviorSubject(null);
|
|
289
|
+
}
|
|
290
|
+
get focusChanged() {
|
|
291
|
+
return this._focusChanged.asObservable();
|
|
292
|
+
}
|
|
293
|
+
select(item) {
|
|
294
|
+
if (this.model !== item) {
|
|
295
|
+
this.model = item;
|
|
296
|
+
this._focusChanged.next(item);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/*
|
|
302
|
+
* Copyright (c) 2016-2026 Broadcom. All Rights Reserved.
|
|
303
|
+
* The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
|
|
304
|
+
* This software is released under MIT license.
|
|
305
|
+
* The full license information can be found in LICENSE in the root directory of this project.
|
|
306
|
+
*/
|
|
307
|
+
class OptionSelectionService {
|
|
308
|
+
constructor() {
|
|
309
|
+
this.loading = false;
|
|
310
|
+
this.editable = false;
|
|
311
|
+
this.showSelectAll = false;
|
|
312
|
+
// Display all options on first open, even if filter text exists.
|
|
313
|
+
// https://github.com/vmware-clarity/ng-clarity/issues/386
|
|
314
|
+
this.showAllOptions = true;
|
|
315
|
+
this._currentInput = '';
|
|
316
|
+
this._inputChanged = new BehaviorSubject('');
|
|
317
|
+
this._selectionChanged = new ReplaySubject(1);
|
|
318
|
+
this._selectAllRequested = new Subject();
|
|
319
|
+
this.editableResolver = (input) => input;
|
|
320
|
+
this._identityFn = (item) => item;
|
|
321
|
+
this.inputChanged = this._inputChanged.asObservable();
|
|
322
|
+
}
|
|
323
|
+
get displayField() {
|
|
324
|
+
return this._displayField;
|
|
325
|
+
}
|
|
326
|
+
set displayField(value) {
|
|
327
|
+
this._displayField = value;
|
|
328
|
+
if (this.selectionModel) {
|
|
329
|
+
this.selectionModel.displayField = value;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
get currentInput() {
|
|
333
|
+
return this._currentInput;
|
|
334
|
+
}
|
|
335
|
+
set currentInput(input) {
|
|
336
|
+
// clear value in single selection model when input is empty
|
|
337
|
+
if (input === '' && !this.multiselectable) {
|
|
338
|
+
this.setSelectionValue(null);
|
|
339
|
+
}
|
|
340
|
+
this._currentInput = input;
|
|
341
|
+
this._inputChanged.next(input);
|
|
342
|
+
}
|
|
343
|
+
// This observable is for notifying the ClrOption to update its
|
|
344
|
+
// selection by comparing the value
|
|
345
|
+
get selectionChanged() {
|
|
346
|
+
return this._selectionChanged.asObservable();
|
|
347
|
+
}
|
|
348
|
+
get multiselectable() {
|
|
349
|
+
return this.selectionModel instanceof MultiSelectComboboxModel;
|
|
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
|
+
}
|
|
366
|
+
select(item) {
|
|
367
|
+
if (item === null || item === undefined || this.selectionModel.containsItem(item)) {
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
this.selectionModel.select(item);
|
|
371
|
+
this._selectionChanged.next(this.selectionModel);
|
|
372
|
+
}
|
|
373
|
+
toggle(item) {
|
|
374
|
+
if (item === null || item === undefined) {
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
if (this.selectionModel.containsItem(item)) {
|
|
378
|
+
this.selectionModel.unselect(item);
|
|
379
|
+
}
|
|
380
|
+
else {
|
|
381
|
+
this.selectionModel.select(item);
|
|
382
|
+
}
|
|
383
|
+
this._selectionChanged.next(this.selectionModel);
|
|
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
|
+
}
|
|
412
|
+
unselect(item) {
|
|
413
|
+
if (item === null || item === undefined || !this.selectionModel.containsItem(item)) {
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
this.selectionModel.unselect(item);
|
|
417
|
+
this._selectionChanged.next(this.selectionModel);
|
|
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
|
+
}
|
|
428
|
+
setSelectionValue(value) {
|
|
429
|
+
if (!this.selectionModel) {
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
const current = this.selectionModel.model;
|
|
433
|
+
if (this.valuesEqualByIdentity(current, value)) {
|
|
434
|
+
return;
|
|
435
|
+
}
|
|
436
|
+
this.selectionModel.model = value;
|
|
437
|
+
this._selectionChanged.next(this.selectionModel);
|
|
438
|
+
}
|
|
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);
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: OptionSelectionService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
473
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: OptionSelectionService }); }
|
|
474
|
+
}
|
|
475
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: OptionSelectionService, decorators: [{
|
|
476
|
+
type: Injectable
|
|
477
|
+
}], ctorParameters: () => [] });
|
|
478
|
+
|
|
479
|
+
/*
|
|
480
|
+
* Copyright (c) 2016-2026 Broadcom. All Rights Reserved.
|
|
481
|
+
* The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
|
|
482
|
+
* This software is released under MIT license.
|
|
483
|
+
* The full license information can be found in LICENSE in the root directory of this project.
|
|
484
|
+
*/
|
|
485
|
+
class ComboboxFocusHandler {
|
|
486
|
+
constructor(rendererFactory, popoverService, selectionService, platformId) {
|
|
487
|
+
this.popoverService = popoverService;
|
|
488
|
+
this.selectionService = selectionService;
|
|
489
|
+
this.platformId = platformId;
|
|
490
|
+
this.pseudoFocus = new PseudoFocusModel();
|
|
491
|
+
this.optionData = [];
|
|
492
|
+
this.handleFocusSubscription();
|
|
493
|
+
// Direct renderer injection can be problematic and leads to failing tests at least
|
|
494
|
+
this.renderer = rendererFactory.createRenderer(null, null);
|
|
495
|
+
}
|
|
496
|
+
get trigger() {
|
|
497
|
+
return this._trigger;
|
|
498
|
+
}
|
|
499
|
+
set trigger(el) {
|
|
500
|
+
this._trigger = el;
|
|
501
|
+
this.addFocusOnBlurListener(el);
|
|
502
|
+
}
|
|
503
|
+
get listbox() {
|
|
504
|
+
return this._listbox;
|
|
505
|
+
}
|
|
506
|
+
set listbox(el) {
|
|
507
|
+
this._listbox = el;
|
|
508
|
+
this.addFocusOnBlurListener(el);
|
|
509
|
+
}
|
|
510
|
+
get textInput() {
|
|
511
|
+
return this._textInput;
|
|
512
|
+
}
|
|
513
|
+
set textInput(el) {
|
|
514
|
+
this._textInput = el;
|
|
515
|
+
this.renderer.listen(el, 'keydown', event => !this.handleTextInput(event));
|
|
516
|
+
this.addFocusOnBlurListener(el);
|
|
517
|
+
}
|
|
518
|
+
focusInput() {
|
|
519
|
+
if (this.textInput && isPlatformBrowser(this.platformId)) {
|
|
520
|
+
this.textInput.focus({ preventScroll: true });
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
focusFirstActive() {
|
|
524
|
+
if (this.optionData.length > 0) {
|
|
525
|
+
if (this.selectionService.selectionModel.isEmpty()) {
|
|
526
|
+
this.pseudoFocus.select(this.optionData[0]);
|
|
527
|
+
}
|
|
528
|
+
else {
|
|
529
|
+
let firstActive;
|
|
530
|
+
if (this.selectionService.multiselectable) {
|
|
531
|
+
firstActive = this.selectionService.selectionModel.model[0];
|
|
532
|
+
}
|
|
533
|
+
else {
|
|
534
|
+
firstActive = this.selectionService.selectionModel.model;
|
|
535
|
+
}
|
|
536
|
+
const activeProxy = this.optionData.find(option => option.value === firstActive);
|
|
537
|
+
if (activeProxy) {
|
|
538
|
+
// active element is visible
|
|
539
|
+
this.pseudoFocus.select(activeProxy);
|
|
540
|
+
}
|
|
541
|
+
else {
|
|
542
|
+
// we have active element, but it's filtered out
|
|
543
|
+
this.pseudoFocus.select(this.optionData[0]);
|
|
544
|
+
}
|
|
545
|
+
this.scrollIntoSelectedModel('auto');
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
addOptionValues(options) {
|
|
550
|
+
this.optionData = options;
|
|
551
|
+
}
|
|
552
|
+
focusOption(option) {
|
|
553
|
+
this.pseudoFocus.select(option);
|
|
554
|
+
}
|
|
555
|
+
handleFocusSubscription() {
|
|
556
|
+
this.popoverService.openChange.subscribe(open => {
|
|
557
|
+
if (!open) {
|
|
558
|
+
this.pseudoFocus.model = null;
|
|
559
|
+
}
|
|
560
|
+
});
|
|
561
|
+
}
|
|
562
|
+
moveFocusTo(direction) {
|
|
563
|
+
let index = this.optionData.findIndex(option => option.equals(this.pseudoFocus.model));
|
|
564
|
+
if (direction === ArrowKeyDirection.UP) {
|
|
565
|
+
if (index === -1 || index === 0) {
|
|
566
|
+
index = this.optionData.length - 1;
|
|
567
|
+
}
|
|
568
|
+
else {
|
|
569
|
+
index--;
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
else if (direction === ArrowKeyDirection.DOWN) {
|
|
573
|
+
if (index === -1 || index === this.optionData.length - 1) {
|
|
574
|
+
index = 0;
|
|
575
|
+
}
|
|
576
|
+
else {
|
|
577
|
+
index++;
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
this.pseudoFocus.select(this.optionData[index]);
|
|
581
|
+
this.scrollIntoSelectedModel();
|
|
582
|
+
}
|
|
583
|
+
openAndMoveTo(direction) {
|
|
584
|
+
if (!this.popoverService.open) {
|
|
585
|
+
this.popoverService.openChange.pipe(take(1)).subscribe(open => {
|
|
586
|
+
if (open) {
|
|
587
|
+
this.moveFocusTo(direction);
|
|
588
|
+
}
|
|
589
|
+
});
|
|
590
|
+
this.popoverService.open = true;
|
|
591
|
+
}
|
|
592
|
+
else {
|
|
593
|
+
this.moveFocusTo(direction);
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
// this service is only interested in keys that may move the focus
|
|
597
|
+
handleTextInput(event) {
|
|
598
|
+
let preventDefault = false;
|
|
599
|
+
const key = event.key;
|
|
600
|
+
if (event) {
|
|
601
|
+
switch (key) {
|
|
602
|
+
case Keys.Enter:
|
|
603
|
+
if (this.popoverService.open && this.pseudoFocus.model) {
|
|
604
|
+
if (this.selectionService.multiselectable) {
|
|
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
|
+
}
|
|
611
|
+
}
|
|
612
|
+
else {
|
|
613
|
+
this.selectionService.select(this.pseudoFocus.model.value);
|
|
614
|
+
}
|
|
615
|
+
preventDefault = true;
|
|
616
|
+
}
|
|
617
|
+
break;
|
|
618
|
+
case Keys.Space:
|
|
619
|
+
if (!this.popoverService.open) {
|
|
620
|
+
this.popoverService.open = true;
|
|
621
|
+
preventDefault = true;
|
|
622
|
+
}
|
|
623
|
+
break;
|
|
624
|
+
case Keys.ArrowUp:
|
|
625
|
+
this.preventViewportScrolling(event);
|
|
626
|
+
this.openAndMoveTo(ArrowKeyDirection.UP);
|
|
627
|
+
preventDefault = true;
|
|
628
|
+
break;
|
|
629
|
+
case Keys.ArrowDown:
|
|
630
|
+
this.preventViewportScrolling(event);
|
|
631
|
+
this.openAndMoveTo(ArrowKeyDirection.DOWN);
|
|
632
|
+
preventDefault = true;
|
|
633
|
+
break;
|
|
634
|
+
default:
|
|
635
|
+
// Any other keypress
|
|
636
|
+
if (event.key !== Keys.Tab &&
|
|
637
|
+
!(this.selectionService.multiselectable && event.key === Keys.Backspace) &&
|
|
638
|
+
!(event.key === Keys.Escape) &&
|
|
639
|
+
!this.popoverService.open) {
|
|
640
|
+
this.popoverService.open = true;
|
|
641
|
+
}
|
|
642
|
+
break;
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
return preventDefault;
|
|
646
|
+
}
|
|
647
|
+
scrollIntoSelectedModel(behavior = 'smooth') {
|
|
648
|
+
if (this.pseudoFocus.model && this.pseudoFocus.model.el) {
|
|
649
|
+
this.pseudoFocus.model.el.scrollIntoView({ behavior, block: 'center', inline: 'nearest' });
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
preventViewportScrolling(event) {
|
|
653
|
+
event.preventDefault();
|
|
654
|
+
event.stopImmediatePropagation();
|
|
655
|
+
}
|
|
656
|
+
addFocusOnBlurListener(el) {
|
|
657
|
+
if (isPlatformBrowser(this.platformId)) {
|
|
658
|
+
this.renderer.listen(el, 'blur', event => {
|
|
659
|
+
if (this.focusOutOfComponent(event)) {
|
|
660
|
+
this.popoverService.open = false;
|
|
661
|
+
}
|
|
662
|
+
});
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
focusOutOfComponent(event) {
|
|
666
|
+
const target = event.relatedTarget;
|
|
667
|
+
return !(this.textInput.contains(target) || this.trigger.contains(target) || this.listbox.contains(target));
|
|
668
|
+
}
|
|
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 }); }
|
|
670
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ComboboxFocusHandler }); }
|
|
671
|
+
}
|
|
672
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ComboboxFocusHandler, decorators: [{
|
|
673
|
+
type: Injectable
|
|
674
|
+
}], ctorParameters: () => [{ type: i0.RendererFactory2 }, { type: i4.ClrPopoverService }, { type: OptionSelectionService }, { type: undefined, decorators: [{
|
|
675
|
+
type: Inject,
|
|
676
|
+
args: [PLATFORM_ID]
|
|
677
|
+
}] }] });
|
|
678
|
+
const COMBOBOX_FOCUS_HANDLER_PROVIDER = customFocusableItemProvider(ComboboxFocusHandler);
|
|
679
|
+
class OptionData {
|
|
680
|
+
constructor(id, value) {
|
|
681
|
+
this.id = id;
|
|
682
|
+
this.value = value;
|
|
683
|
+
}
|
|
684
|
+
equals(other) {
|
|
685
|
+
if (!other) {
|
|
686
|
+
return false;
|
|
687
|
+
}
|
|
688
|
+
return this.id === other.id && this.value === other.value;
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
/*
|
|
693
|
+
* Copyright (c) 2016-2026 Broadcom. All Rights Reserved.
|
|
694
|
+
* The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
|
|
695
|
+
* This software is released under MIT license.
|
|
696
|
+
* The full license information can be found in LICENSE in the root directory of this project.
|
|
697
|
+
*/
|
|
698
|
+
class ClrOption {
|
|
699
|
+
constructor(elRef, commonStrings, focusHandler, optionSelectionService) {
|
|
700
|
+
this.elRef = elRef;
|
|
701
|
+
this.commonStrings = commonStrings;
|
|
702
|
+
this.focusHandler = focusHandler;
|
|
703
|
+
this.optionSelectionService = optionSelectionService;
|
|
704
|
+
// A proxy with only the necessary data to be used for a11y and the focus handler service.
|
|
705
|
+
this.optionProxy = new OptionData(null, null);
|
|
706
|
+
this.optionProxy.el = elRef.nativeElement;
|
|
707
|
+
}
|
|
708
|
+
get optionId() {
|
|
709
|
+
return this._id;
|
|
710
|
+
}
|
|
711
|
+
set optionId(id) {
|
|
712
|
+
this._id = id;
|
|
713
|
+
this.optionProxy.id = this._id;
|
|
714
|
+
}
|
|
715
|
+
get value() {
|
|
716
|
+
return this._value;
|
|
717
|
+
}
|
|
718
|
+
set value(value) {
|
|
719
|
+
this._value = value;
|
|
720
|
+
this.optionProxy.value = value;
|
|
721
|
+
}
|
|
722
|
+
get selected() {
|
|
723
|
+
return (this.optionSelectionService.selectionModel && this.optionSelectionService.selectionModel.containsItem(this.value));
|
|
724
|
+
}
|
|
725
|
+
get focusClass() {
|
|
726
|
+
return this.focusHandler.pseudoFocus.containsItem(this.optionProxy);
|
|
727
|
+
}
|
|
728
|
+
ngOnInit() {
|
|
729
|
+
if (!this._id) {
|
|
730
|
+
this._id = 'clr-option-' + uniqueIdFactory();
|
|
731
|
+
this.optionProxy.id = this._id;
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
onClick(event) {
|
|
735
|
+
event.stopPropagation();
|
|
736
|
+
if (this.optionSelectionService.multiselectable) {
|
|
737
|
+
this.optionSelectionService.toggle(this.value);
|
|
738
|
+
}
|
|
739
|
+
else {
|
|
740
|
+
this.optionSelectionService.select(this.value);
|
|
741
|
+
}
|
|
742
|
+
// As the popover stays open in multi-select mode now, we have to take focus back to the input
|
|
743
|
+
// This way we achieve two things:
|
|
744
|
+
// - do not lose focus
|
|
745
|
+
// - we're still able to use onBlur for "outside-click" handling
|
|
746
|
+
this.focusHandler.focusOption(this.optionProxy);
|
|
747
|
+
this.focusHandler.focusInput();
|
|
748
|
+
}
|
|
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 }); }
|
|
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: `
|
|
751
|
+
<ng-content></ng-content>
|
|
752
|
+
@if (selected) {
|
|
753
|
+
<span class="clr-sr-only">{{ commonStrings.keys.comboboxSelected }}</span>
|
|
754
|
+
}
|
|
755
|
+
`, isInline: true }); }
|
|
756
|
+
}
|
|
757
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ClrOption, decorators: [{
|
|
758
|
+
type: Component,
|
|
759
|
+
args: [{
|
|
760
|
+
selector: 'clr-option',
|
|
761
|
+
template: `
|
|
762
|
+
<ng-content></ng-content>
|
|
763
|
+
@if (selected) {
|
|
764
|
+
<span class="clr-sr-only">{{ commonStrings.keys.comboboxSelected }}</span>
|
|
765
|
+
}
|
|
766
|
+
`,
|
|
767
|
+
host: {
|
|
768
|
+
'[class.clr-combobox-option]': 'true',
|
|
769
|
+
'[attr.role]': '"option"',
|
|
770
|
+
// Do not remove. Or click-selection will not work.
|
|
771
|
+
'[attr.tabindex]': '-1',
|
|
772
|
+
'[attr.id]': 'optionId',
|
|
773
|
+
},
|
|
774
|
+
standalone: false,
|
|
775
|
+
}]
|
|
776
|
+
}], ctorParameters: () => [{ type: i0.ElementRef }, { type: i3.ClrCommonStringsService }, { type: ComboboxFocusHandler }, { type: OptionSelectionService }], propDecorators: { optionId: [{
|
|
777
|
+
type: Input,
|
|
778
|
+
args: ['id']
|
|
779
|
+
}], value: [{
|
|
780
|
+
type: Input,
|
|
781
|
+
args: ['clrValue']
|
|
782
|
+
}], selected: [{
|
|
783
|
+
type: HostBinding,
|
|
784
|
+
args: ['class.active']
|
|
785
|
+
}], focusClass: [{
|
|
786
|
+
type: HostBinding,
|
|
787
|
+
args: ['class.clr-focus']
|
|
788
|
+
}], onClick: [{
|
|
789
|
+
type: HostListener,
|
|
790
|
+
args: ['click', ['$event']]
|
|
791
|
+
}] } });
|
|
792
|
+
|
|
793
|
+
/*
|
|
794
|
+
* Copyright (c) 2016-2026 Broadcom. All Rights Reserved.
|
|
795
|
+
* The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
|
|
796
|
+
* This software is released under MIT license.
|
|
797
|
+
* The full license information can be found in LICENSE in the root directory of this project.
|
|
798
|
+
*/
|
|
799
|
+
let nbOptionsComponents = 0;
|
|
800
|
+
const SELECT_ALL_ID = 'select-all-id';
|
|
801
|
+
class ClrOptions {
|
|
802
|
+
constructor(optionSelectionService, id, el, commonStrings, focusHandler, popoverService, parentHost, document) {
|
|
803
|
+
this.optionSelectionService = optionSelectionService;
|
|
804
|
+
this.id = id;
|
|
805
|
+
this.el = el;
|
|
806
|
+
this.commonStrings = commonStrings;
|
|
807
|
+
this.focusHandler = focusHandler;
|
|
808
|
+
this.popoverService = popoverService;
|
|
809
|
+
this.document = document;
|
|
810
|
+
this.loading = false;
|
|
811
|
+
this.subscriptions = [];
|
|
812
|
+
if (!parentHost) {
|
|
813
|
+
throw new Error('clr-options should only be used inside of a clr-combobox');
|
|
814
|
+
}
|
|
815
|
+
if (!this.optionsId) {
|
|
816
|
+
this.optionsId = 'clr-options-' + nbOptionsComponents++;
|
|
817
|
+
}
|
|
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
|
+
}
|
|
829
|
+
get items() {
|
|
830
|
+
return this._items;
|
|
831
|
+
}
|
|
832
|
+
set items(items) {
|
|
833
|
+
this._items = items;
|
|
834
|
+
this.updateFocusableItems();
|
|
835
|
+
}
|
|
836
|
+
/**
|
|
837
|
+
* Tests if the list of options is empty, meaning it doesn't contain any items
|
|
838
|
+
*/
|
|
839
|
+
get emptyOptions() {
|
|
840
|
+
return !this.optionSelectionService.loading && this.items.length === 0;
|
|
841
|
+
}
|
|
842
|
+
get editable() {
|
|
843
|
+
return this.optionSelectionService.editable;
|
|
844
|
+
}
|
|
845
|
+
get noResultsElementId() {
|
|
846
|
+
return `${this.optionsId}-no-results`;
|
|
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
|
+
}
|
|
876
|
+
ngAfterViewInit() {
|
|
877
|
+
this.focusHandler.listbox = this.el.nativeElement;
|
|
878
|
+
this.subscriptions.push(this.items.changes.subscribe(items => {
|
|
879
|
+
if (items.length) {
|
|
880
|
+
setTimeout(() => {
|
|
881
|
+
this.focusHandler.focusFirstActive();
|
|
882
|
+
});
|
|
883
|
+
}
|
|
884
|
+
else {
|
|
885
|
+
this.focusHandler.pseudoFocus.pop();
|
|
886
|
+
}
|
|
887
|
+
}), this.optionSelectionService.selectAllRequested.subscribe(() => {
|
|
888
|
+
this.toggleSelectAll();
|
|
889
|
+
}));
|
|
890
|
+
}
|
|
891
|
+
ngOnDestroy() {
|
|
892
|
+
this.subscriptions.forEach(sub => sub.unsubscribe());
|
|
893
|
+
}
|
|
894
|
+
searchText(input) {
|
|
895
|
+
return this.commonStrings.parse(this.commonStrings.keys.comboboxSearching, { INPUT: input });
|
|
896
|
+
}
|
|
897
|
+
loadingStateChange(state) {
|
|
898
|
+
this.loading = state === ClrLoadingState.LOADING;
|
|
899
|
+
}
|
|
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: `
|
|
913
|
+
@if (optionSelectionService.loading) {
|
|
914
|
+
<div class="clr-combobox-options-loading">
|
|
915
|
+
<clr-spinner clrInline>
|
|
916
|
+
{{ commonStrings.keys.loading }}
|
|
917
|
+
</clr-spinner>
|
|
918
|
+
<span class="clr-combobox-options-text">
|
|
919
|
+
{{ searchText(optionSelectionService.currentInput) }}
|
|
920
|
+
</span>
|
|
921
|
+
</div>
|
|
922
|
+
}
|
|
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
|
+
|
|
939
|
+
<!-- Rendered if data set is empty -->
|
|
940
|
+
@if (emptyOptions) {
|
|
941
|
+
<div [id]="noResultsElementId" role="option">
|
|
942
|
+
<span class="clr-combobox-options-empty-text">
|
|
943
|
+
{{ commonStrings.keys.comboboxNoResults }}
|
|
944
|
+
</span>
|
|
945
|
+
</div>
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
<!--Option Groups and Options will be projected here-->
|
|
949
|
+
<ng-content></ng-content>
|
|
950
|
+
`, isInline: true, dependencies: [{ kind: "component", type: i5.ClrSpinner, selector: "clr-spinner", inputs: ["clrInline", "clrInverse", "clrSmall", "clrMedium"] }] }); }
|
|
951
|
+
}
|
|
952
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ClrOptions, decorators: [{
|
|
953
|
+
type: Component,
|
|
954
|
+
args: [{
|
|
955
|
+
selector: 'clr-options',
|
|
956
|
+
template: `
|
|
957
|
+
@if (optionSelectionService.loading) {
|
|
958
|
+
<div class="clr-combobox-options-loading">
|
|
959
|
+
<clr-spinner clrInline>
|
|
960
|
+
{{ commonStrings.keys.loading }}
|
|
961
|
+
</clr-spinner>
|
|
962
|
+
<span class="clr-combobox-options-text">
|
|
963
|
+
{{ searchText(optionSelectionService.currentInput) }}
|
|
964
|
+
</span>
|
|
965
|
+
</div>
|
|
966
|
+
}
|
|
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
|
+
|
|
983
|
+
<!-- Rendered if data set is empty -->
|
|
984
|
+
@if (emptyOptions) {
|
|
985
|
+
<div [id]="noResultsElementId" role="option">
|
|
986
|
+
<span class="clr-combobox-options-empty-text">
|
|
987
|
+
{{ commonStrings.keys.comboboxNoResults }}
|
|
988
|
+
</span>
|
|
989
|
+
</div>
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
<!--Option Groups and Options will be projected here-->
|
|
993
|
+
<ng-content></ng-content>
|
|
994
|
+
`,
|
|
995
|
+
providers: [{ provide: LoadingListener, useExisting: ClrOptions }],
|
|
996
|
+
host: {
|
|
997
|
+
'[class.clr-combobox-options]': 'true',
|
|
998
|
+
'[class.clr-combobox-options-multi]': 'optionSelectionService.multiselectable',
|
|
999
|
+
'[class.clr-combobox-options-hidden]': 'emptyOptions && editable',
|
|
1000
|
+
'[attr.role]': '"listbox"',
|
|
1001
|
+
'[id]': 'optionsId',
|
|
1002
|
+
},
|
|
1003
|
+
standalone: false,
|
|
1004
|
+
}]
|
|
1005
|
+
}], ctorParameters: () => [{ type: OptionSelectionService }, { type: undefined, decorators: [{
|
|
1006
|
+
type: Inject,
|
|
1007
|
+
args: [IF_ACTIVE_ID]
|
|
1008
|
+
}] }, { type: i0.ElementRef }, { type: i3.ClrCommonStringsService }, { type: ComboboxFocusHandler }, { type: i4.ClrPopoverService }, { type: i0.ElementRef, decorators: [{
|
|
1009
|
+
type: Optional
|
|
1010
|
+
}, {
|
|
1011
|
+
type: Inject,
|
|
1012
|
+
args: [POPOVER_HOST_ORIGIN]
|
|
1013
|
+
}] }, { type: undefined, decorators: [{
|
|
1014
|
+
type: Inject,
|
|
1015
|
+
args: [DOCUMENT]
|
|
1016
|
+
}] }], propDecorators: { optionsId: [{
|
|
1017
|
+
type: Input,
|
|
1018
|
+
args: ['id']
|
|
1019
|
+
}], selectAllBtn: [{
|
|
1020
|
+
type: ViewChild,
|
|
1021
|
+
args: ['selectAllBtn']
|
|
1022
|
+
}], items: [{
|
|
1023
|
+
type: ContentChildren,
|
|
1024
|
+
args: [ClrOption, { descendants: true }]
|
|
1025
|
+
}] } });
|
|
1026
|
+
|
|
1027
|
+
/*
|
|
1028
|
+
* Copyright (c) 2016-2026 Broadcom. All Rights Reserved.
|
|
1029
|
+
* The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
|
|
1030
|
+
* This software is released under MIT license.
|
|
1031
|
+
* The full license information can be found in LICENSE in the root directory of this project.
|
|
1032
|
+
*/
|
|
1033
|
+
class ClrCombobox extends WrappedFormControl {
|
|
1034
|
+
constructor(vcr, injector, control, renderer, el, optionSelectionService, commonStrings, popoverService, containerService, platformId, focusHandler, cdr, zone, container) {
|
|
1035
|
+
super(vcr, ClrComboboxContainer, injector, control, renderer, el);
|
|
1036
|
+
this.control = control;
|
|
1037
|
+
this.renderer = renderer;
|
|
1038
|
+
this.el = el;
|
|
1039
|
+
this.optionSelectionService = optionSelectionService;
|
|
1040
|
+
this.commonStrings = commonStrings;
|
|
1041
|
+
this.popoverService = popoverService;
|
|
1042
|
+
this.containerService = containerService;
|
|
1043
|
+
this.platformId = platformId;
|
|
1044
|
+
this.focusHandler = focusHandler;
|
|
1045
|
+
this.cdr = cdr;
|
|
1046
|
+
this.zone = zone;
|
|
1047
|
+
this.container = container;
|
|
1048
|
+
this.placeholder = '';
|
|
1049
|
+
this.clrInputChange = new EventEmitter(false);
|
|
1050
|
+
this.clrOpenChange = this.popoverService.openChange;
|
|
1051
|
+
/**
|
|
1052
|
+
* This output should be used to set up a live region using aria-live and populate it with updates that reflect each combobox change.
|
|
1053
|
+
*/
|
|
1054
|
+
this.clrSelectionChange = this.optionSelectionService.selectionChanged;
|
|
1055
|
+
this.focused = false;
|
|
1056
|
+
this.popoverPosition = ClrPopoverPosition.BOTTOM_LEFT;
|
|
1057
|
+
this.index = 1;
|
|
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();
|
|
1064
|
+
this._searchText = '';
|
|
1065
|
+
if (control) {
|
|
1066
|
+
control.valueAccessor = this;
|
|
1067
|
+
}
|
|
1068
|
+
// default to SingleSelectComboboxModel, in case the optional input [ClrMulti] isn't used
|
|
1069
|
+
this.multiSelect = false;
|
|
1070
|
+
}
|
|
1071
|
+
get showSelectAll() {
|
|
1072
|
+
return this.optionSelectionService.showSelectAll;
|
|
1073
|
+
}
|
|
1074
|
+
set showSelectAll(value) {
|
|
1075
|
+
this.optionSelectionService.showSelectAll = value;
|
|
1076
|
+
}
|
|
1077
|
+
get editable() {
|
|
1078
|
+
return this.optionSelectionService.editable;
|
|
1079
|
+
}
|
|
1080
|
+
set editable(value) {
|
|
1081
|
+
this.optionSelectionService.editable = value;
|
|
1082
|
+
}
|
|
1083
|
+
set editableResolver(value) {
|
|
1084
|
+
this.optionSelectionService.editableResolver = value;
|
|
1085
|
+
}
|
|
1086
|
+
set identityFn(value) {
|
|
1087
|
+
this.optionSelectionService.identityFn = value;
|
|
1088
|
+
}
|
|
1089
|
+
get multiSelect() {
|
|
1090
|
+
return this.optionSelectionService.multiselectable;
|
|
1091
|
+
}
|
|
1092
|
+
set multiSelect(value) {
|
|
1093
|
+
if (value) {
|
|
1094
|
+
this.optionSelectionService.selectionModel = new MultiSelectComboboxModel();
|
|
1095
|
+
}
|
|
1096
|
+
else {
|
|
1097
|
+
// in theory, setting this again should not cause errors even though we already set it in constructor,
|
|
1098
|
+
// since the initial call to writeValue (caused by [ngModel] input) should happen after this
|
|
1099
|
+
this.optionSelectionService.selectionModel = new SingleSelectComboboxModel();
|
|
1100
|
+
}
|
|
1101
|
+
this.optionSelectionService.selectionModel.identityFn = this.optionSelectionService.identityFn;
|
|
1102
|
+
this.updateControlValue();
|
|
1103
|
+
}
|
|
1104
|
+
// Override the id of WrappedFormControl, as we want to move it to the embedded input.
|
|
1105
|
+
// Otherwise, the label/component connection does not work and screen readers do not read the label.
|
|
1106
|
+
get id() {
|
|
1107
|
+
return this.controlIdService.id + '-combobox';
|
|
1108
|
+
}
|
|
1109
|
+
set id(id) {
|
|
1110
|
+
super.id = id;
|
|
1111
|
+
}
|
|
1112
|
+
get searchText() {
|
|
1113
|
+
return this._searchText;
|
|
1114
|
+
}
|
|
1115
|
+
set searchText(text) {
|
|
1116
|
+
// if input text has changed since last time, fire a change event so application can react to it
|
|
1117
|
+
if (text !== this._searchText) {
|
|
1118
|
+
if (this.popoverService.open) {
|
|
1119
|
+
this.optionSelectionService.showAllOptions = false;
|
|
1120
|
+
}
|
|
1121
|
+
this._searchText = text;
|
|
1122
|
+
this.clrInputChange.emit(this.searchText);
|
|
1123
|
+
}
|
|
1124
|
+
// We need to trigger this even if unchanged, so the option-items directive will update its list
|
|
1125
|
+
// based on the "showAllOptions" variable which may have changed in the openChange subscription below.
|
|
1126
|
+
// The option-items directive does not listen to openChange, but it listens to currentInput changes.
|
|
1127
|
+
this.optionSelectionService.currentInput = this.searchText;
|
|
1128
|
+
}
|
|
1129
|
+
get openState() {
|
|
1130
|
+
return this.popoverService.open;
|
|
1131
|
+
}
|
|
1132
|
+
get multiSelectModel() {
|
|
1133
|
+
if (!this.multiSelect) {
|
|
1134
|
+
throw Error('multiSelectModel is not available in single selection context');
|
|
1135
|
+
}
|
|
1136
|
+
return this.optionSelectionService.selectionModel.model;
|
|
1137
|
+
}
|
|
1138
|
+
get ariaControls() {
|
|
1139
|
+
return this.options?.optionsId;
|
|
1140
|
+
}
|
|
1141
|
+
get ariaOwns() {
|
|
1142
|
+
return this.options?.optionsId;
|
|
1143
|
+
}
|
|
1144
|
+
get ariaDescribedBySelection() {
|
|
1145
|
+
return 'selection-' + this.id;
|
|
1146
|
+
}
|
|
1147
|
+
get displayField() {
|
|
1148
|
+
return this.optionSelectionService.displayField;
|
|
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
|
+
}
|
|
1168
|
+
get disabled() {
|
|
1169
|
+
return this.control?.disabled;
|
|
1170
|
+
}
|
|
1171
|
+
ngAfterContentInit() {
|
|
1172
|
+
this.initializeSubscriptions();
|
|
1173
|
+
// Initialize with preselected value
|
|
1174
|
+
if (!this.optionSelectionService.selectionModel.isEmpty()) {
|
|
1175
|
+
this.updateInputValue(this.optionSelectionService.selectionModel);
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
ngAfterViewInit() {
|
|
1179
|
+
this.focusHandler.textInput = this.textbox.nativeElement;
|
|
1180
|
+
this.focusHandler.trigger = this.trigger.nativeElement;
|
|
1181
|
+
// The text input is the actual element we are wrapping
|
|
1182
|
+
// This assignment is needed by the wrapper, so it can set
|
|
1183
|
+
// the aria properties on the input element, not on the component.
|
|
1184
|
+
// We calculate on the initial load to prevent flickering
|
|
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([]);
|
|
1203
|
+
}
|
|
1204
|
+
onKeyUp(event) {
|
|
1205
|
+
// if BACKSPACE in multiselect mode, delete the last pill if text is empty
|
|
1206
|
+
if (this.multiSelect) {
|
|
1207
|
+
const multiModel = this.optionSelectionService.selectionModel.model;
|
|
1208
|
+
switch (event.key) {
|
|
1209
|
+
case Keys.Backspace:
|
|
1210
|
+
if (!this._searchText.length) {
|
|
1211
|
+
if (multiModel && multiModel.length > 0) {
|
|
1212
|
+
const lastItem = multiModel[multiModel.length - 1];
|
|
1213
|
+
this.control?.control.markAsTouched();
|
|
1214
|
+
this.optionSelectionService.unselect(lastItem);
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
break;
|
|
1218
|
+
case Keys.Enter:
|
|
1219
|
+
if (this.editable && this._searchText.length > 0 && this.options.emptyOptions) {
|
|
1220
|
+
const parsedInput = this.optionSelectionService.editableResolver(this._searchText);
|
|
1221
|
+
this.control?.control.markAsTouched();
|
|
1222
|
+
this.optionSelectionService.select(parsedInput);
|
|
1223
|
+
this.searchText = '';
|
|
1224
|
+
}
|
|
1225
|
+
break;
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
inputId() {
|
|
1230
|
+
return this.controlIdService.id;
|
|
1231
|
+
}
|
|
1232
|
+
loadingStateChange(state) {
|
|
1233
|
+
this.optionSelectionService.loading = state === ClrLoadingState.LOADING;
|
|
1234
|
+
if (state !== ClrLoadingState.LOADING && isPlatformBrowser(this.platformId)) {
|
|
1235
|
+
setTimeout(() => {
|
|
1236
|
+
this.popoverService?.resetPositions();
|
|
1237
|
+
});
|
|
1238
|
+
this.focusFirstActive();
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
unselect(item) {
|
|
1242
|
+
if (!this.disabled) {
|
|
1243
|
+
this.optionSelectionService.unselect(item);
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
onBlur(event) {
|
|
1247
|
+
if (!event.relatedTarget || !this.options.el?.nativeElement.contains(event.relatedTarget)) {
|
|
1248
|
+
this.onTouchedCallback?.();
|
|
1249
|
+
this.triggerValidation();
|
|
1250
|
+
this.focused = false;
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
onFocus() {
|
|
1254
|
+
this.focused = true;
|
|
1255
|
+
// fix for "expression changed" error when focus is returned to a combobox after a modal is closed
|
|
1256
|
+
// https://github.com/vmware-clarity/ng-clarity/issues/663
|
|
1257
|
+
this.cdr.detectChanges();
|
|
1258
|
+
}
|
|
1259
|
+
onChange() {
|
|
1260
|
+
if (this.editable && !this.multiSelect && this.options.emptyOptions) {
|
|
1261
|
+
const parsedInput = this.optionSelectionService.editableResolver(this._searchText);
|
|
1262
|
+
this.optionSelectionService.setSelectionValue(parsedInput);
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
getSelectionAriaLabel() {
|
|
1266
|
+
if (this.containerService && this.containerService.labelText) {
|
|
1267
|
+
return `${this.containerService.labelText} ${this.commonStrings.keys.comboboxSelection}`;
|
|
1268
|
+
}
|
|
1269
|
+
return this.commonStrings.keys.comboboxSelection;
|
|
1270
|
+
}
|
|
1271
|
+
focusFirstActive() {
|
|
1272
|
+
setTimeout(() => {
|
|
1273
|
+
this.focusHandler.focusFirstActive();
|
|
1274
|
+
});
|
|
1275
|
+
}
|
|
1276
|
+
writeValue(value) {
|
|
1277
|
+
this.optionSelectionService.selectionModel.model = value;
|
|
1278
|
+
this.updateInputValue(this.optionSelectionService.selectionModel);
|
|
1279
|
+
}
|
|
1280
|
+
registerOnTouched(onTouched) {
|
|
1281
|
+
this.onTouchedCallback = onTouched;
|
|
1282
|
+
}
|
|
1283
|
+
registerOnChange(onChange) {
|
|
1284
|
+
this.onChangeCallback = onChange;
|
|
1285
|
+
}
|
|
1286
|
+
getActiveDescendant() {
|
|
1287
|
+
const model = this.focusHandler.pseudoFocus.model;
|
|
1288
|
+
return model ? model.id : this.options?.noResultsElementId;
|
|
1289
|
+
}
|
|
1290
|
+
setDisabledState() {
|
|
1291
|
+
// do nothing
|
|
1292
|
+
}
|
|
1293
|
+
onWrapperClick(event) {
|
|
1294
|
+
if (this.disabled) {
|
|
1295
|
+
return;
|
|
1296
|
+
}
|
|
1297
|
+
this.focusHandler.focusInput();
|
|
1298
|
+
if (this.editable || (!this.editable && this.trigger.nativeElement.contains(event.target))) {
|
|
1299
|
+
this.popoverService.toggleWithEvent(event);
|
|
1300
|
+
}
|
|
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
|
+
}
|
|
1379
|
+
initializeSubscriptions() {
|
|
1380
|
+
this.subscriptions.push(this.optionSelectionService.selectionChanged.subscribe((newSelection) => {
|
|
1381
|
+
this.updateInputValue(newSelection);
|
|
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();
|
|
1391
|
+
}
|
|
1392
|
+
this.updateControlValue();
|
|
1393
|
+
if (this.selectionExpanded) {
|
|
1394
|
+
this.applyLimit(this.multiSelectModel.length);
|
|
1395
|
+
}
|
|
1396
|
+
else {
|
|
1397
|
+
this.calculateLimit();
|
|
1398
|
+
}
|
|
1399
|
+
if (this.multiSelect) {
|
|
1400
|
+
setTimeout(() => {
|
|
1401
|
+
this.popoverService?.updatePosition();
|
|
1402
|
+
});
|
|
1403
|
+
}
|
|
1404
|
+
}));
|
|
1405
|
+
this.subscriptions.push(this.popoverService.openChange.subscribe(open => {
|
|
1406
|
+
if (this.editable && !this.multiSelect) {
|
|
1407
|
+
if (this.searchText) {
|
|
1408
|
+
this.optionSelectionService.showAllOptions = false;
|
|
1409
|
+
this.optionSelectionService.currentInput = this.searchText;
|
|
1410
|
+
}
|
|
1411
|
+
return;
|
|
1412
|
+
}
|
|
1413
|
+
if (open) {
|
|
1414
|
+
this.focusFirstActive();
|
|
1415
|
+
}
|
|
1416
|
+
else {
|
|
1417
|
+
this.optionSelectionService.showAllOptions = true;
|
|
1418
|
+
}
|
|
1419
|
+
if (this.multiSelect) {
|
|
1420
|
+
this.searchText = '';
|
|
1421
|
+
}
|
|
1422
|
+
else {
|
|
1423
|
+
this.searchText = this.getDisplayNames(this.optionSelectionService.selectionModel.model)[0] || '';
|
|
1424
|
+
}
|
|
1425
|
+
}), this.containerWidthChange.pipe(debounceTime(0)).subscribe(() => {
|
|
1426
|
+
if (!this.selectionExpanded && !this.isTotalSelection) {
|
|
1427
|
+
this.calculateLimit();
|
|
1428
|
+
}
|
|
1429
|
+
}));
|
|
1430
|
+
}
|
|
1431
|
+
updateInputValue(model) {
|
|
1432
|
+
if (!this.multiSelect) {
|
|
1433
|
+
this.searchText = model.model ? this.getDisplayNames(model.model)[0] : '';
|
|
1434
|
+
if (this.searchText) {
|
|
1435
|
+
this.optionSelectionService.currentInput = this.searchText;
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1439
|
+
updateControlValue() {
|
|
1440
|
+
if (this.onChangeCallback) {
|
|
1441
|
+
this.onChangeCallback(this.optionSelectionService.selectionModel.model);
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
getDisplayNames(model) {
|
|
1445
|
+
if (this.displayField) {
|
|
1446
|
+
if (!Array.isArray(model)) {
|
|
1447
|
+
model = [model];
|
|
1448
|
+
}
|
|
1449
|
+
return model.map(item => (item ? item[this.displayField] : null));
|
|
1450
|
+
}
|
|
1451
|
+
return [this.optionSelectionService.selectionModel.model];
|
|
1452
|
+
}
|
|
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: [
|
|
1455
|
+
OptionSelectionService,
|
|
1456
|
+
{ provide: LoadingListener, useExisting: ClrCombobox },
|
|
1457
|
+
IF_ACTIVE_ID_PROVIDER,
|
|
1458
|
+
FOCUS_SERVICE_PROVIDER,
|
|
1459
|
+
COMBOBOX_FOCUS_HANDLER_PROVIDER,
|
|
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" }] }); }
|
|
1461
|
+
}
|
|
1462
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ClrCombobox, decorators: [{
|
|
1463
|
+
type: Component,
|
|
1464
|
+
args: [{ selector: 'clr-combobox', providers: [
|
|
1465
|
+
OptionSelectionService,
|
|
1466
|
+
{ provide: LoadingListener, useExisting: ClrCombobox },
|
|
1467
|
+
IF_ACTIVE_ID_PROVIDER,
|
|
1468
|
+
FOCUS_SERVICE_PROVIDER,
|
|
1469
|
+
COMBOBOX_FOCUS_HANDLER_PROVIDER,
|
|
1470
|
+
], hostDirectives: [ClrPopoverHostDirective], host: {
|
|
1471
|
+
'[class.aria-required]': 'true',
|
|
1472
|
+
'[class.clr-combobox]': 'true',
|
|
1473
|
+
'[class.clr-combobox-disabled]': 'control?.disabled',
|
|
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" }]
|
|
1475
|
+
}], ctorParameters: () => [{ type: i0.ViewContainerRef }, { type: i0.Injector }, { type: i1$1.NgControl, decorators: [{
|
|
1476
|
+
type: Self
|
|
1477
|
+
}, {
|
|
1478
|
+
type: Optional
|
|
1479
|
+
}] }, { type: i0.Renderer2 }, { type: i0.ElementRef }, { type: OptionSelectionService }, { type: i3.ClrCommonStringsService }, { type: i4.ClrPopoverService }, { type: ComboboxContainerService, decorators: [{
|
|
1480
|
+
type: Optional
|
|
1481
|
+
}] }, { type: undefined, decorators: [{
|
|
1482
|
+
type: Inject,
|
|
1483
|
+
args: [PLATFORM_ID]
|
|
1484
|
+
}] }, { type: ComboboxFocusHandler }, { type: i0.ChangeDetectorRef }, { type: i0.NgZone }, { type: ClrComboboxContainer, decorators: [{
|
|
1485
|
+
type: Optional
|
|
1486
|
+
}, {
|
|
1487
|
+
type: Host
|
|
1488
|
+
}] }], propDecorators: { placeholder: [{
|
|
1489
|
+
type: Input,
|
|
1490
|
+
args: ['placeholder']
|
|
1491
|
+
}], clrInputChange: [{
|
|
1492
|
+
type: Output,
|
|
1493
|
+
args: ['clrInputChange']
|
|
1494
|
+
}], clrOpenChange: [{
|
|
1495
|
+
type: Output,
|
|
1496
|
+
args: ['clrOpenChange']
|
|
1497
|
+
}], clrSelectionChange: [{
|
|
1498
|
+
type: Output,
|
|
1499
|
+
args: ['clrSelectionChange']
|
|
1500
|
+
}], textbox: [{
|
|
1501
|
+
type: ViewChild,
|
|
1502
|
+
args: ['textboxInput']
|
|
1503
|
+
}], trigger: [{
|
|
1504
|
+
type: ViewChild,
|
|
1505
|
+
args: ['trigger']
|
|
1506
|
+
}], optionSelected: [{
|
|
1507
|
+
type: ContentChild,
|
|
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']
|
|
1518
|
+
}], options: [{
|
|
1519
|
+
type: ContentChild,
|
|
1520
|
+
args: [ClrOptions]
|
|
1521
|
+
}], showSelectAll: [{
|
|
1522
|
+
type: Input,
|
|
1523
|
+
args: [{ alias: 'showSelectAll', transform: booleanAttribute }]
|
|
1524
|
+
}], editable: [{
|
|
1525
|
+
type: Input,
|
|
1526
|
+
args: ['clrEditable']
|
|
1527
|
+
}], editableResolver: [{
|
|
1528
|
+
type: Input,
|
|
1529
|
+
args: ['clrEditableResolverFn']
|
|
1530
|
+
}], identityFn: [{
|
|
1531
|
+
type: Input,
|
|
1532
|
+
args: ['clrComboboxIdentityFn']
|
|
1533
|
+
}], multiSelect: [{
|
|
1534
|
+
type: Input,
|
|
1535
|
+
args: ['clrMulti']
|
|
1536
|
+
}], onKeyUp: [{
|
|
1537
|
+
type: HostListener,
|
|
1538
|
+
args: ['keydown', ['$event']]
|
|
1539
|
+
}] } });
|
|
1540
|
+
|
|
1541
|
+
/*
|
|
1542
|
+
* Copyright (c) 2016-2026 Broadcom. All Rights Reserved.
|
|
1543
|
+
* The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
|
|
1544
|
+
* This software is released under MIT license.
|
|
1545
|
+
* The full license information can be found in LICENSE in the root directory of this project.
|
|
1546
|
+
*/
|
|
1547
|
+
class ClrOptionItems {
|
|
1548
|
+
constructor(template, differs, optionService, vcr) {
|
|
1549
|
+
this.template = template;
|
|
1550
|
+
this.differs = differs;
|
|
1551
|
+
this.optionService = optionService;
|
|
1552
|
+
this.subscriptions = [];
|
|
1553
|
+
this.filter = '';
|
|
1554
|
+
this.differ = null;
|
|
1555
|
+
this.iterableProxy = new NgForOf(vcr, template, differs);
|
|
1556
|
+
this.subscriptions.push(optionService.inputChanged.subscribe(filter => {
|
|
1557
|
+
this.filter = filter;
|
|
1558
|
+
this.updateItems();
|
|
1559
|
+
}));
|
|
1560
|
+
}
|
|
1561
|
+
set rawItems(items) {
|
|
1562
|
+
this._rawItems = items ? items : [];
|
|
1563
|
+
this.updateItems();
|
|
1564
|
+
}
|
|
1565
|
+
set trackBy(value) {
|
|
1566
|
+
this.iterableProxy.ngForTrackBy = value;
|
|
1567
|
+
}
|
|
1568
|
+
set field(field) {
|
|
1569
|
+
this._filterField = field;
|
|
1570
|
+
this.optionService.displayField = field;
|
|
1571
|
+
}
|
|
1572
|
+
get hasResults() {
|
|
1573
|
+
// explicity return `undefined` instead of `false` if the answer is not known
|
|
1574
|
+
return this.filteredItems ? this.filteredItems.length : undefined;
|
|
1575
|
+
}
|
|
1576
|
+
ngDoCheck() {
|
|
1577
|
+
if (!this.differ) {
|
|
1578
|
+
this.differ = this.differs.find(this.filteredItems).create(this.iterableProxy.ngForTrackBy);
|
|
1579
|
+
}
|
|
1580
|
+
if (this.differ) {
|
|
1581
|
+
const changes = this.differ.diff(this.filteredItems);
|
|
1582
|
+
if (changes) {
|
|
1583
|
+
this.iterableProxy.ngDoCheck();
|
|
1584
|
+
}
|
|
1585
|
+
}
|
|
1586
|
+
}
|
|
1587
|
+
ngOnDestroy() {
|
|
1588
|
+
this.subscriptions.forEach(sub => sub.unsubscribe());
|
|
1589
|
+
}
|
|
1590
|
+
updateItems() {
|
|
1591
|
+
if (!this._rawItems || this.filter === undefined || this.filter === null) {
|
|
1592
|
+
return;
|
|
1593
|
+
}
|
|
1594
|
+
const normalizedFilterValue = normalizeValue(this.filter);
|
|
1595
|
+
if (this.optionService.showAllOptions) {
|
|
1596
|
+
this.filteredItems = this._rawItems;
|
|
1597
|
+
}
|
|
1598
|
+
else if (this._filterField) {
|
|
1599
|
+
this.filteredItems = this._rawItems.filter(item => {
|
|
1600
|
+
const objValue = item[this._filterField];
|
|
1601
|
+
return objValue ? normalizeValue(objValue).includes(normalizedFilterValue) : false;
|
|
1602
|
+
});
|
|
1603
|
+
}
|
|
1604
|
+
else {
|
|
1605
|
+
// Filter by all item object values
|
|
1606
|
+
this.filteredItems = this._rawItems.filter(item => {
|
|
1607
|
+
if (typeof item !== 'object') {
|
|
1608
|
+
return normalizeValue(item).includes(normalizedFilterValue);
|
|
1609
|
+
}
|
|
1610
|
+
const objValues = Object.values(item).filter(value => {
|
|
1611
|
+
return value !== null && value !== undefined ? normalizeValue(value).includes(normalizedFilterValue) : false;
|
|
1612
|
+
});
|
|
1613
|
+
return objValues.length > 0;
|
|
1614
|
+
});
|
|
1615
|
+
}
|
|
1616
|
+
this.iterableProxy.ngForOf = this.filteredItems;
|
|
1617
|
+
}
|
|
1618
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ClrOptionItems, deps: [{ token: i0.TemplateRef }, { token: i0.IterableDiffers }, { token: OptionSelectionService }, { token: i0.ViewContainerRef }], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
1619
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.1.3", type: ClrOptionItems, isStandalone: false, selector: "[clrOptionItems][clrOptionItemsOf]", inputs: { rawItems: ["clrOptionItemsOf", "rawItems"], trackBy: ["clrOptionItemsTrackBy", "trackBy"], field: ["clrOptionItemsField", "field"] }, ngImport: i0 }); }
|
|
1620
|
+
}
|
|
1621
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ClrOptionItems, decorators: [{
|
|
1622
|
+
type: Directive,
|
|
1623
|
+
args: [{
|
|
1624
|
+
selector: '[clrOptionItems][clrOptionItemsOf]',
|
|
1625
|
+
standalone: false,
|
|
1626
|
+
}]
|
|
1627
|
+
}], ctorParameters: () => [{ type: i0.TemplateRef }, { type: i0.IterableDiffers }, { type: OptionSelectionService }, { type: i0.ViewContainerRef }], propDecorators: { rawItems: [{
|
|
1628
|
+
type: Input,
|
|
1629
|
+
args: ['clrOptionItemsOf']
|
|
1630
|
+
}], trackBy: [{
|
|
1631
|
+
type: Input,
|
|
1632
|
+
args: ['clrOptionItemsTrackBy']
|
|
1633
|
+
}], field: [{
|
|
1634
|
+
type: Input,
|
|
1635
|
+
args: ['clrOptionItemsField']
|
|
1636
|
+
}] } });
|
|
1637
|
+
function normalizeValue(value) {
|
|
1638
|
+
return value
|
|
1639
|
+
.toString()
|
|
1640
|
+
.normalize('NFD')
|
|
1641
|
+
.replace(/\p{Diacritic}/gu, '')
|
|
1642
|
+
.toLowerCase();
|
|
1643
|
+
}
|
|
1644
|
+
|
|
1645
|
+
/*
|
|
1646
|
+
* Copyright (c) 2016-2026 Broadcom. All Rights Reserved.
|
|
1647
|
+
* The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
|
|
1648
|
+
* This software is released under MIT license.
|
|
1649
|
+
* The full license information can be found in LICENSE in the root directory of this project.
|
|
1650
|
+
*/
|
|
1651
|
+
class ClrOptionGroup {
|
|
1652
|
+
constructor() {
|
|
1653
|
+
this.labelId = uniqueIdFactory();
|
|
1654
|
+
}
|
|
1655
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ClrOptionGroup, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1656
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.1.3", type: ClrOptionGroup, isStandalone: false, selector: "clr-option-group", inputs: { label: ["clrOptionGroupLabel", "label"] }, host: { properties: { "attr.role": "\"group\"", "attr.aria-labelledby": "labelId", "style.display": "clrOptionItems.hasResults ? undefined : \"none\"" } }, queries: [{ propertyName: "clrOptionItems", first: true, predicate: ClrOptionItems, descendants: true }], ngImport: i0, template: `
|
|
1657
|
+
<span [id]="labelId" class="clr-option-group-label" role="presentation">{{ label }}</span>
|
|
1658
|
+
<ng-content></ng-content>
|
|
1659
|
+
`, isInline: true }); }
|
|
1660
|
+
}
|
|
1661
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ClrOptionGroup, decorators: [{
|
|
1662
|
+
type: Component,
|
|
1663
|
+
args: [{
|
|
1664
|
+
selector: 'clr-option-group',
|
|
1665
|
+
host: {
|
|
1666
|
+
'[attr.role]': '"group"',
|
|
1667
|
+
'[attr.aria-labelledby]': 'labelId',
|
|
1668
|
+
'[style.display]': 'clrOptionItems.hasResults ? undefined : "none"',
|
|
1669
|
+
},
|
|
1670
|
+
template: `
|
|
1671
|
+
<span [id]="labelId" class="clr-option-group-label" role="presentation">{{ label }}</span>
|
|
1672
|
+
<ng-content></ng-content>
|
|
1673
|
+
`,
|
|
1674
|
+
standalone: false,
|
|
1675
|
+
}]
|
|
1676
|
+
}], propDecorators: { label: [{
|
|
1677
|
+
type: Input,
|
|
1678
|
+
args: ['clrOptionGroupLabel']
|
|
1679
|
+
}], clrOptionItems: [{
|
|
1680
|
+
type: ContentChild,
|
|
1681
|
+
args: [ClrOptionItems]
|
|
1682
|
+
}] } });
|
|
1683
|
+
|
|
1684
|
+
/*
|
|
1685
|
+
* Copyright (c) 2016-2026 Broadcom. All Rights Reserved.
|
|
1686
|
+
* The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
|
|
1687
|
+
* This software is released under MIT license.
|
|
1688
|
+
* The full license information can be found in LICENSE in the root directory of this project.
|
|
1689
|
+
*/
|
|
1690
|
+
class ClrComboboxModule {
|
|
1691
|
+
constructor() {
|
|
1692
|
+
ClarityIcons.addIcons(successStandardIcon, errorStandardIcon, angleIcon, windowCloseIcon);
|
|
1693
|
+
}
|
|
1694
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ClrComboboxModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
|
|
1695
|
+
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.1.3", ngImport: i0, type: ClrComboboxModule, declarations: [ClrCombobox,
|
|
1696
|
+
ClrComboboxContainer,
|
|
1697
|
+
ClrOptions,
|
|
1698
|
+
ClrOption,
|
|
1699
|
+
ClrOptionGroup,
|
|
1700
|
+
ClrOptionSelected,
|
|
1701
|
+
ClrOptionItems], imports: [CommonModule,
|
|
1702
|
+
FormsModule,
|
|
1703
|
+
ClrIcon,
|
|
1704
|
+
ClrKeyFocusModule,
|
|
1705
|
+
ClrCommonFormsModule,
|
|
1706
|
+
ClrConditionalModule,
|
|
1707
|
+
ClrPopoverModuleNext,
|
|
1708
|
+
ClrSpinnerModule], exports: [ClrCommonFormsModule,
|
|
1709
|
+
ClrCombobox,
|
|
1710
|
+
ClrComboboxContainer,
|
|
1711
|
+
ClrOptions,
|
|
1712
|
+
ClrOption,
|
|
1713
|
+
ClrOptionGroup,
|
|
1714
|
+
ClrOptionSelected,
|
|
1715
|
+
ClrConditionalModule,
|
|
1716
|
+
ClrOptionItems] }); }
|
|
1717
|
+
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ClrComboboxModule, imports: [CommonModule,
|
|
1718
|
+
FormsModule,
|
|
1719
|
+
ClrIcon,
|
|
1720
|
+
ClrKeyFocusModule,
|
|
1721
|
+
ClrCommonFormsModule,
|
|
1722
|
+
ClrConditionalModule,
|
|
1723
|
+
ClrPopoverModuleNext,
|
|
1724
|
+
ClrSpinnerModule, ClrCommonFormsModule,
|
|
1725
|
+
ClrConditionalModule] }); }
|
|
1726
|
+
}
|
|
1727
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ClrComboboxModule, decorators: [{
|
|
1728
|
+
type: NgModule,
|
|
1729
|
+
args: [{
|
|
1730
|
+
imports: [
|
|
1731
|
+
CommonModule,
|
|
1732
|
+
FormsModule,
|
|
1733
|
+
ClrIcon,
|
|
1734
|
+
ClrKeyFocusModule,
|
|
1735
|
+
ClrCommonFormsModule,
|
|
1736
|
+
ClrConditionalModule,
|
|
1737
|
+
ClrPopoverModuleNext,
|
|
1738
|
+
ClrSpinnerModule,
|
|
1739
|
+
],
|
|
1740
|
+
declarations: [
|
|
1741
|
+
ClrCombobox,
|
|
1742
|
+
ClrComboboxContainer,
|
|
1743
|
+
ClrOptions,
|
|
1744
|
+
ClrOption,
|
|
1745
|
+
ClrOptionGroup,
|
|
1746
|
+
ClrOptionSelected,
|
|
1747
|
+
ClrOptionItems,
|
|
1748
|
+
],
|
|
1749
|
+
exports: [
|
|
1750
|
+
ClrCommonFormsModule,
|
|
1751
|
+
ClrCombobox,
|
|
1752
|
+
ClrComboboxContainer,
|
|
1753
|
+
ClrOptions,
|
|
1754
|
+
ClrOption,
|
|
1755
|
+
ClrOptionGroup,
|
|
1756
|
+
ClrOptionSelected,
|
|
1757
|
+
ClrConditionalModule,
|
|
1758
|
+
ClrOptionItems,
|
|
1759
|
+
],
|
|
1760
|
+
}]
|
|
1761
|
+
}], ctorParameters: () => [] });
|
|
1762
|
+
|
|
1763
|
+
/*
|
|
1764
|
+
* Copyright (c) 2016-2026 Broadcom. All Rights Reserved.
|
|
1765
|
+
* The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
|
|
1766
|
+
* This software is released under MIT license.
|
|
1767
|
+
* The full license information can be found in LICENSE in the root directory of this project.
|
|
1768
|
+
*/
|
|
1769
|
+
|
|
1770
|
+
/**
|
|
1771
|
+
* Generated bundle index. Do not edit.
|
|
1772
|
+
*/
|
|
1773
|
+
|
|
1774
|
+
export { ClrCombobox, ClrComboboxContainer, ClrComboboxModule, ClrOption, ClrOptionGroup, ClrOptionItems, ClrOptionSelected, ClrOptions, SELECT_ALL_ID };
|
|
1775
|
+
//# sourceMappingURL=clr-angular-forms-combobox.mjs.map
|