primeng 16.6.0 → 16.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (179) hide show
  1. package/animate/animate.d.ts +3 -2
  2. package/animateonscroll/animateonscroll.d.ts +73 -0
  3. package/animateonscroll/index.d.ts +5 -0
  4. package/animateonscroll/public_api.d.ts +1 -0
  5. package/api/primengconfig.d.ts +1 -0
  6. package/autocomplete/autocomplete.d.ts +177 -54
  7. package/autocomplete/autocomplete.interface.d.ts +30 -0
  8. package/cascadeselect/cascadeselect.d.ts +188 -68
  9. package/cascadeselect/cascadeselect.interface.d.ts +19 -0
  10. package/checkbox/checkbox.d.ts +1 -4
  11. package/chips/chips.d.ts +21 -5
  12. package/colorpicker/colorpicker.d.ts +5 -5
  13. package/contextmenu/contextmenu.d.ts +1 -0
  14. package/dialog/dialog.d.ts +1 -2
  15. package/dom/domhandler.d.ts +4 -1
  16. package/dropdown/dropdown.d.ts +115 -37
  17. package/esm2022/accordion/accordion.mjs +11 -7
  18. package/esm2022/animate/animate.mjs +4 -1
  19. package/esm2022/animateonscroll/animateonscroll.mjs +185 -0
  20. package/esm2022/animateonscroll/primeng-animateonscroll.mjs +5 -0
  21. package/esm2022/animateonscroll/public_api.mjs +2 -0
  22. package/esm2022/api/primengconfig.mjs +2 -1
  23. package/esm2022/autocomplete/autocomplete.interface.mjs +1 -1
  24. package/esm2022/autocomplete/autocomplete.mjs +893 -617
  25. package/esm2022/carousel/carousel.mjs +5 -1
  26. package/esm2022/cascadeselect/cascadeselect.interface.mjs +1 -1
  27. package/esm2022/cascadeselect/cascadeselect.mjs +921 -482
  28. package/esm2022/checkbox/checkbox.mjs +90 -73
  29. package/esm2022/chips/chips.mjs +197 -53
  30. package/esm2022/colorpicker/colorpicker.mjs +56 -37
  31. package/esm2022/contextmenu/contextmenu.mjs +10 -1
  32. package/esm2022/dialog/dialog.mjs +6 -11
  33. package/esm2022/dom/domhandler.mjs +26 -8
  34. package/esm2022/dropdown/dropdown.mjs +711 -587
  35. package/esm2022/inputmask/inputmask.mjs +22 -9
  36. package/esm2022/inputnumber/inputnumber.mjs +142 -83
  37. package/esm2022/inputswitch/inputswitch.mjs +55 -49
  38. package/esm2022/knob/knob.mjs +92 -5
  39. package/esm2022/listbox/listbox.interface.mjs +1 -1
  40. package/esm2022/listbox/listbox.mjs +998 -455
  41. package/esm2022/multiselect/multiselect.mjs +1024 -604
  42. package/esm2022/overlaypanel/overlaypanel.mjs +2 -2
  43. package/esm2022/paginator/paginator.mjs +2 -2
  44. package/esm2022/password/password.mjs +29 -28
  45. package/esm2022/radiobutton/radiobutton.mjs +46 -33
  46. package/esm2022/rating/rating.mjs +172 -80
  47. package/esm2022/selectbutton/selectbutton.mjs +105 -33
  48. package/esm2022/slider/slider.mjs +151 -66
  49. package/esm2022/table/table.mjs +3 -3
  50. package/esm2022/togglebutton/togglebutton.mjs +47 -10
  51. package/esm2022/treetable/treetable.mjs +21 -25
  52. package/esm2022/tristatecheckbox/tristatecheckbox.mjs +53 -35
  53. package/fesm2022/primeng-accordion.mjs +10 -6
  54. package/fesm2022/primeng-accordion.mjs.map +1 -1
  55. package/fesm2022/primeng-animate.mjs +3 -0
  56. package/fesm2022/primeng-animate.mjs.map +1 -1
  57. package/fesm2022/primeng-animateonscroll.mjs +190 -0
  58. package/fesm2022/primeng-animateonscroll.mjs.map +1 -0
  59. package/fesm2022/primeng-api.mjs +1 -0
  60. package/fesm2022/primeng-api.mjs.map +1 -1
  61. package/fesm2022/primeng-autocomplete.mjs +893 -617
  62. package/fesm2022/primeng-autocomplete.mjs.map +1 -1
  63. package/fesm2022/primeng-carousel.mjs +4 -0
  64. package/fesm2022/primeng-carousel.mjs.map +1 -1
  65. package/fesm2022/primeng-cascadeselect.mjs +920 -481
  66. package/fesm2022/primeng-cascadeselect.mjs.map +1 -1
  67. package/fesm2022/primeng-checkbox.mjs +89 -72
  68. package/fesm2022/primeng-checkbox.mjs.map +1 -1
  69. package/fesm2022/primeng-chips.mjs +195 -51
  70. package/fesm2022/primeng-chips.mjs.map +1 -1
  71. package/fesm2022/primeng-colorpicker.mjs +55 -36
  72. package/fesm2022/primeng-colorpicker.mjs.map +1 -1
  73. package/fesm2022/primeng-contextmenu.mjs +9 -0
  74. package/fesm2022/primeng-contextmenu.mjs.map +1 -1
  75. package/fesm2022/primeng-dialog.mjs +5 -10
  76. package/fesm2022/primeng-dialog.mjs.map +1 -1
  77. package/fesm2022/primeng-dom.mjs +25 -7
  78. package/fesm2022/primeng-dom.mjs.map +1 -1
  79. package/fesm2022/primeng-dropdown.mjs +710 -586
  80. package/fesm2022/primeng-dropdown.mjs.map +1 -1
  81. package/fesm2022/primeng-inputmask.mjs +20 -7
  82. package/fesm2022/primeng-inputmask.mjs.map +1 -1
  83. package/fesm2022/primeng-inputnumber.mjs +140 -81
  84. package/fesm2022/primeng-inputnumber.mjs.map +1 -1
  85. package/fesm2022/primeng-inputswitch.mjs +55 -49
  86. package/fesm2022/primeng-inputswitch.mjs.map +1 -1
  87. package/fesm2022/primeng-knob.mjs +92 -5
  88. package/fesm2022/primeng-knob.mjs.map +1 -1
  89. package/fesm2022/primeng-listbox.mjs +997 -454
  90. package/fesm2022/primeng-listbox.mjs.map +1 -1
  91. package/fesm2022/primeng-multiselect.mjs +1023 -603
  92. package/fesm2022/primeng-multiselect.mjs.map +1 -1
  93. package/fesm2022/primeng-overlaypanel.mjs +1 -1
  94. package/fesm2022/primeng-overlaypanel.mjs.map +1 -1
  95. package/fesm2022/primeng-paginator.mjs +1 -1
  96. package/fesm2022/primeng-paginator.mjs.map +1 -1
  97. package/fesm2022/primeng-password.mjs +27 -26
  98. package/fesm2022/primeng-password.mjs.map +1 -1
  99. package/fesm2022/primeng-radiobutton.mjs +46 -33
  100. package/fesm2022/primeng-radiobutton.mjs.map +1 -1
  101. package/fesm2022/primeng-rating.mjs +171 -79
  102. package/fesm2022/primeng-rating.mjs.map +1 -1
  103. package/fesm2022/primeng-selectbutton.mjs +104 -32
  104. package/fesm2022/primeng-selectbutton.mjs.map +1 -1
  105. package/fesm2022/primeng-slider.mjs +150 -65
  106. package/fesm2022/primeng-slider.mjs.map +1 -1
  107. package/fesm2022/primeng-table.mjs +2 -2
  108. package/fesm2022/primeng-table.mjs.map +1 -1
  109. package/fesm2022/primeng-togglebutton.mjs +46 -9
  110. package/fesm2022/primeng-togglebutton.mjs.map +1 -1
  111. package/fesm2022/primeng-treetable.mjs +23 -27
  112. package/fesm2022/primeng-treetable.mjs.map +1 -1
  113. package/fesm2022/primeng-tristatecheckbox.mjs +53 -35
  114. package/fesm2022/primeng-tristatecheckbox.mjs.map +1 -1
  115. package/inputmask/inputmask.d.ts +6 -1
  116. package/inputnumber/inputnumber.d.ts +6 -1
  117. package/inputswitch/inputswitch.d.ts +6 -9
  118. package/knob/knob.d.ts +20 -3
  119. package/listbox/listbox.d.ts +208 -39
  120. package/listbox/listbox.interface.d.ts +16 -1
  121. package/multiselect/multiselect.d.ts +171 -60
  122. package/package.json +138 -132
  123. package/password/password.d.ts +3 -4
  124. package/radiobutton/radiobutton.d.ts +1 -2
  125. package/rating/rating.d.ts +29 -7
  126. package/resources/components/dropdown/dropdown.css +16 -2
  127. package/resources/themes/arya-blue/theme.css +106 -84
  128. package/resources/themes/arya-green/theme.css +106 -84
  129. package/resources/themes/arya-orange/theme.css +106 -84
  130. package/resources/themes/arya-purple/theme.css +106 -84
  131. package/resources/themes/bootstrap4-dark-blue/theme.css +110 -88
  132. package/resources/themes/bootstrap4-dark-purple/theme.css +110 -88
  133. package/resources/themes/bootstrap4-light-blue/theme.css +110 -88
  134. package/resources/themes/bootstrap4-light-purple/theme.css +110 -88
  135. package/resources/themes/fluent-light/theme.css +103 -81
  136. package/resources/themes/lara-dark-blue/theme.css +106 -84
  137. package/resources/themes/lara-dark-indigo/theme.css +106 -84
  138. package/resources/themes/lara-dark-purple/theme.css +106 -84
  139. package/resources/themes/lara-dark-teal/theme.css +106 -84
  140. package/resources/themes/lara-light-blue/theme.css +109 -87
  141. package/resources/themes/lara-light-indigo/theme.css +109 -87
  142. package/resources/themes/lara-light-purple/theme.css +109 -87
  143. package/resources/themes/lara-light-teal/theme.css +109 -87
  144. package/resources/themes/luna-amber/theme.css +110 -88
  145. package/resources/themes/luna-blue/theme.css +110 -88
  146. package/resources/themes/luna-green/theme.css +110 -88
  147. package/resources/themes/luna-pink/theme.css +110 -88
  148. package/resources/themes/md-dark-deeppurple/theme.css +117 -95
  149. package/resources/themes/md-dark-indigo/theme.css +117 -95
  150. package/resources/themes/md-light-deeppurple/theme.css +117 -95
  151. package/resources/themes/md-light-indigo/theme.css +117 -95
  152. package/resources/themes/mdc-dark-deeppurple/theme.css +117 -95
  153. package/resources/themes/mdc-dark-indigo/theme.css +117 -95
  154. package/resources/themes/mdc-light-deeppurple/theme.css +117 -95
  155. package/resources/themes/mdc-light-indigo/theme.css +117 -95
  156. package/resources/themes/mira/theme.css +107 -85
  157. package/resources/themes/nano/theme.css +109 -87
  158. package/resources/themes/nova/theme.css +110 -88
  159. package/resources/themes/nova-accent/theme.css +109 -87
  160. package/resources/themes/nova-alt/theme.css +110 -88
  161. package/resources/themes/rhea/theme.css +109 -87
  162. package/resources/themes/saga-blue/theme.css +106 -84
  163. package/resources/themes/saga-green/theme.css +106 -84
  164. package/resources/themes/saga-orange/theme.css +106 -84
  165. package/resources/themes/saga-purple/theme.css +106 -84
  166. package/resources/themes/soho-dark/theme.css +109 -87
  167. package/resources/themes/soho-light/theme.css +109 -87
  168. package/resources/themes/tailwind-light/theme.css +110 -88
  169. package/resources/themes/vela-blue/theme.css +106 -84
  170. package/resources/themes/vela-green/theme.css +106 -84
  171. package/resources/themes/vela-orange/theme.css +106 -84
  172. package/resources/themes/vela-purple/theme.css +106 -84
  173. package/resources/themes/viva-dark/theme.css +109 -87
  174. package/resources/themes/viva-light/theme.css +109 -87
  175. package/selectbutton/selectbutton.d.ts +15 -3
  176. package/slider/slider.d.ts +12 -6
  177. package/togglebutton/togglebutton.d.ts +7 -1
  178. package/treetable/treetable.d.ts +3 -4
  179. package/tristatecheckbox/tristatecheckbox.d.ts +8 -4
