primeng 16.6.0 → 16.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (179) 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 +711 -587
  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 +998 -455
  41. package/esm2022/multiselect/multiselect.mjs +1024 -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 -33
  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/treetable/treetable.mjs +21 -25
  52. package/esm2022/tristatecheckbox/tristatecheckbox.mjs +53 -35
  53. package/fesm2022/primeng-accordion.mjs +10 -6
  54. package/fesm2022/primeng-accordion.mjs.map +1 -1
  55. package/fesm2022/primeng-animate.mjs +3 -0
  56. package/fesm2022/primeng-animate.mjs.map +1 -1
  57. package/fesm2022/primeng-animateonscroll.mjs +190 -0
  58. package/fesm2022/primeng-animateonscroll.mjs.map +1 -0
  59. package/fesm2022/primeng-api.mjs +1 -0
  60. package/fesm2022/primeng-api.mjs.map +1 -1
  61. package/fesm2022/primeng-autocomplete.mjs +893 -617
  62. package/fesm2022/primeng-autocomplete.mjs.map +1 -1
  63. package/fesm2022/primeng-carousel.mjs +4 -0
  64. package/fesm2022/primeng-carousel.mjs.map +1 -1
  65. package/fesm2022/primeng-cascadeselect.mjs +920 -481
  66. package/fesm2022/primeng-cascadeselect.mjs.map +1 -1
  67. package/fesm2022/primeng-checkbox.mjs +89 -72
  68. package/fesm2022/primeng-checkbox.mjs.map +1 -1
  69. package/fesm2022/primeng-chips.mjs +195 -51
  70. package/fesm2022/primeng-chips.mjs.map +1 -1
  71. package/fesm2022/primeng-colorpicker.mjs +55 -36
  72. package/fesm2022/primeng-colorpicker.mjs.map +1 -1
  73. package/fesm2022/primeng-contextmenu.mjs +9 -0
  74. package/fesm2022/primeng-contextmenu.mjs.map +1 -1
  75. package/fesm2022/primeng-dialog.mjs +5 -10
  76. package/fesm2022/primeng-dialog.mjs.map +1 -1
  77. package/fesm2022/primeng-dom.mjs +25 -7
  78. package/fesm2022/primeng-dom.mjs.map +1 -1
  79. package/fesm2022/primeng-dropdown.mjs +710 -586
  80. package/fesm2022/primeng-dropdown.mjs.map +1 -1
  81. package/fesm2022/primeng-inputmask.mjs +20 -7
  82. package/fesm2022/primeng-inputmask.mjs.map +1 -1
  83. package/fesm2022/primeng-inputnumber.mjs +140 -81
  84. package/fesm2022/primeng-inputnumber.mjs.map +1 -1
  85. package/fesm2022/primeng-inputswitch.mjs +55 -49
  86. package/fesm2022/primeng-inputswitch.mjs.map +1 -1
  87. package/fesm2022/primeng-knob.mjs +92 -5
  88. package/fesm2022/primeng-knob.mjs.map +1 -1
  89. package/fesm2022/primeng-listbox.mjs +997 -454
  90. package/fesm2022/primeng-listbox.mjs.map +1 -1
  91. package/fesm2022/primeng-multiselect.mjs +1023 -603
  92. package/fesm2022/primeng-multiselect.mjs.map +1 -1
  93. package/fesm2022/primeng-overlaypanel.mjs +1 -1
  94. package/fesm2022/primeng-overlaypanel.mjs.map +1 -1
  95. package/fesm2022/primeng-paginator.mjs +1 -1
  96. package/fesm2022/primeng-paginator.mjs.map +1 -1
  97. package/fesm2022/primeng-password.mjs +27 -26
  98. package/fesm2022/primeng-password.mjs.map +1 -1
  99. package/fesm2022/primeng-radiobutton.mjs +46 -33
  100. package/fesm2022/primeng-radiobutton.mjs.map +1 -1
  101. package/fesm2022/primeng-rating.mjs +171 -79
  102. package/fesm2022/primeng-rating.mjs.map +1 -1
  103. package/fesm2022/primeng-selectbutton.mjs +104 -32
  104. package/fesm2022/primeng-selectbutton.mjs.map +1 -1
  105. package/fesm2022/primeng-slider.mjs +150 -65
  106. package/fesm2022/primeng-slider.mjs.map +1 -1
  107. package/fesm2022/primeng-table.mjs +2 -2
  108. package/fesm2022/primeng-table.mjs.map +1 -1
  109. package/fesm2022/primeng-togglebutton.mjs +46 -9
  110. package/fesm2022/primeng-togglebutton.mjs.map +1 -1
  111. package/fesm2022/primeng-treetable.mjs +23 -27
  112. package/fesm2022/primeng-treetable.mjs.map +1 -1
  113. package/fesm2022/primeng-tristatecheckbox.mjs +53 -35
  114. package/fesm2022/primeng-tristatecheckbox.mjs.map +1 -1
  115. package/inputmask/inputmask.d.ts +6 -1
  116. package/inputnumber/inputnumber.d.ts +6 -1
  117. package/inputswitch/inputswitch.d.ts +6 -9
  118. package/knob/knob.d.ts +20 -3
  119. package/listbox/listbox.d.ts +208 -39
  120. package/listbox/listbox.interface.d.ts +16 -1
  121. package/multiselect/multiselect.d.ts +171 -60
  122. package/package.json +138 -132
  123. package/password/password.d.ts +3 -4
  124. package/radiobutton/radiobutton.d.ts +1 -2
  125. package/rating/rating.d.ts +29 -7
  126. package/resources/components/dropdown/dropdown.css +16 -2
  127. package/resources/themes/arya-blue/theme.css +106 -84
  128. package/resources/themes/arya-green/theme.css +106 -84
  129. package/resources/themes/arya-orange/theme.css +106 -84
  130. package/resources/themes/arya-purple/theme.css +106 -84
  131. package/resources/themes/bootstrap4-dark-blue/theme.css +110 -88
  132. package/resources/themes/bootstrap4-dark-purple/theme.css +110 -88
  133. package/resources/themes/bootstrap4-light-blue/theme.css +110 -88
  134. package/resources/themes/bootstrap4-light-purple/theme.css +110 -88
  135. package/resources/themes/fluent-light/theme.css +103 -81
  136. package/resources/themes/lara-dark-blue/theme.css +106 -84
  137. package/resources/themes/lara-dark-indigo/theme.css +106 -84
  138. package/resources/themes/lara-dark-purple/theme.css +106 -84
  139. package/resources/themes/lara-dark-teal/theme.css +106 -84
  140. package/resources/themes/lara-light-blue/theme.css +109 -87
  141. package/resources/themes/lara-light-indigo/theme.css +109 -87
  142. package/resources/themes/lara-light-purple/theme.css +109 -87
  143. package/resources/themes/lara-light-teal/theme.css +109 -87
  144. package/resources/themes/luna-amber/theme.css +110 -88
  145. package/resources/themes/luna-blue/theme.css +110 -88
  146. package/resources/themes/luna-green/theme.css +110 -88
  147. package/resources/themes/luna-pink/theme.css +110 -88
  148. package/resources/themes/md-dark-deeppurple/theme.css +117 -95
  149. package/resources/themes/md-dark-indigo/theme.css +117 -95
  150. package/resources/themes/md-light-deeppurple/theme.css +117 -95
  151. package/resources/themes/md-light-indigo/theme.css +117 -95
  152. package/resources/themes/mdc-dark-deeppurple/theme.css +117 -95
  153. package/resources/themes/mdc-dark-indigo/theme.css +117 -95
  154. package/resources/themes/mdc-light-deeppurple/theme.css +117 -95
  155. package/resources/themes/mdc-light-indigo/theme.css +117 -95
  156. package/resources/themes/mira/theme.css +107 -85
  157. package/resources/themes/nano/theme.css +109 -87
  158. package/resources/themes/nova/theme.css +110 -88
  159. package/resources/themes/nova-accent/theme.css +109 -87
  160. package/resources/themes/nova-alt/theme.css +110 -88
  161. package/resources/themes/rhea/theme.css +109 -87
  162. package/resources/themes/saga-blue/theme.css +106 -84
  163. package/resources/themes/saga-green/theme.css +106 -84
  164. package/resources/themes/saga-orange/theme.css +106 -84
  165. package/resources/themes/saga-purple/theme.css +106 -84
  166. package/resources/themes/soho-dark/theme.css +109 -87
  167. package/resources/themes/soho-light/theme.css +109 -87
  168. package/resources/themes/tailwind-light/theme.css +110 -88
  169. package/resources/themes/vela-blue/theme.css +106 -84
  170. package/resources/themes/vela-green/theme.css +106 -84
  171. package/resources/themes/vela-orange/theme.css +106 -84
  172. package/resources/themes/vela-purple/theme.css +106 -84
  173. package/resources/themes/viva-dark/theme.css +109 -87
  174. package/resources/themes/viva-light/theme.css +109 -87
  175. package/selectbutton/selectbutton.d.ts +15 -3
  176. package/slider/slider.d.ts +12 -6
  177. package/togglebutton/togglebutton.d.ts +7 -1
  178. package/treetable/treetable.d.ts +3 -4
  179. 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
