@watermarkinsights/ripple 5.6.0-7 → 5.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (208) hide show
  1. package/dist/cjs/{chartFunctions-995023b1.js → chartFunctions-6fd43417.js} +1 -1
  2. package/dist/cjs/{functions-e24249e6.js → functions-04149f6d.js} +0 -6
  3. package/dist/cjs/{global-8b5f83e4.js → global-4a315ae6.js} +1 -1
  4. package/dist/cjs/index-e86c28b6.js +0 -12
  5. package/dist/cjs/{intl-5aeba788.js → intl-b1e99809.js} +1 -1
  6. package/dist/cjs/loader.cjs.js +2 -2
  7. package/dist/cjs/priv-calendar.cjs.entry.js +1 -1
  8. package/dist/cjs/priv-chart-popover.cjs.entry.js +1 -1
  9. package/dist/cjs/ripple.cjs.js +2 -2
  10. package/dist/cjs/wm-action-menu_2.cjs.entry.js +1 -1
  11. package/dist/cjs/wm-button.cjs.entry.js +1 -1
  12. package/dist/cjs/wm-chart.cjs.entry.js +3 -3
  13. package/dist/cjs/wm-date-range.cjs.entry.js +1 -1
  14. package/dist/cjs/wm-datepicker.cjs.entry.js +1 -1
  15. package/dist/cjs/wm-file.cjs.entry.js +1 -1
  16. package/dist/cjs/wm-input.cjs.entry.js +2 -2
  17. package/dist/cjs/wm-line-chart.cjs.entry.js +3 -3
  18. package/dist/cjs/wm-modal-header.cjs.entry.js +2 -2
  19. package/dist/cjs/wm-modal.cjs.entry.js +1 -1
  20. package/dist/cjs/wm-navigation_3.cjs.entry.js +2 -2
  21. package/dist/cjs/wm-navigator.cjs.entry.js +1 -1
  22. package/dist/cjs/wm-option_2.cjs.entry.js +338 -33
  23. package/dist/cjs/wm-pagination.cjs.entry.js +1 -1
  24. package/dist/cjs/wm-progress-indicator_3.cjs.entry.js +2 -2
  25. package/dist/cjs/wm-search.cjs.entry.js +2 -2
  26. package/dist/cjs/wm-snackbar.cjs.entry.js +2 -2
  27. package/dist/cjs/wm-tab-item_3.cjs.entry.js +1 -1
  28. package/dist/cjs/wm-tag-input.cjs.entry.js +2 -2
  29. package/dist/cjs/wm-tag-option.cjs.entry.js +1 -1
  30. package/dist/cjs/wm-textarea.cjs.entry.js +2 -2
  31. package/dist/cjs/wm-timepicker.cjs.entry.js +1 -1
  32. package/dist/cjs/wm-toggletip.cjs.entry.js +1 -1
  33. package/dist/cjs/wm-uploader.cjs.entry.js +2 -2
  34. package/dist/collection/collection-manifest.json +2 -5
  35. package/dist/collection/components/wm-option/wm-option.js +9 -35
  36. package/dist/collection/components/wm-option/wm-option.spec.js +1 -1
  37. package/dist/collection/components/{selects/wm-select → wm-select}/wm-select.css +74 -0
  38. package/dist/collection/components/{selects/wm-select → wm-select}/wm-select.js +383 -16
  39. package/dist/collection/components/{selects/wm-select → wm-select}/wm-select.spec.js +62 -62
  40. package/dist/collection/global/functions.js +0 -5
  41. package/dist/esm/{chartFunctions-dfcb1edf.js → chartFunctions-f5eb7f59.js} +1 -1
  42. package/dist/esm/{functions-b791a892.js → functions-cf37f81f.js} +1 -6
  43. package/dist/esm/{global-5c6da026.js → global-016b76a8.js} +1 -1
  44. package/dist/esm/index-558b5a82.js +0 -12
  45. package/dist/esm/{intl-f2f7ce8b.js → intl-c72b75dc.js} +1 -1
  46. package/dist/esm/loader.js +2 -2
  47. package/dist/esm/priv-calendar.entry.js +1 -1
  48. package/dist/esm/priv-chart-popover.entry.js +1 -1
  49. package/dist/esm/ripple.js +2 -2
  50. package/dist/esm/wm-action-menu_2.entry.js +1 -1
  51. package/dist/esm/wm-button.entry.js +1 -1
  52. package/dist/esm/wm-chart.entry.js +3 -3
  53. package/dist/esm/wm-date-range.entry.js +1 -1
  54. package/dist/esm/wm-datepicker.entry.js +1 -1
  55. package/dist/esm/wm-file.entry.js +1 -1
  56. package/dist/esm/wm-input.entry.js +2 -2
  57. package/dist/esm/wm-line-chart.entry.js +3 -3
  58. package/dist/esm/wm-modal-header.entry.js +2 -2
  59. package/dist/esm/wm-modal.entry.js +1 -1
  60. package/dist/esm/wm-navigation_3.entry.js +2 -2
  61. package/dist/esm/wm-navigator.entry.js +1 -1
  62. package/dist/esm/wm-option_2.entry.js +338 -33
  63. package/dist/esm/wm-pagination.entry.js +1 -1
  64. package/dist/esm/wm-progress-indicator_3.entry.js +2 -2
  65. package/dist/esm/wm-search.entry.js +2 -2
  66. package/dist/esm/wm-snackbar.entry.js +2 -2
  67. package/dist/esm/wm-tab-item_3.entry.js +1 -1
  68. package/dist/esm/wm-tag-input.entry.js +2 -2
  69. package/dist/esm/wm-tag-option.entry.js +1 -1
  70. package/dist/esm/wm-textarea.entry.js +2 -2
  71. package/dist/esm/wm-timepicker.entry.js +1 -1
  72. package/dist/esm/wm-toggletip.entry.js +1 -1
  73. package/dist/esm/wm-uploader.entry.js +2 -2
  74. package/dist/esm-es5/{chartFunctions-dfcb1edf.js → chartFunctions-f5eb7f59.js} +1 -1
  75. package/dist/esm-es5/{functions-b791a892.js → functions-cf37f81f.js} +1 -1
  76. package/dist/esm-es5/global-016b76a8.js +1 -0
  77. package/dist/esm-es5/index-558b5a82.js +1 -1
  78. package/dist/esm-es5/{intl-f2f7ce8b.js → intl-c72b75dc.js} +1 -1
  79. package/dist/esm-es5/loader.js +1 -1
  80. package/dist/esm-es5/priv-calendar.entry.js +1 -1
  81. package/dist/esm-es5/priv-chart-popover.entry.js +1 -1
  82. package/dist/esm-es5/ripple.js +1 -1
  83. package/dist/esm-es5/wm-action-menu_2.entry.js +1 -1
  84. package/dist/esm-es5/wm-button.entry.js +1 -1
  85. package/dist/esm-es5/wm-chart.entry.js +1 -1
  86. package/dist/esm-es5/wm-date-range.entry.js +1 -1
  87. package/dist/esm-es5/wm-datepicker.entry.js +1 -1
  88. package/dist/esm-es5/wm-file.entry.js +1 -1
  89. package/dist/esm-es5/wm-input.entry.js +1 -1
  90. package/dist/esm-es5/wm-line-chart.entry.js +1 -1
  91. package/dist/esm-es5/wm-modal-header.entry.js +1 -1
  92. package/dist/esm-es5/wm-modal.entry.js +1 -1
  93. package/dist/esm-es5/wm-navigation_3.entry.js +1 -1
  94. package/dist/esm-es5/wm-navigator.entry.js +1 -1
  95. package/dist/esm-es5/wm-option_2.entry.js +1 -1
  96. package/dist/esm-es5/wm-pagination.entry.js +1 -1
  97. package/dist/esm-es5/wm-progress-indicator_3.entry.js +1 -1
  98. package/dist/esm-es5/wm-search.entry.js +1 -1
  99. package/dist/esm-es5/wm-snackbar.entry.js +1 -1
  100. package/dist/esm-es5/wm-tab-item_3.entry.js +1 -1
  101. package/dist/esm-es5/wm-tag-input.entry.js +1 -1
  102. package/dist/esm-es5/wm-tag-option.entry.js +1 -1
  103. package/dist/esm-es5/wm-textarea.entry.js +1 -1
  104. package/dist/esm-es5/wm-timepicker.entry.js +1 -1
  105. package/dist/esm-es5/wm-toggletip.entry.js +1 -1
  106. package/dist/esm-es5/wm-uploader.entry.js +1 -1
  107. package/dist/ripple/{p-c1e3c2fb.entry.js → p-04f9a60a.entry.js} +1 -1
  108. package/dist/ripple/{p-41550baa.entry.js → p-0b570b66.entry.js} +1 -1
  109. package/dist/ripple/{p-5300b15d.entry.js → p-0d05b4a9.entry.js} +1 -1
  110. package/dist/ripple/p-11aaef56.system.entry.js +1 -0
  111. package/dist/ripple/{p-b01f9998.system.entry.js → p-120dbdc8.system.entry.js} +1 -1
  112. package/dist/ripple/{p-f24572f5.entry.js → p-1aef4b40.entry.js} +1 -1
  113. package/dist/ripple/{p-13f51c06.entry.js → p-20a800a7.entry.js} +1 -1
  114. package/dist/ripple/{p-236af552.entry.js → p-24103be5.entry.js} +1 -1
  115. package/dist/ripple/{p-1389302e.system.entry.js → p-2ccf8450.system.entry.js} +1 -1
  116. package/dist/ripple/{p-6cc07645.system.entry.js → p-2d0bcc88.system.entry.js} +1 -1
  117. package/dist/ripple/{p-0b1c6965.system.entry.js → p-2d0e4020.system.entry.js} +1 -1
  118. package/dist/ripple/{p-215793a4.entry.js → p-31094930.entry.js} +1 -1
  119. package/dist/ripple/{p-1e625a5e.entry.js → p-3ea0e27c.entry.js} +1 -1
  120. package/dist/ripple/{p-db9657eb.system.entry.js → p-446e0b2c.system.entry.js} +1 -1
  121. package/dist/ripple/{p-72165bd2.entry.js → p-451433a6.entry.js} +1 -1
  122. package/dist/ripple/p-481a9e2f.system.js +1 -0
  123. package/dist/ripple/{p-45f9ad09.entry.js → p-508362c5.entry.js} +1 -1
  124. package/dist/ripple/{p-3deaf4d9.system.entry.js → p-53519eed.system.entry.js} +1 -1
  125. package/dist/ripple/p-55668879.system.js +1 -0
  126. package/dist/ripple/{p-725230dd.system.entry.js → p-5967bd2a.system.entry.js} +1 -1
  127. package/dist/ripple/{p-5a9e3108.system.entry.js → p-5db92638.system.entry.js} +1 -1
  128. package/dist/ripple/{p-3e415c49.system.entry.js → p-5f793375.system.entry.js} +1 -1
  129. package/dist/ripple/p-6636a40b.system.entry.js +1 -0
  130. package/dist/ripple/{p-f90e4094.entry.js → p-6f4c4231.entry.js} +1 -1
  131. package/dist/ripple/{p-828adbf1.system.js → p-74032162.system.js} +1 -1
  132. package/dist/ripple/{p-5284791c.entry.js → p-762429a8.entry.js} +1 -1
  133. package/dist/ripple/{p-16f65bf4.js → p-76825602.js} +1 -1
  134. package/dist/ripple/{p-c47fab48.entry.js → p-76d53189.entry.js} +1 -1
  135. package/dist/ripple/{p-fa4f1030.system.entry.js → p-7f483c55.system.entry.js} +1 -1
  136. package/dist/ripple/{p-a3c01e10.system.entry.js → p-7fdcce90.system.entry.js} +1 -1
  137. package/dist/ripple/{p-cad0c5f9.system.entry.js → p-8008f0f2.system.entry.js} +1 -1
  138. package/dist/ripple/{p-3d50db36.entry.js → p-86d81e42.entry.js} +1 -1
  139. package/dist/ripple/{p-f2101a0d.entry.js → p-8a11ee6f.entry.js} +1 -1
  140. package/dist/ripple/{p-c2e00d4a.system.entry.js → p-8b272d80.system.entry.js} +1 -1
  141. package/dist/ripple/{p-885c3527.entry.js → p-9603168d.entry.js} +1 -1
  142. package/dist/ripple/{p-f40e1468.system.entry.js → p-97f276aa.system.entry.js} +1 -1
  143. package/dist/ripple/{p-749597da.system.entry.js → p-984287f7.system.entry.js} +1 -1
  144. package/dist/ripple/{p-ff1ed90f.entry.js → p-9b75d56c.entry.js} +1 -1
  145. package/dist/ripple/{p-a0ecb6d7.system.entry.js → p-9df3574d.system.entry.js} +1 -1
  146. package/dist/ripple/p-a40b0af9.system.js +1 -0
  147. package/dist/ripple/p-a895f1ef.entry.js +1 -0
  148. package/dist/ripple/{p-e081fdc2.system.entry.js → p-ae4460f2.system.entry.js} +1 -1
  149. package/dist/ripple/{p-b9dbf1f4.entry.js → p-bd142645.entry.js} +1 -1
  150. package/dist/ripple/{p-aea13873.entry.js → p-c61366fd.entry.js} +1 -1
  151. package/dist/ripple/{p-ca971eac.system.entry.js → p-c9200deb.system.entry.js} +1 -1
  152. package/dist/ripple/{p-6d129ef8.system.entry.js → p-cd51ee87.system.entry.js} +1 -1
  153. package/dist/ripple/{p-465d44f0.system.entry.js → p-ce8c6180.system.entry.js} +1 -1
  154. package/dist/ripple/{p-84ba5b74.entry.js → p-d0387d35.entry.js} +1 -1
  155. package/dist/ripple/{p-ed1f6b8d.system.entry.js → p-d8994408.system.entry.js} +1 -1
  156. package/dist/ripple/{p-eddbcb21.js → p-db190563.js} +1 -1
  157. package/dist/ripple/{p-83be63f6.entry.js → p-db8917e4.entry.js} +1 -1
  158. package/dist/ripple/{p-505eca1c.system.entry.js → p-dc7a6037.system.entry.js} +1 -1
  159. package/dist/ripple/{p-e687176d.entry.js → p-dfd1b720.entry.js} +1 -1
  160. package/dist/ripple/{p-df157138.entry.js → p-e3899e1f.entry.js} +1 -1
  161. package/dist/ripple/{p-30b905eb.system.entry.js → p-ed1971ff.system.entry.js} +1 -1
  162. package/dist/ripple/{p-2e79fa49.entry.js → p-f177d841.entry.js} +1 -1
  163. package/dist/ripple/{p-149a22bf.entry.js → p-f4b64ded.entry.js} +1 -1
  164. package/dist/ripple/p-f9494a9d.js +1 -0
  165. package/dist/ripple/{p-21d372ed.js → p-fb229776.js} +1 -1
  166. package/dist/ripple/{p-81c2df85.system.js → p-fb751343.system.js} +1 -1
  167. package/dist/ripple/{p-c853e185.system.entry.js → p-ffa15c47.system.entry.js} +1 -1
  168. package/dist/ripple/ripple.esm.js +1 -1
  169. package/dist/ripple/ripple.js +1 -1
  170. package/dist/types/components/wm-option/wm-option.d.ts +1 -2
  171. package/dist/types/components/{selects/priv-option-list/priv-option-list.d.ts → wm-select/wm-select.d.ts} +65 -37
  172. package/dist/types/components.d.ts +2 -148
  173. package/dist/types/global/functions.d.ts +0 -1
  174. package/package.json +2 -2
  175. package/dist/cjs/priv-option-list.cjs.entry.js +0 -384
  176. package/dist/cjs/wm-nested-select.cjs.entry.js +0 -295
  177. package/dist/cjs/wm-optgroup.cjs.entry.js +0 -56
  178. package/dist/collection/components/selects/priv-option-list/priv-option-list.css +0 -104
  179. package/dist/collection/components/selects/priv-option-list/priv-option-list.js +0 -714
  180. package/dist/collection/components/selects/wm-nested-select/wm-nested-select.css +0 -378
  181. package/dist/collection/components/selects/wm-nested-select/wm-nested-select.js +0 -581
  182. package/dist/collection/components/selects/wm-optgroup/wm-optgroup.css +0 -77
  183. package/dist/collection/components/selects/wm-optgroup/wm-optgroup.js +0 -197
  184. package/dist/collection/dev/nested-select.js +0 -62
  185. package/dist/esm/priv-option-list.entry.js +0 -380
  186. package/dist/esm/wm-nested-select.entry.js +0 -291
  187. package/dist/esm/wm-optgroup.entry.js +0 -52
  188. package/dist/esm-es5/global-5c6da026.js +0 -1
  189. package/dist/esm-es5/priv-option-list.entry.js +0 -1
  190. package/dist/esm-es5/wm-nested-select.entry.js +0 -1
  191. package/dist/esm-es5/wm-optgroup.entry.js +0 -1
  192. package/dist/ripple/p-104c0e9b.system.entry.js +0 -1
  193. package/dist/ripple/p-17c8aac1.system.entry.js +0 -1
  194. package/dist/ripple/p-24ba754f.entry.js +0 -1
  195. package/dist/ripple/p-2fb58947.entry.js +0 -1
  196. package/dist/ripple/p-4564a101.system.entry.js +0 -1
  197. package/dist/ripple/p-4ca5ed96.entry.js +0 -1
  198. package/dist/ripple/p-50388b6f.system.entry.js +0 -1
  199. package/dist/ripple/p-5a8e86e9.system.js +0 -1
  200. package/dist/ripple/p-5b9babd9.system.entry.js +0 -1
  201. package/dist/ripple/p-61262e66.js +0 -1
  202. package/dist/ripple/p-8835adfb.entry.js +0 -1
  203. package/dist/ripple/p-aa4a7527.system.js +0 -1
  204. package/dist/ripple/p-ae82b3d2.system.js +0 -1
  205. package/dist/types/components/selects/wm-nested-select/wm-nested-select.d.ts +0 -55
  206. package/dist/types/components/selects/wm-optgroup/wm-optgroup.d.ts +0 -16
  207. package/dist/types/components/selects/wm-select/wm-select.d.ts +0 -53
  208. /package/dist/collection/components/{selects/wm-select → wm-select}/wm-select.e2e.js +0 -0
