@watermarkinsights/ripple 3.4.0 → 3.5.0-3

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 (203) hide show
  1. package/README.md +3 -3
  2. package/dist/cjs/functions-653e695c.js +6164 -0
  3. package/dist/cjs/{global-caec45e5.js → global-ee710089.js} +65 -65
  4. package/dist/cjs/{http-service-9e8c4dd5.js → http-service-494d81de.js} +49 -49
  5. package/dist/cjs/{index-921ef454.js → index-cd179539.js} +157 -103
  6. package/dist/cjs/{interfaces-30a74c1f.js → interfaces-a3338581.js} +29 -29
  7. package/dist/cjs/loader.cjs.js +3 -3
  8. package/dist/cjs/priv-chart-popover.cjs.entry.js +79 -79
  9. package/dist/cjs/priv-datepicker.cjs.entry.js +668 -668
  10. package/dist/cjs/priv-navigator-button.cjs.entry.js +19 -19
  11. package/dist/cjs/priv-navigator-item.cjs.entry.js +24 -24
  12. package/dist/cjs/ripple.cjs.js +5 -5
  13. package/dist/cjs/wm-action-menu_2.cjs.entry.js +342 -342
  14. package/dist/cjs/wm-button.cjs.entry.js +219 -219
  15. package/dist/cjs/wm-chart-slice.cjs.entry.js +14 -14
  16. package/dist/cjs/wm-chart.cjs.entry.js +475 -517
  17. package/dist/cjs/wm-datepicker.cjs.entry.js +257 -257
  18. package/dist/cjs/wm-input.cjs.entry.js +134 -134
  19. package/dist/cjs/wm-modal-footer.cjs.entry.js +32 -32
  20. package/dist/cjs/wm-modal-header.cjs.entry.js +35 -35
  21. package/dist/cjs/wm-modal.cjs.entry.js +162 -162
  22. package/dist/cjs/wm-navigator.cjs.entry.js +269 -269
  23. package/dist/cjs/wm-network-uploader.cjs.entry.js +421 -421
  24. package/dist/cjs/wm-option_2.cjs.entry.js +492 -492
  25. package/dist/cjs/wm-pagination.cjs.entry.js +168 -168
  26. package/dist/cjs/wm-search.cjs.entry.js +232 -232
  27. package/dist/cjs/wm-snackbar.cjs.entry.js +171 -171
  28. package/dist/cjs/wm-tab-item_3.cjs.entry.js +301 -301
  29. package/dist/cjs/wm-tag-input.cjs.entry.js +557 -557
  30. package/dist/cjs/wm-timepicker.cjs.entry.js +384 -384
  31. package/dist/cjs/wm-toggletip.cjs.entry.js +125 -125
  32. package/dist/cjs/wm-uploader.cjs.entry.js +347 -347
  33. package/dist/cjs/wm-wrapper.cjs.entry.js +13 -13
  34. package/dist/collection/collection-manifest.json +2 -2
  35. package/dist/collection/components/wm-action-menu/wm-action-menu.js +460 -460
  36. package/dist/collection/components/wm-button/wm-button.js +485 -485
  37. package/dist/collection/components/wm-chart/priv-chart-popover/priv-chart-popover.js +232 -232
  38. package/dist/collection/components/wm-chart/wm-chart-slice.js +64 -64
  39. package/dist/collection/components/wm-chart/wm-chart.css +71 -84
  40. package/dist/collection/components/wm-chart/wm-chart.js +718 -765
  41. package/dist/collection/components/wm-datepicker/priv-datepicker/priv-datepicker.js +1015 -1015
  42. package/dist/collection/components/wm-datepicker/wm-datepicker.js +462 -462
  43. package/dist/collection/components/wm-input/wm-input.js +423 -423
  44. package/dist/collection/components/wm-menuitem/wm-menuitem.js +444 -444
  45. package/dist/collection/components/wm-modal/wm-modal-footer.js +141 -141
  46. package/dist/collection/components/wm-modal/wm-modal-header.js +92 -92
  47. package/dist/collection/components/wm-modal/wm-modal.js +459 -459
  48. package/dist/collection/components/wm-navigator/priv-navigator-button/priv-navigator-button.js +97 -97
  49. package/dist/collection/components/wm-navigator/priv-navigator-item/priv-navigator-item.js +114 -114
  50. package/dist/collection/components/wm-navigator/wm-navigator.js +468 -468
  51. package/dist/collection/components/wm-option/wm-option.js +422 -422
  52. package/dist/collection/components/wm-pagination/wm-pagination.js +368 -368
  53. package/dist/collection/components/wm-search/wm-search.js +479 -479
  54. package/dist/collection/components/wm-select/wm-select.js +717 -717
  55. package/dist/collection/components/wm-snackbar/wm-snackbar.js +297 -297
  56. package/dist/collection/components/wm-tabs/wm-tab-item/wm-tab-item.js +219 -219
  57. package/dist/collection/components/wm-tabs/wm-tab-list/wm-tab-list.js +331 -331
  58. package/dist/collection/components/wm-tabs/wm-tab-panel/wm-tab-panel.js +104 -104
  59. package/dist/collection/components/wm-tag-input/wm-tag-input.js +787 -787
  60. package/dist/collection/components/wm-timepicker/wm-timepicker.js +589 -589
  61. package/dist/collection/components/wm-toggletip/wm-toggletip.js +241 -241
  62. package/dist/collection/components/wm-uploader/wm-network-uploader/wm-network-uploader.js +753 -753
  63. package/dist/collection/components/wm-uploader/wm-uploader.js +748 -748
  64. package/dist/collection/components/wm-wrapper/wm-wrapper.js +27 -27
  65. package/dist/collection/dev/scripts.js +20 -20
  66. package/dist/collection/global/__mocks__/functions.js +6 -6
  67. package/dist/collection/global/functions.js +445 -445
  68. package/dist/collection/global/global.js +72 -72
  69. package/dist/collection/global/interfaces.js +49 -49
  70. package/dist/collection/global/services/__mocks__/http-service.js +130 -130
  71. package/dist/collection/global/services/http-service.js +50 -50
  72. package/dist/collection/lang/lang.js +5 -5
  73. package/dist/collection/lang/piglatin.js +93 -93
  74. package/dist/esm/functions-e528c934.js +6144 -0
  75. package/dist/esm/{global-08ca269c.js → global-b94f59e6.js} +65 -65
  76. package/dist/esm/{http-service-5d037e16.js → http-service-3dc3b3e7.js} +49 -49
  77. package/dist/esm/{index-f8b130b6.js → index-66f8130e.js} +157 -103
  78. package/dist/esm/{interfaces-61c6305b.js → interfaces-2b97fab2.js} +29 -29
  79. package/dist/esm/loader.js +3 -3
  80. package/dist/esm/polyfills/core-js.js +0 -0
  81. package/dist/esm/polyfills/dom.js +0 -0
  82. package/dist/esm/polyfills/es5-html-element.js +0 -0
  83. package/dist/esm/polyfills/index.js +2 -2
  84. package/dist/esm/polyfills/system.js +0 -0
  85. package/dist/esm/priv-chart-popover.entry.js +79 -79
  86. package/dist/esm/priv-datepicker.entry.js +668 -668
  87. package/dist/esm/priv-navigator-button.entry.js +19 -19
  88. package/dist/esm/priv-navigator-item.entry.js +24 -24
  89. package/dist/esm/ripple.js +5 -5
  90. package/dist/esm/wm-action-menu_2.entry.js +342 -342
  91. package/dist/esm/wm-button.entry.js +219 -219
  92. package/dist/esm/wm-chart-slice.entry.js +14 -14
  93. package/dist/esm/wm-chart.entry.js +475 -517
  94. package/dist/esm/wm-datepicker.entry.js +257 -257
  95. package/dist/esm/wm-input.entry.js +134 -134
  96. package/dist/esm/wm-modal-footer.entry.js +32 -32
  97. package/dist/esm/wm-modal-header.entry.js +35 -35
  98. package/dist/esm/wm-modal.entry.js +162 -162
  99. package/dist/esm/wm-navigator.entry.js +269 -269
  100. package/dist/esm/wm-network-uploader.entry.js +421 -421
  101. package/dist/esm/wm-option_2.entry.js +492 -492
  102. package/dist/esm/wm-pagination.entry.js +168 -168
  103. package/dist/esm/wm-search.entry.js +232 -232
  104. package/dist/esm/wm-snackbar.entry.js +171 -171
  105. package/dist/esm/wm-tab-item_3.entry.js +301 -301
  106. package/dist/esm/wm-tag-input.entry.js +557 -557
  107. package/dist/esm/wm-timepicker.entry.js +384 -384
  108. package/dist/esm/wm-toggletip.entry.js +125 -125
  109. package/dist/esm/wm-uploader.entry.js +347 -347
  110. package/dist/esm/wm-wrapper.entry.js +13 -13
  111. package/dist/ripple/p-092b01f3.entry.js +1 -0
  112. package/dist/ripple/{p-c995acbc.entry.js → p-0c58f50d.entry.js} +1 -1
  113. package/dist/ripple/p-11c09317.entry.js +1 -0
  114. package/dist/ripple/p-1e0c41a9.entry.js +1 -0
  115. package/dist/ripple/p-3003d26d.entry.js +1 -0
  116. package/dist/ripple/p-33524565.entry.js +1 -0
  117. package/dist/ripple/{p-888bec42.js → p-43f1298b.js} +0 -0
  118. package/dist/ripple/{p-5e49aba7.entry.js → p-4cc71463.entry.js} +1 -1
  119. package/dist/ripple/p-588b4475.js +16 -0
  120. package/dist/ripple/p-65e3a656.entry.js +1 -0
  121. package/dist/ripple/p-7a96bb97.js +1 -0
  122. package/dist/ripple/p-83b8aaaf.entry.js +1 -0
  123. package/dist/ripple/p-8923b7d0.entry.js +1 -0
  124. package/dist/ripple/{p-d52033d5.entry.js → p-8b0eb05e.entry.js} +1 -1
  125. package/dist/ripple/p-8cd1396e.entry.js +1 -0
  126. package/dist/ripple/{p-22a1a636.entry.js → p-99db8501.entry.js} +1 -1
  127. package/dist/ripple/p-9baa3039.js +1 -0
  128. package/dist/ripple/p-aa973691.entry.js +1 -0
  129. package/dist/ripple/{p-a61977b2.entry.js → p-b3d5ea85.entry.js} +1 -1
  130. package/dist/ripple/{p-7d009793.entry.js → p-b5189f72.entry.js} +1 -1
  131. package/dist/ripple/p-bc27b604.entry.js +1 -0
  132. package/dist/ripple/p-bc9ca97b.entry.js +1 -0
  133. package/dist/ripple/{p-bba99fcd.entry.js → p-cd58a15c.entry.js} +1 -1
  134. package/dist/ripple/p-dc9c9fda.entry.js +1 -0
  135. package/dist/ripple/p-e7616311.entry.js +1 -0
  136. package/dist/ripple/p-e9e8334e.entry.js +1 -0
  137. package/dist/ripple/p-ec9697db.entry.js +1 -0
  138. package/dist/ripple/p-efdaf3b6.entry.js +1 -0
  139. package/dist/ripple/p-fcdc6395.entry.js +1 -0
  140. package/dist/ripple/{p-a6d6eae7.js → p-fd8070fb.js} +0 -0
  141. package/dist/ripple/ripple.esm.js +1 -1
  142. package/dist/types/components/wm-action-menu/wm-action-menu.d.ts +48 -48
  143. package/dist/types/components/wm-button/wm-button.d.ts +44 -44
  144. package/dist/types/components/wm-chart/priv-chart-popover/priv-chart-popover.d.ts +23 -23
  145. package/dist/types/components/wm-chart/wm-chart-slice.d.ts +8 -8
  146. package/dist/types/components/wm-chart/wm-chart.d.ts +81 -80
  147. package/dist/types/components/wm-datepicker/priv-datepicker/priv-datepicker.d.ts +76 -76
  148. package/dist/types/components/wm-datepicker/wm-datepicker.d.ts +41 -41
  149. package/dist/types/components/wm-input/wm-input.d.ts +46 -46
  150. package/dist/types/components/wm-menuitem/wm-menuitem.d.ts +34 -34
  151. package/dist/types/components/wm-modal/wm-modal-footer.d.ts +15 -15
  152. package/dist/types/components/wm-modal/wm-modal-header.d.ts +12 -12
  153. package/dist/types/components/wm-modal/wm-modal.d.ts +41 -41
  154. package/dist/types/components/wm-navigator/priv-navigator-button/priv-navigator-button.d.ts +10 -10
  155. package/dist/types/components/wm-navigator/priv-navigator-item/priv-navigator-item.d.ts +13 -13
  156. package/dist/types/components/wm-navigator/wm-navigator.d.ts +61 -61
  157. package/dist/types/components/wm-option/wm-option.d.ts +32 -32
  158. package/dist/types/components/wm-pagination/wm-pagination.d.ts +32 -32
  159. package/dist/types/components/wm-search/wm-search.d.ts +86 -86
  160. package/dist/types/components/wm-select/wm-select.d.ts +66 -66
  161. package/dist/types/components/wm-snackbar/wm-snackbar.d.ts +35 -35
  162. package/dist/types/components/wm-tabs/wm-tab-item/wm-tab-item.d.ts +38 -38
  163. package/dist/types/components/wm-tabs/wm-tab-list/wm-tab-list.d.ts +53 -53
  164. package/dist/types/components/wm-tabs/wm-tab-panel/wm-tab-panel.d.ts +20 -20
  165. package/dist/types/components/wm-tag-input/wm-tag-input.d.ts +91 -91
  166. package/dist/types/components/wm-timepicker/wm-timepicker.d.ts +62 -62
  167. package/dist/types/components/wm-toggletip/wm-toggletip.d.ts +27 -27
  168. package/dist/types/components/wm-uploader/wm-network-uploader/wm-network-uploader.d.ts +85 -85
  169. package/dist/types/components/wm-uploader/wm-uploader.d.ts +80 -80
  170. package/dist/types/components/wm-wrapper/wm-wrapper.d.ts +7 -7
  171. package/dist/types/components.d.ts +24 -24
  172. package/dist/types/global/__mocks__/functions.d.ts +6 -6
  173. package/dist/types/global/functions.d.ts +40 -0
  174. package/dist/types/global/global.d.ts +1 -1
  175. package/dist/types/global/interfaces.d.ts +34 -34
  176. package/dist/types/global/services/__mocks__/http-service.d.ts +6 -6
  177. package/dist/types/global/services/http-service.d.ts +4 -4
  178. package/dist/types/lang/lang.d.ts +5 -5
  179. package/dist/types/stencil-public-runtime.d.ts +194 -186
  180. package/package.json +46 -46
  181. package/dist/cjs/functions-a6491ba8.js +0 -7943
  182. package/dist/esm/functions-5aebd578.js +0 -7923
  183. package/dist/ripple/p-0865e7cf.js +0 -16
  184. package/dist/ripple/p-0f663e59.entry.js +0 -1
  185. package/dist/ripple/p-1b790a7d.entry.js +0 -1
  186. package/dist/ripple/p-3b4c01b8.entry.js +0 -1
  187. package/dist/ripple/p-54c1f704.entry.js +0 -1
  188. package/dist/ripple/p-76c615e9.entry.js +0 -1
  189. package/dist/ripple/p-7d993d52.js +0 -1
  190. package/dist/ripple/p-8198cc9c.entry.js +0 -1
  191. package/dist/ripple/p-839d35bd.entry.js +0 -1
  192. package/dist/ripple/p-8708b8cb.entry.js +0 -1
  193. package/dist/ripple/p-8bd66955.entry.js +0 -1
  194. package/dist/ripple/p-957e3a05.entry.js +0 -1
  195. package/dist/ripple/p-af6eb4d5.entry.js +0 -1
  196. package/dist/ripple/p-b3f9f575.entry.js +0 -1
  197. package/dist/ripple/p-cd30198d.entry.js +0 -1
  198. package/dist/ripple/p-d9d6bf11.js +0 -1
  199. package/dist/ripple/p-e097c562.entry.js +0 -1
  200. package/dist/ripple/p-e9db6122.entry.js +0 -1
  201. package/dist/ripple/p-eabbd1e9.entry.js +0 -1
  202. package/dist/ripple/p-ec496b8b.entry.js +0 -1
  203. package/dist/ripple/p-fc8ec142.entry.js +0 -1