@@ -90,7 +176,7 @@ class Listbox {
90
176
  * Defines how multiple items can be selected, when true metaKey needs to be pressed to select or unselect an item and when set to false selection of each item can be toggled individually. On touch enabled devices, metaKeySelection is turned off automatically.
91
177
  * @group Props
92
178
  */
93
- metaKeySelection = true;
179
+ metaKeySelection = false;
94
180
  /**
95
181
  * A property to uniquely identify a value in options.
96
182
  * @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,583 @@ 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
+ this.onChange.emit({ originalEvent: event, value: this.value });
469
+ }
470
+ removeOption(option) {
471
+ return this.modelValue().filter((val) => !ObjectUtils.equals(val, this.getOptionValue(option), this.equalityKey()));
472
+ }
473
+ onOptionSelect(event, option, index = -1) {
474
+ if (this.disabled || this.isOptionDisabled(option)) {
331
475
  return;
332
476
  }
333
- this.onDblClick.emit({
334
- originalEvent: event,
335
- option: option,
336
- value: this.value
337
- });
477
+ event && this.onClick.emit({ originalEvent: event, value: option });
478
+ this.multiple ? this.onOptionSelectMultiple(event, option) : this.onOptionSelectSingle(event, option);
479
+ this.optionTouched = false;
480
+ index !== -1 && this.focusedOptionIndex.set(index);
338
481
  }
339
- onOptionClickSingle(event, option) {
482
+ onOptionSelectMultiple(event, option) {
340
483
  let selected = this.isSelected(option);
341
- let valueChanged = false;
484
+ let value = null;
342
485
  let metaSelection = this.optionTouched ? false : this.metaKeySelection;
343
486
  if (metaSelection) {
344
487
  let metaKey = event.metaKey || event.ctrlKey;
345
488
  if (selected) {
346
- if (metaKey) {
347
- this.value = null;
348
- valueChanged = true;
349
- }
489
+ value = metaKey ? this.removeOption(option) : [this.getOptionValue(option)];
350
490
  }
351
491
  else {
352
- this.value = this.getOptionValue(option);
353
- valueChanged = true;
492
+ value = metaKey ? this.modelValue() || [] : [];
493
+ value = [...value, this.getOptionValue(option)];
354
494
  }
355
495
  }
356
496
  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
- });
497
+ value = selected ? this.removeOption(option) : [...(this.modelValue() || []), this.getOptionValue(option)];
366
498
  }
499
+ this.updateModel(value, event);
367
500
  }
368
- onOptionClickMultiple(event, option) {
501
+ onOptionSelectSingle(event, option) {
369
502
  let selected = this.isSelected(option);
370
503
  let valueChanged = false;
504
+ let value = null;
371
505
  let metaSelection = this.optionTouched ? false : this.metaKeySelection;
372
506
  if (metaSelection) {
373
507
  let metaKey = event.metaKey || event.ctrlKey;
374
508
  if (selected) {
375
509
  if (metaKey) {
376
- this.removeOption(option);
377
- }
378
- else {
379
- this.value = [this.getOptionValue(option)];
510
+ value = null;
511
+ valueChanged = true;
380
512
  }
381
- valueChanged = true;
382
513
  }
383
514
  else {
384
- this.value = metaKey ? this.value || [] : [];
385
- this.value = [...this.value, this.getOptionValue(option)];
515
+ value = this.getOptionValue(option);
386
516
  valueChanged = true;
387
517
  }
388
518
  }
389
519
  else {
390
- if (selected) {
391
- this.removeOption(option);
392
- }
393
- else {
394
- this.value = [...(this.value || []), this.getOptionValue(option)];
395
- }
520
+ value = selected ? null : this.getOptionValue(option);
396
521
  valueChanged = true;
397
522
  }
398
523
  if (valueChanged) {
399
- this.onModelChange(this.value);
400
- this.onChange.emit({
401
- originalEvent: event,
402
- value: this.value
403
- });
524
+ this.updateModel(value, event);
525
+ }
526
+ }
527
+ onOptionSelectRange(event, start = -1, end = -1) {
528
+ start === -1 && (start = this.findNearestSelectedOptionIndex(end, true));
529
+ end === -1 && (end = this.findNearestSelectedOptionIndex(start));
530
+ if (start !== -1 && end !== -1) {
531
+ const rangeStart = Math.min(start, end);
532
+ const rangeEnd = Math.max(start, end);
533
+ const value = this.visibleOptions()
534
+ .slice(rangeStart, rangeEnd + 1)
535
+ .filter((option) => this.isValidOption(option))
536
+ .map((option) => this.getOptionValue(option));
537
+ this.updateModel(value, event);
404
538
  }
405
539
  }
406
- onOptionClickCheckbox(event, option) {
540
+ onToggleAll(event) {
407
541
  if (this.disabled || this.readonly) {
408
542
  return;
409
543
  }
410
- let selected = this.isSelected(option);
411
- if (selected) {
412
- this.removeOption(option);
544
+ DomHandler.focus(this.headerCheckboxViewChild.nativeElement);
545
+ const value = this.allSelected()
546
+ ? []
547
+ : this.visibleOptions()
548
+ .filter((option) => this.isValidOption(option))
549
+ .map((option) => this.getOptionValue(option));
550
+ this.updateModel(value, event);
551
+ event.preventDefault();
552
+ event.stopPropagation();
553
+ }
554
+ allSelected() {
555
+ const allSelected = this.visibleOptions().length > 0 && this.visibleOptions().every((option) => this.isOptionGroup(option) || this.isOptionDisabled(option) || this.isSelected(option));
556
+ return ObjectUtils.isNotEmpty(this.visibleOptions()) && allSelected;
557
+ }
558
+ onOptionTouchEnd() {
559
+ if (this.disabled) {
560
+ return;
413
561
  }
414
- else {
415
- this.value = this.value ? this.value : [];
416
- this.value = [...this.value, this.getOptionValue(option)];
562
+ this.optionTouched = true;
563
+ }
564
+ onOptionMouseDown(event, index) {
565
+ this.changeFocusedOptionIndex(event, index);
566
+ }
567
+ onOptionMouseEnter(event, index) {
568
+ if (this.focusOnHover) {
569
+ this.changeFocusedOptionIndex(event, index);
417
570
  }
418
- this.onModelChange(this.value);
419
- this.onChange.emit({
571
+ }
572
+ onOptionDoubleClick(event, option) {
573
+ if (this.disabled || this.isOptionDisabled(option) || this.readonly) {
574
+ return;
575
+ }
576
+ this.onDblClick.emit({
420
577
  originalEvent: event,
578
+ option: option,
421
579
  value: this.value
422
580
  });
423
581
  }
424
- removeOption(option) {
425
- this.value = this.value.filter((val) => !ObjectUtils.equals(val, this.getOptionValue(option), this.dataKey));
582
+ onFirstHiddenFocus(event) {
583
+ DomHandler.focus(this.listViewChild.nativeElement);
584
+ const firstFocusableEl = DomHandler.getFirstFocusableElement(this.el.nativeElement, ':not([data-p-hidden-focusable="true"])');
585
+ this.lastHiddenFocusableElement.nativeElement.tabIndex = ObjectUtils.isEmpty(firstFocusableEl) ? '-1' : undefined;
586
+ this.firstHiddenFocusableElement.nativeElement.tabIndex = -1;
426
587
  }
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
- }
588
+ onLastHiddenFocus(event) {
589
+ const relatedTarget = event.relatedTarget;
590
+ if (relatedTarget === this.listViewChild.nativeElement) {
591
+ const firstFocusableEl = DomHandler.getFirstFocusableElement(this.el.nativeElement, ':not(.p-hidden-focusable)');
592
+ DomHandler.focus(firstFocusableEl);
593
+ this.firstHiddenFocusableElement.nativeElement.tabIndex = undefined;
439
594
  }
440
595
  else {
441
- selected = ObjectUtils.equals(this.value, optionValue, this.dataKey);
596
+ DomHandler.focus(this.firstHiddenFocusableElement.nativeElement);
442
597
  }
443
- return selected;
598
+ this.lastHiddenFocusableElement.nativeElement.tabIndex = -1;
444
599
  }
445
- get allChecked() {
446
- let optionsToRender = this.optionsToRender;
447
- if (!optionsToRender || optionsToRender.length === 0) {
448
- return false;
600
+ onFocusout(event) {
601
+ if (!this.el.nativeElement.contains(event.relatedTarget) && this.lastHiddenFocusableElement && this.firstHiddenFocusableElement) {
602
+ this.firstHiddenFocusableElement.nativeElement.tabIndex = this.lastHiddenFocusableElement.nativeElement.tabIndex = undefined;
449
603
  }
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
- }
604
+ }
605
+ onListFocus(event) {
606
+ this.focused = true;
607
+ const focusedOptionIndex = this.focusedOptionIndex() !== -1 ? this.focusedOptionIndex() : this.autoOptionFocus ? this.findFirstFocusedOptionIndex() : -1;
608
+ this.focusedOptionIndex.set(focusedOptionIndex);
609
+ this.onFocus.emit(event);
610
+ }
611
+ onListBlur(event) {
612
+ this.focused = false;
613
+ this.focusedOptionIndex.set(-1);
614
+ this.startRangeIndex.set(-1);
615
+ this.searchValue = '';
616
+ }
617
+ onHeaderCheckboxFocus(event) {
618
+ this.headerCheckboxFocus = true;
619
+ }
620
+ onHeaderCheckboxBlur() {
621
+ this.headerCheckboxFocus = false;
622
+ }
623
+ onHeaderCheckboxKeyDown(event) {
624
+ if (this.disabled) {
625
+ event.preventDefault();
626
+ return;
627
+ }
628
+ switch (event.code) {
629
+ case 'Space':
630
+ this.onToggleAll(event);
631
+ break;
632
+ case 'Enter':
633
+ this.onToggleAll(event);
634
+ break;
635
+ case 'Tab':
636
+ this.onHeaderCheckboxTabKeyDown(event);
637
+ break;
638
+ default:
639
+ break;
640
+ }
641
+ }
642
+ onHeaderCheckboxTabKeyDown(event) {
643
+ DomHandler.focus(this.listViewChild.nativeElement);
644
+ event.preventDefault();
645
+ }
646
+ onFilterChange(event) {
647
+ let value = event.target.value?.trim();
648
+ this._filterValue.set(value);
649
+ this.focusedOptionIndex.set(-1);
650
+ this.startRangeIndex.set(-1);
651
+ this.onFilter.emit({ originalEvent: event, filter: this._filterValue() });
652
+ !this.virtualScrollerDisabled && this.scroller.scrollToIndex(0);
653
+ }
654
+ onFilterBlur(event) {
655
+ this.focusedOptionIndex.set(-1);
656
+ this.startRangeIndex.set(-1);
657
+ }
658
+ onListKeyDown(event) {
659
+ const metaKey = event.metaKey || event.ctrlKey;
660
+ switch (event.code) {
661
+ case 'ArrowDown':
662
+ this.onArrowDownKey(event);
663
+ break;
664
+ case 'ArrowUp':
665
+ this.onArrowUpKey(event);
666
+ break;
667
+ case 'Home':
668
+ this.onHomeKey(event);
669
+ break;
670
+ case 'End':
671
+ this.onEndKey(event);
672
+ break;
673
+ case 'PageDown':
674
+ this.onPageDownKey(event);
675
+ break;
676
+ case 'PageUp':
677
+ this.onPageUpKey(event);
678
+ break;
679
+ case 'Enter':
680
+ case 'Space':
681
+ this.onSpaceKey(event);
682
+ break;
683
+ case 'Tab':
684
+ //NOOP
685
+ break;
686
+ case 'ShiftLeft':
687
+ case 'ShiftRight':
688
+ this.onShiftKey();
689
+ break;
690
+ default:
691
+ if (this.multiple && event.code === 'KeyA' && metaKey) {
692
+ const value = this.visibleOptions()
693
+ .filter((option) => this.isValidOption(option))
694
+ .map((option) => this.getOptionValue(option));
695
+ this.updateModel(value, event);
696
+ event.preventDefault();
697
+ break;
471
698
  }
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
- }
699
+ if (!metaKey && ObjectUtils.isPrintableCharacter(event.key)) {
700
+ this.searchOptions(event, event.key);
701
+ event.preventDefault();
491
702
  }
492
- }
493
- return (visibleOptionsLength === selectedDisabledItemsLength ||
494
- visibleOptionsLength === selectedEnabledItemsLength ||
495
- (selectedEnabledItemsLength && visibleOptionsLength === selectedEnabledItemsLength + unselectedDisabledItemsLength + selectedDisabledItemsLength));
703
+ break;
496
704
  }
497
705
  }
498
- get optionsToRender() {
499
- return this._filteredOptions || this.options;
706
+ onFilterKeyDown(event) {
707
+ switch (event.code) {
708
+ case 'ArrowDown':
709
+ this.onArrowDownKey(event);
710
+ break;
711
+ case 'ArrowUp':
712
+ this.onArrowUpKey(event);
713
+ break;
714
+ case 'ArrowLeft':
715
+ case 'ArrowRight':
716
+ this.onArrowLeftKey(event, true);
717
+ break;
718
+ case 'Home':
719
+ this.onHomeKey(event, true);
720
+ break;
721
+ case 'End':
722
+ this.onEndKey(event, true);
723
+ break;
724
+ case 'Enter':
725
+ this.onEnterKey(event);
726
+ break;
727
+ case 'ShiftLeft':
728
+ case 'ShiftRight':
729
+ this.onShiftKey();
730
+ break;
731
+ default:
732
+ break;
733
+ }
500
734
  }
501
- get emptyMessageLabel() {
502
- return this.emptyMessage || this.config.getTranslation(TranslationKeys.EMPTY_MESSAGE);
735
+ onArrowDownKey(event) {
736
+ const optionIndex = this.focusedOptionIndex() !== -1 ? this.findNextOptionIndex(this.focusedOptionIndex()) : this.findFirstFocusedOptionIndex();
737
+ if (this.multiple && event.shiftKey) {
738
+ this.onOptionSelectRange(event, this.startRangeIndex(), optionIndex);
739
+ }
740
+ this.changeFocusedOptionIndex(event, optionIndex);
741
+ event.preventDefault();
503
742
  }
504
- get emptyFilterMessageLabel() {
505
- return this.emptyFilterMessage || this.config.getTranslation(TranslationKeys.EMPTY_FILTER_MESSAGE);
743
+ onArrowUpKey(event) {
744
+ const optionIndex = this.focusedOptionIndex() !== -1 ? this.findPrevOptionIndex(this.focusedOptionIndex()) : this.findLastFocusedOptionIndex();
745
+ if (this.multiple && event.shiftKey) {
746
+ this.onOptionSelectRange(event, optionIndex, this.startRangeIndex());
747
+ }
748
+ this.changeFocusedOptionIndex(event, optionIndex);
749
+ event.preventDefault();
506
750
  }
507
- hasFilter() {
508
- return this._filterValue && this._filterValue.trim().length > 0;
751
+ onArrowLeftKey(event, pressedInInputText = false) {
752
+ pressedInInputText && this.focusedOptionIndex.set(-1);
509
753
  }
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
- }
754
+ onHomeKey(event, pressedInInputText = false) {
755
+ if (pressedInInputText) {
756
+ event.currentTarget.setSelectionRange(0, 0);
757
+ this.focusedOptionIndex.set(-1);
533
758
  }
534
759
  else {
535
- this._filteredOptions = null;
536
- }
537
- }
538
- resetFilter() {
539
- if (this.filterViewChild && this.filterViewChild.nativeElement) {
540
- this.filterViewChild.nativeElement.value = '';
760
+ let metaKey = event.metaKey || event.ctrlKey;
761
+ let optionIndex = this.findFirstOptionIndex();
762
+ if (this.multiple && event.shiftKey && metaKey) {
763
+ this.onOptionSelectRange(event, optionIndex, this.startRangeIndex());
764
+ }
765
+ this.changeFocusedOptionIndex(event, optionIndex);
541
766
  }
542
- this._filterValue = null;
543
- this._filteredOptions = null;
767
+ event.preventDefault();
544
768
  }
545
- get toggleAllDisabled() {
546
- let optionsToRender = this.optionsToRender;
547
- if (!optionsToRender || optionsToRender.length === 0) {
548
- return true;
769
+ onEndKey(event, pressedInInputText = false) {
770
+ if (pressedInInputText) {
771
+ const target = event.currentTarget;
772
+ const len = target.value.length;
773
+ target.setSelectionRange(len, len);
774
+ this.focusedOptionIndex.set(-1);
549
775
  }
550
776
  else {
551
- for (let option of optionsToRender) {
552
- if (!this.isOptionDisabled(option))
553
- return false;
777
+ let metaKey = event.metaKey || event.ctrlKey;
778
+ let optionIndex = this.findLastOptionIndex();
779
+ if (this.multiple && event.shiftKey && metaKey) {
780
+ this.onOptionSelectRange(event, this.startRangeIndex(), optionIndex);
554
781
  }
555
- return true;
782
+ this.changeFocusedOptionIndex(event, optionIndex);
556
783
  }
784
+ event.preventDefault();
557
785
  }
558
- toggleAll(event) {
559
- if (this.disabled || this.toggleAllDisabled || this.readonly) {
560
- return;
786
+ onPageDownKey(event) {
787
+ this.scrollInView(0);
788
+ event.preventDefault();
789
+ }
790
+ onPageUpKey(event) {
791
+ this.scrollInView(this.visibleOptions().length - 1);
792
+ event.preventDefault();
793
+ }
794
+ onEnterKey(event) {
795
+ if (this.focusedOptionIndex() !== -1) {
796
+ if (this.multiple && event.shiftKey)
797
+ this.onOptionSelectRange(event, this.focusedOptionIndex());
798
+ else
799
+ this.onOptionSelect(event, this.visibleOptions()[this.focusedOptionIndex()]);
561
800
  }
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
801
  event.preventDefault();
570
802
  }
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
- }
803
+ onSpaceKey(event) {
804
+ this.onEnterKey(event);
805
+ }
806
+ onShiftKey() {
807
+ const focusedOptionIndex = this.focusedOptionIndex();
808
+ this.startRangeIndex.set(focusedOptionIndex);
809
+ }
810
+ getOptionGroupChildren(optionGroup) {
811
+ return this.optionGroupChildren ? ObjectUtils.resolveFieldData(optionGroup, this.optionGroupChildren) : optionGroup.items;
812
+ }
813
+ getOptionGroupLabel(optionGroup) {
814
+ return this.optionGroupLabel ? ObjectUtils.resolveFieldData(optionGroup, this.optionGroupLabel) : optionGroup && optionGroup.label !== undefined ? optionGroup.label : optionGroup;
815
+ }
816
+ getOptionLabel(option) {
817
+ return this.optionLabel ? ObjectUtils.resolveFieldData(option, this.optionLabel) : option.label != undefined ? option.label : option;
818
+ }
819
+ getOptionIndex(index, scrollerOptions) {
820
+ return this.virtualScrollerDisabled ? index : scrollerOptions && scrollerOptions.getItemOptions(index)['index'];
821
+ }
822
+ getOptionValue(option) {
823
+ return this.optionValue ? ObjectUtils.resolveFieldData(option, this.optionValue) : !this.optionLabel && option && option.value !== undefined ? option.value : option;
824
+ }
825
+ getAriaPosInset(index) {
826
+ return ((this.optionGroupLabel
827
+ ? index -
828
+ this.visibleOptions()
829
+ .slice(0, index)
830
+ .filter((option) => this.isOptionGroup(option)).length
831
+ : index) + 1);
832
+ }
833
+ hasSelectedOption() {
834
+ return ObjectUtils.isNotEmpty(this.modelValue());
835
+ }
836
+ isOptionGroup(option) {
837
+ return this.optionGroupLabel && option.optionGroup && option.group;
838
+ }
839
+ changeFocusedOptionIndex(event, index) {
840
+ if (this.focusedOptionIndex() !== index) {
841
+ this.focusedOptionIndex.set(index);
842
+ this.scrollInView();
843
+ if (this.selectOnFocus && !this.multiple) {
844
+ this.onOptionSelect(event, this.visibleOptions()[index]);
591
845
  }
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
- }
846
+ }
847
+ }
848
+ searchOptions(event, char) {
849
+ this.searchValue = (this.searchValue || '') + char;
850
+ let optionIndex = -1;
851
+ let matched = false;
852
+ if (this.focusedOptionIndex() !== -1) {
853
+ optionIndex = this.visibleOptions()
854
+ .slice(this.focusedOptionIndex())
855
+ .findIndex((option) => this.isOptionMatched(option));
856
+ optionIndex =
857
+ optionIndex === -1
858
+ ? this.visibleOptions()
859
+ .slice(0, this.focusedOptionIndex())
860
+ .findIndex((option) => this.isOptionMatched(option))
861
+ : optionIndex + this.focusedOptionIndex();
862
+ }
863
+ else {
864
+ optionIndex = this.visibleOptions().findIndex((option) => this.isOptionMatched(option));
865
+ }
866
+ if (optionIndex !== -1) {
867
+ matched = true;
868
+ }
869
+ if (optionIndex === -1 && this.focusedOptionIndex() === -1) {
870
+ optionIndex = this.findFirstFocusedOptionIndex();
871
+ }
872
+ if (optionIndex !== -1) {
873
+ this.changeFocusedOptionIndex(event, optionIndex);
874
+ }
875
+ if (this.searchTimeout) {
876
+ clearTimeout(this.searchTimeout);
877
+ }
878
+ this.searchTimeout = setTimeout(() => {
879
+ this.searchValue = '';
880
+ this.searchTimeout = null;
881
+ }, 500);
882
+ return matched;
883
+ }
884
+ isOptionMatched(option) {
885
+ return this.isValidOption(option) && this.getOptionLabel(option).toLocaleLowerCase(this.filterLocale).startsWith(this.searchValue.toLocaleLowerCase(this.filterLocale));
886
+ }
887
+ scrollInView(index = -1) {
888
+ const id = index !== -1 ? `${this.id}_${index}` : this.focusedOptionId;
889
+ const element = DomHandler.findSingle(this.listViewChild.nativeElement, `li[id="${id}"]`);
890
+ if (element) {
891
+ element.scrollIntoView && element.scrollIntoView({ block: 'nearest', inline: 'nearest' });
892
+ }
893
+ else if (!this.virtualScrollerDisabled) {
894
+ this.virtualScroll && this.scroller.scrollToIndex(index !== -1 ? index : this.focusedOptionIndex());
895
+ }
896
+ }
897
+ findFirstOptionIndex() {
898
+ return this.visibleOptions().findIndex((option) => this.isValidOption(option));
899
+ }
900
+ findLastOptionIndex() {
901
+ return ObjectUtils.findLastIndex(this.visibleOptions(), (option) => this.isValidOption(option));
902
+ }
903
+ findFirstFocusedOptionIndex() {
904
+ const selectedIndex = this.findFirstSelectedOptionIndex();
905
+ return selectedIndex < 0 ? this.findFirstOptionIndex() : selectedIndex;
906
+ }
907
+ findLastFocusedOptionIndex() {
908
+ const selectedIndex = this.findLastSelectedOptionIndex();
909
+ return selectedIndex < 0 ? this.findLastOptionIndex() : selectedIndex;
910
+ }
911
+ findLastSelectedOptionIndex() {
912
+ return this.hasSelectedOption() ? ObjectUtils.findLastIndex(this.visibleOptions(), (option) => this.isValidSelectedOption(option)) : -1;
913
+ }
914
+ findNextOptionIndex(index) {
915
+ const matchedOptionIndex = index < this.visibleOptions().length - 1
916
+ ? this.visibleOptions()
917
+ .slice(index + 1)
918
+ .findIndex((option) => this.isValidOption(option))
919
+ : -1;
920
+ return matchedOptionIndex > -1 ? matchedOptionIndex + index + 1 : index;
921
+ }
922
+ findNextSelectedOptionIndex(index) {
923
+ const matchedOptionIndex = this.hasSelectedOption() && index < this.visibleOptions().length - 1
924
+ ? this.visibleOptions()
925
+ .slice(index + 1)
926
+ .findIndex((option) => this.isValidSelectedOption(option))
927
+ : -1;
928
+ return matchedOptionIndex > -1 ? matchedOptionIndex + index + 1 : -1;
929
+ }
930
+ findPrevSelectedOptionIndex(index) {
931
+ const matchedOptionIndex = this.hasSelectedOption() && index > 0 ? ObjectUtils.findLastIndex(this.visibleOptions().slice(0, index), (option) => this.isValidSelectedOption(option)) : -1;
932
+ return matchedOptionIndex > -1 ? matchedOptionIndex : -1;
933
+ }
934
+ findFirstSelectedOptionIndex() {
935
+ return this.hasSelectedOption() ? this.visibleOptions().findIndex((option) => this.isValidSelectedOption(option)) : -1;
936
+ }
937
+ findPrevOptionIndex(index) {
938
+ const matchedOptionIndex = index > 0 ? ObjectUtils.findLastIndex(this.visibleOptions().slice(0, index), (option) => this.isValidOption(option)) : -1;
939
+ return matchedOptionIndex > -1 ? matchedOptionIndex : index;
940
+ }
941
+ findNearestSelectedOptionIndex(index, firstCheckUp = false) {
942
+ let matchedOptionIndex = -1;
943
+ if (this.hasSelectedOption()) {
944
+ if (firstCheckUp) {
945
+ matchedOptionIndex = this.findPrevSelectedOptionIndex(index);
946
+ matchedOptionIndex = matchedOptionIndex === -1 ? this.findNextSelectedOptionIndex(index) : matchedOptionIndex;
604
947
  }
605
948
  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
- }
949
+ matchedOptionIndex = this.findNextSelectedOptionIndex(index);
950
+ matchedOptionIndex = matchedOptionIndex === -1 ? this.findPrevSelectedOptionIndex(index) : matchedOptionIndex;
614
951
  }
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
952
  }
953
+ return matchedOptionIndex > -1 ? matchedOptionIndex : index;
646
954
  }
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;
955
+ equalityKey() {
956
+ return this.optionValue ? null : this.dataKey;
957
+ }
958
+ isValidSelectedOption(option) {
959
+ return this.isValidOption(option) && this.isSelected(option);
960
+ }
961
+ isOptionDisabled(option) {
962
+ return this.optionDisabled ? ObjectUtils.resolveFieldData(option, this.optionDisabled) : false;
653
963
  }
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;
964
+ isSelected(option) {
965
+ const optionValue = this.getOptionValue(option);
966
+ if (this.multiple)
967
+ return (this.modelValue() || []).some((value) => ObjectUtils.equals(value, optionValue, this.equalityKey()));
658
968
  else
659
- return null;
969
+ return ObjectUtils.equals(this.modelValue(), optionValue, this.equalityKey());
660
970
  }
661
- onHeaderCheckboxFocus() {
662
- this.headerCheckboxFocus = true;
971
+ isValidOption(option) {
972
+ return option && !(this.isOptionDisabled(option) || this.isOptionGroup(option));
663
973
  }
664
- onHeaderCheckboxBlur() {
665
- this.headerCheckboxFocus = false;
974
+ isEmpty() {
975
+ return !this._options() || (this._options() && this._options().length === 0);
976
+ }
977
+ hasFilter() {
978
+ return this._filterValue() && this._filterValue().trim().length > 0;
979
+ }
980
+ resetFilter() {
981
+ if (this.filterViewChild && this.filterViewChild.nativeElement) {
982
+ this.filterViewChild.nativeElement.value = '';
983
+ }
984
+ this._filterValue.set(null);
666
985
  }
667
986
  ngOnDestroy() {
668
987
  if (this.translationSubscription) {
669
988
  this.translationSubscription.unsubscribe();
670
989
  }
671
990
  }
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">
991
+ 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 });
992
+ 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: `
993
+ <div [attr.id]="id" [ngClass]="containerClass" [ngStyle]="style" [class]="styleClass" (focusout)="onFocusout($event)">
994
+ <span
995
+ #firstHiddenFocusableElement
996
+ role="presentation"
997
+ [attr.aria-hidden]="true"
998
+ class="p-hidden-accessible p-hidden-focusable"
999
+ [tabindex]="!disabled ? tabindex : -1"
1000
+ (focus)="onFirstHiddenFocus($event)"
1001
+ [attr.data-p-hidden-focusable]="true"
1002
+ >
1003
+ </span>
675
1004
  <div class="p-listbox-header" *ngIf="headerFacet || headerTemplate">
676
1005
  <ng-content select="p-header"></ng-content>
677
- <ng-container *ngTemplateOutlet="headerTemplate"></ng-container>
1006
+ <ng-container *ngTemplateOutlet="headerTemplate; context: { $implicit: modelValue(), options: visibleOptions() }"></ng-container>
678
1007
  </div>
679
1008
  <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" />
1009
+ <div *ngIf="checkbox && multiple && showToggleAll" class="p-checkbox p-component" [ngClass]="{ 'p-checkbox-disabled': disabled || toggleAllDisabled }" (click)="onToggleAll($event)" (keydown)="onHeaderCheckboxKeyDown($event)">
1010
+ <div class="p-hidden-accessible" [attr.data-p-hidden-accessible]="true">
1011
+ <input
1012
+ #headerchkbox
1013
+ type="checkbox"
1014
+ readonly="readonly"
1015
+ [attr.checked]="allSelected()"
1016
+ [disabled]="disabled || toggleAllDisabled"
1017
+ (focus)="onHeaderCheckboxFocus($event)"
1018
+ (blur)="onHeaderCheckboxBlur($event)"
1019
+ [attr.aria-label]="toggleAllAriaLabel"
1020
+ />
683
1021
  </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">
1022
+ <div class="p-checkbox-box" role="checkbox" [attr.aria-checked]="allSelected()" [ngClass]="{ 'p-highlight': allSelected(), 'p-focus': headerCheckboxFocus, 'p-disabled': disabled || toggleAllDisabled }">
1023
+ <ng-container *ngIf="allSelected()">
1024
+ <CheckIcon [styleClass]="'p-checkbox-icon'" *ngIf="!checkIconTemplate" [attr.aria-hidden]="true" />
1025
+ <span *ngIf="checkIconTemplate" class="p-checkbox-icon" [attr.aria-hidden]="true">
688
1026
  <ng-template *ngTemplateOutlet="checkIconTemplate"></ng-template>
689
1027
  </span>
690
1028
  </ng-container>
@@ -696,102 +1034,189 @@ class Listbox {
696
1034
  <ng-template #builtInFilterElement>
697
1035
  <div class="p-listbox-filter-container" *ngIf="filter">
698
1036
  <input
699
- #filter
1037
+ #filterInput
700
1038
  type="text"
701
- [value]="filterValue || ''"
702
- (input)="onFilter($event)"
703
1039
  class="p-listbox-filter p-inputtext p-component"
1040
+ role="searchbox"
1041
+ [value]="_filterValue() || ''"
704
1042
  [disabled]="disabled"
1043
+ [attr.aria-owns]="id + '_list'"
1044
+ [attr.aria-activedescendant]="focusedOptionId"
705
1045
  [attr.placeholder]="filterPlaceHolder"
706
1046
  [attr.aria-label]="ariaFilterLabel"
1047
+ [tabindex]="!disabled && !focused ? tabindex : -1"
1048
+ (input)="onFilterChange($event)"
1049
+ (keydown)="onFilterKeyDown($event)"
1050
+ (blur)="onFilterBlur($event)"
707
1051
  />
708
- <SearchIcon *ngIf="!filterIconTemplate" [styleClass]="'p-listbox-filter-icon'" />
709
- <span *ngIf="filterIconTemplate" class="p-listbox-filter-icon">
1052
+ <SearchIcon *ngIf="!filterIconTemplate" [styleClass]="'p-listbox-filter-icon'" [attr.aria-hidden]="true" />
1053
+ <span *ngIf="filterIconTemplate" class="p-listbox-filter-icon" [attr.aria-hidden]="true">
710
1054
  <ng-template *ngTemplateOutlet="filterIconTemplate"></ng-template>
711
1055
  </span>
712
1056
  </div>
1057
+ <span role="status" attr.aria-live="polite" class="p-hidden-accessible" [attr.data-p-hidden-accessible]="true">
1058
+ {{ filterResultMessageText }}
1059
+ </span>
713
1060
  </ng-template>
714
1061
  </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>
1062
+ <div [ngClass]="'p-listbox-list-wrapper'" [ngStyle]="listStyle" [class]="listStyleClass" [style.max-height]="virtualScroll ? 'auto' : scrollHeight || 'auto'">
1063
+ <p-scroller
1064
+ #scroller
1065
+ *ngIf="virtualScroll"
1066
+ [items]="visibleOptions()"
1067
+ [style]="{ height: scrollHeight }"
1068
+ [itemSize]="virtualScrollItemSize"
1069
+ [autoSize]="true"
1070
+ [tabindex]="-1"
1071
+ [lazy]="lazy"
1072
+ [options]="virtualScrollOptions"
1073
+ (onLazyLoad)="onLazyLoad.emit($event)"
1074
+ >
1075
+ <ng-template pTemplate="content" let-items let-scrollerOptions="options">
1076
+ <ng-container *ngTemplateOutlet="buildInItems; context: { $implicit: items, options: scrollerOptions }"></ng-container>
1077
+ </ng-template>
1078
+ <ng-container *ngIf="loaderTemplate">
1079
+ <ng-template pTemplate="loader" let-scrollerOptions="options">
1080
+ <ng-container *ngTemplateOutlet="loaderTemplate; context: { options: scrollerOptions }"></ng-container>
724
1081
  </ng-template>
725
1082
  </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>
1083
+ </p-scroller>
1084
+ <ng-container *ngIf="!virtualScroll">
1085
+ <ng-container *ngTemplateOutlet="buildInItems; context: { $implicit: visibleOptions(), options: {} }"></ng-container>
1086
+ </ng-container>
1087
+
1088
+ <ng-template #buildInItems let-items let-scrollerOptions="options">
1089
+ <ul
1090
+ #list
1091
+ class="p-listbox-list"
1092
+ role="listbox"
1093
+ [tabindex]="-1"
1094
+ [attr.aria-multiselectable]="true"
1095
+ [ngClass]="scrollerOptions.contentStyleClass"
1096
+ [style]="scrollerOptions.contentStyle"
1097
+ [attr.aria-activedescendant]="focused ? focusedOptionId : undefined"
1098
+ [attr.aria-label]="ariaLabel"
1099
+ [attr.aria-multiselectable]="multiple"
1100
+ [attr.aria-disabled]="disabled"
1101
+ (focus)="onListFocus($event)"
1102
+ (blur)="onListBlur($event)"
1103
+ (keydown)="onListKeyDown($event)"
1104
+ >
1105
+ <ng-template ngFor let-option [ngForOf]="items" let-i="index">
1106
+ <ng-container *ngIf="isOptionGroup(option)">
1107
+ <li [attr.id]="id + '_' + getOptionIndex(i, scrollerOptions)" class="p-listbox-item-group" [ngStyle]="{ height: scrollerOptions.itemSize + 'px' }" role="option">
1108
+ <span *ngIf="!groupTemplate">{{ getOptionGroupLabel(option.optionGroup) }}</span>
1109
+ <ng-container *ngTemplateOutlet="groupTemplate; context: { $implicit: option.optionGroup }"></ng-container>
1110
+ </li>
1111
+ </ng-container>
1112
+ <ng-container *ngIf="!isOptionGroup(option)">
1113
+ <li
1114
+ pRipple
1115
+ class="p-listbox-item"
1116
+ role="option"
1117
+ [attr.id]="id + '_' + getOptionIndex(i, scrollerOptions)"
1118
+ [ngStyle]="{ height: scrollerOptions.itemSize + 'px' }"
1119
+ [ngClass]="{ 'p-listbox-item': true, 'p-highlight': isSelected(option), 'p-focus': focusedOptionIndex() === getOptionIndex(i, scrollerOptions), 'p-disabled': isOptionDisabled(option) }"
1120
+ [attr.aria-label]="getOptionLabel(option)"
1121
+ [attr.aria-selected]="isSelected(option)"
1122
+ [attr.aria-disabled]="isOptionDisabled(option)"
1123
+ [attr.aria-setsize]="ariaSetSize"
1124
+ [ariaPosInset]="getAriaPosInset(getOptionIndex(i, scrollerOptions))"
1125
+ (click)="onOptionSelect($event, option, getOptionIndex(i, scrollerOptions))"
1126
+ (dblclick)="onOptionDoubleClick($event, option)"
1127
+ (mousedown)="onOptionMouseDown($event, getOptionIndex(i, scrollerOptions))"
1128
+ (mouseenter)="onOptionMouseEnter($event, getOptionIndex(i, scrollerOptions))"
1129
+ (touchend)="onOptionTouchEnd()"
1130
+ >
1131
+ <div class="p-checkbox p-component" *ngIf="checkbox && multiple" [ngClass]="{ 'p-checkbox-disabled': disabled || isOptionDisabled(option) }">
1132
+ <div class="p-checkbox-box" [ngClass]="{ 'p-highlight': isSelected(option) }">
1133
+ <ng-container *ngIf="isSelected(option)">
1134
+ <CheckIcon [styleClass]="'p-checkbox-icon'" *ngIf="!checkIconTemplate" [attr.aria-hidden]="true" />
1135
+ <span *ngIf="checkIconTemplate" class="p-checkbox-icon" [attr.aria-hidden]="true">
1136
+ <ng-template *ngTemplateOutlet="checkIconTemplate"></ng-template>
1137
+ </span>
1138
+ </ng-container>
1139
+ </div>
1140
+ </div>
1141
+ <span *ngIf="!itemTemplate">{{ getOptionLabel(option) }}</span>
1142
+ <ng-container *ngTemplateOutlet="itemTemplate; context: { $implicit: option, index: getOptionIndex(i, scrollerOptions) }"></ng-container>
1143
+ </li>
1144
+ </ng-container>
1145
+ </ng-template>
1146
+ <li *ngIf="hasFilter() && isEmpty()" class="p-listbox-empty-message" role="option">
1147
+ <ng-container *ngIf="!emptyFilterTemplate && !emptyTemplate; else emptyFilter">
1148
+ {{ emptyFilterMessageText }}
1149
+ </ng-container>
1150
+ <ng-container #emptyFilter *ngTemplateOutlet="emptyFilterTemplate || emptyTemplate"></ng-container>
755
1151
  </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>
1152
+ <li *ngIf="!hasFilter() && isEmpty()" class="p-listbox-empty-message" role="option">
1153
+ <ng-container *ngIf="!emptyTemplate; else empty">
1154
+ {{ emptyMessageText }}
1155
+ </ng-container>
1156
+ <ng-container #empty *ngTemplateOutlet="emptyTemplate"></ng-container>
1157
+ </li>
1158
+ </ul>
1159
+ </ng-template>
770
1160
  </div>
771
1161
  <div class="p-listbox-footer" *ngIf="footerFacet || footerTemplate">
772
1162
  <ng-content select="p-footer"></ng-content>
773
- <ng-container *ngTemplateOutlet="footerTemplate"></ng-container>
1163
+ <ng-container *ngTemplateOutlet="footerTemplate; context: { $implicit: modelValue(), options: visibleOptions() }"></ng-container>
774
1164
  </div>
1165
+ <span *ngIf="isEmpty()" role="status" aria-live="polite" class="p-hidden-accessible">
1166
+ {{ emptyMessageText }}
1167
+ </span>
1168
+ <span role="status" aria-live="polite" class="p-hidden-accessible">
1169
+ {{ selectedMessageText }}
1170
+ </span>
1171
+ <span
1172
+ #lastHiddenFocusableElement
1173
+ role="presentation"
1174
+ [attr.aria-hidden]="true"
1175
+ class="p-hidden-accessible p-hidden-focusable"
1176
+ [tabindex]="!disabled ? tabindex : -1"
1177
+ (focus)="onLastHiddenFocus($event)"
1178
+ [attr.data-p-hidden-focusable]="true"
1179
+ >
1180
+ </span>
775
1181
  </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 });
1182
+ `, 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
1183
  }
778
1184
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.2", ngImport: i0, type: Listbox, decorators: [{
779
1185
  type: Component,
780
1186
  args: [{ selector: 'p-listbox', template: `
781
- <div [ngClass]="{ 'p-listbox p-component': true, 'p-disabled': disabled }" [ngStyle]="style" [class]="styleClass">
1187
+ <div [attr.id]="id" [ngClass]="containerClass" [ngStyle]="style" [class]="styleClass" (focusout)="onFocusout($event)">
1188
+ <span
1189
+ #firstHiddenFocusableElement
1190
+ role="presentation"
1191
+ [attr.aria-hidden]="true"
1192
+ class="p-hidden-accessible p-hidden-focusable"
1193
+ [tabindex]="!disabled ? tabindex : -1"
1194
+ (focus)="onFirstHiddenFocus($event)"
1195
+ [attr.data-p-hidden-focusable]="true"
1196
+ >
1197
+ </span>
782
1198
  <div class="p-listbox-header" *ngIf="headerFacet || headerTemplate">
783
1199
  <ng-content select="p-header"></ng-content>
784
- <ng-container *ngTemplateOutlet="headerTemplate"></ng-container>
1200
+ <ng-container *ngTemplateOutlet="headerTemplate; context: { $implicit: modelValue(), options: visibleOptions() }"></ng-container>
785
1201
  </div>
786
1202
  <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" />
1203
+ <div *ngIf="checkbox && multiple && showToggleAll" class="p-checkbox p-component" [ngClass]="{ 'p-checkbox-disabled': disabled || toggleAllDisabled }" (click)="onToggleAll($event)" (keydown)="onHeaderCheckboxKeyDown($event)">
1204
+ <div class="p-hidden-accessible" [attr.data-p-hidden-accessible]="true">
1205
+ <input
1206
+ #headerchkbox
1207
+ type="checkbox"
1208
+ readonly="readonly"
1209
+ [attr.checked]="allSelected()"
1210
+ [disabled]="disabled || toggleAllDisabled"
1211
+ (focus)="onHeaderCheckboxFocus($event)"
1212
+ (blur)="onHeaderCheckboxBlur($event)"
1213
+ [attr.aria-label]="toggleAllAriaLabel"
1214
+ />
790
1215
  </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">
1216
+ <div class="p-checkbox-box" role="checkbox" [attr.aria-checked]="allSelected()" [ngClass]="{ 'p-highlight': allSelected(), 'p-focus': headerCheckboxFocus, 'p-disabled': disabled || toggleAllDisabled }">
1217
+ <ng-container *ngIf="allSelected()">
1218
+ <CheckIcon [styleClass]="'p-checkbox-icon'" *ngIf="!checkIconTemplate" [attr.aria-hidden]="true" />
1219
+ <span *ngIf="checkIconTemplate" class="p-checkbox-icon" [attr.aria-hidden]="true">
795
1220
  <ng-template *ngTemplateOutlet="checkIconTemplate"></ng-template>
796
1221
  </span>
797
1222
  </ng-container>
@@ -803,87 +1228,187 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.2", ngImpor
803
1228
  <ng-template #builtInFilterElement>
804
1229
  <div class="p-listbox-filter-container" *ngIf="filter">
805
1230
  <input
806
- #filter
1231
+ #filterInput
807
1232
  type="text"
808
- [value]="filterValue || ''"
809
- (input)="onFilter($event)"
810
1233
  class="p-listbox-filter p-inputtext p-component"
1234
+ role="searchbox"
1235
+ [value]="_filterValue() || ''"
811
1236
  [disabled]="disabled"
1237
+ [attr.aria-owns]="id + '_list'"
1238
+ [attr.aria-activedescendant]="focusedOptionId"
812
1239
  [attr.placeholder]="filterPlaceHolder"
813
1240
  [attr.aria-label]="ariaFilterLabel"
1241
+ [tabindex]="!disabled && !focused ? tabindex : -1"
1242
+ (input)="onFilterChange($event)"
1243
+ (keydown)="onFilterKeyDown($event)"
1244
+ (blur)="onFilterBlur($event)"
814
1245
  />
815
- <SearchIcon *ngIf="!filterIconTemplate" [styleClass]="'p-listbox-filter-icon'" />
816
- <span *ngIf="filterIconTemplate" class="p-listbox-filter-icon">
1246
+ <SearchIcon *ngIf="!filterIconTemplate" [styleClass]="'p-listbox-filter-icon'" [attr.aria-hidden]="true" />
1247
+ <span *ngIf="filterIconTemplate" class="p-listbox-filter-icon" [attr.aria-hidden]="true">
817
1248
  <ng-template *ngTemplateOutlet="filterIconTemplate"></ng-template>
818
1249
  </span>
819
1250
  </div>
1251
+ <span role="status" attr.aria-live="polite" class="p-hidden-accessible" [attr.data-p-hidden-accessible]="true">
1252
+ {{ filterResultMessageText }}
1253
+ </span>
820
1254
  </ng-template>
821
1255
  </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>
1256
+ <div [ngClass]="'p-listbox-list-wrapper'" [ngStyle]="listStyle" [class]="listStyleClass" [style.max-height]="virtualScroll ? 'auto' : scrollHeight || 'auto'">
1257
+ <p-scroller
1258
+ #scroller
1259
+ *ngIf="virtualScroll"
1260
+ [items]="visibleOptions()"
1261
+ [style]="{ height: scrollHeight }"
1262
+ [itemSize]="virtualScrollItemSize"
1263
+ [autoSize]="true"
1264
+ [tabindex]="-1"
1265
+ [lazy]="lazy"
1266
+ [options]="virtualScrollOptions"
1267
+ (onLazyLoad)="onLazyLoad.emit($event)"
1268
+ >
1269
+ <ng-template pTemplate="content" let-items let-scrollerOptions="options">
1270
+ <ng-container *ngTemplateOutlet="buildInItems; context: { $implicit: items, options: scrollerOptions }"></ng-container>
1271
+ </ng-template>
1272
+ <ng-container *ngIf="loaderTemplate">
1273
+ <ng-template pTemplate="loader" let-scrollerOptions="options">
1274
+ <ng-container *ngTemplateOutlet="loaderTemplate; context: { options: scrollerOptions }"></ng-container>
831
1275
  </ng-template>
832
1276
  </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>
1277
+ </p-scroller>
1278
+ <ng-container *ngIf="!virtualScroll">
1279
+ <ng-container *ngTemplateOutlet="buildInItems; context: { $implicit: visibleOptions(), options: {} }"></ng-container>
1280
+ </ng-container>
1281
+
1282
+ <ng-template #buildInItems let-items let-scrollerOptions="options">
1283
+ <ul
1284
+ #list
1285
+ class="p-listbox-list"
1286
+ role="listbox"
1287
+ [tabindex]="-1"
1288
+ [attr.aria-multiselectable]="true"
1289
+ [ngClass]="scrollerOptions.contentStyleClass"
1290
+ [style]="scrollerOptions.contentStyle"
1291
+ [attr.aria-activedescendant]="focused ? focusedOptionId : undefined"
1292
+ [attr.aria-label]="ariaLabel"
1293
+ [attr.aria-multiselectable]="multiple"
1294
+ [attr.aria-disabled]="disabled"
1295
+ (focus)="onListFocus($event)"
1296
+ (blur)="onListBlur($event)"
1297
+ (keydown)="onListKeyDown($event)"
1298
+ >
1299
+ <ng-template ngFor let-option [ngForOf]="items" let-i="index">
1300
+ <ng-container *ngIf="isOptionGroup(option)">
1301
+ <li [attr.id]="id + '_' + getOptionIndex(i, scrollerOptions)" class="p-listbox-item-group" [ngStyle]="{ height: scrollerOptions.itemSize + 'px' }" role="option">
1302
+ <span *ngIf="!groupTemplate">{{ getOptionGroupLabel(option.optionGroup) }}</span>
1303
+ <ng-container *ngTemplateOutlet="groupTemplate; context: { $implicit: option.optionGroup }"></ng-container>
1304
+ </li>
1305
+ </ng-container>
1306
+ <ng-container *ngIf="!isOptionGroup(option)">
1307
+ <li
1308
+ pRipple
1309
+ class="p-listbox-item"
1310
+ role="option"
1311
+ [attr.id]="id + '_' + getOptionIndex(i, scrollerOptions)"
1312
+ [ngStyle]="{ height: scrollerOptions.itemSize + 'px' }"
1313
+ [ngClass]="{ 'p-listbox-item': true, 'p-highlight': isSelected(option), 'p-focus': focusedOptionIndex() === getOptionIndex(i, scrollerOptions), 'p-disabled': isOptionDisabled(option) }"
1314
+ [attr.aria-label]="getOptionLabel(option)"
1315
+ [attr.aria-selected]="isSelected(option)"
1316
+ [attr.aria-disabled]="isOptionDisabled(option)"
1317
+ [attr.aria-setsize]="ariaSetSize"
1318
+ [ariaPosInset]="getAriaPosInset(getOptionIndex(i, scrollerOptions))"
1319
+ (click)="onOptionSelect($event, option, getOptionIndex(i, scrollerOptions))"
1320
+ (dblclick)="onOptionDoubleClick($event, option)"
1321
+ (mousedown)="onOptionMouseDown($event, getOptionIndex(i, scrollerOptions))"
1322
+ (mouseenter)="onOptionMouseEnter($event, getOptionIndex(i, scrollerOptions))"
1323
+ (touchend)="onOptionTouchEnd()"
1324
+ >
1325
+ <div class="p-checkbox p-component" *ngIf="checkbox && multiple" [ngClass]="{ 'p-checkbox-disabled': disabled || isOptionDisabled(option) }">
1326
+ <div class="p-checkbox-box" [ngClass]="{ 'p-highlight': isSelected(option) }">
1327
+ <ng-container *ngIf="isSelected(option)">
1328
+ <CheckIcon [styleClass]="'p-checkbox-icon'" *ngIf="!checkIconTemplate" [attr.aria-hidden]="true" />
1329
+ <span *ngIf="checkIconTemplate" class="p-checkbox-icon" [attr.aria-hidden]="true">
1330
+ <ng-template *ngTemplateOutlet="checkIconTemplate"></ng-template>
1331
+ </span>
1332
+ </ng-container>
1333
+ </div>
1334
+ </div>
1335
+ <span *ngIf="!itemTemplate">{{ getOptionLabel(option) }}</span>
1336
+ <ng-container *ngTemplateOutlet="itemTemplate; context: { $implicit: option, index: getOptionIndex(i, scrollerOptions) }"></ng-container>
1337
+ </li>
1338
+ </ng-container>
1339
+ </ng-template>
1340
+ <li *ngIf="hasFilter() && isEmpty()" class="p-listbox-empty-message" role="option">
1341
+ <ng-container *ngIf="!emptyFilterTemplate && !emptyTemplate; else emptyFilter">
1342
+ {{ emptyFilterMessageText }}
1343
+ </ng-container>
1344
+ <ng-container #emptyFilter *ngTemplateOutlet="emptyFilterTemplate || emptyTemplate"></ng-container>
862
1345
  </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>
1346
+ <li *ngIf="!hasFilter() && isEmpty()" class="p-listbox-empty-message" role="option">
1347
+ <ng-container *ngIf="!emptyTemplate; else empty">
1348
+ {{ emptyMessageText }}
1349
+ </ng-container>
1350
+ <ng-container #empty *ngTemplateOutlet="emptyTemplate"></ng-container>
1351
+ </li>
1352
+ </ul>
1353
+ </ng-template>
877
1354
  </div>
878
1355
  <div class="p-listbox-footer" *ngIf="footerFacet || footerTemplate">
879
1356
  <ng-content select="p-footer"></ng-content>
880
- <ng-container *ngTemplateOutlet="footerTemplate"></ng-container>
1357
+ <ng-container *ngTemplateOutlet="footerTemplate; context: { $implicit: modelValue(), options: visibleOptions() }"></ng-container>
881
1358
  </div>
1359
+ <span *ngIf="isEmpty()" role="status" aria-live="polite" class="p-hidden-accessible">
1360
+ {{ emptyMessageText }}
1361
+ </span>
1362
+ <span role="status" aria-live="polite" class="p-hidden-accessible">
1363
+ {{ selectedMessageText }}
1364
+ </span>
1365
+ <span
1366
+ #lastHiddenFocusableElement
1367
+ role="presentation"
1368
+ [attr.aria-hidden]="true"
1369
+ class="p-hidden-accessible p-hidden-focusable"
1370
+ [tabindex]="!disabled ? tabindex : -1"
1371
+ (focus)="onLastHiddenFocus($event)"
1372
+ [attr.data-p-hidden-focusable]="true"
1373
+ >
1374
+ </span>
882
1375
  </div>
883
1376
  `, providers: [LISTBOX_VALUE_ACCESSOR], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, host: {
884
1377
  class: 'p-element'
885
1378
  }, 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: [{
1379
+ }], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: i0.ChangeDetectorRef }, { type: i1.FilterService }, { type: i1.PrimeNGConfig }, { type: i0.Renderer2 }]; }, propDecorators: { id: [{
1380
+ type: Input
1381
+ }], searchMessage: [{
1382
+ type: Input
1383
+ }], emptySelectionMessage: [{
1384
+ type: Input
1385
+ }], selectionMessage: [{
1386
+ type: Input
1387
+ }], autoOptionFocus: [{
1388
+ type: Input
1389
+ }], selectOnFocus: [{
1390
+ type: Input
1391
+ }], searchLocale: [{
1392
+ type: Input
1393
+ }], focusOnHover: [{
1394
+ type: Input
1395
+ }], filterMessage: [{
1396
+ type: Input
1397
+ }], filterFields: [{
1398
+ type: Input
1399
+ }], lazy: [{
1400
+ type: Input
1401
+ }], virtualScroll: [{
1402
+ type: Input
1403
+ }], virtualScrollItemSize: [{
1404
+ type: Input
1405
+ }], virtualScrollOptions: [{
1406
+ type: Input
1407
+ }], scrollHeight: [{
1408
+ type: Input
1409
+ }], tabindex: [{
1410
+ type: Input
1411
+ }], multiple: [{
887
1412
  type: Input
888
1413
  }], style: [{
889
1414
  type: Input
@@ -943,12 +1468,30 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.2", ngImpor
943
1468
  type: Output
944
1469
  }], onDblClick: [{
945
1470
  type: Output
1471
+ }], onFilter: [{
1472
+ type: Output
1473
+ }], onFocus: [{
1474
+ type: Output
1475
+ }], onBlur: [{
1476
+ type: Output
946
1477
  }], headerCheckboxViewChild: [{
947
1478
  type: ViewChild,
948
1479
  args: ['headerchkbox']
949
1480
  }], filterViewChild: [{
950
1481
  type: ViewChild,
951
1482
  args: ['filter']
1483
+ }], lastHiddenFocusableElement: [{
1484
+ type: ViewChild,
1485
+ args: ['lastHiddenFocusableElement']
1486
+ }], firstHiddenFocusableElement: [{
1487
+ type: ViewChild,
1488
+ args: ['firstHiddenFocusableElement']
1489
+ }], scroller: [{
1490
+ type: ViewChild,
1491
+ args: ['scroller']
1492
+ }], listViewChild: [{
1493
+ type: ViewChild,
1494
+ args: ['list']
952
1495
  }], headerFacet: [{
953
1496
  type: ContentChild,
954
1497
  args: [Header]
@@ -961,14 +1504,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.2", ngImpor
961
1504
  }] } });
962
1505
  class ListboxModule {
963
1506
  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] });
1507
+ 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] });
1508
+ 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
1509
  }
967
1510
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.2", ngImport: i0, type: ListboxModule, decorators: [{
968
1511
  type: NgModule,
969
1512
  args: [{
970
- imports: [CommonModule, SharedModule, RippleModule, SearchIcon, CheckIcon],
971
- exports: [Listbox, SharedModule],
1513
+ imports: [CommonModule, SharedModule, RippleModule, ScrollerModule, SearchIcon, CheckIcon],
1514
+ exports: [Listbox, SharedModule, ScrollerModule],
972
1515
  declarations: [Listbox]
973
1516
  }]
974
1517
  }] });