primeng 16.6.0 → 16.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (175) hide show
  1. package/animate/animate.d.ts +3 -2
  2. package/animateonscroll/animateonscroll.d.ts +73 -0
  3. package/animateonscroll/index.d.ts +5 -0
  4. package/animateonscroll/public_api.d.ts +1 -0
  5. package/api/primengconfig.d.ts +1 -0
  6. package/autocomplete/autocomplete.d.ts +177 -54
  7. package/autocomplete/autocomplete.interface.d.ts +30 -0
  8. package/cascadeselect/cascadeselect.d.ts +188 -68
  9. package/cascadeselect/cascadeselect.interface.d.ts +19 -0
  10. package/checkbox/checkbox.d.ts +1 -4
  11. package/chips/chips.d.ts +21 -5
  12. package/colorpicker/colorpicker.d.ts +5 -5
  13. package/contextmenu/contextmenu.d.ts +1 -0
  14. package/dialog/dialog.d.ts +1 -2
  15. package/dom/domhandler.d.ts +4 -1
  16. package/dropdown/dropdown.d.ts +115 -37
  17. package/esm2022/accordion/accordion.mjs +11 -7
  18. package/esm2022/animate/animate.mjs +4 -1
  19. package/esm2022/animateonscroll/animateonscroll.mjs +185 -0
  20. package/esm2022/animateonscroll/primeng-animateonscroll.mjs +5 -0
  21. package/esm2022/animateonscroll/public_api.mjs +2 -0
  22. package/esm2022/api/primengconfig.mjs +2 -1
  23. package/esm2022/autocomplete/autocomplete.interface.mjs +1 -1
  24. package/esm2022/autocomplete/autocomplete.mjs +893 -617
  25. package/esm2022/carousel/carousel.mjs +5 -1
  26. package/esm2022/cascadeselect/cascadeselect.interface.mjs +1 -1
  27. package/esm2022/cascadeselect/cascadeselect.mjs +921 -482
  28. package/esm2022/checkbox/checkbox.mjs +90 -73
  29. package/esm2022/chips/chips.mjs +197 -53
  30. package/esm2022/colorpicker/colorpicker.mjs +56 -37
  31. package/esm2022/contextmenu/contextmenu.mjs +10 -1
  32. package/esm2022/dialog/dialog.mjs +6 -11
  33. package/esm2022/dom/domhandler.mjs +26 -8
  34. package/esm2022/dropdown/dropdown.mjs +707 -588
  35. package/esm2022/inputmask/inputmask.mjs +22 -9
  36. package/esm2022/inputnumber/inputnumber.mjs +142 -83
  37. package/esm2022/inputswitch/inputswitch.mjs +55 -49
  38. package/esm2022/knob/knob.mjs +92 -5
  39. package/esm2022/listbox/listbox.interface.mjs +1 -1
  40. package/esm2022/listbox/listbox.mjs +996 -454
  41. package/esm2022/multiselect/multiselect.mjs +1022 -604
  42. package/esm2022/overlaypanel/overlaypanel.mjs +2 -2
  43. package/esm2022/paginator/paginator.mjs +2 -2
  44. package/esm2022/password/password.mjs +29 -28
  45. package/esm2022/radiobutton/radiobutton.mjs +46 -33
  46. package/esm2022/rating/rating.mjs +172 -80
  47. package/esm2022/selectbutton/selectbutton.mjs +105 -34
  48. package/esm2022/slider/slider.mjs +151 -66
  49. package/esm2022/table/table.mjs +3 -3
  50. package/esm2022/togglebutton/togglebutton.mjs +47 -10
  51. package/esm2022/tristatecheckbox/tristatecheckbox.mjs +53 -35
  52. package/fesm2022/primeng-accordion.mjs +10 -6
  53. package/fesm2022/primeng-accordion.mjs.map +1 -1
  54. package/fesm2022/primeng-animate.mjs +3 -0
  55. package/fesm2022/primeng-animate.mjs.map +1 -1
  56. package/fesm2022/primeng-animateonscroll.mjs +190 -0
  57. package/fesm2022/primeng-animateonscroll.mjs.map +1 -0
  58. package/fesm2022/primeng-api.mjs +1 -0
  59. package/fesm2022/primeng-api.mjs.map +1 -1
  60. package/fesm2022/primeng-autocomplete.mjs +893 -617
  61. package/fesm2022/primeng-autocomplete.mjs.map +1 -1
  62. package/fesm2022/primeng-carousel.mjs +4 -0
  63. package/fesm2022/primeng-carousel.mjs.map +1 -1
  64. package/fesm2022/primeng-cascadeselect.mjs +920 -481
  65. package/fesm2022/primeng-cascadeselect.mjs.map +1 -1
  66. package/fesm2022/primeng-checkbox.mjs +89 -72
  67. package/fesm2022/primeng-checkbox.mjs.map +1 -1
  68. package/fesm2022/primeng-chips.mjs +195 -51
  69. package/fesm2022/primeng-chips.mjs.map +1 -1
  70. package/fesm2022/primeng-colorpicker.mjs +55 -36
  71. package/fesm2022/primeng-colorpicker.mjs.map +1 -1
  72. package/fesm2022/primeng-contextmenu.mjs +9 -0
  73. package/fesm2022/primeng-contextmenu.mjs.map +1 -1
  74. package/fesm2022/primeng-dialog.mjs +5 -10
  75. package/fesm2022/primeng-dialog.mjs.map +1 -1
  76. package/fesm2022/primeng-dom.mjs +25 -7
  77. package/fesm2022/primeng-dom.mjs.map +1 -1
  78. package/fesm2022/primeng-dropdown.mjs +707 -588
  79. package/fesm2022/primeng-dropdown.mjs.map +1 -1
  80. package/fesm2022/primeng-inputmask.mjs +20 -7
  81. package/fesm2022/primeng-inputmask.mjs.map +1 -1
  82. package/fesm2022/primeng-inputnumber.mjs +140 -81
  83. package/fesm2022/primeng-inputnumber.mjs.map +1 -1
  84. package/fesm2022/primeng-inputswitch.mjs +55 -49
  85. package/fesm2022/primeng-inputswitch.mjs.map +1 -1
  86. package/fesm2022/primeng-knob.mjs +92 -5
  87. package/fesm2022/primeng-knob.mjs.map +1 -1
  88. package/fesm2022/primeng-listbox.mjs +995 -453
  89. package/fesm2022/primeng-listbox.mjs.map +1 -1
  90. package/fesm2022/primeng-multiselect.mjs +1021 -603
  91. package/fesm2022/primeng-multiselect.mjs.map +1 -1
  92. package/fesm2022/primeng-overlaypanel.mjs +1 -1
  93. package/fesm2022/primeng-overlaypanel.mjs.map +1 -1
  94. package/fesm2022/primeng-paginator.mjs +1 -1
  95. package/fesm2022/primeng-paginator.mjs.map +1 -1
  96. package/fesm2022/primeng-password.mjs +27 -26
  97. package/fesm2022/primeng-password.mjs.map +1 -1
  98. package/fesm2022/primeng-radiobutton.mjs +46 -33
  99. package/fesm2022/primeng-radiobutton.mjs.map +1 -1
  100. package/fesm2022/primeng-rating.mjs +171 -79
  101. package/fesm2022/primeng-rating.mjs.map +1 -1
  102. package/fesm2022/primeng-selectbutton.mjs +104 -33
  103. package/fesm2022/primeng-selectbutton.mjs.map +1 -1
  104. package/fesm2022/primeng-slider.mjs +150 -65
  105. package/fesm2022/primeng-slider.mjs.map +1 -1
  106. package/fesm2022/primeng-table.mjs +2 -2
  107. package/fesm2022/primeng-table.mjs.map +1 -1
  108. package/fesm2022/primeng-togglebutton.mjs +46 -9
  109. package/fesm2022/primeng-togglebutton.mjs.map +1 -1
  110. package/fesm2022/primeng-tristatecheckbox.mjs +53 -35
  111. package/fesm2022/primeng-tristatecheckbox.mjs.map +1 -1
  112. package/inputmask/inputmask.d.ts +6 -1
  113. package/inputnumber/inputnumber.d.ts +6 -1
  114. package/inputswitch/inputswitch.d.ts +6 -9
  115. package/knob/knob.d.ts +20 -3
  116. package/listbox/listbox.d.ts +208 -39
  117. package/listbox/listbox.interface.d.ts +15 -0
  118. package/multiselect/multiselect.d.ts +171 -60
  119. package/package.json +126 -120
  120. package/password/password.d.ts +3 -4
  121. package/radiobutton/radiobutton.d.ts +1 -2
  122. package/rating/rating.d.ts +29 -7
  123. package/resources/components/dropdown/dropdown.css +16 -2
  124. package/resources/themes/arya-blue/theme.css +106 -84
  125. package/resources/themes/arya-green/theme.css +106 -84
  126. package/resources/themes/arya-orange/theme.css +106 -84
  127. package/resources/themes/arya-purple/theme.css +106 -84
  128. package/resources/themes/bootstrap4-dark-blue/theme.css +110 -88
  129. package/resources/themes/bootstrap4-dark-purple/theme.css +110 -88
  130. package/resources/themes/bootstrap4-light-blue/theme.css +110 -88
  131. package/resources/themes/bootstrap4-light-purple/theme.css +110 -88
  132. package/resources/themes/fluent-light/theme.css +103 -81
  133. package/resources/themes/lara-dark-blue/theme.css +106 -84
  134. package/resources/themes/lara-dark-indigo/theme.css +106 -84
  135. package/resources/themes/lara-dark-purple/theme.css +106 -84
  136. package/resources/themes/lara-dark-teal/theme.css +106 -84
  137. package/resources/themes/lara-light-blue/theme.css +109 -87
  138. package/resources/themes/lara-light-indigo/theme.css +109 -87
  139. package/resources/themes/lara-light-purple/theme.css +109 -87
  140. package/resources/themes/lara-light-teal/theme.css +109 -87
  141. package/resources/themes/luna-amber/theme.css +110 -88
  142. package/resources/themes/luna-blue/theme.css +110 -88
  143. package/resources/themes/luna-green/theme.css +110 -88
  144. package/resources/themes/luna-pink/theme.css +110 -88
  145. package/resources/themes/md-dark-deeppurple/theme.css +117 -95
  146. package/resources/themes/md-dark-indigo/theme.css +117 -95
  147. package/resources/themes/md-light-deeppurple/theme.css +117 -95
  148. package/resources/themes/md-light-indigo/theme.css +117 -95
  149. package/resources/themes/mdc-dark-deeppurple/theme.css +117 -95
  150. package/resources/themes/mdc-dark-indigo/theme.css +117 -95
  151. package/resources/themes/mdc-light-deeppurple/theme.css +117 -95
  152. package/resources/themes/mdc-light-indigo/theme.css +117 -95
  153. package/resources/themes/mira/theme.css +107 -85
  154. package/resources/themes/nano/theme.css +109 -87
  155. package/resources/themes/nova/theme.css +110 -88
  156. package/resources/themes/nova-accent/theme.css +109 -87
  157. package/resources/themes/nova-alt/theme.css +110 -88
  158. package/resources/themes/rhea/theme.css +109 -87
  159. package/resources/themes/saga-blue/theme.css +106 -84
  160. package/resources/themes/saga-green/theme.css +106 -84
  161. package/resources/themes/saga-orange/theme.css +106 -84
  162. package/resources/themes/saga-purple/theme.css +106 -84
  163. package/resources/themes/soho-dark/theme.css +109 -87
  164. package/resources/themes/soho-light/theme.css +109 -87
  165. package/resources/themes/tailwind-light/theme.css +110 -88
  166. package/resources/themes/vela-blue/theme.css +106 -84
  167. package/resources/themes/vela-green/theme.css +106 -84
  168. package/resources/themes/vela-orange/theme.css +106 -84
  169. package/resources/themes/vela-purple/theme.css +106 -84
  170. package/resources/themes/viva-dark/theme.css +109 -87
  171. package/resources/themes/viva-light/theme.css +109 -87
  172. package/selectbutton/selectbutton.d.ts +15 -3
  173. package/slider/slider.d.ts +12 -6
  174. package/togglebutton/togglebutton.d.ts +7 -1
  175. package/tristatecheckbox/tristatecheckbox.d.ts +8 -4
@@ -1,16 +1,18 @@
1
1
  import * as i0 from '@angular/core';
2
- import { forwardRef, EventEmitter, Component, ChangeDetectionStrategy, ViewEncapsulation, Input, Output, ViewChild, ContentChild, ContentChildren, NgModule } from '@angular/core';
2
+ import { forwardRef, EventEmitter, signal, computed, Component, ChangeDetectionStrategy, ViewEncapsulation, Input, Output, ViewChild, ContentChild, ContentChildren, NgModule } from '@angular/core';
3
3
  import * as i2 from '@angular/common';
4
4
  import { CommonModule } from '@angular/common';
5
5
  import * as i1 from 'primeng/api';
6
- import { TranslationKeys, Header, Footer, PrimeTemplate, SharedModule } from 'primeng/api';
6
+ import { Header, Footer, PrimeTemplate, SharedModule } from 'primeng/api';
7
7
  import { DomHandler } from 'primeng/dom';
8
- import { ObjectUtils } from 'primeng/utils';
8
+ import { ObjectUtils, UniqueComponentId } from 'primeng/utils';
9
9
  import { NG_VALUE_ACCESSOR } from '@angular/forms';