@@ -1,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,413 @@ 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
+ if (event.target.tagName === 'INPUT' || event.target.getAttribute('data-pc-section') === 'clearicon' || event.target.closest('[data-pc-section="clearicon"]')) {
1213
+ event.preventDefault();
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.focusInputViewChild?.nativeElement.focus({ preventScroll: true });
1220
+ this.onClick.emit(event);
1221
+ this.cd.detectChanges();
1222
+ }
1223
+ onFirstHiddenFocus(event) {
1224
+ const focusableEl = event.relatedTarget === this.focusInputViewChild?.nativeElement
1225
+ ? DomHandler.getFirstFocusableElement(this.overlayViewChild?.overlayViewChild?.nativeElement, ':not([data-p-hidden-focusable="true"])')
1226
+ : this.focusInputViewChild?.nativeElement;
1227
+ DomHandler.focus(focusableEl);
1228
+ }
1229
+ onInputFocus(event) {
1230
+ this.focused = true;
1231
+ const focusedOptionIndex = this.focusedOptionIndex() !== -1 ? this.focusedOptionIndex() : this.overlayVisible && this.autoOptionFocus ? this.findFirstFocusedOptionIndex() : -1;
1232
+ this.focusedOptionIndex.set(focusedOptionIndex);
1233
+ this.overlayVisible && this.scrollInView(this.focusedOptionIndex());
1234
+ this.onFocus.emit({ originalEvent: event });
1235
+ }
1236
+ onInputBlur(event) {
1237
+ this.focused = false;
1238
+ this.onBlur.emit({ originalEvent: event });
1239
+ if (!this.preventModelTouched) {
1240
+ this.onModelTouched();
1241
+ }
1242
+ this.preventModelTouched = false;
1243
+ }
1244
+ onFilterInputChange(event) {
1245
+ let value = event.target.value?.trim();
1246
+ this._filterValue.set(value);
1247
+ this.focusedOptionIndex.set(-1);
1248
+ this.onFilter.emit({ originalEvent: event, filter: this._filterValue() });
1249
+ !this.virtualScrollerDisabled && this.scroller.scrollToIndex(0);
1250
+ }
1251
+ onLastHiddenFocus(event) {
1252
+ const focusableEl = event.relatedTarget === this.focusInputViewChild?.nativeElement
1253
+ ? DomHandler.getLastFocusableElement(this.overlayViewChild?.overlayViewChild?.nativeElement, ':not([data-p-hidden-focusable="true"])')
1254
+ : this.focusInputViewChild?.nativeElement;
1255
+ DomHandler.focus(focusableEl);
1256
+ }
1257
+ onOptionMouseEnter(event, index) {
1258
+ if (this.focusOnHover) {
1259
+ this.changeFocusedOptionIndex(event, index);
1260
+ }
1261
+ }
1262
+ onHeaderCheckboxKeyDown(event) {
1263
+ if (this.disabled) {
1264
+ event.preventDefault();
1265
+ return;
1266
+ }
1267
+ switch (event.code) {
1268
+ case 'Space':
1269
+ this.onToggleAll(event);
1270
+ break;
1271
+ case 'Enter':
1272
+ this.onToggleAll(event);
1273
+ break;
1274
+ default:
1275
+ break;
1276
+ }
1277
+ }
1278
+ onFilterBlur(event) {
1279
+ this.focusedOptionIndex.set(-1);
1280
+ }
1281
+ onHeaderCheckboxFocus() {
1282
+ this.headerCheckboxFocus = true;
1283
+ }
1284
+ onHeaderCheckboxBlur() {
1285
+ this.headerCheckboxFocus = false;
1286
+ }
1287
+ onToggleAll(event) {
1288
+ if (this.disabled || this.readonly) {
1289
+ return;
1290
+ }
1291
+ DomHandler.focus(this.headerCheckboxViewChild.nativeElement);
1292
+ const value = this.allSelected()
1293
+ ? []
1294
+ : this.visibleOptions()
1295
+ .filter((option) => this.isValidOption(option))
1296
+ .map((option) => this.getOptionValue(option));
1297
+ this.updateModel(value, event);
803
1298
  this.onChange.emit({ originalEvent: event, value: this.value });
804
- this.updateFilledState();
805
- this.updateLabel();
1299
+ this.headerCheckboxFocus = true;
806
1300
  event.preventDefault();
807
1301
  event.stopPropagation();
808
1302
  }
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
- }
1303
+ changeFocusedOptionIndex(event, index) {
1304
+ if (this.focusedOptionIndex() !== index) {
1305
+ this.focusedOptionIndex.set(index);
1306
+ this.scrollInView();
1307
+ }
1308
+ }
1309
+ get virtualScrollerDisabled() {
1310
+ return !this.virtualScroll;
1311
+ }
1312
+ scrollInView(index = -1) {
1313
+ const id = index !== -1 ? `${this.id}_${index}` : this.focusedOptionId;
1314
+ if (this.itemsViewChild && this.itemsViewChild.nativeElement) {
1315
+ const element = DomHandler.findSingle(this.itemsViewChild.nativeElement, `li[id="${id}"]`);
1316
+ if (element) {
1317
+ element.scrollIntoView && element.scrollIntoView({ block: 'nearest', inline: 'nearest' });
842
1318
  }
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
- }
1319
+ else if (!this.virtualScrollerDisabled) {
1320
+ setTimeout(() => {
1321
+ this.virtualScroll && this.scroller?.scrollToIndex(index !== -1 ? index : this.focusedOptionIndex());
1322
+ }, 0);
852
1323
  }
853
- });
854
- this.value = val;
1324
+ }
855
1325
  }