@@ -1,589 +1,589 @@
1
- import { h, Component, Host, Element, Prop, Watch, State, Listen, Event } from "@stencil/core";
2
- import { intl, generateId, shouldOpenUp, isRelatedTarget } from "../../global/functions";
3
- export class Timepicker {
4
- constructor() {
5
- this.disabled = false;
6
- this.value = "";
7
- this.label = "";
8
- this.labelPosition = "top";
9
- this.requiredField = false;
10
- this.isExpanded = false;
11
- this.isTabbing = false;
12
- this.twelveHrValid = /^(0?[0-9]|1[0-2])\s*:?\s*([0-5][0-9])?\s?(a|p|am|pm)?$/i;
13
- this.twentyFourHrValid = /^(0?[0-9]|1[0-9]|2[0-4])\s*:?\s*([0-5][0-9])?$/;
14
- this.uid = "";
15
- this.timeFormat = "hh:mm";
16
- this.times = [];
17
- this.openUp = false;
18
- this.buttonAriaLabel = intl.formatMessage({
19
- id: "time.selectTime",
20
- defaultMessage: "Select time",
21
- });
22
- }
23
- toggleTabbingOn() {
24
- this.isTabbing = true;
25
- }
26
- toggleTabbingOff() {
27
- this.isTabbing = false;
28
- }
29
- blurHandler(ev) {
30
- if (ev.target !== this.el && this.isExpanded) {
31
- this.close();
32
- }
33
- }
34
- handleKey(ev) {
35
- switch (ev.key) {
36
- case "ArrowDown":
37
- ev.preventDefault();
38
- if (this.isExpanded === false) {
39
- this.open("next");
40
- }
41
- else {
42
- this.moveDown(this.selectedOption);
43
- }
44
- break;
45
- case "ArrowUp":
46
- ev.preventDefault();
47
- if (this.isExpanded === false) {
48
- this.open("previous");
49
- }
50
- else {
51
- this.moveUp(this.selectedOption);
52
- }
53
- break;
54
- case "Enter":
55
- case " ":
56
- if (this.isExpanded) {
57
- ev.preventDefault();
58
- this.handleOptionClick(this.selectedOption.textContent);
59
- }
60
- break;
61
- case "Escape":
62
- ev.preventDefault();
63
- if (this.isExpanded) {
64
- ev.stopPropagation(); // for instance if select is in a modal, esc should close the select but not the modal
65
- this.close();
66
- }
67
- break;
68
- case "Tab":
69
- if (this.isExpanded) {
70
- this.close();
71
- }
72
- break;
73
- case "Home":
74
- ev.preventDefault();
75
- if (this.isExpanded) {
76
- this.focusOption(this.optionsList[0]);
77
- this.setDropdownPosition("first");
78
- }
79
- break;
80
- case "End":
81
- ev.preventDefault();
82
- if (this.isExpanded) {
83
- this.focusOption(this.optionsList[this.optionsList.length - 1]);
84
- this.setDropdownPosition("last");
85
- }
86
- break;
87
- }
88
- }
89
- updateErrorState() {
90
- this.displayedErrorMessage = this.errorMessage;
91
- }
92
- componentWillLoad() {
93
- if (this.label === "") {
94
- throw new Error("You must include a label prop for the datepicker input (for accessibility requirements), even if the label position is none.");
95
- }
96
- this.el.focus = function () {
97
- if (!this.disabled) {
98
- this.shadowRoot.querySelector("input").focus();
99
- }
100
- };
101
- this.uid = this.el.id ? this.el.id : generateId();
102
- this.updateErrorState();
103
- this.timeFormat = intl.formatMessage({
104
- id: "time.timeFormat",
105
- defaultMessage: "hh:mm",
106
- });
107
- this.times = this.generateTimes();
108
- }
109
- componentDidLoad() {
110
- this.optionsEl.classList.add("hidden");
111
- this.optionsList = Array.from(this.optionsEl.querySelectorAll("li"));
112
- if (this.value) {
113
- this.processInput();
114
- }
115
- }
116
- isValidTime(input) {
117
- return this.twelveHrValid.test(input) || this.twentyFourHrValid.test(input);
118
- }
119
- generateTimes() {
120
- let times = [];
121
- let startTime = 0;
122
- for (let i = 0; startTime < 24 * 60; i++) {
123
- const h = Math.floor(startTime / 60);
124
- const hour = h.toString().padStart(2, "0");
125
- const m = (startTime % 60).toString();
126
- const min = m.padStart(2, "0");
127
- times[i] = `${hour}:${min}`;
128
- startTime = startTime + 15;
129
- }
130
- return times;
131
- }
132
- findNearestTimeInterval(time) {
133
- let formattedTime = this.formatToStorage(time);
134
- const minutes = parseInt(formattedTime.slice(3, 5));
135
- // first find the closest 15 min increment
136
- if (minutes % 15 !== 0) {
137
- const hour = parseInt(formattedTime.slice(0, 2));
138
- let roundedMinutes = (Math.round(minutes / 15) * 15).toString().padStart(2, "0");
139
- if (roundedMinutes === "60") {
140
- roundedMinutes = "00";
141
- let roundedHour = hour + 1;
142
- if (roundedHour === 24) {
143
- roundedHour = 0;
144
- }
145
- formattedTime = formattedTime.replace(`${hour.toString().padStart(2, "0")}:`, `${roundedHour.toString().padStart(2, "0")}:`);
146
- }
147
- if (parseInt(roundedMinutes) < 8) {
148
- roundedMinutes = "00";
149
- }
150
- time = formattedTime.replace(`:${minutes.toString().padStart(2, "0")}`, `:${roundedMinutes}`);
151
- }
152
- return time;
153
- }
154
- handleListSelection(time) {
155
- time = this.findNearestTimeInterval(time);
156
- const option = this.optionsList.filter((o) => o.textContent == this.formatToDisplay(time))[0];
157
- // not DOM focus, just setting the proper ARIA attributes
158
- option && this.focusOption(option);
159
- }
160
- setValue(time) {
161
- const previousValue = this.value;
162
- this.inputEl.value = this.formatToDisplay(time);
163
- this.value = this.formatToStorage(time);
164
- // update the selection in the list
165
- // so it opens on the closest item
166
- this.handleListSelection(time);
167
- if (previousValue !== this.value) {
168
- this.wmTimepickerNewValidValue.emit({ value: this.value });
169
- }
170
- // deprecated in favor of wmTimepickerNewValidValue and input
171
- this.wmTimepickerOnChange.emit({
172
- value: this.value,
173
- isValid: !!this.displayedErrorMessage,
174
- });
175
- }
176
- processInput() {
177
- if (this.isValidTime(this.value)) {
178
- this.setValue(this.value);
179
- }
180
- const newErrorMessage = this.determineErrorMessage();
181
- this.displayedErrorMessage = newErrorMessage;
182
- }
183
- determineErrorMessage() {
184
- let message = this.errorMessage;
185
- const requiredError = intl.formatMessage({
186
- id: "time.requiredError",
187
- defaultMessage: "A time is required.",
188
- });
189
- const invalidError = intl.formatMessage({
190
- id: "time.invalidTime",
191
- defaultMessage: "Please enter a valid time.",
192
- });
193
- const isValid = this.isValidTime(this.value);
194
- if (isValid && !this.errorMessage) {
195
- message = null;
196
- }
197
- else if (!isValid && !this.errorMessage) {
198
- if (this.requiredField && !this.value) {
199
- message = requiredError;
200
- }
201
- else if (this.value) {
202
- message = invalidError;
203
- }
204
- else {
205
- message = null;
206
- }
207
- }
208
- return message;
209
- }
210
- splitTime(time) {
211
- const splitTime = this.twelveHrValid.exec(time) || this.twentyFourHrValid.exec(time);
212
- let hours = parseInt(splitTime[1]);
213
- let minutes = splitTime[2] || "00";
214
- let amPm = splitTime ? splitTime[3] : undefined;
215
- // handles edge case uncaught by regex '0pm'
216
- if (hours === 0 && (amPm === null || amPm === void 0 ? void 0 : amPm.toUpperCase().includes("P"))) {
217
- // if user types '0pm' we change it to midnight
218
- amPm = "AM";
219
- }
220
- return [hours, minutes, amPm];
221
- }
222
- formatToDisplay(time) {
223
- let [hours, minutes, amPm] = this.splitTime(time);
224
- // at the moment the component only displays in 12hr AM/PM
225
- if (hours === 12 && !amPm) {
226
- amPm = "PM";
227
- }
228
- if (hours === 0 || hours === 24) {
229
- hours = 12;
230
- }
231
- if (hours > 12) {
232
- hours -= 12;
233
- amPm = "PM";
234
- }
235
- if (amPm && amPm.toUpperCase().includes("P")) {
236
- amPm = "PM";
237
- }
238
- else {
239
- amPm = "AM";
240
- }
241
- return `${hours.toString().padStart(2, "0")}:${minutes} ${amPm}`;
242
- }
243
- formatToStorage(time) {
244
- let [hours, minutes, amPm] = this.splitTime(time);
245
- if (hours === 24) {
246
- hours = 0;
247
- }
248
- if (hours === 12 && amPm && amPm.toUpperCase().includes("A")) {
249
- hours -= 12;
250
- }
251
- if (amPm && amPm.toUpperCase().includes("P") && hours !== 12) {
252
- hours += 12;
253
- }
254
- return `${hours.toString().padStart(2, "0")}:${minutes}`;
255
- }
256
- open(itemToSelect) {
257
- this.el.focus();
258
- this.openUp = shouldOpenUp(this.el, this.optionsEl);
259
- this.isExpanded = true;
260
- this.optionsEl.classList.remove("hidden");
261
- if (this.errorMessage || !this.value) {
262
- this.handleListSelection("09:00");
263
- }
264
- this.setDropdownPosition("center", this.selectedOption);
265
- window.requestAnimationFrame(() => {
266
- if (itemToSelect === "next") {
267
- this.moveDown(this.selectedOption);
268
- }
269
- else if (itemToSelect === "previous") {
270
- this.moveUp(this.selectedOption);
271
- }
272
- });
273
- }
274
- close(returnFocus = true) {
275
- this.isExpanded = false;
276
- window.setTimeout(() => {
277
- this.optionsEl.classList.add("hidden");
278
- // Returns focus to button after popup closes (no need if user is tabbing)
279
- // Delay is necessary for screenreader to get new expanded state before focus
280
- // window.requestAnimationFrame is probably enough, but since we are already using setTimeout it may as well be here
281
- // also UX wise, it makes sense for the button to only be focused after the animation is complete
282
- if (returnFocus) {
283
- this.buttonEl.focus();
284
- }
285
- }, 150);
286
- }
287
- focusOption(item) {
288
- // not an actual focus, just accessibility stuff
289
- this.optionsList.forEach((option) => {
290
- option.removeAttribute("aria-selected");
291
- });
292
- this.inputEl.setAttribute("aria-activedescendant", item.id);
293
- item.setAttribute("aria-selected", "true");
294
- this.selectedOption = item;
295
- }
296
- setDropdownPosition(position, item) {
297
- switch (position) {
298
- case "top":
299
- const prevItem = item === null || item === void 0 ? void 0 : item.previousElementSibling;
300
- this.optionsEl.scrollTop =
301
- prevItem.getBoundingClientRect().top - this.optionsEl.getBoundingClientRect().top + this.optionsEl.scrollTop;
302
- break;
303
- case "bottom":
304
- const nextItem = item === null || item === void 0 ? void 0 : item.nextElementSibling;
305
- this.optionsEl.scrollTop =
306
- nextItem.getBoundingClientRect().bottom -
307
- this.optionsEl.getBoundingClientRect().top +
308
- this.optionsEl.scrollTop -
309
- this.optionsEl.offsetHeight;
310
- break;
311
- case "center":
312
- this.optionsEl.scrollTop =
313
- (this.optionsList.findIndex((x) => x.textContent === (item === null || item === void 0 ? void 0 : item.textContent)) - 2) * item.offsetHeight;
314
- break;
315
- case "first":
316
- this.optionsEl.scrollTop = 0;
317
- break;
318
- case "last":
319
- this.optionsEl.scrollTop = this.optionsList[0].clientHeight * this.optionsList.length;
320
- break;
321
- }
322
- }
323
- moveUp(el) {
324
- const prevEl = el.previousElementSibling;
325
- if (prevEl) {
326
- // scroll option to top of dropdown if partially obscured / out of view
327
- if (prevEl.getBoundingClientRect().top < this.optionsEl.getBoundingClientRect().top) {
328
- this.setDropdownPosition("top", el);
329
- }
330
- this.focusOption(prevEl);
331
- }
332
- else {
333
- this.focusOption(this.optionsList[this.optionsList.length - 1]);
334
- this.setDropdownPosition("last");
335
- }
336
- }
337
- moveDown(el) {
338
- const nextEl = el.nextElementSibling;
339
- if (nextEl) {
340
- // scroll option to bottom of dropdown if partially obscured / out of view
341
- if (nextEl.getBoundingClientRect().bottom > this.optionsEl.getBoundingClientRect().bottom) {
342
- this.setDropdownPosition("bottom", el);
343
- }
344
- this.focusOption(nextEl);
345
- }
346
- else {
347
- this.focusOption(this.optionsList[0]);
348
- this.setDropdownPosition("first");
349
- }
350
- }
351
- handleOptionClick(time) {
352
- this.close();
353
- this.setValue(time);
354
- this.processInput();
355
- }
356
- handleInput() {
357
- this.value = this.inputEl.value;
358
- if (this.isValidTime(this.value)) {
359
- this.handleListSelection(this.findNearestTimeInterval(this.value));
360
- this.setDropdownPosition("center", this.selectedOption);
361
- }
362
- }
363
- handleInputBlur(ev) {
364
- // do not validate if clicking to an element that should prevent validation (e.g. close button on modal)
365
- const shouldPreventValidation = this.preventValidation && isRelatedTarget(ev, this.preventValidation);
366
- if (!shouldPreventValidation) {
367
- this.processInput();
368
- }
369
- this.tpWrapper.classList.remove("focus");
370
- }
371
- renderOptions() {
372
- return this.times.map((time, index) => (h("li", { id: `option${index + 1}`, role: "option", "aria-selected": time === "09:00" ? "true" : false, onClick: () => this.handleOptionClick(time) }, this.formatToDisplay(time))));
373
- }
374
- render() {
375
- return (h(Host, { id: this.uid, invalid: !!this.displayedErrorMessage ? "true" : null, onBlur: () => this.close(false) },
376
- h("div", { class: `wrapper label-${this.labelPosition} ${!!this.displayedErrorMessage ? "invalid" : ""}`, ref: (t) => (this.tpWrapper = t) },
377
- h("div", { class: "label-wrapper" }, this.labelPosition !== "none" && (h("label", { id: `label-${this.uid}`, class: "label", htmlFor: `time-input-${this.uid}` },
378
- this.label,
379
- this.requiredField && (h("span", { class: "required", "aria-hidden": "true" }, "*"))))),
380
- h("div", null,
381
- h("div", { class: "inner-wrapper" },
382
- h("input", { id: `time-input-${this.uid}`, "aria-label": this.label, "aria-describedby": `error-${this.uid}`, ref: (el) => (this.inputEl = el), onBlur: (ev) => this.handleInputBlur(ev), onInput: () => this.handleInput(), disabled: this.disabled, required: this.requiredField, placeholder: this.timeFormat, autocomplete: "off", onFocus: () => this.tpWrapper.classList.add("focus") }),
383
- h("button", { id: `btn-${this.uid}`, class: this.isTabbing ? "user-is-tabbing" : "", ref: (el) => (this.buttonEl = el), disabled: this.disabled, "aria-controls": `list-${this.uid}`, "aria-expanded": this.isExpanded ? "true" : "false", "aria-label": this.buttonAriaLabel, "aria-describedby": `time-input-${this.uid}`, onClick: () => (this.isExpanded ? this.close() : this.open()) },
384
- h("span", { class: "clock" })),
385
- h("ul", { class: `options ${this.isExpanded ? "open" : ""} ${this.openUp ? "upwards" : ""}`, id: `list-${this.uid}`, role: "listbox", "aria-labelledby": `label-${this.uid}`, "aria-describedby": this.isExpanded ? "collapsed" : null, ref: (el) => (this.optionsEl = el) }, this.renderOptions())),
386
- h("div", { id: `error-${this.uid}`, class: "error", "aria-live": "assertive", "aria-atomic": "true" }, this.displayedErrorMessage)))));
387
- }
388
- static get is() { return "wm-timepicker"; }
389
- static get encapsulation() { return "shadow"; }
390
- static get originalStyleUrls() { return {
391
- "$": ["wm-timepicker.scss"]
392
- }; }
393
- static get styleUrls() { return {
394
- "$": ["wm-timepicker.css"]
395
- }; }
396
- static get properties() { return {
397
- "disabled": {
398
- "type": "boolean",
399
- "mutable": false,
400
- "complexType": {
401
- "original": "boolean",
402
- "resolved": "boolean",
403
- "references": {}
404
- },
405
- "required": false,
406
- "optional": false,
407
- "docs": {
408
- "tags": [],
409
- "text": ""
410
- },
411
- "attribute": "disabled",
412
- "reflect": false,
413
- "defaultValue": "false"
414
- },
415
- "value": {
416
- "type": "string",
417
- "mutable": true,
418
- "complexType": {
419
- "original": "string",
420
- "resolved": "string",
421
- "references": {}
422
- },
423
- "required": false,
424
- "optional": false,
425
- "docs": {
426
- "tags": [],
427
- "text": ""
428
- },
429
- "attribute": "value",
430
- "reflect": true,
431
- "defaultValue": "\"\""
432
- },
433
- "errorMessage": {
434
- "type": "string",
435
- "mutable": false,
436
- "complexType": {
437
- "original": "string | null",
438
- "resolved": "null | string | undefined",
439
- "references": {}
440
- },
441
- "required": false,
442
- "optional": true,
443
- "docs": {
444
- "tags": [],
445
- "text": ""
446
- },
447
- "attribute": "error-message",
448
- "reflect": false
449
- },
450
- "label": {
451
- "type": "string",
452
- "mutable": false,
453
- "complexType": {
454
- "original": "string",
455
- "resolved": "string",
456
- "references": {}
457
- },
458
- "required": false,
459
- "optional": false,
460
- "docs": {
461
- "tags": [],
462
- "text": ""
463
- },
464
- "attribute": "label",
465
- "reflect": false,
466
- "defaultValue": "\"\""
467
- },
468
- "labelPosition": {
469
- "type": "string",
470
- "mutable": false,
471
- "complexType": {
472
- "original": "\"top\" | \"left\" | \"none\"",
473
- "resolved": "\"left\" | \"none\" | \"top\"",
474
- "references": {}
475
- },
476
- "required": false,
477
- "optional": false,
478
- "docs": {
479
- "tags": [],
480
- "text": ""
481
- },
482
- "attribute": "label-position",
483
- "reflect": false,
484
- "defaultValue": "\"top\""
485
- },
486
- "requiredField": {
487
- "type": "boolean",
488
- "mutable": false,
489
- "complexType": {
490
- "original": "boolean",
491
- "resolved": "boolean",
492
- "references": {}
493
- },
494
- "required": false,
495
- "optional": false,
496
- "docs": {
497
- "tags": [],
498
- "text": ""
499
- },
500
- "attribute": "required-field",
501
- "reflect": false,
502
- "defaultValue": "false"
503
- },
504
- "preventValidation": {
505
- "type": "string",
506
- "mutable": false,
507
- "complexType": {
508
- "original": "string",
509
- "resolved": "string | undefined",
510
- "references": {}
511
- },
512
- "required": false,
513
- "optional": true,
514
- "docs": {
515
- "tags": [],
516
- "text": ""
517
- },
518
- "attribute": "prevent-validation",
519
- "reflect": false
520
- }
521
- }; }
522
- static get states() { return {
523
- "selectedOption": {},
524
- "isExpanded": {},
525
- "isTabbing": {},
526
- "displayedErrorMessage": {}
527
- }; }
528
- static get events() { return [{
529
- "method": "wmTimepickerNewValidValue",
530
- "name": "wmTimepickerNewValidValue",
531
- "bubbles": true,
532
- "cancelable": true,
533
- "composed": true,
534
- "docs": {
535
- "tags": [],
536
- "text": ""
537
- },
538
- "complexType": {
539
- "original": "{ value: string }",
540
- "resolved": "{ value: string; }",
541
- "references": {}
542
- }
543
- }, {
544
- "method": "wmTimepickerOnChange",
545
- "name": "wmTimepickerOnChange",
546
- "bubbles": true,
547
- "cancelable": true,
548
- "composed": true,
549
- "docs": {
550
- "tags": [],
551
- "text": ""
552
- },
553
- "complexType": {
554
- "original": "{\n // deprecated in favor of wmTimepickerNewValidValue and standard input event\n value: string;\n isValid: boolean;\n }",
555
- "resolved": "{ value: string; isValid: boolean; }",
556
- "references": {}
557
- }
558
- }]; }
559
- static get elementRef() { return "el"; }
560
- static get watchers() { return [{
561
- "propName": "errorMessage",
562
- "methodName": "updateErrorState"
563
- }]; }
564
- static get listeners() { return [{
565
- "name": "wmUserIsTabbing",
566
- "method": "toggleTabbingOn",
567
- "target": "window",
568
- "capture": false,
569
- "passive": false
570
- }, {
571
- "name": "wmUserIsNotTabbing",
572
- "method": "toggleTabbingOff",
573
- "target": "window",
574
- "capture": false,
575
- "passive": false
576
- }, {
577
- "name": "click",
578
- "method": "blurHandler",
579
- "target": "document",
580
- "capture": false,
581
- "passive": false
582
- }, {
583
- "name": "keydown",
584
- "method": "handleKey",
585
- "target": undefined,
586
- "capture": false,
587
- "passive": false
588
- }]; }
589
- }
1
+ import { h, Component, Host, Element, Prop, Watch, State, Listen, Event } from "@stencil/core";
2
+ import { intl, generateId, shouldOpenUp, isRelatedTarget } from "../../global/functions";
3
+ export class Timepicker {
4
+ constructor() {
5
+ this.disabled = false;
6
+ this.value = "";
7
+ this.label = "";
8
+ this.labelPosition = "top";
9
+ this.requiredField = false;
10
+ this.isExpanded = false;
11
+ this.isTabbing = false;
12
+ this.twelveHrValid = /^(0?[0-9]|1[0-2])\s*:?\s*([0-5][0-9])?\s?(a|p|am|pm)?$/i;
13
+ this.twentyFourHrValid = /^(0?[0-9]|1[0-9]|2[0-4])\s*:?\s*([0-5][0-9])?$/;
14
+ this.uid = "";
15
+ this.timeFormat = "hh:mm";
16
+ this.times = [];
17
+ this.openUp = false;
18
+ this.buttonAriaLabel = intl.formatMessage({
19
+ id: "time.selectTime",
20
+ defaultMessage: "Select time",
21
+ });
22
+ }
23
+ toggleTabbingOn() {
24
+ this.isTabbing = true;
25
+ }
26
+ toggleTabbingOff() {
27
+ this.isTabbing = false;
28
+ }
29
+ blurHandler(ev) {
30
+ if (ev.target !== this.el && this.isExpanded) {
31
+ this.close();
32
+ }
33
+ }
34
+ handleKey(ev) {
35
+ switch (ev.key) {
36
+ case "ArrowDown":
37
+ ev.preventDefault();
38
+ if (this.isExpanded === false) {
39
+ this.open("next");
40
+ }
41
+ else {
42
+ this.moveDown(this.selectedOption);
43
+ }
44
+ break;
45
+ case "ArrowUp":
46
+ ev.preventDefault();
47
+ if (this.isExpanded === false) {
48
+ this.open("previous");
49
+ }
50
+ else {
51
+ this.moveUp(this.selectedOption);
52
+ }
53
+ break;
54
+ case "Enter":
55
+ case " ":
56
+ if (this.isExpanded) {
57
+ ev.preventDefault();
58
+ this.handleOptionClick(this.selectedOption.textContent);
59
+ }
60
+ break;
61
+ case "Escape":
62
+ ev.preventDefault();
63
+ if (this.isExpanded) {
64
+ ev.stopPropagation(); // for instance if select is in a modal, esc should close the select but not the modal
65
+ this.close();
66
+ }
67
+ break;
68
+ case "Tab":
69
+ if (this.isExpanded) {
70
+ this.close();
71
+ }
72
+ break;
73
+ case "Home":
74
+ ev.preventDefault();
75
+ if (this.isExpanded) {
76
+ this.focusOption(this.optionsList[0]);
77
+ this.setDropdownPosition("first");
78
+ }
79
+ break;
80
+ case "End":
81
+ ev.preventDefault();
82
+ if (this.isExpanded) {
83
+ this.focusOption(this.optionsList[this.optionsList.length - 1]);
84
+ this.setDropdownPosition("last");
85
+ }
86
+ break;
87
+ }
88
+ }
89
+ updateErrorState() {
90
+ this.displayedErrorMessage = this.errorMessage;
91
+ }
92
+ componentWillLoad() {
93
+ if (this.label === "") {
94
+ throw new Error("You must include a label prop for the datepicker input (for accessibility requirements), even if the label position is none.");
95
+ }
96
+ this.el.focus = function () {
97
+ if (!this.disabled) {
98
+ this.shadowRoot.querySelector("input").focus();
99
+ }
100
+ };
101
+ this.uid = this.el.id ? this.el.id : generateId();
102
+ this.updateErrorState();
103
+ this.timeFormat = intl.formatMessage({
104
+ id: "time.timeFormat",
105
+ defaultMessage: "hh:mm",
106
+ });
107
+ this.times = this.generateTimes();
108
+ }
109
+ componentDidLoad() {
110
+ this.optionsEl.classList.add("hidden");
111
+ this.optionsList = Array.from(this.optionsEl.querySelectorAll("li"));
112
+ if (this.value) {
113
+ this.processInput();
114
+ }
115
+ }
116
+ isValidTime(input) {
117
+ return this.twelveHrValid.test(input) || this.twentyFourHrValid.test(input);
118
+ }
119
+ generateTimes() {
120
+ let times = [];
121
+ let startTime = 0;
122
+ for (let i = 0; startTime < 24 * 60; i++) {
123
+ const h = Math.floor(startTime / 60);
124
+ const hour = h.toString().padStart(2, "0");
125
+ const m = (startTime % 60).toString();
126
+ const min = m.padStart(2, "0");
127
+ times[i] = `${hour}:${min}`;
128
+ startTime = startTime + 15;
129
+ }
130
+ return times;
131
+ }
132
+ findNearestTimeInterval(time) {
133
+ let formattedTime = this.formatToStorage(time);
134
+ const minutes = parseInt(formattedTime.slice(3, 5));
135
+ // first find the closest 15 min increment
136
+ if (minutes % 15 !== 0) {
137
+ const hour = parseInt(formattedTime.slice(0, 2));
138
+ let roundedMinutes = (Math.round(minutes / 15) * 15).toString().padStart(2, "0");
139
+ if (roundedMinutes === "60") {
140
+ roundedMinutes = "00";
141
+ let roundedHour = hour + 1;
142
+ if (roundedHour === 24) {
143
+ roundedHour = 0;
144
+ }
145
+ formattedTime = formattedTime.replace(`${hour.toString().padStart(2, "0")}:`, `${roundedHour.toString().padStart(2, "0")}:`);
146
+ }
147
+ if (parseInt(roundedMinutes) < 8) {
148
+ roundedMinutes = "00";
149
+ }
150
+ time = formattedTime.replace(`:${minutes.toString().padStart(2, "0")}`, `:${roundedMinutes}`);
151
+ }
152
+ return time;
153
+ }
154
+ handleListSelection(time) {
155
+ time = this.findNearestTimeInterval(time);
156
+ const option = this.optionsList.filter((o) => o.textContent == this.formatToDisplay(time))[0];
157
+ // not DOM focus, just setting the proper ARIA attributes
158
+ option && this.focusOption(option);
159
+ }
160
+ setValue(time) {
161
+ const previousValue = this.value;
162
+ this.inputEl.value = this.formatToDisplay(time);
163
+ this.value = this.formatToStorage(time);
164
+ // update the selection in the list
165
+ // so it opens on the closest item
166
+ this.handleListSelection(time);
167
+ if (previousValue !== this.value) {
168
+ this.wmTimepickerNewValidValue.emit({ value: this.value });
169
+ }
170
+ // deprecated in favor of wmTimepickerNewValidValue and input
171
+ this.wmTimepickerOnChange.emit({
172
+ value: this.value,
173
+ isValid: !!this.displayedErrorMessage,
174
+ });
175
+ }
176
+ processInput() {
177
+ if (this.isValidTime(this.value)) {
178
+ this.setValue(this.value);
179
+ }
180
+ const newErrorMessage = this.determineErrorMessage();
181
+ this.displayedErrorMessage = newErrorMessage;
182
+ }
183
+ determineErrorMessage() {
184
+ let message = this.errorMessage;
185
+ const requiredError = intl.formatMessage({
186
+ id: "time.requiredError",
187
+ defaultMessage: "A time is required.",
188
+ });
189
+ const invalidError = intl.formatMessage({
190
+ id: "time.invalidTime",
191
+ defaultMessage: "Please enter a valid time.",
192
+ });
193
+ const isValid = this.isValidTime(this.value);
194
+ if (isValid && !this.errorMessage) {
195
+ message = null;
196
+ }
197
+ else if (!isValid && !this.errorMessage) {
198
+ if (this.requiredField && !this.value) {
199
+ message = requiredError;
200
+ }
201
+ else if (this.value) {
202
+ message = invalidError;
203
+ }
204
+ else {
205
+ message = null;
206
+ }
207
+ }
208
+ return message;
209
+ }
210
+ splitTime(time) {
211
+ const splitTime = this.twelveHrValid.exec(time) || this.twentyFourHrValid.exec(time);
212
+ let hours = parseInt(splitTime[1]);
213
+ let minutes = splitTime[2] || "00";
214
+ let amPm = splitTime ? splitTime[3] : undefined;
215
+ // handles edge case uncaught by regex '0pm'
216
+ if (hours === 0 && (amPm === null || amPm === void 0 ? void 0 : amPm.toUpperCase().includes("P"))) {
217
+ // if user types '0pm' we change it to midnight
218
+ amPm = "AM";
219
+ }
220
+ return [hours, minutes, amPm];
221
+ }
222
+ formatToDisplay(time) {
223
+ let [hours, minutes, amPm] = this.splitTime(time);
224
+ // at the moment the component only displays in 12hr AM/PM
225
+ if (hours === 12 && !amPm) {
226
+ amPm = "PM";
227
+ }
228
+ if (hours === 0 || hours === 24) {
229
+ hours = 12;
230
+ }
231
+ if (hours > 12) {
232
+ hours -= 12;
233
+ amPm = "PM";
234
+ }
235
+ if (amPm && amPm.toUpperCase().includes("P")) {
236
+ amPm = "PM";
237
+ }
238
+ else {
239
+ amPm = "AM";
240
+ }
241
+ return `${hours.toString().padStart(2, "0")}:${minutes} ${amPm}`;
242
+ }
243
+ formatToStorage(time) {
244
+ let [hours, minutes, amPm] = this.splitTime(time);
245
+ if (hours === 24) {
246
+ hours = 0;
247
+ }
248
+ if (hours === 12 && amPm && amPm.toUpperCase().includes("A")) {
249
+ hours -= 12;
250
+ }
251
+ if (amPm && amPm.toUpperCase().includes("P") && hours !== 12) {
252
+ hours += 12;
253
+ }
254
+ return `${hours.toString().padStart(2, "0")}:${minutes}`;
255
+ }
256
+ open(itemToSelect) {
257
+ this.el.focus();
258
+ this.openUp = shouldOpenUp(this.el, this.optionsEl);
259
+ this.isExpanded = true;
260
+ this.optionsEl.classList.remove("hidden");
261
+ if (this.errorMessage || !this.value) {
262
+ this.handleListSelection("09:00");
263
+ }
264
+ this.setDropdownPosition("center", this.selectedOption);
265
+ window.requestAnimationFrame(() => {
266
+ if (itemToSelect === "next") {
267
+ this.moveDown(this.selectedOption);
268
+ }
269
+ else if (itemToSelect === "previous") {
270
+ this.moveUp(this.selectedOption);
271
+ }
272
+ });
273
+ }
274
+ close(returnFocus = true) {
275
+ this.isExpanded = false;
276
+ window.setTimeout(() => {
277
+ this.optionsEl.classList.add("hidden");
278
+ // Returns focus to button after popup closes (no need if user is tabbing)
279
+ // Delay is necessary for screenreader to get new expanded state before focus
280
+ // window.requestAnimationFrame is probably enough, but since we are already using setTimeout it may as well be here
281
+ // also UX wise, it makes sense for the button to only be focused after the animation is complete
282
+ if (returnFocus) {
283
+ this.buttonEl.focus();
284
+ }
285
+ }, 150);
286
+ }
287
+ focusOption(item) {
288
+ // not an actual focus, just accessibility stuff
289
+ this.optionsList.forEach((option) => {
290
+ option.removeAttribute("aria-selected");
291
+ });
292
+ this.inputEl.setAttribute("aria-activedescendant", item.id);
293
+ item.setAttribute("aria-selected", "true");
294
+ this.selectedOption = item;
295
+ }
296
+ setDropdownPosition(position, item) {
297
+ switch (position) {
298
+ case "top":
299
+ const prevItem = item === null || item === void 0 ? void 0 : item.previousElementSibling;
300
+ this.optionsEl.scrollTop =
301
+ prevItem.getBoundingClientRect().top - this.optionsEl.getBoundingClientRect().top + this.optionsEl.scrollTop;
302
+ break;
303
+ case "bottom":
304
+ const nextItem = item === null || item === void 0 ? void 0 : item.nextElementSibling;
305
+ this.optionsEl.scrollTop =
306
+ nextItem.getBoundingClientRect().bottom -
307
+ this.optionsEl.getBoundingClientRect().top +
308
+ this.optionsEl.scrollTop -
309
+ this.optionsEl.offsetHeight;
310
+ break;
311
+ case "center":
312
+ this.optionsEl.scrollTop =
313
+ (this.optionsList.findIndex((x) => x.textContent === (item === null || item === void 0 ? void 0 : item.textContent)) - 2) * item.offsetHeight;
314
+ break;
315
+ case "first":
316
+ this.optionsEl.scrollTop = 0;
317
+ break;
318
+ case "last":
319
+ this.optionsEl.scrollTop = this.optionsList[0].clientHeight * this.optionsList.length;
320
+ break;
321
+ }
322
+ }
323
+ moveUp(el) {
324
+ const prevEl = el.previousElementSibling;
325
+ if (prevEl) {
326
+ // scroll option to top of dropdown if partially obscured / out of view
327
+ if (prevEl.getBoundingClientRect().top < this.optionsEl.getBoundingClientRect().top) {
328
+ this.setDropdownPosition("top", el);
329
+ }
330
+ this.focusOption(prevEl);
331
+ }
332
+ else {
333
+ this.focusOption(this.optionsList[this.optionsList.length - 1]);
334
+ this.setDropdownPosition("last");
335
+ }
336
+ }
337
+ moveDown(el) {
338
+ const nextEl = el.nextElementSibling;
339
+ if (nextEl) {
340
+ // scroll option to bottom of dropdown if partially obscured / out of view
341
+ if (nextEl.getBoundingClientRect().bottom > this.optionsEl.getBoundingClientRect().bottom) {
342
+ this.setDropdownPosition("bottom", el);
343
+ }
344
+ this.focusOption(nextEl);
345
+ }
346
+ else {
347
+ this.focusOption(this.optionsList[0]);
348
+ this.setDropdownPosition("first");
349
+ }
350
+ }
351
+ handleOptionClick(time) {
352
+ this.close();
353
+ this.setValue(time);
354
+ this.processInput();
355
+ }
356
+ handleInput() {
357
+ this.value = this.inputEl.value;
358
+ if (this.isValidTime(this.value)) {
359
+ this.handleListSelection(this.findNearestTimeInterval(this.value));
360
+ this.setDropdownPosition("center", this.selectedOption);
361
+ }
362
+ }
363
+ handleInputBlur(ev) {
364
+ // do not validate if clicking to an element that should prevent validation (e.g. close button on modal)
365
+ const shouldPreventValidation = this.preventValidation && isRelatedTarget(ev, this.preventValidation);
366
+ if (!shouldPreventValidation) {
367
+ this.processInput();
368
+ }
369
+ this.tpWrapper.classList.remove("focus");
370
+ }
371
+ renderOptions() {
372
+ return this.times.map((time, index) => (h("li", { id: `option${index + 1}`, role: "option", "aria-selected": time === "09:00" ? "true" : false, onClick: () => this.handleOptionClick(time) }, this.formatToDisplay(time))));
373
+ }
374
+ render() {
375
+ return (h(Host, { id: this.uid, invalid: !!this.displayedErrorMessage ? "true" : null, onBlur: () => this.close(false) },
376
+ h("div", { class: `wrapper label-${this.labelPosition} ${!!this.displayedErrorMessage ? "invalid" : ""}`, ref: (t) => (this.tpWrapper = t) },
377
+ h("div", { class: "label-wrapper" }, this.labelPosition !== "none" && (h("label", { id: `label-${this.uid}`, class: "label", htmlFor: `time-input-${this.uid}` },
378
+ this.label,
379
+ this.requiredField && (h("span", { class: "required", "aria-hidden": "true" }, "*"))))),
380
+ h("div", null,
381
+ h("div", { class: "inner-wrapper" },
382
+ h("input", { id: `time-input-${this.uid}`, "aria-label": this.label, "aria-describedby": `error-${this.uid}`, ref: (el) => (this.inputEl = el), onBlur: (ev) => this.handleInputBlur(ev), onInput: () => this.handleInput(), disabled: this.disabled, required: this.requiredField, placeholder: this.timeFormat, autocomplete: "off", onFocus: () => this.tpWrapper.classList.add("focus") }),
383
+ h("button", { id: `btn-${this.uid}`, class: this.isTabbing ? "user-is-tabbing" : "", ref: (el) => (this.buttonEl = el), disabled: this.disabled, "aria-controls": `list-${this.uid}`, "aria-expanded": this.isExpanded ? "true" : "false", "aria-label": this.buttonAriaLabel, "aria-describedby": `time-input-${this.uid}`, onClick: () => (this.isExpanded ? this.close() : this.open()) },
384
+ h("span", { class: "clock" })),
385
+ h("ul", { class: `options ${this.isExpanded ? "open" : ""} ${this.openUp ? "upwards" : ""}`, id: `list-${this.uid}`, role: "listbox", "aria-labelledby": `label-${this.uid}`, "aria-describedby": this.isExpanded ? "collapsed" : null, ref: (el) => (this.optionsEl = el) }, this.renderOptions())),
386
+ h("div", { id: `error-${this.uid}`, class: "error", "aria-live": "assertive", "aria-atomic": "true" }, this.displayedErrorMessage)))));
387
+ }
388
+ static get is() { return "wm-timepicker"; }
389
+ static get encapsulation() { return "shadow"; }
390
+ static get originalStyleUrls() { return {
391
+ "$": ["wm-timepicker.scss"]
392
+ }; }
393
+ static get styleUrls() { return {
394
+ "$": ["wm-timepicker.css"]
395
+ }; }
396
+ static get properties() { return {
397
+ "disabled": {
398
+ "type": "boolean",
399
+ "mutable": false,
400
+ "complexType": {
401
+ "original": "boolean",
402
+ "resolved": "boolean",
403
+ "references": {}
404
+ },
405
+ "required": false,
406
+ "optional": false,
407
+ "docs": {
408
+ "tags": [],
409
+ "text": ""
410
+ },
411
+ "attribute": "disabled",
412
+ "reflect": false,
413
+ "defaultValue": "false"
414
+ },
415
+ "value": {
416
+ "type": "string",
417
+ "mutable": true,
418
+ "complexType": {
419
+ "original": "string",
420
+ "resolved": "string",
421
+ "references": {}
422
+ },
423
+ "required": false,
424
+ "optional": false,
425
+ "docs": {
426
+ "tags": [],
427
+ "text": ""
428
+ },
429
+ "attribute": "value",
430
+ "reflect": true,
431
+ "defaultValue": "\"\""
432
+ },
433
+ "errorMessage": {
434
+ "type": "string",
435
+ "mutable": false,
436
+ "complexType": {
437
+ "original": "string | null",
438
+ "resolved": "null | string | undefined",
439
+ "references": {}
440
+ },
441
+ "required": false,
442
+ "optional": true,
443
+ "docs": {
444
+ "tags": [],
445
+ "text": ""
446
+ },
447
+ "attribute": "error-message",
448
+ "reflect": false
449
+ },
450
+ "label": {
451
+ "type": "string",
452
+ "mutable": false,
453
+ "complexType": {
454
+ "original": "string",
455
+ "resolved": "string",
456
+ "references": {}
457
+ },
458
+ "required": false,
459
+ "optional": false,
460
+ "docs": {
461
+ "tags": [],
462
+ "text": ""
463
+ },
464
+ "attribute": "label",
465
+ "reflect": false,
466
+ "defaultValue": "\"\""
467
+ },
468
+ "labelPosition": {
469
+ "type": "string",
470
+ "mutable": false,
471
+ "complexType": {
472
+ "original": "\"top\" | \"left\" | \"none\"",
473
+ "resolved": "\"left\" | \"none\" | \"top\"",
474
+ "references": {}
475
+ },
476
+ "required": false,
477
+ "optional": false,
478
+ "docs": {
479
+ "tags": [],
480
+ "text": ""
481
+ },
482
+ "attribute": "label-position",
483
+ "reflect": false,
484
+ "defaultValue": "\"top\""
485
+ },
486
+ "requiredField": {
487
+ "type": "boolean",
488
+ "mutable": false,
489
+ "complexType": {
490
+ "original": "boolean",
491
+ "resolved": "boolean",
492
+ "references": {}
493
+ },
494
+ "required": false,
495
+ "optional": false,
496
+ "docs": {
497
+ "tags": [],
498
+ "text": ""
499
+ },
500
+ "attribute": "required-field",
501
+ "reflect": false,
502
+ "defaultValue": "false"
503
+ },
504
+ "preventValidation": {
505
+ "type": "string",
506
+ "mutable": false,
507
+ "complexType": {
508
+ "original": "string",
509
+ "resolved": "string | undefined",
510
+ "references": {}
511
+ },
512
+ "required": false,
513
+ "optional": true,
514
+ "docs": {
515
+ "tags": [],
516
+ "text": ""
517
+ },
518
+ "attribute": "prevent-validation",
519
+ "reflect": false
520
+ }
521
+ }; }
522
+ static get states() { return {
523
+ "selectedOption": {},
524
+ "isExpanded": {},
525
+ "isTabbing": {},
526
+ "displayedErrorMessage": {}
527
+ }; }
528
+ static get events() { return [{
529
+ "method": "wmTimepickerNewValidValue",
530
+ "name": "wmTimepickerNewValidValue",
531
+ "bubbles": true,
532
+ "cancelable": true,
533
+ "composed": true,
534
+ "docs": {
535
+ "tags": [],
536
+ "text": ""
537
+ },
538
+ "complexType": {
539
+ "original": "{ value: string }",
540
+ "resolved": "{ value: string; }",
541
+ "references": {}
542
+ }
543
+ }, {
544
+ "method": "wmTimepickerOnChange",
545
+ "name": "wmTimepickerOnChange",
546
+ "bubbles": true,
547
+ "cancelable": true,
548
+ "composed": true,
549
+ "docs": {
550
+ "tags": [],
551
+ "text": ""
552
+ },
553
+ "complexType": {
554
+ "original": "{\r\n // deprecated in favor of wmTimepickerNewValidValue and standard input event\r\n value: string;\r\n isValid: boolean;\r\n }",
555
+ "resolved": "{ value: string; isValid: boolean; }",
556
+ "references": {}
557
+ }
558
+ }]; }
559
+ static get elementRef() { return "el"; }
560
+ static get watchers() { return [{
561
+ "propName": "errorMessage",
562
+ "methodName": "updateErrorState"
563
+ }]; }
564
+ static get listeners() { return [{
565
+ "name": "wmUserIsTabbing",
566
+ "method": "toggleTabbingOn",
567
+ "target": "window",
568
+ "capture": false,
569
+ "passive": false
570
+ }, {
571
+ "name": "wmUserIsNotTabbing",
572
+ "method": "toggleTabbingOff",
573
+ "target": "window",
574
+ "capture": false,
575
+ "passive": false
576
+ }, {
577
+ "name": "click",
578
+ "method": "blurHandler",
579
+ "target": "document",
580
+ "capture": false,
581
+ "passive": false
582
+ }, {
583
+ "name": "keydown",
584
+ "method": "handleKey",
585
+ "target": undefined,
586
+ "capture": false,
587
+ "passive": false
588
+ }]; }
589
+ }