10
10
  import * as i3 from 'primeng/ripple';
11
11
  import { RippleModule } from 'primeng/ripple';
12
12
  import { SearchIcon } from 'primeng/icons/search';
13
13
  import { CheckIcon } from 'primeng/icons/check';
14
+ import * as i4 from 'primeng/scroller';
15
+ import { ScrollerModule } from 'primeng/scroller';
14
16
 
15
17
  const LISTBOX_VALUE_ACCESSOR = {
16
18
  provide: NG_VALUE_ACCESSOR,
@@ -26,6 +28,90 @@ class Listbox {
26
28
  cd;
27
29
  filterService;
28
30
  config;
31
+ renderer;
32
+ /**
33
+ * Unique identifier of the component.
34
+ * @group Props
35
+ */
36
+ id;
37
+ /**
38
+ * Text to display when the search is active. Defaults to global value in i18n translation configuration.
39
+ * @group Props
40
+ * @defaultValue '{0} results are available'
41
+ */
42
+ searchMessage;
43
+ /**
44
+ * Text to display when filtering does not return any results. Defaults to global value in i18n translation configuration.
45
+ * @group Props
46
+ * @defaultValue 'No selected item'
47
+ */
48
+ emptySelectionMessage;
49
+ /**
50
+ * Text to be displayed in hidden accessible field when options are selected. Defaults to global value in i18n translation configuration.
51
+ * @group Props
52
+ * @defaultValue '{0} items selected'
53
+ */
54
+ selectionMessage;
55
+ /**
56
+ * Whether to focus on the first visible or selected element when the overlay panel is shown.
57
+ * @group Props
58
+ */
59
+ autoOptionFocus = true;
60
+ /**
61
+ * When enabled, the focused option is selected.
62
+ * @group Props
63
+ */
64
+ selectOnFocus;
65
+ /**
66
+ * Locale to use in searching. The default locale is the host environment's current locale.
67
+ * @group Props
68
+ */
69
+ searchLocale;
70
+ /**
71
+ * When enabled, the hovered option will be focused.
72
+ * @group Props
73
+ */
74
+ focusOnHover;
75
+ /**
76
+ * Text to display when filtering.
77
+ * @group Props
78
+ */
79
+ filterMessage;
80
+ /**
81
+ * Fields used when filtering the options, defaults to optionLabel.
82
+ * @group Props
83
+ */
84
+ filterFields;
85
+ /**
86
+ * Defines if data is loaded and interacted with in lazy manner.
87
+ * @group Props
88
+ */
89
+ lazy = false;
90
+ /**
91
+ * Whether the data should be loaded on demand during scroll.
92
+ * @group Props
93
+ */
94
+ virtualScroll;
95
+ /**
96
+ * Height of an item in the list for VirtualScrolling.
97
+ * @group Props
98
+ */
99
+ virtualScrollItemSize;
100
+ /**
101
+ * Whether to use the scroller feature. The properties of scroller component can be used like an object in it.
102
+ * @group Props
103
+ */
104
+ virtualScrollOptions;
105
+ /**
106
+ * Height of the viewport in pixels, a scrollbar is defined if height of list exceeds this value.
107
+ * @group Props
108
+ */
109
+ scrollHeight = '200px';
110
+ /**
111
+ * Index of the element in tabbing order.
112
+ * @group Props
113
+ */
114
+ tabindex = 0;
29
115
  /**
30
116
  * When specified, allows selecting multiple values.
31
117
  * @group Props
@@ -120,7 +206,7 @@ class Listbox {
120
206
  * Name of the label field of an option group.
121
207
  * @group Props
122
208
  */
123
- optionGroupLabel;
209
+ optionGroupLabel = 'label';
124
210
  /**
125
211
  * Name of the disabled field of an option.
126
212
  * @group Props
@@ -156,23 +242,20 @@ class Listbox {
156
242
  * @group Props
157
243
  */
158
244
  get options() {
159
- return this._options;
245
+ return this._options();
160
246
  }
161
247
  set options(val) {
162
- this._options = val;
163
- if (this.hasFilter())
164
- this.activateFilter();
248
+ this._options.set(val);
165
249
  }
166
250
  /**
167
251
  * When specified, filter displays with this value.
168
252
  * @group Props
169
253
  */
170
254
  get filterValue() {
171
- return this._filterValue;
255
+ return this._filterValue();
172
256
  }
173
257
  set filterValue(val) {
174
- this._filterValue = val;
175
- this.activateFilter();
258
+ this._filterValue.set(val);
176
259
  }
177
260
  /**
178
261
  * Callback to invoke on value change.
@@ -192,12 +275,33 @@ class Listbox {
192
275
  * @group Emits
193
276
  */
194
277
  onDblClick = new EventEmitter();
278
+ /**
279
+ * Callback to invoke when data is filtered.
280
+ * @param {ListboxFilterEvent} event - Custom filter event.
281
+ * @group Emits
282
+ */
283
+ onFilter = new EventEmitter();
284
+ /**
285
+ * Callback to invoke when component receives focus.
286
+ * @param {FocusEvent} event - Focus event.
287
+ * @group Emits
288
+ */
289
+ onFocus = new EventEmitter();
290
+ /**
291
+ * Callback to invoke when component loses focus.
292
+ * @param {FocusEvent} event - Blur event.
293
+ * @group Emits
294
+ */
295
+ onBlur = new EventEmitter();
195
296
  headerCheckboxViewChild;
196
297
  filterViewChild;
298
+ lastHiddenFocusableElement;
299
+ firstHiddenFocusableElement;
300
+ scroller;
301
+ listViewChild;
197
302
  headerFacet;
198
303
  footerFacet;
199
304
  templates;
200
- _options;
201
305
  itemTemplate;
202
306
  groupTemplate;
203
307
  headerTemplate;
@@ -207,7 +311,7 @@ class Listbox {
207
311
  emptyTemplate;
208
312
  filterIconTemplate;
209
313
  checkIconTemplate;
210
- _filterValue;
314
+ _filterValue = signal(null);
211
315
  _filteredOptions;
212
316
  filterOptions;
213
317
  filtered;
@@ -218,19 +322,76 @@ class Listbox {
218
322
  focus;
219
323
  headerCheckboxFocus;
220
324
  translationSubscription;
221
- constructor(el, cd, filterService, config) {
325
+ focused;
326
+ get containerClass() {
327
+ return {
328
+ 'p-listbox p-component': true,
329
+ 'p-focus': this.focused,
330
+ 'p-disabled': this.disabled
331
+ };
332
+ }
333
+ get focusedOptionId() {
334
+ return this.focusedOptionIndex() !== -1 ? `${this.id}_${this.focusedOptionIndex()}` : null;
335
+ }
336
+ get filterResultMessageText() {
337
+ return ObjectUtils.isNotEmpty(this.visibleOptions()) ? this.filterMessageText.replaceAll('{0}', this.visibleOptions().length) : this.emptyFilterMessageText;
338
+ }
339
+ get filterMessageText() {
340
+ return this.filterMessage || this.config.translation.searchMessage || '';
341
+ }
342
+ get searchMessageText() {
343
+ return this.searchMessage || this.config.translation.searchMessage || '';
344
+ }
345
+ get emptyFilterMessageText() {
346
+ return this.emptyFilterMessage || this.config.translation.emptySearchMessage || this.config.translation.emptyFilterMessage || '';
347
+ }
348
+ get selectionMessageText() {
349
+ return this.selectionMessage || this.config.translation.selectionMessage || '';
350
+ }
351
+ get emptySelectionMessageText() {
352
+ return this.emptySelectionMessage || this.config.translation.emptySelectionMessage || '';
353
+ }
354
+ get selectedMessageText() {
355
+ return this.hasSelectedOption() ? this.selectionMessageText.replaceAll('{0}', this.multiple ? this.modelValue().length : '1') : this.emptySelectionMessageText;
356
+ }
357
+ get ariaSetSize() {
358
+ return this.visibleOptions().filter((option) => !this.isOptionGroup(option)).length;
359
+ }
360
+ get virtualScrollerDisabled() {
361
+ return !this.virtualScroll;
362
+ }
363
+ get searchFields() {
364
+ return this.filterFields || [this.optionLabel];
365
+ }
366
+ get toggleAllAriaLabel() {
367
+ return this.config.translation.aria ? this.config.translation.aria[this.allSelected() ? 'selectAll' : 'unselectAll'] : undefined;
368
+ }
369
+ searchValue;
370
+ searchTimeout;
371
+ _options = signal(null);
372
+ startRangeIndex = signal(-1);
373
+ focusedOptionIndex = signal(-1);
374
+ modelValue = signal(null);
375
+ visibleOptions = computed(() => {
376
+ const options = this.group ? this.flatOptions(this._options()) : this._options() || [];
377
+ return this._filterValue() ? this.filterService.filter(options, this.searchFields, this._filterValue(), this.filterMatchMode, this.filterLocale) : options;
378
+ });
379
+ constructor(el, cd, filterService, config, renderer) {
222
380
  this.el = el;
223
381
  this.cd = cd;
224
382
  this.filterService = filterService;
225
383
  this.config = config;
384
+ this.renderer = renderer;
226
385
  }
227
386
  ngOnInit() {
387
+ this.id = this.id || UniqueComponentId();
228
388
  this.translationSubscription = this.config.translationObserver.subscribe(() => {
229
389
  this.cd.markForCheck();
230
390
  });
391
+ this.autoUpdateModel();
231
392
  if (this.filterBy) {
232
393
  this.filterOptions = {
233
- filter: (value) => this.onFilter(value),
394
+ filter: (value) => this.onFilterChange(value),
234
395
  reset: () => this.resetFilter()
235
396
  };
236
397
  }
@@ -271,21 +432,6 @@ class Listbox {
271
432
  }
272
433
  });
273
434
  }
274
- getOptionLabel(option) {
275
- return this.optionLabel ? ObjectUtils.resolveFieldData(option, this.optionLabel) : option.label != undefined ? option.label : option;
276
- }
277
- getOptionGroupChildren(optionGroup) {
278
- return this.optionGroupChildren ? ObjectUtils.resolveFieldData(optionGroup, this.optionGroupChildren) : optionGroup.items;
279
- }
280
- getOptionGroupLabel(optionGroup) {
281
- return this.optionGroupLabel ? ObjectUtils.resolveFieldData(optionGroup, this.optionGroupLabel) : optionGroup.label != undefined ? optionGroup.label : optionGroup;
282
- }
283
- getOptionValue(option) {
284
- return this.optionValue ? ObjectUtils.resolveFieldData(option, this.optionValue) : this.optionLabel || option.value === undefined ? option : option.value;
285
- }
286
- isOptionDisabled(option) {
287
- return this.optionDisabled ? ObjectUtils.resolveFieldData(option, this.optionDisabled) : option.disabled !== undefined ? option.disabled : false;
288
- }
289
435
  writeValue(value) {
290
436
  this.value = value;
291
437
  this.cd.markForCheck();
@@ -300,391 +446,582 @@ class Listbox {
300
446
  this.disabled = val;
301
447
  this.cd.markForCheck();
302
448
  }
303
- onOptionClick(event, option) {
304
- if (this.disabled || this.isOptionDisabled(option) || this.readonly) {
305
- return;
306
- }
307
- if (this.multiple) {
308
- if (this.checkbox)
309
- this.onOptionClickCheckbox(event, option);
310
- else
311
- this.onOptionClickMultiple(event, option);
312
- }
313
- else {
314
- this.onOptionClickSingle(event, option);
315
- }
316
- this.onClick.emit({
317
- originalEvent: event,
318
- option: option,
319
- value: this.value
320
- });
321
- this.optionTouched = false;
449
+ flatOptions(options) {
450
+ return (options || []).reduce((result, option, index) => {
451
+ result.push({ optionGroup: option, group: true, index });
452
+ const optionGroupChildren = this.getOptionGroupChildren(option);
453
+ optionGroupChildren && optionGroupChildren.forEach((o) => result.push(o));
454
+ return result;
455
+ }, []);
322
456
  }
323
- onOptionTouchEnd(option) {
324
- if (this.disabled || this.isOptionDisabled(option) || this.readonly) {
325
- return;
457
+ autoUpdateModel() {
458
+ if (this.selectOnFocus && this.autoOptionFocus && !this.hasSelectedOption() && !this.multiple) {
459
+ const focusedOptionIndex = this.findFirstFocusedOptionIndex();
460
+ this.focusedOptionIndex.set(focusedOptionIndex);
461
+ this.onOptionSelect(null, this.visibleOptions()[this.focusedOptionIndex()]);
326
462
  }
327
- this.optionTouched = true;
328
463
  }
329
- onOptionDoubleClick(event, option) {
330
- if (this.disabled || this.isOptionDisabled(option) || this.readonly) {
464
+ updateModel(value, event) {
465
+ this.value = value;
466
+ this.modelValue.set(value);
467
+ this.onModelChange(value);
468
+ }
469
+ removeOption(option) {
470
+ return this.modelValue().filter((val) => !ObjectUtils.equals(val, this.getOptionValue(option), this.equalityKey()));
471
+ }
472
+ onOptionSelect(event, option, index = -1) {
473
+ if (this.disabled || this.isOptionDisabled(option)) {
331
474
  return;
332
475
  }
333
- this.onDblClick.emit({
334
- originalEvent: event,
335
- option: option,
336
- value: this.value
337
- });
476
+ this.multiple ? this.onOptionSelectMultiple(event, option) : this.onOptionSelectSingle(event, option);
477
+ this.optionTouched = false;
478
+ index !== -1 && this.focusedOptionIndex.set(index);
338
479
  }
339
- onOptionClickSingle(event, option) {
480
+ onOptionSelectMultiple(event, option) {
340
481
  let selected = this.isSelected(option);
341
- let valueChanged = false;
482
+ let value = null;
342
483
  let metaSelection = this.optionTouched ? false : this.metaKeySelection;
343
484
  if (metaSelection) {
344
485
  let metaKey = event.metaKey || event.ctrlKey;
345
486
  if (selected) {
346
- if (metaKey) {
347
- this.value = null;
348
- valueChanged = true;
349
- }
487
+ value = metaKey ? this.removeOption(option) : [this.getOptionValue(option)];
350
488
  }
351
489
  else {
352
- this.value = this.getOptionValue(option);
353
- valueChanged = true;
490
+ value = metaKey ? this.modelValue() || [] : [];
491
+ value = [...value, this.getOptionValue(option)];
354
492
  }
355
493
  }
356
494
  else {
357
- this.value = selected ? null : this.getOptionValue(option);
358
- valueChanged = true;
359
- }
360
- if (valueChanged) {
361
- this.onModelChange(this.value);
362
- this.onChange.emit({
363
- originalEvent: event,
364
- value: this.value
365
- });
495
+ value = selected ? this.removeOption(option) : [...(this.modelValue() || []), this.getOptionValue(option)];
366
496
  }
497
+ this.updateModel(value, event);
367
498
  }
368
- onOptionClickMultiple(event, option) {
499
+ onOptionSelectSingle(event, option) {
369
500
  let selected = this.isSelected(option);
370
501
  let valueChanged = false;
502
+ let value = null;
371
503
  let metaSelection = this.optionTouched ? false : this.metaKeySelection;
372
504
  if (metaSelection) {
373
505
  let metaKey = event.metaKey || event.ctrlKey;
374
506
  if (selected) {
375
507
  if (metaKey) {
376
- this.removeOption(option);
377
- }
378
- else {
379
- this.value = [this.getOptionValue(option)];
508
+ value = null;
509
+ valueChanged = true;
380
510
  }
381
- valueChanged = true;
382
511
  }
383
512
  else {
384
- this.value = metaKey ? this.value || [] : [];
385
- this.value = [...this.value, this.getOptionValue(option)];
513
+ value = this.getOptionValue(option);
386
514
  valueChanged = true;
387
515
  }
388
516
  }
389
517
  else {
390
- if (selected) {
391
- this.removeOption(option);
392
- }
393
- else {
394
- this.value = [...(this.value || []), this.getOptionValue(option)];
395
- }
518
+ value = selected ? null : this.getOptionValue(option);
396
519
  valueChanged = true;
397
520
  }
398
521
  if (valueChanged) {
399
- this.onModelChange(this.value);
400
- this.onChange.emit({
401
- originalEvent: event,
402
- value: this.value
403
- });
522
+ this.updateModel(value, event);
523
+ }
524
+ }
525
+ onOptionSelectRange(event, start = -1, end = -1) {
526
+ start === -1 && (start = this.findNearestSelectedOptionIndex(end, true));
527
+ end === -1 && (end = this.findNearestSelectedOptionIndex(start));
528
+ if (start !== -1 && end !== -1) {
529
+ const rangeStart = Math.min(start, end);
530
+ const rangeEnd = Math.max(start, end);
531
+ const value = this.visibleOptions()
532
+ .slice(rangeStart, rangeEnd + 1)
533
+ .filter((option) => this.isValidOption(option))
534
+ .map((option) => this.getOptionValue(option));
535
+ this.updateModel(value, event);
404
536
  }
405
537
  }
406
- onOptionClickCheckbox(event, option) {
538
+ onToggleAll(event) {
407
539
  if (this.disabled || this.readonly) {
408
540
  return;
409
541
  }
410
- let selected = this.isSelected(option);
411
- if (selected) {
412
- this.removeOption(option);
542
+ DomHandler.focus(this.headerCheckboxViewChild.nativeElement);
543
+ const value = this.allSelected()
544
+ ? []
545
+ : this.visibleOptions()
546
+ .filter((option) => this.isValidOption(option))
547
+ .map((option) => this.getOptionValue(option));
548
+ this.updateModel(value, event);
549
+ this.onChange.emit({ originalEvent: event, value: this.value });
550
+ event.preventDefault();
551
+ event.stopPropagation();
552
+ }
553
+ allSelected() {
554
+ const allSelected = this.visibleOptions().length > 0 && this.visibleOptions().every((option) => this.isOptionGroup(option) || this.isOptionDisabled(option) || this.isSelected(option));
555
+ return ObjectUtils.isNotEmpty(this.visibleOptions()) && allSelected;
556
+ }
557
+ onOptionTouchEnd() {
558
+ if (this.disabled) {
559
+ return;
413
560
  }
414
- else {
415
- this.value = this.value ? this.value : [];
416
- this.value = [...this.value, this.getOptionValue(option)];
561
+ this.optionTouched = true;
562
+ }
563
+ onOptionMouseDown(event, index) {
564
+ this.changeFocusedOptionIndex(event, index);
565
+ }
566
+ onOptionMouseEnter(event, index) {
567
+ if (this.focusOnHover) {
568
+ this.changeFocusedOptionIndex(event, index);
417
569
  }
418
- this.onModelChange(this.value);
419
- this.onChange.emit({
570
+ }
571
+ onOptionDoubleClick(event, option) {
572
+ if (this.disabled || this.isOptionDisabled(option) || this.readonly) {
573
+ return;
574
+ }
575
+ this.onDblClick.emit({
420
576
  originalEvent: event,
577
+ option: option,
421
578
  value: this.value
422
579
  });
423
580
  }
424
- removeOption(option) {
425
- this.value = this.value.filter((val) => !ObjectUtils.equals(val, this.getOptionValue(option), this.dataKey));
581
+ onFirstHiddenFocus(event) {
582
+ DomHandler.focus(this.listViewChild.nativeElement);
583
+ const firstFocusableEl = DomHandler.getFirstFocusableElement(this.el.nativeElement, ':not([data-p-hidden-focusable="true"])');
584
+ this.lastHiddenFocusableElement.nativeElement.tabIndex = ObjectUtils.isEmpty(firstFocusableEl) ? '-1' : undefined;
585
+ this.firstHiddenFocusableElement.nativeElement.tabIndex = -1;
426
586
  }
427
- isSelected(option) {
428
- let selected = false;
429
- let optionValue = this.getOptionValue(option);
430
- if (this.multiple) {
431
- if (this.value) {
432
- for (let val of this.value) {
433
- if (ObjectUtils.equals(val, optionValue, this.dataKey)) {
434
- selected = true;
435
- break;
436
- }
437
- }
438
- }
587
+ onLastHiddenFocus(event) {
588
+ const relatedTarget = event.relatedTarget;
589
+ if (relatedTarget === this.listViewChild.nativeElement) {
590
+ const firstFocusableEl = DomHandler.getFirstFocusableElement(this.el.nativeElement, ':not(.p-hidden-focusable)');
591
+ DomHandler.focus(firstFocusableEl);
592
+ this.firstHiddenFocusableElement.nativeElement.tabIndex = undefined;
439
593
  }
440
594
  else {
441
- selected = ObjectUtils.equals(this.value, optionValue, this.dataKey);
595
+ DomHandler.focus(this.firstHiddenFocusableElement.nativeElement);
442
596
  }
443
- return selected;
597
+ this.lastHiddenFocusableElement.nativeElement.tabIndex = -1;
444
598
  }
445
- get allChecked() {
446
- let optionsToRender = this.optionsToRender;
447
- if (!optionsToRender || optionsToRender.length === 0) {
448
- return false;
599
+ onFocusout(event) {
600
+ if (!this.el.nativeElement.contains(event.relatedTarget) && this.lastHiddenFocusableElement && this.firstHiddenFocusableElement) {
601
+ this.firstHiddenFocusableElement.nativeElement.tabIndex = this.lastHiddenFocusableElement.nativeElement.tabIndex = undefined;
449
602
  }
450
- else {
451
- let selectedDisabledItemsLength = 0;
452
- let unselectedDisabledItemsLength = 0;
453
- let selectedEnabledItemsLength = 0;
454
- let visibleOptionsLength = this.group ? 0 : this.optionsToRender.length;
455
- for (let option of optionsToRender) {
456
- if (!this.group) {
457
- let disabled = this.isOptionDisabled(option);
458
- let selected = this.isSelected(option);
459
- if (disabled) {
460
- if (selected)
461
- selectedDisabledItemsLength++;
462
- else
463
- unselectedDisabledItemsLength++;
464
- }
465
- else {
466
- if (selected)
467
- selectedEnabledItemsLength++;
468
- else
469
- return false;
470
- }
603
+ }
604
+ onListFocus(event) {
605
+ this.focused = true;
606
+ const focusedOptionIndex = this.focusedOptionIndex() !== -1 ? this.focusedOptionIndex() : this.autoOptionFocus ? this.findFirstFocusedOptionIndex() : -1;
607
+ this.focusedOptionIndex.set(focusedOptionIndex);
608
+ this.onFocus.emit(event);
609
+ }
610
+ onListBlur(event) {
611
+ this.focused = false;
612
+ this.focusedOptionIndex.set(-1);
613
+ this.startRangeIndex.set(-1);
614
+ this.searchValue = '';
615
+ }
616
+ onHeaderCheckboxFocus(event) {
617
+ this.headerCheckboxFocus = true;
618
+ }
619
+ onHeaderCheckboxBlur() {
620
+ this.headerCheckboxFocus = false;
621
+ }
622
+ onHeaderCheckboxKeyDown(event) {
623
+ if (this.disabled) {
624
+ event.preventDefault();
625
+ return;
626
+ }
627
+ switch (event.code) {
628
+ case 'Space':
629
+ this.onToggleAll(event);
630
+ break;
631
+ case 'Enter':
632
+ this.onToggleAll(event);
633
+ break;
634
+ case 'Tab':
635
+ this.onHeaderCheckboxTabKeyDown(event);
636
+ break;
637
+ default:
638
+ break;
639
+ }
640
+ }
641
+ onHeaderCheckboxTabKeyDown(event) {
642
+ DomHandler.focus(this.listViewChild.nativeElement);
643
+ event.preventDefault();
644
+ }
645
+ onFilterChange(event) {
646
+ let value = event.target.value?.trim();
647
+ this._filterValue.set(value);
648
+ this.focusedOptionIndex.set(-1);
649
+ this.startRangeIndex.set(-1);
650
+ this.onFilter.emit({ originalEvent: event, filter: this._filterValue() });
651
+ !this.virtualScrollerDisabled && this.scroller.scrollToIndex(0);
652
+ }
653
+ onFilterBlur(event) {
654
+ this.focusedOptionIndex.set(-1);
655
+ this.startRangeIndex.set(-1);
656
+ }
657
+ onListKeyDown(event) {
658
+ const metaKey = event.metaKey || event.ctrlKey;
659
+ switch (event.code) {
660
+ case 'ArrowDown':
661
+ this.onArrowDownKey(event);
662
+ break;
663
+ case 'ArrowUp':
664
+ this.onArrowUpKey(event);
665
+ break;
666
+ case 'Home':
667
+ this.onHomeKey(event);
668
+ break;
669
+ case 'End':
670
+ this.onEndKey(event);
671
+ break;
672
+ case 'PageDown':
673
+ this.onPageDownKey(event);
674
+ break;
675
+ case 'PageUp':
676
+ this.onPageUpKey(event);
677
+ break;
678
+ case 'Enter':
679
+ case 'Space':
680
+ this.onSpaceKey(event);
681
+ break;
682
+ case 'Tab':
683
+ //NOOP
684
+ break;
685
+ case 'ShiftLeft':
686
+ case 'ShiftRight':
687
+ this.onShiftKey();
688
+ break;
689
+ default:
690
+ if (this.multiple && event.code === 'KeyA' && metaKey) {
691
+ const value = this.visibleOptions()
692
+ .filter((option) => this.isValidOption(option))
693
+ .map((option) => this.getOptionValue(option));
694
+ this.updateModel(value, event);
695
+ event.preventDefault();
696
+ break;
471
697
  }
472
- else {
473
- for (let opt of this.getOptionGroupChildren(option)) {
474
- let disabled = this.isOptionDisabled(opt);
475
- let selected = this.isSelected(opt);
476
- if (disabled) {
477
- if (selected)
478
- selectedDisabledItemsLength++;
479
- else
480
- unselectedDisabledItemsLength++;
481
- }
482
- else {
483
- if (selected)
484
- selectedEnabledItemsLength++;
485
- else {
486
- return false;
487
- }
488
- }
489
- visibleOptionsLength++;
490
- }
698
+ if (!metaKey && ObjectUtils.isPrintableCharacter(event.key)) {
699
+ this.searchOptions(event, event.key);
700
+ event.preventDefault();
491
701
  }
492
- }
493
- return (visibleOptionsLength === selectedDisabledItemsLength ||
494
- visibleOptionsLength === selectedEnabledItemsLength ||
495
- (selectedEnabledItemsLength && visibleOptionsLength === selectedEnabledItemsLength + unselectedDisabledItemsLength + selectedDisabledItemsLength));
702
+ break;
496
703
  }
497
704
  }
498
- get optionsToRender() {
499
- return this._filteredOptions || this.options;
705
+ onFilterKeyDown(event) {
706
+ switch (event.code) {
707
+ case 'ArrowDown':
708
+ this.onArrowDownKey(event);
709
+ break;
710
+ case 'ArrowUp':
711
+ this.onArrowUpKey(event);
712
+ break;
713
+ case 'ArrowLeft':
714
+ case 'ArrowRight':
715
+ this.onArrowLeftKey(event, true);
716
+ break;
717
+ case 'Home':
718
+ this.onHomeKey(event, true);
719
+ break;
720
+ case 'End':
721
+ this.onEndKey(event, true);
722
+ break;
723
+ case 'Enter':
724
+ this.onEnterKey(event);
725
+ break;
726
+ case 'ShiftLeft':
727
+ case 'ShiftRight':
728
+ this.onShiftKey();
729
+ break;
730
+ default:
731
+ break;
732
+ }
500
733
  }
501
- get emptyMessageLabel() {
502
- return this.emptyMessage || this.config.getTranslation(TranslationKeys.EMPTY_MESSAGE);
734
+ onArrowDownKey(event) {
735
+ const optionIndex = this.focusedOptionIndex() !== -1 ? this.findNextOptionIndex(this.focusedOptionIndex()) : this.findFirstFocusedOptionIndex();
736
+ if (this.multiple && event.shiftKey) {
737
+ this.onOptionSelectRange(event, this.startRangeIndex(), optionIndex);
738
+ }
739
+ this.changeFocusedOptionIndex(event, optionIndex);
740
+ event.preventDefault();
503
741
  }
504
- get emptyFilterMessageLabel() {
505
- return this.emptyFilterMessage || this.config.getTranslation(TranslationKeys.EMPTY_FILTER_MESSAGE);
742
+ onArrowUpKey(event) {
743
+ const optionIndex = this.focusedOptionIndex() !== -1 ? this.findPrevOptionIndex(this.focusedOptionIndex()) : this.findLastFocusedOptionIndex();
744
+ if (this.multiple && event.shiftKey) {
745
+ this.onOptionSelectRange(event, optionIndex, this.startRangeIndex());
746
+ }
747
+ this.changeFocusedOptionIndex(event, optionIndex);
748
+ event.preventDefault();
506
749
  }
507
- hasFilter() {
508
- return this._filterValue && this._filterValue.trim().length > 0;
750
+ onArrowLeftKey(event, pressedInInputText = false) {
751
+ pressedInInputText && this.focusedOptionIndex.set(-1);
509
752
  }
510
- isEmpty() {
511
- return !this.optionsToRender || (this.optionsToRender && this.optionsToRender.length === 0);
512
- }
513
- onFilter(event) {
514
- this._filterValue = event.target.value;
515
- this.activateFilter();
516
- }
517
- activateFilter() {
518
- if (this.hasFilter() && this._options) {
519
- if (this.group) {
520
- let searchFields = (this.filterBy || this.optionLabel || 'label').split(',');
521
- let filteredGroups = [];
522
- for (let optgroup of this.options) {
523
- let filteredSubOptions = this.filterService.filter(this.getOptionGroupChildren(optgroup), searchFields, this.filterValue, this.filterMatchMode, this.filterLocale);
524
- if (filteredSubOptions && filteredSubOptions.length) {
525
- filteredGroups.push({ ...optgroup, ...{ [this.optionGroupChildren]: filteredSubOptions } });
526
- }
527
- }
528
- this._filteredOptions = filteredGroups;
529
- }
530
- else {
531
- this._filteredOptions = this._options.filter((option) => this.filterService.filters[this.filterMatchMode](this.getOptionLabel(option), this._filterValue, this.filterLocale));
532
- }
753
+ onHomeKey(event, pressedInInputText = false) {
754
+ if (pressedInInputText) {
755
+ event.currentTarget.setSelectionRange(0, 0);
756
+ this.focusedOptionIndex.set(-1);
533
757
  }
534
758
  else {
535
- this._filteredOptions = null;
536
- }
537
- }
538
- resetFilter() {
539
- if (this.filterViewChild && this.filterViewChild.nativeElement) {
540
- this.filterViewChild.nativeElement.value = '';
759
+ let metaKey = event.metaKey || event.ctrlKey;
760
+ let optionIndex = this.findFirstOptionIndex();
761
+ if (this.multiple && event.shiftKey && metaKey) {
762
+ this.onOptionSelectRange(event, optionIndex, this.startRangeIndex());
763
+ }
764
+ this.changeFocusedOptionIndex(event, optionIndex);
541
765
  }
542
- this._filterValue = null;
543
- this._filteredOptions = null;
766
+ event.preventDefault();
544
767
  }
545
- get toggleAllDisabled() {
546
- let optionsToRender = this.optionsToRender;
547
- if (!optionsToRender || optionsToRender.length === 0) {
548
- return true;
768
+ onEndKey(event, pressedInInputText = false) {
769
+ if (pressedInInputText) {
770
+ const target = event.currentTarget;
771
+ const len = target.value.length;
772
+ target.setSelectionRange(len, len);
773
+ this.focusedOptionIndex.set(-1);
549
774
  }
550
775
  else {
551
- for (let option of optionsToRender) {
552
- if (!this.isOptionDisabled(option))
553
- return false;
776
+ let metaKey = event.metaKey || event.ctrlKey;
777
+ let optionIndex = this.findLastOptionIndex();
778
+ if (this.multiple && event.shiftKey && metaKey) {
779
+ this.onOptionSelectRange(event, this.startRangeIndex(), optionIndex);
554
780
  }
555
- return true;
781
+ this.changeFocusedOptionIndex(event, optionIndex);
556
782
  }
783
+ event.preventDefault();
557
784
  }
558
- toggleAll(event) {
559
- if (this.disabled || this.toggleAllDisabled || this.readonly) {
560
- return;
785
+ onPageDownKey(event) {
786
+ this.scrollInView(0);
787
+ event.preventDefault();
788
+ }
789
+ onPageUpKey(event) {
790
+ this.scrollInView(this.visibleOptions().length - 1);
791
+ event.preventDefault();
792
+ }
793
+ onEnterKey(event) {
794
+ if (this.focusedOptionIndex() !== -1) {
795
+ if (this.multiple && event.shiftKey)
796
+ this.onOptionSelectRange(event, this.focusedOptionIndex());
797
+ else
798
+ this.onOptionSelect(event, this.visibleOptions()[this.focusedOptionIndex()]);
561
799
  }
562
- let allChecked = this.allChecked;
563
- if (allChecked)
564
- this.uncheckAll();
565
- else
566
- this.checkAll();
567
- this.onModelChange(this.value);
568
- this.onChange.emit({ originalEvent: event, value: this.value });
569
800
  event.preventDefault();
570
801
  }
571
- checkAll() {
572
- let optionsToRender = this.optionsToRender;
573
- let val = [];
574
- optionsToRender.forEach((opt) => {
575
- if (!this.group) {
576
- let optionDisabled = this.isOptionDisabled(opt);
577
- if (!optionDisabled || (optionDisabled && this.isSelected(opt))) {
578
- val.push(this.getOptionValue(opt));
579
- }
580
- }
581
- else {
582
- let subOptions = this.getOptionGroupChildren(opt);
583
- if (subOptions) {
584
- subOptions.forEach((option) => {
585
- let optionDisabled = this.isOptionDisabled(option);
586
- if (!optionDisabled || (optionDisabled && this.isSelected(option))) {
587
- val.push(this.getOptionValue(option));
588
- }
589
- });
590
- }
802
+ onSpaceKey(event) {
803
+ this.onEnterKey(event);
804
+ }
805
+ onShiftKey() {
806
+ const focusedOptionIndex = this.focusedOptionIndex();
807
+ this.startRangeIndex.set(focusedOptionIndex);
808
+ }
809
+ getOptionGroupChildren(optionGroup) {
810
+ return this.optionGroupChildren ? ObjectUtils.resolveFieldData(optionGroup, this.optionGroupChildren) : optionGroup.items;
811
+ }
812
+ getOptionGroupLabel(optionGroup) {
813
+ return this.optionGroupLabel ? ObjectUtils.resolveFieldData(optionGroup, this.optionGroupLabel) : optionGroup && optionGroup.label !== undefined ? optionGroup.label : optionGroup;
814
+ }
815
+ getOptionLabel(option) {
816
+ return this.optionLabel ? ObjectUtils.resolveFieldData(option, this.optionLabel) : option.label != undefined ? option.label : option;
817
+ }
818
+ getOptionIndex(index, scrollerOptions) {
819
+ return this.virtualScrollerDisabled ? index : scrollerOptions && scrollerOptions.getItemOptions(index)['index'];
820
+ }
821
+ getOptionValue(option) {
822
+ return this.optionValue ? ObjectUtils.resolveFieldData(option, this.optionValue) : !this.optionLabel && option && option.value !== undefined ? option.value : option;
823
+ }
824
+ getAriaPosInset(index) {
825
+ return ((this.optionGroupLabel
826
+ ? index -
827
+ this.visibleOptions()
828
+ .slice(0, index)
829
+ .filter((option) => this.isOptionGroup(option)).length
830
+ : index) + 1);
831
+ }
832
+ hasSelectedOption() {
833
+ return ObjectUtils.isNotEmpty(this.modelValue());
834
+ }
835
+ isOptionGroup(option) {
836
+ return this.optionGroupLabel && option.optionGroup && option.group;
837
+ }
838
+ changeFocusedOptionIndex(event, index) {
839
+ if (this.focusedOptionIndex() !== index) {
840
+ this.focusedOptionIndex.set(index);
841
+ this.scrollInView();
842
+ if (this.selectOnFocus && !this.multiple) {
843
+ this.onOptionSelect(event, this.visibleOptions()[index]);
591
844
  }
592
- });
593
- this.value = val;
594
- }
595
- uncheckAll() {
596
- let optionsToRender = this.optionsToRender;
597
- let val = [];
598
- optionsToRender.forEach((opt) => {
599
- if (!this.group) {
600
- let optionDisabled = this.isOptionDisabled(opt);
601
- if (optionDisabled && this.isSelected(opt)) {
602
- val.push(this.getOptionValue(opt));
603
- }
845
+ }
846
+ }
847
+ searchOptions(event, char) {
848
+ this.searchValue = (this.searchValue || '') + char;
849
+ let optionIndex = -1;
850
+ let matched = false;
851
+ if (this.focusedOptionIndex() !== -1) {
852
+ optionIndex = this.visibleOptions()
853
+ .slice(this.focusedOptionIndex())
854
+ .findIndex((option) => this.isOptionMatched(option));
855
+ optionIndex =
856
+ optionIndex === -1
857
+ ? this.visibleOptions()
858
+ .slice(0, this.focusedOptionIndex())
859
+ .findIndex((option) => this.isOptionMatched(option))
860
+ : optionIndex + this.focusedOptionIndex();
861
+ }
862
+ else {
863
+ optionIndex = this.visibleOptions().findIndex((option) => this.isOptionMatched(option));
864
+ }
865
+ if (optionIndex !== -1) {
866
+ matched = true;
867
+ }
868
+ if (optionIndex === -1 && this.focusedOptionIndex() === -1) {
869
+ optionIndex = this.findFirstFocusedOptionIndex();
870
+ }
871
+ if (optionIndex !== -1) {
872
+ this.changeFocusedOptionIndex(event, optionIndex);
873
+ }
874
+ if (this.searchTimeout) {
875
+ clearTimeout(this.searchTimeout);
876
+ }
877
+ this.searchTimeout = setTimeout(() => {
878
+ this.searchValue = '';
879
+ this.searchTimeout = null;
880
+ }, 500);
881
+ return matched;
882
+ }
883
+ isOptionMatched(option) {
884
+ return this.isValidOption(option) && this.getOptionLabel(option).toLocaleLowerCase(this.filterLocale).startsWith(this.searchValue.toLocaleLowerCase(this.filterLocale));
885
+ }
886
+ scrollInView(index = -1) {
887
+ const id = index !== -1 ? `${this.id}_${index}` : this.focusedOptionId;
888
+ const element = DomHandler.findSingle(this.listViewChild.nativeElement, `li[id="${id}"]`);
889
+ if (element) {
890
+ element.scrollIntoView && element.scrollIntoView({ block: 'nearest', inline: 'nearest' });
891
+ }
892
+ else if (!this.virtualScrollerDisabled) {
893
+ this.virtualScroll && this.scroller.scrollToIndex(index !== -1 ? index : this.focusedOptionIndex());
894
+ }
895
+ }
896
+ findFirstOptionIndex() {
897
+ return this.visibleOptions().findIndex((option) => this.isValidOption(option));
898
+ }
899
+ findLastOptionIndex() {
900
+ return ObjectUtils.findLastIndex(this.visibleOptions(), (option) => this.isValidOption(option));
901
+ }
902
+ findFirstFocusedOptionIndex() {
903
+ const selectedIndex = this.findFirstSelectedOptionIndex();
904
+ return selectedIndex < 0 ? this.findFirstOptionIndex() : selectedIndex;
905
+ }
906
+ findLastFocusedOptionIndex() {
907
+ const selectedIndex = this.findLastSelectedOptionIndex();
908
+ return selectedIndex < 0 ? this.findLastOptionIndex() : selectedIndex;
909
+ }
910
+ findLastSelectedOptionIndex() {
911
+ return this.hasSelectedOption() ? ObjectUtils.findLastIndex(this.visibleOptions(), (option) => this.isValidSelectedOption(option)) : -1;
912
+ }
913
+ findNextOptionIndex(index) {
914
+ const matchedOptionIndex = index < this.visibleOptions().length - 1
915
+ ? this.visibleOptions()
916
+ .slice(index + 1)
917
+ .findIndex((option) => this.isValidOption(option))
918
+ : -1;
919
+ return matchedOptionIndex > -1 ? matchedOptionIndex + index + 1 : index;
920
+ }
921
+ findNextSelectedOptionIndex(index) {
922
+ const matchedOptionIndex = this.hasSelectedOption() && index < this.visibleOptions().length - 1
923
+ ? this.visibleOptions()
924
+ .slice(index + 1)
925
+ .findIndex((option) => this.isValidSelectedOption(option))
926
+ : -1;
927
+ return matchedOptionIndex > -1 ? matchedOptionIndex + index + 1 : -1;
928
+ }
929
+ findPrevSelectedOptionIndex(index) {
930
+ const matchedOptionIndex = this.hasSelectedOption() && index > 0 ? ObjectUtils.findLastIndex(this.visibleOptions().slice(0, index), (option) => this.isValidSelectedOption(option)) : -1;
931
+ return matchedOptionIndex > -1 ? matchedOptionIndex : -1;
932
+ }
933
+ findFirstSelectedOptionIndex() {
934
+ return this.hasSelectedOption() ? this.visibleOptions().findIndex((option) => this.isValidSelectedOption(option)) : -1;
935
+ }
936
+ findPrevOptionIndex(index) {
937
+ const matchedOptionIndex = index > 0 ? ObjectUtils.findLastIndex(this.visibleOptions().slice(0, index), (option) => this.isValidOption(option)) : -1;
938
+ return matchedOptionIndex > -1 ? matchedOptionIndex : index;
939
+ }
940
+ findNearestSelectedOptionIndex(index, firstCheckUp = false) {
941
+ let matchedOptionIndex = -1;
942
+ if (this.hasSelectedOption()) {
943
+ if (firstCheckUp) {
944
+ matchedOptionIndex = this.findPrevSelectedOptionIndex(index);
945
+ matchedOptionIndex = matchedOptionIndex === -1 ? this.findNextSelectedOptionIndex(index) : matchedOptionIndex;
604
946
  }
605
947
  else {
606
- if (opt.items) {
607
- opt.items.forEach((option) => {
608
- let optionDisabled = this.isOptionDisabled(option);
609
- if (optionDisabled && this.isSelected(option)) {
610
- val.push(this.getOptionValue(option));
611
- }
612
- });
613
- }
948
+ matchedOptionIndex = this.findNextSelectedOptionIndex(index);
949
+ matchedOptionIndex = matchedOptionIndex === -1 ? this.findPrevSelectedOptionIndex(index) : matchedOptionIndex;
614
950
  }
615
- });
616
- this.value = val;
617
- }
618
- onOptionKeyDown(event, option) {
619
- if (this.readonly) {
620
- return;
621
- }
622
- let item = event.currentTarget;
623
- switch (event.which) {
624
- //down
625
- case 40:
626
- var nextItem = this.findNextItem(item);
627
- if (nextItem) {
628
- nextItem.focus();
629
- }
630
- event.preventDefault();
631
- break;
632
- //up
633
- case 38:
634
- var prevItem = this.findPrevItem(item);
635
- if (prevItem) {
636
- prevItem.focus();
637
- }
638
- event.preventDefault();
639
- break;
640
- //enter
641
- case 13:
642
- this.onOptionClick(event, option);
643
- event.preventDefault();
644
- break;
645
951
  }
952
+ return matchedOptionIndex > -1 ? matchedOptionIndex : index;
646
953
  }
647
- findNextItem(item) {
648
- let nextItem = item.nextElementSibling;
649
- if (nextItem)
650
- return DomHandler.hasClass(nextItem, 'p-disabled') || DomHandler.isHidden(nextItem) || DomHandler.hasClass(nextItem, 'p-listbox-item-group') ? this.findNextItem(nextItem) : nextItem;
651
- else
652
- return null;
954
+ equalityKey() {
955
+ return this.optionValue ? null : this.dataKey;
956
+ }
957
+ isValidSelectedOption(option) {
958
+ return this.isValidOption(option) && this.isSelected(option);
959
+ }
960
+ isOptionDisabled(option) {
961
+ return this.optionDisabled ? ObjectUtils.resolveFieldData(option, this.optionDisabled) : false;
653
962
  }
654
- findPrevItem(item) {
655
- let prevItem = item.previousElementSibling;
656
- if (prevItem)
657
- return DomHandler.hasClass(prevItem, 'p-disabled') || DomHandler.isHidden(prevItem) || DomHandler.hasClass(prevItem, 'p-listbox-item-group') ? this.findPrevItem(prevItem) : prevItem;
963
+ isSelected(option) {
964
+ const optionValue = this.getOptionValue(option);
965
+ if (this.multiple)
966
+ return (this.modelValue() || []).some((value) => ObjectUtils.equals(value, optionValue, this.equalityKey()));
658
967
  else
659
- return null;
968
+ return ObjectUtils.equals(this.modelValue(), optionValue, this.equalityKey());
660
969
  }
661
- onHeaderCheckboxFocus() {
662
- this.headerCheckboxFocus = true;
970
+ isValidOption(option) {
971
+ return option && !(this.isOptionDisabled(option) || this.isOptionGroup(option));
663
972
  }
664
- onHeaderCheckboxBlur() {
665
- this.headerCheckboxFocus = false;
973
+ isEmpty() {
974
+ return !this._options() || (this._options() && this._options().length === 0);
975
+ }
976
+ hasFilter() {
977
+ return this._filterValue() && this._filterValue().trim().length > 0;
978
+ }
979
+ resetFilter() {
980
+ if (this.filterViewChild && this.filterViewChild.nativeElement) {
981
+ this.filterViewChild.nativeElement.value = '';
982
+ }
983
+ this._filterValue.set(null);
666
984
  }
667
985
  ngOnDestroy() {
668
986
  if (this.translationSubscription) {
669
987
  this.translationSubscription.unsubscribe();
670
988
  }
671
989
  }
672
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.2", ngImport: i0, type: Listbox, deps: [{ token: i0.ElementRef }, { token: i0.ChangeDetectorRef }, { token: i1.FilterService }, { token: i1.PrimeNGConfig }], target: i0.ɵɵFactoryTarget.Component });
673
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.0.2", type: Listbox, selector: "p-listbox", inputs: { multiple: "multiple", style: "style", styleClass: "styleClass", listStyle: "listStyle", listStyleClass: "listStyleClass", readonly: "readonly", disabled: "disabled", checkbox: "checkbox", filter: "filter", filterBy: "filterBy", filterMatchMode: "filterMatchMode", filterLocale: "filterLocale", metaKeySelection: "metaKeySelection", dataKey: "dataKey", showToggleAll: "showToggleAll", optionLabel: "optionLabel", optionValue: "optionValue", optionGroupChildren: "optionGroupChildren", optionGroupLabel: "optionGroupLabel", optionDisabled: "optionDisabled", ariaFilterLabel: "ariaFilterLabel", filterPlaceHolder: "filterPlaceHolder", emptyFilterMessage: "emptyFilterMessage", emptyMessage: "emptyMessage", group: "group", options: "options", filterValue: "filterValue" }, outputs: { onChange: "onChange", onClick: "onClick", onDblClick: "onDblClick" }, host: { classAttribute: "p-element" }, providers: [LISTBOX_VALUE_ACCESSOR], queries: [{ propertyName: "headerFacet", first: true, predicate: Header, descendants: true }, { propertyName: "footerFacet", first: true, predicate: Footer, descendants: true }, { propertyName: "templates", predicate: PrimeTemplate }], viewQueries: [{ propertyName: "headerCheckboxViewChild", first: true, predicate: ["headerchkbox"], descendants: true }, { propertyName: "filterViewChild", first: true, predicate: ["filter"], descendants: true }], ngImport: i0, template: `
674
- <div [ngClass]="{ 'p-listbox p-component': true, 'p-disabled': disabled }" [ngStyle]="style" [class]="styleClass">
990
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.2", ngImport: i0, type: Listbox, deps: [{ token: i0.ElementRef }, { token: i0.ChangeDetectorRef }, { token: i1.FilterService }, { token: i1.PrimeNGConfig }, { token: i0.Renderer2 }], target: i0.ɵɵFactoryTarget.Component });
991
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.0.2", type: Listbox, selector: "p-listbox", inputs: { id: "id", searchMessage: "searchMessage", emptySelectionMessage: "emptySelectionMessage", selectionMessage: "selectionMessage", autoOptionFocus: "autoOptionFocus", selectOnFocus: "selectOnFocus", searchLocale: "searchLocale", focusOnHover: "focusOnHover", filterMessage: "filterMessage", filterFields: "filterFields", lazy: "lazy", virtualScroll: "virtualScroll", virtualScrollItemSize: "virtualScrollItemSize", virtualScrollOptions: "virtualScrollOptions", scrollHeight: "scrollHeight", tabindex: "tabindex", multiple: "multiple", style: "style", styleClass: "styleClass", listStyle: "listStyle", listStyleClass: "listStyleClass", readonly: "readonly", disabled: "disabled", checkbox: "checkbox", filter: "filter", filterBy: "filterBy", filterMatchMode: "filterMatchMode", filterLocale: "filterLocale", metaKeySelection: "metaKeySelection", dataKey: "dataKey", showToggleAll: "showToggleAll", optionLabel: "optionLabel", optionValue: "optionValue", optionGroupChildren: "optionGroupChildren", optionGroupLabel: "optionGroupLabel", optionDisabled: "optionDisabled", ariaFilterLabel: "ariaFilterLabel", filterPlaceHolder: "filterPlaceHolder", emptyFilterMessage: "emptyFilterMessage", emptyMessage: "emptyMessage", group: "group", options: "options", filterValue: "filterValue" }, outputs: { onChange: "onChange", onClick: "onClick", onDblClick: "onDblClick", onFilter: "onFilter", onFocus: "onFocus", onBlur: "onBlur" }, host: { classAttribute: "p-element" }, providers: [LISTBOX_VALUE_ACCESSOR], queries: [{ propertyName: "headerFacet", first: true, predicate: Header, descendants: true }, { propertyName: "footerFacet", first: true, predicate: Footer, descendants: true }, { propertyName: "templates", predicate: PrimeTemplate }], viewQueries: [{ propertyName: "headerCheckboxViewChild", first: true, predicate: ["headerchkbox"], descendants: true }, { propertyName: "filterViewChild", first: true, predicate: ["filter"], descendants: true }, { propertyName: "lastHiddenFocusableElement", first: true, predicate: ["lastHiddenFocusableElement"], descendants: true }, { propertyName: "firstHiddenFocusableElement", first: true, predicate: ["firstHiddenFocusableElement"], descendants: true }, { propertyName: "scroller", first: true, predicate: ["scroller"], descendants: true }, { propertyName: "listViewChild", first: true, predicate: ["list"], descendants: true }], ngImport: i0, template: `
992
+ <div [attr.id]="id" [ngClass]="containerClass" [ngStyle]="style" [class]="styleClass" (focusout)="onFocusout($event)">
993
+ <span
994
+ #firstHiddenFocusableElement
995
+ role="presentation"
996
+ [attr.aria-hidden]="true"
997
+ class="p-hidden-accessible p-hidden-focusable"
998
+ [tabindex]="!disabled ? tabindex : -1"
999
+ (focus)="onFirstHiddenFocus($event)"
1000
+ [attr.data-p-hidden-focusable]="true"
1001
+ >
1002
+ </span>
675
1003
  <div class="p-listbox-header" *ngIf="headerFacet || headerTemplate">
676
1004
  <ng-content select="p-header"></ng-content>
677
- <ng-container *ngTemplateOutlet="headerTemplate"></ng-container>
1005
+ <ng-container *ngTemplateOutlet="headerTemplate; context: { $implicit: modelValue(), options: visibleOptions() }"></ng-container>
678
1006
  </div>
679
1007
  <div class="p-listbox-header" *ngIf="(checkbox && multiple && showToggleAll) || filter">
680
- <div class="p-checkbox p-component" *ngIf="checkbox && multiple && showToggleAll" [ngClass]="{ 'p-checkbox-disabled': disabled || toggleAllDisabled }">
681
- <div class="p-hidden-accessible">
682
- <input type="checkbox" readonly="readonly" [checked]="allChecked" (focus)="onHeaderCheckboxFocus()" (blur)="onHeaderCheckboxBlur()" (keydown.space)="toggleAll($event)" [disabled]="disabled || toggleAllDisabled" />
1008
+ <div *ngIf="checkbox && multiple && showToggleAll" class="p-checkbox p-component" [ngClass]="{ 'p-checkbox-disabled': disabled || toggleAllDisabled }" (click)="onToggleAll($event)" (keydown)="onHeaderCheckboxKeyDown($event)">
1009
+ <div class="p-hidden-accessible" [attr.data-p-hidden-accessible]="true">
1010
+ <input
1011
+ #headerchkbox
1012
+ type="checkbox"
1013
+ readonly="readonly"
1014
+ [attr.checked]="allSelected()"
1015
+ [disabled]="disabled || toggleAllDisabled"
1016
+ (focus)="onHeaderCheckboxFocus($event)"
1017
+ (blur)="onHeaderCheckboxBlur($event)"
1018
+ [attr.aria-label]="toggleAllAriaLabel"
1019
+ />
683
1020
  </div>
684
- <div #headerchkbox class="p-checkbox-box" [ngClass]="{ 'p-highlight': allChecked, 'p-focus': headerCheckboxFocus, 'p-disabled': disabled || toggleAllDisabled }" (click)="toggleAll($event)">
685
- <ng-container *ngIf="allChecked">
686
- <CheckIcon [styleClass]="'p-checkbox-icon'" *ngIf="!checkIconTemplate" />
687
- <span *ngIf="checkIconTemplate" class="p-checkbox-icon">
1021
+ <div class="p-checkbox-box" role="checkbox" [attr.aria-checked]="allSelected()" [ngClass]="{ 'p-highlight': allSelected(), 'p-focus': headerCheckboxFocus, 'p-disabled': disabled || toggleAllDisabled }">
1022
+ <ng-container *ngIf="allSelected()">
1023
+ <CheckIcon [styleClass]="'p-checkbox-icon'" *ngIf="!checkIconTemplate" [attr.aria-hidden]="true" />
1024
+ <span *ngIf="checkIconTemplate" class="p-checkbox-icon" [attr.aria-hidden]="true">
688
1025
  <ng-template *ngTemplateOutlet="checkIconTemplate"></ng-template>
689
1026
  </span>
690
1027
  </ng-container>
@@ -696,102 +1033,189 @@ class Listbox {
696
1033
  <ng-template #builtInFilterElement>
697
1034
  <div class="p-listbox-filter-container" *ngIf="filter">
698
1035
  <input
699
- #filter
1036
+ #filterInput
700
1037
  type="text"
701
- [value]="filterValue || ''"
702
- (input)="onFilter($event)"
703
1038
  class="p-listbox-filter p-inputtext p-component"
1039
+ role="searchbox"
1040
+ [value]="_filterValue() || ''"
704
1041
  [disabled]="disabled"
1042
+ [attr.aria-owns]="id + '_list'"
1043
+ [attr.aria-activedescendant]="focusedOptionId"
705
1044
  [attr.placeholder]="filterPlaceHolder"
706
1045
  [attr.aria-label]="ariaFilterLabel"
1046
+ [tabindex]="!disabled && !focused ? tabindex : -1"
1047
+ (input)="onFilterChange($event)"
1048
+ (keydown)="onFilterKeyDown($event)"
1049
+ (blur)="onFilterBlur($event)"
707
1050
  />
708
- <SearchIcon *ngIf="!filterIconTemplate" [styleClass]="'p-listbox-filter-icon'" />
709
- <span *ngIf="filterIconTemplate" class="p-listbox-filter-icon">
1051
+ <SearchIcon *ngIf="!filterIconTemplate" [styleClass]="'p-listbox-filter-icon'" [attr.aria-hidden]="true" />
1052
+ <span *ngIf="filterIconTemplate" class="p-listbox-filter-icon" [attr.aria-hidden]="true">
710
1053
  <ng-template *ngTemplateOutlet="filterIconTemplate"></ng-template>
711
1054
  </span>
712
1055
  </div>
1056
+ <span role="status" attr.aria-live="polite" class="p-hidden-accessible" [attr.data-p-hidden-accessible]="true">
1057
+ {{ filterResultMessageText }}
1058
+ </span>
713
1059
  </ng-template>
714
1060
  </div>
715
- <div [ngClass]="'p-listbox-list-wrapper'" [ngStyle]="listStyle" [class]="listStyleClass">
716
- <ul class="p-listbox-list" role="listbox" [attr.aria-multiselectable]="multiple">
717
- <ng-container *ngIf="group">
718
- <ng-template ngFor let-optgroup [ngForOf]="optionsToRender">
719
- <li class="p-listbox-item-group">
720
- <span *ngIf="!groupTemplate">{{ getOptionGroupLabel(optgroup) || 'empty' }}</span>
721
- <ng-container *ngTemplateOutlet="groupTemplate; context: { $implicit: optgroup }"></ng-container>
722
- </li>
723
- <ng-container *ngTemplateOutlet="itemslist; context: { $implicit: getOptionGroupChildren(optgroup) }"></ng-container>
1061
+ <div [ngClass]="'p-listbox-list-wrapper'" [ngStyle]="listStyle" [class]="listStyleClass" [style.max-height]="virtualScroll ? 'auto' : scrollHeight || 'auto'">
1062
+ <p-scroller
1063
+ #scroller
1064
+ *ngIf="virtualScroll"
1065
+ [items]="visibleOptions()"
1066
+ [style]="{ height: scrollHeight }"
1067
+ [itemSize]="virtualScrollItemSize"
1068
+ [autoSize]="true"
1069
+ [tabindex]="-1"
1070
+ [lazy]="lazy"
1071
+ [options]="virtualScrollOptions"
1072
+ (onLazyLoad)="onLazyLoad.emit($event)"
1073
+ >
1074
+ <ng-template pTemplate="content" let-items let-scrollerOptions="options">
1075
+ <ng-container *ngTemplateOutlet="buildInItems; context: { $implicit: items, options: scrollerOptions }"></ng-container>
1076
+ </ng-template>
1077
+ <ng-container *ngIf="loaderTemplate">
1078
+ <ng-template pTemplate="loader" let-scrollerOptions="options">
1079
+ <ng-container *ngTemplateOutlet="loaderTemplate; context: { options: scrollerOptions }"></ng-container>
724
1080
  </ng-template>
725
1081
  </ng-container>
726
- <ng-container *ngIf="!group">
727
- <ng-container *ngTemplateOutlet="itemslist; context: { $implicit: optionsToRender }"></ng-container>
728
- </ng-container>
729
- <ng-template #itemslist let-optionsToDisplay>
730
- <li
731
- *ngFor="let option of optionsToDisplay; let i = index"
732
- [attr.tabindex]="disabled || isOptionDisabled(option) ? null : '0'"
733
- pRipple
734
- [ngClass]="{ 'p-listbox-item': true, 'p-highlight': isSelected(option), 'p-disabled': this.isOptionDisabled(option) }"
735
- role="option"
736
- [attr.aria-label]="getOptionLabel(option)"
737
- [attr.aria-selected]="isSelected(option)"
738
- (click)="onOptionClick($event, option)"
739
- (dblclick)="onOptionDoubleClick($event, option)"
740
- (touchend)="onOptionTouchEnd(option)"
741
- (keydown)="onOptionKeyDown($event, option)"
742
- >
743
- <div class="p-checkbox p-component" *ngIf="checkbox && multiple" [ngClass]="{ 'p-checkbox-disabled': disabled || isOptionDisabled(option) }">
744
- <div class="p-checkbox-box" [ngClass]="{ 'p-highlight': isSelected(option) }">
745
- <ng-container *ngIf="isSelected(option)">
746
- <CheckIcon [styleClass]="'p-checkbox-icon'" *ngIf="!checkIconTemplate" />
747
- <span *ngIf="checkIconTemplate" class="p-checkbox-icon">
748
- <ng-template *ngTemplateOutlet="checkIconTemplate"></ng-template>
749
- </span>
750
- </ng-container>
751
- </div>
752
- </div>
753
- <span *ngIf="!itemTemplate">{{ getOptionLabel(option) }}</span>
754
- <ng-container *ngTemplateOutlet="itemTemplate; context: { $implicit: option, index: i }"></ng-container>
1082
+ </p-scroller>
1083
+ <ng-container *ngIf="!virtualScroll">
1084
+ <ng-container *ngTemplateOutlet="buildInItems; context: { $implicit: visibleOptions(), options: {} }"></ng-container>
1085
+ </ng-container>
1086
+
1087
+ <ng-template #buildInItems let-items let-scrollerOptions="options">
1088
+ <ul
1089
+ #list
1090
+ class="p-listbox-list"
1091
+ role="listbox"
1092
+ [tabindex]="-1"
1093
+ [attr.aria-multiselectable]="true"
1094
+ [ngClass]="scrollerOptions.contentStyleClass"
1095
+ [style]="scrollerOptions.contentStyle"
1096
+ [attr.aria-activedescendant]="focused ? focusedOptionId : undefined"
1097
+ [attr.aria-label]="ariaLabel"
1098
+ [attr.aria-multiselectable]="multiple"
1099
+ [attr.aria-disabled]="disabled"
1100
+ (focus)="onListFocus($event)"
1101
+ (blur)="onListBlur($event)"
1102
+ (keydown)="onListKeyDown($event)"
1103
+ >
1104
+ <ng-template ngFor let-option [ngForOf]="items" let-i="index">
1105
+ <ng-container *ngIf="isOptionGroup(option)">
1106
+ <li [attr.id]="id + '_' + getOptionIndex(i, scrollerOptions)" class="p-listbox-item-group" [ngStyle]="{ height: scrollerOptions.itemSize + 'px' }" role="option">
1107
+ <span *ngIf="!groupTemplate">{{ getOptionGroupLabel(option.optionGroup) }}</span>
1108
+ <ng-container *ngTemplateOutlet="groupTemplate; context: { $implicit: option.optionGroup }"></ng-container>
1109
+ </li>
1110
+ </ng-container>
1111
+ <ng-container *ngIf="!isOptionGroup(option)">
1112
+ <li
1113
+ pRipple
1114
+ class="p-listbox-item"
1115
+ role="option"
1116
+ [attr.id]="id + '_' + getOptionIndex(i, scrollerOptions)"
1117
+ [ngStyle]="{ height: scrollerOptions.itemSize + 'px' }"
1118
+ [ngClass]="{ 'p-listbox-item': true, 'p-highlight': isSelected(option), 'p-focus': focusedOptionIndex() === getOptionIndex(i, scrollerOptions), 'p-disabled': isOptionDisabled(option) }"
1119
+ [attr.aria-label]="getOptionLabel(option)"
1120
+ [attr.aria-selected]="isSelected(option)"
1121
+ [attr.aria-disabled]="isOptionDisabled(option)"
1122
+ [attr.aria-setsize]="ariaSetSize"
1123
+ [ariaPosInset]="getAriaPosInset(getOptionIndex(i, scrollerOptions))"
1124
+ (click)="onOptionSelect($event, option, getOptionIndex(i, scrollerOptions))"
1125
+ (dblclick)="onOptionDoubleClick($event, option)"
1126
+ (mousedown)="onOptionMouseDown($event, getOptionIndex(i, scrollerOptions))"
1127
+ (mouseenter)="onOptionMouseEnter($event, getOptionIndex(i, scrollerOptions))"
1128
+ (touchend)="onOptionTouchEnd()"
1129
+ >
1130
+ <div class="p-checkbox p-component" *ngIf="checkbox && multiple" [ngClass]="{ 'p-checkbox-disabled': disabled || isOptionDisabled(option) }">
1131
+ <div class="p-checkbox-box" [ngClass]="{ 'p-highlight': isSelected(option) }">
1132
+ <ng-container *ngIf="isSelected(option)">
1133
+ <CheckIcon [styleClass]="'p-checkbox-icon'" *ngIf="!checkIconTemplate" [attr.aria-hidden]="true" />
1134
+ <span *ngIf="checkIconTemplate" class="p-checkbox-icon" [attr.aria-hidden]="true">
1135
+ <ng-template *ngTemplateOutlet="checkIconTemplate"></ng-template>
1136
+ </span>
1137
+ </ng-container>
1138
+ </div>
1139
+ </div>
1140
+ <span *ngIf="!itemTemplate">{{ getOptionLabel(option) }}</span>
1141
+ <ng-container *ngTemplateOutlet="itemTemplate; context: { $implicit: option, index: getOptionIndex(i, scrollerOptions) }"></ng-container>
1142
+ </li>
1143
+ </ng-container>
1144
+ </ng-template>
1145
+ <li *ngIf="hasFilter() && isEmpty()" class="p-listbox-empty-message" role="option">
1146
+ <ng-container *ngIf="!emptyFilterTemplate && !emptyTemplate; else emptyFilter">
1147
+ {{ emptyFilterMessageText }}
1148
+ </ng-container>
1149
+ <ng-container #emptyFilter *ngTemplateOutlet="emptyFilterTemplate || emptyTemplate"></ng-container>
755
1150
  </li>
756
- </ng-template>
757
- <li *ngIf="hasFilter() && isEmpty()" class="p-listbox-empty-message">
758
- <ng-container *ngIf="!emptyFilterTemplate && !emptyTemplate; else emptyFilter">
759
- {{ emptyFilterMessageLabel }}
760
- </ng-container>
761
- <ng-container #emptyFilter *ngTemplateOutlet="emptyFilterTemplate || emptyTemplate"></ng-container>
762
- </li>
763
- <li *ngIf="!hasFilter() && isEmpty()" class="p-listbox-empty-message">
764
- <ng-container *ngIf="!emptyTemplate; else empty">
765
- {{ emptyMessageLabel }}
766
- </ng-container>
767
- <ng-container #empty *ngTemplateOutlet="emptyTemplate"></ng-container>
768
- </li>
769
- </ul>
1151
+ <li *ngIf="!hasFilter() && isEmpty()" class="p-listbox-empty-message" role="option">
1152
+ <ng-container *ngIf="!emptyTemplate; else empty">
1153
+ {{ emptyMessageText }}
1154
+ </ng-container>
1155
+ <ng-container #empty *ngTemplateOutlet="emptyTemplate"></ng-container>
1156
+ </li>
1157
+ </ul>
1158
+ </ng-template>
770
1159
  </div>
771
1160
  <div class="p-listbox-footer" *ngIf="footerFacet || footerTemplate">
772
1161
  <ng-content select="p-footer"></ng-content>
773
- <ng-container *ngTemplateOutlet="footerTemplate"></ng-container>
1162
+ <ng-container *ngTemplateOutlet="footerTemplate; context: { $implicit: modelValue(), options: visibleOptions() }"></ng-container>
774
1163
  </div>
1164
+ <span *ngIf="isEmpty()" role="status" aria-live="polite" class="p-hidden-accessible">
1165
+ {{ emptyMessageText }}
1166
+ </span>
1167
+ <span role="status" aria-live="polite" class="p-hidden-accessible">
1168
+ {{ selectedMessageText }}
1169
+ </span>
1170
+ <span
1171
+ #lastHiddenFocusableElement
1172
+ role="presentation"
1173
+ [attr.aria-hidden]="true"
1174
+ class="p-hidden-accessible p-hidden-focusable"
1175
+ [tabindex]="!disabled ? tabindex : -1"
1176
+ (focus)="onLastHiddenFocus($event)"
1177
+ [attr.data-p-hidden-focusable]="true"
1178
+ >
1179
+ </span>
775
1180
  </div>
776
- `, isInline: true, styles: ["@layer primeng{.p-listbox-list-wrapper{overflow:auto}.p-listbox-list{list-style-type:none;margin:0;padding:0}.p-listbox-item{cursor:pointer;position:relative;overflow:hidden;display:flex;align-items:center;-webkit-user-select:none;user-select:none}.p-listbox-header{display:flex;align-items:center}.p-listbox-filter-container{position:relative;flex:1 1 auto}.p-listbox-filter-icon{position:absolute;top:50%;margin-top:-.5rem}.p-listbox-filter{width:100%}}\n"], dependencies: [{ kind: "directive", type: i0.forwardRef(function () { return i2.NgClass; }), selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i0.forwardRef(function () { return i2.NgForOf; }), selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i0.forwardRef(function () { return i2.NgIf; }), selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i0.forwardRef(function () { return i2.NgTemplateOutlet; }), selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i0.forwardRef(function () { return i2.NgStyle; }), selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i0.forwardRef(function () { return i3.Ripple; }), selector: "[pRipple]" }, { kind: "component", type: i0.forwardRef(function () { return SearchIcon; }), selector: "SearchIcon" }, { kind: "component", type: i0.forwardRef(function () { return CheckIcon; }), selector: "CheckIcon" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
1181
+ `, isInline: true, styles: ["@layer primeng{.p-listbox-list-wrapper{overflow:auto}.p-listbox-list{list-style-type:none;margin:0;padding:0}.p-listbox-item{cursor:pointer;position:relative;overflow:hidden;display:flex;align-items:center;-webkit-user-select:none;user-select:none}.p-listbox-header{display:flex;align-items:center}.p-listbox-filter-container{position:relative;flex:1 1 auto}.p-listbox-filter-icon{position:absolute;top:50%;margin-top:-.5rem}.p-listbox-filter{width:100%}}\n"], dependencies: [{ kind: "directive", type: i0.forwardRef(function () { return i2.NgClass; }), selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i0.forwardRef(function () { return i2.NgForOf; }), selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i0.forwardRef(function () { return i2.NgIf; }), selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i0.forwardRef(function () { return i2.NgTemplateOutlet; }), selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i0.forwardRef(function () { return i2.NgStyle; }), selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i0.forwardRef(function () { return i1.PrimeTemplate; }), selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "directive", type: i0.forwardRef(function () { return i3.Ripple; }), selector: "[pRipple]" }, { kind: "component", type: i0.forwardRef(function () { return i4.Scroller; }), selector: "p-scroller", inputs: ["id", "style", "styleClass", "tabindex", "items", "itemSize", "scrollHeight", "scrollWidth", "orientation", "step", "delay", "resizeDelay", "appendOnly", "inline", "lazy", "disabled", "loaderDisabled", "columns", "showSpacer", "showLoader", "numToleratedItems", "loading", "autoSize", "trackBy", "options"], outputs: ["onLazyLoad", "onScroll", "onScrollIndexChange"] }, { kind: "component", type: i0.forwardRef(function () { return SearchIcon; }), selector: "SearchIcon" }, { kind: "component", type: i0.forwardRef(function () { return CheckIcon; }), selector: "CheckIcon" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
777
1182
  }
778
1183
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.2", ngImport: i0, type: Listbox, decorators: [{
779
1184
  type: Component,
780
1185
  args: [{ selector: 'p-listbox', template: `
781
- <div [ngClass]="{ 'p-listbox p-component': true, 'p-disabled': disabled }" [ngStyle]="style" [class]="styleClass">
1186
+ <div [attr.id]="id" [ngClass]="containerClass" [ngStyle]="style" [class]="styleClass" (focusout)="onFocusout($event)">
1187
+ <span
1188
+ #firstHiddenFocusableElement
1189
+ role="presentation"
1190
+ [attr.aria-hidden]="true"
1191
+ class="p-hidden-accessible p-hidden-focusable"
1192
+ [tabindex]="!disabled ? tabindex : -1"
1193
+ (focus)="onFirstHiddenFocus($event)"
1194
+ [attr.data-p-hidden-focusable]="true"
1195
+ >
1196
+ </span>
782
1197
  <div class="p-listbox-header" *ngIf="headerFacet || headerTemplate">
783
1198
  <ng-content select="p-header"></ng-content>
784
- <ng-container *ngTemplateOutlet="headerTemplate"></ng-container>
1199
+ <ng-container *ngTemplateOutlet="headerTemplate; context: { $implicit: modelValue(), options: visibleOptions() }"></ng-container>
785
1200
  </div>
786
1201
  <div class="p-listbox-header" *ngIf="(checkbox && multiple && showToggleAll) || filter">
787
- <div class="p-checkbox p-component" *ngIf="checkbox && multiple && showToggleAll" [ngClass]="{ 'p-checkbox-disabled': disabled || toggleAllDisabled }">
788
- <div class="p-hidden-accessible">
789
- <input type="checkbox" readonly="readonly" [checked]="allChecked" (focus)="onHeaderCheckboxFocus()" (blur)="onHeaderCheckboxBlur()" (keydown.space)="toggleAll($event)" [disabled]="disabled || toggleAllDisabled" />
1202
+ <div *ngIf="checkbox && multiple && showToggleAll" class="p-checkbox p-component" [ngClass]="{ 'p-checkbox-disabled': disabled || toggleAllDisabled }" (click)="onToggleAll($event)" (keydown)="onHeaderCheckboxKeyDown($event)">
1203
+ <div class="p-hidden-accessible" [attr.data-p-hidden-accessible]="true">
1204
+ <input
1205
+ #headerchkbox
1206
+ type="checkbox"
1207
+ readonly="readonly"
1208
+ [attr.checked]="allSelected()"
1209
+ [disabled]="disabled || toggleAllDisabled"
1210
+ (focus)="onHeaderCheckboxFocus($event)"
1211
+ (blur)="onHeaderCheckboxBlur($event)"
1212
+ [attr.aria-label]="toggleAllAriaLabel"
1213
+ />
790
1214
  </div>
791
- <div #headerchkbox class="p-checkbox-box" [ngClass]="{ 'p-highlight': allChecked, 'p-focus': headerCheckboxFocus, 'p-disabled': disabled || toggleAllDisabled }" (click)="toggleAll($event)">
792
- <ng-container *ngIf="allChecked">
793
- <CheckIcon [styleClass]="'p-checkbox-icon'" *ngIf="!checkIconTemplate" />
794
- <span *ngIf="checkIconTemplate" class="p-checkbox-icon">
1215
+ <div class="p-checkbox-box" role="checkbox" [attr.aria-checked]="allSelected()" [ngClass]="{ 'p-highlight': allSelected(), 'p-focus': headerCheckboxFocus, 'p-disabled': disabled || toggleAllDisabled }">
1216
+ <ng-container *ngIf="allSelected()">
1217
+ <CheckIcon [styleClass]="'p-checkbox-icon'" *ngIf="!checkIconTemplate" [attr.aria-hidden]="true" />
1218
+ <span *ngIf="checkIconTemplate" class="p-checkbox-icon" [attr.aria-hidden]="true">
795
1219
  <ng-template *ngTemplateOutlet="checkIconTemplate"></ng-template>
796
1220
  </span>
797
1221
  </ng-container>
@@ -803,87 +1227,187 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.2", ngImpor
803
1227
  <ng-template #builtInFilterElement>
804
1228
  <div class="p-listbox-filter-container" *ngIf="filter">
805
1229
  <input
806
- #filter
1230
+ #filterInput
807
1231
  type="text"
808
- [value]="filterValue || ''"
809
- (input)="onFilter($event)"
810
1232
  class="p-listbox-filter p-inputtext p-component"
1233
+ role="searchbox"
1234
+ [value]="_filterValue() || ''"
811
1235
  [disabled]="disabled"
1236
+ [attr.aria-owns]="id + '_list'"
1237
+ [attr.aria-activedescendant]="focusedOptionId"
812
1238
  [attr.placeholder]="filterPlaceHolder"
813
1239
  [attr.aria-label]="ariaFilterLabel"
1240
+ [tabindex]="!disabled && !focused ? tabindex : -1"
1241
+ (input)="onFilterChange($event)"
1242
+ (keydown)="onFilterKeyDown($event)"
1243
+ (blur)="onFilterBlur($event)"
814
1244
  />
815
- <SearchIcon *ngIf="!filterIconTemplate" [styleClass]="'p-listbox-filter-icon'" />
816
- <span *ngIf="filterIconTemplate" class="p-listbox-filter-icon">
1245
+ <SearchIcon *ngIf="!filterIconTemplate" [styleClass]="'p-listbox-filter-icon'" [attr.aria-hidden]="true" />
1246
+ <span *ngIf="filterIconTemplate" class="p-listbox-filter-icon" [attr.aria-hidden]="true">
817
1247
  <ng-template *ngTemplateOutlet="filterIconTemplate"></ng-template>
818
1248
  </span>
819
1249
  </div>
1250
+ <span role="status" attr.aria-live="polite" class="p-hidden-accessible" [attr.data-p-hidden-accessible]="true">
1251
+ {{ filterResultMessageText }}
1252
+ </span>
820
1253
  </ng-template>
821
1254
  </div>
822
- <div [ngClass]="'p-listbox-list-wrapper'" [ngStyle]="listStyle" [class]="listStyleClass">
823
- <ul class="p-listbox-list" role="listbox" [attr.aria-multiselectable]="multiple">
824
- <ng-container *ngIf="group">
825
- <ng-template ngFor let-optgroup [ngForOf]="optionsToRender">
826
- <li class="p-listbox-item-group">
827
- <span *ngIf="!groupTemplate">{{ getOptionGroupLabel(optgroup) || 'empty' }}</span>
828
- <ng-container *ngTemplateOutlet="groupTemplate; context: { $implicit: optgroup }"></ng-container>
829
- </li>
830
- <ng-container *ngTemplateOutlet="itemslist; context: { $implicit: getOptionGroupChildren(optgroup) }"></ng-container>
1255
+ <div [ngClass]="'p-listbox-list-wrapper'" [ngStyle]="listStyle" [class]="listStyleClass" [style.max-height]="virtualScroll ? 'auto' : scrollHeight || 'auto'">
1256
+ <p-scroller
1257
+ #scroller
1258
+ *ngIf="virtualScroll"
1259
+ [items]="visibleOptions()"
1260
+ [style]="{ height: scrollHeight }"
1261
+ [itemSize]="virtualScrollItemSize"
1262
+ [autoSize]="true"
1263
+ [tabindex]="-1"
1264
+ [lazy]="lazy"
1265
+ [options]="virtualScrollOptions"
1266
+ (onLazyLoad)="onLazyLoad.emit($event)"
1267
+ >
1268
+ <ng-template pTemplate="content" let-items let-scrollerOptions="options">
1269
+ <ng-container *ngTemplateOutlet="buildInItems; context: { $implicit: items, options: scrollerOptions }"></ng-container>
1270
+ </ng-template>
1271
+ <ng-container *ngIf="loaderTemplate">
1272
+ <ng-template pTemplate="loader" let-scrollerOptions="options">
1273
+ <ng-container *ngTemplateOutlet="loaderTemplate; context: { options: scrollerOptions }"></ng-container>
831
1274
  </ng-template>
832
1275
  </ng-container>
833
- <ng-container *ngIf="!group">
834
- <ng-container *ngTemplateOutlet="itemslist; context: { $implicit: optionsToRender }"></ng-container>
835
- </ng-container>
836
- <ng-template #itemslist let-optionsToDisplay>
837
- <li
838
- *ngFor="let option of optionsToDisplay; let i = index"
839
- [attr.tabindex]="disabled || isOptionDisabled(option) ? null : '0'"
840
- pRipple
841
- [ngClass]="{ 'p-listbox-item': true, 'p-highlight': isSelected(option), 'p-disabled': this.isOptionDisabled(option) }"
842
- role="option"
843
- [attr.aria-label]="getOptionLabel(option)"
844
- [attr.aria-selected]="isSelected(option)"
845
- (click)="onOptionClick($event, option)"
846
- (dblclick)="onOptionDoubleClick($event, option)"
847
- (touchend)="onOptionTouchEnd(option)"
848
- (keydown)="onOptionKeyDown($event, option)"
849
- >
850
- <div class="p-checkbox p-component" *ngIf="checkbox && multiple" [ngClass]="{ 'p-checkbox-disabled': disabled || isOptionDisabled(option) }">
851
- <div class="p-checkbox-box" [ngClass]="{ 'p-highlight': isSelected(option) }">
852
- <ng-container *ngIf="isSelected(option)">
853
- <CheckIcon [styleClass]="'p-checkbox-icon'" *ngIf="!checkIconTemplate" />
854
- <span *ngIf="checkIconTemplate" class="p-checkbox-icon">
855
- <ng-template *ngTemplateOutlet="checkIconTemplate"></ng-template>
856
- </span>
857
- </ng-container>
858
- </div>
859
- </div>
860
- <span *ngIf="!itemTemplate">{{ getOptionLabel(option) }}</span>
861
- <ng-container *ngTemplateOutlet="itemTemplate; context: { $implicit: option, index: i }"></ng-container>
1276
+ </p-scroller>
1277
+ <ng-container *ngIf="!virtualScroll">
1278
+ <ng-container *ngTemplateOutlet="buildInItems; context: { $implicit: visibleOptions(), options: {} }"></ng-container>
1279
+ </ng-container>
1280
+
1281
+ <ng-template #buildInItems let-items let-scrollerOptions="options">
1282
+ <ul
1283
+ #list
1284
+ class="p-listbox-list"
1285
+ role="listbox"
1286
+ [tabindex]="-1"
1287
+ [attr.aria-multiselectable]="true"
1288
+ [ngClass]="scrollerOptions.contentStyleClass"
1289
+ [style]="scrollerOptions.contentStyle"
1290
+ [attr.aria-activedescendant]="focused ? focusedOptionId : undefined"
1291
+ [attr.aria-label]="ariaLabel"
1292
+ [attr.aria-multiselectable]="multiple"
1293
+ [attr.aria-disabled]="disabled"
1294
+ (focus)="onListFocus($event)"
1295
+ (blur)="onListBlur($event)"
1296
+ (keydown)="onListKeyDown($event)"
1297
+ >
1298
+ <ng-template ngFor let-option [ngForOf]="items" let-i="index">
1299
+ <ng-container *ngIf="isOptionGroup(option)">
1300
+ <li [attr.id]="id + '_' + getOptionIndex(i, scrollerOptions)" class="p-listbox-item-group" [ngStyle]="{ height: scrollerOptions.itemSize + 'px' }" role="option">
1301
+ <span *ngIf="!groupTemplate">{{ getOptionGroupLabel(option.optionGroup) }}</span>
1302
+ <ng-container *ngTemplateOutlet="groupTemplate; context: { $implicit: option.optionGroup }"></ng-container>
1303
+ </li>
1304
+ </ng-container>
1305
+ <ng-container *ngIf="!isOptionGroup(option)">
1306
+ <li
1307
+ pRipple
1308
+ class="p-listbox-item"
1309
+ role="option"
1310
+ [attr.id]="id + '_' + getOptionIndex(i, scrollerOptions)"
1311
+ [ngStyle]="{ height: scrollerOptions.itemSize + 'px' }"
1312
+ [ngClass]="{ 'p-listbox-item': true, 'p-highlight': isSelected(option), 'p-focus': focusedOptionIndex() === getOptionIndex(i, scrollerOptions), 'p-disabled': isOptionDisabled(option) }"
1313
+ [attr.aria-label]="getOptionLabel(option)"
1314
+ [attr.aria-selected]="isSelected(option)"
1315
+ [attr.aria-disabled]="isOptionDisabled(option)"
1316
+ [attr.aria-setsize]="ariaSetSize"
1317
+ [ariaPosInset]="getAriaPosInset(getOptionIndex(i, scrollerOptions))"
1318
+ (click)="onOptionSelect($event, option, getOptionIndex(i, scrollerOptions))"
1319
+ (dblclick)="onOptionDoubleClick($event, option)"
1320
+ (mousedown)="onOptionMouseDown($event, getOptionIndex(i, scrollerOptions))"
1321
+ (mouseenter)="onOptionMouseEnter($event, getOptionIndex(i, scrollerOptions))"
1322
+ (touchend)="onOptionTouchEnd()"
1323
+ >
1324
+ <div class="p-checkbox p-component" *ngIf="checkbox && multiple" [ngClass]="{ 'p-checkbox-disabled': disabled || isOptionDisabled(option) }">
1325
+ <div class="p-checkbox-box" [ngClass]="{ 'p-highlight': isSelected(option) }">
1326
+ <ng-container *ngIf="isSelected(option)">
1327
+ <CheckIcon [styleClass]="'p-checkbox-icon'" *ngIf="!checkIconTemplate" [attr.aria-hidden]="true" />
1328
+ <span *ngIf="checkIconTemplate" class="p-checkbox-icon" [attr.aria-hidden]="true">
1329
+ <ng-template *ngTemplateOutlet="checkIconTemplate"></ng-template>
1330
+ </span>
1331
+ </ng-container>
1332
+ </div>
1333
+ </div>
1334
+ <span *ngIf="!itemTemplate">{{ getOptionLabel(option) }}</span>
1335
+ <ng-container *ngTemplateOutlet="itemTemplate; context: { $implicit: option, index: getOptionIndex(i, scrollerOptions) }"></ng-container>
1336
+ </li>
1337
+ </ng-container>
1338
+ </ng-template>
1339
+ <li *ngIf="hasFilter() && isEmpty()" class="p-listbox-empty-message" role="option">
1340
+ <ng-container *ngIf="!emptyFilterTemplate && !emptyTemplate; else emptyFilter">
1341
+ {{ emptyFilterMessageText }}
1342
+ </ng-container>
1343
+ <ng-container #emptyFilter *ngTemplateOutlet="emptyFilterTemplate || emptyTemplate"></ng-container>
862
1344
  </li>
863
- </ng-template>
864
- <li *ngIf="hasFilter() && isEmpty()" class="p-listbox-empty-message">
865
- <ng-container *ngIf="!emptyFilterTemplate && !emptyTemplate; else emptyFilter">
866
- {{ emptyFilterMessageLabel }}
867
- </ng-container>
868
- <ng-container #emptyFilter *ngTemplateOutlet="emptyFilterTemplate || emptyTemplate"></ng-container>
869
- </li>
870
- <li *ngIf="!hasFilter() && isEmpty()" class="p-listbox-empty-message">
871
- <ng-container *ngIf="!emptyTemplate; else empty">
872
- {{ emptyMessageLabel }}
873
- </ng-container>
874
- <ng-container #empty *ngTemplateOutlet="emptyTemplate"></ng-container>
875
- </li>
876
- </ul>
1345
+ <li *ngIf="!hasFilter() && isEmpty()" class="p-listbox-empty-message" role="option">
1346
+ <ng-container *ngIf="!emptyTemplate; else empty">
1347
+ {{ emptyMessageText }}
1348
+ </ng-container>
1349
+ <ng-container #empty *ngTemplateOutlet="emptyTemplate"></ng-container>
1350
+ </li>
1351
+ </ul>
1352
+ </ng-template>
877
1353
  </div>
878
1354
  <div class="p-listbox-footer" *ngIf="footerFacet || footerTemplate">
879
1355
  <ng-content select="p-footer"></ng-content>
880
- <ng-container *ngTemplateOutlet="footerTemplate"></ng-container>
1356
+ <ng-container *ngTemplateOutlet="footerTemplate; context: { $implicit: modelValue(), options: visibleOptions() }"></ng-container>
881
1357
  </div>
1358
+ <span *ngIf="isEmpty()" role="status" aria-live="polite" class="p-hidden-accessible">
1359
+ {{ emptyMessageText }}
1360
+ </span>
1361
+ <span role="status" aria-live="polite" class="p-hidden-accessible">
1362
+ {{ selectedMessageText }}
1363
+ </span>
1364
+ <span
1365
+ #lastHiddenFocusableElement
1366
+ role="presentation"
1367
+ [attr.aria-hidden]="true"
1368
+ class="p-hidden-accessible p-hidden-focusable"
1369
+ [tabindex]="!disabled ? tabindex : -1"
1370
+ (focus)="onLastHiddenFocus($event)"
1371
+ [attr.data-p-hidden-focusable]="true"
1372
+ >
1373
+ </span>
882
1374
  </div>
883
1375
  `, providers: [LISTBOX_VALUE_ACCESSOR], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, host: {
884
1376
  class: 'p-element'
885
1377
  }, styles: ["@layer primeng{.p-listbox-list-wrapper{overflow:auto}.p-listbox-list{list-style-type:none;margin:0;padding:0}.p-listbox-item{cursor:pointer;position:relative;overflow:hidden;display:flex;align-items:center;-webkit-user-select:none;user-select:none}.p-listbox-header{display:flex;align-items:center}.p-listbox-filter-container{position:relative;flex:1 1 auto}.p-listbox-filter-icon{position:absolute;top:50%;margin-top:-.5rem}.p-listbox-filter{width:100%}}\n"] }]
886
- }], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: i0.ChangeDetectorRef }, { type: i1.FilterService }, { type: i1.PrimeNGConfig }]; }, propDecorators: { multiple: [{
1378
+ }], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: i0.ChangeDetectorRef }, { type: i1.FilterService }, { type: i1.PrimeNGConfig }, { type: i0.Renderer2 }]; }, propDecorators: { id: [{
1379
+ type: Input
1380
+ }], searchMessage: [{
1381
+ type: Input
1382
+ }], emptySelectionMessage: [{
1383
+ type: Input
1384
+ }], selectionMessage: [{
1385
+ type: Input
1386
+ }], autoOptionFocus: [{
1387
+ type: Input
1388
+ }], selectOnFocus: [{
1389
+ type: Input
1390
+ }], searchLocale: [{
1391
+ type: Input
1392
+ }], focusOnHover: [{
1393
+ type: Input
1394
+ }], filterMessage: [{
1395
+ type: Input
1396
+ }], filterFields: [{
1397
+ type: Input
1398
+ }], lazy: [{
1399
+ type: Input
1400
+ }], virtualScroll: [{
1401
+ type: Input
1402
+ }], virtualScrollItemSize: [{
1403
+ type: Input
1404
+ }], virtualScrollOptions: [{
1405
+ type: Input
1406
+ }], scrollHeight: [{
1407
+ type: Input
1408
+ }], tabindex: [{
1409
+ type: Input
1410
+ }], multiple: [{
887
1411
  type: Input
888
1412
  }], style: [{
889
1413
  type: Input
@@ -943,12 +1467,30 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.2", ngImpor
943
1467
  type: Output
944
1468
  }], onDblClick: [{
945
1469
  type: Output
1470
+ }], onFilter: [{
1471
+ type: Output
1472
+ }], onFocus: [{
1473
+ type: Output
1474
+ }], onBlur: [{
1475
+ type: Output
946
1476
  }], headerCheckboxViewChild: [{
947
1477
  type: ViewChild,
948
1478
  args: ['headerchkbox']
949
1479
  }], filterViewChild: [{
950
1480
  type: ViewChild,
951
1481
  args: ['filter']
1482
+ }], lastHiddenFocusableElement: [{
1483
+ type: ViewChild,
1484
+ args: ['lastHiddenFocusableElement']
1485
+ }], firstHiddenFocusableElement: [{
1486
+ type: ViewChild,
1487
+ args: ['firstHiddenFocusableElement']
1488
+ }], scroller: [{
1489
+ type: ViewChild,
1490
+ args: ['scroller']
1491
+ }], listViewChild: [{
1492
+ type: ViewChild,
1493
+ args: ['list']
952
1494
  }], headerFacet: [{
953
1495
  type: ContentChild,
954
1496
  args: [Header]
@@ -961,14 +1503,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.2", ngImpor
961
1503
  }] } });
962
1504
  class ListboxModule {
963
1505
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.2", ngImport: i0, type: ListboxModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
964
- static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "16.0.2", ngImport: i0, type: ListboxModule, declarations: [Listbox], imports: [CommonModule, SharedModule, RippleModule, SearchIcon, CheckIcon], exports: [Listbox, SharedModule] });
965
- static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "16.0.2", ngImport: i0, type: ListboxModule, imports: [CommonModule, SharedModule, RippleModule, SearchIcon, CheckIcon, SharedModule] });
1506
+ static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "16.0.2", ngImport: i0, type: ListboxModule, declarations: [Listbox], imports: [CommonModule, SharedModule, RippleModule, ScrollerModule, SearchIcon, CheckIcon], exports: [Listbox, SharedModule, ScrollerModule] });
1507
+ static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "16.0.2", ngImport: i0, type: ListboxModule, imports: [CommonModule, SharedModule, RippleModule, ScrollerModule, SearchIcon, CheckIcon, SharedModule, ScrollerModule] });
966
1508
  }
967
1509
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.2", ngImport: i0, type: ListboxModule, decorators: [{
968
1510
  type: NgModule,
969
1511
  args: [{
970
- imports: [CommonModule, SharedModule, RippleModule, SearchIcon, CheckIcon],
971
- exports: [Listbox, SharedModule],
1512
+ imports: [CommonModule, SharedModule, RippleModule, ScrollerModule, SearchIcon, CheckIcon],
1513
+ exports: [Listbox, SharedModule, ScrollerModule],
972
1514
  declarations: [Listbox]
973
1515
  }]
974
1516
  }] });