@@ -1,14 +1,45 @@
1
1
  import { h, Host } from "@stencil/core";
2
2
  import { forceUpdate } from "@stencil/core";
3
- import { getTextDir, shouldOpenUp, intl, isElOrChild, toBool, handleDisabledAttribute, } from "../../../global/functions";
4
- import { globalMessages } from "../../../global/intl";
3
+ import { getTextDir, shouldOpenUp, intl, debounce, toBool, handleDisabledAttribute } from "../../global/functions";
4
+ import { globalMessages } from "../../global/intl";
5
5
  export class Select {
6
6
  constructor() {
7
+ this.selectAllMessage = intl.formatMessage({
8
+ id: "select.selectAll",
9
+ defaultMessage: "Select All",
10
+ });
11
+ this.deselectAllMessage = intl.formatMessage({
12
+ id: "select.deselectAll",
13
+ defaultMessage: "Deselect All",
14
+ });
15
+ this.allOptionsSelectedMessage = intl.formatMessage({
16
+ id: "select.allOptionsSelected",
17
+ defaultMessage: "All options selected",
18
+ });
19
+ this.allOptionsDeselectedMessage = intl.formatMessage({
20
+ id: "select.allOptionsDeselected",
21
+ defaultMessage: "All options deselected",
22
+ });
23
+ this.keysSoFar = "";
24
+ this.searchIndex = 0;
7
25
  this.openUp = false;
8
26
  //////////////////////////////////////
27
+ //////////////////////////////////////
9
28
  // for multiselect button text
10
29
  this.overflowCount = 0;
11
30
  this.displayedOptions = [];
31
+ this.debouncedClearKeysSoFar = debounce(() => {
32
+ this.keysSoFar = "";
33
+ }, 500);
34
+ this.debouncedSearch = debounce(() => {
35
+ this.wmSelectSearchChanged.emit({ searchTerm: this.searchTerm });
36
+ if (this.filteredOptions.length) {
37
+ this.announce(this.resultsFoundMessage);
38
+ }
39
+ else {
40
+ this.announce(this.noResultsFoundMessage);
41
+ }
42
+ }, 150);
12
43
  this.disabled = false;
13
44
  this.maxHeight = undefined;
14
45
  this.label = undefined;
@@ -39,33 +70,85 @@ export class Select {
39
70
  get childOptions() {
40
71
  return Array.from(this.el.querySelectorAll("wm-option"));
41
72
  }
73
+ get cloneOptions() {
74
+ return Array.from(this.el.shadowRoot.querySelectorAll("wm-option"));
75
+ }
76
+ get allOptionEls() {
77
+ // this includes both slotted wm-options and internally created wm-options
78
+ return this.cloneOptions.concat(this.childOptions);
79
+ }
80
+ get visibleOptionEls() {
81
+ return this.allOptionEls.filter((option) => !option.classList.contains("hidden") && !option.classList.contains("filtered-out"));
82
+ }
42
83
  get isDisabled() {
43
84
  // string "false" needs to be treated as bool False because react wrappers convert bool to string.
44
85
  return toBool(this.disabled);
45
86
  }
87
+ //////////////////////////////////////
88
+ // for search variants
89
+ get searchTerm() {
90
+ return this.searchFieldEl ? this.searchFieldEl.value : "";
91
+ }
92
+ get filteredOptions() {
93
+ return this.childOptions.filter((option) => { var _a; return (_a = option.textContent) === null || _a === void 0 ? void 0 : _a.toLowerCase().includes(this.searchTerm.toLowerCase()); });
94
+ }
46
95
  get selectedOptions() {
47
96
  return Array.from(this.el.querySelectorAll("wm-option")).filter((x) => x.selected);
48
97
  }
49
98
  get allSelected() {
50
99
  return this.childOptions.every((option) => option.selected);
51
100
  }
101
+ get visibleSelectAllButton() {
102
+ return this.selectAll && this.multiple && this.searchTerm === "";
103
+ }
52
104
  //////////////////////////////////////
53
- handleOptionSelection() {
105
+ get resultsFoundMessage() {
106
+ return intl.formatMessage({
107
+ id: "select.searchResultsFound",
108
+ defaultMessage: "{numResults, plural, one {1 option found} other {# options found}}",
109
+ description: "The message read by the screen reader, indicating how many results a search returned",
110
+ }, { numResults: this.filteredOptions.length });
111
+ }
112
+ get noResultsFoundMessage() {
113
+ return intl.formatMessage({
114
+ id: "select.noSearchResults",
115
+ defaultMessage: "No results found. Please try your search again.",
116
+ description: "The message displayed when no options pass the search filter",
117
+ });
118
+ }
119
+ handleOptionSelection(ev) {
120
+ this.focusOption(ev.detail);
54
121
  if (!this.multiple) {
55
122
  this.close();
56
123
  }
57
124
  }
125
+ handleOptionCloneSelection(ev) {
126
+ const correspondingOption = this.findCorrespondingOption(ev.detail);
127
+ correspondingOption.click();
128
+ }
58
129
  handleChildEnter() {
59
130
  // only occurs for multiselects. handle the click, then close
60
131
  this.close();
61
132
  }
133
+ handleChildUp(ev) {
134
+ this.moveUp(ev.detail);
135
+ }
136
+ handleChildDown(ev) {
137
+ this.moveDown(ev.detail);
138
+ }
139
+ moveToFirstOption() {
140
+ this.focusOption(this.visibleOptionEls[0]);
141
+ }
142
+ moveToLastOption() {
143
+ this.focusOption(this.visibleOptionEls[this.visibleOptionEls.length - 1]);
144
+ }
62
145
  closePopupOnEscape() {
63
146
  this.close();
64
147
  }
65
148
  handleOptionBlur(ev) {
66
149
  // if the Option is blurred to something other than the component emit a blur event with the appropriate relatedTarget
67
150
  // keeps our component's blur events accurate, and closes when focusing browser address bar
68
- if (!isElOrChild(this.el, ev.detail.relatedTarget)) {
151
+ if (!this.isElOrChild(ev.detail.relatedTarget)) {
69
152
  const event = new CustomEvent("blur");
70
153
  // @ts-ignore
71
154
  event.relatedTarget = ev.detail.relatedTarget;
@@ -73,16 +156,34 @@ export class Select {
73
156
  }
74
157
  }
75
158
  handleClick(ev) {
76
- if (!isElOrChild(this.el, ev.target)) {
159
+ if (!this.isElOrChild(ev.target)) {
77
160
  this.close();
78
161
  }
79
162
  }
163
+ handleSelectAllClick() {
164
+ this.allSelected ? this.deselectAllOptions() : this.selectAllOptions();
165
+ }
166
+ selectAllOptions() {
167
+ this.announce(this.allOptionsSelectedMessage);
168
+ this.wmSelectAllSelected.emit();
169
+ }
170
+ deselectAllOptions() {
171
+ this.announce(this.allOptionsDeselectedMessage);
172
+ this.wmSelectAllDeselected.emit();
173
+ }
80
174
  handleButtonBlur(ev) {
81
- if (isElOrChild(this.el, ev.relatedTarget)) {
175
+ if (this.isElOrChild(ev.relatedTarget)) {
82
176
  // do not emit a blur event when opening the dropdown and focusing the Options
83
177
  ev.stopPropagation();
84
178
  }
85
179
  }
180
+ handleSearchFieldBlur(ev) {
181
+ this.searchFieldWrapperEl.classList.remove("focus");
182
+ if (this.isElOrChild(ev.relatedTarget)) {
183
+ // do not emit a blur event when moving from searchfield to options
184
+ ev.stopPropagation();
185
+ }
186
+ }
86
187
  handleKey(ev) {
87
188
  switch (ev.key) {
88
189
  case "ArrowDown":
@@ -119,7 +220,6 @@ export class Select {
119
220
  // on update of children or children selected state, reset button text and rerender
120
221
  this.setButtonText();
121
222
  forceUpdate(this.el);
122
- this.optionListEl.handleChildChange(_);
123
223
  }
124
224
  componentDidLoad() {
125
225
  this.wmSelectDidLoad.emit();
@@ -127,11 +227,55 @@ export class Select {
127
227
  forceUpdate(this.el);
128
228
  // Dev can overwrite the max-height rule set in the Sass file
129
229
  if (this.maxHeight) {
130
- // this.listboxEl.style.maxHeight = this.maxHeight;
131
- this.dropdownEl.style.maxHeight = this.maxHeight;
230
+ this.listboxEl.style.maxHeight = this.maxHeight;
231
+ }
232
+ if (this.multiple) {
233
+ this.updateOptionVisibility();
132
234
  }
133
235
  this.setButtonText();
134
236
  }
237
+ componentWillUpdate() {
238
+ if (this.multiple) {
239
+ // find last visible clone option and apply .last class
240
+ const visibleCloneOptions = this.visibleOptionEls.filter((option) => option.classList.contains("clone"));
241
+ // if all options are clones, nothing to separate from
242
+ const allClones = visibleCloneOptions.length === this.visibleOptionEls.length;
243
+ visibleCloneOptions.forEach((option, idx) => {
244
+ if (!allClones && idx === visibleCloneOptions.length - 1) {
245
+ option.classList.add("last");
246
+ }
247
+ else {
248
+ option.classList.remove("last");
249
+ }
250
+ });
251
+ }
252
+ }
253
+ moveUp(el) {
254
+ const focusableEls = this.visibleOptionEls;
255
+ if (this.selectAllEl) {
256
+ focusableEls.unshift(this.selectAllEl);
257
+ }
258
+ if (this.searchFieldEl) {
259
+ focusableEls.unshift(this.searchFieldEl);
260
+ }
261
+ const prevEl = focusableEls[focusableEls.indexOf(el) - 1] || focusableEls[focusableEls.length - 1];
262
+ if (prevEl) {
263
+ this.focusOption(prevEl);
264
+ }
265
+ }
266
+ moveDown(el) {
267
+ const focusableEls = this.visibleOptionEls;
268
+ if (this.selectAllEl) {
269
+ focusableEls.unshift(this.selectAllEl);
270
+ }
271
+ if (this.searchFieldEl) {
272
+ focusableEls.unshift(this.searchFieldEl);
273
+ }
274
+ const nextEl = focusableEls[focusableEls.indexOf(el) + 1] || focusableEls[0];
275
+ if (nextEl) {
276
+ this.focusOption(nextEl);
277
+ }
278
+ }
135
279
  open(optionToSelect) {
136
280
  if (!this.isDisabled) {
137
281
  const elHeight = this.el.clientHeight;
@@ -144,22 +288,60 @@ export class Select {
144
288
  this.isExpanded = true;
145
289
  this.dropdownEl.classList.remove("hidden");
146
290
  window.requestAnimationFrame(() => {
147
- this.optionListEl.handleInitialFocus(optionToSelect);
291
+ switch (optionToSelect) {
292
+ case "next":
293
+ // search variant focuses search field
294
+ // all others focus option "after" last selected option (this can be the first option)
295
+ if (this.search) {
296
+ this.searchFieldEl.focus();
297
+ this.listboxEl.scrollTop = 0;
298
+ }
299
+ else {
300
+ this.moveDown(this.visibleOptionEls.filter((x) => x.selected).slice(-1)[0]);
301
+ }
302
+ break;
303
+ case "previous":
304
+ // search variant focuses last option
305
+ // all others focus option "above" first selected option (this can be the last option)
306
+ if (this.search) {
307
+ this.focusOption(this.visibleOptionEls[this.visibleOptionEls.length - 1]);
308
+ }
309
+ else {
310
+ this.moveUp(this.visibleOptionEls.filter((x) => x.selected)[0]);
311
+ }
312
+ break;
313
+ default:
314
+ // search variant focuses search field
315
+ // all others focus the selected option
316
+ // if no option is selected (empty multiselect), focuses first option
317
+ if (this.search) {
318
+ this.searchFieldEl.focus();
319
+ this.listboxEl.scrollTop = 0;
320
+ }
321
+ else if (this.selectedOptions.length > 0) {
322
+ this.focusOption(this.visibleOptionEls.filter((x) => x.selected)[0]);
323
+ }
324
+ else {
325
+ this.focusOption(this.visibleOptionEls[0]);
326
+ }
327
+ break;
328
+ }
148
329
  });
149
330
  }
150
331
  }
151
332
  close(returnFocus = true) {
152
333
  if (this.isExpanded) {
153
334
  this.isExpanded = false;
154
- this.optionListEl.unfocusAll();
335
+ this.allOptionEls.map((i) => (i.focused = false));
155
336
  window.setTimeout(() => {
156
337
  this.dropdownEl.classList.add("hidden");
157
- if (this.optionListEl.multiple) {
158
- this.optionListEl.updateOptionVisibility();
338
+ if (this.multiple) {
339
+ this.updateOptionVisibility();
159
340
  }
160
341
  // clear search field, reset filtered / bolded state of wm-options
161
342
  if (this.search) {
162
- this.optionListEl.clearSearch();
343
+ this.searchFieldEl.value = "";
344
+ this.wmSelectSearchChanged.emit({ searchTerm: this.searchTerm });
163
345
  }
164
346
  // Returns focus to button after popup closes (no need if user is tabbing)
165
347
  // Delay is necessary for screenreader to get new expanded state before focus
@@ -171,6 +353,87 @@ export class Select {
171
353
  }, 150);
172
354
  }
173
355
  }
356
+ updateOptionVisibility() {
357
+ // this runs for search multiselects, where selected options are rendered at the top of the dropdown list
358
+ // slotted wm-options are hidden if selected, and clone wm-options are made visible if selected
359
+ this.childOptions.forEach((option, idx) => {
360
+ const cloneOption = this.cloneOptions[idx];
361
+ if (option.selected) {
362
+ option.classList.add("hidden");
363
+ cloneOption.classList.remove("hidden");
364
+ }
365
+ else {
366
+ option.classList.remove("hidden");
367
+ cloneOption.classList.add("hidden");
368
+ }
369
+ });
370
+ }
371
+ focusOption(option) {
372
+ this.allOptionEls.forEach((i) => (i.focused = i === option));
373
+ // option must be scrolledIntoView before focused
374
+ // if focus is called first the option might be positioned incorrectly (in the center of the dropdown) and scrollIntoView will do nothing as the option will already be in view
375
+ option.scrollIntoView({ block: "nearest" });
376
+ // scrollIntoView does not work when the container of the element it's called on is not rendered to the page (in our case the dropdown is still closed and has transform: scaleY(0))
377
+ // when opening the dropdown, scrollIntoView must be delayed to a point where the browser recognizes content within it as able to be scrolled to
378
+ // in Safari in particular, the soonest this seems to happen is 20ms. The longest we can wait before any jumping in the open dropdown is noticeable is 60ms
379
+ window.setTimeout(() => option.scrollIntoView({ block: "nearest" }), 60);
380
+ option.focus();
381
+ }
382
+ findAndFocusOption(ev) {
383
+ const letterPressed = ev.detail.letter.toLowerCase();
384
+ const optionPressed = ev.detail.optionEl;
385
+ if (!this.keysSoFar && letterPressed == " ") {
386
+ // if the first key entered is space, treat as a click. Otherwise include in
387
+ optionPressed.click();
388
+ }
389
+ else {
390
+ if (!this.keysSoFar) {
391
+ // if first character entered, set currently focused option as the starting index for the search
392
+ this.visibleOptionEls.forEach((option, idx) => {
393
+ if (option.focused) {
394
+ this.searchIndex = idx;
395
+ }
396
+ });
397
+ }
398
+ this.keysSoFar += letterPressed;
399
+ let nextMatch = this.findMatchInRange(this.visibleOptionEls, this.searchIndex + 1, this.visibleOptionEls.length);
400
+ if (!nextMatch) {
401
+ // if match can't be found from starting option downwards, search from top
402
+ nextMatch = this.findMatchInRange(this.visibleOptionEls, 0, this.searchIndex);
403
+ }
404
+ if (nextMatch) {
405
+ // findMatchInRange still might have returned null, only focus if match found
406
+ this.focusOption(nextMatch);
407
+ }
408
+ this.debouncedClearKeysSoFar();
409
+ }
410
+ }
411
+ findMatchInRange(list, startIndex, endIndex) {
412
+ let match = null;
413
+ let optionsInRange = list.slice(startIndex, endIndex);
414
+ // Find the first option starting with the keysSoFar substring, searching in
415
+ // the specified range of options
416
+ optionsInRange.forEach((option) => {
417
+ const label = option.textContent;
418
+ if (!match && label && label.toLowerCase().startsWith(this.keysSoFar)) {
419
+ match = option;
420
+ }
421
+ });
422
+ return match;
423
+ }
424
+ findCorrespondingOption(el) {
425
+ // if clone, returns the child wm-option
426
+ // if child wm-option, returns clone
427
+ const isClone = el.classList.contains("clone");
428
+ return isClone
429
+ ? this.childOptions[this.cloneOptions.indexOf(el)]
430
+ : this.cloneOptions[this.childOptions.indexOf(el)];
431
+ }
432
+ isElOrChild(el) {
433
+ var _a;
434
+ // determines whether or not the element is the component, a child of the component, or exists within the component's shadowroot
435
+ return el === this.el || this.el.contains(el) || ((_a = this.el.shadowRoot) === null || _a === void 0 ? void 0 : _a.contains(el));
436
+ }
174
437
  announceError() {
175
438
  if (this.errorMessage) {
176
439
  setTimeout(() => this.announce(this.errorMessage), 100);
@@ -186,8 +449,41 @@ export class Select {
186
449
  this.wmSelectBlurred.emit();
187
450
  }
188
451
  }
452
+ // on search field or select all. keydown on options is handled via events.
453
+ handleKeyDown(ev) {
454
+ const el = ev.target;
455
+ switch (ev.key) {
456
+ case "ArrowDown":
457
+ ev.preventDefault();
458
+ this.moveDown(ev.target);
459
+ break;
460
+ case "ArrowUp":
461
+ ev.preventDefault();
462
+ this.moveUp(ev.target);
463
+ break;
464
+ case "Escape":
465
+ ev.preventDefault();
466
+ this.close();
467
+ break;
468
+ case "Enter":
469
+ if (el === this.selectAllEl) {
470
+ ev.preventDefault();
471
+ this.handleSelectAllClick();
472
+ this.close();
473
+ }
474
+ break;
475
+ case " ":
476
+ if (el === this.selectAllEl) {
477
+ ev.preventDefault();
478
+ this.handleSelectAllClick();
479
+ }
480
+ break;
481
+ }
482
+ }
189
483
  setButtonText() {
190
- this.displayedOptions = this.childOptions.filter((x) => x.selected);
484
+ this.displayedOptions = this.childOptions
485
+ .filter((x) => x.selected)
486
+ .map((y) => (!y.classList.contains("hidden") ? y : this.findCorrespondingOption(y)));
191
487
  // handle overflow + counter for multiselect
192
488
  if (this.multiple) {
193
489
  // this is a fixed measurement accounting for the max width of a 3 character overflow counter
@@ -231,6 +527,21 @@ export class Select {
231
527
  return (h("span", null, h("span", { class: "overflow-counter" }, "+", this.overflowCount)));
232
528
  }
233
529
  }
530
+ renderSearchField() {
531
+ return (h("div", { class: "search" }, h("div", { class: "searchfield-wrapper", ref: (el) => (this.searchFieldWrapperEl = el) }, h("div", { class: "icon" }), h("input", { ref: (el) => (this.searchFieldEl = el), class: "searchfield", role: "combobox", "aria-controls": "list", "aria-expanded": this.isExpanded ? "true" : "false", onKeyDown: (ev) => this.handleKeyDown(ev), onFocus: () => this.searchFieldWrapperEl.classList.add("focus"), onBlur: (ev) => this.handleSearchFieldBlur(ev), onInput: () => this.debouncedSearch(), placeholder: this.searchPlaceholder }))));
532
+ }
533
+ renderSearchFailedMessage() {
534
+ return h("div", { class: "search-results-message" }, this.noResultsFoundMessage);
535
+ }
536
+ renderSelectAllButton() {
537
+ return (h("button", { ref: (el) => (this.selectAllEl = el), class: "select-all", onClick: () => this.handleSelectAllClick(), onKeyDown: (ev) => this.handleKeyDown(ev), tabindex: "-1" }, this.allSelected ? this.deselectAllMessage : this.selectAllMessage));
538
+ }
539
+ renderCloneOptions() {
540
+ return Array.from(this.el.children).map((o) => {
541
+ const option = o;
542
+ return (h("wm-option", { class: "clone", subinfo: option.subinfo, selected: option.selected }, option.textContent));
543
+ });
544
+ }
234
545
  render() {
235
546
  const showSubinfo = !this.multiple && this.selectedOptions.length > 0 && this.selectedOptions[0].subinfo;
236
547
  const buttonProps = {
@@ -244,7 +555,7 @@ export class Select {
244
555
  };
245
556
  return (h(Host, { onBlur: (ev) => this.handleComponentBlur(ev) }, h("div", { class: `wrapper ${getTextDir()} label-${this.labelPosition} ${this.errorMessage ? "invalid" : ""}` }, h("div", { class: "label-wrapper" }, h("label", { class: "label", id: "label", htmlFor: "selectbtn" }, this.label,
246
557
  // we can't use aria-required or required attributes because it's invalid on the elements we're using (button controlling a listbox)
247
- this.requiredField ? (h("span", { class: "required" }, h("span", { class: "sr-only" }, globalMessages.requiredField), h("span", { "aria-hidden": "true" }, "*"))) : (""))), h("div", { class: "button-wrapper" }, h("button", Object.assign({}, buttonProps, { class: "displayedoption", ref: (el) => (this.buttonEl = el), onBlur: (ev) => this.handleButtonBlur(ev), onFocus: () => this.close() }), h("span", { class: `overflowcontrol ${showSubinfo ? "hassubinfo" : ""}` }, h("span", { class: "button-text" }, this.renderButtonText()), showSubinfo && h("span", { class: "subinfo" }, this.selectedOptions[0].subinfo)), this.renderOverflowCount()), h("div", { class: `dropdown ${this.isExpanded ? "open" : ""} ${this.openUp ? "upwards" : ""}`, ref: (el) => (this.dropdownEl = el) }, h("priv-option-list", { ref: (el) => (this.optionListEl = el), multiple: this.multiple, search: this.search, selectAll: this.selectAll, searchPlaceholder: this.searchPlaceholder, onOptionListCloseRequested: () => this.close(), onOptionListAllSelected: () => this.wmSelectAllSelected.emit(), onOptionListAllDeselected: () => this.wmSelectAllDeselected.emit() }, h("slot", null))), h("div", { id: "error", class: this.errorMessage ? "error-message" : "" }, this.errorMessage), h("div", { id: "announcement", "aria-live": "polite", "aria-atomic": "true", class: "sr-only", ref: (el) => (this.liveRegionEl = el) }, this.announcement)))));
558
+ this.requiredField ? (h("span", { class: "required" }, h("span", { class: "sr-only" }, globalMessages.requiredField), h("span", { "aria-hidden": "true" }, "*"))) : (""))), h("div", { class: "button-wrapper" }, h("button", Object.assign({}, buttonProps, { class: "displayedoption", ref: (el) => (this.buttonEl = el), onBlur: (ev) => this.handleButtonBlur(ev), onFocus: () => this.close() }), h("span", { class: `overflowcontrol ${showSubinfo ? "hassubinfo" : ""}` }, h("span", { class: "button-text" }, this.renderButtonText()), showSubinfo && h("span", { class: "subinfo" }, this.selectedOptions[0].subinfo)), this.renderOverflowCount()), h("div", { class: `dropdown ${this.isExpanded ? "open" : ""} ${this.openUp ? "upwards" : ""}`, ref: (el) => (this.dropdownEl = el) }, this.search && this.renderSearchField(), this.visibleSelectAllButton && this.renderSelectAllButton(), h("div", { id: "list", class: "options-wrapper", tabindex: -1, role: "listbox", "aria-multiselectable": this.multiple ? "true" : null, "aria-labelledby": "label", ref: (el) => (this.listboxEl = el) }, this.search && this.filteredOptions.length === 0 && this.renderSearchFailedMessage(), this.multiple && this.renderCloneOptions(), h("slot", null))), h("div", { id: "error", class: this.errorMessage ? "error-message" : "" }, this.errorMessage), h("div", { id: "announcement", "aria-live": "polite", "aria-atomic": "true", class: "sr-only", ref: (el) => (this.liveRegionEl = el) }, this.announcement)))));
248
559
  }
249
560
  static get is() { return "wm-select"; }
250
561
  static get encapsulation() { return "shadow"; }
@@ -513,6 +824,26 @@ export class Select {
513
824
  "resolved": "void",
514
825
  "references": {}
515
826
  }
827
+ }, {
828
+ "method": "wmSelectSearchChanged",
829
+ "name": "wmSelectSearchChanged",
830
+ "bubbles": true,
831
+ "cancelable": true,
832
+ "composed": true,
833
+ "docs": {
834
+ "tags": [],
835
+ "text": ""
836
+ },
837
+ "complexType": {
838
+ "original": "Object",
839
+ "resolved": "Object",
840
+ "references": {
841
+ "Object": {
842
+ "location": "global",
843
+ "id": "global::Object"
844
+ }
845
+ }
846
+ }
516
847
  }, {
517
848
  "method": "wmSelectAllSelected",
518
849
  "name": "wmSelectAllSelected",
@@ -562,12 +893,42 @@ export class Select {
562
893
  "target": undefined,
563
894
  "capture": false,
564
895
  "passive": false
896
+ }, {
897
+ "name": "intCloneClicked",
898
+ "method": "handleOptionCloneSelection",
899
+ "target": undefined,
900
+ "capture": false,
901
+ "passive": false
565
902
  }, {
566
903
  "name": "wmEnterKeyPressed",
567
904
  "method": "handleChildEnter",
568
905
  "target": undefined,
569
906
  "capture": false,
570
907
  "passive": false
908
+ }, {
909
+ "name": "wmKeyUpPressed",
910
+ "method": "handleChildUp",
911
+ "target": undefined,
912
+ "capture": false,
913
+ "passive": false
914
+ }, {
915
+ "name": "wmKeyDownPressed",
916
+ "method": "handleChildDown",
917
+ "target": undefined,
918
+ "capture": false,
919
+ "passive": false
920
+ }, {
921
+ "name": "wmHomeKeyPressed",
922
+ "method": "moveToFirstOption",
923
+ "target": undefined,
924
+ "capture": false,
925
+ "passive": false
926
+ }, {
927
+ "name": "wmEndKeyPressed",
928
+ "method": "moveToLastOption",
929
+ "target": undefined,
930
+ "capture": false,
931
+ "passive": false
571
932
  }, {
572
933
  "name": "wmEscKeyPressed",
573
934
  "method": "closePopupOnEscape",
@@ -592,6 +953,12 @@ export class Select {
592
953
  "target": undefined,
593
954
  "capture": false,
594
955
  "passive": false
956
+ }, {
957
+ "name": "wmLetterPressed",
958
+ "method": "findAndFocusOption",
959
+ "target": undefined,
960
+ "capture": false,
961
+ "passive": false
595
962
  }];
596
963
  }
597
964
  }