856
- show() {
857
- if (!this.overlayVisible) {
858
- this.overlayVisible = true;
859
- this.preventDocumentDefault = true;
860
- this.cd.markForCheck();
1326
+ get focusedOptionId() {
1327
+ return this.focusedOptionIndex() !== -1 ? `${this.id}_${this.focusedOptionIndex()}` : null;
1328
+ }
1329
+ checkSelectionLimit() {
1330
+ if (this.selectionLimit && this.value && this.value.length === this.selectionLimit) {
1331
+ this.maxSelectionLimitReached = true;
861
1332
  }
1333
+ else {
1334
+ this.maxSelectionLimitReached = false;
1335
+ }
1336
+ }
1337
+ writeValue(value) {
1338
+ this.value = value;
1339
+ this.modelValue.set(this.value);
1340
+ this.updateModel(this.value);
1341
+ this.checkSelectionLimit();
1342
+ this.cd.markForCheck();
1343
+ }
1344
+ registerOnChange(fn) {
1345
+ this.onModelChange = fn;
1346
+ }
1347
+ registerOnTouched(fn) {
1348
+ this.onModelTouched = fn;
1349
+ }
1350
+ setDisabledState(val) {
1351
+ this.disabled = val;
1352
+ this.cd.markForCheck();
1353
+ }
1354
+ allSelected() {
1355
+ const allSelected = this.visibleOptions().length > 0 && this.visibleOptions().every((option) => this.isOptionGroup(option) || this.isOptionDisabled(option) || this.isSelected(option));
1356
+ return ObjectUtils.isNotEmpty(this.visibleOptions()) && allSelected;
1357
+ }
1358
+ /**
1359
+ * Displays the panel.
1360
+ * @group Method
1361
+ */
1362
+ show(isFocus) {
1363
+ this.overlayVisible = true;
1364
+ const focusedOptionIndex = this.focusedOptionIndex() !== -1 ? this.focusedOptionIndex() : this.autoOptionFocus ? this.findFirstFocusedOptionIndex() : -1;
1365
+ this.focusedOptionIndex.set(focusedOptionIndex);
1366
+ if (isFocus) {
1367
+ DomHandler.focus(this.focusInputViewChild?.nativeElement);
1368
+ }
1369
+ this.cd.markForCheck();
1370
+ }
1371
+ /**
1372
+ * Hides the panel.
1373
+ * @group Method
1374
+ */
1375
+ hide(isFocus) {
1376
+ this.overlayVisible = false;
1377
+ this.focusedOptionIndex.set(-1);
1378
+ if (this.filter && this.resetFilterOnHide) {
1379
+ this.resetFilter();
1380
+ }
1381
+ isFocus && DomHandler.focus(this.focusInputViewChild?.nativeElement);
1382
+ this.onPanelHide.emit();
1383
+ this.cd.markForCheck();
862
1384
  }
863
1385
  onOverlayAnimationStart(event) {
864
1386
  switch (event.toState) {
865
1387
  case 'visible':
1388
+ this.itemsWrapper = DomHandler.findSingle(this.overlayViewChild?.overlayViewChild?.nativeElement, this.virtualScroll ? '.p-scroller' : '.p-multiselect-items-wrapper');
866
1389
  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();
1390
+ if (this._options() && this._options().length) {
1391
+ if (this.virtualScroll) {
1392
+ const selectedIndex = ObjectUtils.isNotEmpty(this.modelValue()) ? this.focusedOptionIndex() : -1;
1393
+ if (selectedIndex !== -1) {
1394
+ this.scroller?.scrollToIndex(0);
1395
+ }
1396
+ }
1397
+ else {
1398
+ let selectedListItem = DomHandler.findSingle(this.itemsWrapper, '.p-multiselect-item.p-highlight');
1399
+ if (selectedListItem) {
1400
+ selectedListItem.scrollIntoView({ block: 'nearest', inline: 'center' });
1401
+ }
871
1402
  }
1403
+ this.onPanelShow.emit();
872
1404
  }
873
- this.onPanelShow.emit();
874
- break;
875
1405
  case 'void':
1406
+ this.itemsWrapper = null;
876
1407
  this.onModelTouched();
877
1408
  break;
878
1409
  }
879
1410
  }
880
- hide() {
881
- this.overlayVisible = false;
882
- if (this.resetFilterOnHide) {
883
- this.resetFilter();
884
- }
885
- this.onPanelHide.emit();
886
- this.cd.markForCheck();
887
- }
888
1411
  resetFilter() {
889
1412
  if (this.filterInputChild && this.filterInputChild.nativeElement) {
890
1413
  this.filterInputChild.nativeElement.value = '';
@@ -899,80 +1422,15 @@ class MultiSelect {
899
1422
  }
900
1423
  clear(event) {
901
1424
  this.value = null;
902
- this.updateLabel();
903
- this.updateFilledState();
904
1425
  this.checkSelectionLimit();
1426
+ this.updateModel(null, event);
905
1427
  this.onClear.emit();
906
- this.onModelChange(this.value);
907
1428
  event.stopPropagation();
908
1429
  }
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
- }
1430
+ removeOption(optionValue, event) {
1431
+ let value = this.modelValue().filter((val) => !ObjectUtils.equals(val, optionValue, this.equalityKey()));
1432
+ this.updateModel(value, event);
1433
+ event && event.stopPropagation();
976
1434
  }
977
1435
  findNextItem(item) {
978
1436
  let nextItem = item.nextElementSibling;
@@ -988,161 +1446,63 @@ class MultiSelect {
988
1446
  else
989
1447
  return null;
990
1448
  }
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
- }
1449
+ findNextOptionIndex(index) {
1450
+ const matchedOptionIndex = index < this.visibleOptions().length - 1
1451
+ ? this.visibleOptions()
1452
+ .slice(index + 1)
1453
+ .findIndex((option) => this.isValidOption(option))
1454
+ : -1;
1455
+ return matchedOptionIndex > -1 ? matchedOptionIndex + index + 1 : index;
1456
+ }
1457
+ findPrevOptionIndex(index) {
1458
+ const matchedOptionIndex = index > 0 ? ObjectUtils.findLastIndex(this.visibleOptions().slice(0, index), (option) => this.isValidOption(option)) : -1;
1459
+ return matchedOptionIndex > -1 ? matchedOptionIndex : index;
1460
+ }
1461
+ findLastSelectedOptionIndex() {
1462
+ return this.hasSelectedOption() ? ObjectUtils.findLastIndex(this.visibleOptions(), (option) => this.isValidSelectedOption(option)) : -1;
1463
+ }
1464
+ findLastFocusedOptionIndex() {
1465
+ const selectedIndex = this.findLastSelectedOptionIndex();
1466
+ return selectedIndex < 0 ? this.findLastOptionIndex() : selectedIndex;
1467
+ }
1468
+ findLastOptionIndex() {
1469
+ return ObjectUtils.findLastIndex(this.visibleOptions(), (option) => this.isValidOption(option));
1470
+ }
1471
+ searchOptions(event, char) {
1472
+ this.searchValue = (this.searchValue || '') + char;
1473
+ let optionIndex = -1;
1474
+ let matched = false;
1475
+ if (this.focusedOptionIndex() !== -1) {
1476
+ optionIndex = this.visibleOptions()
1477
+ .slice(this.focusedOptionIndex())
1478
+ .findIndex((option) => this.isOptionMatched(option));
1479
+ optionIndex =
1480
+ optionIndex === -1
1481
+ ? this.visibleOptions()
1482
+ .slice(0, this.focusedOptionIndex())
1483
+ .findIndex((option) => this.isOptionMatched(option))
1484
+ : optionIndex + this.focusedOptionIndex();
1037
1485
  }
1038
1486
  else {
1039
- this.valuesAsString = this.placeholder || this.defaultLabel;
1487
+ optionIndex = this.visibleOptions().findIndex((option) => this.isOptionMatched(option));
1040
1488
  }
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;
1489
+ if (optionIndex !== -1) {
1490
+ matched = true;
1055
1491
  }
1056
- else {
1057
- return this.searchLabelByValue(val, this.options);
1492
+ if (optionIndex === -1 && this.focusedOptionIndex() === -1) {
1493
+ optionIndex = this.findFirstFocusedOptionIndex();
1058
1494
  }
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;
1495
+ if (optionIndex !== -1) {
1496
+ this.changeFocusedOptionIndex(event, optionIndex);
1076
1497
  }
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));
1498
+ if (this.searchTimeout) {
1499
+ clearTimeout(this.searchTimeout);
1123
1500
  }
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();
1501
+ this.searchTimeout = setTimeout(() => {
1502
+ this.searchValue = '';
1503
+ this.searchTimeout = null;
1504
+ }, 500);
1505
+ return matched;
1146
1506
  }
1147
1507
  activateFilter() {
1148
1508
  if (this.hasFilter() && this._options) {
@@ -1165,75 +1525,69 @@ class MultiSelect {
1165
1525
  this._filteredOptions = null;
1166
1526
  }
1167
1527
  }
1168
- onHeaderCheckboxFocus() {
1169
- this.headerCheckboxFocus = true;
1528
+ hasFocusableElements() {
1529
+ return DomHandler.getFocusableElements(this.overlayViewChild.overlayViewChild.nativeElement, ':not([data-p-hidden-focusable="true"])').length > 0;
1170
1530
  }
1171
- onHeaderCheckboxBlur() {
1172
- this.headerCheckboxFocus = false;
1531
+ hasFilter() {
1532
+ return this._filterValue() && this._filterValue().trim().length > 0;
1173
1533
  }
1174
1534
  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">
1535
+ 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: `
1536
+ <div #container [attr.id]="id" [ngClass]="containerClass" [ngStyle]="style" [class]="styleClass" (click)="onContainerClick($event)">
1537
+ <div class="p-hidden-accessible" [attr.data-p-hidden-accessible]="true">
1184
1538
  <input
1185
- #in
1186
- type="text"
1187
- [attr.label]="label"
1188
- readonly="readonly"
1539
+ #focusInput
1540
+ [pTooltip]="tooltip"
1541
+ [tooltipPosition]="tooltipPosition"
1542
+ [positionStyle]="tooltipPositionStyle"
1543
+ [tooltipStyleClass]="tooltipStyleClass"
1544
+ [attr.aria-disabled]="disabled"
1189
1545
  [attr.id]="inputId"
1190
- [attr.name]="name"
1546
+ role="combobox"
1547
+ [attr.aria-label]="ariaLabel"
1548
+ [attr.aria-labelledby]="ariaLabelledBy"
1549
+ [attr.aria-haspopup]="'listbox'"
1550
+ [attr.aria-expanded]="overlayVisible"
1551
+ [attr.aria-controls]="id + '_list'"
1552
+ [attr.tabindex]="!disabled ? tabindex : -1"
1553
+ [attr.aria-activedescendant]="focused ? focusedOptionId : undefined"
1191
1554
  (focus)="onInputFocus($event)"
1192
1555
  (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"
1556
+ (keydown)="onKeyDown($event)"
1200
1557
  />
1201
1558
  </div>
1202
1559
  <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
- >
1560
+ <div [ngClass]="labelClass">
1207
1561
  <ng-container *ngIf="!selectedItemsTemplate">
1208
- <ng-container *ngIf="display === 'comma'">{{ valuesAsString || 'empty' }}</ng-container>
1562
+ <ng-container *ngIf="display === 'comma'">{{ label() || 'empty' }}</ng-container>
1209
1563
  <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>
1564
+ <div #token *ngFor="let item of chipSelectedItems(); let i = index" class="p-multiselect-token">
1565
+ <span class="p-multiselect-token-label">{{ getLabelByValue(item) }}</span>
1212
1566
  <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)">
1567
+ <TimesCircleIcon *ngIf="!removeTokenIconTemplate" [styleClass]="'p-multiselect-token-icon'" (click)="removeOption(item, event)" [attr.data-pc-section]="'clearicon'" [attr.aria-hidden]="true" />
1568
+ <span *ngIf="removeTokenIconTemplate" class="p-multiselect-token-icon" (click)="removeOption(item, event)" [attr.data-pc-section]="'clearicon'" [attr.aria-hidden]="true">
1215
1569
  <ng-container *ngTemplateOutlet="removeTokenIconTemplate"></ng-container>
1216
1570
  </span>
1217
1571
  </ng-container>
1218
1572
  </div>
1219
- <ng-container *ngIf="!value || value.length === 0">{{ placeholder || defaultLabel || 'empty' }}</ng-container>
1573
+ <ng-container *ngIf="!modelValue() || modelValue().length === 0">{{ placeholder || defaultLabel || 'empty' }}</ng-container>
1220
1574
  </ng-container>
1221
1575
  </ng-container>
1222
- <ng-container *ngTemplateOutlet="selectedItemsTemplate; context: { $implicit: value, removeChip: removeChip.bind(this) }"></ng-container>
1576
+ <ng-container *ngTemplateOutlet="selectedItemsTemplate; context: { $implicit: modelValue(), removeChip: removeOption.bind(this) }"></ng-container>
1223
1577
  </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)">
1578
+ <ng-container *ngIf="modelValue() != null && filled && !disabled && showClear">
1579
+ <TimesIcon *ngIf="!clearIconTemplate" [styleClass]="'p-multiselect-clear-icon'" (click)="clear($event)" [attr.data-pc-section]="'clearicon'" [attr.aria-hidden]="true" />
1580
+ <span *ngIf="clearIconTemplate" class="p-multiselect-clear-icon" (click)="clear($event)" [attr.data-pc-section]="'clearicon'" [attr.aria-hidden]="true">
1227
1581
  <ng-template *ngTemplateOutlet="clearIconTemplate"></ng-template>
1228
1582
  </span>
1229
1583
  </ng-container>
1230
1584
  </div>
1231
- <div [ngClass]="{ 'p-multiselect-trigger': true }">
1585
+ <div class="p-multiselect-trigger">
1232
1586
  <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'" />
1587
+ <span *ngIf="dropdownIcon" class="p-multiselect-trigger-icon" [ngClass]="dropdownIcon" [attr.data-pc-section]="'triggericon'" [attr.aria-hidden]="true"></span>
1588
+ <ChevronDownIcon *ngIf="!dropdownIcon" [styleClass]="'p-multiselect-trigger-icon'" [attr.data-pc-section]="'triggericon'" [attr.aria-hidden]="true" />
1235
1589
  </ng-container>
1236
- <span *ngIf="dropdownIconTemplate" class="p-multiselect-trigger-icon">
1590
+ <span *ngIf="dropdownIconTemplate" class="p-multiselect-trigger-icon" [attr.data-pc-section]="'triggericon'" [attr.aria-hidden]="true">
1237
1591
  <ng-template *ngTemplateOutlet="dropdownIconTemplate"></ng-template>
1238
1592
  </span>
1239
1593
  </div>
@@ -1251,7 +1605,18 @@ class MultiSelect {
1251
1605
  (onHide)="hide()"
1252
1606
  >
1253
1607
  <ng-template pTemplate="content">
1254
- <div [ngClass]="['p-multiselect-panel p-component']" [ngStyle]="panelStyle" [class]="panelStyleClass" (keydown)="onKeydown($event)">
1608
+ <div [ngClass]="'p-multiselect-panel p-component'" [ngStyle]="panelStyle" [class]="panelStyleClass">
1609
+ <span
1610
+ #firstHiddenFocusableEl
1611
+ role="presentation"
1612
+ [attr.aria-hidden]="'true'"
1613
+ class="p-hidden-accessible p-hidden-focusable"
1614
+ [attr.tabindex]="0"
1615
+ (focus)="onFirstHiddenFocus($event)"
1616
+ [attr.data-p-hidden-accessible]="true"
1617
+ [attr.data-p-hidden-focusable]="true"
1618
+ >
1619
+ </span>
1255
1620
  <div class="p-multiselect-header" *ngIf="showHeader">
1256
1621
  <ng-content select="p-header"></ng-content>
1257
1622
  <ng-container *ngTemplateOutlet="headerTemplate"></ng-container>
@@ -1259,28 +1624,29 @@ class MultiSelect {
1259
1624
  <ng-container *ngTemplateOutlet="filterTemplate; context: { options: filterOptions }"></ng-container>
1260
1625
  </ng-container>
1261
1626
  <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">
1627
+ <div
1628
+ class="p-checkbox p-component"
1629
+ *ngIf="showToggleAll && !selectionLimit"
1630
+ [ngClass]="{ 'p-checkbox-disabled': disabled || toggleAllDisabled }"
1631
+ (click)="onToggleAll($event)"
1632
+ (keydown)="onHeaderCheckboxKeyDown($event)"
1633
+ >
1634
+ <div class="p-hidden-accessible" [attr.data-p-hidden-accessible]="true">
1264
1635
  <input
1636
+ #headerCheckbox
1265
1637
  type="checkbox"
1266
- readonly="readonly"
1267
- [checked]="allChecked"
1638
+ [readonly]="readonly"
1639
+ [attr.checked]="allSelected()"
1268
1640
  (focus)="onHeaderCheckboxFocus()"
1269
1641
  (blur)="onHeaderCheckboxBlur()"
1270
- (keydown.space)="toggleAll($event)"
1271
1642
  [disabled]="disabled || toggleAllDisabled"
1643
+ [attr.aria-label]="toggleAllAriaLabel"
1272
1644
  />
1273
1645
  </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">
1646
+ <div class="p-checkbox-box" role="checkbox" [attr.aria-checked]="allSelected()" [ngClass]="{ 'p-highlight': allSelected(), 'p-focus': headerCheckboxFocus, 'p-disabled': disabled || toggleAllDisabled }">
1647
+ <ng-container *ngIf="allSelected()">
1648
+ <CheckIcon [styleClass]="'p-checkbox-icon'" *ngIf="!checkIconTemplate" [attr.aria-hidden]="true" />
1649
+ <span *ngIf="checkIconTemplate" class="p-checkbox-icon" [attr.aria-hidden]="true">
1284
1650
  <ng-template *ngTemplateOutlet="checkIconTemplate"></ng-template>
1285
1651
  </span>
1286
1652
  </ng-container>
@@ -1291,9 +1657,14 @@ class MultiSelect {
1291
1657
  #filterInput
1292
1658
  type="text"
1293
1659
  [attr.autocomplete]="autocomplete"
1294
- role="textbox"
1295
- [value]="filterValue || ''"
1660
+ [attr.placeholder]="filterPlaceHolder"
1661
+ role="searchbox"
1662
+ [attr.aria-owns]="id + '_list'"
1663
+ [attr.aria-activedescendant]="focusedOptionId"
1664
+ [value]="_filterValue() || ''"
1296
1665
  (input)="onFilterInputChange($event)"
1666
+ (keydown)="onFilterKeyDown($event)"
1667
+ (blur)="onFilterBlur($event)"
1297
1668
  class="p-multiselect-filter p-inputtext p-component"
1298
1669
  [disabled]="disabled"
1299
1670
  [attr.placeholder]="filterPlaceHolder"
@@ -1317,7 +1688,7 @@ class MultiSelect {
1317
1688
  <p-scroller
1318
1689
  *ngIf="virtualScroll"
1319
1690
  #scroller
1320
- [items]="optionsToRender"
1691
+ [items]="visibleOptions()"
1321
1692
  [style]="{ height: scrollHeight }"
1322
1693
  [itemSize]="virtualScrollItemSize || _itemSize"
1323
1694
  [autoSize]="true"
@@ -1336,38 +1707,37 @@ class MultiSelect {
1336
1707
  </ng-container>
1337
1708
  </p-scroller>
1338
1709
  <ng-container *ngIf="!virtualScroll">
1339
- <ng-container *ngTemplateOutlet="buildInItems; context: { $implicit: optionsToRender, options: {} }"></ng-container>
1710
+ <ng-container *ngTemplateOutlet="buildInItems; context: { $implicit: visibleOptions(), options: {} }"></ng-container>
1340
1711
  </ng-container>
1341
1712
 
1342
1713
  <ng-template #buildInItems let-items let-scrollerOptions="options">
1343
1714
  <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>
1715
+ <ng-template ngFor let-option [ngForOf]="items" let-i="index">
1716
+ <ng-container *ngIf="option.group">
1717
+ <li [attr.id]="id + '_' + getOptionIndex(i, scrollerOptions)" class="p-multiselect-item-group" [ngStyle]="{ height: scrollerOptions.itemSize + 'px' }" role="option">
1718
+ <span *ngIf="!groupTemplate">{{ getOptionGroupLabel(option.optionGroup) }}</span>
1719
+ <ng-container *ngTemplateOutlet="groupTemplate; context: { $implicit: option.optionGroup }"></ng-container>
1349
1720
  </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">
1721
+ </ng-container>
1722
+ <ng-container *ngIf="!option.group">
1358
1723
  <p-multiSelectItem
1724
+ [id]="id + '_' + getOptionIndex(i, scrollerOptions)"
1359
1725
  [option]="option"
1360
1726
  [selected]="isSelected(option)"
1361
1727
  [label]="getOptionLabel(option)"
1362
1728
  [disabled]="isOptionDisabled(option)"
1363
- (onClick)="onOptionClick($event)"
1364
- (onKeydown)="onOptionKeydown($event)"
1365
1729
  [template]="itemTemplate"
1366
1730
  [checkIconTemplate]="checkIconTemplate"
1367
1731
  [itemSize]="scrollerOptions.itemSize"
1732
+ [focused]="focusedOptionIndex() === getOptionIndex(i, scrollerOptions)"
1733
+ [ariaPosInset]="getAriaPosInset(getOptionIndex(i, scrollerOptions))"
1734
+ [ariaSetSize]="ariaSetSize"
1735
+ (onClick)="onOptionSelect($event, false, getOptionIndex(i, scrollerOptions))"
1736
+ (onMouseEnter)="onOptionMouseEnter($event, getOptionIndex(i, scrollerOptions))"
1368
1737
  ></p-multiSelectItem>
1369
- </ng-template>
1738
+ </ng-container>
1370
1739
  </ng-template>
1740
+
1371
1741
  <li *ngIf="hasFilter() && isEmpty()" class="p-multiselect-empty-message" [ngStyle]="{ height: scrollerOptions.itemSize + 'px' }">
1372
1742
  <ng-container *ngIf="!emptyFilterTemplate && !emptyTemplate; else emptyFilter">
1373
1743
  {{ emptyFilterMessageLabel }}
@@ -1387,76 +1757,81 @@ class MultiSelect {
1387
1757
  <ng-content select="p-footer"></ng-content>
1388
1758
  <ng-container *ngTemplateOutlet="footerTemplate"></ng-container>
1389
1759
  </div>
1760
+
1761
+ <span
1762
+ #lastHiddenFocusableEl
1763
+ role="presentation"
1764
+ [attr.aria-hidden]="true"
1765
+ class="p-hidden-accessible p-hidden-focusable"
1766
+ [attr.tabindex]="0"
1767
+ (focus)="onLastHiddenFocus($event)"
1768
+ [attr.data-p-hidden-accessible]="true"
1769
+ [attr.data-p-hidden-focusable]="true"
1770
+ ></span>
1390
1771
  </div>
1391
1772
  </ng-template>
1392
1773
  </p-overlay>
1393
1774
  </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 });
1775
+ `, 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
1776
  }
1396
1777
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.2", ngImport: i0, type: MultiSelect, decorators: [{
1397
1778
  type: Component,
1398
1779
  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">
1780
+ <div #container [attr.id]="id" [ngClass]="containerClass" [ngStyle]="style" [class]="styleClass" (click)="onContainerClick($event)">
1781
+ <div class="p-hidden-accessible" [attr.data-p-hidden-accessible]="true">
1407
1782
  <input
1408
- #in
1409
- type="text"
1410
- [attr.label]="label"
1411
- readonly="readonly"
1783
+ #focusInput
1784
+ [pTooltip]="tooltip"
1785
+ [tooltipPosition]="tooltipPosition"
1786
+ [positionStyle]="tooltipPositionStyle"
1787
+ [tooltipStyleClass]="tooltipStyleClass"
1788
+ [attr.aria-disabled]="disabled"
1412
1789
  [attr.id]="inputId"
1413
- [attr.name]="name"
1790
+ role="combobox"
1791
+ [attr.aria-label]="ariaLabel"
1792
+ [attr.aria-labelledby]="ariaLabelledBy"
1793
+ [attr.aria-haspopup]="'listbox'"
1794
+ [attr.aria-expanded]="overlayVisible"
1795
+ [attr.aria-controls]="id + '_list'"
1796
+ [attr.tabindex]="!disabled ? tabindex : -1"
1797
+ [attr.aria-activedescendant]="focused ? focusedOptionId : undefined"
1414
1798
  (focus)="onInputFocus($event)"
1415
1799
  (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"
1800
+ (keydown)="onKeyDown($event)"
1423
1801
  />
1424
1802
  </div>
1425
1803
  <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
- >
1804
+ <div [ngClass]="labelClass">
1430
1805
  <ng-container *ngIf="!selectedItemsTemplate">
1431
- <ng-container *ngIf="display === 'comma'">{{ valuesAsString || 'empty' }}</ng-container>
1806
+ <ng-container *ngIf="display === 'comma'">{{ label() || 'empty' }}</ng-container>
1432
1807
  <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>
1808
+ <div #token *ngFor="let item of chipSelectedItems(); let i = index" class="p-multiselect-token">
1809
+ <span class="p-multiselect-token-label">{{ getLabelByValue(item) }}</span>
1435
1810
  <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)">
1811
+ <TimesCircleIcon *ngIf="!removeTokenIconTemplate" [styleClass]="'p-multiselect-token-icon'" (click)="removeOption(item, event)" [attr.data-pc-section]="'clearicon'" [attr.aria-hidden]="true" />
1812
+ <span *ngIf="removeTokenIconTemplate" class="p-multiselect-token-icon" (click)="removeOption(item, event)" [attr.data-pc-section]="'clearicon'" [attr.aria-hidden]="true">
1438
1813
  <ng-container *ngTemplateOutlet="removeTokenIconTemplate"></ng-container>
1439
1814
  </span>
1440
1815
  </ng-container>
1441
1816
  </div>
1442
- <ng-container *ngIf="!value || value.length === 0">{{ placeholder || defaultLabel || 'empty' }}</ng-container>
1817
+ <ng-container *ngIf="!modelValue() || modelValue().length === 0">{{ placeholder || defaultLabel || 'empty' }}</ng-container>
1443
1818
  </ng-container>
1444
1819
  </ng-container>
1445
- <ng-container *ngTemplateOutlet="selectedItemsTemplate; context: { $implicit: value, removeChip: removeChip.bind(this) }"></ng-container>
1820
+ <ng-container *ngTemplateOutlet="selectedItemsTemplate; context: { $implicit: modelValue(), removeChip: removeOption.bind(this) }"></ng-container>
1446
1821
  </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)">
1822
+ <ng-container *ngIf="modelValue() != null && filled && !disabled && showClear">
1823
+ <TimesIcon *ngIf="!clearIconTemplate" [styleClass]="'p-multiselect-clear-icon'" (click)="clear($event)" [attr.data-pc-section]="'clearicon'" [attr.aria-hidden]="true" />
1824
+ <span *ngIf="clearIconTemplate" class="p-multiselect-clear-icon" (click)="clear($event)" [attr.data-pc-section]="'clearicon'" [attr.aria-hidden]="true">
1450
1825
  <ng-template *ngTemplateOutlet="clearIconTemplate"></ng-template>
1451
1826
  </span>
1452
1827
  </ng-container>
1453
1828
  </div>
1454
- <div [ngClass]="{ 'p-multiselect-trigger': true }">
1829
+ <div class="p-multiselect-trigger">
1455
1830
  <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'" />
1831
+ <span *ngIf="dropdownIcon" class="p-multiselect-trigger-icon" [ngClass]="dropdownIcon" [attr.data-pc-section]="'triggericon'" [attr.aria-hidden]="true"></span>
1832
+ <ChevronDownIcon *ngIf="!dropdownIcon" [styleClass]="'p-multiselect-trigger-icon'" [attr.data-pc-section]="'triggericon'" [attr.aria-hidden]="true" />
1458
1833
  </ng-container>
1459
- <span *ngIf="dropdownIconTemplate" class="p-multiselect-trigger-icon">
1834
+ <span *ngIf="dropdownIconTemplate" class="p-multiselect-trigger-icon" [attr.data-pc-section]="'triggericon'" [attr.aria-hidden]="true">
1460
1835
  <ng-template *ngTemplateOutlet="dropdownIconTemplate"></ng-template>
1461
1836
  </span>
1462
1837
  </div>
@@ -1474,7 +1849,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.2", ngImpor
1474
1849
  (onHide)="hide()"
1475
1850
  >
1476
1851
  <ng-template pTemplate="content">
1477
- <div [ngClass]="['p-multiselect-panel p-component']" [ngStyle]="panelStyle" [class]="panelStyleClass" (keydown)="onKeydown($event)">
1852
+ <div [ngClass]="'p-multiselect-panel p-component'" [ngStyle]="panelStyle" [class]="panelStyleClass">
1853
+ <span
1854
+ #firstHiddenFocusableEl
1855
+ role="presentation"
1856
+ [attr.aria-hidden]="'true'"
1857
+ class="p-hidden-accessible p-hidden-focusable"
1858
+ [attr.tabindex]="0"
1859
+ (focus)="onFirstHiddenFocus($event)"
1860
+ [attr.data-p-hidden-accessible]="true"
1861
+ [attr.data-p-hidden-focusable]="true"
1862
+ >
1863
+ </span>
1478
1864
  <div class="p-multiselect-header" *ngIf="showHeader">
1479
1865
  <ng-content select="p-header"></ng-content>
1480
1866
  <ng-container *ngTemplateOutlet="headerTemplate"></ng-container>
@@ -1482,28 +1868,29 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.2", ngImpor
1482
1868
  <ng-container *ngTemplateOutlet="filterTemplate; context: { options: filterOptions }"></ng-container>
1483
1869
  </ng-container>
1484
1870
  <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">
1871
+ <div
1872
+ class="p-checkbox p-component"
1873
+ *ngIf="showToggleAll && !selectionLimit"
1874
+ [ngClass]="{ 'p-checkbox-disabled': disabled || toggleAllDisabled }"
1875
+ (click)="onToggleAll($event)"
1876
+ (keydown)="onHeaderCheckboxKeyDown($event)"
1877
+ >
1878
+ <div class="p-hidden-accessible" [attr.data-p-hidden-accessible]="true">
1487
1879
  <input
1880
+ #headerCheckbox
1488
1881
  type="checkbox"
1489
- readonly="readonly"
1490
- [checked]="allChecked"
1882
+ [readonly]="readonly"
1883
+ [attr.checked]="allSelected()"
1491
1884
  (focus)="onHeaderCheckboxFocus()"
1492
1885
  (blur)="onHeaderCheckboxBlur()"
1493
- (keydown.space)="toggleAll($event)"
1494
1886
  [disabled]="disabled || toggleAllDisabled"
1887
+ [attr.aria-label]="toggleAllAriaLabel"
1495
1888
  />
1496
1889
  </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">
1890
+ <div class="p-checkbox-box" role="checkbox" [attr.aria-checked]="allSelected()" [ngClass]="{ 'p-highlight': allSelected(), 'p-focus': headerCheckboxFocus, 'p-disabled': disabled || toggleAllDisabled }">
1891
+ <ng-container *ngIf="allSelected()">
1892
+ <CheckIcon [styleClass]="'p-checkbox-icon'" *ngIf="!checkIconTemplate" [attr.aria-hidden]="true" />
1893
+ <span *ngIf="checkIconTemplate" class="p-checkbox-icon" [attr.aria-hidden]="true">
1507
1894
  <ng-template *ngTemplateOutlet="checkIconTemplate"></ng-template>
1508
1895
  </span>
1509
1896
  </ng-container>
@@ -1514,9 +1901,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.2", ngImpor
1514
1901
  #filterInput
1515
1902
  type="text"
1516
1903
  [attr.autocomplete]="autocomplete"
1517
- role="textbox"
1518
- [value]="filterValue || ''"
1904
+ [attr.placeholder]="filterPlaceHolder"
1905
+ role="searchbox"
1906
+ [attr.aria-owns]="id + '_list'"
1907
+ [attr.aria-activedescendant]="focusedOptionId"
1908
+ [value]="_filterValue() || ''"
1519
1909
  (input)="onFilterInputChange($event)"
1910
+ (keydown)="onFilterKeyDown($event)"
1911
+ (blur)="onFilterBlur($event)"
1520
1912
  class="p-multiselect-filter p-inputtext p-component"
1521
1913
  [disabled]="disabled"
1522
1914
  [attr.placeholder]="filterPlaceHolder"
@@ -1540,7 +1932,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.2", ngImpor
1540
1932
  <p-scroller
1541
1933
  *ngIf="virtualScroll"
1542
1934
  #scroller
1543
- [items]="optionsToRender"
1935
+ [items]="visibleOptions()"
1544
1936
  [style]="{ height: scrollHeight }"
1545
1937
  [itemSize]="virtualScrollItemSize || _itemSize"
1546
1938
  [autoSize]="true"
@@ -1559,38 +1951,37 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.2", ngImpor
1559
1951
  </ng-container>
1560
1952
  </p-scroller>
1561
1953
  <ng-container *ngIf="!virtualScroll">
1562
- <ng-container *ngTemplateOutlet="buildInItems; context: { $implicit: optionsToRender, options: {} }"></ng-container>
1954
+ <ng-container *ngTemplateOutlet="buildInItems; context: { $implicit: visibleOptions(), options: {} }"></ng-container>
1563
1955
  </ng-container>
1564
1956
 
1565
1957
  <ng-template #buildInItems let-items let-scrollerOptions="options">
1566
1958
  <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>
1959
+ <ng-template ngFor let-option [ngForOf]="items" let-i="index">
1960
+ <ng-container *ngIf="option.group">
1961
+ <li [attr.id]="id + '_' + getOptionIndex(i, scrollerOptions)" class="p-multiselect-item-group" [ngStyle]="{ height: scrollerOptions.itemSize + 'px' }" role="option">
1962
+ <span *ngIf="!groupTemplate">{{ getOptionGroupLabel(option.optionGroup) }}</span>
1963
+ <ng-container *ngTemplateOutlet="groupTemplate; context: { $implicit: option.optionGroup }"></ng-container>
1572
1964
  </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">
1965
+ </ng-container>
1966
+ <ng-container *ngIf="!option.group">
1581
1967
  <p-multiSelectItem
1968
+ [id]="id + '_' + getOptionIndex(i, scrollerOptions)"
1582
1969
  [option]="option"
1583
1970
  [selected]="isSelected(option)"
1584
1971
  [label]="getOptionLabel(option)"
1585
1972
  [disabled]="isOptionDisabled(option)"
1586
- (onClick)="onOptionClick($event)"
1587
- (onKeydown)="onOptionKeydown($event)"
1588
1973
  [template]="itemTemplate"
1589
1974
  [checkIconTemplate]="checkIconTemplate"
1590
1975
  [itemSize]="scrollerOptions.itemSize"
1976
+ [focused]="focusedOptionIndex() === getOptionIndex(i, scrollerOptions)"
1977
+ [ariaPosInset]="getAriaPosInset(getOptionIndex(i, scrollerOptions))"
1978
+ [ariaSetSize]="ariaSetSize"
1979
+ (onClick)="onOptionSelect($event, false, getOptionIndex(i, scrollerOptions))"
1980
+ (onMouseEnter)="onOptionMouseEnter($event, getOptionIndex(i, scrollerOptions))"
1591
1981
  ></p-multiSelectItem>
1592
- </ng-template>
1982
+ </ng-container>
1593
1983
  </ng-template>
1984
+
1594
1985
  <li *ngIf="hasFilter() && isEmpty()" class="p-multiselect-empty-message" [ngStyle]="{ height: scrollerOptions.itemSize + 'px' }">
1595
1986
  <ng-container *ngIf="!emptyFilterTemplate && !emptyTemplate; else emptyFilter">
1596
1987
  {{ emptyFilterMessageLabel }}
@@ -1610,6 +2001,17 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.2", ngImpor
1610
2001
  <ng-content select="p-footer"></ng-content>
1611
2002
  <ng-container *ngTemplateOutlet="footerTemplate"></ng-container>
1612
2003
  </div>
2004
+
2005
+ <span
2006
+ #lastHiddenFocusableEl
2007
+ role="presentation"
2008
+ [attr.aria-hidden]="true"
2009
+ class="p-hidden-accessible p-hidden-focusable"
2010
+ [attr.tabindex]="0"
2011
+ (focus)="onLastHiddenFocus($event)"
2012
+ [attr.data-p-hidden-accessible]="true"
2013
+ [attr.data-p-hidden-focusable]="true"
2014
+ ></span>
1613
2015
  </div>
1614
2016
  </ng-template>
1615
2017
  </p-overlay>
@@ -1617,10 +2019,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.2", ngImpor
1617
2019
  `, host: {
1618
2020
  class: 'p-element p-inputwrapper',
1619
2021
  '[class.p-inputwrapper-filled]': 'filled',
1620
- '[class.p-inputwrapper-focus]': 'focus || overlayVisible',
1621
- '[class.p-multiselect-clearable]': 'showClear && !disabled'
2022
+ '[class.p-inputwrapper-focus]': 'focused || overlayVisible'
1622
2023
  }, 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: [{
2024
+ }], 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: [{
2025
+ type: Input
2026
+ }], ariaLabel: [{
2027
+ type: Input
2028
+ }], style: [{
1624
2029
  type: Input
1625
2030
  }], styleClass: [{
1626
2031
  type: Input
@@ -1652,14 +2057,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.2", ngImpor
1652
2057
  type: Input
1653
2058
  }], name: [{
1654
2059
  type: Input
1655
- }], label: [{
1656
- type: Input
1657
2060
  }], ariaLabelledBy: [{
1658
2061
  type: Input
1659
2062
  }], displaySelectedLabel: [{
1660
2063
  type: Input
1661
2064
  }], maxSelectedLabels: [{
1662
2065
  type: Input
2066
+ }], selectionLimit: [{
2067
+ type: Input
1663
2068
  }], selectedItemsLabel: [{
1664
2069
  type: Input
1665
2070
  }], showToggleAll: [{
@@ -1736,8 +2141,34 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.2", ngImpor
1736
2141
  type: Input
1737
2142
  }], itemSize: [{
1738
2143
  type: Input
1739
- }], selectionLimit: [{
2144
+ }], focusOnHover: [{
2145
+ type: Input
2146
+ }], filterFields: [{
2147
+ type: Input
2148
+ }], selectOnFocus: [{
2149
+ type: Input
2150
+ }], autoOptionFocus: [{
1740
2151
  type: Input
2152
+ }], onChange: [{
2153
+ type: Output
2154
+ }], onFilter: [{
2155
+ type: Output
2156
+ }], onFocus: [{
2157
+ type: Output
2158
+ }], onBlur: [{
2159
+ type: Output
2160
+ }], onClick: [{
2161
+ type: Output
2162
+ }], onClear: [{
2163
+ type: Output
2164
+ }], onPanelShow: [{
2165
+ type: Output
2166
+ }], onPanelHide: [{
2167
+ type: Output
2168
+ }], onLazyLoad: [{
2169
+ type: Output
2170
+ }], onRemove: [{
2171
+ type: Output
1741
2172
  }], containerViewChild: [{
1742
2173
  type: ViewChild,
1743
2174
  args: ['container']
@@ -1747,15 +2178,24 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.2", ngImpor
1747
2178
  }], filterInputChild: [{
1748
2179
  type: ViewChild,
1749
2180
  args: ['filterInput']
1750
- }], accessibleViewChild: [{
2181
+ }], focusInputViewChild: [{
1751
2182
  type: ViewChild,
1752
- args: ['in']
2183
+ args: ['focusInput']
1753
2184
  }], itemsViewChild: [{
1754
2185
  type: ViewChild,
1755
2186
  args: ['items']
1756
2187
  }], scroller: [{
1757
2188
  type: ViewChild,
1758
2189
  args: ['scroller']
2190
+ }], lastHiddenFocusableElementOnOverlay: [{
2191
+ type: ViewChild,
2192
+ args: ['lastHiddenFocusableEl']
2193
+ }], firstHiddenFocusableElementOnOverlay: [{
2194
+ type: ViewChild,
2195
+ args: ['firstHiddenFocusableEl']
2196
+ }], headerCheckboxViewChild: [{
2197
+ type: ViewChild,
2198
+ args: ['headerCheckbox']
1759
2199
  }], footerFacet: [{
1760
2200
  type: ContentChild,
1761
2201
  args: [Footer]
@@ -1765,26 +2205,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.2", ngImpor
1765
2205
  }], templates: [{
1766
2206
  type: ContentChildren,
1767
2207
  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
2208
  }] } });
1789
2209
  class MultiSelectModule {
1790
2210
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.2", ngImport: i0, type: MultiSelectModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });