primeng 16.6.0 → 16.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (175) hide show
  1. package/animate/animate.d.ts +3 -2
  2. package/animateonscroll/animateonscroll.d.ts +73 -0
  3. package/animateonscroll/index.d.ts +5 -0
  4. package/animateonscroll/public_api.d.ts +1 -0
  5. package/api/primengconfig.d.ts +1 -0
  6. package/autocomplete/autocomplete.d.ts +177 -54
  7. package/autocomplete/autocomplete.interface.d.ts +30 -0
  8. package/cascadeselect/cascadeselect.d.ts +188 -68
  9. package/cascadeselect/cascadeselect.interface.d.ts +19 -0
  10. package/checkbox/checkbox.d.ts +1 -4
  11. package/chips/chips.d.ts +21 -5
  12. package/colorpicker/colorpicker.d.ts +5 -5
  13. package/contextmenu/contextmenu.d.ts +1 -0
  14. package/dialog/dialog.d.ts +1 -2
  15. package/dom/domhandler.d.ts +4 -1
  16. package/dropdown/dropdown.d.ts +115 -37
  17. package/esm2022/accordion/accordion.mjs +11 -7
  18. package/esm2022/animate/animate.mjs +4 -1
  19. package/esm2022/animateonscroll/animateonscroll.mjs +185 -0
  20. package/esm2022/animateonscroll/primeng-animateonscroll.mjs +5 -0
  21. package/esm2022/animateonscroll/public_api.mjs +2 -0
  22. package/esm2022/api/primengconfig.mjs +2 -1
  23. package/esm2022/autocomplete/autocomplete.interface.mjs +1 -1
  24. package/esm2022/autocomplete/autocomplete.mjs +893 -617
  25. package/esm2022/carousel/carousel.mjs +5 -1
  26. package/esm2022/cascadeselect/cascadeselect.interface.mjs +1 -1
  27. package/esm2022/cascadeselect/cascadeselect.mjs +921 -482
  28. package/esm2022/checkbox/checkbox.mjs +90 -73
  29. package/esm2022/chips/chips.mjs +197 -53
  30. package/esm2022/colorpicker/colorpicker.mjs +56 -37
  31. package/esm2022/contextmenu/contextmenu.mjs +10 -1
  32. package/esm2022/dialog/dialog.mjs +6 -11
  33. package/esm2022/dom/domhandler.mjs +26 -8
  34. package/esm2022/dropdown/dropdown.mjs +707 -588
  35. package/esm2022/inputmask/inputmask.mjs +22 -9
  36. package/esm2022/inputnumber/inputnumber.mjs +142 -83
  37. package/esm2022/inputswitch/inputswitch.mjs +55 -49
  38. package/esm2022/knob/knob.mjs +92 -5
  39. package/esm2022/listbox/listbox.interface.mjs +1 -1
  40. package/esm2022/listbox/listbox.mjs +996 -454
  41. package/esm2022/multiselect/multiselect.mjs +1022 -604
  42. package/esm2022/overlaypanel/overlaypanel.mjs +2 -2
  43. package/esm2022/paginator/paginator.mjs +2 -2
  44. package/esm2022/password/password.mjs +29 -28
  45. package/esm2022/radiobutton/radiobutton.mjs +46 -33
  46. package/esm2022/rating/rating.mjs +172 -80
  47. package/esm2022/selectbutton/selectbutton.mjs +105 -34
  48. package/esm2022/slider/slider.mjs +151 -66
  49. package/esm2022/table/table.mjs +3 -3
  50. package/esm2022/togglebutton/togglebutton.mjs +47 -10
  51. package/esm2022/tristatecheckbox/tristatecheckbox.mjs +53 -35
  52. package/fesm2022/primeng-accordion.mjs +10 -6
  53. package/fesm2022/primeng-accordion.mjs.map +1 -1
  54. package/fesm2022/primeng-animate.mjs +3 -0
  55. package/fesm2022/primeng-animate.mjs.map +1 -1
  56. package/fesm2022/primeng-animateonscroll.mjs +190 -0
  57. package/fesm2022/primeng-animateonscroll.mjs.map +1 -0
  58. package/fesm2022/primeng-api.mjs +1 -0
  59. package/fesm2022/primeng-api.mjs.map +1 -1
  60. package/fesm2022/primeng-autocomplete.mjs +893 -617
  61. package/fesm2022/primeng-autocomplete.mjs.map +1 -1
  62. package/fesm2022/primeng-carousel.mjs +4 -0
  63. package/fesm2022/primeng-carousel.mjs.map +1 -1
  64. package/fesm2022/primeng-cascadeselect.mjs +920 -481
  65. package/fesm2022/primeng-cascadeselect.mjs.map +1 -1
  66. package/fesm2022/primeng-checkbox.mjs +89 -72
  67. package/fesm2022/primeng-checkbox.mjs.map +1 -1
  68. package/fesm2022/primeng-chips.mjs +195 -51
  69. package/fesm2022/primeng-chips.mjs.map +1 -1
  70. package/fesm2022/primeng-colorpicker.mjs +55 -36
  71. package/fesm2022/primeng-colorpicker.mjs.map +1 -1
  72. package/fesm2022/primeng-contextmenu.mjs +9 -0
  73. package/fesm2022/primeng-contextmenu.mjs.map +1 -1
  74. package/fesm2022/primeng-dialog.mjs +5 -10
  75. package/fesm2022/primeng-dialog.mjs.map +1 -1
  76. package/fesm2022/primeng-dom.mjs +25 -7
  77. package/fesm2022/primeng-dom.mjs.map +1 -1
  78. package/fesm2022/primeng-dropdown.mjs +707 -588
  79. package/fesm2022/primeng-dropdown.mjs.map +1 -1
  80. package/fesm2022/primeng-inputmask.mjs +20 -7
  81. package/fesm2022/primeng-inputmask.mjs.map +1 -1
  82. package/fesm2022/primeng-inputnumber.mjs +140 -81
  83. package/fesm2022/primeng-inputnumber.mjs.map +1 -1
  84. package/fesm2022/primeng-inputswitch.mjs +55 -49
  85. package/fesm2022/primeng-inputswitch.mjs.map +1 -1
  86. package/fesm2022/primeng-knob.mjs +92 -5
  87. package/fesm2022/primeng-knob.mjs.map +1 -1
  88. package/fesm2022/primeng-listbox.mjs +995 -453
  89. package/fesm2022/primeng-listbox.mjs.map +1 -1
  90. package/fesm2022/primeng-multiselect.mjs +1021 -603
  91. package/fesm2022/primeng-multiselect.mjs.map +1 -1
  92. package/fesm2022/primeng-overlaypanel.mjs +1 -1
  93. package/fesm2022/primeng-overlaypanel.mjs.map +1 -1
  94. package/fesm2022/primeng-paginator.mjs +1 -1
  95. package/fesm2022/primeng-paginator.mjs.map +1 -1
  96. package/fesm2022/primeng-password.mjs +27 -26
  97. package/fesm2022/primeng-password.mjs.map +1 -1
  98. package/fesm2022/primeng-radiobutton.mjs +46 -33
  99. package/fesm2022/primeng-radiobutton.mjs.map +1 -1
  100. package/fesm2022/primeng-rating.mjs +171 -79
  101. package/fesm2022/primeng-rating.mjs.map +1 -1
  102. package/fesm2022/primeng-selectbutton.mjs +104 -33
  103. package/fesm2022/primeng-selectbutton.mjs.map +1 -1
  104. package/fesm2022/primeng-slider.mjs +150 -65
  105. package/fesm2022/primeng-slider.mjs.map +1 -1
  106. package/fesm2022/primeng-table.mjs +2 -2
  107. package/fesm2022/primeng-table.mjs.map +1 -1
  108. package/fesm2022/primeng-togglebutton.mjs +46 -9
  109. package/fesm2022/primeng-togglebutton.mjs.map +1 -1
  110. package/fesm2022/primeng-tristatecheckbox.mjs +53 -35
  111. package/fesm2022/primeng-tristatecheckbox.mjs.map +1 -1
  112. package/inputmask/inputmask.d.ts +6 -1
  113. package/inputnumber/inputnumber.d.ts +6 -1
  114. package/inputswitch/inputswitch.d.ts +6 -9
  115. package/knob/knob.d.ts +20 -3
  116. package/listbox/listbox.d.ts +208 -39
  117. package/listbox/listbox.interface.d.ts +15 -0
  118. package/multiselect/multiselect.d.ts +171 -60
  119. package/package.json +126 -120
  120. package/password/password.d.ts +3 -4
  121. package/radiobutton/radiobutton.d.ts +1 -2
  122. package/rating/rating.d.ts +29 -7
  123. package/resources/components/dropdown/dropdown.css +16 -2
  124. package/resources/themes/arya-blue/theme.css +106 -84
  125. package/resources/themes/arya-green/theme.css +106 -84
  126. package/resources/themes/arya-orange/theme.css +106 -84
  127. package/resources/themes/arya-purple/theme.css +106 -84
  128. package/resources/themes/bootstrap4-dark-blue/theme.css +110 -88
  129. package/resources/themes/bootstrap4-dark-purple/theme.css +110 -88
  130. package/resources/themes/bootstrap4-light-blue/theme.css +110 -88
  131. package/resources/themes/bootstrap4-light-purple/theme.css +110 -88
  132. package/resources/themes/fluent-light/theme.css +103 -81
  133. package/resources/themes/lara-dark-blue/theme.css +106 -84
  134. package/resources/themes/lara-dark-indigo/theme.css +106 -84
  135. package/resources/themes/lara-dark-purple/theme.css +106 -84
  136. package/resources/themes/lara-dark-teal/theme.css +106 -84
  137. package/resources/themes/lara-light-blue/theme.css +109 -87
  138. package/resources/themes/lara-light-indigo/theme.css +109 -87
  139. package/resources/themes/lara-light-purple/theme.css +109 -87
  140. package/resources/themes/lara-light-teal/theme.css +109 -87
  141. package/resources/themes/luna-amber/theme.css +110 -88
  142. package/resources/themes/luna-blue/theme.css +110 -88
  143. package/resources/themes/luna-green/theme.css +110 -88
  144. package/resources/themes/luna-pink/theme.css +110 -88
  145. package/resources/themes/md-dark-deeppurple/theme.css +117 -95
  146. package/resources/themes/md-dark-indigo/theme.css +117 -95
  147. package/resources/themes/md-light-deeppurple/theme.css +117 -95
  148. package/resources/themes/md-light-indigo/theme.css +117 -95
  149. package/resources/themes/mdc-dark-deeppurple/theme.css +117 -95
  150. package/resources/themes/mdc-dark-indigo/theme.css +117 -95
  151. package/resources/themes/mdc-light-deeppurple/theme.css +117 -95
  152. package/resources/themes/mdc-light-indigo/theme.css +117 -95
  153. package/resources/themes/mira/theme.css +107 -85
  154. package/resources/themes/nano/theme.css +109 -87
  155. package/resources/themes/nova/theme.css +110 -88
  156. package/resources/themes/nova-accent/theme.css +109 -87
  157. package/resources/themes/nova-alt/theme.css +110 -88
  158. package/resources/themes/rhea/theme.css +109 -87
  159. package/resources/themes/saga-blue/theme.css +106 -84
  160. package/resources/themes/saga-green/theme.css +106 -84
  161. package/resources/themes/saga-orange/theme.css +106 -84
  162. package/resources/themes/saga-purple/theme.css +106 -84
  163. package/resources/themes/soho-dark/theme.css +109 -87
  164. package/resources/themes/soho-light/theme.css +109 -87
  165. package/resources/themes/tailwind-light/theme.css +110 -88
  166. package/resources/themes/vela-blue/theme.css +106 -84
  167. package/resources/themes/vela-green/theme.css +106 -84
  168. package/resources/themes/vela-orange/theme.css +106 -84
  169. package/resources/themes/vela-purple/theme.css +106 -84
  170. package/resources/themes/viva-dark/theme.css +109 -87
  171. package/resources/themes/viva-light/theme.css +109 -87
  172. package/selectbutton/selectbutton.d.ts +15 -3
  173. package/slider/slider.d.ts +12 -6
  174. package/togglebutton/togglebutton.d.ts +7 -1
  175. package/tristatecheckbox/tristatecheckbox.d.ts +8 -4
@@ -1,7 +1,7 @@
1
1
  import * as i1 from '@angular/common';
2
2
  import { CommonModule } from '@angular/common';
3
3
  import * as i0 from '@angular/core';
4
- import { forwardRef, EventEmitter, Component, ViewEncapsulation, Input, Output, ChangeDetectionStrategy, ViewChild, ContentChild, ContentChildren, NgModule } from '@angular/core';
4
+ import { forwardRef, EventEmitter, Component, ViewEncapsulation, Input, Output, signal, computed, ChangeDetectionStrategy, ViewChild, ContentChild, ContentChildren, NgModule } from '@angular/core';
5
5
  import { NG_VALUE_ACCESSOR } from '@angular/forms';
6
6
  import * as i3 from 'primeng/api';
7
7
  import { TranslationKeys, Footer, Header, PrimeTemplate, SharedModule } from 'primeng/api';
@@ -14,7 +14,7 @@ import * as i6 from 'primeng/scroller';
14
14
  import { ScrollerModule } from 'primeng/scroller';
15
15
  import * as i5 from 'primeng/tooltip';
16
16
  import { TooltipModule } from 'primeng/tooltip';
17
- import { ObjectUtils } from 'primeng/utils';
17
+ import { ObjectUtils, UniqueComponentId } from 'primeng/utils';
18
18
  import { CheckIcon } from 'primeng/icons/check';
19
19
  import { SearchIcon } from 'primeng/icons/search';
20
20
  import { TimesCircleIcon } from 'primeng/icons/timescircle';
@@ -27,15 +27,19 @@ const MULTISELECT_VALUE_ACCESSOR = {
27
27
  multi: true
28
28
  };
29
29
  class MultiSelectItem {
30
+ id;
30
31
  option;
31
32
  selected;
32
33
  label;
33
34
  disabled;
34
35
  itemSize;
36
+ focused;
37
+ ariaPosInset;
38
+ ariaSetSize;
35
39
  template;
36
40
  checkIconTemplate;
37
41
  onClick = new EventEmitter();
38
- onKeydown = new EventEmitter();
42
+ onMouseEnter = new EventEmitter();
39
43
  onOptionClick(event) {
40
44
  this.onClick.emit({
41
45
  originalEvent: event,
@@ -43,35 +47,42 @@ class MultiSelectItem {
43
47
  selected: this.selected
44
48
  });
45
49
  }
46
- onOptionKeydown(event) {
47
- this.onKeydown.emit({
50
+ onOptionMouseEnter(event) {
51
+ this.onMouseEnter.emit({
48
52
  originalEvent: event,
49
- option: this.option
53
+ option: this.option,
54
+ selected: this.selected
50
55
  });
51
56
  }
52
57
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.2", ngImport: i0, type: MultiSelectItem, deps: [], target: i0.ɵɵFactoryTarget.Component });
53
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.0.2", type: MultiSelectItem, selector: "p-multiSelectItem", inputs: { option: "option", selected: "selected", label: "label", disabled: "disabled", itemSize: "itemSize", template: "template", checkIconTemplate: "checkIconTemplate" }, outputs: { onClick: "onClick", onKeydown: "onKeydown" }, host: { classAttribute: "p-element" }, ngImport: i0, template: `
58
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.0.2", type: MultiSelectItem, selector: "p-multiSelectItem", inputs: { id: "id", option: "option", selected: "selected", label: "label", disabled: "disabled", itemSize: "itemSize", focused: "focused", ariaPosInset: "ariaPosInset", ariaSetSize: "ariaSetSize", template: "template", checkIconTemplate: "checkIconTemplate" }, outputs: { onClick: "onClick", onMouseEnter: "onMouseEnter" }, host: { classAttribute: "p-element" }, ngImport: i0, template: `
54
59
  <li
60
+ pRipple
61
+ [ngStyle]="{ height: itemSize + 'px' }"
55
62
  class="p-multiselect-item"
56
- (click)="onOptionClick($event)"
57
- (keydown)="onOptionKeydown($event)"
63
+ [ngClass]="{ 'p-multiselect-item': true, 'p-highlight': selected, 'p-disabled': disabled, 'p-focus': focused }"
64
+ [id]="id"
58
65
  [attr.aria-label]="label"
59
- [attr.tabindex]="disabled ? null : '0'"
60
- [ngStyle]="{ height: itemSize + 'px' }"
61
- [ngClass]="{ 'p-highlight': selected, 'p-disabled': disabled }"
62
- pRipple
66
+ [attr.aria-setsize]="ariaSetSize"
67
+ [attr.aria-posinset]="ariaPosInset"
68
+ [attr.aria-selected]="selected"
69
+ [attr.data-p-focused]="focused"
70
+ [attr.data-p-highlight]="selected"
71
+ [attr.data-p-disabled]="disabled"
72
+ (click)="onOptionClick($event)"
73
+ (mouseenter)="onOptionMouseEnter($event)"
63
74
  >
64
75
  <div class="p-checkbox p-component">
65
- <div class="p-checkbox-box" [ngClass]="{ 'p-highlight': selected }">
76
+ <div class="p-checkbox-box" [ngClass]="{ 'p-highlight': selected }" [attr.aria-checked]="selected">
66
77
  <ng-container *ngIf="selected">
67
- <CheckIcon *ngIf="!checkIconTemplate" [styleClass]="'p-checkbox-icon'" />
68
- <span *ngIf="checkIconTemplate" class="p-checkbox-icon">
78
+ <CheckIcon *ngIf="!checkIconTemplate" [styleClass]="'p-checkbox-icon'" [attr.aria-hidden]="true" />
79
+ <span *ngIf="checkIconTemplate" class="p-checkbox-icon" [attr.aria-hidden]="true">
69
80
  <ng-template *ngTemplateOutlet="checkIconTemplate"></ng-template>
70
81
  </span>
71
82
  </ng-container>
72
83
  </div>
73
84
  </div>
74
- <span *ngIf="!template">{{ label }}</span>
85
+ <span *ngIf="!template">{{ label ?? 'empty' }}</span>
75
86
  <ng-container *ngTemplateOutlet="template; context: { $implicit: option }"></ng-container>
76
87
  </li>
77
88
  `, isInline: true, dependencies: [{ kind: "directive", type: i0.forwardRef(function () { return i1.NgClass; }), selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i0.forwardRef(function () { return i1.NgIf; }), selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i0.forwardRef(function () { return i1.NgTemplateOutlet; }), selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i0.forwardRef(function () { return i1.NgStyle; }), selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i0.forwardRef(function () { return i2.Ripple; }), selector: "[pRipple]" }, { kind: "component", type: i0.forwardRef(function () { return CheckIcon; }), selector: "CheckIcon" }], encapsulation: i0.ViewEncapsulation.None });
@@ -82,26 +93,32 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.2", ngImpor
82
93
  selector: 'p-multiSelectItem',
83
94
  template: `
84
95
  <li
96
+ pRipple
97
+ [ngStyle]="{ height: itemSize + 'px' }"
85
98
  class="p-multiselect-item"
86
- (click)="onOptionClick($event)"
87
- (keydown)="onOptionKeydown($event)"
99
+ [ngClass]="{ 'p-multiselect-item': true, 'p-highlight': selected, 'p-disabled': disabled, 'p-focus': focused }"
100
+ [id]="id"
88
101
  [attr.aria-label]="label"
89
- [attr.tabindex]="disabled ? null : '0'"
90
- [ngStyle]="{ height: itemSize + 'px' }"
91
- [ngClass]="{ 'p-highlight': selected, 'p-disabled': disabled }"
92
- pRipple
102
+ [attr.aria-setsize]="ariaSetSize"
103
+ [attr.aria-posinset]="ariaPosInset"
104
+ [attr.aria-selected]="selected"
105
+ [attr.data-p-focused]="focused"
106
+ [attr.data-p-highlight]="selected"
107
+ [attr.data-p-disabled]="disabled"
108
+ (click)="onOptionClick($event)"
109
+ (mouseenter)="onOptionMouseEnter($event)"
93
110
  >
94
111
  <div class="p-checkbox p-component">
95
- <div class="p-checkbox-box" [ngClass]="{ 'p-highlight': selected }">
112
+ <div class="p-checkbox-box" [ngClass]="{ 'p-highlight': selected }" [attr.aria-checked]="selected">
96
113
  <ng-container *ngIf="selected">
97
- <CheckIcon *ngIf="!checkIconTemplate" [styleClass]="'p-checkbox-icon'" />
98
- <span *ngIf="checkIconTemplate" class="p-checkbox-icon">
114
+ <CheckIcon *ngIf="!checkIconTemplate" [styleClass]="'p-checkbox-icon'" [attr.aria-hidden]="true" />
115
+ <span *ngIf="checkIconTemplate" class="p-checkbox-icon" [attr.aria-hidden]="true">
99
116
  <ng-template *ngTemplateOutlet="checkIconTemplate"></ng-template>
100
117
  </span>
101
118
  </ng-container>
102
119
  </div>
103
120
  </div>
104
- <span *ngIf="!template">{{ label }}</span>
121
+ <span *ngIf="!template">{{ label ?? 'empty' }}</span>
105
122
  <ng-container *ngTemplateOutlet="template; context: { $implicit: option }"></ng-container>
106
123
  </li>
107
124
  `,
@@ -110,7 +127,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.2", ngImpor
110
127
  class: 'p-element'
111
128
  }
112
129
  }]
113
- }], propDecorators: { option: [{
130
+ }], propDecorators: { id: [{
131
+ type: Input
132
+ }], option: [{
114
133
  type: Input
115
134
  }], selected: [{
116
135
  type: Input
@@ -120,13 +139,19 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.2", ngImpor
120
139
  type: Input
121
140
  }], itemSize: [{
122
141
  type: Input
142
+ }], focused: [{
143
+ type: Input
144
+ }], ariaPosInset: [{
145
+ type: Input
146
+ }], ariaSetSize: [{
147
+ type: Input
123
148
  }], template: [{
124
149
  type: Input
125
150
  }], checkIconTemplate: [{
126
151
  type: Input
127
152
  }], onClick: [{
128
153
  type: Output
129
- }], onKeydown: [{
154
+ }], onMouseEnter: [{
130
155
  type: Output
131
156
  }] } });
132
157
  /**
@@ -141,6 +166,16 @@ class MultiSelect {
141
166
  filterService;
142
167
  config;
143
168
  overlayService;
169
+ /**
170
+ * Unique identifier of the component
171
+ * @group Props
172
+ */
173
+ id;
174
+ /**
175
+ * Defines a string that labels the input for accessibility.
176
+ * @group Props
177
+ */
178
+ ariaLabel;
144
179
  /**
145
180
  * Inline style of the element.
146
181
  * @group Props
@@ -205,7 +240,7 @@ class MultiSelect {
205
240
  * Index of the element in tabbing order.
206
241
  * @group Props
207
242
  */
208
- tabindex;
243
+ tabindex = 0;
209
244
  /**
210
245
  * Target element to attach the overlay, valid values are "body" or a local ng-template variable of another element (note: use binding with brackets for template variables, e.g. [appendTo]="mydiv" for a div element having #mydiv as variable name).
211
246
  * @group Props
@@ -221,11 +256,6 @@ class MultiSelect {
221
256
  * @group Props
222
257
  */
223
258
  name;
224
- /**
225
- * Label of the input for accessibility.
226
- * @group Props
227
- */
228
- label;
229
259
  /**
230
260
  * Establishes relationships between the component and label(s) where its value should be one or more element IDs.
231
261
  * @group Props
@@ -241,11 +271,16 @@ class MultiSelect {
241
271
  * @group Props
242
272
  */
243
273
  maxSelectedLabels = 3;
274
+ /**
275
+ * Decides how many selected item labels to show at most.
276
+ * @group Props
277
+ */
278
+ selectionLimit;
244
279
  /**
245
280
  * Label to display after exceeding max selected labels e.g. ({0} items selected), defaults "ellipsis" keyword to indicate a text-overflow.
246
281
  * @group Props
247
282
  */
248
- selectedItemsLabel = 'ellipsis';
283
+ selectedItemsLabel = '{0} items selected';
249
284
  /**
250
285
  * Whether to show the checkbox at header to toggle all items at once.
251
286
  * @group Props
@@ -290,7 +325,7 @@ class MultiSelect {
290
325
  * Name of the label field of an option group.
291
326
  * @group Props
292
327
  */
293
- optionGroupLabel;
328
+ optionGroupLabel = 'label';
294
329
  /**
295
330
  * Name of the options field of an option group.
296
331
  * @group Props
@@ -380,7 +415,7 @@ class MultiSelect {
380
415
  * Defines the autocomplete is active.
381
416
  * @group Props
382
417
  */
383
- autocomplete = 'on';
418
+ autocomplete = 'off';
384
419
  /**
385
420
  * When enabled, a clear icon is displayed to clear the value.
386
421
  * @group Props
@@ -441,7 +476,7 @@ class MultiSelect {
441
476
  */
442
477
  set defaultLabel(val) {
443
478
  this._defaultLabel = val;
444
- this.updateLabel();
479
+ console.warn('defaultLabel property is deprecated since 16.6.0, use placeholder instead');
445
480
  }
446
481
  get defaultLabel() {
447
482
  return this._defaultLabel;
@@ -452,7 +487,6 @@ class MultiSelect {
452
487
  */
453
488
  set placeholder(val) {
454
489
  this._placeholder = val;
455
- this.updateLabel();
456
490
  }
457
491
  get placeholder() {
458
492
  return this._placeholder;
@@ -462,22 +496,21 @@ class MultiSelect {
462
496
  * @group Props
463
497
  */
464
498
  get options() {
465
- return this._options;
499
+ const options = this._options();
500
+ return options;
466
501
  }
467
502
  set options(val) {
468
- this._options = val;
469
- this.updateLabel();
503
+ this._options.set(val);
470
504
  }
471
505
  /**
472
506
  * When specified, filter displays with this value.
473
507
  * @group Props
474
508
  */
475
509
  get filterValue() {
476
- return this._filterValue;
510
+ return this._filterValue();
477
511
  }
478
512
  set filterValue(val) {
479
- this._filterValue = val;
480
- this.activateFilter();
513
+ this._filterValue.set(val);
481
514
  }
482
515
  /**
483
516
  * Item size of item to be virtual scrolled.
@@ -492,25 +525,25 @@ class MultiSelect {
492
525
  console.warn('The itemSize property is deprecated, use virtualScrollItemSize property instead.');
493
526
  }
494
527
  /**
495
- * Number of maximum options that can be selected.
528
+ * Fields used when filtering the options, defaults to optionLabel.
496
529
  * @group Props
497
530
  */
498
- get selectionLimit() {
499
- return this._selectionLimit;
500
- }
501
- set selectionLimit(val) {
502
- this._selectionLimit = val;
503
- this.checkSelectionLimit();
504
- }
505
- containerViewChild;
506
- overlayViewChild;
507
- filterInputChild;
508
- accessibleViewChild;
509
- itemsViewChild;
510
- scroller;
511
- footerFacet;
512
- headerFacet;
513
- templates;
531
+ focusOnHover = false;
532
+ /**
533
+ * Fields used when filtering the options, defaults to optionLabel.
534
+ * @group Props
535
+ */
536
+ filterFields;
537
+ /**
538
+ * Determines if the option will be selected on focus.
539
+ * @group Props
540
+ */
541
+ selectOnFocus = false;
542
+ /**
543
+ * Whether to focus on the first visible or selected element when the overlay panel is shown.
544
+ * @group Props
545
+ */
546
+ autoOptionFocus = true;
514
547
  /**
515
548
  * Callback to invoke when value changes.
516
549
  * @param {MultiSelectChangeEvent} event - Custom change event.
@@ -568,6 +601,20 @@ class MultiSelect {
568
601
  * @group Emits
569
602
  */
570
603
  onRemove = new EventEmitter();
604
+ containerViewChild;
605
+ overlayViewChild;
606
+ filterInputChild;
607
+ focusInputViewChild;
608
+ itemsViewChild;
609
+ scroller;
610
+ lastHiddenFocusableElementOnOverlay;
611
+ firstHiddenFocusableElementOnOverlay;
612
+ headerCheckboxViewChild;
613
+ footerFacet;
614
+ headerFacet;
615
+ templates;
616
+ searchValue;
617
+ searchTimeout;
571
618
  _autoZIndex;
572
619
  _baseZIndex;
573
620
  _showTransitionOptions;
@@ -582,8 +629,6 @@ class MultiSelect {
582
629
  onModelTouched = () => { };
583
630
  valuesAsString;
584
631
  focus;
585
- filled;
586
- _filterValue;
587
632
  filtered;
588
633
  itemTemplate;
589
634
  groupTemplate;
@@ -602,10 +647,112 @@ class MultiSelect {
602
647
  dropdownIconTemplate;
603
648
  headerCheckboxFocus;
604
649
  filterOptions;
605
- _options;
606
650
  maxSelectionLimitReached;
607
651
  preventModelTouched;
608
652
  preventDocumentDefault;
653
+ focused = false;
654
+ itemsWrapper;
655
+ modelValue = signal(null);
656
+ _filterValue = signal(null);
657
+ _options = signal(null);
658
+ startRangeIndex = signal(-1);
659
+ focusedOptionIndex = signal(-1);
660
+ get containerClass() {
661
+ return {
662
+ 'p-multiselect p-component p-inputwrapper': true,
663
+ 'p-disabled': this.disabled,
664
+ 'p-multiselect-clearable': this.showClear && !this.disabled,
665
+ 'p-multiselect-chip': this.display === 'chip',
666
+ 'p-focus': this.focused,
667
+ 'p-inputwrapper-filled': ObjectUtils.isNotEmpty(this.modelValue()),
668
+ 'p-inputwrapper-focus': this.focused || this.overlayVisible
669
+ };
670
+ }
671
+ get inputClass() {
672
+ return {
673
+ 'p-multiselect-label p-inputtext': true,
674
+ 'p-placeholder': (this.placeholder || this.defaultLabel) && (this.label() === this.placeholder || this.label() === this.defaultLabel),
675
+ 'p-multiselect-label-empty': !this.selectedItemsTemplate && (this.label() === 'p-emptylabel' || this.label().length === 0)
676
+ };
677
+ }
678
+ get panelClass() {
679
+ return {
680
+ 'p-multiselect-panel p-component': true,
681
+ 'p-input-filled': this.config.inputStyle === 'filled',
682
+ 'p-ripple-disabled': this.config.ripple === false
683
+ };
684
+ }
685
+ get labelClass() {
686
+ return {
687
+ 'p-multiselect-label': true,
688
+ 'p-placeholder': this.label() === this.placeholder || this.label() === this.defaultLabel,
689
+ 'p-multiselect-label-empty': !this.placeholder && !this.defaultLabel && (!this.modelValue() || this.modelValue().length === 0)
690
+ };
691
+ }
692
+ get emptyMessageLabel() {
693
+ return this.emptyMessage || this.config.getTranslation(TranslationKeys.EMPTY_MESSAGE);
694
+ }
695
+ get emptyFilterMessageLabel() {
696
+ return this.emptyFilterMessage || this.config.getTranslation(TranslationKeys.EMPTY_FILTER_MESSAGE);
697
+ }
698
+ get filled() {
699
+ if (typeof this.modelValue() === 'string')
700
+ return !!this.modelValue();
701
+ return this.modelValue() || this.modelValue() != null || this.modelValue() != undefined;
702
+ }
703
+ get isVisibleClearIcon() {
704
+ return this.modelValue() != null && this.modelValue() !== '' && this.showClear && !this.disabled;
705
+ }
706
+ get toggleAllAriaLabel() {
707
+ return this.config.translation.aria ? this.config.translation.aria[this.allSelected() ? 'selectAll' : 'unselectAll'] : undefined;
708
+ }
709
+ get closeAriaLabel() {
710
+ return this.config.translation.aria ? this.config.translation.aria.close : undefined;
711
+ }
712
+ visibleOptions = computed(() => {
713
+ const options = this.group ? this.flatOptions(this.options) : this.options || [];
714
+ if (this._filterValue()) {
715
+ const filteredOptions = this.filterService.filter(options, this.searchFields(), this._filterValue(), this.filterMatchMode, this.filterLocale);
716
+ if (this.group) {
717
+ const optionGroups = this.options || [];
718
+ const filtered = [];
719
+ optionGroups.forEach((group) => {
720
+ const groupChildren = this.getOptionGroupChildren(group);
721
+ const filteredItems = groupChildren.filter((item) => filteredOptions.includes(item));
722
+ if (filteredItems.length > 0)
723
+ filtered.push({ ...group, [typeof this.optionGroupChildren === 'string' ? this.optionGroupChildren : 'items']: [...filteredItems] });
724
+ });
725
+ return this.flatOptions(filtered);
726
+ }
727
+ return filteredOptions;
728
+ }
729
+ return options;
730
+ });
731
+ label = computed(() => {
732
+ let label;
733
+ const modelValue = this.modelValue();
734
+ if (modelValue && modelValue.length) {
735
+ if (ObjectUtils.isNotEmpty(this.maxSelectedLabels) && modelValue.length > this.maxSelectedLabels) {
736
+ return this.getSelectedItemsLabel();
737
+ }
738
+ else {
739
+ label = '';
740
+ for (let i = 0; i < modelValue.length; i++) {
741
+ if (i !== 0) {
742
+ label += ', ';
743
+ }
744
+ label += this.getLabelByValue(modelValue[i]);
745
+ }
746
+ }
747
+ }
748
+ else {
749
+ label = this.placeholder || this.defaultLabel || '';
750
+ }
751
+ return label;
752
+ });
753
+ chipSelectedItems = computed(() => {
754
+ return ObjectUtils.isNotEmpty(this.maxSelectedLabels) && this.modelValue() && this.modelValue().length > this.maxSelectedLabels ? this.modelValue().slice(0, this.maxSelectedLabels) : this.modelValue();
755
+ });
609
756
  constructor(el, renderer, cd, zone, filterService, config, overlayService) {
610
757
  this.el = el;
611
758
  this.renderer = renderer;
@@ -616,7 +763,8 @@ class MultiSelect {
616
763
  this.overlayService = overlayService;
617
764
  }
618
765
  ngOnInit() {
619
- this.updateLabel();
766
+ this.id = this.id || UniqueComponentId();
767
+ this.autoUpdateModel();
620
768
  if (this.filterBy) {
621
769
  this.filterOptions = {
622
770
  filter: (value) => this.onFilterInputChange(value),
@@ -693,6 +841,154 @@ class MultiSelect {
693
841
  this.filtered = false;
694
842
  }
695
843
  }
844
+ flatOptions(options) {
845
+ return (options || []).reduce((result, option, index) => {
846
+ result.push({ optionGroup: option, group: true, index });
847
+ const optionGroupChildren = this.getOptionGroupChildren(option);
848
+ optionGroupChildren && optionGroupChildren.forEach((o) => result.push(o));
849
+ return result;
850
+ }, []);
851
+ }
852
+ autoUpdateModel() {
853
+ if (this.selectOnFocus && this.autoOptionFocus && !this.hasSelectedOption()) {
854
+ this.focusedOptionIndex.set(this.findFirstFocusedOptionIndex());
855
+ const value = this.getOptionValue(this.visibleOptions()[this.focusedOptionIndex()]);
856
+ this.onOptionSelect({ originalEvent: null, option: [value] });
857
+ }
858
+ }
859
+ updateModel(value, event) {
860
+ this.value = value;
861
+ this.onModelChange(value);
862
+ this.modelValue.set(value);
863
+ this.onChange.emit({
864
+ originalEvent: event,
865
+ value: value
866
+ });
867
+ }
868
+ onOptionSelect(event, isFocus = false, index = -1) {
869
+ const { originalEvent, option } = event;
870
+ if (this.disabled || this.isOptionDisabled(option)) {
871
+ return;
872
+ }
873
+ let selected = this.isSelected(option);
874
+ let value = null;
875
+ if (selected) {
876
+ value = this.modelValue().filter((val) => !ObjectUtils.equals(val, this.getOptionValue(option), this.equalityKey()));
877
+ }
878
+ else {
879
+ value = [...(this.modelValue() || []), this.getOptionValue(option)];
880
+ }
881
+ this.updateModel(value, originalEvent);
882
+ index !== -1 && this.focusedOptionIndex.set(index);
883
+ isFocus && DomHandler.focus(this.focusInputViewChild?.nativeElement);
884
+ }
885
+ onOptionSelectRange(event, start = -1, end = -1) {
886
+ start === -1 && (start = this.findNearestSelectedOptionIndex(end, true));
887
+ end === -1 && (end = this.findNearestSelectedOptionIndex(start));
888
+ if (start !== -1 && end !== -1) {
889
+ const rangeStart = Math.min(start, end);
890
+ const rangeEnd = Math.max(start, end);
891
+ const value = this.visibleOptions()
892
+ .slice(rangeStart, rangeEnd + 1)
893
+ .filter((option) => this.isValidOption(option))
894
+ .map((option) => this.getOptionValue(option));
895
+ this.updateModel(value, event);
896
+ }
897
+ }
898
+ searchFields() {
899
+ return this.filterFields || [this.optionLabel];
900
+ }
901
+ findNearestSelectedOptionIndex(index, firstCheckUp = false) {
902
+ let matchedOptionIndex = -1;
903
+ if (this.hasSelectedOption()) {
904
+ if (firstCheckUp) {
905
+ matchedOptionIndex = this.findPrevSelectedOptionIndex(index);
906
+ matchedOptionIndex = matchedOptionIndex === -1 ? this.findNextSelectedOptionIndex(index) : matchedOptionIndex;
907
+ }
908
+ else {
909
+ matchedOptionIndex = this.findNextSelectedOptionIndex(index);
910
+ matchedOptionIndex = matchedOptionIndex === -1 ? this.findPrevSelectedOptionIndex(index) : matchedOptionIndex;
911
+ }
912
+ }
913
+ return matchedOptionIndex > -1 ? matchedOptionIndex : index;
914
+ }
915
+ findPrevSelectedOptionIndex(index) {
916
+ const matchedOptionIndex = this.hasSelectedOption() && index > 0 ? ObjectUtils.findLastIndex(this.visibleOptions().slice(0, index), (option) => this.isValidSelectedOption(option)) : -1;
917
+ return matchedOptionIndex > -1 ? matchedOptionIndex : -1;
918
+ }
919
+ findFirstFocusedOptionIndex() {
920
+ const selectedIndex = this.findFirstSelectedOptionIndex();
921
+ return selectedIndex < 0 ? this.findFirstOptionIndex() : selectedIndex;
922
+ }
923
+ findFirstOptionIndex() {
924
+ return this.visibleOptions().findIndex((option) => this.isValidOption(option));
925
+ }
926
+ findFirstSelectedOptionIndex() {
927
+ return this.hasSelectedOption() ? this.visibleOptions().findIndex((option) => this.isValidSelectedOption(option)) : -1;
928
+ }
929
+ findNextSelectedOptionIndex(index) {
930
+ const matchedOptionIndex = this.hasSelectedOption() && index < this.visibleOptions().length - 1
931
+ ? this.visibleOptions()
932
+ .slice(index + 1)
933
+ .findIndex((option) => this.isValidSelectedOption(option))
934
+ : -1;
935
+ return matchedOptionIndex > -1 ? matchedOptionIndex + index + 1 : -1;
936
+ }
937
+ equalityKey() {
938
+ return this.optionValue ? null : this.dataKey;
939
+ }
940
+ hasSelectedOption() {
941
+ return ObjectUtils.isNotEmpty(this.modelValue());
942
+ }
943
+ isValidSelectedOption(option) {
944
+ return this.isValidOption(option) && this.isSelected(option);
945
+ }
946
+ isOptionGroup(option) {
947
+ return (this.group || this.optionGroupLabel) && option.optionGroup && option.group;
948
+ }
949
+ isValidOption(option) {
950
+ return option && !(this.isOptionDisabled(option) || this.isOptionGroup(option));
951
+ }
952
+ isOptionDisabled(option) {
953
+ let disabled = this.optionDisabled ? ObjectUtils.resolveFieldData(option, this.optionDisabled) : option && option.disabled !== undefined ? option.disabled : false;
954
+ return disabled || (this.maxSelectionLimitReached && !this.isSelected(option));
955
+ }
956
+ isSelected(option) {
957
+ const optionValue = this.getOptionValue(option);
958
+ return (this.modelValue() || []).some((value) => ObjectUtils.equals(value, optionValue, this.equalityKey()));
959
+ }
960
+ isOptionMatched(option) {
961
+ return this.isValidOption(option) && this.getOptionLabel(option).toLocaleLowerCase(this.filterLocale).startsWith(this.searchValue.toLocaleLowerCase(this.filterLocale));
962
+ }
963
+ isEmpty() {
964
+ return !this._options() || (this._options() && this._options().length === 0);
965
+ }
966
+ getOptionIndex(index, scrollerOptions) {
967
+ return this.virtualScrollerDisabled ? index : scrollerOptions && scrollerOptions.getItemOptions(index)['index'];
968
+ }
969
+ getAriaPosInset(index) {
970
+ return ((this.optionGroupLabel
971
+ ? index -
972
+ this.visibleOptions()
973
+ .slice(0, index)
974
+ .filter((option) => this.isOptionGroup(option)).length
975
+ : index) + 1);
976
+ }
977
+ get ariaSetSize() {
978
+ return this.visibleOptions().filter((option) => !this.isOptionGroup(option)).length;
979
+ }
980
+ getLabelByValue(value) {
981
+ const options = this.group ? this.flatOptions(this._options()) : this._options() || [];
982
+ const matchedOption = options.find((option) => !this.isOptionGroup(option) && ObjectUtils.equals(this.getOptionValue(option), value, this.equalityKey()));
983
+ return matchedOption ? this.getOptionLabel(matchedOption) : null;
984
+ }
985
+ getSelectedItemsLabel() {
986
+ let pattern = /{(.*?)}/;
987
+ if (pattern.test(this.selectedItemsLabel)) {
988
+ return this.selectedItemsLabel.replace(this.selectedItemsLabel.match(pattern)[0], this.modelValue().length + '');
989
+ }
990
+ return this.selectedItemsLabel;
991
+ }
696
992
  getOptionLabel(option) {
697
993
  return this.optionLabel ? ObjectUtils.resolveFieldData(option, this.optionLabel) : option && option.label != undefined ? option.label : option;
698
994
  }
@@ -705,186 +1001,411 @@ class MultiSelect {
705
1001
  getOptionGroupChildren(optionGroup) {
706
1002
  return this.optionGroupChildren ? ObjectUtils.resolveFieldData(optionGroup, this.optionGroupChildren) : optionGroup.items;
707
1003
  }
708
- isOptionDisabled(option) {
709
- let disabled = this.optionDisabled ? ObjectUtils.resolveFieldData(option, this.optionDisabled) : option && option.disabled !== undefined ? option.disabled : false;
710
- return disabled || (this.maxSelectionLimitReached && !this.isSelected(option));
711
- }
712
- writeValue(value) {
713
- this.value = value;
714
- this.updateLabel();
715
- this.updateFilledState();
716
- this.checkSelectionLimit();
717
- this.cd.markForCheck();
718
- }
719
- checkSelectionLimit() {
720
- if (this.selectionLimit && this.value && this.value.length === this.selectionLimit) {
721
- this.maxSelectionLimitReached = true;
1004
+ onKeyDown(event) {
1005
+ if (this.disabled) {
1006
+ event.preventDefault();
1007
+ return;
722
1008
  }
723
- else {
724
- this.maxSelectionLimitReached = false;
1009
+ const metaKey = event.metaKey || event.ctrlKey;
1010
+ switch (event.code) {
1011
+ case 'ArrowDown':
1012
+ this.onArrowDownKey(event);
1013
+ break;
1014
+ case 'ArrowUp':
1015
+ this.onArrowUpKey(event);
1016
+ break;
1017
+ case 'Home':
1018
+ this.onHomeKey(event);
1019
+ break;
1020
+ case 'End':
1021
+ this.onEndKey(event);
1022
+ break;
1023
+ case 'PageDown':
1024
+ this.onPageDownKey(event);
1025
+ break;
1026
+ case 'PageUp':
1027
+ this.onPageUpKey(event);
1028
+ break;
1029
+ case 'Enter':
1030
+ case 'Space':
1031
+ this.onEnterKey(event);
1032
+ break;
1033
+ case 'Escape':
1034
+ this.onEscapeKey(event);
1035
+ break;
1036
+ case 'Tab':
1037
+ this.onTabKey(event);
1038
+ break;
1039
+ case 'ShiftLeft':
1040
+ case 'ShiftRight':
1041
+ this.onShiftKey();
1042
+ break;
1043
+ default:
1044
+ if (event.code === 'KeyA' && metaKey) {
1045
+ const value = this.visibleOptions()
1046
+ .filter((option) => this.isValidOption(option))
1047
+ .map((option) => this.getOptionValue(option));
1048
+ this.updateModel(value, event);
1049
+ event.preventDefault();
1050
+ break;
1051
+ }
1052
+ if (!metaKey && ObjectUtils.isPrintableCharacter(event.key)) {
1053
+ !this.overlayVisible && this.show();
1054
+ this.searchOptions(event, event.key);
1055
+ event.preventDefault();
1056
+ }
1057
+ break;
725
1058
  }
726
1059
  }
727
- updateFilledState() {
728
- this.filled = this.value && this.value.length > 0;
1060
+ onFilterKeyDown(event) {
1061
+ switch (event.code) {
1062
+ case 'ArrowDown':
1063
+ this.onArrowDownKey(event);
1064
+ break;
1065
+ case 'ArrowUp':
1066
+ this.onArrowUpKey(event, true);
1067
+ break;
1068
+ case 'ArrowLeft':
1069
+ case 'ArrowRight':
1070
+ this.onArrowLeftKey(event, true);
1071
+ break;
1072
+ case 'Home':
1073
+ this.onHomeKey(event, true);
1074
+ break;
1075
+ case 'End':
1076
+ this.onEndKey(event, true);
1077
+ break;
1078
+ case 'Enter':
1079
+ this.onEnterKey(event);
1080
+ break;
1081
+ case 'Escape':
1082
+ this.onEscapeKey(event);
1083
+ break;
1084
+ case 'Tab':
1085
+ this.onTabKey(event, true);
1086
+ break;
1087
+ default:
1088
+ break;
1089
+ }
729
1090
  }
730
- registerOnChange(fn) {
731
- this.onModelChange = fn;
1091
+ onArrowLeftKey(event, pressedInInputText = false) {
1092
+ pressedInInputText && this.focusedOptionIndex.set(-1);
732
1093
  }
733
- registerOnTouched(fn) {
734
- this.onModelTouched = fn;
1094
+ onArrowDownKey(event) {
1095
+ const optionIndex = this.focusedOptionIndex() !== -1 ? this.findNextOptionIndex(this.focusedOptionIndex()) : this.findFirstFocusedOptionIndex();
1096
+ if (event.shiftKey) {
1097
+ this.onOptionSelectRange(event, this.startRangeIndex(), optionIndex);
1098
+ }
1099
+ this.changeFocusedOptionIndex(event, optionIndex);
1100
+ !this.overlayVisible && this.show();
1101
+ event.preventDefault();
735
1102
  }
736
- setDisabledState(val) {
737
- this.disabled = val;
738
- this.cd.markForCheck();
1103
+ onArrowUpKey(event, pressedInInputText = false) {
1104
+ if (event.altKey && !pressedInInputText) {
1105
+ if (this.focusedOptionIndex() !== -1) {
1106
+ this.onOptionSelect(event, this.visibleOptions()[this.focusedOptionIndex()]);
1107
+ }
1108
+ this.overlayVisible && this.hide();
1109
+ event.preventDefault();
1110
+ }
1111
+ else {
1112
+ const optionIndex = this.focusedOptionIndex() !== -1 ? this.findPrevOptionIndex(this.focusedOptionIndex()) : this.findLastFocusedOptionIndex();
1113
+ if (event.shiftKey) {
1114
+ this.onOptionSelectRange(event, optionIndex, this.startRangeIndex());
1115
+ }
1116
+ this.changeFocusedOptionIndex(event, optionIndex);
1117
+ !this.overlayVisible && this.show();
1118
+ event.preventDefault();
1119
+ }
739
1120
  }
740
- onOptionClick(event) {
741
- let option = event.option;
742
- if (this.isOptionDisabled(option)) {
743
- return;
1121
+ onHomeKey(event, pressedInInputText = false) {
1122
+ const { currentTarget } = event;
1123
+ if (pressedInInputText) {
1124
+ const len = currentTarget.value.length;
1125
+ currentTarget.setSelectionRange(0, event.shiftKey ? len : 0);
1126
+ this.focusedOptionIndex.set(-1);
744
1127
  }
745
- let optionValue = this.getOptionValue(option);
746
- let selectionIndex = this.findSelectionIndex(optionValue);
747
- if (selectionIndex != -1) {
748
- this.value = this.value.filter((val, i) => i != selectionIndex);
749
- this.onRemove.emit({ newValue: this.value, removed: optionValue });
750
- if (this.selectionLimit) {
751
- this.maxSelectionLimitReached = false;
1128
+ else {
1129
+ let metaKey = event.metaKey || event.ctrlKey;
1130
+ let optionIndex = this.findFirstOptionIndex();
1131
+ if (event.shiftKey && metaKey) {
1132
+ this.onOptionSelectRange(event, optionIndex, this.startRangeIndex());
752
1133
  }
1134
+ this.changeFocusedOptionIndex(event, optionIndex);
1135
+ !this.overlayVisible && this.show();
1136
+ }
1137
+ event.preventDefault();
1138
+ }
1139
+ onEndKey(event, pressedInInputText = false) {
1140
+ const { currentTarget } = event;
1141
+ if (pressedInInputText) {
1142
+ const len = currentTarget.value.length;
1143
+ currentTarget.setSelectionRange(event.shiftKey ? 0 : len, len);
1144
+ this.focusedOptionIndex.set(-1);
753
1145
  }
754
1146
  else {
755
- if (!this.selectionLimit || !this.value || this.value.length < this.selectionLimit) {
756
- this.value = [...(this.value || []), optionValue];
1147
+ let metaKey = event.metaKey || event.ctrlKey;
1148
+ let optionIndex = this.findLastFocusedOptionIndex();
1149
+ if (event.shiftKey && metaKey) {
1150
+ this.onOptionSelectRange(event, this.startRangeIndex(), optionIndex);
757
1151
  }
758
- this.checkSelectionLimit();
1152
+ this.changeFocusedOptionIndex(event, optionIndex);
1153
+ !this.overlayVisible && this.show();
759
1154
  }
760
- this.onModelChange(this.value);
761
- this.onChange.emit({ originalEvent: event.originalEvent, value: this.value, itemValue: optionValue });
762
- this.updateLabel();
763
- this.updateFilledState();
1155
+ event.preventDefault();
764
1156
  }
765
- isSelected(option) {
766
- return this.findSelectionIndex(this.getOptionValue(option)) != -1;
767
- }
768
- findSelectionIndex(val) {
769
- let index = -1;
770
- if (this.value) {
771
- for (let i = 0; i < this.value.length; i++) {
772
- if (ObjectUtils.equals(this.value[i], val, this.dataKey)) {
773
- index = i;
774
- break;
1157
+ onPageDownKey(event) {
1158
+ this.scrollInView(this.visibleOptions().length - 1);
1159
+ event.preventDefault();
1160
+ }
1161
+ onPageUpKey(event) {
1162
+ this.scrollInView(0);
1163
+ event.preventDefault();
1164
+ }
1165
+ onEnterKey(event) {
1166
+ if (!this.overlayVisible) {
1167
+ this.onArrowDownKey(event);
1168
+ }
1169
+ else {
1170
+ if (this.focusedOptionIndex() !== -1) {
1171
+ if (event.shiftKey) {
1172
+ this.onOptionSelectRange(event, this.focusedOptionIndex());
1173
+ }
1174
+ else {
1175
+ this.onOptionSelect({ originalEvent: event, option: this.visibleOptions()[this.focusedOptionIndex()] });
775
1176
  }
776
1177
  }
777
1178
  }
778
- return index;
1179
+ event.preventDefault();
779
1180
  }
780
- get toggleAllDisabled() {
781
- let optionsToRender = this.optionsToRender;
782
- if (!optionsToRender || optionsToRender.length === 0) {
783
- return true;
1181
+ onEscapeKey(event) {
1182
+ this.overlayVisible && this.hide(true);
1183
+ event.preventDefault();
1184
+ }
1185
+ onDeleteKey(event) {
1186
+ if (this.showClear) {
1187
+ this.clear(event);
1188
+ event.preventDefault();
784
1189
  }
785
- else {
786
- for (let option of optionsToRender) {
787
- if (!this.isOptionDisabled(option))
788
- return false;
1190
+ }
1191
+ onTabKey(event, pressedInInputText = false) {
1192
+ if (!pressedInInputText) {
1193
+ if (this.overlayVisible && this.hasFocusableElements()) {
1194
+ DomHandler.focus(event.shiftKey ? this.lastHiddenFocusableElementOnOverlay.nativeElement : this.firstHiddenFocusableElementOnOverlay.nativeElement);
1195
+ event.preventDefault();
1196
+ }
1197
+ else {
1198
+ if (this.focusedOptionIndex() !== -1) {
1199
+ this.onOptionSelect({ originalEvent: event, option: this.visibleOptions()[this.focusedOptionIndex()] });
1200
+ }
1201
+ this.overlayVisible && this.hide(this.filter);
789
1202
  }
790
- return true;
791
1203
  }
792
1204
  }
793
- toggleAll(event) {
794
- if (this.disabled || this.toggleAllDisabled || this.readonly) {
1205
+ onShiftKey() {
1206
+ this.startRangeIndex.set(this.focusedOptionIndex());
1207
+ }
1208
+ onContainerClick(event) {
1209
+ if (this.disabled || this.readonly || event.target.isSameNode(this.focusInputViewChild?.nativeElement)) {
795
1210
  return;
796
1211
  }
797
- let allChecked = this.allChecked;
798
- if (allChecked)
799
- this.uncheckAll();
800
- else
801
- this.checkAll();
802
- this.onModelChange(this.value);
1212
+ this.focusInputViewChild?.nativeElement.focus({ preventScroll: true });
1213
+ if (event.target.tagName === 'INPUT' || event.target.getAttribute('data-pc-section') === 'clearicon' || event.target.closest('[data-pc-section="clearicon"]')) {
1214
+ return;
1215
+ }
1216
+ else if (!this.overlayViewChild || !this.overlayViewChild.el.nativeElement.contains(event.target)) {
1217
+ this.overlayVisible ? this.hide(true) : this.show(true);
1218
+ }
1219
+ this.onClick.emit(event);
1220
+ this.cd.detectChanges();
1221
+ }
1222
+ onFirstHiddenFocus(event) {
1223
+ const focusableEl = event.relatedTarget === this.focusInputViewChild?.nativeElement
1224
+ ? DomHandler.getFirstFocusableElement(this.overlayViewChild?.overlayViewChild?.nativeElement, ':not([data-p-hidden-focusable="true"])')
1225
+ : this.focusInputViewChild?.nativeElement;
1226
+ DomHandler.focus(focusableEl);
1227
+ }
1228
+ onInputFocus(event) {
1229
+ this.focused = true;
1230
+ const focusedOptionIndex = this.focusedOptionIndex() !== -1 ? this.focusedOptionIndex() : this.overlayVisible && this.autoOptionFocus ? this.findFirstFocusedOptionIndex() : -1;
1231
+ this.focusedOptionIndex.set(focusedOptionIndex);
1232
+ this.overlayVisible && this.scrollInView(this.focusedOptionIndex());
1233
+ this.onFocus.emit({ originalEvent: event });
1234
+ }
1235
+ onInputBlur(event) {
1236
+ this.focused = false;
1237
+ this.onBlur.emit({ originalEvent: event });
1238
+ if (!this.preventModelTouched) {
1239
+ this.onModelTouched();
1240
+ }
1241
+ this.preventModelTouched = false;
1242
+ }
1243
+ onFilterInputChange(event) {
1244
+ let value = event.target.value?.trim();
1245
+ this._filterValue.set(value);
1246
+ this.focusedOptionIndex.set(-1);
1247
+ this.onFilter.emit({ originalEvent: event, filter: this._filterValue() });
1248
+ !this.virtualScrollerDisabled && this.scroller.scrollToIndex(0);
1249
+ }
1250
+ onLastHiddenFocus(event) {
1251
+ const focusableEl = event.relatedTarget === this.focusInputViewChild?.nativeElement
1252
+ ? DomHandler.getLastFocusableElement(this.overlayViewChild?.overlayViewChild?.nativeElement, ':not([data-p-hidden-focusable="true"])')
1253
+ : this.focusInputViewChild?.nativeElement;
1254
+ DomHandler.focus(focusableEl);
1255
+ }
1256
+ onOptionMouseEnter(event, index) {
1257
+ if (this.focusOnHover) {
1258
+ this.changeFocusedOptionIndex(event, index);
1259
+ }
1260
+ }
1261
+ onHeaderCheckboxKeyDown(event) {
1262
+ if (this.disabled) {
1263
+ event.preventDefault();
1264
+ return;
1265
+ }
1266
+ switch (event.code) {
1267
+ case 'Space':
1268
+ this.onToggleAll(event);
1269
+ break;
1270
+ case 'Enter':
1271
+ this.onToggleAll(event);
1272
+ break;
1273
+ default:
1274
+ break;
1275
+ }
1276
+ }
1277
+ onFilterBlur(event) {
1278
+ this.focusedOptionIndex.set(-1);
1279
+ }
1280
+ onHeaderCheckboxFocus() {
1281
+ this.headerCheckboxFocus = true;
1282
+ }
1283
+ onHeaderCheckboxBlur() {
1284
+ this.headerCheckboxFocus = false;
1285
+ }
1286
+ onToggleAll(event) {
1287
+ if (this.disabled || this.readonly) {
1288
+ return;
1289
+ }
1290
+ DomHandler.focus(this.headerCheckboxViewChild.nativeElement);
1291
+ const value = this.allSelected()
1292
+ ? []
1293
+ : this.visibleOptions()
1294
+ .filter((option) => this.isValidOption(option))
1295
+ .map((option) => this.getOptionValue(option));
1296
+ this.updateModel(value, event);
803
1297
  this.onChange.emit({ originalEvent: event, value: this.value });
804
- this.updateFilledState();
805
- this.updateLabel();
1298
+ this.headerCheckboxFocus = true;
806
1299
  event.preventDefault();
807
1300
  event.stopPropagation();
808
1301
  }
809
- checkAll() {
810
- let optionsToRender = this.optionsToRender;
811
- let val = [];
812
- optionsToRender.forEach((opt) => {
813
- if (!this.group) {
814
- let optionDisabled = this.isOptionDisabled(opt);
815
- if (!optionDisabled || (optionDisabled && this.isSelected(opt))) {
816
- val.push(this.getOptionValue(opt));
817
- }
818
- }
819
- else {
820
- let subOptions = this.getOptionGroupChildren(opt);
821
- if (subOptions) {
822
- subOptions.forEach((option) => {
823
- let optionDisabled = this.isOptionDisabled(option);
824
- if (!optionDisabled || (optionDisabled && this.isSelected(option))) {
825
- val.push(this.getOptionValue(option));
826
- }
827
- });
828
- }
829
- }
830
- });
831
- this.value = val;
832
- }
833
- uncheckAll() {
834
- let optionsToRender = this.optionsToRender;
835
- let val = [];
836
- optionsToRender.forEach((opt) => {
837
- if (!this.group) {
838
- let optionDisabled = this.isOptionDisabled(opt);
839
- if (optionDisabled && this.isSelected(opt)) {
840
- val.push(this.getOptionValue(opt));
841
- }
1302
+ changeFocusedOptionIndex(event, index) {
1303
+ if (this.focusedOptionIndex() !== index) {
1304
+ this.focusedOptionIndex.set(index);
1305
+ this.scrollInView();
1306
+ }
1307
+ }
1308
+ get virtualScrollerDisabled() {
1309
+ return !this.virtualScroll;
1310
+ }
1311
+ scrollInView(index = -1) {
1312
+ const id = index !== -1 ? `${this.id}_${index}` : this.focusedOptionId;
1313
+ if (this.itemsViewChild && this.itemsViewChild.nativeElement) {
1314
+ const element = DomHandler.findSingle(this.itemsViewChild.nativeElement, `li[id="${id}"]`);
1315
+ if (element) {
1316
+ element.scrollIntoView && element.scrollIntoView({ block: 'nearest', inline: 'nearest' });
842
1317
  }
843
- else {
844
- if (opt.items) {
845
- opt.items.forEach((option) => {
846
- let optionDisabled = this.isOptionDisabled(option);
847
- if (optionDisabled && this.isSelected(option)) {
848
- val.push(this.getOptionValue(option));
849
- }
850
- });
851
- }
1318
+ else if (!this.virtualScrollerDisabled) {
1319
+ setTimeout(() => {
1320
+ this.virtualScroll && this.scroller?.scrollToIndex(index !== -1 ? index : this.focusedOptionIndex());
1321
+ }, 0);
852
1322
  }
853
- });
854
- this.value = val;
1323
+ }
855
1324
  }
856
- show() {
857
- if (!this.overlayVisible) {
858
- this.overlayVisible = true;
859
- this.preventDocumentDefault = true;
860
- this.cd.markForCheck();
1325
+ get focusedOptionId() {
1326
+ return this.focusedOptionIndex() !== -1 ? `${this.id}_${this.focusedOptionIndex()}` : null;
1327
+ }
1328
+ checkSelectionLimit() {
1329
+ if (this.selectionLimit && this.value && this.value.length === this.selectionLimit) {
1330
+ this.maxSelectionLimitReached = true;
861
1331
  }
1332
+ else {
1333
+ this.maxSelectionLimitReached = false;
1334
+ }
1335
+ }
1336
+ writeValue(value) {
1337
+ this.value = this.modelValue();
1338
+ this.updateModel(this.value);
1339
+ this.checkSelectionLimit();
1340
+ this.cd.markForCheck();
1341
+ }
1342
+ registerOnChange(fn) {
1343
+ this.onModelChange = fn;
1344
+ }
1345
+ registerOnTouched(fn) {
1346
+ this.onModelTouched = fn;
1347
+ }
1348
+ setDisabledState(val) {
1349
+ this.disabled = val;
1350
+ this.cd.markForCheck();
1351
+ }
1352
+ allSelected() {
1353
+ const allSelected = this.visibleOptions().length > 0 && this.visibleOptions().every((option) => this.isOptionGroup(option) || this.isOptionDisabled(option) || this.isSelected(option));
1354
+ return ObjectUtils.isNotEmpty(this.visibleOptions()) && allSelected;
1355
+ }
1356
+ /**
1357
+ * Displays the panel.
1358
+ * @group Method
1359
+ */
1360
+ show(isFocus) {
1361
+ this.overlayVisible = true;
1362
+ const focusedOptionIndex = this.focusedOptionIndex() !== -1 ? this.focusedOptionIndex() : this.autoOptionFocus ? this.findFirstFocusedOptionIndex() : -1;
1363
+ this.focusedOptionIndex.set(focusedOptionIndex);
1364
+ if (isFocus) {
1365
+ DomHandler.focus(this.focusInputViewChild?.nativeElement);
1366
+ }
1367
+ this.cd.markForCheck();
1368
+ }
1369
+ /**
1370
+ * Hides the panel.
1371
+ * @group Method
1372
+ */
1373
+ hide(isFocus) {
1374
+ this.overlayVisible = false;
1375
+ this.focusedOptionIndex.set(-1);
1376
+ if (this.filter && this.resetFilterOnHide) {
1377
+ this.resetFilter();
1378
+ }
1379
+ isFocus && DomHandler.focus(this.focusInputViewChild?.nativeElement);
1380
+ this.onPanelHide.emit();
1381
+ this.cd.markForCheck();
862
1382
  }
863
1383
  onOverlayAnimationStart(event) {
864
1384
  switch (event.toState) {
865
1385
  case 'visible':
1386
+ this.itemsWrapper = DomHandler.findSingle(this.overlayViewChild?.overlayViewChild?.nativeElement, this.virtualScroll ? '.p-scroller' : '.p-multiselect-items-wrapper');
866
1387
  this.virtualScroll && this.scroller?.setContentEl(this.itemsViewChild?.nativeElement);
867
- if (this.filterInputChild && this.filterInputChild.nativeElement) {
868
- this.preventModelTouched = true;
869
- if (this.autofocusFilter) {
870
- this.filterInputChild.nativeElement.focus();
1388
+ if (this._options() && this._options().length) {
1389
+ if (this.virtualScroll) {
1390
+ const selectedIndex = ObjectUtils.isNotEmpty(this.modelValue()) ? this.focusedOptionIndex() : -1;
1391
+ if (selectedIndex !== -1) {
1392
+ this.scroller?.scrollToIndex(0);
1393
+ }
1394
+ }
1395
+ else {
1396
+ let selectedListItem = DomHandler.findSingle(this.itemsWrapper, '.p-multiselect-item.p-highlight');
1397
+ if (selectedListItem) {
1398
+ selectedListItem.scrollIntoView({ block: 'nearest', inline: 'center' });
1399
+ }
871
1400
  }
1401
+ this.onPanelShow.emit();
872
1402
  }
873
- this.onPanelShow.emit();
874
- break;
875
1403
  case 'void':
1404
+ this.itemsWrapper = null;
876
1405
  this.onModelTouched();
877
1406
  break;
878
1407
  }
879
1408
  }
880
- hide() {
881
- this.overlayVisible = false;
882
- if (this.resetFilterOnHide) {
883
- this.resetFilter();
884
- }
885
- this.onPanelHide.emit();
886
- this.cd.markForCheck();
887
- }
888
1409
  resetFilter() {
889
1410
  if (this.filterInputChild && this.filterInputChild.nativeElement) {
890
1411
  this.filterInputChild.nativeElement.value = '';
@@ -899,80 +1420,15 @@ class MultiSelect {
899
1420
  }
900
1421
  clear(event) {
901
1422
  this.value = null;
902
- this.updateLabel();
903
- this.updateFilledState();
904
1423
  this.checkSelectionLimit();
1424
+ this.updateModel(null, event);
905
1425
  this.onClear.emit();
906
- this.onModelChange(this.value);
907
1426
  event.stopPropagation();
908
1427
  }
909
- onMouseclick(event, input) {
910
- if (this.disabled || this.readonly || event.target.isSameNode(this.accessibleViewChild?.nativeElement)) {
911
- return;
912
- }
913
- this.onClick.emit(event);
914
- if (!this.overlayViewChild?.el?.nativeElement?.contains(event.target) && !DomHandler.hasClass(event.target, 'p-multiselect-token-icon')) {
915
- if (this.overlayVisible) {
916
- this.hide();
917
- }
918
- else {
919
- this.show();
920
- }
921
- input.focus();
922
- }
923
- this.cd.detectChanges();
924
- }
925
- removeChip(chip, event) {
926
- this.value = this.value.filter((val) => !ObjectUtils.equals(val, chip, this.dataKey));
927
- this.onModelChange(this.value);
928
- this.checkSelectionLimit();
929
- this.onChange.emit({ originalEvent: event, value: this.value, itemValue: chip });
930
- this.updateLabel();
931
- this.updateFilledState();
932
- }
933
- onInputFocus(event) {
934
- this.focus = true;
935
- this.onFocus.emit({ originalEvent: event });
936
- }
937
- onInputBlur(event) {
938
- this.focus = false;
939
- this.onBlur.emit({ originalEvent: event });
940
- if (!this.preventModelTouched) {
941
- this.onModelTouched();
942
- }
943
- this.preventModelTouched = false;
944
- }
945
- onOptionKeydown(event) {
946
- if (this.readonly) {
947
- return;
948
- }
949
- switch (event.originalEvent.which) {
950
- //down
951
- case 40:
952
- var nextItem = this.findNextItem(event.originalEvent.target.parentElement);
953
- if (nextItem) {
954
- nextItem.focus();
955
- }
956
- event.originalEvent.preventDefault();
957
- break;
958
- //up
959
- case 38:
960
- var prevItem = this.findPrevItem(event.originalEvent.target.parentElement);
961
- if (prevItem) {
962
- prevItem.focus();
963
- }
964
- event.originalEvent.preventDefault();
965
- break;
966
- //enter
967
- case 13:
968
- this.onOptionClick(event);
969
- event.originalEvent.preventDefault();
970
- break;
971
- case 27:
972
- case 9:
973
- this.hide();
974
- break;
975
- }
1428
+ removeOption(optionValue, event) {
1429
+ let value = this.modelValue().filter((val) => !ObjectUtils.equals(val, optionValue, this.equalityKey()));
1430
+ this.updateModel(value, event);
1431
+ event && event.stopPropagation();
976
1432
  }
977
1433
  findNextItem(item) {
978
1434
  let nextItem = item.nextElementSibling;
@@ -988,161 +1444,63 @@ class MultiSelect {
988
1444
  else
989
1445
  return null;
990
1446
  }
991
- onKeydown(event) {
992
- switch (event.which) {
993
- //down
994
- case 40:
995
- if (!this.overlayVisible && event.altKey) {
996
- this.show();
997
- event.preventDefault();
998
- }
999
- break;
1000
- //space
1001
- case 32:
1002
- if (!this.overlayVisible) {
1003
- this.show();
1004
- event.preventDefault();
1005
- }
1006
- break;
1007
- //escape
1008
- case 27:
1009
- this.hide();
1010
- break;
1011
- }
1012
- }
1013
- updateLabel() {
1014
- if (this.value && this.options && this.value.length && this.displaySelectedLabel) {
1015
- let label = '';
1016
- for (let i = 0; i < this.value.length; i++) {
1017
- let itemLabel = this.findLabelByValue(this.value[i]);
1018
- if (itemLabel) {
1019
- if (label.length > 0) {
1020
- label = label + ', ';
1021
- }
1022
- label = label + itemLabel;
1023
- }
1024
- }
1025
- if (this.value.length <= this.maxSelectedLabels || this.selectedItemsLabel === 'ellipsis') {
1026
- this.valuesAsString = label;
1027
- }
1028
- else {
1029
- let pattern = /{(.*?)}/;
1030
- if (pattern.test(this.selectedItemsLabel)) {
1031
- this.valuesAsString = this.selectedItemsLabel.replace(this.selectedItemsLabel.match(pattern)[0], this.value.length + '');
1032
- }
1033
- else {
1034
- this.valuesAsString = this.selectedItemsLabel;
1035
- }
1036
- }
1447
+ findNextOptionIndex(index) {
1448
+ const matchedOptionIndex = index < this.visibleOptions().length - 1
1449
+ ? this.visibleOptions()
1450
+ .slice(index + 1)
1451
+ .findIndex((option) => this.isValidOption(option))
1452
+ : -1;
1453
+ return matchedOptionIndex > -1 ? matchedOptionIndex + index + 1 : index;
1454
+ }
1455
+ findPrevOptionIndex(index) {
1456
+ const matchedOptionIndex = index > 0 ? ObjectUtils.findLastIndex(this.visibleOptions().slice(0, index), (option) => this.isValidOption(option)) : -1;
1457
+ return matchedOptionIndex > -1 ? matchedOptionIndex : index;
1458
+ }
1459
+ findLastSelectedOptionIndex() {
1460
+ return this.hasSelectedOption() ? ObjectUtils.findLastIndex(this.visibleOptions(), (option) => this.isValidSelectedOption(option)) : -1;
1461
+ }
1462
+ findLastFocusedOptionIndex() {
1463
+ const selectedIndex = this.findLastSelectedOptionIndex();
1464
+ return selectedIndex < 0 ? this.findLastOptionIndex() : selectedIndex;
1465
+ }
1466
+ findLastOptionIndex() {
1467
+ return ObjectUtils.findLastIndex(this.visibleOptions(), (option) => this.isValidOption(option));
1468
+ }
1469
+ searchOptions(event, char) {
1470
+ this.searchValue = (this.searchValue || '') + char;
1471
+ let optionIndex = -1;
1472
+ let matched = false;
1473
+ if (this.focusedOptionIndex() !== -1) {
1474
+ optionIndex = this.visibleOptions()
1475
+ .slice(this.focusedOptionIndex())
1476
+ .findIndex((option) => this.isOptionMatched(option));
1477
+ optionIndex =
1478
+ optionIndex === -1
1479
+ ? this.visibleOptions()
1480
+ .slice(0, this.focusedOptionIndex())
1481
+ .findIndex((option) => this.isOptionMatched(option))
1482
+ : optionIndex + this.focusedOptionIndex();
1037
1483
  }
1038
1484
  else {
1039
- this.valuesAsString = this.placeholder || this.defaultLabel;
1485
+ optionIndex = this.visibleOptions().findIndex((option) => this.isOptionMatched(option));
1040
1486
  }
1041
- }
1042
- findLabelByValue(val) {
1043
- if (this.group) {
1044
- let label = null;
1045
- for (let i = 0; i < this.options.length; i++) {
1046
- let subOptions = this.getOptionGroupChildren(this.options[i]);
1047
- if (subOptions) {
1048
- label = this.searchLabelByValue(val, subOptions);
1049
- if (label) {
1050
- break;
1051
- }
1052
- }
1053
- }
1054
- return label;
1487
+ if (optionIndex !== -1) {
1488
+ matched = true;
1055
1489
  }
1056
- else {
1057
- return this.searchLabelByValue(val, this.options);
1490
+ if (optionIndex === -1 && this.focusedOptionIndex() === -1) {
1491
+ optionIndex = this.findFirstFocusedOptionIndex();
1058
1492
  }
1059
- }
1060
- searchLabelByValue(val, options) {
1061
- let label = null;
1062
- for (let i = 0; i < options.length; i++) {
1063
- let option = options[i];
1064
- let optionValue = this.getOptionValue(option);
1065
- if ((val == null && optionValue == null) || ObjectUtils.equals(val, optionValue, this.dataKey)) {
1066
- label = this.getOptionLabel(option);
1067
- break;
1068
- }
1069
- }
1070
- return label;
1071
- }
1072
- get allChecked() {
1073
- let optionsToRender = this.optionsToRender;
1074
- if (!optionsToRender || optionsToRender.length === 0) {
1075
- return false;
1493
+ if (optionIndex !== -1) {
1494
+ this.changeFocusedOptionIndex(event, optionIndex);
1076
1495
  }
1077
- else {
1078
- let selectedDisabledItemsLength = 0;
1079
- let unselectedDisabledItemsLength = 0;
1080
- let selectedEnabledItemsLength = 0;
1081
- let visibleOptionsLength = this.group ? 0 : this.optionsToRender.length;
1082
- for (let option of optionsToRender) {
1083
- if (!this.group) {
1084
- let disabled = this.isOptionDisabled(option);
1085
- let selected = this.isSelected(option);
1086
- if (disabled) {
1087
- if (selected)
1088
- selectedDisabledItemsLength++;
1089
- else
1090
- unselectedDisabledItemsLength++;
1091
- }
1092
- else {
1093
- if (selected)
1094
- selectedEnabledItemsLength++;
1095
- else
1096
- return false;
1097
- }
1098
- }
1099
- else {
1100
- for (let opt of this.getOptionGroupChildren(option)) {
1101
- let disabled = this.isOptionDisabled(opt);
1102
- let selected = this.isSelected(opt);
1103
- if (disabled) {
1104
- if (selected)
1105
- selectedDisabledItemsLength++;
1106
- else
1107
- unselectedDisabledItemsLength++;
1108
- }
1109
- else {
1110
- if (selected)
1111
- selectedEnabledItemsLength++;
1112
- else {
1113
- return false;
1114
- }
1115
- }
1116
- visibleOptionsLength++;
1117
- }
1118
- }
1119
- }
1120
- return (visibleOptionsLength === selectedDisabledItemsLength ||
1121
- visibleOptionsLength === selectedEnabledItemsLength ||
1122
- (selectedEnabledItemsLength && visibleOptionsLength === selectedEnabledItemsLength + unselectedDisabledItemsLength + selectedDisabledItemsLength));
1496
+ if (this.searchTimeout) {
1497
+ clearTimeout(this.searchTimeout);
1123
1498
  }
1124
- }
1125
- get optionsToRender() {
1126
- return (this._filteredOptions || this.options);
1127
- }
1128
- get emptyMessageLabel() {
1129
- return this.emptyMessage || this.config.getTranslation(TranslationKeys.EMPTY_MESSAGE);
1130
- }
1131
- get emptyFilterMessageLabel() {
1132
- return this.emptyFilterMessage || this.config.getTranslation(TranslationKeys.EMPTY_FILTER_MESSAGE);
1133
- }
1134
- hasFilter() {
1135
- return this._filterValue && this._filterValue.trim().length > 0;
1136
- }
1137
- isEmpty() {
1138
- return !this.optionsToRender || (this.optionsToRender && this.optionsToRender.length === 0);
1139
- }
1140
- onFilterInputChange(event) {
1141
- this._filterValue = event.target.value;
1142
- this.activateFilter();
1143
- this.filtered = true;
1144
- this.onFilter.emit({ originalEvent: event, filter: this._filterValue });
1145
- this.cd.detectChanges();
1499
+ this.searchTimeout = setTimeout(() => {
1500
+ this.searchValue = '';
1501
+ this.searchTimeout = null;
1502
+ }, 500);
1503
+ return matched;
1146
1504
  }
1147
1505
  activateFilter() {
1148
1506
  if (this.hasFilter() && this._options) {
@@ -1165,75 +1523,69 @@ class MultiSelect {
1165
1523
  this._filteredOptions = null;
1166
1524
  }
1167
1525
  }
1168
- onHeaderCheckboxFocus() {
1169
- this.headerCheckboxFocus = true;
1526
+ hasFocusableElements() {
1527
+ return DomHandler.getFocusableElements(this.overlayViewChild.overlayViewChild.nativeElement, ':not([data-p-hidden-focusable="true"])').length > 0;
1170
1528
  }
1171
- onHeaderCheckboxBlur() {
1172
- this.headerCheckboxFocus = false;
1529
+ hasFilter() {
1530
+ return this._filterValue() && this._filterValue().trim().length > 0;
1173
1531
  }
1174
1532
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.2", ngImport: i0, type: MultiSelect, deps: [{ token: i0.ElementRef }, { token: i0.Renderer2 }, { token: i0.ChangeDetectorRef }, { token: i0.NgZone }, { token: i3.FilterService }, { token: i3.PrimeNGConfig }, { token: i3.OverlayService }], target: i0.ɵɵFactoryTarget.Component });
1175
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.0.2", type: MultiSelect, selector: "p-multiSelect", inputs: { style: "style", styleClass: "styleClass", panelStyle: "panelStyle", panelStyleClass: "panelStyleClass", inputId: "inputId", disabled: "disabled", readonly: "readonly", group: "group", filter: "filter", filterPlaceHolder: "filterPlaceHolder", filterLocale: "filterLocale", overlayVisible: "overlayVisible", tabindex: "tabindex", appendTo: "appendTo", dataKey: "dataKey", name: "name", label: "label", ariaLabelledBy: "ariaLabelledBy", displaySelectedLabel: "displaySelectedLabel", maxSelectedLabels: "maxSelectedLabels", selectedItemsLabel: "selectedItemsLabel", showToggleAll: "showToggleAll", emptyFilterMessage: "emptyFilterMessage", emptyMessage: "emptyMessage", resetFilterOnHide: "resetFilterOnHide", dropdownIcon: "dropdownIcon", optionLabel: "optionLabel", optionValue: "optionValue", optionDisabled: "optionDisabled", optionGroupLabel: "optionGroupLabel", optionGroupChildren: "optionGroupChildren", showHeader: "showHeader", filterBy: "filterBy", scrollHeight: "scrollHeight", lazy: "lazy", virtualScroll: "virtualScroll", virtualScrollItemSize: "virtualScrollItemSize", virtualScrollOptions: "virtualScrollOptions", overlayOptions: "overlayOptions", ariaFilterLabel: "ariaFilterLabel", filterMatchMode: "filterMatchMode", tooltip: "tooltip", tooltipPosition: "tooltipPosition", tooltipPositionStyle: "tooltipPositionStyle", tooltipStyleClass: "tooltipStyleClass", autofocusFilter: "autofocusFilter", display: "display", autocomplete: "autocomplete", showClear: "showClear", autoZIndex: "autoZIndex", baseZIndex: "baseZIndex", showTransitionOptions: "showTransitionOptions", hideTransitionOptions: "hideTransitionOptions", defaultLabel: "defaultLabel", placeholder: "placeholder", options: "options", filterValue: "filterValue", itemSize: "itemSize", selectionLimit: "selectionLimit" }, outputs: { onChange: "onChange", onFilter: "onFilter", onFocus: "onFocus", onBlur: "onBlur", onClick: "onClick", onClear: "onClear", onPanelShow: "onPanelShow", onPanelHide: "onPanelHide", onLazyLoad: "onLazyLoad", onRemove: "onRemove" }, host: { properties: { "class.p-inputwrapper-filled": "filled", "class.p-inputwrapper-focus": "focus || overlayVisible", "class.p-multiselect-clearable": "showClear && !disabled" }, classAttribute: "p-element p-inputwrapper" }, providers: [MULTISELECT_VALUE_ACCESSOR], queries: [{ propertyName: "footerFacet", first: true, predicate: Footer, descendants: true }, { propertyName: "headerFacet", first: true, predicate: Header, descendants: true }, { propertyName: "templates", predicate: PrimeTemplate }], viewQueries: [{ propertyName: "containerViewChild", first: true, predicate: ["container"], descendants: true }, { propertyName: "overlayViewChild", first: true, predicate: ["overlay"], descendants: true }, { propertyName: "filterInputChild", first: true, predicate: ["filterInput"], descendants: true }, { propertyName: "accessibleViewChild", first: true, predicate: ["in"], descendants: true }, { propertyName: "itemsViewChild", first: true, predicate: ["items"], descendants: true }, { propertyName: "scroller", first: true, predicate: ["scroller"], descendants: true }], ngImport: i0, template: `
1176
- <div
1177
- #container
1178
- [ngClass]="{ 'p-multiselect p-component': true, 'p-multiselect-open': overlayVisible, 'p-multiselect-chip': display === 'chip', 'p-focus': focus, 'p-disabled': disabled }"
1179
- [ngStyle]="style"
1180
- [class]="styleClass"
1181
- (click)="onMouseclick($event, in)"
1182
- >
1183
- <div class="p-hidden-accessible">
1533
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.0.2", type: MultiSelect, selector: "p-multiSelect", inputs: { id: "id", ariaLabel: "ariaLabel", style: "style", styleClass: "styleClass", panelStyle: "panelStyle", panelStyleClass: "panelStyleClass", inputId: "inputId", disabled: "disabled", readonly: "readonly", group: "group", filter: "filter", filterPlaceHolder: "filterPlaceHolder", filterLocale: "filterLocale", overlayVisible: "overlayVisible", tabindex: "tabindex", appendTo: "appendTo", dataKey: "dataKey", name: "name", ariaLabelledBy: "ariaLabelledBy", displaySelectedLabel: "displaySelectedLabel", maxSelectedLabels: "maxSelectedLabels", selectionLimit: "selectionLimit", selectedItemsLabel: "selectedItemsLabel", showToggleAll: "showToggleAll", emptyFilterMessage: "emptyFilterMessage", emptyMessage: "emptyMessage", resetFilterOnHide: "resetFilterOnHide", dropdownIcon: "dropdownIcon", optionLabel: "optionLabel", optionValue: "optionValue", optionDisabled: "optionDisabled", optionGroupLabel: "optionGroupLabel", optionGroupChildren: "optionGroupChildren", showHeader: "showHeader", filterBy: "filterBy", scrollHeight: "scrollHeight", lazy: "lazy", virtualScroll: "virtualScroll", virtualScrollItemSize: "virtualScrollItemSize", virtualScrollOptions: "virtualScrollOptions", overlayOptions: "overlayOptions", ariaFilterLabel: "ariaFilterLabel", filterMatchMode: "filterMatchMode", tooltip: "tooltip", tooltipPosition: "tooltipPosition", tooltipPositionStyle: "tooltipPositionStyle", tooltipStyleClass: "tooltipStyleClass", autofocusFilter: "autofocusFilter", display: "display", autocomplete: "autocomplete", showClear: "showClear", autoZIndex: "autoZIndex", baseZIndex: "baseZIndex", showTransitionOptions: "showTransitionOptions", hideTransitionOptions: "hideTransitionOptions", defaultLabel: "defaultLabel", placeholder: "placeholder", options: "options", filterValue: "filterValue", itemSize: "itemSize", focusOnHover: "focusOnHover", filterFields: "filterFields", selectOnFocus: "selectOnFocus", autoOptionFocus: "autoOptionFocus" }, outputs: { onChange: "onChange", onFilter: "onFilter", onFocus: "onFocus", onBlur: "onBlur", onClick: "onClick", onClear: "onClear", onPanelShow: "onPanelShow", onPanelHide: "onPanelHide", onLazyLoad: "onLazyLoad", onRemove: "onRemove" }, host: { properties: { "class.p-inputwrapper-filled": "filled", "class.p-inputwrapper-focus": "focused || overlayVisible" }, classAttribute: "p-element p-inputwrapper" }, providers: [MULTISELECT_VALUE_ACCESSOR], queries: [{ propertyName: "footerFacet", first: true, predicate: Footer, descendants: true }, { propertyName: "headerFacet", first: true, predicate: Header, descendants: true }, { propertyName: "templates", predicate: PrimeTemplate }], viewQueries: [{ propertyName: "containerViewChild", first: true, predicate: ["container"], descendants: true }, { propertyName: "overlayViewChild", first: true, predicate: ["overlay"], descendants: true }, { propertyName: "filterInputChild", first: true, predicate: ["filterInput"], descendants: true }, { propertyName: "focusInputViewChild", first: true, predicate: ["focusInput"], descendants: true }, { propertyName: "itemsViewChild", first: true, predicate: ["items"], descendants: true }, { propertyName: "scroller", first: true, predicate: ["scroller"], descendants: true }, { propertyName: "lastHiddenFocusableElementOnOverlay", first: true, predicate: ["lastHiddenFocusableEl"], descendants: true }, { propertyName: "firstHiddenFocusableElementOnOverlay", first: true, predicate: ["firstHiddenFocusableEl"], descendants: true }, { propertyName: "headerCheckboxViewChild", first: true, predicate: ["headerCheckbox"], descendants: true }], ngImport: i0, template: `
1534
+ <div #container [attr.id]="id" [ngClass]="containerClass" [ngStyle]="style" [class]="styleClass" (click)="onContainerClick($event)">
1535
+ <div class="p-hidden-accessible" [attr.data-p-hidden-accessible]="true">
1184
1536
  <input
1185
- #in
1186
- type="text"
1187
- [attr.label]="label"
1188
- readonly="readonly"
1537
+ #focusInput
1538
+ [pTooltip]="tooltip"
1539
+ [tooltipPosition]="tooltipPosition"
1540
+ [positionStyle]="tooltipPositionStyle"
1541
+ [tooltipStyleClass]="tooltipStyleClass"
1542
+ [attr.aria-disabled]="disabled"
1189
1543
  [attr.id]="inputId"
1190
- [attr.name]="name"
1544
+ role="combobox"
1545
+ [attr.aria-label]="ariaLabel"
1546
+ [attr.aria-labelledby]="ariaLabelledBy"
1547
+ [attr.aria-haspopup]="'listbox'"
1548
+ [attr.aria-expanded]="overlayVisible"
1549
+ [attr.aria-controls]="id + '_list'"
1550
+ [attr.tabindex]="!disabled ? tabindex : -1"
1551
+ [attr.aria-activedescendant]="focused ? focusedOptionId : undefined"
1191
1552
  (focus)="onInputFocus($event)"
1192
1553
  (blur)="onInputBlur($event)"
1193
- [disabled]="disabled"
1194
- [attr.tabindex]="tabindex"
1195
- (keydown)="onKeydown($event)"
1196
- aria-haspopup="listbox"
1197
- [attr.aria-expanded]="overlayVisible"
1198
- [attr.aria-labelledby]="ariaLabelledBy"
1199
- role="listbox"
1554
+ (keydown)="onKeyDown($event)"
1200
1555
  />
1201
1556
  </div>
1202
1557
  <div class="p-multiselect-label-container" [pTooltip]="tooltip" [tooltipPosition]="tooltipPosition" [positionStyle]="tooltipPositionStyle" [tooltipStyleClass]="tooltipStyleClass">
1203
- <div
1204
- class="p-multiselect-label"
1205
- [ngClass]="{ 'p-placeholder': valuesAsString === (defaultLabel || placeholder), 'p-multiselect-label-empty': (valuesAsString == null || valuesAsString.length === 0) && (placeholder == null || placeholder.length === 0) }"
1206
- >
1558
+ <div [ngClass]="labelClass">
1207
1559
  <ng-container *ngIf="!selectedItemsTemplate">
1208
- <ng-container *ngIf="display === 'comma'">{{ valuesAsString || 'empty' }}</ng-container>
1560
+ <ng-container *ngIf="display === 'comma'">{{ label() || 'empty' }}</ng-container>
1209
1561
  <ng-container *ngIf="display === 'chip'">
1210
- <div #token *ngFor="let item of value; let i = index" class="p-multiselect-token">
1211
- <span class="p-multiselect-token-label">{{ findLabelByValue(item) }}</span>
1562
+ <div #token *ngFor="let item of chipSelectedItems(); let i = index" class="p-multiselect-token">
1563
+ <span class="p-multiselect-token-label">{{ getLabelByValue(item) }}</span>
1212
1564
  <ng-container *ngIf="!disabled">
1213
- <TimesCircleIcon *ngIf="!removeTokenIconTemplate" [styleClass]="'p-multiselect-token-icon'" (click)="removeChip(item, event)" />
1214
- <span *ngIf="removeTokenIconTemplate" class="p-multiselect-token-icon" (click)="removeChip(item, event)">
1565
+ <TimesCircleIcon *ngIf="!removeTokenIconTemplate" [styleClass]="'p-multiselect-token-icon'" (click)="removeOption(item, event)" [attr.data-pc-section]="'clearicon'" [attr.aria-hidden]="true" />
1566
+ <span *ngIf="removeTokenIconTemplate" class="p-multiselect-token-icon" (click)="removeOption(item, event)" [attr.data-pc-section]="'clearicon'" [attr.aria-hidden]="true">
1215
1567
  <ng-container *ngTemplateOutlet="removeTokenIconTemplate"></ng-container>
1216
1568
  </span>
1217
1569
  </ng-container>
1218
1570
  </div>
1219
- <ng-container *ngIf="!value || value.length === 0">{{ placeholder || defaultLabel || 'empty' }}</ng-container>
1571
+ <ng-container *ngIf="!modelValue() || modelValue().length === 0">{{ placeholder || defaultLabel || 'empty' }}</ng-container>
1220
1572
  </ng-container>
1221
1573
  </ng-container>
1222
- <ng-container *ngTemplateOutlet="selectedItemsTemplate; context: { $implicit: value, removeChip: removeChip.bind(this) }"></ng-container>
1574
+ <ng-container *ngTemplateOutlet="selectedItemsTemplate; context: { $implicit: modelValue(), removeChip: removeOption.bind(this) }"></ng-container>
1223
1575
  </div>
1224
- <ng-container *ngIf="value != null && filled && !disabled && showClear">
1225
- <TimesIcon *ngIf="!clearIconTemplate" [styleClass]="'p-multiselect-clear-icon'" (click)="clear($event)" />
1226
- <span *ngIf="clearIconTemplate" class="p-multiselect-clear-icon" (click)="clear($event)">
1576
+ <ng-container *ngIf="modelValue() != null && filled && !disabled && showClear">
1577
+ <TimesIcon *ngIf="!clearIconTemplate" [styleClass]="'p-multiselect-clear-icon'" (click)="clear($event)" [attr.data-pc-section]="'clearicon'" [attr.aria-hidden]="true" />
1578
+ <span *ngIf="clearIconTemplate" class="p-multiselect-clear-icon" (click)="clear($event)" [attr.data-pc-section]="'clearicon'" [attr.aria-hidden]="true">
1227
1579
  <ng-template *ngTemplateOutlet="clearIconTemplate"></ng-template>
1228
1580
  </span>
1229
1581
  </ng-container>
1230
1582
  </div>
1231
- <div [ngClass]="{ 'p-multiselect-trigger': true }">
1583
+ <div class="p-multiselect-trigger">
1232
1584
  <ng-container *ngIf="!dropdownIconTemplate">
1233
- <span *ngIf="dropdownIcon" class="p-multiselect-trigger-icon" [ngClass]="dropdownIcon"></span>
1234
- <ChevronDownIcon *ngIf="!dropdownIcon" [styleClass]="'p-multiselect-trigger-icon'" />
1585
+ <span *ngIf="dropdownIcon" class="p-multiselect-trigger-icon" [ngClass]="dropdownIcon" [attr.data-pc-section]="'triggericon'" [attr.aria-hidden]="true"></span>
1586
+ <ChevronDownIcon *ngIf="!dropdownIcon" [styleClass]="'p-multiselect-trigger-icon'" [attr.data-pc-section]="'triggericon'" [attr.aria-hidden]="true" />
1235
1587
  </ng-container>
1236
- <span *ngIf="dropdownIconTemplate" class="p-multiselect-trigger-icon">
1588
+ <span *ngIf="dropdownIconTemplate" class="p-multiselect-trigger-icon" [attr.data-pc-section]="'triggericon'" [attr.aria-hidden]="true">
1237
1589
  <ng-template *ngTemplateOutlet="dropdownIconTemplate"></ng-template>
1238
1590
  </span>
1239
1591
  </div>
@@ -1251,7 +1603,18 @@ class MultiSelect {
1251
1603
  (onHide)="hide()"
1252
1604
  >
1253
1605
  <ng-template pTemplate="content">
1254
- <div [ngClass]="['p-multiselect-panel p-component']" [ngStyle]="panelStyle" [class]="panelStyleClass" (keydown)="onKeydown($event)">
1606
+ <div [ngClass]="'p-multiselect-panel p-component'" [ngStyle]="panelStyle" [class]="panelStyleClass">
1607
+ <span
1608
+ #firstHiddenFocusableEl
1609
+ role="presentation"
1610
+ [attr.aria-hidden]="'true'"
1611
+ class="p-hidden-accessible p-hidden-focusable"
1612
+ [attr.tabindex]="0"
1613
+ (focus)="onFirstHiddenFocus($event)"
1614
+ [attr.data-p-hidden-accessible]="true"
1615
+ [attr.data-p-hidden-focusable]="true"
1616
+ >
1617
+ </span>
1255
1618
  <div class="p-multiselect-header" *ngIf="showHeader">
1256
1619
  <ng-content select="p-header"></ng-content>
1257
1620
  <ng-container *ngTemplateOutlet="headerTemplate"></ng-container>
@@ -1259,28 +1622,29 @@ class MultiSelect {
1259
1622
  <ng-container *ngTemplateOutlet="filterTemplate; context: { options: filterOptions }"></ng-container>
1260
1623
  </ng-container>
1261
1624
  <ng-template #builtInFilterElement>
1262
- <div class="p-checkbox p-component" *ngIf="showToggleAll && !selectionLimit" [ngClass]="{ 'p-checkbox-disabled': disabled || toggleAllDisabled }">
1263
- <div class="p-hidden-accessible">
1625
+ <div
1626
+ class="p-checkbox p-component"
1627
+ *ngIf="showToggleAll && !selectionLimit"
1628
+ [ngClass]="{ 'p-checkbox-disabled': disabled || toggleAllDisabled }"
1629
+ (click)="onToggleAll($event)"
1630
+ (keydown)="onHeaderCheckboxKeyDown($event)"
1631
+ >
1632
+ <div class="p-hidden-accessible" [attr.data-p-hidden-accessible]="true">
1264
1633
  <input
1634
+ #headerCheckbox
1265
1635
  type="checkbox"
1266
- readonly="readonly"
1267
- [checked]="allChecked"
1636
+ [readonly]="readonly"
1637
+ [attr.checked]="allSelected()"
1268
1638
  (focus)="onHeaderCheckboxFocus()"
1269
1639
  (blur)="onHeaderCheckboxBlur()"
1270
- (keydown.space)="toggleAll($event)"
1271
1640
  [disabled]="disabled || toggleAllDisabled"
1641
+ [attr.aria-label]="toggleAllAriaLabel"
1272
1642
  />
1273
1643
  </div>
1274
- <div
1275
- class="p-checkbox-box"
1276
- role="checkbox"
1277
- [attr.aria-checked]="allChecked"
1278
- [ngClass]="{ 'p-highlight': allChecked, 'p-focus': headerCheckboxFocus, 'p-disabled': disabled || toggleAllDisabled }"
1279
- (click)="toggleAll($event)"
1280
- >
1281
- <ng-container *ngIf="allChecked">
1282
- <CheckIcon [styleClass]="'p-checkbox-icon'" *ngIf="!checkIconTemplate" />
1283
- <span *ngIf="checkIconTemplate" class="p-checkbox-icon">
1644
+ <div class="p-checkbox-box" role="checkbox" [attr.aria-checked]="allSelected()" [ngClass]="{ 'p-highlight': allSelected(), 'p-focus': headerCheckboxFocus, 'p-disabled': disabled || toggleAllDisabled }">
1645
+ <ng-container *ngIf="allSelected()">
1646
+ <CheckIcon [styleClass]="'p-checkbox-icon'" *ngIf="!checkIconTemplate" [attr.aria-hidden]="true" />
1647
+ <span *ngIf="checkIconTemplate" class="p-checkbox-icon" [attr.aria-hidden]="true">
1284
1648
  <ng-template *ngTemplateOutlet="checkIconTemplate"></ng-template>
1285
1649
  </span>
1286
1650
  </ng-container>
@@ -1291,9 +1655,14 @@ class MultiSelect {
1291
1655
  #filterInput
1292
1656
  type="text"
1293
1657
  [attr.autocomplete]="autocomplete"
1294
- role="textbox"
1295
- [value]="filterValue || ''"
1658
+ [attr.placeholder]="filterPlaceHolder"
1659
+ role="searchbox"
1660
+ [attr.aria-owns]="id + '_list'"
1661
+ [attr.aria-activedescendant]="focusedOptionId"
1662
+ [value]="_filterValue() || ''"
1296
1663
  (input)="onFilterInputChange($event)"
1664
+ (keydown)="onFilterKeyDown($event)"
1665
+ (blur)="onFilterBlur($event)"
1297
1666
  class="p-multiselect-filter p-inputtext p-component"
1298
1667
  [disabled]="disabled"
1299
1668
  [attr.placeholder]="filterPlaceHolder"
@@ -1317,7 +1686,7 @@ class MultiSelect {
1317
1686
  <p-scroller
1318
1687
  *ngIf="virtualScroll"
1319
1688
  #scroller
1320
- [items]="optionsToRender"
1689
+ [items]="visibleOptions()"
1321
1690
  [style]="{ height: scrollHeight }"
1322
1691
  [itemSize]="virtualScrollItemSize || _itemSize"
1323
1692
  [autoSize]="true"
@@ -1336,38 +1705,37 @@ class MultiSelect {
1336
1705
  </ng-container>
1337
1706
  </p-scroller>
1338
1707
  <ng-container *ngIf="!virtualScroll">
1339
- <ng-container *ngTemplateOutlet="buildInItems; context: { $implicit: optionsToRender, options: {} }"></ng-container>
1708
+ <ng-container *ngTemplateOutlet="buildInItems; context: { $implicit: visibleOptions(), options: {} }"></ng-container>
1340
1709
  </ng-container>
1341
1710
 
1342
1711
  <ng-template #buildInItems let-items let-scrollerOptions="options">
1343
1712
  <ul #items class="p-multiselect-items p-component" [ngClass]="scrollerOptions.contentStyleClass" [style]="scrollerOptions.contentStyle" role="listbox" aria-multiselectable="true">
1344
- <ng-container *ngIf="group">
1345
- <ng-template ngFor let-optgroup [ngForOf]="items">
1346
- <li class="p-multiselect-item-group" [ngStyle]="{ height: scrollerOptions.itemSize + 'px' }">
1347
- <span *ngIf="!groupTemplate">{{ getOptionGroupLabel(optgroup) || 'empty' }}</span>
1348
- <ng-container *ngTemplateOutlet="groupTemplate; context: { $implicit: optgroup }"></ng-container>
1713
+ <ng-template ngFor let-option [ngForOf]="items" let-i="index">
1714
+ <ng-container *ngIf="option.group">
1715
+ <li [attr.id]="id + '_' + getOptionIndex(i, scrollerOptions)" class="p-multiselect-item-group" [ngStyle]="{ height: scrollerOptions.itemSize + 'px' }" role="option">
1716
+ <span *ngIf="!groupTemplate">{{ getOptionGroupLabel(option.optionGroup) }}</span>
1717
+ <ng-container *ngTemplateOutlet="groupTemplate; context: { $implicit: option.optionGroup }"></ng-container>
1349
1718
  </li>
1350
- <ng-container *ngTemplateOutlet="itemslist; context: { $implicit: getOptionGroupChildren(optgroup) }"></ng-container>
1351
- </ng-template>
1352
- </ng-container>
1353
- <ng-container *ngIf="!group">
1354
- <ng-container *ngTemplateOutlet="itemslist; context: { $implicit: items }"></ng-container>
1355
- </ng-container>
1356
- <ng-template #itemslist let-optionsToDisplay let-selectedOption="selectedOption">
1357
- <ng-template ngFor let-option let-i="index" [ngForOf]="optionsToDisplay">
1719
+ </ng-container>
1720
+ <ng-container *ngIf="!option.group">
1358
1721
  <p-multiSelectItem
1722
+ [id]="id + '_' + getOptionIndex(i, scrollerOptions)"
1359
1723
  [option]="option"
1360
1724
  [selected]="isSelected(option)"
1361
1725
  [label]="getOptionLabel(option)"
1362
1726
  [disabled]="isOptionDisabled(option)"
1363
- (onClick)="onOptionClick($event)"
1364
- (onKeydown)="onOptionKeydown($event)"
1365
1727
  [template]="itemTemplate"
1366
1728
  [checkIconTemplate]="checkIconTemplate"
1367
1729
  [itemSize]="scrollerOptions.itemSize"
1730
+ [focused]="focusedOptionIndex() === getOptionIndex(i, scrollerOptions)"
1731
+ [ariaPosInset]="getAriaPosInset(getOptionIndex(i, scrollerOptions))"
1732
+ [ariaSetSize]="ariaSetSize"
1733
+ (onClick)="onOptionSelect($event, false, getOptionIndex(i, scrollerOptions))"
1734
+ (onMouseEnter)="onOptionMouseEnter($event, getOptionIndex(i, scrollerOptions))"
1368
1735
  ></p-multiSelectItem>
1369
- </ng-template>
1736
+ </ng-container>
1370
1737
  </ng-template>
1738
+
1371
1739
  <li *ngIf="hasFilter() && isEmpty()" class="p-multiselect-empty-message" [ngStyle]="{ height: scrollerOptions.itemSize + 'px' }">
1372
1740
  <ng-container *ngIf="!emptyFilterTemplate && !emptyTemplate; else emptyFilter">
1373
1741
  {{ emptyFilterMessageLabel }}
@@ -1387,76 +1755,81 @@ class MultiSelect {
1387
1755
  <ng-content select="p-footer"></ng-content>
1388
1756
  <ng-container *ngTemplateOutlet="footerTemplate"></ng-container>
1389
1757
  </div>
1758
+
1759
+ <span
1760
+ #lastHiddenFocusableEl
1761
+ role="presentation"
1762
+ [attr.aria-hidden]="true"
1763
+ class="p-hidden-accessible p-hidden-focusable"
1764
+ [attr.tabindex]="0"
1765
+ (focus)="onLastHiddenFocus($event)"
1766
+ [attr.data-p-hidden-accessible]="true"
1767
+ [attr.data-p-hidden-focusable]="true"
1768
+ ></span>
1390
1769
  </div>
1391
1770
  </ng-template>
1392
1771
  </p-overlay>
1393
1772
  </div>
1394
- `, isInline: true, styles: ["@layer primeng{.p-multiselect{display:inline-flex;cursor:pointer;position:relative;-webkit-user-select:none;user-select:none}.p-multiselect-trigger{display:flex;align-items:center;justify-content:center;flex-shrink:0}.p-multiselect-label-container{overflow:hidden;flex:1 1 auto;cursor:pointer;display:flex}.p-multiselect-label{display:block;white-space:nowrap;cursor:pointer;overflow:hidden;text-overflow:ellipsis}.p-multiselect-label-empty{overflow:hidden;visibility:hidden}.p-multiselect-token{cursor:default;display:inline-flex;align-items:center;flex:0 0 auto}.p-multiselect-token-icon{cursor:pointer}.p-multiselect-token-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:100px}.p-multiselect-items-wrapper{overflow:auto}.p-multiselect-items{margin:0;padding:0;list-style-type:none}.p-multiselect-item{cursor:pointer;display:flex;align-items:center;font-weight:400;white-space:nowrap;position:relative;overflow:hidden}.p-multiselect-header{display:flex;align-items:center;justify-content:space-between}.p-multiselect-filter-container{position:relative;flex:1 1 auto}.p-multiselect-filter-icon{position:absolute;top:50%;margin-top:-.5rem}.p-multiselect-filter-container .p-inputtext{width:100%}.p-multiselect-close{display:flex;align-items:center;justify-content:center;flex-shrink:0;overflow:hidden;position:relative}.p-fluid .p-multiselect{display:flex}.p-multiselect-clear-icon{position:absolute;top:50%;margin-top:-.5rem;cursor:pointer}.p-multiselect-clearable{position:relative}}\n"], dependencies: [{ kind: "directive", type: i0.forwardRef(function () { return i1.NgClass; }), selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i0.forwardRef(function () { return i1.NgForOf; }), selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i0.forwardRef(function () { return i1.NgIf; }), selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i0.forwardRef(function () { return i1.NgTemplateOutlet; }), selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i0.forwardRef(function () { return i1.NgStyle; }), selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: i0.forwardRef(function () { return i4.Overlay; }), selector: "p-overlay", inputs: ["visible", "mode", "style", "styleClass", "contentStyle", "contentStyleClass", "target", "appendTo", "autoZIndex", "baseZIndex", "showTransitionOptions", "hideTransitionOptions", "listener", "responsive", "options"], outputs: ["visibleChange", "onBeforeShow", "onShow", "onBeforeHide", "onHide", "onAnimationStart", "onAnimationDone"] }, { kind: "directive", type: i0.forwardRef(function () { return i3.PrimeTemplate; }), selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "directive", type: i0.forwardRef(function () { return i5.Tooltip; }), selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "appendTo", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions"] }, { kind: "directive", type: i0.forwardRef(function () { return i2.Ripple; }), selector: "[pRipple]" }, { kind: "component", type: i0.forwardRef(function () { return i6.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 CheckIcon; }), selector: "CheckIcon" }, { kind: "component", type: i0.forwardRef(function () { return SearchIcon; }), selector: "SearchIcon" }, { kind: "component", type: i0.forwardRef(function () { return TimesCircleIcon; }), selector: "TimesCircleIcon" }, { kind: "component", type: i0.forwardRef(function () { return TimesIcon; }), selector: "TimesIcon" }, { kind: "component", type: i0.forwardRef(function () { return ChevronDownIcon; }), selector: "ChevronDownIcon" }, { kind: "component", type: i0.forwardRef(function () { return MultiSelectItem; }), selector: "p-multiSelectItem", inputs: ["option", "selected", "label", "disabled", "itemSize", "template", "checkIconTemplate"], outputs: ["onClick", "onKeydown"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
1773
+ `, isInline: true, styles: ["@layer primeng{.p-multiselect{display:inline-flex;cursor:pointer;position:relative;-webkit-user-select:none;user-select:none}.p-multiselect-trigger{display:flex;align-items:center;justify-content:center;flex-shrink:0}.p-multiselect-label-container{overflow:hidden;flex:1 1 auto;cursor:pointer;display:flex}.p-multiselect-label{display:block;white-space:nowrap;cursor:pointer;overflow:hidden;text-overflow:ellipsis}.p-multiselect-label-empty{overflow:hidden;visibility:hidden}.p-multiselect-token{cursor:default;display:inline-flex;align-items:center;flex:0 0 auto}.p-multiselect-token-icon{cursor:pointer}.p-multiselect-token-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:100px}.p-multiselect-items-wrapper{overflow:auto}.p-multiselect-items{margin:0;padding:0;list-style-type:none}.p-multiselect-item{cursor:pointer;display:flex;align-items:center;font-weight:400;white-space:nowrap;position:relative;overflow:hidden}.p-multiselect-header{display:flex;align-items:center;justify-content:space-between}.p-multiselect-filter-container{position:relative;flex:1 1 auto}.p-multiselect-filter-icon{position:absolute;top:50%;margin-top:-.5rem}.p-multiselect-filter-container .p-inputtext{width:100%}.p-multiselect-close{display:flex;align-items:center;justify-content:center;flex-shrink:0;overflow:hidden;position:relative}.p-fluid .p-multiselect{display:flex}.p-multiselect-clear-icon{position:absolute;top:50%;margin-top:-.5rem;cursor:pointer}.p-multiselect-clearable{position:relative}}\n"], dependencies: [{ kind: "directive", type: i0.forwardRef(function () { return i1.NgClass; }), selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i0.forwardRef(function () { return i1.NgForOf; }), selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i0.forwardRef(function () { return i1.NgIf; }), selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i0.forwardRef(function () { return i1.NgTemplateOutlet; }), selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i0.forwardRef(function () { return i1.NgStyle; }), selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: i0.forwardRef(function () { return i4.Overlay; }), selector: "p-overlay", inputs: ["visible", "mode", "style", "styleClass", "contentStyle", "contentStyleClass", "target", "appendTo", "autoZIndex", "baseZIndex", "showTransitionOptions", "hideTransitionOptions", "listener", "responsive", "options"], outputs: ["visibleChange", "onBeforeShow", "onShow", "onBeforeHide", "onHide", "onAnimationStart", "onAnimationDone"] }, { kind: "directive", type: i0.forwardRef(function () { return i3.PrimeTemplate; }), selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "directive", type: i0.forwardRef(function () { return i5.Tooltip; }), selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "appendTo", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions"] }, { kind: "directive", type: i0.forwardRef(function () { return i2.Ripple; }), selector: "[pRipple]" }, { kind: "component", type: i0.forwardRef(function () { return i6.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 CheckIcon; }), selector: "CheckIcon" }, { kind: "component", type: i0.forwardRef(function () { return SearchIcon; }), selector: "SearchIcon" }, { kind: "component", type: i0.forwardRef(function () { return TimesCircleIcon; }), selector: "TimesCircleIcon" }, { kind: "component", type: i0.forwardRef(function () { return TimesIcon; }), selector: "TimesIcon" }, { kind: "component", type: i0.forwardRef(function () { return ChevronDownIcon; }), selector: "ChevronDownIcon" }, { kind: "component", type: i0.forwardRef(function () { return MultiSelectItem; }), selector: "p-multiSelectItem", inputs: ["id", "option", "selected", "label", "disabled", "itemSize", "focused", "ariaPosInset", "ariaSetSize", "template", "checkIconTemplate"], outputs: ["onClick", "onMouseEnter"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
1395
1774
  }
1396
1775
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.2", ngImport: i0, type: MultiSelect, decorators: [{
1397
1776
  type: Component,
1398
1777
  args: [{ selector: 'p-multiSelect', template: `
1399
- <div
1400
- #container
1401
- [ngClass]="{ 'p-multiselect p-component': true, 'p-multiselect-open': overlayVisible, 'p-multiselect-chip': display === 'chip', 'p-focus': focus, 'p-disabled': disabled }"
1402
- [ngStyle]="style"
1403
- [class]="styleClass"
1404
- (click)="onMouseclick($event, in)"
1405
- >
1406
- <div class="p-hidden-accessible">
1778
+ <div #container [attr.id]="id" [ngClass]="containerClass" [ngStyle]="style" [class]="styleClass" (click)="onContainerClick($event)">
1779
+ <div class="p-hidden-accessible" [attr.data-p-hidden-accessible]="true">
1407
1780
  <input
1408
- #in
1409
- type="text"
1410
- [attr.label]="label"
1411
- readonly="readonly"
1781
+ #focusInput
1782
+ [pTooltip]="tooltip"
1783
+ [tooltipPosition]="tooltipPosition"
1784
+ [positionStyle]="tooltipPositionStyle"
1785
+ [tooltipStyleClass]="tooltipStyleClass"
1786
+ [attr.aria-disabled]="disabled"
1412
1787
  [attr.id]="inputId"
1413
- [attr.name]="name"
1788
+ role="combobox"
1789
+ [attr.aria-label]="ariaLabel"
1790
+ [attr.aria-labelledby]="ariaLabelledBy"
1791
+ [attr.aria-haspopup]="'listbox'"
1792
+ [attr.aria-expanded]="overlayVisible"
1793
+ [attr.aria-controls]="id + '_list'"
1794
+ [attr.tabindex]="!disabled ? tabindex : -1"
1795
+ [attr.aria-activedescendant]="focused ? focusedOptionId : undefined"
1414
1796
  (focus)="onInputFocus($event)"
1415
1797
  (blur)="onInputBlur($event)"
1416
- [disabled]="disabled"
1417
- [attr.tabindex]="tabindex"
1418
- (keydown)="onKeydown($event)"
1419
- aria-haspopup="listbox"
1420
- [attr.aria-expanded]="overlayVisible"
1421
- [attr.aria-labelledby]="ariaLabelledBy"
1422
- role="listbox"
1798
+ (keydown)="onKeyDown($event)"
1423
1799
  />
1424
1800
  </div>
1425
1801
  <div class="p-multiselect-label-container" [pTooltip]="tooltip" [tooltipPosition]="tooltipPosition" [positionStyle]="tooltipPositionStyle" [tooltipStyleClass]="tooltipStyleClass">
1426
- <div
1427
- class="p-multiselect-label"
1428
- [ngClass]="{ 'p-placeholder': valuesAsString === (defaultLabel || placeholder), 'p-multiselect-label-empty': (valuesAsString == null || valuesAsString.length === 0) && (placeholder == null || placeholder.length === 0) }"
1429
- >
1802
+ <div [ngClass]="labelClass">
1430
1803
  <ng-container *ngIf="!selectedItemsTemplate">
1431
- <ng-container *ngIf="display === 'comma'">{{ valuesAsString || 'empty' }}</ng-container>
1804
+ <ng-container *ngIf="display === 'comma'">{{ label() || 'empty' }}</ng-container>
1432
1805
  <ng-container *ngIf="display === 'chip'">
1433
- <div #token *ngFor="let item of value; let i = index" class="p-multiselect-token">
1434
- <span class="p-multiselect-token-label">{{ findLabelByValue(item) }}</span>
1806
+ <div #token *ngFor="let item of chipSelectedItems(); let i = index" class="p-multiselect-token">
1807
+ <span class="p-multiselect-token-label">{{ getLabelByValue(item) }}</span>
1435
1808
  <ng-container *ngIf="!disabled">
1436
- <TimesCircleIcon *ngIf="!removeTokenIconTemplate" [styleClass]="'p-multiselect-token-icon'" (click)="removeChip(item, event)" />
1437
- <span *ngIf="removeTokenIconTemplate" class="p-multiselect-token-icon" (click)="removeChip(item, event)">
1809
+ <TimesCircleIcon *ngIf="!removeTokenIconTemplate" [styleClass]="'p-multiselect-token-icon'" (click)="removeOption(item, event)" [attr.data-pc-section]="'clearicon'" [attr.aria-hidden]="true" />
1810
+ <span *ngIf="removeTokenIconTemplate" class="p-multiselect-token-icon" (click)="removeOption(item, event)" [attr.data-pc-section]="'clearicon'" [attr.aria-hidden]="true">
1438
1811
  <ng-container *ngTemplateOutlet="removeTokenIconTemplate"></ng-container>
1439
1812
  </span>
1440
1813
  </ng-container>
1441
1814
  </div>
1442
- <ng-container *ngIf="!value || value.length === 0">{{ placeholder || defaultLabel || 'empty' }}</ng-container>
1815
+ <ng-container *ngIf="!modelValue() || modelValue().length === 0">{{ placeholder || defaultLabel || 'empty' }}</ng-container>
1443
1816
  </ng-container>
1444
1817
  </ng-container>
1445
- <ng-container *ngTemplateOutlet="selectedItemsTemplate; context: { $implicit: value, removeChip: removeChip.bind(this) }"></ng-container>
1818
+ <ng-container *ngTemplateOutlet="selectedItemsTemplate; context: { $implicit: modelValue(), removeChip: removeOption.bind(this) }"></ng-container>
1446
1819
  </div>
1447
- <ng-container *ngIf="value != null && filled && !disabled && showClear">
1448
- <TimesIcon *ngIf="!clearIconTemplate" [styleClass]="'p-multiselect-clear-icon'" (click)="clear($event)" />
1449
- <span *ngIf="clearIconTemplate" class="p-multiselect-clear-icon" (click)="clear($event)">
1820
+ <ng-container *ngIf="modelValue() != null && filled && !disabled && showClear">
1821
+ <TimesIcon *ngIf="!clearIconTemplate" [styleClass]="'p-multiselect-clear-icon'" (click)="clear($event)" [attr.data-pc-section]="'clearicon'" [attr.aria-hidden]="true" />
1822
+ <span *ngIf="clearIconTemplate" class="p-multiselect-clear-icon" (click)="clear($event)" [attr.data-pc-section]="'clearicon'" [attr.aria-hidden]="true">
1450
1823
  <ng-template *ngTemplateOutlet="clearIconTemplate"></ng-template>
1451
1824
  </span>
1452
1825
  </ng-container>
1453
1826
  </div>
1454
- <div [ngClass]="{ 'p-multiselect-trigger': true }">
1827
+ <div class="p-multiselect-trigger">
1455
1828
  <ng-container *ngIf="!dropdownIconTemplate">
1456
- <span *ngIf="dropdownIcon" class="p-multiselect-trigger-icon" [ngClass]="dropdownIcon"></span>
1457
- <ChevronDownIcon *ngIf="!dropdownIcon" [styleClass]="'p-multiselect-trigger-icon'" />
1829
+ <span *ngIf="dropdownIcon" class="p-multiselect-trigger-icon" [ngClass]="dropdownIcon" [attr.data-pc-section]="'triggericon'" [attr.aria-hidden]="true"></span>
1830
+ <ChevronDownIcon *ngIf="!dropdownIcon" [styleClass]="'p-multiselect-trigger-icon'" [attr.data-pc-section]="'triggericon'" [attr.aria-hidden]="true" />
1458
1831
  </ng-container>
1459
- <span *ngIf="dropdownIconTemplate" class="p-multiselect-trigger-icon">
1832
+ <span *ngIf="dropdownIconTemplate" class="p-multiselect-trigger-icon" [attr.data-pc-section]="'triggericon'" [attr.aria-hidden]="true">
1460
1833
  <ng-template *ngTemplateOutlet="dropdownIconTemplate"></ng-template>
1461
1834
  </span>
1462
1835
  </div>
@@ -1474,7 +1847,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.2", ngImpor
1474
1847
  (onHide)="hide()"
1475
1848
  >
1476
1849
  <ng-template pTemplate="content">
1477
- <div [ngClass]="['p-multiselect-panel p-component']" [ngStyle]="panelStyle" [class]="panelStyleClass" (keydown)="onKeydown($event)">
1850
+ <div [ngClass]="'p-multiselect-panel p-component'" [ngStyle]="panelStyle" [class]="panelStyleClass">
1851
+ <span
1852
+ #firstHiddenFocusableEl
1853
+ role="presentation"
1854
+ [attr.aria-hidden]="'true'"
1855
+ class="p-hidden-accessible p-hidden-focusable"
1856
+ [attr.tabindex]="0"
1857
+ (focus)="onFirstHiddenFocus($event)"
1858
+ [attr.data-p-hidden-accessible]="true"
1859
+ [attr.data-p-hidden-focusable]="true"
1860
+ >
1861
+ </span>
1478
1862
  <div class="p-multiselect-header" *ngIf="showHeader">
1479
1863
  <ng-content select="p-header"></ng-content>
1480
1864
  <ng-container *ngTemplateOutlet="headerTemplate"></ng-container>
@@ -1482,28 +1866,29 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.2", ngImpor
1482
1866
  <ng-container *ngTemplateOutlet="filterTemplate; context: { options: filterOptions }"></ng-container>
1483
1867
  </ng-container>
1484
1868
  <ng-template #builtInFilterElement>
1485
- <div class="p-checkbox p-component" *ngIf="showToggleAll && !selectionLimit" [ngClass]="{ 'p-checkbox-disabled': disabled || toggleAllDisabled }">
1486
- <div class="p-hidden-accessible">
1869
+ <div
1870
+ class="p-checkbox p-component"
1871
+ *ngIf="showToggleAll && !selectionLimit"
1872
+ [ngClass]="{ 'p-checkbox-disabled': disabled || toggleAllDisabled }"
1873
+ (click)="onToggleAll($event)"
1874
+ (keydown)="onHeaderCheckboxKeyDown($event)"
1875
+ >
1876
+ <div class="p-hidden-accessible" [attr.data-p-hidden-accessible]="true">
1487
1877
  <input
1878
+ #headerCheckbox
1488
1879
  type="checkbox"
1489
- readonly="readonly"
1490
- [checked]="allChecked"
1880
+ [readonly]="readonly"
1881
+ [attr.checked]="allSelected()"
1491
1882
  (focus)="onHeaderCheckboxFocus()"
1492
1883
  (blur)="onHeaderCheckboxBlur()"
1493
- (keydown.space)="toggleAll($event)"
1494
1884
  [disabled]="disabled || toggleAllDisabled"
1885
+ [attr.aria-label]="toggleAllAriaLabel"
1495
1886
  />
1496
1887
  </div>
1497
- <div
1498
- class="p-checkbox-box"
1499
- role="checkbox"
1500
- [attr.aria-checked]="allChecked"
1501
- [ngClass]="{ 'p-highlight': allChecked, 'p-focus': headerCheckboxFocus, 'p-disabled': disabled || toggleAllDisabled }"
1502
- (click)="toggleAll($event)"
1503
- >
1504
- <ng-container *ngIf="allChecked">
1505
- <CheckIcon [styleClass]="'p-checkbox-icon'" *ngIf="!checkIconTemplate" />
1506
- <span *ngIf="checkIconTemplate" class="p-checkbox-icon">
1888
+ <div class="p-checkbox-box" role="checkbox" [attr.aria-checked]="allSelected()" [ngClass]="{ 'p-highlight': allSelected(), 'p-focus': headerCheckboxFocus, 'p-disabled': disabled || toggleAllDisabled }">
1889
+ <ng-container *ngIf="allSelected()">
1890
+ <CheckIcon [styleClass]="'p-checkbox-icon'" *ngIf="!checkIconTemplate" [attr.aria-hidden]="true" />
1891
+ <span *ngIf="checkIconTemplate" class="p-checkbox-icon" [attr.aria-hidden]="true">
1507
1892
  <ng-template *ngTemplateOutlet="checkIconTemplate"></ng-template>
1508
1893
  </span>
1509
1894
  </ng-container>
@@ -1514,9 +1899,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.2", ngImpor
1514
1899
  #filterInput
1515
1900
  type="text"
1516
1901
  [attr.autocomplete]="autocomplete"
1517
- role="textbox"
1518
- [value]="filterValue || ''"
1902
+ [attr.placeholder]="filterPlaceHolder"
1903
+ role="searchbox"
1904
+ [attr.aria-owns]="id + '_list'"
1905
+ [attr.aria-activedescendant]="focusedOptionId"
1906
+ [value]="_filterValue() || ''"
1519
1907
  (input)="onFilterInputChange($event)"
1908
+ (keydown)="onFilterKeyDown($event)"
1909
+ (blur)="onFilterBlur($event)"
1520
1910
  class="p-multiselect-filter p-inputtext p-component"
1521
1911
  [disabled]="disabled"
1522
1912
  [attr.placeholder]="filterPlaceHolder"
@@ -1540,7 +1930,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.2", ngImpor
1540
1930
  <p-scroller
1541
1931
  *ngIf="virtualScroll"
1542
1932
  #scroller
1543
- [items]="optionsToRender"
1933
+ [items]="visibleOptions()"
1544
1934
  [style]="{ height: scrollHeight }"
1545
1935
  [itemSize]="virtualScrollItemSize || _itemSize"
1546
1936
  [autoSize]="true"
@@ -1559,38 +1949,37 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.2", ngImpor
1559
1949
  </ng-container>
1560
1950
  </p-scroller>
1561
1951
  <ng-container *ngIf="!virtualScroll">
1562
- <ng-container *ngTemplateOutlet="buildInItems; context: { $implicit: optionsToRender, options: {} }"></ng-container>
1952
+ <ng-container *ngTemplateOutlet="buildInItems; context: { $implicit: visibleOptions(), options: {} }"></ng-container>
1563
1953
  </ng-container>
1564
1954
 
1565
1955
  <ng-template #buildInItems let-items let-scrollerOptions="options">
1566
1956
  <ul #items class="p-multiselect-items p-component" [ngClass]="scrollerOptions.contentStyleClass" [style]="scrollerOptions.contentStyle" role="listbox" aria-multiselectable="true">
1567
- <ng-container *ngIf="group">
1568
- <ng-template ngFor let-optgroup [ngForOf]="items">
1569
- <li class="p-multiselect-item-group" [ngStyle]="{ height: scrollerOptions.itemSize + 'px' }">
1570
- <span *ngIf="!groupTemplate">{{ getOptionGroupLabel(optgroup) || 'empty' }}</span>
1571
- <ng-container *ngTemplateOutlet="groupTemplate; context: { $implicit: optgroup }"></ng-container>
1957
+ <ng-template ngFor let-option [ngForOf]="items" let-i="index">
1958
+ <ng-container *ngIf="option.group">
1959
+ <li [attr.id]="id + '_' + getOptionIndex(i, scrollerOptions)" class="p-multiselect-item-group" [ngStyle]="{ height: scrollerOptions.itemSize + 'px' }" role="option">
1960
+ <span *ngIf="!groupTemplate">{{ getOptionGroupLabel(option.optionGroup) }}</span>
1961
+ <ng-container *ngTemplateOutlet="groupTemplate; context: { $implicit: option.optionGroup }"></ng-container>
1572
1962
  </li>
1573
- <ng-container *ngTemplateOutlet="itemslist; context: { $implicit: getOptionGroupChildren(optgroup) }"></ng-container>
1574
- </ng-template>
1575
- </ng-container>
1576
- <ng-container *ngIf="!group">
1577
- <ng-container *ngTemplateOutlet="itemslist; context: { $implicit: items }"></ng-container>
1578
- </ng-container>
1579
- <ng-template #itemslist let-optionsToDisplay let-selectedOption="selectedOption">
1580
- <ng-template ngFor let-option let-i="index" [ngForOf]="optionsToDisplay">
1963
+ </ng-container>
1964
+ <ng-container *ngIf="!option.group">
1581
1965
  <p-multiSelectItem
1966
+ [id]="id + '_' + getOptionIndex(i, scrollerOptions)"
1582
1967
  [option]="option"
1583
1968
  [selected]="isSelected(option)"
1584
1969
  [label]="getOptionLabel(option)"
1585
1970
  [disabled]="isOptionDisabled(option)"
1586
- (onClick)="onOptionClick($event)"
1587
- (onKeydown)="onOptionKeydown($event)"
1588
1971
  [template]="itemTemplate"
1589
1972
  [checkIconTemplate]="checkIconTemplate"
1590
1973
  [itemSize]="scrollerOptions.itemSize"
1974
+ [focused]="focusedOptionIndex() === getOptionIndex(i, scrollerOptions)"
1975
+ [ariaPosInset]="getAriaPosInset(getOptionIndex(i, scrollerOptions))"
1976
+ [ariaSetSize]="ariaSetSize"
1977
+ (onClick)="onOptionSelect($event, false, getOptionIndex(i, scrollerOptions))"
1978
+ (onMouseEnter)="onOptionMouseEnter($event, getOptionIndex(i, scrollerOptions))"
1591
1979
  ></p-multiSelectItem>
1592
- </ng-template>
1980
+ </ng-container>
1593
1981
  </ng-template>
1982
+
1594
1983
  <li *ngIf="hasFilter() && isEmpty()" class="p-multiselect-empty-message" [ngStyle]="{ height: scrollerOptions.itemSize + 'px' }">
1595
1984
  <ng-container *ngIf="!emptyFilterTemplate && !emptyTemplate; else emptyFilter">
1596
1985
  {{ emptyFilterMessageLabel }}
@@ -1610,6 +1999,17 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.2", ngImpor
1610
1999
  <ng-content select="p-footer"></ng-content>
1611
2000
  <ng-container *ngTemplateOutlet="footerTemplate"></ng-container>
1612
2001
  </div>
2002
+
2003
+ <span
2004
+ #lastHiddenFocusableEl
2005
+ role="presentation"
2006
+ [attr.aria-hidden]="true"
2007
+ class="p-hidden-accessible p-hidden-focusable"
2008
+ [attr.tabindex]="0"
2009
+ (focus)="onLastHiddenFocus($event)"
2010
+ [attr.data-p-hidden-accessible]="true"
2011
+ [attr.data-p-hidden-focusable]="true"
2012
+ ></span>
1613
2013
  </div>
1614
2014
  </ng-template>
1615
2015
  </p-overlay>
@@ -1617,10 +2017,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.2", ngImpor
1617
2017
  `, host: {
1618
2018
  class: 'p-element p-inputwrapper',
1619
2019
  '[class.p-inputwrapper-filled]': 'filled',
1620
- '[class.p-inputwrapper-focus]': 'focus || overlayVisible',
1621
- '[class.p-multiselect-clearable]': 'showClear && !disabled'
2020
+ '[class.p-inputwrapper-focus]': 'focused || overlayVisible'
1622
2021
  }, providers: [MULTISELECT_VALUE_ACCESSOR], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, styles: ["@layer primeng{.p-multiselect{display:inline-flex;cursor:pointer;position:relative;-webkit-user-select:none;user-select:none}.p-multiselect-trigger{display:flex;align-items:center;justify-content:center;flex-shrink:0}.p-multiselect-label-container{overflow:hidden;flex:1 1 auto;cursor:pointer;display:flex}.p-multiselect-label{display:block;white-space:nowrap;cursor:pointer;overflow:hidden;text-overflow:ellipsis}.p-multiselect-label-empty{overflow:hidden;visibility:hidden}.p-multiselect-token{cursor:default;display:inline-flex;align-items:center;flex:0 0 auto}.p-multiselect-token-icon{cursor:pointer}.p-multiselect-token-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:100px}.p-multiselect-items-wrapper{overflow:auto}.p-multiselect-items{margin:0;padding:0;list-style-type:none}.p-multiselect-item{cursor:pointer;display:flex;align-items:center;font-weight:400;white-space:nowrap;position:relative;overflow:hidden}.p-multiselect-header{display:flex;align-items:center;justify-content:space-between}.p-multiselect-filter-container{position:relative;flex:1 1 auto}.p-multiselect-filter-icon{position:absolute;top:50%;margin-top:-.5rem}.p-multiselect-filter-container .p-inputtext{width:100%}.p-multiselect-close{display:flex;align-items:center;justify-content:center;flex-shrink:0;overflow:hidden;position:relative}.p-fluid .p-multiselect{display:flex}.p-multiselect-clear-icon{position:absolute;top:50%;margin-top:-.5rem;cursor:pointer}.p-multiselect-clearable{position:relative}}\n"] }]
1623
- }], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: i0.Renderer2 }, { type: i0.ChangeDetectorRef }, { type: i0.NgZone }, { type: i3.FilterService }, { type: i3.PrimeNGConfig }, { type: i3.OverlayService }]; }, propDecorators: { style: [{
2022
+ }], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: i0.Renderer2 }, { type: i0.ChangeDetectorRef }, { type: i0.NgZone }, { type: i3.FilterService }, { type: i3.PrimeNGConfig }, { type: i3.OverlayService }]; }, propDecorators: { id: [{
2023
+ type: Input
2024
+ }], ariaLabel: [{
2025
+ type: Input
2026
+ }], style: [{
1624
2027
  type: Input
1625
2028
  }], styleClass: [{
1626
2029
  type: Input
@@ -1652,14 +2055,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.2", ngImpor
1652
2055
  type: Input
1653
2056
  }], name: [{
1654
2057
  type: Input
1655
- }], label: [{
1656
- type: Input
1657
2058
  }], ariaLabelledBy: [{
1658
2059
  type: Input
1659
2060
  }], displaySelectedLabel: [{
1660
2061
  type: Input
1661
2062
  }], maxSelectedLabels: [{
1662
2063
  type: Input
2064
+ }], selectionLimit: [{
2065
+ type: Input
1663
2066
  }], selectedItemsLabel: [{
1664
2067
  type: Input
1665
2068
  }], showToggleAll: [{
@@ -1736,8 +2139,34 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.2", ngImpor
1736
2139
  type: Input
1737
2140
  }], itemSize: [{
1738
2141
  type: Input
1739
- }], selectionLimit: [{
2142
+ }], focusOnHover: [{
2143
+ type: Input
2144
+ }], filterFields: [{
2145
+ type: Input
2146
+ }], selectOnFocus: [{
2147
+ type: Input
2148
+ }], autoOptionFocus: [{
1740
2149
  type: Input
2150
+ }], onChange: [{
2151
+ type: Output
2152
+ }], onFilter: [{
2153
+ type: Output
2154
+ }], onFocus: [{
2155
+ type: Output
2156
+ }], onBlur: [{
2157
+ type: Output
2158
+ }], onClick: [{
2159
+ type: Output
2160
+ }], onClear: [{
2161
+ type: Output
2162
+ }], onPanelShow: [{
2163
+ type: Output
2164
+ }], onPanelHide: [{
2165
+ type: Output
2166
+ }], onLazyLoad: [{
2167
+ type: Output
2168
+ }], onRemove: [{
2169
+ type: Output
1741
2170
  }], containerViewChild: [{
1742
2171
  type: ViewChild,
1743
2172
  args: ['container']
@@ -1747,15 +2176,24 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.2", ngImpor
1747
2176
  }], filterInputChild: [{
1748
2177
  type: ViewChild,
1749
2178
  args: ['filterInput']
1750
- }], accessibleViewChild: [{
2179
+ }], focusInputViewChild: [{
1751
2180
  type: ViewChild,
1752
- args: ['in']
2181
+ args: ['focusInput']
1753
2182
  }], itemsViewChild: [{
1754
2183
  type: ViewChild,
1755
2184
  args: ['items']
1756
2185
  }], scroller: [{
1757
2186
  type: ViewChild,
1758
2187
  args: ['scroller']
2188
+ }], lastHiddenFocusableElementOnOverlay: [{
2189
+ type: ViewChild,
2190
+ args: ['lastHiddenFocusableEl']
2191
+ }], firstHiddenFocusableElementOnOverlay: [{
2192
+ type: ViewChild,
2193
+ args: ['firstHiddenFocusableEl']
2194
+ }], headerCheckboxViewChild: [{
2195
+ type: ViewChild,
2196
+ args: ['headerCheckbox']
1759
2197
  }], footerFacet: [{
1760
2198
  type: ContentChild,
1761
2199
  args: [Footer]
@@ -1765,26 +2203,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.2", ngImpor
1765
2203
  }], templates: [{
1766
2204
  type: ContentChildren,
1767
2205
  args: [PrimeTemplate]
1768
- }], onChange: [{
1769
- type: Output
1770
- }], onFilter: [{
1771
- type: Output
1772
- }], onFocus: [{
1773
- type: Output
1774
- }], onBlur: [{
1775
- type: Output
1776
- }], onClick: [{
1777
- type: Output
1778
- }], onClear: [{
1779
- type: Output
1780
- }], onPanelShow: [{
1781
- type: Output
1782
- }], onPanelHide: [{
1783
- type: Output
1784
- }], onLazyLoad: [{
1785
- type: Output
1786
- }], onRemove: [{
1787
- type: Output
1788
2206
  }] } });
1789
2207
  class MultiSelectModule {
1790
2208
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.2", ngImport: i0, type: MultiSelectModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });