@kirbydesign/extensions-angular 5.1.0 → 6.0.0-dev-67d5287
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/kirbydesign-extensions-angular-combobox.mjs +931 -852
- package/fesm2022/kirbydesign-extensions-angular-combobox.mjs.map +1 -1
- package/fesm2022/kirbydesign-extensions-angular-image-banner.mjs +206 -128
- package/fesm2022/kirbydesign-extensions-angular-image-banner.mjs.map +1 -1
- package/fesm2022/kirbydesign-extensions-angular-localization.mjs +541 -411
- package/fesm2022/kirbydesign-extensions-angular-localization.mjs.map +1 -1
- package/fesm2022/kirbydesign-extensions-angular-sidebar-menu.mjs +1464 -605
- package/fesm2022/kirbydesign-extensions-angular-sidebar-menu.mjs.map +1 -1
- package/fesm2022/kirbydesign-extensions-angular-skeleton-loader.mjs +82 -40
- package/fesm2022/kirbydesign-extensions-angular-skeleton-loader.mjs.map +1 -1
- package/fesm2022/kirbydesign-extensions-angular-spot-illustration.mjs +164 -155
- package/fesm2022/kirbydesign-extensions-angular-spot-illustration.mjs.map +1 -1
- package/fesm2022/kirbydesign-extensions-angular.mjs +1 -8
- package/fesm2022/kirbydesign-extensions-angular.mjs.map +1 -1
- package/package.json +13 -13
- package/types/kirbydesign-extensions-angular-combobox.d.ts +276 -215
- package/types/kirbydesign-extensions-angular-image-banner.d.ts +102 -66
- package/types/kirbydesign-extensions-angular-localization.d.ts +185 -204
- package/types/kirbydesign-extensions-angular-sidebar-menu.d.ts +137 -120
- package/types/kirbydesign-extensions-angular-skeleton-loader.d.ts +24 -17
- package/types/kirbydesign-extensions-angular-spot-illustration.d.ts +224 -212
- package/types/kirbydesign-extensions-angular.d.ts +2 -2
- package/combobox/package.json +0 -4
- package/image-banner/package.json +0 -4
- package/localization/package.json +0 -4
- package/sidebar-menu/package.json +0 -4
- package/skeleton-loader/package.json +0 -4
- package/spot-illustration/package.json +0 -4
|
@@ -1,855 +1,934 @@
|
|
|
1
|
-
import * as i0 from
|
|
2
|
-
import {
|
|
3
|
-
import { NG_VALUE_ACCESSOR } from
|
|
4
|
-
import { CardComponent } from
|
|
5
|
-
import { UniqueIdGenerator } from
|
|
6
|
-
import { ItemComponent } from
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import
|
|
11
|
-
import
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
|
|
16
|
-
|
|
1
|
+
import * as i0 from "@angular/core";
|
|
2
|
+
import { ChangeDetectionStrategy, Component, ContentChild, ContentChildren, Directive, ElementRef, EventEmitter, HostBinding, HostListener, Input, Output, RendererStyleFlags2, TemplateRef, ViewChild, ViewChildren, forwardRef } from "@angular/core";
|
|
3
|
+
import { NG_VALUE_ACCESSOR } from "@angular/forms";
|
|
4
|
+
import { CardComponent } from "@kirbydesign/designsystem/card";
|
|
5
|
+
import { UniqueIdGenerator } from "@kirbydesign/designsystem/helpers";
|
|
6
|
+
import { ItemComponent } from "@kirbydesign/designsystem/item";
|
|
7
|
+
import { HorizontalDirection, PopoverComponent } from "@kirbydesign/designsystem/popover";
|
|
8
|
+
import { AffixDirective, InputComponent, InputSize } from "@kirbydesign/designsystem/form-field";
|
|
9
|
+
import { FORM_FIELD_CONTROL } from "@kirbydesign/designsystem/types";
|
|
10
|
+
import * as i1 from "@kirbydesign/designsystem/shared";
|
|
11
|
+
import { forwardAttributes } from "@kirbydesign/designsystem/shared";
|
|
12
|
+
import { IconComponent } from "@kirbydesign/designsystem/icon";
|
|
13
|
+
import { NgTemplateOutlet } from "@angular/common";
|
|
14
|
+
import { CdkFixedSizeVirtualScroll, CdkVirtualForOf, CdkVirtualScrollViewport } from "@angular/cdk/scrolling";
|
|
15
|
+
var ComboboxItemTemplateDirective = class ComboboxItemTemplateDirective {
|
|
16
|
+
static {
|
|
17
|
+
this.ɵfac = i0.ɵɵngDeclareFactory({
|
|
18
|
+
minVersion: "12.0.0",
|
|
19
|
+
version: "22.2.2",
|
|
20
|
+
ngImport: i0,
|
|
21
|
+
type: ComboboxItemTemplateDirective,
|
|
22
|
+
deps: [],
|
|
23
|
+
target: i0.ɵɵFactoryTarget.Directive
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
static {
|
|
27
|
+
this.ɵdir = i0.ɵɵngDeclareDirective({
|
|
28
|
+
minVersion: "14.0.0",
|
|
29
|
+
version: "22.2.2",
|
|
30
|
+
type: ComboboxItemTemplateDirective,
|
|
31
|
+
isStandalone: true,
|
|
32
|
+
selector: "[kirbyXComboboxItemTemplate]",
|
|
33
|
+
ngImport: i0
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
38
|
+
minVersion: "12.0.0",
|
|
39
|
+
version: "22.2.2",
|
|
40
|
+
ngImport: i0,
|
|
41
|
+
type: ComboboxItemTemplateDirective,
|
|
42
|
+
decorators: [{
|
|
43
|
+
type: Directive,
|
|
44
|
+
args: [{
|
|
45
|
+
selector: "[kirbyXComboboxItemTemplate]",
|
|
46
|
+
standalone: true
|
|
47
|
+
}]
|
|
48
|
+
}]
|
|
49
|
+
});
|
|
17
50
|
var OpenState;
|
|
18
|
-
(function
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
51
|
+
(function(OpenState) {
|
|
52
|
+
OpenState[OpenState["closed"] = 0] = "closed";
|
|
53
|
+
OpenState[OpenState["opening"] = 1] = "opening";
|
|
54
|
+
OpenState[OpenState["open"] = 2] = "open";
|
|
22
55
|
})(OpenState || (OpenState = {}));
|
|
56
|
+
var ComboboxComponent = class ComboboxComponent {
|
|
57
|
+
static {
|
|
58
|
+
this.HEIGHT_OF_STANDARD_ITEM = 44;
|
|
59
|
+
}
|
|
60
|
+
compareAlphabetically(textA, textB) {
|
|
61
|
+
if (textA < textB) return -1;
|
|
62
|
+
if (textA > textB) return 1;
|
|
63
|
+
return 0;
|
|
64
|
+
}
|
|
65
|
+
getItemText(item) {
|
|
66
|
+
if (this.isTypeString(item)) return item;
|
|
67
|
+
if (this.objectHasItemTextProperty(item)) {
|
|
68
|
+
const textValue = item[this.itemTextProperty];
|
|
69
|
+
if (typeof textValue === "string") return textValue;
|
|
70
|
+
}
|
|
71
|
+
return "";
|
|
72
|
+
}
|
|
73
|
+
get dropdownMaxHeight() {
|
|
74
|
+
return 8 * ComboboxComponent.HEIGHT_OF_STANDARD_ITEM;
|
|
75
|
+
}
|
|
76
|
+
get dropdownMinHeight() {
|
|
77
|
+
return this.itemHeight;
|
|
78
|
+
}
|
|
79
|
+
get viewportHeight() {
|
|
80
|
+
const itemCount = this.searchItems?.length ?? 0;
|
|
81
|
+
if (itemCount === 0) return this.dropdownMinHeight;
|
|
82
|
+
return Math.min(this.dropdownMaxHeight, Math.max(this.dropdownMinHeight, itemCount * this.itemHeight));
|
|
83
|
+
}
|
|
84
|
+
isTypeString(item) {
|
|
85
|
+
return typeof item === "string";
|
|
86
|
+
}
|
|
87
|
+
objectHasItemTextProperty(item) {
|
|
88
|
+
return item != void 0 && typeof item === "object" && this.itemTextProperty in item;
|
|
89
|
+
}
|
|
90
|
+
objectHasItemIdProperty(item) {
|
|
91
|
+
return item != void 0 && typeof item === "object" && this.itemIdProperty in item;
|
|
92
|
+
}
|
|
93
|
+
getItemId(item) {
|
|
94
|
+
if (this.isTypeString(item)) return item;
|
|
95
|
+
if (this.objectHasItemIdProperty(item)) {
|
|
96
|
+
const idValue = item[this.itemIdProperty];
|
|
97
|
+
if (this.isTypeString(idValue)) return idValue;
|
|
98
|
+
}
|
|
99
|
+
console.error("Each item must have an id property for scroll to work. Ensure that the itemIdProperty input is set correctly, and that each item has a unique id value.");
|
|
100
|
+
return "";
|
|
101
|
+
}
|
|
102
|
+
get searchItems() {
|
|
103
|
+
return this._searchItems;
|
|
104
|
+
}
|
|
105
|
+
set searchItems(value) {
|
|
106
|
+
this._searchItems = value;
|
|
107
|
+
if (!this._searchItems) return;
|
|
108
|
+
if (this.textInput?.nativeElement?.value) {
|
|
109
|
+
this.focusedItem = this._searchItems[0];
|
|
110
|
+
this._virtualScrollViewport?.scrollToIndex(0);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
get items() {
|
|
114
|
+
return this._items;
|
|
115
|
+
}
|
|
116
|
+
set items(value) {
|
|
117
|
+
this._items = value;
|
|
118
|
+
this.searchItems = this._items;
|
|
119
|
+
}
|
|
120
|
+
get selectedItem() {
|
|
121
|
+
return this._selectedItem;
|
|
122
|
+
}
|
|
123
|
+
set selectedItem(value) {
|
|
124
|
+
if (value !== this._selectedItem) {
|
|
125
|
+
this._selectedItem = value;
|
|
126
|
+
this.focusedItem = this._selectedItem;
|
|
127
|
+
this.value = this._selectedItem;
|
|
128
|
+
this.setInputDisplayValue(this.getItemText(this.selectedItem));
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
get focusedItem() {
|
|
132
|
+
return this._focusedItem;
|
|
133
|
+
}
|
|
134
|
+
set focusedItem(item) {
|
|
135
|
+
this.setAriaPosinsetOnElement(item);
|
|
136
|
+
if (this._focusedItem === item) return;
|
|
137
|
+
this._focusedItem = item;
|
|
138
|
+
}
|
|
139
|
+
getIndexOfItem(item) {
|
|
140
|
+
return this.searchItems.indexOf(item);
|
|
141
|
+
}
|
|
142
|
+
getKirbyElement(item) {
|
|
143
|
+
const kirbyItems = this.getKirbyItems();
|
|
144
|
+
if (!kirbyItems || !item) return;
|
|
145
|
+
const itemId = this.getItemId(item);
|
|
146
|
+
return kirbyItems.find((el) => el.nativeElement.id === itemId);
|
|
147
|
+
}
|
|
148
|
+
setAriaPosinsetOnElement(item) {
|
|
149
|
+
const element = this.getKirbyElement(item);
|
|
150
|
+
const index = this.getIndexOfItem(item);
|
|
151
|
+
const setsize = this.searchItems.length;
|
|
152
|
+
if (!element) return;
|
|
153
|
+
this.renderer.setAttribute(element.nativeElement, "aria-setsize", setsize.toString());
|
|
154
|
+
this.renderer.setAttribute(element.nativeElement, "aria-posinset", `${index + 1}`);
|
|
155
|
+
}
|
|
156
|
+
set popout(direction) {
|
|
157
|
+
this._popout = direction || HorizontalDirection.right;
|
|
158
|
+
}
|
|
159
|
+
get popout() {
|
|
160
|
+
return this._popout;
|
|
161
|
+
}
|
|
162
|
+
get _isDisabled() {
|
|
163
|
+
return this.disabled ? "disabled" : null;
|
|
164
|
+
}
|
|
165
|
+
get hasError() {
|
|
166
|
+
return this._hasError;
|
|
167
|
+
}
|
|
168
|
+
set hasError(value) {
|
|
169
|
+
if (this._hasError !== value) {
|
|
170
|
+
this._hasError = value;
|
|
171
|
+
this.hasErrorChange.emit(this._hasError);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
get _noBlurOnScroll() {
|
|
175
|
+
return true;
|
|
176
|
+
}
|
|
177
|
+
get _isBlockLevel() {
|
|
178
|
+
return this.expand === "block";
|
|
179
|
+
}
|
|
180
|
+
get _isOpening() {
|
|
181
|
+
return this.state === OpenState.opening;
|
|
182
|
+
}
|
|
183
|
+
get isOpen() {
|
|
184
|
+
return this.state === OpenState.open;
|
|
185
|
+
}
|
|
186
|
+
get hasValue() {
|
|
187
|
+
return this.selectedItem != null;
|
|
188
|
+
}
|
|
189
|
+
get interactiveElement() {
|
|
190
|
+
return this.textInput.nativeElement;
|
|
191
|
+
}
|
|
192
|
+
forwardAriaLabelToCombobox() {
|
|
193
|
+
forwardAttributes(this.elementRef.nativeElement, this._attributesToForward, this.renderer, this.rootElement.nativeElement);
|
|
194
|
+
}
|
|
195
|
+
set kirbyItemsSlotted(kirbyItems) {
|
|
196
|
+
if (this.disposeItemClickListeners?.length > 0) this.unlistenAllSlottedItems();
|
|
197
|
+
kirbyItems.forEach((kirbyItem) => {
|
|
198
|
+
this.renderer.setAttribute(kirbyItem.nativeElement, "role", "option");
|
|
199
|
+
const disposeClickListener = this.renderer.listen(kirbyItem.nativeElement, "click", () => {
|
|
200
|
+
const id = kirbyItem.nativeElement.getAttribute("id");
|
|
201
|
+
const item = this.items.find((it) => this.getItemId(it) === id);
|
|
202
|
+
if (item) this.onItemSelect(item);
|
|
203
|
+
});
|
|
204
|
+
this.disposeItemClickListeners.push(disposeClickListener);
|
|
205
|
+
});
|
|
206
|
+
this._kirbyItemsSlotted = kirbyItems;
|
|
207
|
+
}
|
|
208
|
+
get kirbyItemsSlotted() {
|
|
209
|
+
return this._kirbyItemsSlotted;
|
|
210
|
+
}
|
|
211
|
+
constructor(renderer, elementRef, cdr, resizeObserverService) {
|
|
212
|
+
this.renderer = renderer;
|
|
213
|
+
this.elementRef = elementRef;
|
|
214
|
+
this.cdr = cdr;
|
|
215
|
+
this.resizeObserverService = resizeObserverService;
|
|
216
|
+
this.state = OpenState.closed;
|
|
217
|
+
this._popout = HorizontalDirection.right;
|
|
218
|
+
this._attributesToForward = ["aria-label", "aria-labelledby"];
|
|
219
|
+
this._listboxId = UniqueIdGenerator.scopedTo("kirby-x-combobox").next();
|
|
220
|
+
this._comboboxId = UniqueIdGenerator.scopedTo("kirby-input").next();
|
|
221
|
+
this._defaultSearchFunction = (searchTerm, itemsToSearch) => {
|
|
222
|
+
if (!searchTerm) return itemsToSearch;
|
|
223
|
+
const lowerSearchTerm = searchTerm.toLowerCase();
|
|
224
|
+
return itemsToSearch.filter((item) => this.getItemText(item).toLowerCase().includes(lowerSearchTerm)).sort((a, b) => {
|
|
225
|
+
const textA = this.getItemText(a).toLowerCase();
|
|
226
|
+
const textB = this.getItemText(b).toLowerCase();
|
|
227
|
+
const textAStartsWith = textA.startsWith(searchTerm);
|
|
228
|
+
const textBStartsWith = textB.startsWith(searchTerm);
|
|
229
|
+
if (textAStartsWith && textBStartsWith) return this.compareAlphabetically(textA, textB);
|
|
230
|
+
if (textAStartsWith) return -1;
|
|
231
|
+
if (textBStartsWith) return 1;
|
|
232
|
+
return this.compareAlphabetically(textA, textB);
|
|
233
|
+
});
|
|
234
|
+
};
|
|
235
|
+
this.noSearchResultsText = "No results found.";
|
|
236
|
+
this.selectionClearedAnnouncement = "Selection cleared.";
|
|
237
|
+
this._liveRegionText = "";
|
|
238
|
+
this.itemTextProperty = "text";
|
|
239
|
+
this.itemHeight = 44;
|
|
240
|
+
this.itemIdProperty = "id";
|
|
241
|
+
this.searchFunction = this._defaultSearchFunction;
|
|
242
|
+
this._searchItems = [];
|
|
243
|
+
this._items = [];
|
|
244
|
+
this._selectedItem = void 0;
|
|
245
|
+
this._focusedItem = void 0;
|
|
246
|
+
this.placeholder = "Please search...";
|
|
247
|
+
this.disabled = false;
|
|
248
|
+
this.hasErrorChange = new EventEmitter();
|
|
249
|
+
this._hasError = false;
|
|
250
|
+
this.size = InputSize.medium;
|
|
251
|
+
this.change = new EventEmitter();
|
|
252
|
+
this.value = void 0;
|
|
253
|
+
this.disposeItemClickListeners = [];
|
|
254
|
+
this.onChange = () => {};
|
|
255
|
+
this.onTouched = () => {};
|
|
256
|
+
}
|
|
257
|
+
onToggle(event) {
|
|
258
|
+
event.stopPropagation();
|
|
259
|
+
this.textInput?.nativeElement.focus();
|
|
260
|
+
this.toggle();
|
|
261
|
+
}
|
|
262
|
+
toggle() {
|
|
263
|
+
if (this.disabled) return;
|
|
264
|
+
this.isOpen ? this.close() : this.open();
|
|
265
|
+
}
|
|
266
|
+
ngAfterViewInit() {
|
|
267
|
+
if (this.expand === "block") {
|
|
268
|
+
const { width: initialWidth } = this.elementRef.nativeElement.getBoundingClientRect();
|
|
269
|
+
this.setPopoverCardStyle("max-width", "initial");
|
|
270
|
+
this.setPopoverCardStyle("min-width", "initial");
|
|
271
|
+
this.setPopoverCardStyle("--kirby-card-width", `${initialWidth}px`);
|
|
272
|
+
this.resizeObserverService.observe(this.elementRef, (entry) => {
|
|
273
|
+
const newWidth = entry.contentRect.width;
|
|
274
|
+
if (newWidth > 0) this.setPopoverCardStyle("--kirby-card-width", `${newWidth}px`);
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
this.forwardAriaLabelToCombobox();
|
|
278
|
+
}
|
|
279
|
+
setPopoverCardStyle(style, value) {
|
|
280
|
+
this.renderer.setStyle(this.cardElement?.nativeElement, style, value, RendererStyleFlags2.DashCase);
|
|
281
|
+
}
|
|
282
|
+
ngOnDestroy() {
|
|
283
|
+
this.unlistenAllSlottedItems();
|
|
284
|
+
this.unobserveResize();
|
|
285
|
+
}
|
|
286
|
+
open() {
|
|
287
|
+
if (this.disabled) return;
|
|
288
|
+
if (!this.isOpen) {
|
|
289
|
+
this.state = OpenState.opening;
|
|
290
|
+
this.showPopOver();
|
|
291
|
+
this.focusedItem = this.selectedItem;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
showPopOver() {
|
|
295
|
+
if (this.state === OpenState.opening) {
|
|
296
|
+
this.state = OpenState.open;
|
|
297
|
+
this.popover?.show();
|
|
298
|
+
this.cdr.markForCheck();
|
|
299
|
+
requestAnimationFrame(() => this.scrollToIndexIntoViewWhenOpeningPopup());
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
close() {
|
|
303
|
+
if (this.disabled) return;
|
|
304
|
+
if (this.isOpen) {
|
|
305
|
+
this.state = OpenState.closed;
|
|
306
|
+
this.setInputDisplayValue(this.getItemText(this.selectedItem));
|
|
307
|
+
this.searchItems = this.items;
|
|
308
|
+
this.popover?.hide();
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
onItemSelect(item) {
|
|
312
|
+
if (item instanceof HTMLElement) return;
|
|
313
|
+
this.selectItem(item);
|
|
314
|
+
this.close();
|
|
315
|
+
}
|
|
316
|
+
writeValue(value) {
|
|
317
|
+
this.selectItemByInput(value);
|
|
318
|
+
this.setInputDisplayValue(this.getItemText(this.value));
|
|
319
|
+
this.cdr.markForCheck();
|
|
320
|
+
}
|
|
321
|
+
registerOnChange(fn) {
|
|
322
|
+
this.onChange = fn;
|
|
323
|
+
}
|
|
324
|
+
registerOnTouched(fn) {
|
|
325
|
+
this.onTouched = fn;
|
|
326
|
+
}
|
|
327
|
+
setDisabledState(isDisabled) {
|
|
328
|
+
this.disabled = isDisabled;
|
|
329
|
+
this.cdr.markForCheck();
|
|
330
|
+
}
|
|
331
|
+
selectItem(item) {
|
|
332
|
+
if (item != this.selectedItem) {
|
|
333
|
+
this.selectedItem = item;
|
|
334
|
+
this.focusedItem = item;
|
|
335
|
+
this.change.emit(this.value);
|
|
336
|
+
this.onChange(this.value);
|
|
337
|
+
this.setInputDisplayValue(this.getItemText(item));
|
|
338
|
+
this.searchItems = this.items;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
selectItemByInput(input) {
|
|
342
|
+
this.selectedItem = this.findItemByInput(input);
|
|
343
|
+
}
|
|
344
|
+
onInput(event) {
|
|
345
|
+
if (!this.isOpen) this.open();
|
|
346
|
+
const input = event.target;
|
|
347
|
+
if (!input.value) this.clearSelection();
|
|
348
|
+
this.updateSearchResults(input.value);
|
|
349
|
+
if (document.activeElement !== this.textInput?.nativeElement) this.textInput?.nativeElement.focus();
|
|
350
|
+
}
|
|
351
|
+
clearSelection() {
|
|
352
|
+
this.selectItem(void 0);
|
|
353
|
+
this.announce(this.selectionClearedAnnouncement);
|
|
354
|
+
}
|
|
355
|
+
announce(message) {
|
|
356
|
+
clearTimeout(this._announceTimeout);
|
|
357
|
+
this._liveRegionText = "";
|
|
358
|
+
this._announceTimeout = setTimeout(() => {
|
|
359
|
+
this._liveRegionText = message;
|
|
360
|
+
this.cdr.markForCheck();
|
|
361
|
+
}, 100);
|
|
362
|
+
}
|
|
363
|
+
updateSearchResults(inputValue) {
|
|
364
|
+
this.searchItems = inputValue ? this.searchFunction(inputValue, this.items) : this.items;
|
|
365
|
+
if (inputValue && this.searchItems.length === 0) this.announce(this.noSearchResultsText);
|
|
366
|
+
}
|
|
367
|
+
onPopoverWillHide() {
|
|
368
|
+
this.state = OpenState.closed;
|
|
369
|
+
this.rootElement.nativeElement.focus();
|
|
370
|
+
this.onTouched();
|
|
371
|
+
}
|
|
372
|
+
onPopoverClick() {
|
|
373
|
+
this.close();
|
|
374
|
+
}
|
|
375
|
+
onMouseDown(event) {
|
|
376
|
+
if (this.disabled) {
|
|
377
|
+
event.preventDefault();
|
|
378
|
+
event.stopImmediatePropagation();
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
onTouchStart(event) {
|
|
382
|
+
if (this.isOpen) event.stopPropagation();
|
|
383
|
+
}
|
|
384
|
+
onFocusOut() {
|
|
385
|
+
setTimeout(() => {
|
|
386
|
+
const activeElement = document.activeElement;
|
|
387
|
+
const isOnTriggerButton = activeElement && this.elementRef.nativeElement.contains(activeElement);
|
|
388
|
+
const isInsidePopover = activeElement && activeElement.closest("kirby-popover");
|
|
389
|
+
if (isOnTriggerButton || isInsidePopover) return;
|
|
390
|
+
if (this.isOpen && activeElement === document.body) {
|
|
391
|
+
this.textInput?.nativeElement.focus();
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
if (this.isOpen) this.close();
|
|
395
|
+
this.onTouched();
|
|
396
|
+
}, 0);
|
|
397
|
+
}
|
|
398
|
+
onTab() {
|
|
399
|
+
if (this.isOpen) {
|
|
400
|
+
this.selectItem(this.focusedItem);
|
|
401
|
+
this.close();
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
onEscape() {
|
|
405
|
+
this.close();
|
|
406
|
+
}
|
|
407
|
+
onEnter(event) {
|
|
408
|
+
event.preventDefault();
|
|
409
|
+
event.stopPropagation();
|
|
410
|
+
if (this.isOpen) this.selectItem(this.focusedItem);
|
|
411
|
+
this.toggle();
|
|
412
|
+
}
|
|
413
|
+
onArrowKeys(event) {
|
|
414
|
+
if (this.disabled) return;
|
|
415
|
+
const keyEvent = event;
|
|
416
|
+
event.preventDefault();
|
|
417
|
+
if (!this.isOpen) {
|
|
418
|
+
this.open();
|
|
419
|
+
if (this.selectedItem) this.setFocusOnSelectedItem();
|
|
420
|
+
else this.setFocusOnFirstOrLastItem(keyEvent);
|
|
421
|
+
} else this.handleFocusNavigation(keyEvent);
|
|
422
|
+
}
|
|
423
|
+
handleFocusNavigation(keyEvent) {
|
|
424
|
+
switch (keyEvent.key) {
|
|
425
|
+
case "ArrowDown":
|
|
426
|
+
this.shiftFocusIndex(1);
|
|
427
|
+
break;
|
|
428
|
+
case "ArrowUp":
|
|
429
|
+
this.shiftFocusIndex(-1);
|
|
430
|
+
break;
|
|
431
|
+
case "PageDown":
|
|
432
|
+
this.shiftFocusIndex(10);
|
|
433
|
+
break;
|
|
434
|
+
case "PageUp":
|
|
435
|
+
this.shiftFocusIndex(-10);
|
|
436
|
+
break;
|
|
437
|
+
case "Home":
|
|
438
|
+
this.setFocusOnFirstItem();
|
|
439
|
+
break;
|
|
440
|
+
case "End": this.setFocusOnLastItem();
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
shiftFocusIndex(numberOfItems) {
|
|
444
|
+
const currentIndex = this.searchItems.indexOf(this.focusedItem);
|
|
445
|
+
let newIndex;
|
|
446
|
+
if (numberOfItems > 0) newIndex = Math.min(this.searchItems.length - 1, currentIndex + numberOfItems);
|
|
447
|
+
else newIndex = Math.max(0, currentIndex + numberOfItems);
|
|
448
|
+
if (newIndex !== currentIndex) this.focusedItem = this.searchItems[newIndex];
|
|
449
|
+
this.scrollFocusedItemIntoViewWhileNavigating();
|
|
450
|
+
}
|
|
451
|
+
setFocusOnFirstOrLastItem(keyEvent) {
|
|
452
|
+
switch (keyEvent.key) {
|
|
453
|
+
case "ArrowDown":
|
|
454
|
+
case "PageDown":
|
|
455
|
+
case "Home":
|
|
456
|
+
this.setFocusOnFirstItem();
|
|
457
|
+
break;
|
|
458
|
+
case "ArrowUp":
|
|
459
|
+
case "PageUp":
|
|
460
|
+
case "End": this.setFocusOnLastItem();
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
setFocusOnSelectedItem() {
|
|
464
|
+
if (this.selectedItem) this.focusedItem = this.selectedItem;
|
|
465
|
+
}
|
|
466
|
+
setFocusOnLastItem() {
|
|
467
|
+
if (this.searchItems.length > 0) this.focusedItem = this.searchItems[this.searchItems.length - 1];
|
|
468
|
+
}
|
|
469
|
+
setFocusOnFirstItem() {
|
|
470
|
+
if (this.searchItems.length > 0) this.focusedItem = this.searchItems[0];
|
|
471
|
+
}
|
|
472
|
+
unlistenAllSlottedItems() {
|
|
473
|
+
let disposeClickListener;
|
|
474
|
+
while ((disposeClickListener = this.disposeItemClickListeners.pop()) !== void 0) disposeClickListener();
|
|
475
|
+
}
|
|
476
|
+
unobserveResize() {
|
|
477
|
+
this.resizeObserverService.unobserve(this.elementRef);
|
|
478
|
+
}
|
|
479
|
+
findItemByInput(input) {
|
|
480
|
+
if (!this.items || this.items.length === 0) return input;
|
|
481
|
+
return this.items.find((it) => this.getItemText(it) === input) ?? input;
|
|
482
|
+
}
|
|
483
|
+
setInputDisplayValue(value) {
|
|
484
|
+
if (this.interactiveElement) this.renderer.setProperty(this.interactiveElement, "value", value);
|
|
485
|
+
}
|
|
486
|
+
scrollFocusedItemIntoViewWhileNavigating() {
|
|
487
|
+
if (!this.focusedItem || !this._virtualScrollViewport) return;
|
|
488
|
+
const focusedIndex = this.searchItems.indexOf(this.focusedItem);
|
|
489
|
+
if (focusedIndex === -1) return;
|
|
490
|
+
const itemTop = focusedIndex * this.itemHeight;
|
|
491
|
+
const itemBottom = itemTop + this.itemHeight;
|
|
492
|
+
const scrollOffset = this._virtualScrollViewport.measureScrollOffset();
|
|
493
|
+
const viewportSize = this._virtualScrollViewport.getViewportSize();
|
|
494
|
+
if (itemTop < scrollOffset) this._virtualScrollViewport.scrollToOffset(itemTop);
|
|
495
|
+
else if (itemBottom > scrollOffset + viewportSize) this._virtualScrollViewport.scrollToOffset(itemBottom - viewportSize);
|
|
496
|
+
this._virtualScrollViewport.checkViewportSize();
|
|
497
|
+
}
|
|
498
|
+
getKirbyItems() {
|
|
499
|
+
return this.kirbyItemsSlotted && this.kirbyItemsSlotted.length ? this.kirbyItemsSlotted : this.kirbyItemsDefault;
|
|
500
|
+
}
|
|
501
|
+
scrollToIndexIntoViewWhenOpeningPopup() {
|
|
502
|
+
const focusedIndex = this.searchItems.indexOf(this.focusedItem);
|
|
503
|
+
this._virtualScrollViewport?.setRenderedRange({
|
|
504
|
+
start: 0,
|
|
505
|
+
end: 20
|
|
506
|
+
});
|
|
507
|
+
this._virtualScrollViewport?.checkViewportSize();
|
|
508
|
+
this._virtualScrollViewport?.scrollToIndex(focusedIndex);
|
|
509
|
+
}
|
|
510
|
+
static {
|
|
511
|
+
this.ɵfac = i0.ɵɵngDeclareFactory({
|
|
512
|
+
minVersion: "12.0.0",
|
|
513
|
+
version: "22.2.2",
|
|
514
|
+
ngImport: i0,
|
|
515
|
+
type: ComboboxComponent,
|
|
516
|
+
deps: [
|
|
517
|
+
{ token: i0.Renderer2 },
|
|
518
|
+
{ token: i0.ElementRef },
|
|
519
|
+
{ token: i0.ChangeDetectorRef },
|
|
520
|
+
{ token: i1.ResizeObserverService }
|
|
521
|
+
],
|
|
522
|
+
target: i0.ɵɵFactoryTarget.Component
|
|
523
|
+
});
|
|
524
|
+
}
|
|
525
|
+
static {
|
|
526
|
+
this.ɵcmp = i0.ɵɵngDeclareComponent({
|
|
527
|
+
minVersion: "17.0.0",
|
|
528
|
+
version: "22.2.2",
|
|
529
|
+
type: ComboboxComponent,
|
|
530
|
+
isStandalone: true,
|
|
531
|
+
selector: "kirby-x-combobox",
|
|
532
|
+
inputs: {
|
|
533
|
+
noSearchResultsText: "noSearchResultsText",
|
|
534
|
+
selectionClearedAnnouncement: "selectionClearedAnnouncement",
|
|
535
|
+
itemTextProperty: "itemTextProperty",
|
|
536
|
+
itemHeight: "itemHeight",
|
|
537
|
+
itemIdProperty: "itemIdProperty",
|
|
538
|
+
searchFunction: "searchFunction",
|
|
539
|
+
items: "items",
|
|
540
|
+
selectedItem: "selectedItem",
|
|
541
|
+
placeholder: "placeholder",
|
|
542
|
+
popout: "popout",
|
|
543
|
+
expand: "expand",
|
|
544
|
+
disabled: "disabled",
|
|
545
|
+
hasError: "hasError",
|
|
546
|
+
size: "size"
|
|
547
|
+
},
|
|
548
|
+
outputs: {
|
|
549
|
+
hasErrorChange: "hasErrorChange",
|
|
550
|
+
change: "change"
|
|
551
|
+
},
|
|
552
|
+
host: {
|
|
553
|
+
listeners: {
|
|
554
|
+
"mousedown": "onMouseDown($event)",
|
|
555
|
+
"touchstart": "onTouchStart($event)",
|
|
556
|
+
"focusout": "onFocusOut()",
|
|
557
|
+
"keydown.tab": "onTab()",
|
|
558
|
+
"keydown.escape": "onEscape()",
|
|
559
|
+
"keydown.enter": "onEnter($event)",
|
|
560
|
+
"keydown.arrowUp": "onArrowKeys($event)",
|
|
561
|
+
"keydown.arrowDown": "onArrowKeys($event)",
|
|
562
|
+
"keydown.pageUp": "onArrowKeys($event)",
|
|
563
|
+
"keydown.pageDown": "onArrowKeys($event)",
|
|
564
|
+
"keydown.home": "onArrowKeys($event)",
|
|
565
|
+
"keydown.end": "onArrowKeys($event)"
|
|
566
|
+
},
|
|
567
|
+
properties: {
|
|
568
|
+
"attr.disabled": "this._isDisabled",
|
|
569
|
+
"attr.no-blur": "this._noBlurOnScroll",
|
|
570
|
+
"class.expand": "this._isBlockLevel",
|
|
571
|
+
"class.is-opening": "this._isOpening",
|
|
572
|
+
"class.is-open": "this.isOpen",
|
|
573
|
+
"class.has-value": "this.hasValue"
|
|
574
|
+
}
|
|
575
|
+
},
|
|
576
|
+
providers: [{
|
|
577
|
+
provide: NG_VALUE_ACCESSOR,
|
|
578
|
+
useExisting: forwardRef(() => ComboboxComponent),
|
|
579
|
+
multi: true
|
|
580
|
+
}, {
|
|
581
|
+
provide: FORM_FIELD_CONTROL,
|
|
582
|
+
useExisting: forwardRef(() => ComboboxComponent)
|
|
583
|
+
}],
|
|
584
|
+
queries: [
|
|
585
|
+
{
|
|
586
|
+
propertyName: "itemTemplate",
|
|
587
|
+
first: true,
|
|
588
|
+
predicate: ComboboxItemTemplateDirective,
|
|
589
|
+
descendants: true,
|
|
590
|
+
read: TemplateRef
|
|
591
|
+
},
|
|
592
|
+
{
|
|
593
|
+
propertyName: "slottedItems",
|
|
594
|
+
predicate: ComboboxItemTemplateDirective,
|
|
595
|
+
read: ElementRef
|
|
596
|
+
},
|
|
597
|
+
{
|
|
598
|
+
propertyName: "kirbyItemsSlotted",
|
|
599
|
+
predicate: ItemComponent,
|
|
600
|
+
read: ElementRef
|
|
601
|
+
}
|
|
602
|
+
],
|
|
603
|
+
viewQueries: [
|
|
604
|
+
{
|
|
605
|
+
propertyName: "cardElement",
|
|
606
|
+
first: true,
|
|
607
|
+
predicate: CardComponent,
|
|
608
|
+
descendants: true,
|
|
609
|
+
read: ElementRef
|
|
610
|
+
},
|
|
611
|
+
{
|
|
612
|
+
propertyName: "popover",
|
|
613
|
+
first: true,
|
|
614
|
+
predicate: PopoverComponent,
|
|
615
|
+
descendants: true
|
|
616
|
+
},
|
|
617
|
+
{
|
|
618
|
+
propertyName: "rootElement",
|
|
619
|
+
first: true,
|
|
620
|
+
predicate: ["rootElement"],
|
|
621
|
+
descendants: true,
|
|
622
|
+
read: ElementRef,
|
|
623
|
+
static: true
|
|
624
|
+
},
|
|
625
|
+
{
|
|
626
|
+
propertyName: "textInput",
|
|
627
|
+
first: true,
|
|
628
|
+
predicate: InputComponent,
|
|
629
|
+
descendants: true,
|
|
630
|
+
read: ElementRef,
|
|
631
|
+
static: true
|
|
632
|
+
},
|
|
633
|
+
{
|
|
634
|
+
propertyName: "_virtualScrollViewport",
|
|
635
|
+
first: true,
|
|
636
|
+
predicate: CdkVirtualScrollViewport,
|
|
637
|
+
descendants: true
|
|
638
|
+
},
|
|
639
|
+
{
|
|
640
|
+
propertyName: "kirbyItemsDefault",
|
|
641
|
+
predicate: ItemComponent,
|
|
642
|
+
descendants: true,
|
|
643
|
+
read: ElementRef
|
|
644
|
+
}
|
|
645
|
+
],
|
|
646
|
+
ngImport: i0,
|
|
647
|
+
template: "<span class=\"kirby-visually-hidden\" aria-live=\"polite\" aria-atomic=\"true\">\n {{ _liveRegionText }}\n</span>\n<div (click)=\"onToggle($event)\" #rootElement>\n <input\n kirby-input\n type=\"text\"\n aria-haspopup=\"listbox\"\n role=\"combobox\"\n [hasError]=\"hasError\"\n [size]=\"size\"\n [disabled]=\"disabled\"\n [placeholder]=\"placeholder\"\n (change)=\"$event.stopPropagation()\"\n [attr.id]=\"_comboboxId\"\n [attr.aria-controls]=\"_listboxId\"\n [attr.aria-expanded]=\"isOpen\"\n [attr.aria-activedescendant]=\"isOpen && focusedItem ? getItemId(focusedItem) : null\"\n (input)=\"onInput($event)\"\n />\n <kirby-icon [name]=\"isOpen ? 'arrow-up' : 'arrow-down'\" kirby-affix=\"suffix\"></kirby-icon>\n</div>\n\n<kirby-popover\n [target]=\"rootElement\"\n [popout]=\"popout\"\n (click)=\"onPopoverClick()\"\n (mousedown)=\"$event.preventDefault()\"\n (willHide)=\"onPopoverWillHide()\"\n>\n <kirby-card [attr.id]=\"_listboxId\" [attr.aria-labelledby]=\"_comboboxId\">\n @if (searchItems.length === 0) {\n <kirby-item>\n <p class=\"kirby-item-title no-results\">{{ noSearchResultsText }}</p>\n </kirby-item>\n } @else {\n <cdk-virtual-scroll-viewport\n class=\"combobox-viewport\"\n role=\"listbox\"\n [itemSize]=\"itemHeight\"\n [style.height.px]=\"viewportHeight\"\n >\n <ng-container *cdkVirtualFor=\"let item of searchItems\">\n <ng-container\n *ngTemplateOutlet=\"\n itemTemplate || defaultItemTemplate;\n context: {\n $implicit: item,\n selected: item === selectedItem,\n focused: item === focusedItem,\n }\n \"\n ></ng-container>\n </ng-container>\n </cdk-virtual-scroll-viewport>\n }\n </kirby-card>\n</kirby-popover>\n\n<ng-template #defaultItemTemplate let-item let-selected=\"selected\" let-focused=\"focused\">\n <kirby-item\n role=\"option\"\n [selectable]=\"true\"\n [selected]=\"selected\"\n [class.focused]=\"focused\"\n [attr.id]=\"getItemId(item)\"\n [attr.aria-selected]=\"selected\"\n (click)=\"onItemSelect(item)\"\n >\n <p class=\"kirby-item-title\">{{ getItemText(item) }}</p>\n @if (selected) {\n <kirby-icon name=\"checkmark-selected\" slot=\"end\"></kirby-icon>\n }\n </kirby-item>\n</ng-template>\n",
|
|
648
|
+
styles: [".kirby-line-clamp{display:-webkit-box;-webkit-line-clamp:var(--line-clamp, none);-webkit-box-orient:vertical;overflow:hidden}:host:focus-visible{transition:all 80ms linear 0ms;box-shadow:var(--kirby-focus-ring-md-inset);outline:none;border-radius:var(--kirby-border-radius-pill)}:host{display:inline-block;position:relative;max-width:calc(100vw - calc(2 * var(--kirby-spacing-s)))}:host.clicked{box-shadow:none}:host.expand{display:block}:host.expand kirby-card{width:100%;min-width:initial;max-width:initial}:host div{position:relative}:host div>input[kirby-input]{padding-right:var(--kirby-spacing-xl)}:host div>input[kirby-input]:disabled~kirby-icon{opacity:.4}:host div>kirby-icon{color:var(--kirby-color-content-raised);position:absolute;top:0;bottom:0;right:var(--kirby-spacing-xs)}:host.has-value>div>input[kirby-input]{text-overflow:ellipsis}kirby-popover{--max-height: 352px}kirby-card{display:flex;flex-direction:column;max-height:352px;overflow:hidden;box-shadow:var(--kirby-elevation-4);max-width:calc(100vw - calc(2 * var(--kirby-spacing-s)));min-width:calc(320px - calc(2 * var(--kirby-spacing-s)))}@media(min-width:321px){kirby-card{min-width:calc(375px - calc(2 * var(--kirby-spacing-s)))}}.combobox-viewport{display:block;width:100%}.no-results{color:var(--kirby-color-content-base-quiet)}\n"],
|
|
649
|
+
dependencies: [
|
|
650
|
+
{
|
|
651
|
+
kind: "component",
|
|
652
|
+
type: InputComponent,
|
|
653
|
+
selector: "input[kirby-input]",
|
|
654
|
+
inputs: [
|
|
655
|
+
"type",
|
|
656
|
+
"size",
|
|
657
|
+
"borderless",
|
|
658
|
+
"hasError",
|
|
659
|
+
"autocomplete",
|
|
660
|
+
"autocorrect",
|
|
661
|
+
"value",
|
|
662
|
+
"maxlength",
|
|
663
|
+
"inputmode"
|
|
664
|
+
],
|
|
665
|
+
outputs: ["hasErrorChange"]
|
|
666
|
+
},
|
|
667
|
+
{
|
|
668
|
+
kind: "directive",
|
|
669
|
+
type: NgTemplateOutlet,
|
|
670
|
+
selector: "[ngTemplateOutlet]",
|
|
671
|
+
inputs: [
|
|
672
|
+
"ngTemplateOutletContext",
|
|
673
|
+
"ngTemplateOutlet",
|
|
674
|
+
"ngTemplateOutletInjector"
|
|
675
|
+
]
|
|
676
|
+
},
|
|
677
|
+
{
|
|
678
|
+
kind: "component",
|
|
679
|
+
type: PopoverComponent,
|
|
680
|
+
selector: "kirby-popover",
|
|
681
|
+
inputs: ["popout", "target"],
|
|
682
|
+
outputs: ["willHide"]
|
|
683
|
+
},
|
|
684
|
+
{
|
|
685
|
+
kind: "component",
|
|
686
|
+
type: CardComponent,
|
|
687
|
+
selector: "kirby-card",
|
|
688
|
+
inputs: [
|
|
689
|
+
"title",
|
|
690
|
+
"subtitle",
|
|
691
|
+
"backgroundImageUrl",
|
|
692
|
+
"hasPadding",
|
|
693
|
+
"sizes",
|
|
694
|
+
"variant"
|
|
695
|
+
]
|
|
696
|
+
},
|
|
697
|
+
{
|
|
698
|
+
kind: "component",
|
|
699
|
+
type: ItemComponent,
|
|
700
|
+
selector: "kirby-item",
|
|
701
|
+
inputs: [
|
|
702
|
+
"disabled",
|
|
703
|
+
"selected",
|
|
704
|
+
"disclosure",
|
|
705
|
+
"selectable",
|
|
706
|
+
"reorderable",
|
|
707
|
+
"size",
|
|
708
|
+
"href",
|
|
709
|
+
"rotateIcon"
|
|
710
|
+
]
|
|
711
|
+
},
|
|
712
|
+
{
|
|
713
|
+
kind: "component",
|
|
714
|
+
type: IconComponent,
|
|
715
|
+
selector: "kirby-icon",
|
|
716
|
+
inputs: ["size", "name"]
|
|
717
|
+
},
|
|
718
|
+
{
|
|
719
|
+
kind: "directive",
|
|
720
|
+
type: AffixDirective,
|
|
721
|
+
selector: "[kirby-affix]",
|
|
722
|
+
inputs: ["kirby-affix"]
|
|
723
|
+
},
|
|
724
|
+
{
|
|
725
|
+
kind: "directive",
|
|
726
|
+
type: CdkFixedSizeVirtualScroll,
|
|
727
|
+
selector: "cdk-virtual-scroll-viewport[itemSize]",
|
|
728
|
+
inputs: [
|
|
729
|
+
"itemSize",
|
|
730
|
+
"minBufferPx",
|
|
731
|
+
"maxBufferPx"
|
|
732
|
+
]
|
|
733
|
+
},
|
|
734
|
+
{
|
|
735
|
+
kind: "component",
|
|
736
|
+
type: CdkVirtualScrollViewport,
|
|
737
|
+
selector: "cdk-virtual-scroll-viewport",
|
|
738
|
+
inputs: ["orientation", "appendOnly"],
|
|
739
|
+
outputs: ["scrolledIndexChange"]
|
|
740
|
+
},
|
|
741
|
+
{
|
|
742
|
+
kind: "directive",
|
|
743
|
+
type: CdkVirtualForOf,
|
|
744
|
+
selector: "[cdkVirtualFor][cdkVirtualForOf]",
|
|
745
|
+
inputs: [
|
|
746
|
+
"cdkVirtualForOf",
|
|
747
|
+
"cdkVirtualForTrackBy",
|
|
748
|
+
"cdkVirtualForTemplate",
|
|
749
|
+
"cdkVirtualForTemplateCacheSize"
|
|
750
|
+
]
|
|
751
|
+
}
|
|
752
|
+
],
|
|
753
|
+
changeDetection: i0.ChangeDetectionStrategy.OnPush
|
|
754
|
+
});
|
|
755
|
+
}
|
|
756
|
+
};
|
|
757
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
758
|
+
minVersion: "12.0.0",
|
|
759
|
+
version: "22.2.2",
|
|
760
|
+
ngImport: i0,
|
|
761
|
+
type: ComboboxComponent,
|
|
762
|
+
decorators: [{
|
|
763
|
+
type: Component,
|
|
764
|
+
args: [{
|
|
765
|
+
selector: "kirby-x-combobox",
|
|
766
|
+
providers: [{
|
|
767
|
+
provide: NG_VALUE_ACCESSOR,
|
|
768
|
+
useExisting: forwardRef(() => ComboboxComponent),
|
|
769
|
+
multi: true
|
|
770
|
+
}, {
|
|
771
|
+
provide: FORM_FIELD_CONTROL,
|
|
772
|
+
useExisting: forwardRef(() => ComboboxComponent)
|
|
773
|
+
}],
|
|
774
|
+
imports: [
|
|
775
|
+
InputComponent,
|
|
776
|
+
NgTemplateOutlet,
|
|
777
|
+
PopoverComponent,
|
|
778
|
+
CardComponent,
|
|
779
|
+
ItemComponent,
|
|
780
|
+
IconComponent,
|
|
781
|
+
AffixDirective,
|
|
782
|
+
CdkFixedSizeVirtualScroll,
|
|
783
|
+
CdkVirtualScrollViewport,
|
|
784
|
+
CdkVirtualForOf
|
|
785
|
+
],
|
|
786
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
787
|
+
template: "<span class=\"kirby-visually-hidden\" aria-live=\"polite\" aria-atomic=\"true\">\n {{ _liveRegionText }}\n</span>\n<div (click)=\"onToggle($event)\" #rootElement>\n <input\n kirby-input\n type=\"text\"\n aria-haspopup=\"listbox\"\n role=\"combobox\"\n [hasError]=\"hasError\"\n [size]=\"size\"\n [disabled]=\"disabled\"\n [placeholder]=\"placeholder\"\n (change)=\"$event.stopPropagation()\"\n [attr.id]=\"_comboboxId\"\n [attr.aria-controls]=\"_listboxId\"\n [attr.aria-expanded]=\"isOpen\"\n [attr.aria-activedescendant]=\"isOpen && focusedItem ? getItemId(focusedItem) : null\"\n (input)=\"onInput($event)\"\n />\n <kirby-icon [name]=\"isOpen ? 'arrow-up' : 'arrow-down'\" kirby-affix=\"suffix\"></kirby-icon>\n</div>\n\n<kirby-popover\n [target]=\"rootElement\"\n [popout]=\"popout\"\n (click)=\"onPopoverClick()\"\n (mousedown)=\"$event.preventDefault()\"\n (willHide)=\"onPopoverWillHide()\"\n>\n <kirby-card [attr.id]=\"_listboxId\" [attr.aria-labelledby]=\"_comboboxId\">\n @if (searchItems.length === 0) {\n <kirby-item>\n <p class=\"kirby-item-title no-results\">{{ noSearchResultsText }}</p>\n </kirby-item>\n } @else {\n <cdk-virtual-scroll-viewport\n class=\"combobox-viewport\"\n role=\"listbox\"\n [itemSize]=\"itemHeight\"\n [style.height.px]=\"viewportHeight\"\n >\n <ng-container *cdkVirtualFor=\"let item of searchItems\">\n <ng-container\n *ngTemplateOutlet=\"\n itemTemplate || defaultItemTemplate;\n context: {\n $implicit: item,\n selected: item === selectedItem,\n focused: item === focusedItem,\n }\n \"\n ></ng-container>\n </ng-container>\n </cdk-virtual-scroll-viewport>\n }\n </kirby-card>\n</kirby-popover>\n\n<ng-template #defaultItemTemplate let-item let-selected=\"selected\" let-focused=\"focused\">\n <kirby-item\n role=\"option\"\n [selectable]=\"true\"\n [selected]=\"selected\"\n [class.focused]=\"focused\"\n [attr.id]=\"getItemId(item)\"\n [attr.aria-selected]=\"selected\"\n (click)=\"onItemSelect(item)\"\n >\n <p class=\"kirby-item-title\">{{ getItemText(item) }}</p>\n @if (selected) {\n <kirby-icon name=\"checkmark-selected\" slot=\"end\"></kirby-icon>\n }\n </kirby-item>\n</ng-template>\n",
|
|
788
|
+
styles: [".kirby-line-clamp{display:-webkit-box;-webkit-line-clamp:var(--line-clamp, none);-webkit-box-orient:vertical;overflow:hidden}:host:focus-visible{transition:all 80ms linear 0ms;box-shadow:var(--kirby-focus-ring-md-inset);outline:none;border-radius:var(--kirby-border-radius-pill)}:host{display:inline-block;position:relative;max-width:calc(100vw - calc(2 * var(--kirby-spacing-s)))}:host.clicked{box-shadow:none}:host.expand{display:block}:host.expand kirby-card{width:100%;min-width:initial;max-width:initial}:host div{position:relative}:host div>input[kirby-input]{padding-right:var(--kirby-spacing-xl)}:host div>input[kirby-input]:disabled~kirby-icon{opacity:.4}:host div>kirby-icon{color:var(--kirby-color-content-raised);position:absolute;top:0;bottom:0;right:var(--kirby-spacing-xs)}:host.has-value>div>input[kirby-input]{text-overflow:ellipsis}kirby-popover{--max-height: 352px}kirby-card{display:flex;flex-direction:column;max-height:352px;overflow:hidden;box-shadow:var(--kirby-elevation-4);max-width:calc(100vw - calc(2 * var(--kirby-spacing-s)));min-width:calc(320px - calc(2 * var(--kirby-spacing-s)))}@media(min-width:321px){kirby-card{min-width:calc(375px - calc(2 * var(--kirby-spacing-s)))}}.combobox-viewport{display:block;width:100%}.no-results{color:var(--kirby-color-content-base-quiet)}\n"]
|
|
789
|
+
}]
|
|
790
|
+
}],
|
|
791
|
+
ctorParameters: () => [
|
|
792
|
+
{ type: i0.Renderer2 },
|
|
793
|
+
{ type: i0.ElementRef },
|
|
794
|
+
{ type: i0.ChangeDetectorRef },
|
|
795
|
+
{ type: i1.ResizeObserverService }
|
|
796
|
+
],
|
|
797
|
+
propDecorators: {
|
|
798
|
+
noSearchResultsText: [{ type: Input }],
|
|
799
|
+
selectionClearedAnnouncement: [{ type: Input }],
|
|
800
|
+
itemTextProperty: [{ type: Input }],
|
|
801
|
+
itemHeight: [{ type: Input }],
|
|
802
|
+
itemIdProperty: [{ type: Input }],
|
|
803
|
+
searchFunction: [{ type: Input }],
|
|
804
|
+
items: [{ type: Input }],
|
|
805
|
+
selectedItem: [{ type: Input }],
|
|
806
|
+
placeholder: [{ type: Input }],
|
|
807
|
+
popout: [{ type: Input }],
|
|
808
|
+
expand: [{ type: Input }],
|
|
809
|
+
disabled: [{ type: Input }],
|
|
810
|
+
_isDisabled: [{
|
|
811
|
+
type: HostBinding,
|
|
812
|
+
args: ["attr.disabled"]
|
|
813
|
+
}],
|
|
814
|
+
hasErrorChange: [{ type: Output }],
|
|
815
|
+
hasError: [{ type: Input }],
|
|
816
|
+
size: [{ type: Input }],
|
|
817
|
+
_noBlurOnScroll: [{
|
|
818
|
+
type: HostBinding,
|
|
819
|
+
args: ["attr.no-blur"]
|
|
820
|
+
}],
|
|
821
|
+
change: [{ type: Output }],
|
|
822
|
+
_isBlockLevel: [{
|
|
823
|
+
type: HostBinding,
|
|
824
|
+
args: ["class.expand"]
|
|
825
|
+
}],
|
|
826
|
+
_isOpening: [{
|
|
827
|
+
type: HostBinding,
|
|
828
|
+
args: ["class.is-opening"]
|
|
829
|
+
}],
|
|
830
|
+
isOpen: [{
|
|
831
|
+
type: HostBinding,
|
|
832
|
+
args: ["class.is-open"]
|
|
833
|
+
}],
|
|
834
|
+
hasValue: [{
|
|
835
|
+
type: HostBinding,
|
|
836
|
+
args: ["class.has-value"]
|
|
837
|
+
}],
|
|
838
|
+
itemTemplate: [{
|
|
839
|
+
type: ContentChild,
|
|
840
|
+
args: [ComboboxItemTemplateDirective, { read: TemplateRef }]
|
|
841
|
+
}],
|
|
842
|
+
slottedItems: [{
|
|
843
|
+
type: ContentChildren,
|
|
844
|
+
args: [ComboboxItemTemplateDirective, { read: ElementRef }]
|
|
845
|
+
}],
|
|
846
|
+
cardElement: [{
|
|
847
|
+
type: ViewChild,
|
|
848
|
+
args: [CardComponent, { read: ElementRef }]
|
|
849
|
+
}],
|
|
850
|
+
popover: [{
|
|
851
|
+
type: ViewChild,
|
|
852
|
+
args: [PopoverComponent]
|
|
853
|
+
}],
|
|
854
|
+
rootElement: [{
|
|
855
|
+
type: ViewChild,
|
|
856
|
+
args: ["rootElement", {
|
|
857
|
+
static: true,
|
|
858
|
+
read: ElementRef
|
|
859
|
+
}]
|
|
860
|
+
}],
|
|
861
|
+
textInput: [{
|
|
862
|
+
type: ViewChild,
|
|
863
|
+
args: [InputComponent, {
|
|
864
|
+
static: true,
|
|
865
|
+
read: ElementRef
|
|
866
|
+
}]
|
|
867
|
+
}],
|
|
868
|
+
_virtualScrollViewport: [{
|
|
869
|
+
type: ViewChild,
|
|
870
|
+
args: [CdkVirtualScrollViewport]
|
|
871
|
+
}],
|
|
872
|
+
kirbyItemsDefault: [{
|
|
873
|
+
type: ViewChildren,
|
|
874
|
+
args: [ItemComponent, { read: ElementRef }]
|
|
875
|
+
}],
|
|
876
|
+
kirbyItemsSlotted: [{
|
|
877
|
+
type: ContentChildren,
|
|
878
|
+
args: [ItemComponent, { read: ElementRef }]
|
|
879
|
+
}],
|
|
880
|
+
onMouseDown: [{
|
|
881
|
+
type: HostListener,
|
|
882
|
+
args: ["mousedown", ["$event"]]
|
|
883
|
+
}],
|
|
884
|
+
onTouchStart: [{
|
|
885
|
+
type: HostListener,
|
|
886
|
+
args: ["touchstart", ["$event"]]
|
|
887
|
+
}],
|
|
888
|
+
onFocusOut: [{
|
|
889
|
+
type: HostListener,
|
|
890
|
+
args: ["focusout"]
|
|
891
|
+
}],
|
|
892
|
+
onTab: [{
|
|
893
|
+
type: HostListener,
|
|
894
|
+
args: ["keydown.tab"]
|
|
895
|
+
}],
|
|
896
|
+
onEscape: [{
|
|
897
|
+
type: HostListener,
|
|
898
|
+
args: ["keydown.escape"]
|
|
899
|
+
}],
|
|
900
|
+
onEnter: [{
|
|
901
|
+
type: HostListener,
|
|
902
|
+
args: ["keydown.enter", ["$event"]]
|
|
903
|
+
}],
|
|
904
|
+
onArrowKeys: [
|
|
905
|
+
{
|
|
906
|
+
type: HostListener,
|
|
907
|
+
args: ["keydown.arrowUp", ["$event"]]
|
|
908
|
+
},
|
|
909
|
+
{
|
|
910
|
+
type: HostListener,
|
|
911
|
+
args: ["keydown.arrowDown", ["$event"]]
|
|
912
|
+
},
|
|
913
|
+
{
|
|
914
|
+
type: HostListener,
|
|
915
|
+
args: ["keydown.pageUp", ["$event"]]
|
|
916
|
+
},
|
|
917
|
+
{
|
|
918
|
+
type: HostListener,
|
|
919
|
+
args: ["keydown.pageDown", ["$event"]]
|
|
920
|
+
},
|
|
921
|
+
{
|
|
922
|
+
type: HostListener,
|
|
923
|
+
args: ["keydown.home", ["$event"]]
|
|
924
|
+
},
|
|
925
|
+
{
|
|
926
|
+
type: HostListener,
|
|
927
|
+
args: ["keydown.end", ["$event"]]
|
|
928
|
+
}
|
|
929
|
+
]
|
|
930
|
+
}
|
|
931
|
+
});
|
|
932
|
+
export { ComboboxComponent, ComboboxItemTemplateDirective };
|
|
23
933
|
|
|
24
|
-
|
|
25
|
-
static { this.HEIGHT_OF_STANDARD_ITEM = 44; }
|
|
26
|
-
compareAlphabetically(textA, textB) {
|
|
27
|
-
if (textA < textB) {
|
|
28
|
-
return -1;
|
|
29
|
-
}
|
|
30
|
-
if (textA > textB) {
|
|
31
|
-
return 1;
|
|
32
|
-
}
|
|
33
|
-
return 0;
|
|
34
|
-
}
|
|
35
|
-
getItemText(item) {
|
|
36
|
-
if (this.isTypeString(item)) {
|
|
37
|
-
return item;
|
|
38
|
-
}
|
|
39
|
-
if (this.objectHasItemTextProperty(item)) {
|
|
40
|
-
const objectItem = item;
|
|
41
|
-
const textValue = objectItem[this.itemTextProperty];
|
|
42
|
-
if (typeof textValue === 'string') {
|
|
43
|
-
return textValue;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
return '';
|
|
47
|
-
}
|
|
48
|
-
get dropdownMaxHeight() {
|
|
49
|
-
return 8 * ComboboxComponent.HEIGHT_OF_STANDARD_ITEM;
|
|
50
|
-
}
|
|
51
|
-
get dropdownMinHeight() {
|
|
52
|
-
return this.itemHeight;
|
|
53
|
-
}
|
|
54
|
-
get viewportHeight() {
|
|
55
|
-
const itemCount = this.searchItems?.length ?? 0;
|
|
56
|
-
if (itemCount === 0) {
|
|
57
|
-
return this.dropdownMinHeight;
|
|
58
|
-
}
|
|
59
|
-
return Math.min(this.dropdownMaxHeight, Math.max(this.dropdownMinHeight, itemCount * this.itemHeight));
|
|
60
|
-
}
|
|
61
|
-
isTypeString(item) {
|
|
62
|
-
return typeof item === 'string';
|
|
63
|
-
}
|
|
64
|
-
objectHasItemTextProperty(item) {
|
|
65
|
-
return item != undefined && typeof item === 'object' && this.itemTextProperty in item;
|
|
66
|
-
}
|
|
67
|
-
objectHasItemIdProperty(item) {
|
|
68
|
-
return item != undefined && typeof item === 'object' && this.itemIdProperty in item;
|
|
69
|
-
}
|
|
70
|
-
getItemId(item) {
|
|
71
|
-
if (this.isTypeString(item)) {
|
|
72
|
-
return item;
|
|
73
|
-
}
|
|
74
|
-
if (this.objectHasItemIdProperty(item)) {
|
|
75
|
-
const objectItem = item;
|
|
76
|
-
const idValue = objectItem[this.itemIdProperty];
|
|
77
|
-
if (this.isTypeString(idValue)) {
|
|
78
|
-
return idValue;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
console.error('Each item must have an id property for scroll to work. Ensure that the itemIdProperty input is set correctly, and that each item has a unique id value.');
|
|
82
|
-
return '';
|
|
83
|
-
}
|
|
84
|
-
get searchItems() {
|
|
85
|
-
return this._searchItems;
|
|
86
|
-
}
|
|
87
|
-
set searchItems(value) {
|
|
88
|
-
this._searchItems = value;
|
|
89
|
-
if (!this._searchItems) {
|
|
90
|
-
return;
|
|
91
|
-
}
|
|
92
|
-
// only set the focused item of the search result, if the input has value
|
|
93
|
-
if (this.textInput?.nativeElement?.value) {
|
|
94
|
-
this.focusedItem = this._searchItems[0];
|
|
95
|
-
this._virtualScrollViewport?.scrollToIndex(0);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
get items() {
|
|
99
|
-
return this._items;
|
|
100
|
-
}
|
|
101
|
-
/**
|
|
102
|
-
* The list of items to display in the dropdown.
|
|
103
|
-
* @param value
|
|
104
|
-
*/
|
|
105
|
-
set items(value) {
|
|
106
|
-
this._items = value;
|
|
107
|
-
this.searchItems = this._items;
|
|
108
|
-
}
|
|
109
|
-
get selectedItem() {
|
|
110
|
-
return this._selectedItem;
|
|
111
|
-
}
|
|
112
|
-
/**
|
|
113
|
-
* The currently selected item.
|
|
114
|
-
* @param value
|
|
115
|
-
*/
|
|
116
|
-
set selectedItem(value) {
|
|
117
|
-
// Allow clearing by setting undefined
|
|
118
|
-
if (value !== this._selectedItem) {
|
|
119
|
-
this._selectedItem = value;
|
|
120
|
-
this.focusedItem = this._selectedItem;
|
|
121
|
-
// Keep ControlValueAccessor value in sync with the selected data item
|
|
122
|
-
this.value = this._selectedItem;
|
|
123
|
-
this.setInputDisplayValue(this.getItemText(this.selectedItem));
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
get focusedItem() {
|
|
127
|
-
return this._focusedItem;
|
|
128
|
-
}
|
|
129
|
-
set focusedItem(item) {
|
|
130
|
-
// the focus might not have changed, but where in the list might
|
|
131
|
-
this.setAriaPosinsetOnElement(item);
|
|
132
|
-
if (this._focusedItem === item) {
|
|
133
|
-
return;
|
|
134
|
-
}
|
|
135
|
-
this._focusedItem = item;
|
|
136
|
-
}
|
|
137
|
-
getIndexOfItem(item) {
|
|
138
|
-
return this.searchItems.indexOf(item);
|
|
139
|
-
}
|
|
140
|
-
getKirbyElement(item) {
|
|
141
|
-
const kirbyItems = this.getKirbyItems();
|
|
142
|
-
if (!kirbyItems || !item) {
|
|
143
|
-
return undefined;
|
|
144
|
-
}
|
|
145
|
-
const itemId = this.getItemId(item);
|
|
146
|
-
return kirbyItems.find((el) => el.nativeElement.id === itemId);
|
|
147
|
-
}
|
|
148
|
-
setAriaPosinsetOnElement(item) {
|
|
149
|
-
const element = this.getKirbyElement(item);
|
|
150
|
-
const index = this.getIndexOfItem(item);
|
|
151
|
-
const setsize = this.searchItems.length;
|
|
152
|
-
if (!element) {
|
|
153
|
-
return;
|
|
154
|
-
}
|
|
155
|
-
this.renderer.setAttribute(element.nativeElement, 'aria-setsize', setsize.toString());
|
|
156
|
-
this.renderer.setAttribute(element.nativeElement, 'aria-posinset', `${index + 1}`);
|
|
157
|
-
}
|
|
158
|
-
/**
|
|
159
|
-
* The direction in which the dropdown should open relative to the input.
|
|
160
|
-
* @param direction
|
|
161
|
-
*/
|
|
162
|
-
set popout(direction) {
|
|
163
|
-
this._popout = direction || HorizontalDirection.right;
|
|
164
|
-
}
|
|
165
|
-
get popout() {
|
|
166
|
-
return this._popout;
|
|
167
|
-
}
|
|
168
|
-
get _isDisabled() {
|
|
169
|
-
return this.disabled ? 'disabled' : null;
|
|
170
|
-
}
|
|
171
|
-
/**
|
|
172
|
-
* When `true`, the combobox will be styled with error styles.
|
|
173
|
-
*/
|
|
174
|
-
get hasError() {
|
|
175
|
-
return this._hasError;
|
|
176
|
-
}
|
|
177
|
-
set hasError(value) {
|
|
178
|
-
if (this._hasError !== value) {
|
|
179
|
-
this._hasError = value;
|
|
180
|
-
this.hasErrorChange.emit(this._hasError);
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
// Prevent Ionic blur on scroll
|
|
184
|
-
get _noBlurOnScroll() {
|
|
185
|
-
return true;
|
|
186
|
-
}
|
|
187
|
-
get _isBlockLevel() {
|
|
188
|
-
return this.expand === 'block';
|
|
189
|
-
}
|
|
190
|
-
get _isOpening() {
|
|
191
|
-
return this.state === OpenState.opening;
|
|
192
|
-
}
|
|
193
|
-
get isOpen() {
|
|
194
|
-
return this.state === OpenState.open;
|
|
195
|
-
}
|
|
196
|
-
get hasValue() {
|
|
197
|
-
return this.selectedItem != null;
|
|
198
|
-
}
|
|
199
|
-
get interactiveElement() {
|
|
200
|
-
return this.textInput.nativeElement;
|
|
201
|
-
}
|
|
202
|
-
forwardAriaLabelToCombobox() {
|
|
203
|
-
forwardAttributes(this.elementRef.nativeElement, this._attributesToForward, this.renderer, this.rootElement.nativeElement);
|
|
204
|
-
}
|
|
205
|
-
set kirbyItemsSlotted(kirbyItems) {
|
|
206
|
-
const hasSlottedItems = this.disposeItemClickListeners?.length > 0;
|
|
207
|
-
if (hasSlottedItems) {
|
|
208
|
-
this.unlistenAllSlottedItems();
|
|
209
|
-
}
|
|
210
|
-
kirbyItems.forEach((kirbyItem) => {
|
|
211
|
-
this.renderer.setAttribute(kirbyItem.nativeElement, 'role', 'option');
|
|
212
|
-
const disposeClickListener = this.renderer.listen(kirbyItem.nativeElement, 'click', () => {
|
|
213
|
-
const id = kirbyItem.nativeElement.getAttribute('id');
|
|
214
|
-
const item = this.items.find((it) => this.getItemId(it) === id);
|
|
215
|
-
if (item) {
|
|
216
|
-
this.onItemSelect(item);
|
|
217
|
-
}
|
|
218
|
-
});
|
|
219
|
-
this.disposeItemClickListeners.push(disposeClickListener);
|
|
220
|
-
});
|
|
221
|
-
this._kirbyItemsSlotted = kirbyItems;
|
|
222
|
-
}
|
|
223
|
-
get kirbyItemsSlotted() {
|
|
224
|
-
return this._kirbyItemsSlotted;
|
|
225
|
-
}
|
|
226
|
-
constructor(renderer, elementRef, cdr, resizeObserverService) {
|
|
227
|
-
this.renderer = renderer;
|
|
228
|
-
this.elementRef = elementRef;
|
|
229
|
-
this.cdr = cdr;
|
|
230
|
-
this.resizeObserverService = resizeObserverService;
|
|
231
|
-
this.state = OpenState.closed;
|
|
232
|
-
this._popout = HorizontalDirection.right;
|
|
233
|
-
this._attributesToForward = ['aria-label', 'aria-labelledby'];
|
|
234
|
-
this._listboxId = UniqueIdGenerator.scopedTo('kirby-x-combobox').next();
|
|
235
|
-
this._comboboxId = UniqueIdGenerator.scopedTo('kirby-input').next();
|
|
236
|
-
this._defaultSearchFunction = (searchTerm, itemsToSearch) => {
|
|
237
|
-
if (!searchTerm) {
|
|
238
|
-
return itemsToSearch;
|
|
239
|
-
}
|
|
240
|
-
const lowerSearchTerm = searchTerm.toLowerCase();
|
|
241
|
-
return itemsToSearch
|
|
242
|
-
.filter((item) => this.getItemText(item).toLowerCase().includes(lowerSearchTerm))
|
|
243
|
-
.sort((a, b) => {
|
|
244
|
-
const textA = this.getItemText(a).toLowerCase();
|
|
245
|
-
const textB = this.getItemText(b).toLowerCase();
|
|
246
|
-
const textAStartsWith = textA.startsWith(searchTerm);
|
|
247
|
-
const textBStartsWith = textB.startsWith(searchTerm);
|
|
248
|
-
if (textAStartsWith && textBStartsWith) {
|
|
249
|
-
return this.compareAlphabetically(textA, textB);
|
|
250
|
-
}
|
|
251
|
-
if (textAStartsWith) {
|
|
252
|
-
return -1;
|
|
253
|
-
}
|
|
254
|
-
if (textBStartsWith) {
|
|
255
|
-
return 1;
|
|
256
|
-
}
|
|
257
|
-
return this.compareAlphabetically(textA, textB);
|
|
258
|
-
});
|
|
259
|
-
};
|
|
260
|
-
/**
|
|
261
|
-
* The text to display when the search returns no results.
|
|
262
|
-
*/
|
|
263
|
-
this.noSearchResultsText = 'No results found.';
|
|
264
|
-
/**
|
|
265
|
-
* The text announced by screen readers when the selection is cleared.
|
|
266
|
-
*/
|
|
267
|
-
this.selectionClearedAnnouncement = 'Selection cleared.';
|
|
268
|
-
this._liveRegionText = '';
|
|
269
|
-
/**
|
|
270
|
-
* The name of the property to use as the display text for each item.
|
|
271
|
-
*/
|
|
272
|
-
this.itemTextProperty = 'text';
|
|
273
|
-
/**
|
|
274
|
-
* The height of each item in the dropdown, in pixels. This is used to calculate the height of the dropdown based on the number of items.
|
|
275
|
-
*/
|
|
276
|
-
this.itemHeight = 44;
|
|
277
|
-
/**
|
|
278
|
-
* The name of the property to use as the unique identifier for each item.
|
|
279
|
-
*/
|
|
280
|
-
this.itemIdProperty = 'id';
|
|
281
|
-
/**
|
|
282
|
-
* A function that takes a search term and the list of items, and returns a filtered list of items to display in the dropdown.
|
|
283
|
-
*/
|
|
284
|
-
this.searchFunction = this._defaultSearchFunction;
|
|
285
|
-
this._searchItems = [];
|
|
286
|
-
this._items = [];
|
|
287
|
-
this._selectedItem = undefined;
|
|
288
|
-
// _focusedItem keeps track of which element has focus and will be selected
|
|
289
|
-
// if it is activated (by pressing ENTER)
|
|
290
|
-
this._focusedItem = undefined;
|
|
291
|
-
/**
|
|
292
|
-
* The placeholder text to display in the input when no item is selected and the input is empty.
|
|
293
|
-
*/
|
|
294
|
-
this.placeholder = 'Please search...';
|
|
295
|
-
/**
|
|
296
|
-
* When `true`, the user cannot interact with the combobox.
|
|
297
|
-
*/
|
|
298
|
-
this.disabled = false;
|
|
299
|
-
/**
|
|
300
|
-
* Will emit the new value when error changes.
|
|
301
|
-
*/
|
|
302
|
-
this.hasErrorChange = new EventEmitter();
|
|
303
|
-
this._hasError = false;
|
|
304
|
-
/**
|
|
305
|
-
* The size of the input element.
|
|
306
|
-
*/
|
|
307
|
-
this.size = InputSize.medium;
|
|
308
|
-
/**
|
|
309
|
-
* Emitted when an item is selected (tap on mobile, click/keypress on web)
|
|
310
|
-
*/
|
|
311
|
-
this.change = new EventEmitter();
|
|
312
|
-
this.value = undefined;
|
|
313
|
-
this.disposeItemClickListeners = [];
|
|
314
|
-
this.onChange = () => {
|
|
315
|
-
/* empty */
|
|
316
|
-
};
|
|
317
|
-
this.onTouched = () => {
|
|
318
|
-
/* empty */
|
|
319
|
-
};
|
|
320
|
-
}
|
|
321
|
-
onToggle(event) {
|
|
322
|
-
event.stopPropagation();
|
|
323
|
-
this.textInput?.nativeElement.focus();
|
|
324
|
-
this.toggle();
|
|
325
|
-
}
|
|
326
|
-
toggle() {
|
|
327
|
-
if (this.disabled) {
|
|
328
|
-
return;
|
|
329
|
-
}
|
|
330
|
-
this.isOpen ? this.close() : this.open();
|
|
331
|
-
}
|
|
332
|
-
ngAfterViewInit() {
|
|
333
|
-
if (this.expand === 'block') {
|
|
334
|
-
const { width: initialWidth } = this.elementRef.nativeElement.getBoundingClientRect();
|
|
335
|
-
this.setPopoverCardStyle('max-width', 'initial');
|
|
336
|
-
this.setPopoverCardStyle('min-width', 'initial');
|
|
337
|
-
// Ensure initial width is set even if the resize observer callback also fires initially:
|
|
338
|
-
this.setPopoverCardStyle('--kirby-card-width', `${initialWidth}px`);
|
|
339
|
-
this.resizeObserverService.observe(this.elementRef, (entry) => {
|
|
340
|
-
const newWidth = entry.contentRect.width;
|
|
341
|
-
if (newWidth > 0) {
|
|
342
|
-
this.setPopoverCardStyle('--kirby-card-width', `${newWidth}px`);
|
|
343
|
-
}
|
|
344
|
-
});
|
|
345
|
-
}
|
|
346
|
-
this.forwardAriaLabelToCombobox();
|
|
347
|
-
}
|
|
348
|
-
/* Utility that makes it easier to set styles on card element */
|
|
349
|
-
setPopoverCardStyle(style, value) {
|
|
350
|
-
this.renderer.setStyle(this.cardElement?.nativeElement, style, value, RendererStyleFlags2.DashCase);
|
|
351
|
-
}
|
|
352
|
-
ngOnDestroy() {
|
|
353
|
-
this.unlistenAllSlottedItems();
|
|
354
|
-
this.unobserveResize();
|
|
355
|
-
}
|
|
356
|
-
open() {
|
|
357
|
-
if (this.disabled) {
|
|
358
|
-
return;
|
|
359
|
-
}
|
|
360
|
-
if (!this.isOpen) {
|
|
361
|
-
this.state = OpenState.opening;
|
|
362
|
-
this.showPopOver();
|
|
363
|
-
this.focusedItem = this.selectedItem;
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
showPopOver() {
|
|
367
|
-
if (this.state === OpenState.opening) {
|
|
368
|
-
this.state = OpenState.open;
|
|
369
|
-
this.popover?.show();
|
|
370
|
-
this.cdr.markForCheck();
|
|
371
|
-
// PopoverComponent.show() appends the element to document.body synchronously but
|
|
372
|
-
// finishes positioning in a requestAnimationFrame. One rAF is enough to let the
|
|
373
|
-
// browser compute layout so the CDK viewport has a real size before we scroll.
|
|
374
|
-
requestAnimationFrame(() => this.scrollToIndexIntoViewWhenOpeningPopup());
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
close() {
|
|
378
|
-
if (this.disabled) {
|
|
379
|
-
return;
|
|
380
|
-
}
|
|
381
|
-
if (this.isOpen) {
|
|
382
|
-
this.state = OpenState.closed;
|
|
383
|
-
this.setInputDisplayValue(this.getItemText(this.selectedItem));
|
|
384
|
-
this.searchItems = this.items;
|
|
385
|
-
this.popover?.hide();
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
onItemSelect(item) {
|
|
389
|
-
// Guard: slotted click listeners should map back to a data item, but be safe.
|
|
390
|
-
if (item instanceof HTMLElement)
|
|
391
|
-
return;
|
|
392
|
-
this.selectItem(item);
|
|
393
|
-
this.close();
|
|
394
|
-
}
|
|
395
|
-
/**
|
|
396
|
-
* Sets the select's value. Part of the ControlValueAccessor interface
|
|
397
|
-
* required to integrate with Angular's core forms API.
|
|
398
|
-
*
|
|
399
|
-
* @param value New value to be written to the model.
|
|
400
|
-
*/
|
|
401
|
-
writeValue(value) {
|
|
402
|
-
this.selectItemByInput(value);
|
|
403
|
-
// When written from outside, reflect selected text in the input
|
|
404
|
-
this.setInputDisplayValue(this.getItemText(this.value));
|
|
405
|
-
this.cdr.markForCheck();
|
|
406
|
-
}
|
|
407
|
-
/**
|
|
408
|
-
* Saves a callback function to be invoked when the select's value
|
|
409
|
-
* changes from user input. Part of the ControlValueAccessor interface
|
|
410
|
-
* required to integrate with Angular's core forms API.
|
|
411
|
-
*
|
|
412
|
-
* @param fn Callback to be triggered when the value changes.
|
|
413
|
-
*/
|
|
414
|
-
registerOnChange(fn) {
|
|
415
|
-
this.onChange = fn;
|
|
416
|
-
}
|
|
417
|
-
/**
|
|
418
|
-
* Saves a callback function to be invoked when the select is blurred
|
|
419
|
-
* by the user. Part of the ControlValueAccessor interface required
|
|
420
|
-
* to integrate with Angular's core forms API.
|
|
421
|
-
*
|
|
422
|
-
* @param fn Callback to be triggered when the component has been touched.
|
|
423
|
-
*/
|
|
424
|
-
registerOnTouched(fn) {
|
|
425
|
-
this.onTouched = fn;
|
|
426
|
-
}
|
|
427
|
-
/**
|
|
428
|
-
* Disables the select. Part of the ControlValueAccessor interface required
|
|
429
|
-
* to integrate with Angular's core forms API.
|
|
430
|
-
*
|
|
431
|
-
* @param isDisabled Sets whether the component is disabled.
|
|
432
|
-
*/
|
|
433
|
-
setDisabledState(isDisabled) {
|
|
434
|
-
this.disabled = isDisabled;
|
|
435
|
-
this.cdr.markForCheck();
|
|
436
|
-
}
|
|
437
|
-
selectItem(item) {
|
|
438
|
-
if (item != this.selectedItem) {
|
|
439
|
-
this.selectedItem = item;
|
|
440
|
-
this.focusedItem = item;
|
|
441
|
-
this.change.emit(this.value);
|
|
442
|
-
this.onChange(this.value);
|
|
443
|
-
this.setInputDisplayValue(this.getItemText(item));
|
|
444
|
-
this.searchItems = this.items;
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
selectItemByInput(input) {
|
|
448
|
-
this.selectedItem = this.findItemByInput(input);
|
|
449
|
-
}
|
|
450
|
-
onInput(event) {
|
|
451
|
-
if (!this.isOpen) {
|
|
452
|
-
this.open();
|
|
453
|
-
}
|
|
454
|
-
const input = event.target;
|
|
455
|
-
if (!input.value) {
|
|
456
|
-
this.clearSelection();
|
|
457
|
-
}
|
|
458
|
-
this.updateSearchResults(input.value);
|
|
459
|
-
// Screen readers (VoiceOver/NVDA) may move real browser focus to a list-item button.
|
|
460
|
-
// When the list re-renders after filtering, that button is removed from the DOM and
|
|
461
|
-
// focus falls to `document.body`. Restore it to the input so the user can keep typing
|
|
462
|
-
// and the focusout handler doesn't mistake the body-focus for a genuine blur-away.
|
|
463
|
-
if (document.activeElement !== this.textInput?.nativeElement) {
|
|
464
|
-
this.textInput?.nativeElement.focus();
|
|
465
|
-
}
|
|
466
|
-
}
|
|
467
|
-
clearSelection() {
|
|
468
|
-
this.selectItem(undefined);
|
|
469
|
-
// Reset after a short delay so the same message can be announced again on subsequent clears
|
|
470
|
-
this.announce(this.selectionClearedAnnouncement);
|
|
471
|
-
}
|
|
472
|
-
announce(message) {
|
|
473
|
-
clearTimeout(this._announceTimeout);
|
|
474
|
-
this._liveRegionText = '';
|
|
475
|
-
this._announceTimeout = setTimeout(() => {
|
|
476
|
-
this._liveRegionText = message;
|
|
477
|
-
this.cdr.markForCheck();
|
|
478
|
-
}, 100);
|
|
479
|
-
}
|
|
480
|
-
updateSearchResults(inputValue) {
|
|
481
|
-
this.searchItems = inputValue ? this.searchFunction(inputValue, this.items) : this.items;
|
|
482
|
-
if (inputValue && this.searchItems.length === 0) {
|
|
483
|
-
this.announce(this.noSearchResultsText);
|
|
484
|
-
}
|
|
485
|
-
}
|
|
486
|
-
onPopoverWillHide() {
|
|
487
|
-
this.state = OpenState.closed;
|
|
488
|
-
this.rootElement.nativeElement.focus();
|
|
489
|
-
this.onTouched();
|
|
490
|
-
}
|
|
491
|
-
onPopoverClick() {
|
|
492
|
-
this.close();
|
|
493
|
-
}
|
|
494
|
-
onMouseDown(event) {
|
|
495
|
-
if (this.disabled) {
|
|
496
|
-
event.preventDefault();
|
|
497
|
-
event.stopImmediatePropagation();
|
|
498
|
-
}
|
|
499
|
-
}
|
|
500
|
-
onTouchStart(event) {
|
|
501
|
-
if (this.isOpen) {
|
|
502
|
-
event.stopPropagation();
|
|
503
|
-
}
|
|
504
|
-
}
|
|
505
|
-
onFocusOut() {
|
|
506
|
-
// Defer the check to allow the browser (and screen readers like VoiceOver/NVDA)
|
|
507
|
-
// to settle focus. `relatedTarget` is often `null` with screen readers, so we
|
|
508
|
-
// inspect `document.activeElement` after the microtask queue has flushed.
|
|
509
|
-
setTimeout(() => {
|
|
510
|
-
const activeElement = document.activeElement;
|
|
511
|
-
const isOnTriggerButton = activeElement && this.elementRef.nativeElement.contains(activeElement);
|
|
512
|
-
const isInsidePopover = activeElement && activeElement.closest('kirby-popover');
|
|
513
|
-
if (isOnTriggerButton || isInsidePopover) {
|
|
514
|
-
// Focus is still within the component — nothing to do.
|
|
515
|
-
return;
|
|
516
|
-
}
|
|
517
|
-
// When a screen reader (VoiceOver/NVDA) moves real focus to a list-item button
|
|
518
|
-
// and the user then types a character, CDK virtual scroll re-renders the list and
|
|
519
|
-
// removes the focused button from the DOM. The browser then drops focus to
|
|
520
|
-
// `document.body`. Detect this case: if the popover is still showing and focus is
|
|
521
|
-
// on the body, it means focus was lost due to DOM removal — restore it to the
|
|
522
|
-
// input rather than closing the popover.
|
|
523
|
-
if (this.isOpen && activeElement === document.body) {
|
|
524
|
-
this.textInput?.nativeElement.focus();
|
|
525
|
-
return;
|
|
526
|
-
}
|
|
527
|
-
if (this.isOpen) {
|
|
528
|
-
this.close();
|
|
529
|
-
}
|
|
530
|
-
this.onTouched();
|
|
531
|
-
}, 0);
|
|
532
|
-
}
|
|
533
|
-
onTab() {
|
|
534
|
-
if (this.isOpen) {
|
|
535
|
-
this.selectItem(this.focusedItem);
|
|
536
|
-
this.close();
|
|
537
|
-
}
|
|
538
|
-
}
|
|
539
|
-
onEscape() {
|
|
540
|
-
this.close();
|
|
541
|
-
}
|
|
542
|
-
onEnter(event) {
|
|
543
|
-
event.preventDefault();
|
|
544
|
-
event.stopPropagation();
|
|
545
|
-
if (this.isOpen) {
|
|
546
|
-
this.selectItem(this.focusedItem);
|
|
547
|
-
}
|
|
548
|
-
this.toggle();
|
|
549
|
-
}
|
|
550
|
-
onArrowKeys(event) {
|
|
551
|
-
if (this.disabled)
|
|
552
|
-
return;
|
|
553
|
-
const keyEvent = event; // safe in this context
|
|
554
|
-
// Avoid page scroll
|
|
555
|
-
event.preventDefault();
|
|
556
|
-
if (!this.isOpen) {
|
|
557
|
-
this.open();
|
|
558
|
-
if (this.selectedItem) {
|
|
559
|
-
this.setFocusOnSelectedItem();
|
|
560
|
-
}
|
|
561
|
-
else {
|
|
562
|
-
this.setFocusOnFirstOrLastItem(keyEvent);
|
|
563
|
-
}
|
|
564
|
-
}
|
|
565
|
-
else {
|
|
566
|
-
this.handleFocusNavigation(keyEvent);
|
|
567
|
-
}
|
|
568
|
-
}
|
|
569
|
-
handleFocusNavigation(keyEvent) {
|
|
570
|
-
switch (keyEvent.key) {
|
|
571
|
-
case 'ArrowDown':
|
|
572
|
-
this.shiftFocusIndex(1);
|
|
573
|
-
break;
|
|
574
|
-
case 'ArrowUp':
|
|
575
|
-
this.shiftFocusIndex(-1);
|
|
576
|
-
break;
|
|
577
|
-
case 'PageDown':
|
|
578
|
-
this.shiftFocusIndex(10);
|
|
579
|
-
break;
|
|
580
|
-
case 'PageUp':
|
|
581
|
-
this.shiftFocusIndex(-10);
|
|
582
|
-
break;
|
|
583
|
-
case 'Home':
|
|
584
|
-
this.setFocusOnFirstItem();
|
|
585
|
-
break;
|
|
586
|
-
case 'End':
|
|
587
|
-
this.setFocusOnLastItem();
|
|
588
|
-
break;
|
|
589
|
-
default:
|
|
590
|
-
break;
|
|
591
|
-
}
|
|
592
|
-
}
|
|
593
|
-
shiftFocusIndex(numberOfItems) {
|
|
594
|
-
// Handle up/down navigation when open
|
|
595
|
-
const currentIndex = this.searchItems.indexOf(this.focusedItem);
|
|
596
|
-
let newIndex;
|
|
597
|
-
if (numberOfItems > 0) {
|
|
598
|
-
newIndex = Math.min(this.searchItems.length - 1, currentIndex + numberOfItems);
|
|
599
|
-
}
|
|
600
|
-
else {
|
|
601
|
-
newIndex = Math.max(0, currentIndex + numberOfItems);
|
|
602
|
-
}
|
|
603
|
-
if (newIndex !== currentIndex) {
|
|
604
|
-
this.focusedItem = this.searchItems[newIndex];
|
|
605
|
-
}
|
|
606
|
-
this.scrollFocusedItemIntoViewWhileNavigating();
|
|
607
|
-
}
|
|
608
|
-
setFocusOnFirstOrLastItem(keyEvent) {
|
|
609
|
-
switch (keyEvent.key) {
|
|
610
|
-
case 'ArrowDown':
|
|
611
|
-
case 'PageDown':
|
|
612
|
-
case 'Home':
|
|
613
|
-
this.setFocusOnFirstItem();
|
|
614
|
-
break;
|
|
615
|
-
case 'ArrowUp':
|
|
616
|
-
case 'PageUp':
|
|
617
|
-
case 'End':
|
|
618
|
-
this.setFocusOnLastItem();
|
|
619
|
-
break;
|
|
620
|
-
default:
|
|
621
|
-
break;
|
|
622
|
-
}
|
|
623
|
-
}
|
|
624
|
-
setFocusOnSelectedItem() {
|
|
625
|
-
if (this.selectedItem) {
|
|
626
|
-
this.focusedItem = this.selectedItem;
|
|
627
|
-
}
|
|
628
|
-
}
|
|
629
|
-
setFocusOnLastItem() {
|
|
630
|
-
if (this.searchItems.length > 0) {
|
|
631
|
-
this.focusedItem = this.searchItems[this.searchItems.length - 1];
|
|
632
|
-
}
|
|
633
|
-
}
|
|
634
|
-
setFocusOnFirstItem() {
|
|
635
|
-
if (this.searchItems.length > 0) {
|
|
636
|
-
this.focusedItem = this.searchItems[0];
|
|
637
|
-
}
|
|
638
|
-
}
|
|
639
|
-
unlistenAllSlottedItems() {
|
|
640
|
-
let disposeClickListener;
|
|
641
|
-
while ((disposeClickListener = this.disposeItemClickListeners.pop()) !== undefined) {
|
|
642
|
-
disposeClickListener();
|
|
643
|
-
}
|
|
644
|
-
}
|
|
645
|
-
unobserveResize() {
|
|
646
|
-
this.resizeObserverService.unobserve(this.elementRef);
|
|
647
|
-
}
|
|
648
|
-
/**
|
|
649
|
-
* Resolve a model/data item based on current `items` by comparing displayed text.
|
|
650
|
-
* This is used to map an incoming form value or input `selectedItem` to an item
|
|
651
|
-
* from the list.
|
|
652
|
-
*/
|
|
653
|
-
findItemByInput(input) {
|
|
654
|
-
if (!this.items || this.items.length === 0) {
|
|
655
|
-
return input;
|
|
656
|
-
}
|
|
657
|
-
return this.items.find((it) => this.getItemText(it) === input) ?? input;
|
|
658
|
-
}
|
|
659
|
-
setInputDisplayValue(value) {
|
|
660
|
-
// We intentionally update the DOM input imperatively ("hard way")
|
|
661
|
-
// to ensure the visible value is cleared/updated immediately.
|
|
662
|
-
if (this.interactiveElement) {
|
|
663
|
-
this.renderer.setProperty(this.interactiveElement, 'value', value);
|
|
664
|
-
}
|
|
665
|
-
}
|
|
666
|
-
scrollFocusedItemIntoViewWhileNavigating() {
|
|
667
|
-
if (!this.focusedItem || !this._virtualScrollViewport) {
|
|
668
|
-
return;
|
|
669
|
-
}
|
|
670
|
-
const focusedIndex = this.searchItems.indexOf(this.focusedItem);
|
|
671
|
-
if (focusedIndex === -1)
|
|
672
|
-
return;
|
|
673
|
-
const itemTop = focusedIndex * this.itemHeight;
|
|
674
|
-
const itemBottom = itemTop + this.itemHeight;
|
|
675
|
-
const scrollOffset = this._virtualScrollViewport.measureScrollOffset();
|
|
676
|
-
const viewportSize = this._virtualScrollViewport.getViewportSize();
|
|
677
|
-
if (itemTop < scrollOffset) {
|
|
678
|
-
// Item is above the visible area — scroll up so item appears at the top.
|
|
679
|
-
this._virtualScrollViewport.scrollToOffset(itemTop);
|
|
680
|
-
}
|
|
681
|
-
else if (itemBottom > scrollOffset + viewportSize) {
|
|
682
|
-
// Item is below the visible area — scroll down so item appears at the bottom.
|
|
683
|
-
this._virtualScrollViewport.scrollToOffset(itemBottom - viewportSize);
|
|
684
|
-
}
|
|
685
|
-
this._virtualScrollViewport.checkViewportSize();
|
|
686
|
-
}
|
|
687
|
-
getKirbyItems() {
|
|
688
|
-
return this.kirbyItemsSlotted && this.kirbyItemsSlotted.length
|
|
689
|
-
? this.kirbyItemsSlotted
|
|
690
|
-
: this.kirbyItemsDefault;
|
|
691
|
-
}
|
|
692
|
-
scrollToIndexIntoViewWhenOpeningPopup() {
|
|
693
|
-
const focusedIndex = this.searchItems.indexOf(this.focusedItem);
|
|
694
|
-
this._virtualScrollViewport?.setRenderedRange({ start: 0, end: 20 });
|
|
695
|
-
this._virtualScrollViewport?.checkViewportSize();
|
|
696
|
-
this._virtualScrollViewport?.scrollToIndex(focusedIndex);
|
|
697
|
-
}
|
|
698
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.23", ngImport: i0, type: ComboboxComponent, deps: [{ token: i0.Renderer2 }, { token: i0.ElementRef }, { token: i0.ChangeDetectorRef }, { token: i1.ResizeObserverService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
699
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.23", type: ComboboxComponent, isStandalone: true, selector: "kirby-x-combobox", inputs: { noSearchResultsText: "noSearchResultsText", selectionClearedAnnouncement: "selectionClearedAnnouncement", itemTextProperty: "itemTextProperty", itemHeight: "itemHeight", itemIdProperty: "itemIdProperty", searchFunction: "searchFunction", items: "items", selectedItem: "selectedItem", placeholder: "placeholder", popout: "popout", expand: "expand", disabled: "disabled", hasError: "hasError", size: "size" }, outputs: { hasErrorChange: "hasErrorChange", change: "change" }, host: { listeners: { "mousedown": "onMouseDown($event)", "touchstart": "onTouchStart($event)", "focusout": "onFocusOut()", "keydown.tab": "onTab()", "keydown.escape": "onEscape()", "keydown.enter": "onEnter($event)", "keydown.arrowUp": "onArrowKeys($event)", "keydown.arrowDown": "onArrowKeys($event)", "keydown.pageUp": "onArrowKeys($event)", "keydown.pageDown": "onArrowKeys($event)", "keydown.home": "onArrowKeys($event)", "keydown.end": "onArrowKeys($event)" }, properties: { "attr.disabled": "this._isDisabled", "attr.no-blur": "this._noBlurOnScroll", "class.expand": "this._isBlockLevel", "class.is-opening": "this._isOpening", "class.is-open": "this.isOpen", "class.has-value": "this.hasValue" } }, providers: [
|
|
700
|
-
{
|
|
701
|
-
provide: NG_VALUE_ACCESSOR,
|
|
702
|
-
useExisting: forwardRef(() => ComboboxComponent),
|
|
703
|
-
multi: true,
|
|
704
|
-
},
|
|
705
|
-
{
|
|
706
|
-
provide: FORM_FIELD_CONTROL,
|
|
707
|
-
useExisting: forwardRef(() => ComboboxComponent),
|
|
708
|
-
},
|
|
709
|
-
], queries: [{ propertyName: "itemTemplate", first: true, predicate: ListItemTemplateDirective, descendants: true, read: TemplateRef }, { propertyName: "slottedItems", predicate: ListItemTemplateDirective, read: ElementRef }, { propertyName: "kirbyItemsSlotted", predicate: ItemComponent, read: ElementRef }], viewQueries: [{ propertyName: "cardElement", first: true, predicate: CardComponent, descendants: true, read: ElementRef }, { propertyName: "popover", first: true, predicate: PopoverComponent, descendants: true }, { propertyName: "rootElement", first: true, predicate: ["rootElement"], descendants: true, read: ElementRef, static: true }, { propertyName: "textInput", first: true, predicate: InputComponent, descendants: true, read: ElementRef, static: true }, { propertyName: "_virtualScrollViewport", first: true, predicate: CdkVirtualScrollViewport, descendants: true }, { propertyName: "kirbyItemsDefault", predicate: ItemComponent, descendants: true, read: ElementRef }], ngImport: i0, template: "<span class=\"kirby-visually-hidden\" aria-live=\"polite\" aria-atomic=\"true\">\n {{ _liveRegionText }}\n</span>\n<div (click)=\"onToggle($event)\" #rootElement>\n <input\n kirby-input\n type=\"text\"\n aria-haspopup=\"listbox\"\n role=\"combobox\"\n [hasError]=\"hasError\"\n [size]=\"size\"\n [disabled]=\"disabled\"\n [placeholder]=\"placeholder\"\n (change)=\"$event.stopPropagation()\"\n [attr.id]=\"_comboboxId\"\n [attr.aria-controls]=\"_listboxId\"\n [attr.aria-expanded]=\"isOpen\"\n [attr.aria-activedescendant]=\"isOpen && focusedItem ? getItemId(focusedItem) : null\"\n (input)=\"onInput($event)\"\n />\n <kirby-icon [name]=\"isOpen ? 'arrow-up' : 'arrow-down'\" kirby-affix=\"suffix\"></kirby-icon>\n</div>\n\n<kirby-popover\n [target]=\"rootElement\"\n [popout]=\"popout\"\n (click)=\"onPopoverClick()\"\n (mousedown)=\"$event.preventDefault()\"\n (willHide)=\"onPopoverWillHide()\"\n>\n <kirby-card [attr.id]=\"_listboxId\" [attr.aria-labelledby]=\"_comboboxId\">\n @if (searchItems.length === 0) {\n <kirby-item>\n <p class=\"kirby-item-title no-results\">{{ noSearchResultsText }}</p>\n </kirby-item>\n } @else {\n <cdk-virtual-scroll-viewport\n class=\"combobox-viewport\"\n role=\"listbox\"\n [itemSize]=\"itemHeight\"\n [style.height.px]=\"viewportHeight\"\n >\n <ng-container *cdkVirtualFor=\"let item of searchItems\">\n <ng-container\n *ngTemplateOutlet=\"\n itemTemplate || defaultItemTemplate;\n context: {\n $implicit: item,\n selected: item === selectedItem,\n focused: item === focusedItem,\n }\n \"\n ></ng-container>\n </ng-container>\n </cdk-virtual-scroll-viewport>\n }\n </kirby-card>\n</kirby-popover>\n\n<ng-template #defaultItemTemplate let-item let-selected=\"selected\" let-focused=\"focused\">\n <kirby-item\n role=\"option\"\n [selectable]=\"true\"\n [selected]=\"selected\"\n [class.focused]=\"focused\"\n [attr.id]=\"getItemId(item)\"\n [attr.aria-selected]=\"selected\"\n (click)=\"onItemSelect(item)\"\n >\n <p class=\"kirby-item-title\">{{ getItemText(item) }}</p>\n @if (selected) {\n <kirby-icon name=\"checkmark-selected\" slot=\"end\"></kirby-icon>\n }\n </kirby-item>\n</ng-template>\n", styles: [".kirby-line-clamp{display:-webkit-box;-webkit-line-clamp:var(--line-clamp, none);-webkit-box-orient:vertical;overflow:hidden}:host:focus-visible{transition:all 80ms linear 0ms;box-shadow:var(--kirby-focus-ring-md-inset);outline:none;border-radius:var(--kirby-border-radius-pill)}:host{display:inline-block;position:relative;max-width:calc(100vw - calc(2 * var(--kirby-spacing-s)))}:host.clicked{box-shadow:none}:host.expand{display:block}:host.expand kirby-card{width:100%;min-width:initial;max-width:initial}:host div{position:relative}:host div>input[kirby-input]{padding-right:var(--kirby-spacing-xl)}:host div>input[kirby-input]:disabled~kirby-icon{color:var(--kirby-semi-dark-shade)}:host div>kirby-icon{position:absolute;top:0;bottom:0;right:var(--kirby-spacing-xs)}:host.has-value>div>input[kirby-input]{text-overflow:ellipsis}kirby-popover{--max-height: 352px}kirby-card{display:flex;flex-direction:column;max-height:352px;overflow:hidden;box-shadow:var(--kirby-elevation-4);max-width:calc(100vw - calc(2 * var(--kirby-spacing-s)));min-width:calc(320px - calc(2 * var(--kirby-spacing-s)))}@media(min-width:321px){kirby-card{min-width:calc(375px - calc(2 * var(--kirby-spacing-s)))}}.combobox-viewport{display:block;width:100%}.no-results{color:var(--kirby-text-color-semi-dark)}\n"], dependencies: [{ kind: "component", type: InputComponent, selector: "input[kirby-input]", inputs: ["type", "size", "borderless", "hasError", "autocomplete", "autocorrect", "value", "maxlength", "inputmode"], outputs: ["hasErrorChange"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: PopoverComponent, selector: "kirby-popover", inputs: ["popout", "target"], outputs: ["willHide"] }, { kind: "component", type: CardComponent, selector: "kirby-card", inputs: ["title", "subtitle", "backgroundImageUrl", "hasPadding", "sizes", "variant"] }, { kind: "component", type: ItemComponent, selector: "kirby-item", inputs: ["disabled", "selected", "disclosure", "selectable", "reorderable", "size", "href", "rotateIcon"] }, { kind: "component", type: IconComponent, selector: "kirby-icon", inputs: ["size", "name"] }, { kind: "directive", type: AffixDirective, selector: "[kirby-affix]", inputs: ["kirby-affix"] }, { kind: "directive", type: CdkFixedSizeVirtualScroll, selector: "cdk-virtual-scroll-viewport[itemSize]", inputs: ["itemSize", "minBufferPx", "maxBufferPx"] }, { kind: "component", type: CdkVirtualScrollViewport, selector: "cdk-virtual-scroll-viewport", inputs: ["orientation", "appendOnly"], outputs: ["scrolledIndexChange"] }, { kind: "directive", type: CdkVirtualForOf, selector: "[cdkVirtualFor][cdkVirtualForOf]", inputs: ["cdkVirtualForOf", "cdkVirtualForTrackBy", "cdkVirtualForTemplate", "cdkVirtualForTemplateCacheSize"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
710
|
-
}
|
|
711
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.23", ngImport: i0, type: ComboboxComponent, decorators: [{
|
|
712
|
-
type: Component,
|
|
713
|
-
args: [{ selector: 'kirby-x-combobox', providers: [
|
|
714
|
-
{
|
|
715
|
-
provide: NG_VALUE_ACCESSOR,
|
|
716
|
-
useExisting: forwardRef(() => ComboboxComponent),
|
|
717
|
-
multi: true,
|
|
718
|
-
},
|
|
719
|
-
{
|
|
720
|
-
provide: FORM_FIELD_CONTROL,
|
|
721
|
-
useExisting: forwardRef(() => ComboboxComponent),
|
|
722
|
-
},
|
|
723
|
-
], imports: [
|
|
724
|
-
InputComponent,
|
|
725
|
-
NgTemplateOutlet,
|
|
726
|
-
PopoverComponent,
|
|
727
|
-
CardComponent,
|
|
728
|
-
ItemComponent,
|
|
729
|
-
IconComponent,
|
|
730
|
-
AffixDirective,
|
|
731
|
-
CdkFixedSizeVirtualScroll,
|
|
732
|
-
CdkVirtualScrollViewport,
|
|
733
|
-
CdkVirtualForOf,
|
|
734
|
-
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<span class=\"kirby-visually-hidden\" aria-live=\"polite\" aria-atomic=\"true\">\n {{ _liveRegionText }}\n</span>\n<div (click)=\"onToggle($event)\" #rootElement>\n <input\n kirby-input\n type=\"text\"\n aria-haspopup=\"listbox\"\n role=\"combobox\"\n [hasError]=\"hasError\"\n [size]=\"size\"\n [disabled]=\"disabled\"\n [placeholder]=\"placeholder\"\n (change)=\"$event.stopPropagation()\"\n [attr.id]=\"_comboboxId\"\n [attr.aria-controls]=\"_listboxId\"\n [attr.aria-expanded]=\"isOpen\"\n [attr.aria-activedescendant]=\"isOpen && focusedItem ? getItemId(focusedItem) : null\"\n (input)=\"onInput($event)\"\n />\n <kirby-icon [name]=\"isOpen ? 'arrow-up' : 'arrow-down'\" kirby-affix=\"suffix\"></kirby-icon>\n</div>\n\n<kirby-popover\n [target]=\"rootElement\"\n [popout]=\"popout\"\n (click)=\"onPopoverClick()\"\n (mousedown)=\"$event.preventDefault()\"\n (willHide)=\"onPopoverWillHide()\"\n>\n <kirby-card [attr.id]=\"_listboxId\" [attr.aria-labelledby]=\"_comboboxId\">\n @if (searchItems.length === 0) {\n <kirby-item>\n <p class=\"kirby-item-title no-results\">{{ noSearchResultsText }}</p>\n </kirby-item>\n } @else {\n <cdk-virtual-scroll-viewport\n class=\"combobox-viewport\"\n role=\"listbox\"\n [itemSize]=\"itemHeight\"\n [style.height.px]=\"viewportHeight\"\n >\n <ng-container *cdkVirtualFor=\"let item of searchItems\">\n <ng-container\n *ngTemplateOutlet=\"\n itemTemplate || defaultItemTemplate;\n context: {\n $implicit: item,\n selected: item === selectedItem,\n focused: item === focusedItem,\n }\n \"\n ></ng-container>\n </ng-container>\n </cdk-virtual-scroll-viewport>\n }\n </kirby-card>\n</kirby-popover>\n\n<ng-template #defaultItemTemplate let-item let-selected=\"selected\" let-focused=\"focused\">\n <kirby-item\n role=\"option\"\n [selectable]=\"true\"\n [selected]=\"selected\"\n [class.focused]=\"focused\"\n [attr.id]=\"getItemId(item)\"\n [attr.aria-selected]=\"selected\"\n (click)=\"onItemSelect(item)\"\n >\n <p class=\"kirby-item-title\">{{ getItemText(item) }}</p>\n @if (selected) {\n <kirby-icon name=\"checkmark-selected\" slot=\"end\"></kirby-icon>\n }\n </kirby-item>\n</ng-template>\n", styles: [".kirby-line-clamp{display:-webkit-box;-webkit-line-clamp:var(--line-clamp, none);-webkit-box-orient:vertical;overflow:hidden}:host:focus-visible{transition:all 80ms linear 0ms;box-shadow:var(--kirby-focus-ring-md-inset);outline:none;border-radius:var(--kirby-border-radius-pill)}:host{display:inline-block;position:relative;max-width:calc(100vw - calc(2 * var(--kirby-spacing-s)))}:host.clicked{box-shadow:none}:host.expand{display:block}:host.expand kirby-card{width:100%;min-width:initial;max-width:initial}:host div{position:relative}:host div>input[kirby-input]{padding-right:var(--kirby-spacing-xl)}:host div>input[kirby-input]:disabled~kirby-icon{color:var(--kirby-semi-dark-shade)}:host div>kirby-icon{position:absolute;top:0;bottom:0;right:var(--kirby-spacing-xs)}:host.has-value>div>input[kirby-input]{text-overflow:ellipsis}kirby-popover{--max-height: 352px}kirby-card{display:flex;flex-direction:column;max-height:352px;overflow:hidden;box-shadow:var(--kirby-elevation-4);max-width:calc(100vw - calc(2 * var(--kirby-spacing-s)));min-width:calc(320px - calc(2 * var(--kirby-spacing-s)))}@media(min-width:321px){kirby-card{min-width:calc(375px - calc(2 * var(--kirby-spacing-s)))}}.combobox-viewport{display:block;width:100%}.no-results{color:var(--kirby-text-color-semi-dark)}\n"] }]
|
|
735
|
-
}], ctorParameters: () => [{ type: i0.Renderer2 }, { type: i0.ElementRef }, { type: i0.ChangeDetectorRef }, { type: i1.ResizeObserverService }], propDecorators: { noSearchResultsText: [{
|
|
736
|
-
type: Input
|
|
737
|
-
}], selectionClearedAnnouncement: [{
|
|
738
|
-
type: Input
|
|
739
|
-
}], itemTextProperty: [{
|
|
740
|
-
type: Input
|
|
741
|
-
}], itemHeight: [{
|
|
742
|
-
type: Input
|
|
743
|
-
}], itemIdProperty: [{
|
|
744
|
-
type: Input
|
|
745
|
-
}], searchFunction: [{
|
|
746
|
-
type: Input
|
|
747
|
-
}], items: [{
|
|
748
|
-
type: Input
|
|
749
|
-
}], selectedItem: [{
|
|
750
|
-
type: Input
|
|
751
|
-
}], placeholder: [{
|
|
752
|
-
type: Input
|
|
753
|
-
}], popout: [{
|
|
754
|
-
type: Input
|
|
755
|
-
}], expand: [{
|
|
756
|
-
type: Input
|
|
757
|
-
}], disabled: [{
|
|
758
|
-
type: Input
|
|
759
|
-
}], _isDisabled: [{
|
|
760
|
-
type: HostBinding,
|
|
761
|
-
args: ['attr.disabled']
|
|
762
|
-
}], hasErrorChange: [{
|
|
763
|
-
type: Output
|
|
764
|
-
}], hasError: [{
|
|
765
|
-
type: Input
|
|
766
|
-
}], size: [{
|
|
767
|
-
type: Input
|
|
768
|
-
}], _noBlurOnScroll: [{
|
|
769
|
-
type: HostBinding,
|
|
770
|
-
args: ['attr.no-blur']
|
|
771
|
-
}], change: [{
|
|
772
|
-
type: Output
|
|
773
|
-
}], _isBlockLevel: [{
|
|
774
|
-
type: HostBinding,
|
|
775
|
-
args: ['class.expand']
|
|
776
|
-
}], _isOpening: [{
|
|
777
|
-
type: HostBinding,
|
|
778
|
-
args: ['class.is-opening']
|
|
779
|
-
}], isOpen: [{
|
|
780
|
-
type: HostBinding,
|
|
781
|
-
args: ['class.is-open']
|
|
782
|
-
}], hasValue: [{
|
|
783
|
-
type: HostBinding,
|
|
784
|
-
args: ['class.has-value']
|
|
785
|
-
}], itemTemplate: [{
|
|
786
|
-
type: ContentChild,
|
|
787
|
-
args: [ListItemTemplateDirective, { read: TemplateRef }]
|
|
788
|
-
}], slottedItems: [{
|
|
789
|
-
type: ContentChildren,
|
|
790
|
-
args: [ListItemTemplateDirective, { read: ElementRef }]
|
|
791
|
-
}], cardElement: [{
|
|
792
|
-
type: ViewChild,
|
|
793
|
-
args: [CardComponent, { read: ElementRef }]
|
|
794
|
-
}], popover: [{
|
|
795
|
-
type: ViewChild,
|
|
796
|
-
args: [PopoverComponent]
|
|
797
|
-
}], rootElement: [{
|
|
798
|
-
type: ViewChild,
|
|
799
|
-
args: ['rootElement', { static: true, read: ElementRef }]
|
|
800
|
-
}], textInput: [{
|
|
801
|
-
type: ViewChild,
|
|
802
|
-
args: [InputComponent, { static: true, read: ElementRef }]
|
|
803
|
-
}], _virtualScrollViewport: [{
|
|
804
|
-
type: ViewChild,
|
|
805
|
-
args: [CdkVirtualScrollViewport]
|
|
806
|
-
}], kirbyItemsDefault: [{
|
|
807
|
-
type: ViewChildren,
|
|
808
|
-
args: [ItemComponent, { read: ElementRef }]
|
|
809
|
-
}], kirbyItemsSlotted: [{
|
|
810
|
-
type: ContentChildren,
|
|
811
|
-
args: [ItemComponent, { read: ElementRef }]
|
|
812
|
-
}], onMouseDown: [{
|
|
813
|
-
type: HostListener,
|
|
814
|
-
args: ['mousedown', ['$event']]
|
|
815
|
-
}], onTouchStart: [{
|
|
816
|
-
type: HostListener,
|
|
817
|
-
args: ['touchstart', ['$event']]
|
|
818
|
-
}], onFocusOut: [{
|
|
819
|
-
type: HostListener,
|
|
820
|
-
args: ['focusout']
|
|
821
|
-
}], onTab: [{
|
|
822
|
-
type: HostListener,
|
|
823
|
-
args: ['keydown.tab']
|
|
824
|
-
}], onEscape: [{
|
|
825
|
-
type: HostListener,
|
|
826
|
-
args: ['keydown.escape']
|
|
827
|
-
}], onEnter: [{
|
|
828
|
-
type: HostListener,
|
|
829
|
-
args: ['keydown.enter', ['$event']]
|
|
830
|
-
}], onArrowKeys: [{
|
|
831
|
-
type: HostListener,
|
|
832
|
-
args: ['keydown.arrowUp', ['$event']]
|
|
833
|
-
}, {
|
|
834
|
-
type: HostListener,
|
|
835
|
-
args: ['keydown.arrowDown', ['$event']]
|
|
836
|
-
}, {
|
|
837
|
-
type: HostListener,
|
|
838
|
-
args: ['keydown.pageUp', ['$event']]
|
|
839
|
-
}, {
|
|
840
|
-
type: HostListener,
|
|
841
|
-
args: ['keydown.pageDown', ['$event']]
|
|
842
|
-
}, {
|
|
843
|
-
type: HostListener,
|
|
844
|
-
args: ['keydown.home', ['$event']]
|
|
845
|
-
}, {
|
|
846
|
-
type: HostListener,
|
|
847
|
-
args: ['keydown.end', ['$event']]
|
|
848
|
-
}] } });
|
|
849
|
-
|
|
850
|
-
/**
|
|
851
|
-
* Generated bundle index. Do not edit.
|
|
852
|
-
*/
|
|
853
|
-
|
|
854
|
-
export { ComboboxComponent };
|
|
855
|
-
//# sourceMappingURL=kirbydesign-extensions-angular-combobox.mjs.map
|
|
934
|
+
//# sourceMappingURL=kirbydesign-extensions-angular-combobox.mjs.map
|