@pod-os/elements 0.6.1-bea440b.0 → 0.7.1-2196a40.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 (411) hide show
  1. package/dist/cjs/{animation-5840e4df.js → animation-9bea118f.js} +115 -84
  2. package/dist/cjs/{app-globals-1aedd05c.js → app-globals-6352043e.js} +1 -1
  3. package/dist/cjs/{cubic-bezier-dcb7bfef.js → cubic-bezier-0b702a31.js} +13 -12
  4. package/dist/cjs/data-0c9489d7.js +1510 -0
  5. package/dist/cjs/dir-011f46ea.js +20 -0
  6. package/dist/cjs/elements.cjs.js +5 -5
  7. package/dist/cjs/focus-visible-2624ec15.js +76 -0
  8. package/dist/cjs/framework-delegate-437c0645.js +119 -0
  9. package/dist/cjs/{gesture-controller-fbbe9a65.js → gesture-controller-00a6b02f.js} +6 -2
  10. package/dist/cjs/{haptic-09e73337.js → haptic-7358cb0b.js} +37 -8
  11. package/dist/cjs/{hardware-back-button-01027575.js → hardware-back-button-25372ec7.js} +11 -8
  12. package/dist/cjs/{helpers-398ced09.js → helpers-cb08f5ae.js} +115 -15
  13. package/dist/cjs/{index-2067b305.js → index-1b07c737.js} +35 -24
  14. package/dist/cjs/{index-68ae43d2.js → index-2dc3637c.js} +34 -28
  15. package/dist/cjs/index-57b9fa9e.js +30 -0
  16. package/dist/cjs/{index-d01d9183.js → index-643851c6.js} +34 -19
  17. package/dist/cjs/index-731691ca.js +48 -0
  18. package/dist/cjs/{index-6bbae9b1.js → index-7d56774d.js} +12 -8
  19. package/dist/cjs/index-9fca5d6f.js +140 -0
  20. package/dist/cjs/index-b2a479e4.js +38 -0
  21. package/dist/cjs/{index-b4a9ece2.js → index-eaa0d16e.js} +7 -0
  22. package/dist/cjs/{tap-click-f24cb477.js → index-ed2ce04f.js} +37 -19
  23. package/dist/cjs/{input-shims-e959d9e2.js → input-shims-427999f7.js} +89 -38
  24. package/dist/cjs/ion-accordion-group.cjs.entry.js +205 -0
  25. package/dist/cjs/ion-accordion.cjs.entry.js +336 -0
  26. package/dist/cjs/ion-action-sheet_3.cjs.entry.js +865 -0
  27. package/dist/cjs/{ion-app_45.cjs.entry.js → ion-app_46.cjs.entry.js} +1775 -1471
  28. package/dist/cjs/ion-avatar.cjs.entry.js +2 -2
  29. package/dist/cjs/ion-back-button.cjs.entry.js +12 -11
  30. package/dist/cjs/ion-backdrop.cjs.entry.js +4 -4
  31. package/dist/cjs/ion-breadcrumb.cjs.entry.js +101 -0
  32. package/dist/cjs/ion-breadcrumbs.cjs.entry.js +133 -0
  33. package/dist/cjs/ion-buttons_3.cjs.entry.js +793 -0
  34. package/dist/cjs/ion-card-subtitle.cjs.entry.js +4 -4
  35. package/dist/cjs/{ion-list-header_3.cjs.entry.js → ion-checkbox_4.cjs.entry.js} +124 -23
  36. package/dist/cjs/ion-chip.cjs.entry.js +5 -10
  37. package/dist/cjs/ion-datetime-button.cjs.entry.js +346 -0
  38. package/dist/cjs/ion-datetime.cjs.entry.js +1548 -856
  39. package/dist/cjs/ion-fab-button.cjs.entry.js +26 -9
  40. package/dist/cjs/ion-fab-list.cjs.entry.js +4 -4
  41. package/dist/cjs/ion-fab.cjs.entry.js +15 -13
  42. package/dist/cjs/ion-img.cjs.entry.js +28 -4
  43. package/dist/cjs/ion-infinite-scroll-content.cjs.entry.js +5 -5
  44. package/dist/cjs/ion-infinite-scroll.cjs.entry.js +13 -13
  45. package/dist/cjs/ion-input.cjs.entry.js +57 -23
  46. package/dist/cjs/ion-item-option.cjs.entry.js +8 -8
  47. package/dist/cjs/ion-item-options.cjs.entry.js +5 -5
  48. package/dist/cjs/ion-item-sliding.cjs.entry.js +59 -63
  49. package/dist/cjs/ion-loading.cjs.entry.js +37 -36
  50. package/dist/cjs/ion-menu-button.cjs.entry.js +14 -12
  51. package/dist/cjs/ion-menu-toggle.cjs.entry.js +8 -7
  52. package/dist/cjs/ion-menu.cjs.entry.js +71 -57
  53. package/dist/cjs/ion-modal.cjs.entry.js +1384 -154
  54. package/dist/cjs/ion-nav-link.cjs.entry.js +5 -2
  55. package/dist/cjs/ion-nav.cjs.entry.js +177 -171
  56. package/dist/cjs/ion-picker-column.cjs.entry.js +25 -27
  57. package/dist/cjs/ion-picker.cjs.entry.js +22 -17
  58. package/dist/cjs/ion-popover.cjs.entry.js +1208 -182
  59. package/dist/cjs/ion-range.cjs.entry.js +137 -57
  60. package/dist/cjs/ion-refresher-content.cjs.entry.js +7 -12
  61. package/dist/cjs/ion-refresher.cjs.entry.js +150 -148
  62. package/dist/cjs/ion-reorder-group.cjs.entry.js +42 -35
  63. package/dist/cjs/ion-reorder.cjs.entry.js +5 -4
  64. package/dist/cjs/ion-route-redirect.cjs.entry.js +1 -1
  65. package/dist/cjs/ion-route.cjs.entry.js +1 -1
  66. package/dist/cjs/ion-router-link.cjs.entry.js +5 -5
  67. package/dist/cjs/ion-router-outlet.cjs.entry.js +24 -20
  68. package/dist/cjs/ion-router.cjs.entry.js +291 -194
  69. package/dist/cjs/ion-segment-button.cjs.entry.js +10 -17
  70. package/dist/cjs/ion-segment.cjs.entry.js +119 -26
  71. package/dist/cjs/ion-select-option.cjs.entry.js +3 -3
  72. package/dist/cjs/ion-select.cjs.entry.js +113 -78
  73. package/dist/cjs/ion-slide.cjs.entry.js +3 -3
  74. package/dist/cjs/ion-slides.cjs.entry.js +26 -27
  75. package/dist/cjs/ion-spinner.cjs.entry.js +10 -9
  76. package/dist/cjs/ion-split-pane.cjs.entry.js +16 -12
  77. package/dist/cjs/ion-tab-bar.cjs.entry.js +16 -23
  78. package/dist/cjs/ion-tab-button.cjs.entry.js +6 -6
  79. package/dist/cjs/ion-tab.cjs.entry.js +4 -4
  80. package/dist/cjs/ion-tabs.cjs.entry.js +4 -5
  81. package/dist/cjs/ion-text.cjs.entry.js +3 -3
  82. package/dist/cjs/ion-textarea.cjs.entry.js +31 -29
  83. package/dist/cjs/ion-thumbnail.cjs.entry.js +2 -2
  84. package/dist/cjs/ion-toast.cjs.entry.js +49 -59
  85. package/dist/cjs/ion-toggle.cjs.entry.js +41 -22
  86. package/dist/cjs/ion-virtual-scroll.cjs.entry.js +39 -31
  87. package/dist/cjs/{ionic-global-56e10eb5.js → ionic-global-f2d95fd3.js} +112 -93
  88. package/dist/cjs/{ios.transition-32e4623d.js → ios.transition-0f31ec9a.js} +78 -53
  89. package/dist/cjs/{keyboard-9e8103e4.js → keyboard-79afcba2.js} +6 -3
  90. package/dist/cjs/keyboard-controller-a934d106.js +42 -0
  91. package/dist/cjs/loader.cjs.js +5 -5
  92. package/dist/cjs/{md.transition-169c54f0.js → md.transition-d2a33a23.js} +15 -17
  93. package/dist/cjs/{menu-toggle-util-087678e0.js → menu-toggle-util-0a43ff7c.js} +5 -2
  94. package/dist/cjs/{overlays-49fe9ba7.js → overlays-65c716aa.js} +187 -71
  95. package/dist/cjs/spinner-configs-cd0abbeb.js +147 -0
  96. package/dist/cjs/{status-tap-ada894ff.js → status-tap-beaa3a71.js} +10 -5
  97. package/dist/cjs/{swipe-back-c4a778df.js → swipe-back-666ea8e6.js} +34 -15
  98. package/dist/cjs/test-component.cjs.entry.js +1 -1
  99. package/dist/cjs/{theme-2259d0f5.js → theme-fc63803b.js} +9 -5
  100. package/dist/collection/apps/pos-app-image-viewer/pos-app-image-viewer.js +1 -0
  101. package/dist/collection/collection-manifest.json +8 -1
  102. package/dist/collection/components/pos-image/pos-image.css +4 -0
  103. package/dist/custom-elements/index.js +12770 -4850
  104. package/dist/custom-elements/{tap-click.js → index2.js} +36 -18
  105. package/dist/custom-elements/input-shims.js +87 -38
  106. package/dist/custom-elements/ios.transition.js +74 -50
  107. package/dist/custom-elements/md.transition.js +11 -14
  108. package/dist/custom-elements/status-tap.js +7 -4
  109. package/dist/custom-elements/swipe-back.js +31 -13
  110. package/dist/elements/elements.css +1 -1
  111. package/dist/elements/elements.esm.js +1 -1
  112. package/dist/elements/p-0268cbd3.entry.js +7 -0
  113. package/dist/elements/p-0587332d.entry.js +1 -0
  114. package/dist/elements/{p-cfc0e54d.js → p-0991c811.js} +3 -0
  115. package/dist/elements/p-0a69a563.entry.js +1 -0
  116. package/dist/elements/p-0b95be17.entry.js +1 -0
  117. package/dist/elements/p-0d284fe0.entry.js +1 -0
  118. package/dist/elements/p-0fd77b33.entry.js +1 -0
  119. package/dist/elements/p-120dec2b.entry.js +1 -0
  120. package/dist/elements/p-12880671.entry.js +1 -0
  121. package/dist/elements/p-14ccd586.entry.js +1 -0
  122. package/dist/elements/{p-83d45051.entry.js → p-14df6ac0.entry.js} +1 -1
  123. package/dist/elements/{p-e860be6a.entry.js → p-17079f06.entry.js} +1 -1
  124. package/dist/elements/p-19e4a688.js +4 -0
  125. package/dist/elements/p-1afc4eb4.js +4 -0
  126. package/dist/elements/p-1beaf6bf.js +4 -0
  127. package/dist/elements/p-1d1c6a6f.entry.js +1 -0
  128. package/dist/elements/{p-31d30e42.entry.js → p-1d98f84b.entry.js} +1 -1
  129. package/dist/elements/p-278ca4c9.js +4 -0
  130. package/dist/elements/p-27f5629c.entry.js +1 -0
  131. package/dist/elements/p-29c0f03f.js +4 -0
  132. package/dist/elements/p-2da59aca.js +4 -0
  133. package/dist/elements/p-3152143f.js +4 -0
  134. package/dist/elements/p-343ff720.entry.js +7 -0
  135. package/dist/elements/p-36d4c9a8.js +4 -0
  136. package/dist/elements/p-3a30dfb2.entry.js +1 -0
  137. package/dist/elements/p-3c013bf1.entry.js +1 -0
  138. package/dist/elements/{p-74ba1e42.entry.js → p-3c318da5.entry.js} +1 -1
  139. package/dist/elements/p-3cee3222.entry.js +1 -0
  140. package/dist/elements/p-480b3c4f.entry.js +1 -0
  141. package/dist/elements/p-4e9d8f18.entry.js +1 -0
  142. package/dist/elements/p-53e23176.js +2 -0
  143. package/dist/elements/p-548524f3.js +4 -0
  144. package/dist/elements/p-5739fa41.entry.js +1 -0
  145. package/dist/elements/p-5808c505.js +1 -0
  146. package/dist/elements/p-58a8cc2a.js +4 -0
  147. package/dist/elements/p-6035415e.entry.js +1 -0
  148. package/dist/elements/p-60eeae90.js +4 -0
  149. package/dist/elements/p-610b03ff.entry.js +4 -0
  150. package/dist/elements/p-67777478.entry.js +1 -0
  151. package/dist/elements/p-6ab826e1.entry.js +1 -0
  152. package/dist/elements/p-6f5a2827.entry.js +1 -0
  153. package/dist/elements/p-779676c5.entry.js +1 -0
  154. package/dist/elements/p-7916ecc5.entry.js +1 -0
  155. package/dist/elements/p-79f06b80.entry.js +1 -0
  156. package/dist/elements/p-7b5991c1.entry.js +1 -0
  157. package/dist/elements/p-7d0def79.js +5 -0
  158. package/dist/elements/p-8112afea.js +4 -0
  159. package/dist/elements/p-83678d7d.entry.js +4 -0
  160. package/dist/elements/p-87e45c94.entry.js +1 -0
  161. package/dist/elements/p-8da6a31e.entry.js +1 -0
  162. package/dist/elements/p-8f80768f.entry.js +4 -0
  163. package/dist/elements/p-8fe0433b.js +4 -0
  164. package/dist/elements/{p-4cb27b48.entry.js → p-9147d82b.entry.js} +1 -1
  165. package/dist/elements/p-97abb434.entry.js +1 -0
  166. package/dist/elements/p-98497a4b.entry.js +1 -0
  167. package/dist/elements/p-9c719139.js +4 -0
  168. package/dist/elements/p-9ca37332.js +4 -0
  169. package/dist/elements/{p-37de7110.js → p-9d48def2.js} +3 -0
  170. package/dist/elements/p-a79a6ad9.entry.js +79 -0
  171. package/dist/elements/p-a805f2f9.entry.js +1 -0
  172. package/dist/elements/p-a86a5bfa.entry.js +1 -0
  173. package/dist/elements/{p-9c1dbe52.entry.js → p-ac34eab7.entry.js} +1 -1
  174. package/dist/elements/p-ad366eab.entry.js +4 -0
  175. package/dist/elements/p-aef3a931.js +7 -0
  176. package/dist/elements/p-b0537eb3.entry.js +1 -0
  177. package/dist/elements/p-b337f3b8.js +4 -0
  178. package/dist/elements/p-b34bf73f.entry.js +7 -0
  179. package/dist/elements/p-b41e66f0.entry.js +1 -0
  180. package/dist/elements/p-b47e7091.entry.js +1 -0
  181. package/dist/elements/p-b840320e.js +4 -0
  182. package/dist/elements/p-b934ac5d.entry.js +1 -0
  183. package/dist/elements/p-b98314e0.entry.js +4 -0
  184. package/dist/elements/p-bd12806f.entry.js +1 -0
  185. package/dist/elements/p-c16d38d5.js +4 -0
  186. package/dist/elements/p-c84205a3.js +4 -0
  187. package/dist/elements/{p-06675ac7.entry.js → p-cbe318f8.entry.js} +1 -1
  188. package/dist/elements/p-cfed7395.js +4 -0
  189. package/dist/elements/p-d22a1dc7.entry.js +7 -0
  190. package/dist/elements/p-d3e75c94.entry.js +1 -0
  191. package/dist/elements/p-d8f6d3ce.entry.js +14 -0
  192. package/dist/elements/p-d9880221.entry.js +4 -0
  193. package/dist/elements/p-da5db8fb.entry.js +1 -0
  194. package/dist/elements/{p-91fe653f.js → p-dcc6b03c.js} +3 -0
  195. package/dist/elements/{p-305e246c.entry.js → p-dd846020.entry.js} +1 -1
  196. package/dist/elements/p-df240b2a.entry.js +4 -0
  197. package/dist/elements/p-dffd8689.js +4 -0
  198. package/dist/elements/p-e495a095.js +4 -0
  199. package/dist/elements/p-e5fc7d42.entry.js +1 -0
  200. package/dist/elements/{p-aaa8393e.entry.js → p-eb137e9d.entry.js} +1 -1
  201. package/dist/elements/p-f4e54a17.js +7 -0
  202. package/dist/elements/p-f67d0717.entry.js +1 -0
  203. package/dist/elements/p-f7f4c640.js +1 -0
  204. package/dist/elements/p-f851b91a.js +4 -0
  205. package/dist/elements/p-fb27ee76.entry.js +1 -0
  206. package/dist/elements/p-fbddca35.entry.js +1 -0
  207. package/dist/elements/p-fc1df8e0.entry.js +7 -0
  208. package/dist/elements/p-fdac5f3a.js +4 -0
  209. package/dist/esm/{animation-fe6ed422.js → animation-801a007a.js} +115 -84
  210. package/dist/esm/{app-globals-27d92837.js → app-globals-05a3abfb.js} +1 -1
  211. package/dist/esm/{cubic-bezier-108b8579.js → cubic-bezier-538b6253.js} +13 -12
  212. package/dist/esm/data-62c81c24.js +1463 -0
  213. package/dist/esm/dir-defb16c6.js +18 -0
  214. package/dist/esm/elements.js +5 -5
  215. package/dist/esm/focus-visible-78d55799.js +74 -0
  216. package/dist/esm/framework-delegate-7e2b767b.js +115 -0
  217. package/dist/esm/{gesture-controller-8f35af24.js → gesture-controller-c466ff14.js} +6 -2
  218. package/dist/esm/{haptic-c424e670.js → haptic-e7d5ef4d.js} +38 -9
  219. package/dist/esm/{hardware-back-button-bb4c578a.js → hardware-back-button-242191a7.js} +11 -8
  220. package/dist/esm/{helpers-44e3bd9f.js → helpers-aeff219b.js} +113 -16
  221. package/dist/esm/index-0dbaca1a.js +28 -0
  222. package/dist/esm/index-1f3d8582.js +34 -0
  223. package/dist/esm/{index-97199683.js → index-2be9a18b.js} +34 -28
  224. package/dist/esm/{index-8d682224.js → index-51e4a829.js} +12 -8
  225. package/dist/esm/{index-8a463a85.js → index-6048aed6.js} +35 -24
  226. package/dist/esm/index-65ecd543.js +25 -0
  227. package/dist/esm/{tap-click-a7e55ef5.js → index-b212db1c.js} +37 -19
  228. package/dist/esm/{index-e4deec27.js → index-cb938ffb.js} +7 -1
  229. package/dist/esm/{index-3a1bd803.js → index-d39eb62b.js} +35 -20
  230. package/dist/esm/index-ebf7f059.js +128 -0
  231. package/dist/esm/{input-shims-3b48722f.js → input-shims-8a389148.js} +89 -38
  232. package/dist/esm/ion-accordion-group.entry.js +201 -0
  233. package/dist/esm/ion-accordion.entry.js +332 -0
  234. package/dist/esm/ion-action-sheet_3.entry.js +859 -0
  235. package/dist/esm/{ion-app_45.entry.js → ion-app_46.entry.js} +1775 -1472
  236. package/dist/esm/ion-avatar.entry.js +2 -2
  237. package/dist/esm/ion-back-button.entry.js +12 -11
  238. package/dist/esm/ion-backdrop.entry.js +4 -4
  239. package/dist/esm/ion-breadcrumb.entry.js +97 -0
  240. package/dist/esm/ion-breadcrumbs.entry.js +129 -0
  241. package/dist/esm/ion-buttons_3.entry.js +787 -0
  242. package/dist/esm/ion-card-subtitle.entry.js +4 -4
  243. package/dist/esm/{ion-list-header_3.entry.js → ion-checkbox_4.entry.js} +124 -24
  244. package/dist/esm/ion-chip.entry.js +5 -10
  245. package/dist/esm/ion-datetime-button.entry.js +342 -0
  246. package/dist/esm/ion-datetime.entry.js +1548 -856
  247. package/dist/esm/ion-fab-button.entry.js +26 -9
  248. package/dist/esm/ion-fab-list.entry.js +4 -4
  249. package/dist/esm/ion-fab.entry.js +15 -13
  250. package/dist/esm/ion-img.entry.js +28 -4
  251. package/dist/esm/ion-infinite-scroll-content.entry.js +5 -5
  252. package/dist/esm/ion-infinite-scroll.entry.js +13 -13
  253. package/dist/esm/ion-input.entry.js +57 -23
  254. package/dist/esm/ion-item-option.entry.js +8 -8
  255. package/dist/esm/ion-item-options.entry.js +5 -5
  256. package/dist/esm/ion-item-sliding.entry.js +59 -63
  257. package/dist/esm/ion-loading.entry.js +37 -36
  258. package/dist/esm/ion-menu-button.entry.js +14 -12
  259. package/dist/esm/ion-menu-toggle.entry.js +8 -7
  260. package/dist/esm/ion-menu.entry.js +71 -57
  261. package/dist/esm/ion-modal.entry.js +1375 -145
  262. package/dist/esm/ion-nav-link.entry.js +5 -2
  263. package/dist/esm/ion-nav.entry.js +177 -171
  264. package/dist/esm/ion-picker-column.entry.js +25 -27
  265. package/dist/esm/ion-picker.entry.js +22 -17
  266. package/dist/esm/ion-popover.entry.js +1208 -182
  267. package/dist/esm/ion-range.entry.js +137 -57
  268. package/dist/esm/ion-refresher-content.entry.js +7 -12
  269. package/dist/esm/ion-refresher.entry.js +149 -147
  270. package/dist/esm/ion-reorder-group.entry.js +42 -35
  271. package/dist/esm/ion-reorder.entry.js +5 -4
  272. package/dist/esm/ion-route-redirect.entry.js +1 -1
  273. package/dist/esm/ion-route.entry.js +1 -1
  274. package/dist/esm/ion-router-link.entry.js +5 -5
  275. package/dist/esm/ion-router-outlet.entry.js +24 -20
  276. package/dist/esm/ion-router.entry.js +291 -194
  277. package/dist/esm/ion-segment-button.entry.js +10 -17
  278. package/dist/esm/ion-segment.entry.js +119 -26
  279. package/dist/esm/ion-select-option.entry.js +3 -3
  280. package/dist/esm/ion-select.entry.js +113 -78
  281. package/dist/esm/ion-slide.entry.js +3 -3
  282. package/dist/esm/ion-slides.entry.js +26 -27
  283. package/dist/esm/ion-spinner.entry.js +10 -9
  284. package/dist/esm/ion-split-pane.entry.js +16 -12
  285. package/dist/esm/ion-tab-bar.entry.js +16 -23
  286. package/dist/esm/ion-tab-button.entry.js +6 -6
  287. package/dist/esm/ion-tab.entry.js +4 -4
  288. package/dist/esm/ion-tabs.entry.js +4 -5
  289. package/dist/esm/ion-text.entry.js +3 -3
  290. package/dist/esm/ion-textarea.entry.js +31 -29
  291. package/dist/esm/ion-thumbnail.entry.js +2 -2
  292. package/dist/esm/ion-toast.entry.js +49 -59
  293. package/dist/esm/ion-toggle.entry.js +41 -22
  294. package/dist/esm/ion-virtual-scroll.entry.js +39 -31
  295. package/dist/esm/{ionic-global-2e28f7c7.js → ionic-global-6cd57191.js} +112 -93
  296. package/dist/esm/{ios.transition-a783e3cd.js → ios.transition-bbd952f2.js} +78 -53
  297. package/dist/{custom-elements/keyboard.js → esm/keyboard-413afe04.js} +6 -3
  298. package/dist/esm/keyboard-controller-33693bc2.js +40 -0
  299. package/dist/esm/loader.js +5 -5
  300. package/dist/esm/{md.transition-5a4a8c82.js → md.transition-5170a6d3.js} +15 -17
  301. package/dist/esm/{menu-toggle-util-562dfc9c.js → menu-toggle-util-82bf888a.js} +5 -2
  302. package/dist/esm/{overlays-fc9a0625.js → overlays-0c7f05e1.js} +186 -71
  303. package/dist/esm/spinner-configs-cbcd1f62.js +145 -0
  304. package/dist/esm/{status-tap-69e62ad6.js → status-tap-ad757b8a.js} +10 -5
  305. package/dist/esm/swipe-back-7ef22876.js +69 -0
  306. package/dist/esm/test-component.entry.js +1 -1
  307. package/dist/esm/{theme-d21826a7.js → theme-7cf2cab0.js} +9 -5
  308. package/package.json +4 -5
  309. package/LICENSE +0 -21
  310. package/dist/cjs/button-active-c14dab31.js +0 -66
  311. package/dist/cjs/focus-visible-16c98640.js +0 -45
  312. package/dist/cjs/framework-delegate-c45292a3.js +0 -37
  313. package/dist/cjs/ion-action-sheet.cjs.entry.js +0 -265
  314. package/dist/cjs/ion-alert.cjs.entry.js +0 -456
  315. package/dist/cjs/ion-buttons.cjs.entry.js +0 -42
  316. package/dist/cjs/ion-checkbox.cjs.entry.js +0 -117
  317. package/dist/cjs/ion-note.cjs.entry.js +0 -29
  318. package/dist/cjs/ion-select-popover.cjs.entry.js +0 -35
  319. package/dist/cjs/spinner-configs-fb16b986.js +0 -112
  320. package/dist/custom-elements/focus-visible.js +0 -43
  321. package/dist/elements/p-03bda390.js +0 -1
  322. package/dist/elements/p-0be044f1.entry.js +0 -1
  323. package/dist/elements/p-119c7c6c.entry.js +0 -1
  324. package/dist/elements/p-14c7c3ea.entry.js +0 -1
  325. package/dist/elements/p-1d4a2c61.js +0 -1
  326. package/dist/elements/p-1d894ac4.entry.js +0 -1
  327. package/dist/elements/p-1dafa1ce.entry.js +0 -1
  328. package/dist/elements/p-23b89ccb.entry.js +0 -1
  329. package/dist/elements/p-2c03b9ab.entry.js +0 -1
  330. package/dist/elements/p-346985f2.js +0 -1
  331. package/dist/elements/p-360f1c62.entry.js +0 -1
  332. package/dist/elements/p-373e1f25.entry.js +0 -1
  333. package/dist/elements/p-40547acb.entry.js +0 -1
  334. package/dist/elements/p-40b68014.entry.js +0 -1
  335. package/dist/elements/p-42e4f702.entry.js +0 -1
  336. package/dist/elements/p-489807e5.js +0 -1
  337. package/dist/elements/p-4ad72d54.entry.js +0 -1
  338. package/dist/elements/p-4cca7b5e.entry.js +0 -1
  339. package/dist/elements/p-4f24d306.js +0 -1
  340. package/dist/elements/p-519d6a53.entry.js +0 -1
  341. package/dist/elements/p-536e8e52.entry.js +0 -1
  342. package/dist/elements/p-599bb53f.entry.js +0 -1
  343. package/dist/elements/p-5eb7a546.js +0 -1
  344. package/dist/elements/p-60df2bed.entry.js +0 -1
  345. package/dist/elements/p-65133e33.js +0 -1
  346. package/dist/elements/p-6693fce8.js +0 -1
  347. package/dist/elements/p-689bdcc1.entry.js +0 -1
  348. package/dist/elements/p-70713b3d.entry.js +0 -1
  349. package/dist/elements/p-707d5d76.js +0 -1
  350. package/dist/elements/p-7212b7f2.js +0 -1
  351. package/dist/elements/p-73992898.entry.js +0 -1
  352. package/dist/elements/p-792c1e0f.entry.js +0 -1
  353. package/dist/elements/p-7e5300af.js +0 -2
  354. package/dist/elements/p-8068987c.entry.js +0 -1
  355. package/dist/elements/p-83accf46.entry.js +0 -1
  356. package/dist/elements/p-86635d06.entry.js +0 -1
  357. package/dist/elements/p-874c2b44.js +0 -1
  358. package/dist/elements/p-89c12ce8.entry.js +0 -1
  359. package/dist/elements/p-8bcba3f7.entry.js +0 -1
  360. package/dist/elements/p-8c759f51.entry.js +0 -1
  361. package/dist/elements/p-8f945e6b.entry.js +0 -1
  362. package/dist/elements/p-9300ab6a.js +0 -1
  363. package/dist/elements/p-93cacd51.entry.js +0 -1
  364. package/dist/elements/p-9408d0b4.entry.js +0 -1
  365. package/dist/elements/p-98c79eda.js +0 -1
  366. package/dist/elements/p-99f8abed.js +0 -1
  367. package/dist/elements/p-9ca7e079.js +0 -1
  368. package/dist/elements/p-a4648b74.entry.js +0 -1
  369. package/dist/elements/p-aab0f63c.js +0 -1
  370. package/dist/elements/p-ad4e2295.entry.js +0 -1
  371. package/dist/elements/p-afb8f7d5.entry.js +0 -1
  372. package/dist/elements/p-b055ec44.js +0 -1
  373. package/dist/elements/p-b078d63b.entry.js +0 -1
  374. package/dist/elements/p-b3460325.entry.js +0 -1
  375. package/dist/elements/p-b3f4042e.entry.js +0 -67
  376. package/dist/elements/p-b5406b58.entry.js +0 -1
  377. package/dist/elements/p-b5ef0c91.entry.js +0 -1
  378. package/dist/elements/p-b6ba623e.entry.js +0 -1
  379. package/dist/elements/p-b9926d8b.entry.js +0 -1
  380. package/dist/elements/p-bfd4cfcd.entry.js +0 -1
  381. package/dist/elements/p-c08dd7d0.entry.js +0 -1
  382. package/dist/elements/p-c0db9c51.entry.js +0 -1
  383. package/dist/elements/p-c1e7fbfb.js +0 -1
  384. package/dist/elements/p-ca69d6c9.js +0 -1
  385. package/dist/elements/p-cc4cb1ac.entry.js +0 -1
  386. package/dist/elements/p-cf8a7031.entry.js +0 -1
  387. package/dist/elements/p-cff82b6f.js +0 -1
  388. package/dist/elements/p-d6d1e65f.entry.js +0 -1
  389. package/dist/elements/p-d9462b66.entry.js +0 -1
  390. package/dist/elements/p-e2e0fee9.entry.js +0 -1
  391. package/dist/elements/p-e642b266.entry.js +0 -1
  392. package/dist/elements/p-e953934f.entry.js +0 -1
  393. package/dist/elements/p-f0474f46.js +0 -1
  394. package/dist/elements/p-f10a94f6.entry.js +0 -1
  395. package/dist/elements/p-f2426182.entry.js +0 -1
  396. package/dist/elements/p-f327fd21.js +0 -1
  397. package/dist/elements/p-f84987ee.js +0 -1
  398. package/dist/elements/p-f8a3367d.entry.js +0 -1
  399. package/dist/esm/button-active-fd9d6d91.js +0 -64
  400. package/dist/esm/focus-visible-edb28f19.js +0 -43
  401. package/dist/esm/framework-delegate-9cd8048f.js +0 -34
  402. package/dist/esm/ion-action-sheet.entry.js +0 -261
  403. package/dist/esm/ion-alert.entry.js +0 -452
  404. package/dist/esm/ion-buttons.entry.js +0 -38
  405. package/dist/esm/ion-checkbox.entry.js +0 -113
  406. package/dist/esm/ion-note.entry.js +0 -25
  407. package/dist/esm/ion-select-popover.entry.js +0 -31
  408. package/dist/esm/keyboard-e6abcb80.js +0 -125
  409. package/dist/esm/spinner-configs-aaf2a1a9.js +0 -110
  410. package/dist/esm/swipe-back-d84cfc8a.js +0 -50
  411. package/readme.md +0 -55
@@ -1,568 +1,156 @@
1
- import { r as registerInstance, c as createEvent, h, H as Host, a as getElement } from './index-e4deec27.js';
2
- import { g as getIonMode } from './ionic-global-2e28f7c7.js';
3
- import { b as addEventListener, c as clamp, k as findItemLabel, j as renderHiddenInput } from './helpers-44e3bd9f.js';
4
- import { b as pickerController } from './overlays-fc9a0625.js';
5
- import { h as hostContext } from './theme-d21826a7.js';
6
- import './hardware-back-button-bb4c578a.js';
1
+ import { r as registerInstance, d as createEvent, w as writeTask, h, H as Host, c as getElement } from './index-cb938ffb.js';
2
+ import { g as chevronBack, c as chevronForward, f as chevronDown, j as caretUpSharp, k as caretDownSharp } from './index-65ecd543.js';
3
+ import { g as getIonMode } from './ionic-global-6cd57191.js';
4
+ import { startFocusVisible } from './focus-visible-78d55799.js';
5
+ import { r as raf, j as renderHiddenInput, g as getElementRoot } from './helpers-aeff219b.js';
6
+ import { p as printIonWarning, a as printIonError } from './index-1f3d8582.js';
7
+ import { i as isRTL } from './dir-defb16c6.js';
8
+ import { c as createColorClasses } from './theme-7cf2cab0.js';
9
+ import { g as generateDayAriaLabel, a as getDay, i as isBefore, b as isAfter, c as isSameDay, d as getPreviousMonth, e as getNextMonth, v as validateParts, f as getPartsFromCalendarDay, h as getEndOfWeek, j as getStartOfWeek, k as getPreviousDay, l as getNextDay, m as getPreviousWeek, n as getNextWeek, p as parseMinParts, o as parseMaxParts, q as parseDate, w as warnIfValueOutOfBounds, r as convertToArrayOfNumbers, s as convertDataToISO, t as getToday, u as getClosestValidDate, x as getNumDaysInMonth, y as getCombinedDateColumnData, z as getMonthColumnData, A as getDayColumnData, B as getYearColumnData, C as isMonthFirstLocale, D as getTimeColumnsData, E as isLocaleDayPeriodRTL, F as getDaysOfWeek, G as getMonthAndYear, H as getDaysOfMonth, I as generateMonths, J as is24Hour, K as getLocalizedTime, L as getMonthAndDay, M as formatValue, N as getNextYear, O as getPreviousYear, P as clampDate, Q as parseAmPm, R as calculateHourFromAMPM } from './data-62c81c24.js';
7
10
 
8
- /**
9
- * Gets a date value given a format
10
- * Defaults to the current date if
11
- * no date given
11
+ /*!
12
+ * (C) Ionic http://ionicframework.com - MIT License
12
13
  */
13
- const getDateValue = (date, format) => {
14
- const getValue = getValueFromFormat(date, format);
15
- if (getValue !== undefined) {
16
- if (format === FORMAT_A || format === FORMAT_a) {
17
- date.ampm = getValue;
18
- }
19
- return getValue;
20
- }
21
- const defaultDate = parseDate(new Date().toISOString());
22
- return getValueFromFormat(defaultDate, format);
23
- };
24
- const renderDatetime = (template, value, locale) => {
25
- if (value === undefined) {
26
- return undefined;
27
- }
28
- const tokens = [];
29
- let hasText = false;
30
- FORMAT_KEYS.forEach((format, index) => {
31
- if (template.indexOf(format.f) > -1) {
32
- const token = '{' + index + '}';
33
- const text = renderTextFormat(format.f, value[format.k], value, locale);
34
- if (!hasText && text !== undefined && value[format.k] != null) {
35
- hasText = true;
36
- }
37
- tokens.push(token, text || '');
38
- template = template.replace(format.f, token);
39
- }
40
- });
41
- if (!hasText) {
42
- return undefined;
43
- }
44
- for (let i = 0; i < tokens.length; i += 2) {
45
- template = template.replace(tokens[i], tokens[i + 1]);
46
- }
47
- return template;
48
- };
49
- const renderTextFormat = (format, value, date, locale) => {
50
- if ((format === FORMAT_DDDD || format === FORMAT_DDD)) {
51
- try {
52
- value = (new Date(date.year, date.month - 1, date.day)).getDay();
53
- if (format === FORMAT_DDDD) {
54
- return (locale.dayNames ? locale.dayNames : DAY_NAMES)[value];
55
- }
56
- return (locale.dayShortNames ? locale.dayShortNames : DAY_SHORT_NAMES)[value];
57
- }
58
- catch (e) {
59
- // ignore
60
- }
61
- return undefined;
62
- }
63
- if (format === FORMAT_A) {
64
- return date !== undefined && date.hour !== undefined
65
- ? (date.hour < 12 ? 'AM' : 'PM')
66
- : value ? value.toUpperCase() : '';
67
- }
68
- if (format === FORMAT_a) {
69
- return date !== undefined && date.hour !== undefined
70
- ? (date.hour < 12 ? 'am' : 'pm')
71
- : value || '';
72
- }
73
- if (value == null) {
74
- return '';
75
- }
76
- if (format === FORMAT_YY || format === FORMAT_MM ||
77
- format === FORMAT_DD || format === FORMAT_HH ||
78
- format === FORMAT_mm || format === FORMAT_ss) {
79
- return twoDigit(value);
80
- }
81
- if (format === FORMAT_YYYY) {
82
- return fourDigit(value);
83
- }
84
- if (format === FORMAT_MMMM) {
85
- return (locale.monthNames ? locale.monthNames : MONTH_NAMES)[value - 1];
86
- }
87
- if (format === FORMAT_MMM) {
88
- return (locale.monthShortNames ? locale.monthShortNames : MONTH_SHORT_NAMES)[value - 1];
89
- }
90
- if (format === FORMAT_hh || format === FORMAT_h) {
91
- if (value === 0) {
92
- return '12';
93
- }
94
- if (value > 12) {
95
- value -= 12;
96
- }
97
- if (format === FORMAT_hh && value < 10) {
98
- return ('0' + value);
99
- }
100
- }
101
- return value.toString();
102
- };
103
- const dateValueRange = (format, min, max) => {
104
- const opts = [];
105
- if (format === FORMAT_YYYY || format === FORMAT_YY) {
106
- // year
107
- if (max.year === undefined || min.year === undefined) {
108
- throw new Error('min and max year is undefined');
109
- }
110
- for (let i = max.year; i >= min.year; i--) {
111
- opts.push(i);
112
- }
113
- }
114
- else if (format === FORMAT_MMMM || format === FORMAT_MMM ||
115
- format === FORMAT_MM || format === FORMAT_M ||
116
- format === FORMAT_hh || format === FORMAT_h) {
117
- // month or 12-hour
118
- for (let i = 1; i < 13; i++) {
119
- opts.push(i);
120
- }
14
+ const isYearDisabled = (refYear, minParts, maxParts) => {
15
+ if (minParts && minParts.year > refYear) {
16
+ return true;
121
17
  }
122
- else if (format === FORMAT_DDDD || format === FORMAT_DDD ||
123
- format === FORMAT_DD || format === FORMAT_D) {
124
- // day
125
- for (let i = 1; i < 32; i++) {
126
- opts.push(i);
127
- }
18
+ if (maxParts && maxParts.year < refYear) {
19
+ return true;
128
20
  }
129
- else if (format === FORMAT_HH || format === FORMAT_H) {
130
- // 24-hour
131
- for (let i = 0; i < 24; i++) {
132
- opts.push(i);
133
- }
134
- }
135
- else if (format === FORMAT_mm || format === FORMAT_m) {
136
- // minutes
137
- for (let i = 0; i < 60; i++) {
138
- opts.push(i);
139
- }
140
- }
141
- else if (format === FORMAT_ss || format === FORMAT_s) {
142
- // seconds
143
- for (let i = 0; i < 60; i++) {
144
- opts.push(i);
145
- }
146
- }
147
- else if (format === FORMAT_A || format === FORMAT_a) {
148
- // AM/PM
149
- opts.push('am', 'pm');
150
- }
151
- return opts;
152
- };
153
- const dateSortValue = (year, month, day, hour = 0, minute = 0) => {
154
- return parseInt(`1${fourDigit(year)}${twoDigit(month)}${twoDigit(day)}${twoDigit(hour)}${twoDigit(minute)}`, 10);
155
- };
156
- const dateDataSortValue = (data) => {
157
- return dateSortValue(data.year, data.month, data.day, data.hour, data.minute);
158
- };
159
- const daysInMonth = (month, year) => {
160
- return (month === 4 || month === 6 || month === 9 || month === 11) ? 30 : (month === 2) ? isLeapYear(year) ? 29 : 28 : 31;
161
- };
162
- const isLeapYear = (year) => {
163
- return (year % 4 === 0 && year % 100 !== 0) || (year % 400 === 0);
164
- };
165
- const ISO_8601_REGEXP = /^(\d{4}|[+\-]\d{6})(?:-(\d{2})(?:-(\d{2}))?)?(?:T(\d{2}):(\d{2})(?::(\d{2})(?:\.(\d{3}))?)?(?:(Z)|([+\-])(\d{2})(?::(\d{2}))?)?)?$/;
166
- const TIME_REGEXP = /^((\d{2}):(\d{2})(?::(\d{2})(?:\.(\d{3}))?)?(?:(Z)|([+\-])(\d{2})(?::(\d{2}))?)?)?$/;
167
- const parseDate = (val) => {
168
- // manually parse IS0 cuz Date.parse cannot be trusted
169
- // ISO 8601 format: 1994-12-15T13:47:20Z
170
- let parse = null;
171
- if (val != null && val !== '') {
172
- // try parsing for just time first, HH:MM
173
- parse = TIME_REGEXP.exec(val);
174
- if (parse) {
175
- // adjust the array so it fits nicely with the datetime parse
176
- parse.unshift(undefined, undefined);
177
- parse[2] = parse[3] = undefined;
178
- }
179
- else {
180
- // try parsing for full ISO datetime
181
- parse = ISO_8601_REGEXP.exec(val);
182
- }
183
- }
184
- if (parse === null) {
185
- // wasn't able to parse the ISO datetime
186
- return undefined;
187
- }
188
- // ensure all the parse values exist with at least 0
189
- for (let i = 1; i < 8; i++) {
190
- parse[i] = parse[i] !== undefined ? parseInt(parse[i], 10) : undefined;
191
- }
192
- let tzOffset = 0;
193
- if (parse[9] && parse[10]) {
194
- // hours
195
- tzOffset = parseInt(parse[10], 10) * 60;
196
- if (parse[11]) {
197
- // minutes
198
- tzOffset += parseInt(parse[11], 10);
199
- }
200
- if (parse[9] === '-') {
201
- // + or -
202
- tzOffset *= -1;
203
- }
204
- }
205
- return {
206
- year: parse[1],
207
- month: parse[2],
208
- day: parse[3],
209
- hour: parse[4],
210
- minute: parse[5],
211
- second: parse[6],
212
- millisecond: parse[7],
213
- tzOffset,
214
- };
21
+ return false;
215
22
  };
216
23
  /**
217
- * Converts a valid UTC datetime string to JS Date time object.
218
- * By default uses the users local timezone, but an optional
219
- * timezone can be provided.
220
- * Note: This is not meant for time strings
221
- * such as "01:47"
24
+ * Returns true if a given day should
25
+ * not be interactive according to its value,
26
+ * or the max/min dates.
222
27
  */
223
- const getDateTime = (dateString = '', timeZone = '') => {
28
+ const isDayDisabled = (refParts, minParts, maxParts, dayValues) => {
224
29
  /**
225
- * If user passed in undefined
226
- * or null, convert it to the
227
- * empty string since the rest
228
- * of this functions expects
229
- * a string
30
+ * If this is a filler date (i.e. padding)
31
+ * then the date is disabled.
230
32
  */
231
- if (dateString === undefined || dateString === null) {
232
- dateString = '';
33
+ if (refParts.day === null) {
34
+ return true;
233
35
  }
234
36
  /**
235
- * Ensures that YYYY-MM-DD, YYYY-MM,
236
- * YYYY-DD, YYYY, etc does not get affected
237
- * by timezones and stays on the day/month
238
- * that the user provided
37
+ * If user passed in a list of acceptable day values
38
+ * check to make sure that the date we are looking
39
+ * at is in this array.
239
40
  */
240
- if (dateString.length === 10 ||
241
- dateString.length === 7 ||
242
- dateString.length === 4) {
243
- dateString += ' ';
244
- }
245
- const date = (typeof dateString === 'string' && dateString.length > 0) ? new Date(dateString) : new Date();
246
- const localDateTime = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours(), date.getMinutes(), date.getSeconds(), date.getMilliseconds()));
247
- if (timeZone && timeZone.length > 0) {
248
- return new Date(date.getTime() - getTimezoneOffset(localDateTime, timeZone));
41
+ if (dayValues !== undefined && !dayValues.includes(refParts.day)) {
42
+ return true;
249
43
  }
250
- return localDateTime;
251
- };
252
- const getTimezoneOffset = (localDate, timeZone) => {
253
- const utcDateTime = new Date(localDate.toLocaleString('en-US', { timeZone: 'utc' }));
254
- const tzDateTime = new Date(localDate.toLocaleString('en-US', { timeZone }));
255
- return utcDateTime.getTime() - tzDateTime.getTime();
256
- };
257
- const updateDate = (existingData, newData, displayTimezone) => {
258
- if (!newData || typeof newData === 'string') {
259
- const dateTime = getDateTime(newData, displayTimezone);
260
- if (!Number.isNaN(dateTime.getTime())) {
261
- newData = dateTime.toISOString();
262
- }
263
- }
264
- if (newData && newData !== '') {
265
- if (typeof newData === 'string') {
266
- // new date is a string, and hopefully in the ISO format
267
- // convert it to our DatetimeData if a valid ISO
268
- newData = parseDate(newData);
269
- if (newData) {
270
- // successfully parsed the ISO string to our DatetimeData
271
- Object.assign(existingData, newData);
272
- return true;
273
- }
274
- }
275
- else if ((newData.year || newData.hour || newData.month || newData.day || newData.minute || newData.second)) {
276
- // newData is from the datetime picker's selected values
277
- // update the existing datetimeValue with the new values
278
- if (newData.ampm !== undefined && newData.hour !== undefined) {
279
- // change the value of the hour based on whether or not it is am or pm
280
- // if the meridiem is pm and equal to 12, it remains 12
281
- // otherwise we add 12 to the hour value
282
- // if the meridiem is am and equal to 12, we change it to 0
283
- // otherwise we use its current hour value
284
- // for example: 8 pm becomes 20, 12 am becomes 0, 4 am becomes 4
285
- newData.hour.value = (newData.ampm.value === 'pm')
286
- ? (newData.hour.value === 12 ? 12 : newData.hour.value + 12)
287
- : (newData.hour.value === 12 ? 0 : newData.hour.value);
288
- }
289
- // merge new values from the picker's selection
290
- // to the existing DatetimeData values
291
- for (const key of Object.keys(newData)) {
292
- existingData[key] = newData[key].value;
293
- }
294
- return true;
295
- }
296
- else if (newData.ampm) {
297
- // Even though in the picker column hour values are between 1 and 12, the hour value is actually normalized
298
- // to [0, 23] interval. Because of this when changing between AM and PM we have to update the hour so it points
299
- // to the correct HH hour
300
- newData.hour = {
301
- value: newData.hour
302
- ? newData.hour.value
303
- : (newData.ampm.value === 'pm'
304
- ? (existingData.hour < 12 ? existingData.hour + 12 : existingData.hour)
305
- : (existingData.hour >= 12 ? existingData.hour - 12 : existingData.hour))
306
- };
307
- existingData['hour'] = newData['hour'].value;
308
- existingData['ampm'] = newData['ampm'].value;
309
- return true;
310
- }
311
- // eww, invalid data
312
- console.warn(`Error parsing date: "${newData}". Please provide a valid ISO 8601 datetime format: https://www.w3.org/TR/NOTE-datetime`);
44
+ /**
45
+ * Given a min date, perform the following
46
+ * checks. If any of them are true, then the
47
+ * day should be disabled:
48
+ * 1. Is the current year < the min allowed year?
49
+ * 2. Is the current year === min allowed year,
50
+ * but the current month < the min allowed month?
51
+ * 3. Is the current year === min allowed year, the
52
+ * current month === min allow month, but the current
53
+ * day < the min allowed day?
54
+ */
55
+ if (minParts && isBefore(refParts, minParts)) {
56
+ return true;
313
57
  }
314
- else {
315
- // blank data, clear everything out
316
- for (const k in existingData) {
317
- if (existingData.hasOwnProperty(k)) {
318
- delete existingData[k];
319
- }
320
- }
58
+ /**
59
+ * Given a max date, perform the following
60
+ * checks. If any of them are true, then the
61
+ * day should be disabled:
62
+ * 1. Is the current year > the max allowed year?
63
+ * 2. Is the current year === max allowed year,
64
+ * but the current month > the max allowed month?
65
+ * 3. Is the current year === max allowed year, the
66
+ * current month === max allow month, but the current
67
+ * day > the max allowed day?
68
+ */
69
+ if (maxParts && isAfter(refParts, maxParts)) {
70
+ return true;
321
71
  }
72
+ /**
73
+ * If none of these checks
74
+ * passed then the date should
75
+ * be interactive.
76
+ */
322
77
  return false;
323
78
  };
324
- const parseTemplate = (template) => {
325
- const formats = [];
326
- template = template.replace(/[^\w\s]/gi, ' ');
327
- FORMAT_KEYS.forEach(format => {
328
- if (format.f.length > 1 && template.indexOf(format.f) > -1 && template.indexOf(format.f + format.f.charAt(0)) < 0) {
329
- template = template.replace(format.f, ' ' + format.f + ' ');
330
- }
331
- });
332
- const words = template.split(' ').filter(w => w.length > 0);
333
- words.forEach((word, i) => {
334
- FORMAT_KEYS.forEach(format => {
335
- if (word === format.f) {
336
- if (word === FORMAT_A || word === FORMAT_a) {
337
- // this format is an am/pm format, so it's an "a" or "A"
338
- if ((formats.indexOf(FORMAT_h) < 0 && formats.indexOf(FORMAT_hh) < 0) ||
339
- VALID_AMPM_PREFIX.indexOf(words[i - 1]) === -1) {
340
- // template does not already have a 12-hour format
341
- // or this am/pm format doesn't have a hour, minute, or second format immediately before it
342
- // so do not treat this word "a" or "A" as the am/pm format
343
- return;
344
- }
345
- }
346
- formats.push(word);
347
- }
348
- });
349
- });
350
- return formats;
351
- };
352
- const getValueFromFormat = (date, format) => {
353
- if (format === FORMAT_A || format === FORMAT_a) {
354
- return (date.hour < 12 ? 'am' : 'pm');
355
- }
356
- if (format === FORMAT_hh || format === FORMAT_h) {
357
- return (date.hour > 12 ? date.hour - 12 : (date.hour === 0 ? 12 : date.hour));
358
- }
359
- return date[convertFormatToKey(format)];
360
- };
361
- const convertFormatToKey = (format) => {
362
- for (const k in FORMAT_KEYS) {
363
- if (FORMAT_KEYS[k].f === format) {
364
- return FORMAT_KEYS[k].k;
365
- }
366
- }
367
- return undefined;
368
- };
369
- const convertDataToISO = (data) => {
370
- // https://www.w3.org/TR/NOTE-datetime
371
- let rtn = '';
372
- if (data.year !== undefined) {
373
- // YYYY
374
- rtn = fourDigit(data.year);
375
- if (data.month !== undefined) {
376
- // YYYY-MM
377
- rtn += '-' + twoDigit(data.month);
378
- if (data.day !== undefined) {
379
- // YYYY-MM-DD
380
- rtn += '-' + twoDigit(data.day);
381
- if (data.hour !== undefined) {
382
- // YYYY-MM-DDTHH:mm:SS
383
- rtn += `T${twoDigit(data.hour)}:${twoDigit(data.minute)}:${twoDigit(data.second)}`;
384
- if (data.millisecond > 0) {
385
- // YYYY-MM-DDTHH:mm:SS.SSS
386
- rtn += '.' + threeDigit(data.millisecond);
387
- }
388
- if (data.tzOffset === undefined) {
389
- // YYYY-MM-DDTHH:mm:SSZ
390
- rtn += 'Z';
391
- }
392
- else {
393
- // YYYY-MM-DDTHH:mm:SS+/-HH:mm
394
- rtn += (data.tzOffset > 0 ? '+' : '-') + twoDigit(Math.floor(Math.abs(data.tzOffset / 60))) + ':' + twoDigit(data.tzOffset % 60);
395
- }
396
- }
397
- }
398
- }
399
- }
400
- else if (data.hour !== undefined) {
401
- // HH:mm
402
- rtn = twoDigit(data.hour) + ':' + twoDigit(data.minute);
403
- if (data.second !== undefined) {
404
- // HH:mm:SS
405
- rtn += ':' + twoDigit(data.second);
406
- if (data.millisecond !== undefined) {
407
- // HH:mm:SS.SSS
408
- rtn += '.' + threeDigit(data.millisecond);
409
- }
410
- }
411
- }
412
- return rtn;
79
+ /**
80
+ * Given a locale, a date, the selected date(s), and today's date,
81
+ * generate the state for a given calendar day button.
82
+ */
83
+ const getCalendarDayState = (locale, refParts, activeParts, todayParts, minParts, maxParts, dayValues) => {
84
+ /**
85
+ * activeParts signals what day(s) are currently selected in the datetime.
86
+ * If multiple="true", this will be an array, but the logic in this util
87
+ * is the same whether we have one selected day or many because we're only
88
+ * calculating the state for one button. So, we treat a single activeParts value
89
+ * the same as an array of length one.
90
+ */
91
+ const activePartsArray = Array.isArray(activeParts) ? activeParts : [activeParts];
92
+ /**
93
+ * The day button is active if it is selected, or in other words, if refParts
94
+ * matches at least one selected date.
95
+ */
96
+ const isActive = activePartsArray.find((parts) => isSameDay(refParts, parts)) !== undefined;
97
+ const isToday = isSameDay(refParts, todayParts);
98
+ const disabled = isDayDisabled(refParts, minParts, maxParts, dayValues);
99
+ /**
100
+ * Note that we always return one object regardless of whether activeParts
101
+ * was an array, since we pare down to one value for isActive.
102
+ */
103
+ return {
104
+ disabled,
105
+ isActive,
106
+ isToday,
107
+ ariaSelected: isActive ? 'true' : null,
108
+ ariaLabel: generateDayAriaLabel(locale, isToday, refParts),
109
+ text: refParts.day != null ? getDay(locale, refParts) : null,
110
+ };
413
111
  };
414
112
  /**
415
- * Use to convert a string of comma separated strings or
416
- * an array of strings, and clean up any user input
113
+ * Returns `true` if the month is disabled given the
114
+ * current date value and min/max date constraints.
417
115
  */
418
- const convertToArrayOfStrings = (input, type) => {
419
- if (input == null) {
420
- return undefined;
421
- }
422
- if (typeof input === 'string') {
423
- // convert the string to an array of strings
424
- // auto remove any [] characters
425
- input = input.replace(/\[|\]/g, '').split(',');
116
+ const isMonthDisabled = (refParts, { minParts, maxParts, }) => {
117
+ // If the year is disabled then the month is disabled.
118
+ if (isYearDisabled(refParts.year, minParts, maxParts)) {
119
+ return true;
426
120
  }
427
- let values;
428
- if (Array.isArray(input)) {
429
- // trim up each string value
430
- values = input.map(val => val.toString().trim());
121
+ // If the date value is before the min date, then the month is disabled.
122
+ // If the date value is after the max date, then the month is disabled.
123
+ if ((minParts && isBefore(refParts, minParts)) || (maxParts && isAfter(refParts, maxParts))) {
124
+ return true;
431
125
  }
432
- if (values === undefined || values.length === 0) {
433
- console.warn(`Invalid "${type}Names". Must be an array of strings, or a comma separated string.`);
434
- }
435
- return values;
126
+ return false;
436
127
  };
437
128
  /**
438
- * Use to convert a string of comma separated numbers or
439
- * an array of numbers, and clean up any user input
129
+ * Given a working date, an optional minimum date range,
130
+ * and an optional maximum date range; determine if the
131
+ * previous navigation button is disabled.
440
132
  */
441
- const convertToArrayOfNumbers = (input, type) => {
442
- if (typeof input === 'string') {
443
- // convert the string to an array of strings
444
- // auto remove any whitespace and [] characters
445
- input = input.replace(/\[|\]|\s/g, '').split(',');
446
- }
447
- let values;
448
- if (Array.isArray(input)) {
449
- // ensure each value is an actual number in the returned array
450
- values = input
451
- .map((num) => parseInt(num, 10))
452
- .filter(isFinite);
453
- }
454
- else {
455
- values = [input];
456
- }
457
- if (values.length === 0) {
458
- console.warn(`Invalid "${type}Values". Must be an array of numbers, or a comma separated string of numbers.`);
459
- }
460
- return values;
461
- };
462
- const twoDigit = (val) => {
463
- return ('0' + (val !== undefined ? Math.abs(val) : '0')).slice(-2);
464
- };
465
- const threeDigit = (val) => {
466
- return ('00' + (val !== undefined ? Math.abs(val) : '0')).slice(-3);
133
+ const isPrevMonthDisabled = (refParts, minParts, maxParts) => {
134
+ const prevMonth = Object.assign(Object.assign({}, getPreviousMonth(refParts)), { day: null });
135
+ return isMonthDisabled(prevMonth, {
136
+ minParts,
137
+ maxParts,
138
+ });
467
139
  };
468
- const fourDigit = (val) => {
469
- return ('000' + (val !== undefined ? Math.abs(val) : '0')).slice(-4);
140
+ /**
141
+ * Given a working date and a maximum date range,
142
+ * determine if the next navigation button is disabled.
143
+ */
144
+ const isNextMonthDisabled = (refParts, maxParts) => {
145
+ const nextMonth = Object.assign(Object.assign({}, getNextMonth(refParts)), { day: null });
146
+ return isMonthDisabled(nextMonth, {
147
+ maxParts,
148
+ });
470
149
  };
471
- const FORMAT_YYYY = 'YYYY';
472
- const FORMAT_YY = 'YY';
473
- const FORMAT_MMMM = 'MMMM';
474
- const FORMAT_MMM = 'MMM';
475
- const FORMAT_MM = 'MM';
476
- const FORMAT_M = 'M';
477
- const FORMAT_DDDD = 'DDDD';
478
- const FORMAT_DDD = 'DDD';
479
- const FORMAT_DD = 'DD';
480
- const FORMAT_D = 'D';
481
- const FORMAT_HH = 'HH';
482
- const FORMAT_H = 'H';
483
- const FORMAT_hh = 'hh';
484
- const FORMAT_h = 'h';
485
- const FORMAT_mm = 'mm';
486
- const FORMAT_m = 'm';
487
- const FORMAT_ss = 'ss';
488
- const FORMAT_s = 's';
489
- const FORMAT_A = 'A';
490
- const FORMAT_a = 'a';
491
- const FORMAT_KEYS = [
492
- { f: FORMAT_YYYY, k: 'year' },
493
- { f: FORMAT_MMMM, k: 'month' },
494
- { f: FORMAT_DDDD, k: 'day' },
495
- { f: FORMAT_MMM, k: 'month' },
496
- { f: FORMAT_DDD, k: 'day' },
497
- { f: FORMAT_YY, k: 'year' },
498
- { f: FORMAT_MM, k: 'month' },
499
- { f: FORMAT_DD, k: 'day' },
500
- { f: FORMAT_HH, k: 'hour' },
501
- { f: FORMAT_hh, k: 'hour' },
502
- { f: FORMAT_mm, k: 'minute' },
503
- { f: FORMAT_ss, k: 'second' },
504
- { f: FORMAT_M, k: 'month' },
505
- { f: FORMAT_D, k: 'day' },
506
- { f: FORMAT_H, k: 'hour' },
507
- { f: FORMAT_h, k: 'hour' },
508
- { f: FORMAT_m, k: 'minute' },
509
- { f: FORMAT_s, k: 'second' },
510
- { f: FORMAT_A, k: 'ampm' },
511
- { f: FORMAT_a, k: 'ampm' },
512
- ];
513
- const DAY_NAMES = [
514
- 'Sunday',
515
- 'Monday',
516
- 'Tuesday',
517
- 'Wednesday',
518
- 'Thursday',
519
- 'Friday',
520
- 'Saturday',
521
- ];
522
- const DAY_SHORT_NAMES = [
523
- 'Sun',
524
- 'Mon',
525
- 'Tue',
526
- 'Wed',
527
- 'Thu',
528
- 'Fri',
529
- 'Sat',
530
- ];
531
- const MONTH_NAMES = [
532
- 'January',
533
- 'February',
534
- 'March',
535
- 'April',
536
- 'May',
537
- 'June',
538
- 'July',
539
- 'August',
540
- 'September',
541
- 'October',
542
- 'November',
543
- 'December',
544
- ];
545
- const MONTH_SHORT_NAMES = [
546
- 'Jan',
547
- 'Feb',
548
- 'Mar',
549
- 'Apr',
550
- 'May',
551
- 'Jun',
552
- 'Jul',
553
- 'Aug',
554
- 'Sep',
555
- 'Oct',
556
- 'Nov',
557
- 'Dec',
558
- ];
559
- const VALID_AMPM_PREFIX = [
560
- FORMAT_hh, FORMAT_h, FORMAT_mm, FORMAT_m, FORMAT_ss, FORMAT_s
561
- ];
562
150
 
563
- const datetimeIosCss = ":host{padding-left:var(--padding-start);padding-right:var(--padding-end);padding-top:var(--padding-top);padding-bottom:var(--padding-bottom);display:flex;position:relative;min-width:16px;min-height:1.2em;font-family:var(--ion-font-family, inherit);text-overflow:ellipsis;white-space:nowrap;overflow:hidden;z-index:2}@supports (margin-inline-start: 0) or (-webkit-margin-start: 0){:host{padding-left:unset;padding-right:unset;-webkit-padding-start:var(--padding-start);padding-inline-start:var(--padding-start);-webkit-padding-end:var(--padding-end);padding-inline-end:var(--padding-end)}}:host(.in-item){position:static}:host(.datetime-placeholder){color:var(--placeholder-color)}:host(.datetime-disabled){opacity:0.3;pointer-events:none}:host(.datetime-readonly){pointer-events:none}button{left:0;top:0;margin-left:0;margin-right:0;margin-top:0;margin-bottom:0;position:absolute;width:100%;height:100%;border:0;background:transparent;cursor:pointer;appearance:none;outline:none}[dir=rtl] button,:host-context([dir=rtl]) button{left:unset;right:unset;right:0}button::-moz-focus-inner{border:0}.datetime-text{font-family:inherit;font-size:inherit;font-style:inherit;font-weight:inherit;letter-spacing:inherit;text-decoration:inherit;text-indent:inherit;text-overflow:inherit;text-transform:inherit;text-align:inherit;white-space:inherit;color:inherit;flex:1;min-height:inherit;direction:ltr;overflow:inherit}[dir=rtl] .datetime-text,:host-context([dir=rtl]) .datetime-text{direction:rtl}:host{--placeholder-color:var(--ion-color-step-400, #999999);--padding-top:10px;--padding-end:10px;--padding-bottom:10px;--padding-start:20px}";
151
+ const datetimeIosCss = ":host{display:flex;flex-flow:column;background:var(--background);overflow:hidden}ion-picker-column-internal{min-width:26px}:host(.datetime-size-fixed){width:auto;height:auto}:host(.datetime-size-fixed:not(.datetime-prefer-wheel)){max-width:350px}:host(.datetime-size-fixed.datetime-prefer-wheel){min-width:350px;max-width:max-content}:host(.datetime-size-cover){width:100%}:host .calendar-body,:host .datetime-year{opacity:0}:host(:not(.datetime-ready)) .datetime-year{position:absolute;pointer-events:none}:host(.datetime-ready) .calendar-body{opacity:1}:host(.datetime-ready) .datetime-year{display:none;opacity:1}:host .wheel-order-year-first .day-column{order:3;text-align:end}:host .wheel-order-year-first .month-column{order:2;text-align:end}:host .wheel-order-year-first .year-column{order:1;text-align:start}:host .datetime-calendar,:host .datetime-year{display:flex;flex:1 1 auto;flex-flow:column}:host(.show-month-and-year) .datetime-year{display:flex}@supports (background: -webkit-named-image(apple-pay-logo-black)) and (not (aspect-ratio: 1/1)){:host(.show-month-and-year) .calendar-next-prev,:host(.show-month-and-year) .calendar-days-of-week,:host(.show-month-and-year) .calendar-body,:host(.show-month-and-year) .datetime-time{left:-99999px;position:absolute;visibility:hidden;pointer-events:none}:host-context([dir=rtl]):host(.show-month-and-year) .calendar-next-prev,:host-context([dir=rtl]).show-month-and-year .calendar-next-prev,:host-context([dir=rtl]):host(.show-month-and-year) .calendar-days-of-week,:host-context([dir=rtl]).show-month-and-year .calendar-days-of-week,:host-context([dir=rtl]):host(.show-month-and-year) .calendar-body,:host-context([dir=rtl]).show-month-and-year .calendar-body,:host-context([dir=rtl]):host(.show-month-and-year) .datetime-time,:host-context([dir=rtl]).show-month-and-year .datetime-time{left:unset;right:unset;right:-99999px}}@supports (not (background: -webkit-named-image(apple-pay-logo-black))) or ((background: -webkit-named-image(apple-pay-logo-black)) and (aspect-ratio: 1/1)){:host(.show-month-and-year) .calendar-next-prev,:host(.show-month-and-year) .calendar-days-of-week,:host(.show-month-and-year) .calendar-body,:host(.show-month-and-year) .datetime-time{display:none}}:host(.month-year-picker-open) .datetime-footer{display:none}:host(.datetime-readonly),:host(.datetime-disabled){pointer-events:none}:host(.datetime-disabled){opacity:0.4}:host .datetime-header .datetime-title{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}:host .datetime-action-buttons.has-clear-button{width:100%}:host .datetime-action-buttons ion-buttons{display:flex;justify-content:space-between}:host .calendar-action-buttons{display:flex;justify-content:space-between}:host .calendar-action-buttons ion-item,:host .calendar-action-buttons ion-button{--background:translucent}:host .calendar-action-buttons ion-item ion-label{display:flex;align-items:center}:host .calendar-action-buttons ion-item ion-icon{padding-left:4px;padding-right:0;padding-top:0;padding-bottom:0}@supports (margin-inline-start: 0) or (-webkit-margin-start: 0){:host .calendar-action-buttons ion-item ion-icon{padding-left:unset;padding-right:unset;-webkit-padding-start:4px;padding-inline-start:4px;-webkit-padding-end:0;padding-inline-end:0}}:host .calendar-days-of-week{display:grid;grid-template-columns:repeat(7, 1fr);text-align:center}:host .calendar-body{display:flex;flex-grow:1;scroll-snap-type:x mandatory;overflow-x:scroll;overflow-y:hidden;scrollbar-width:none;outline:none}:host .calendar-body .calendar-month{scroll-snap-align:start;scroll-snap-stop:always;flex-shrink:0;width:100%}:host .calendar-body .calendar-month-disabled{scroll-snap-align:none}:host .calendar-body::-webkit-scrollbar{display:none}:host .calendar-body .calendar-month-grid{display:grid;grid-template-columns:repeat(7, 1fr);height:100%}:host .calendar-day{padding-left:0px;padding-right:0px;padding-top:0px;padding-bottom:0px;margin-left:0px;margin-right:0px;margin-top:0px;margin-bottom:0px;display:flex;position:relative;align-items:center;justify-content:center;border:none;outline:none;background:none;color:currentColor;font-family:var(--ion-font-family, inherit);cursor:pointer;appearance:none;z-index:0}@supports (margin-inline-start: 0) or (-webkit-margin-start: 0){:host .calendar-day{padding-left:unset;padding-right:unset;-webkit-padding-start:0px;padding-inline-start:0px;-webkit-padding-end:0px;padding-inline-end:0px}}@supports (margin-inline-start: 0) or (-webkit-margin-start: 0){:host .calendar-day{margin-left:unset;margin-right:unset;-webkit-margin-start:0px;margin-inline-start:0px;-webkit-margin-end:0px;margin-inline-end:0px}}:host .calendar-day[disabled]{pointer-events:none;opacity:0.4}:host .calendar-day:after{border-radius:32px;padding-left:4px;padding-right:4px;padding-top:4px;padding-bottom:4px;position:absolute;top:50%;left:50%;width:32px;height:32px;transform:translate(-50%, -50%);content:\" \";z-index:-1}@supports (margin-inline-start: 0) or (-webkit-margin-start: 0){:host .calendar-day:after{padding-left:unset;padding-right:unset;-webkit-padding-start:4px;padding-inline-start:4px;-webkit-padding-end:4px;padding-inline-end:4px}}:host .datetime-time{display:flex;justify-content:space-between}:host(.datetime-presentation-time) .datetime-time{padding-left:0;padding-right:0;padding-top:0;padding-bottom:0}:host ion-popover{--height:200px}:host .time-header{display:flex;align-items:center}:host .time-body{border-radius:8px;padding-left:12px;padding-right:12px;padding-top:6px;padding-bottom:6px;display:flex;border:none;background:var(--ion-color-step-300, #edeef0);color:var(--ion-text-color, #000);font-family:inherit;font-size:inherit;cursor:pointer;appearance:none}@supports (margin-inline-start: 0) or (-webkit-margin-start: 0){:host .time-body{padding-left:unset;padding-right:unset;-webkit-padding-start:12px;padding-inline-start:12px;-webkit-padding-end:12px;padding-inline-end:12px}}:host .time-body-active{color:var(--ion-color-base)}:host(.in-item){position:static}:host(.show-month-and-year) .calendar-action-buttons ion-item{--color:var(--ion-color-base)}:host{--background:var(--ion-color-light, #ffffff);--background-rgb:var(--ion-color-light-rgb);--title-color:var(--ion-color-step-600, #666666)}:host(.datetime-presentation-date-time:not(.datetime-prefer-wheel)),:host(.datetime-presentation-time-date:not(.datetime-prefer-wheel)),:host(.datetime-presentation-date:not(.datetime-prefer-wheel)){min-height:350px}:host .datetime-header{padding-left:16px;padding-right:16px;padding-top:16px;padding-bottom:16px;border-bottom:0.55px solid var(--ion-color-step-200, #cccccc);font-size:14px}@supports (margin-inline-start: 0) or (-webkit-margin-start: 0){:host .datetime-header{padding-left:unset;padding-right:unset;-webkit-padding-start:16px;padding-inline-start:16px;-webkit-padding-end:16px;padding-inline-end:16px}}:host .datetime-header .datetime-title{color:var(--title-color)}:host .datetime-header .datetime-selected-date{margin-top:10px}:host .calendar-action-buttons ion-item{--padding-start:16px;--background-hover:transparent;--background-activated:transparent;font-size:16px;font-weight:600}:host .calendar-action-buttons ion-item ion-icon,:host .calendar-action-buttons ion-buttons ion-button{color:var(--ion-color-base)}:host .calendar-action-buttons ion-buttons{padding-left:0;padding-right:0;padding-top:8px;padding-bottom:0}:host .calendar-action-buttons ion-buttons ion-button{margin-left:0;margin-right:0;margin-top:0;margin-bottom:0}:host .calendar-days-of-week{padding-left:8px;padding-right:8px;padding-top:0;padding-bottom:0;color:var(--ion-color-step-300, #b3b3b3);font-size:12px;font-weight:600;line-height:24px;text-transform:uppercase}@supports (margin-inline-start: 0) or (-webkit-margin-start: 0){:host .calendar-days-of-week{padding-left:unset;padding-right:unset;-webkit-padding-start:8px;padding-inline-start:8px;-webkit-padding-end:8px;padding-inline-end:8px}}:host .calendar-body .calendar-month .calendar-month-grid{padding-left:8px;padding-right:8px;padding-top:8px;padding-bottom:8px;height:calc(100% - 16px)}@supports (margin-inline-start: 0) or (-webkit-margin-start: 0){:host .calendar-body .calendar-month .calendar-month-grid{padding-left:unset;padding-right:unset;-webkit-padding-start:8px;padding-inline-start:8px;-webkit-padding-end:8px;padding-inline-end:8px}}:host .calendar-day{font-size:20px}:host .calendar-day:after{opacity:0.2}:host .calendar-day:focus:after{background:var(--ion-color-base)}:host .calendar-day.calendar-day-today{color:var(--ion-color-base)}:host .calendar-day.calendar-day-active{color:var(--ion-color-base);font-weight:600}:host .calendar-day.calendar-day-active:after{background:var(--ion-color-base)}:host .calendar-day.calendar-day-today.calendar-day-active{color:var(--ion-color-contrast)}:host .calendar-day.calendar-day-today.calendar-day-active:after{background:var(--ion-color-base);opacity:1}:host .datetime-time{padding-left:16px;padding-right:16px;padding-top:8px;padding-bottom:16px;font-size:16px}@supports (margin-inline-start: 0) or (-webkit-margin-start: 0){:host .datetime-time{padding-left:unset;padding-right:unset;-webkit-padding-start:16px;padding-inline-start:16px;-webkit-padding-end:16px;padding-inline-end:16px}}:host .datetime-time .time-header{font-weight:600}:host .datetime-buttons{padding-left:8px;padding-right:8px;padding-top:8px;padding-bottom:8px;border-top:0.55px solid var(--ion-color-step-200, #cccccc)}@supports (margin-inline-start: 0) or (-webkit-margin-start: 0){:host .datetime-buttons{padding-left:unset;padding-right:unset;-webkit-padding-start:8px;padding-inline-start:8px;-webkit-padding-end:8px;padding-inline-end:8px}}:host .datetime-buttons ::slotted(ion-buttons),:host .datetime-buttons ion-buttons{display:flex;align-items:center;justify-content:space-between}:host .datetime-action-buttons{width:100%}";
564
152
 
565
- const datetimeMdCss = ":host{padding-left:var(--padding-start);padding-right:var(--padding-end);padding-top:var(--padding-top);padding-bottom:var(--padding-bottom);display:flex;position:relative;min-width:16px;min-height:1.2em;font-family:var(--ion-font-family, inherit);text-overflow:ellipsis;white-space:nowrap;overflow:hidden;z-index:2}@supports (margin-inline-start: 0) or (-webkit-margin-start: 0){:host{padding-left:unset;padding-right:unset;-webkit-padding-start:var(--padding-start);padding-inline-start:var(--padding-start);-webkit-padding-end:var(--padding-end);padding-inline-end:var(--padding-end)}}:host(.in-item){position:static}:host(.datetime-placeholder){color:var(--placeholder-color)}:host(.datetime-disabled){opacity:0.3;pointer-events:none}:host(.datetime-readonly){pointer-events:none}button{left:0;top:0;margin-left:0;margin-right:0;margin-top:0;margin-bottom:0;position:absolute;width:100%;height:100%;border:0;background:transparent;cursor:pointer;appearance:none;outline:none}[dir=rtl] button,:host-context([dir=rtl]) button{left:unset;right:unset;right:0}button::-moz-focus-inner{border:0}.datetime-text{font-family:inherit;font-size:inherit;font-style:inherit;font-weight:inherit;letter-spacing:inherit;text-decoration:inherit;text-indent:inherit;text-overflow:inherit;text-transform:inherit;text-align:inherit;white-space:inherit;color:inherit;flex:1;min-height:inherit;direction:ltr;overflow:inherit}[dir=rtl] .datetime-text,:host-context([dir=rtl]) .datetime-text{direction:rtl}:host{--placeholder-color:var(--ion-placeholder-color, var(--ion-color-step-400, #999999));--padding-top:10px;--padding-end:0;--padding-bottom:11px;--padding-start:16px}";
153
+ const datetimeMdCss = ":host{display:flex;flex-flow:column;background:var(--background);overflow:hidden}ion-picker-column-internal{min-width:26px}:host(.datetime-size-fixed){width:auto;height:auto}:host(.datetime-size-fixed:not(.datetime-prefer-wheel)){max-width:350px}:host(.datetime-size-fixed.datetime-prefer-wheel){min-width:350px;max-width:max-content}:host(.datetime-size-cover){width:100%}:host .calendar-body,:host .datetime-year{opacity:0}:host(:not(.datetime-ready)) .datetime-year{position:absolute;pointer-events:none}:host(.datetime-ready) .calendar-body{opacity:1}:host(.datetime-ready) .datetime-year{display:none;opacity:1}:host .wheel-order-year-first .day-column{order:3;text-align:end}:host .wheel-order-year-first .month-column{order:2;text-align:end}:host .wheel-order-year-first .year-column{order:1;text-align:start}:host .datetime-calendar,:host .datetime-year{display:flex;flex:1 1 auto;flex-flow:column}:host(.show-month-and-year) .datetime-year{display:flex}@supports (background: -webkit-named-image(apple-pay-logo-black)) and (not (aspect-ratio: 1/1)){:host(.show-month-and-year) .calendar-next-prev,:host(.show-month-and-year) .calendar-days-of-week,:host(.show-month-and-year) .calendar-body,:host(.show-month-and-year) .datetime-time{left:-99999px;position:absolute;visibility:hidden;pointer-events:none}:host-context([dir=rtl]):host(.show-month-and-year) .calendar-next-prev,:host-context([dir=rtl]).show-month-and-year .calendar-next-prev,:host-context([dir=rtl]):host(.show-month-and-year) .calendar-days-of-week,:host-context([dir=rtl]).show-month-and-year .calendar-days-of-week,:host-context([dir=rtl]):host(.show-month-and-year) .calendar-body,:host-context([dir=rtl]).show-month-and-year .calendar-body,:host-context([dir=rtl]):host(.show-month-and-year) .datetime-time,:host-context([dir=rtl]).show-month-and-year .datetime-time{left:unset;right:unset;right:-99999px}}@supports (not (background: -webkit-named-image(apple-pay-logo-black))) or ((background: -webkit-named-image(apple-pay-logo-black)) and (aspect-ratio: 1/1)){:host(.show-month-and-year) .calendar-next-prev,:host(.show-month-and-year) .calendar-days-of-week,:host(.show-month-and-year) .calendar-body,:host(.show-month-and-year) .datetime-time{display:none}}:host(.month-year-picker-open) .datetime-footer{display:none}:host(.datetime-readonly),:host(.datetime-disabled){pointer-events:none}:host(.datetime-disabled){opacity:0.4}:host .datetime-header .datetime-title{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}:host .datetime-action-buttons.has-clear-button{width:100%}:host .datetime-action-buttons ion-buttons{display:flex;justify-content:space-between}:host .calendar-action-buttons{display:flex;justify-content:space-between}:host .calendar-action-buttons ion-item,:host .calendar-action-buttons ion-button{--background:translucent}:host .calendar-action-buttons ion-item ion-label{display:flex;align-items:center}:host .calendar-action-buttons ion-item ion-icon{padding-left:4px;padding-right:0;padding-top:0;padding-bottom:0}@supports (margin-inline-start: 0) or (-webkit-margin-start: 0){:host .calendar-action-buttons ion-item ion-icon{padding-left:unset;padding-right:unset;-webkit-padding-start:4px;padding-inline-start:4px;-webkit-padding-end:0;padding-inline-end:0}}:host .calendar-days-of-week{display:grid;grid-template-columns:repeat(7, 1fr);text-align:center}:host .calendar-body{display:flex;flex-grow:1;scroll-snap-type:x mandatory;overflow-x:scroll;overflow-y:hidden;scrollbar-width:none;outline:none}:host .calendar-body .calendar-month{scroll-snap-align:start;scroll-snap-stop:always;flex-shrink:0;width:100%}:host .calendar-body .calendar-month-disabled{scroll-snap-align:none}:host .calendar-body::-webkit-scrollbar{display:none}:host .calendar-body .calendar-month-grid{display:grid;grid-template-columns:repeat(7, 1fr);height:100%}:host .calendar-day{padding-left:0px;padding-right:0px;padding-top:0px;padding-bottom:0px;margin-left:0px;margin-right:0px;margin-top:0px;margin-bottom:0px;display:flex;position:relative;align-items:center;justify-content:center;border:none;outline:none;background:none;color:currentColor;font-family:var(--ion-font-family, inherit);cursor:pointer;appearance:none;z-index:0}@supports (margin-inline-start: 0) or (-webkit-margin-start: 0){:host .calendar-day{padding-left:unset;padding-right:unset;-webkit-padding-start:0px;padding-inline-start:0px;-webkit-padding-end:0px;padding-inline-end:0px}}@supports (margin-inline-start: 0) or (-webkit-margin-start: 0){:host .calendar-day{margin-left:unset;margin-right:unset;-webkit-margin-start:0px;margin-inline-start:0px;-webkit-margin-end:0px;margin-inline-end:0px}}:host .calendar-day[disabled]{pointer-events:none;opacity:0.4}:host .calendar-day:after{border-radius:32px;padding-left:4px;padding-right:4px;padding-top:4px;padding-bottom:4px;position:absolute;top:50%;left:50%;width:32px;height:32px;transform:translate(-50%, -50%);content:\" \";z-index:-1}@supports (margin-inline-start: 0) or (-webkit-margin-start: 0){:host .calendar-day:after{padding-left:unset;padding-right:unset;-webkit-padding-start:4px;padding-inline-start:4px;-webkit-padding-end:4px;padding-inline-end:4px}}:host .datetime-time{display:flex;justify-content:space-between}:host(.datetime-presentation-time) .datetime-time{padding-left:0;padding-right:0;padding-top:0;padding-bottom:0}:host ion-popover{--height:200px}:host .time-header{display:flex;align-items:center}:host .time-body{border-radius:8px;padding-left:12px;padding-right:12px;padding-top:6px;padding-bottom:6px;display:flex;border:none;background:var(--ion-color-step-300, #edeef0);color:var(--ion-text-color, #000);font-family:inherit;font-size:inherit;cursor:pointer;appearance:none}@supports (margin-inline-start: 0) or (-webkit-margin-start: 0){:host .time-body{padding-left:unset;padding-right:unset;-webkit-padding-start:12px;padding-inline-start:12px;-webkit-padding-end:12px;padding-inline-end:12px}}:host .time-body-active{color:var(--ion-color-base)}:host(.in-item){position:static}:host(.show-month-and-year) .calendar-action-buttons ion-item{--color:var(--ion-color-base)}:host{--background:var(--ion-color-step-100, #ffffff);--title-color:var(--ion-color-contrast)}:host .datetime-header{padding-left:20px;padding-right:20px;padding-top:20px;padding-bottom:20px;background:var(--ion-color-base);color:var(--title-color)}@supports (margin-inline-start: 0) or (-webkit-margin-start: 0){:host .datetime-header{padding-left:unset;padding-right:unset;-webkit-padding-start:20px;padding-inline-start:20px;-webkit-padding-end:20px;padding-inline-end:20px}}:host .datetime-header .datetime-title{font-size:12px;text-transform:uppercase}:host .datetime-header .datetime-selected-date{margin-top:30px;font-size:34px}:host .datetime-calendar .calendar-action-buttons ion-item{--padding-start:20px}:host .calendar-action-buttons ion-item,:host .calendar-action-buttons ion-button{color:var(--ion-color-step-650, #595959)}:host .calendar-days-of-week{padding-left:10px;padding-right:10px;padding-top:0px;padding-bottom:0px;color:var(--ion-color-step-500, gray);font-size:14px;line-height:36px}@supports (margin-inline-start: 0) or (-webkit-margin-start: 0){:host .calendar-days-of-week{padding-left:unset;padding-right:unset;-webkit-padding-start:10px;padding-inline-start:10px;-webkit-padding-end:10px;padding-inline-end:10px}}:host .calendar-body .calendar-month .calendar-month-grid{padding-left:10px;padding-right:10px;padding-top:3px;padding-bottom:0px;grid-template-rows:repeat(6, 1fr)}@supports (margin-inline-start: 0) or (-webkit-margin-start: 0){:host .calendar-body .calendar-month .calendar-month-grid{padding-left:unset;padding-right:unset;-webkit-padding-start:10px;padding-inline-start:10px;-webkit-padding-end:10px;padding-inline-end:10px}}:host .calendar-day{padding-left:0px;padding-right:0;padding-top:13px;padding-bottom:13px;font-size:14px}@supports (margin-inline-start: 0) or (-webkit-margin-start: 0){:host .calendar-day{padding-left:unset;padding-right:unset;-webkit-padding-start:0px;padding-inline-start:0px;-webkit-padding-end:0;padding-inline-end:0}}:host .calendar-day:focus:after{background:rgba(var(--ion-color-base-rgb), 0.2);box-shadow:0px 0px 0px 4px rgba(var(--ion-color-base-rgb), 0.2)}:host .calendar-day.calendar-day-today{color:var(--ion-color-base)}:host .calendar-day.calendar-day-today:after{border:1px solid var(--ion-color-base)}:host .calendar-day.calendar-day-active{color:var(--ion-color-contrast)}:host .calendar-day.calendar-day-active:after{border:1px solid var(--ion-color-base);background:var(--ion-color-base)}:host .datetime-time{padding-left:16px;padding-right:16px;padding-top:8px;padding-bottom:8px}@supports (margin-inline-start: 0) or (-webkit-margin-start: 0){:host .datetime-time{padding-left:unset;padding-right:unset;-webkit-padding-start:16px;padding-inline-start:16px;-webkit-padding-end:16px;padding-inline-end:16px}}:host .time-header{color:var(--ion-color-step-650, #595959)}:host(.datetime-presentation-month) .datetime-year,:host(.datetime-presentation-year) .datetime-year,:host(.datetime-presentation-month-year) .datetime-year{margin-top:20px;margin-bottom:20px}:host .datetime-buttons{padding-left:10px;padding-right:10px;padding-top:10px;padding-bottom:10px;display:flex;align-items:center;justify-content:flex-end}@supports (margin-inline-start: 0) or (-webkit-margin-start: 0){:host .datetime-buttons{padding-left:unset;padding-right:unset;-webkit-padding-start:10px;padding-inline-start:10px;-webkit-padding-end:10px;padding-inline-end:10px}}:host .datetime-view-buttons ion-button{color:var(--ion-color-step-800, #333333)}";
566
154
 
567
155
  const Datetime = class {
568
156
  constructor(hostRef) {
@@ -572,12 +160,32 @@ const Datetime = class {
572
160
  this.ionFocus = createEvent(this, "ionFocus", 7);
573
161
  this.ionBlur = createEvent(this, "ionBlur", 7);
574
162
  this.ionStyle = createEvent(this, "ionStyle", 7);
163
+ this.ionRender = createEvent(this, "ionRender", 7);
575
164
  this.inputId = `ion-dt-${datetimeIds++}`;
576
- this.locale = {};
577
- this.datetimeMin = {};
578
- this.datetimeMax = {};
579
- this.datetimeValue = {};
580
- this.isExpanded = false;
165
+ this.prevPresentation = null;
166
+ /**
167
+ * Duplicate reference to `activeParts` that does not trigger a re-render of the component.
168
+ * Allows caching an instance of the `activeParts` in between render cycles.
169
+ */
170
+ this.activePartsClone = [];
171
+ this.showMonthAndYear = false;
172
+ this.activeParts = [];
173
+ this.workingParts = {
174
+ month: 5,
175
+ day: 28,
176
+ year: 2021,
177
+ hour: 13,
178
+ minute: 52,
179
+ ampm: 'pm',
180
+ };
181
+ this.isPresented = false;
182
+ this.isTimePopoverOpen = false;
183
+ /**
184
+ * The color to use from your application's color palette.
185
+ * Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`.
186
+ * For more information on colors, see [theming](/docs/theming/basics).
187
+ */
188
+ this.color = 'primary';
581
189
  /**
582
190
  * The name of the control, which is submitted with the form data.
583
191
  */
@@ -591,13 +199,13 @@ const Datetime = class {
591
199
  */
592
200
  this.readonly = false;
593
201
  /**
594
- * The display format of the date and time as text that shows
595
- * within the item. When the `pickerFormat` input is not used, then the
596
- * `displayFormat` is used for both display the formatted text, and determining
597
- * the datetime picker's columns. See the `pickerFormat` input description for
598
- * more info. Defaults to `MMM D, YYYY`.
202
+ * Which values you want to select. `'date'` will show
203
+ * a calendar picker to select the month, day, and year. `'time'`
204
+ * will show a time picker to select the hour, minute, and (optionally)
205
+ * AM/PM. `'date-time'` will show the date picker first and time picker second.
206
+ * `'time-date'` will show the time picker first and date picker second.
599
207
  */
600
- this.displayFormat = 'MMM D, YYYY';
208
+ this.presentation = 'date-time';
601
209
  /**
602
210
  * The text to display on the picker's cancel button.
603
211
  */
@@ -606,9 +214,527 @@ const Datetime = class {
606
214
  * The text to display on the picker's "Done" button.
607
215
  */
608
216
  this.doneText = 'Done';
609
- this.onClick = () => {
610
- this.setFocus();
611
- this.open();
217
+ /**
218
+ * The text to display on the picker's "Clear" button.
219
+ */
220
+ this.clearText = 'Clear';
221
+ /**
222
+ * The locale to use for `ion-datetime`. This
223
+ * impacts month and day name formatting.
224
+ * The `'default'` value refers to the default
225
+ * locale set by your device.
226
+ */
227
+ this.locale = 'default';
228
+ /**
229
+ * The first day of the week to use for `ion-datetime`. The
230
+ * default value is `0` and represents Sunday.
231
+ */
232
+ this.firstDayOfWeek = 0;
233
+ /**
234
+ * If `true`, multiple dates can be selected at once. Only
235
+ * applies to `presentation="date"` and `preferWheel="false"`.
236
+ */
237
+ this.multiple = false;
238
+ /**
239
+ * If `true`, a header will be shown above the calendar
240
+ * picker. This will include both the slotted title, and
241
+ * the selected date.
242
+ */
243
+ this.showDefaultTitle = false;
244
+ /**
245
+ * If `true`, the default "Cancel" and "OK" buttons
246
+ * will be rendered at the bottom of the `ion-datetime`
247
+ * component. Developers can also use the `button` slot
248
+ * if they want to customize these buttons. If custom
249
+ * buttons are set in the `button` slot then the
250
+ * default buttons will not be rendered.
251
+ */
252
+ this.showDefaultButtons = false;
253
+ /**
254
+ * If `true`, a "Clear" button will be rendered alongside
255
+ * the default "Cancel" and "OK" buttons at the bottom of the `ion-datetime`
256
+ * component. Developers can also use the `button` slot
257
+ * if they want to customize these buttons. If custom
258
+ * buttons are set in the `button` slot then the
259
+ * default buttons will not be rendered.
260
+ */
261
+ this.showClearButton = false;
262
+ /**
263
+ * If `true`, the default "Time" label will be rendered
264
+ * for the time selector of the `ion-datetime` component.
265
+ * Developers can also use the `time-label` slot
266
+ * if they want to customize this label. If a custom
267
+ * label is set in the `time-label` slot then the
268
+ * default label will not be rendered.
269
+ */
270
+ this.showDefaultTimeLabel = true;
271
+ /**
272
+ * If `cover`, the `ion-datetime` will expand to cover the full width of its container.
273
+ * If `fixed`, the `ion-datetime` will have a fixed width.
274
+ */
275
+ this.size = 'fixed';
276
+ /**
277
+ * If `true`, a wheel picker will be rendered instead of a calendar grid
278
+ * where possible. If `false`, a calendar grid will be rendered instead of
279
+ * a wheel picker where possible.
280
+ *
281
+ * A wheel picker can be rendered instead of a grid when `presentation` is
282
+ * one of the following values: `'date'`, `'date-time'`, or `'time-date'`.
283
+ *
284
+ * A wheel picker will always be rendered regardless of
285
+ * the `preferWheel` value when `presentation` is one of the following values:
286
+ * `'time'`, `'month'`, `'month-year'`, or `'year'`.
287
+ */
288
+ this.preferWheel = false;
289
+ /**
290
+ * Returns the DatetimePart interface
291
+ * to use when rendering an initial set of
292
+ * data. This should be used when rendering an
293
+ * interface in an environment where the `value`
294
+ * may not be set. This function works
295
+ * by returning the first selected date in
296
+ * "activePartsClone" and then falling back to
297
+ * defaultParts if no active date is selected.
298
+ */
299
+ this.getActivePartsWithFallback = () => {
300
+ var _a;
301
+ const { defaultParts } = this;
302
+ return (_a = this.getActivePart()) !== null && _a !== void 0 ? _a : defaultParts;
303
+ };
304
+ this.getActivePart = () => {
305
+ const { activePartsClone } = this;
306
+ return Array.isArray(activePartsClone) ? activePartsClone[0] : activePartsClone;
307
+ };
308
+ this.closeParentOverlay = () => {
309
+ const popoverOrModal = this.el.closest('ion-modal, ion-popover');
310
+ if (popoverOrModal) {
311
+ popoverOrModal.dismiss();
312
+ }
313
+ };
314
+ this.setWorkingParts = (parts) => {
315
+ this.workingParts = Object.assign({}, parts);
316
+ };
317
+ this.setActiveParts = (parts, removeDate = false) => {
318
+ const { multiple, minParts, maxParts, activePartsClone } = this;
319
+ /**
320
+ * When setting the active parts, it is possible
321
+ * to set invalid data. For example,
322
+ * when updating January 31 to February,
323
+ * February 31 does not exist. As a result
324
+ * we need to validate the active parts and
325
+ * ensure that we are only setting valid dates.
326
+ * Additionally, we need to update the working parts
327
+ * too in the event that the validated parts are different.
328
+ */
329
+ const validatedParts = validateParts(parts, minParts, maxParts);
330
+ this.setWorkingParts(validatedParts);
331
+ if (multiple) {
332
+ /**
333
+ * We read from activePartsClone here because valueChanged() only updates that,
334
+ * so it's the more reliable source of truth. If we read from activeParts, then
335
+ * if you click July 1, manually set the value to July 2, and then click July 3,
336
+ * the new value would be [July 1, July 3], ignoring the value set.
337
+ *
338
+ * We can then pass the new value to activeParts (rather than activePartsClone)
339
+ * since the clone will be updated automatically by activePartsChanged().
340
+ */
341
+ const activePartsArray = Array.isArray(activePartsClone) ? activePartsClone : [activePartsClone];
342
+ if (removeDate) {
343
+ this.activeParts = activePartsArray.filter((p) => !isSameDay(p, validatedParts));
344
+ }
345
+ else {
346
+ this.activeParts = [...activePartsArray, validatedParts];
347
+ }
348
+ }
349
+ else {
350
+ this.activeParts = Object.assign({}, validatedParts);
351
+ }
352
+ const hasSlottedButtons = this.el.querySelector('[slot="buttons"]') !== null;
353
+ if (hasSlottedButtons || this.showDefaultButtons) {
354
+ return;
355
+ }
356
+ this.confirm();
357
+ };
358
+ this.initializeKeyboardListeners = () => {
359
+ const calendarBodyRef = this.calendarBodyRef;
360
+ if (!calendarBodyRef) {
361
+ return;
362
+ }
363
+ const root = this.el.shadowRoot;
364
+ /**
365
+ * Get a reference to the month
366
+ * element we are currently viewing.
367
+ */
368
+ const currentMonth = calendarBodyRef.querySelector('.calendar-month:nth-of-type(2)');
369
+ /**
370
+ * When focusing the calendar body, we want to pass focus
371
+ * to the working day, but other days should
372
+ * only be accessible using the arrow keys. Pressing
373
+ * Tab should jump between bodies of selectable content.
374
+ */
375
+ const checkCalendarBodyFocus = (ev) => {
376
+ var _a;
377
+ const record = ev[0];
378
+ /**
379
+ * If calendar body was already focused
380
+ * when this fired or if the calendar body
381
+ * if not currently focused, we should not re-focus
382
+ * the inner day.
383
+ */
384
+ if (((_a = record.oldValue) === null || _a === void 0 ? void 0 : _a.includes('ion-focused')) || !calendarBodyRef.classList.contains('ion-focused')) {
385
+ return;
386
+ }
387
+ this.focusWorkingDay(currentMonth);
388
+ };
389
+ const mo = new MutationObserver(checkCalendarBodyFocus);
390
+ mo.observe(calendarBodyRef, { attributeFilter: ['class'], attributeOldValue: true });
391
+ this.destroyKeyboardMO = () => {
392
+ mo === null || mo === void 0 ? void 0 : mo.disconnect();
393
+ };
394
+ /**
395
+ * We must use keydown not keyup as we want
396
+ * to prevent scrolling when using the arrow keys.
397
+ */
398
+ calendarBodyRef.addEventListener('keydown', (ev) => {
399
+ const activeElement = root.activeElement;
400
+ if (!activeElement || !activeElement.classList.contains('calendar-day')) {
401
+ return;
402
+ }
403
+ const parts = getPartsFromCalendarDay(activeElement);
404
+ let partsToFocus;
405
+ switch (ev.key) {
406
+ case 'ArrowDown':
407
+ ev.preventDefault();
408
+ partsToFocus = getNextWeek(parts);
409
+ break;
410
+ case 'ArrowUp':
411
+ ev.preventDefault();
412
+ partsToFocus = getPreviousWeek(parts);
413
+ break;
414
+ case 'ArrowRight':
415
+ ev.preventDefault();
416
+ partsToFocus = getNextDay(parts);
417
+ break;
418
+ case 'ArrowLeft':
419
+ ev.preventDefault();
420
+ partsToFocus = getPreviousDay(parts);
421
+ break;
422
+ case 'Home':
423
+ ev.preventDefault();
424
+ partsToFocus = getStartOfWeek(parts);
425
+ break;
426
+ case 'End':
427
+ ev.preventDefault();
428
+ partsToFocus = getEndOfWeek(parts);
429
+ break;
430
+ case 'PageUp':
431
+ ev.preventDefault();
432
+ partsToFocus = ev.shiftKey ? getPreviousYear(parts) : getPreviousMonth(parts);
433
+ break;
434
+ case 'PageDown':
435
+ ev.preventDefault();
436
+ partsToFocus = ev.shiftKey ? getNextYear(parts) : getNextMonth(parts);
437
+ break;
438
+ /**
439
+ * Do not preventDefault here
440
+ * as we do not want to override other
441
+ * browser defaults such as pressing Enter/Space
442
+ * to select a day.
443
+ */
444
+ default:
445
+ return;
446
+ }
447
+ /**
448
+ * If the day we want to move focus to is
449
+ * disabled, do not do anything.
450
+ */
451
+ if (isDayDisabled(partsToFocus, this.minParts, this.maxParts)) {
452
+ return;
453
+ }
454
+ this.setWorkingParts(Object.assign(Object.assign({}, this.workingParts), partsToFocus));
455
+ /**
456
+ * Give view a chance to re-render
457
+ * then move focus to the new working day
458
+ */
459
+ requestAnimationFrame(() => this.focusWorkingDay(currentMonth));
460
+ });
461
+ };
462
+ this.focusWorkingDay = (currentMonth) => {
463
+ /**
464
+ * Get the number of padding days so
465
+ * we know how much to offset our next selector by
466
+ * to grab the correct calenday-day element.
467
+ */
468
+ const padding = currentMonth.querySelectorAll('.calendar-day-padding');
469
+ const { day } = this.workingParts;
470
+ if (day === null) {
471
+ return;
472
+ }
473
+ /**
474
+ * Get the calendar day element
475
+ * and focus it.
476
+ */
477
+ const dayEl = currentMonth.querySelector(`.calendar-day:nth-of-type(${padding.length + day})`);
478
+ if (dayEl) {
479
+ dayEl.focus();
480
+ }
481
+ };
482
+ this.processMinParts = () => {
483
+ const { min, defaultParts } = this;
484
+ if (min === undefined) {
485
+ this.minParts = undefined;
486
+ return;
487
+ }
488
+ this.minParts = parseMinParts(min, defaultParts);
489
+ };
490
+ this.processMaxParts = () => {
491
+ const { max, defaultParts } = this;
492
+ if (max === undefined) {
493
+ this.maxParts = undefined;
494
+ return;
495
+ }
496
+ this.maxParts = parseMaxParts(max, defaultParts);
497
+ };
498
+ this.initializeCalendarListener = () => {
499
+ const calendarBodyRef = this.calendarBodyRef;
500
+ if (!calendarBodyRef) {
501
+ return;
502
+ }
503
+ /**
504
+ * For performance reasons, we only render 3
505
+ * months at a time: The current month, the previous
506
+ * month, and the next month. We have a scroll listener
507
+ * on the calendar body to append/prepend new months.
508
+ *
509
+ * We can do this because Stencil is smart enough to not
510
+ * re-create the .calendar-month containers, but rather
511
+ * update the content within those containers.
512
+ *
513
+ * As an added bonus, WebKit has some troubles with
514
+ * scroll-snap-stop: always, so not rendering all of
515
+ * the months in a row allows us to mostly sidestep
516
+ * that issue.
517
+ */
518
+ const months = calendarBodyRef.querySelectorAll('.calendar-month');
519
+ const startMonth = months[0];
520
+ const workingMonth = months[1];
521
+ const endMonth = months[2];
522
+ const mode = getIonMode(this);
523
+ const needsiOSRubberBandFix = mode === 'ios' && typeof navigator !== 'undefined' && navigator.maxTouchPoints > 1;
524
+ /**
525
+ * Before setting up the scroll listener,
526
+ * scroll the middle month into view.
527
+ * scrollIntoView() will scroll entire page
528
+ * if element is not in viewport. Use scrollLeft instead.
529
+ */
530
+ writeTask(() => {
531
+ calendarBodyRef.scrollLeft = startMonth.clientWidth * (isRTL(this.el) ? -1 : 1);
532
+ const getChangedMonth = (parts) => {
533
+ const box = calendarBodyRef.getBoundingClientRect();
534
+ const root = this.el.shadowRoot;
535
+ /**
536
+ * Get the element that is in the center of the calendar body.
537
+ * This will be an element inside of the active month.
538
+ */
539
+ const elementAtCenter = root.elementFromPoint(box.x + box.width / 2, box.y + box.height / 2);
540
+ /**
541
+ * If there is no element then the
542
+ * component may be re-rendering on a slow device.
543
+ */
544
+ if (!elementAtCenter)
545
+ return;
546
+ const month = elementAtCenter.closest('.calendar-month');
547
+ if (!month)
548
+ return;
549
+ /**
550
+ * The edge of the month must be lined up with
551
+ * the edge of the calendar body in order for
552
+ * the component to update. Otherwise, it
553
+ * may be the case that the user has paused their
554
+ * swipe or the browser has not finished snapping yet.
555
+ * Rather than check if the x values are equal,
556
+ * we give it a tolerance of 2px to account for
557
+ * sub pixel rendering.
558
+ */
559
+ const monthBox = month.getBoundingClientRect();
560
+ if (Math.abs(monthBox.x - box.x) > 2)
561
+ return;
562
+ /**
563
+ * From here, we can determine if the start
564
+ * month or the end month was scrolled into view.
565
+ * If no month was changed, then we can return from
566
+ * the scroll callback early.
567
+ */
568
+ if (month === startMonth) {
569
+ return getPreviousMonth(parts);
570
+ }
571
+ else if (month === endMonth) {
572
+ return getNextMonth(parts);
573
+ }
574
+ else {
575
+ return;
576
+ }
577
+ };
578
+ const updateActiveMonth = () => {
579
+ if (needsiOSRubberBandFix) {
580
+ calendarBodyRef.style.removeProperty('pointer-events');
581
+ appliediOSRubberBandFix = false;
582
+ }
583
+ /**
584
+ * If the month did not change
585
+ * then we can return early.
586
+ */
587
+ const newDate = getChangedMonth(this.workingParts);
588
+ if (!newDate)
589
+ return;
590
+ const { month, day, year } = newDate;
591
+ if (isMonthDisabled({ month, year, day: null }, {
592
+ minParts: Object.assign(Object.assign({}, this.minParts), { day: null }),
593
+ maxParts: Object.assign(Object.assign({}, this.maxParts), { day: null }),
594
+ })) {
595
+ return;
596
+ }
597
+ /**
598
+ * Prevent scrolling for other browsers
599
+ * to give the DOM time to update and the container
600
+ * time to properly snap.
601
+ */
602
+ calendarBodyRef.style.setProperty('overflow', 'hidden');
603
+ /**
604
+ * Use a writeTask here to ensure
605
+ * that the state is updated and the
606
+ * correct month is scrolled into view
607
+ * in the same frame. This is not
608
+ * typically a problem on newer devices
609
+ * but older/slower device may have a flicker
610
+ * if we did not do this.
611
+ */
612
+ writeTask(() => {
613
+ this.setWorkingParts(Object.assign(Object.assign({}, this.workingParts), { month, day: day, year }));
614
+ calendarBodyRef.scrollLeft = workingMonth.clientWidth * (isRTL(this.el) ? -1 : 1);
615
+ calendarBodyRef.style.removeProperty('overflow');
616
+ });
617
+ };
618
+ /**
619
+ * When the container finishes scrolling we
620
+ * need to update the DOM with the selected month.
621
+ */
622
+ let scrollTimeout;
623
+ /**
624
+ * We do not want to attempt to set pointer-events
625
+ * multiple times within a single swipe gesture as
626
+ * that adds unnecessary work to the main thread.
627
+ */
628
+ let appliediOSRubberBandFix = false;
629
+ const scrollCallback = () => {
630
+ if (scrollTimeout) {
631
+ clearTimeout(scrollTimeout);
632
+ }
633
+ /**
634
+ * On iOS it is possible to quickly rubber band
635
+ * the scroll area before the scroll timeout has fired.
636
+ * This results in users reaching the end of the scrollable
637
+ * container before the DOM has updated.
638
+ * By setting `pointer-events: none` we can ensure that
639
+ * subsequent swipes do not happen while the container
640
+ * is snapping.
641
+ */
642
+ if (!appliediOSRubberBandFix && needsiOSRubberBandFix) {
643
+ calendarBodyRef.style.setProperty('pointer-events', 'none');
644
+ appliediOSRubberBandFix = true;
645
+ }
646
+ // Wait ~3 frames
647
+ scrollTimeout = setTimeout(updateActiveMonth, 50);
648
+ };
649
+ calendarBodyRef.addEventListener('scroll', scrollCallback);
650
+ this.destroyCalendarListener = () => {
651
+ calendarBodyRef.removeEventListener('scroll', scrollCallback);
652
+ };
653
+ });
654
+ };
655
+ /**
656
+ * Clean up all listeners except for the overlay
657
+ * listener. This is so that we can re-create the listeners
658
+ * if the datetime has been hidden/presented by a modal or popover.
659
+ */
660
+ this.destroyInteractionListeners = () => {
661
+ const { destroyCalendarListener, destroyKeyboardMO } = this;
662
+ if (destroyCalendarListener !== undefined) {
663
+ destroyCalendarListener();
664
+ }
665
+ if (destroyKeyboardMO !== undefined) {
666
+ destroyKeyboardMO();
667
+ }
668
+ };
669
+ this.processValue = (value) => {
670
+ /**
671
+ * TODO FW-2646 remove value !== ''
672
+ */
673
+ const hasValue = value !== '' && value !== null && value !== undefined;
674
+ let valueToProcess = hasValue ? parseDate(value) : this.defaultParts;
675
+ const { minParts, maxParts, multiple } = this;
676
+ if (!multiple && Array.isArray(value)) {
677
+ this.value = value[0];
678
+ valueToProcess = valueToProcess[0];
679
+ }
680
+ /**
681
+ * Datetime should only warn of out of bounds values
682
+ * if set by the user. If the `value` is undefined,
683
+ * we will default to today's date which may be out
684
+ * of bounds. In this case, the warning makes it look
685
+ * like the developer did something wrong which is
686
+ * not true.
687
+ */
688
+ if (hasValue) {
689
+ warnIfValueOutOfBounds(valueToProcess, minParts, maxParts);
690
+ }
691
+ /**
692
+ * If there are multiple values, pick an arbitrary one to clamp to. This way,
693
+ * if the values are across months, we always show at least one of them. Note
694
+ * that the values don't necessarily have to be in order.
695
+ */
696
+ const singleValue = Array.isArray(valueToProcess) ? valueToProcess[0] : valueToProcess;
697
+ const { month, day, year, hour, minute, tzOffset } = clampDate(singleValue, minParts, maxParts);
698
+ const ampm = parseAmPm(hour);
699
+ this.setWorkingParts({
700
+ month,
701
+ day,
702
+ year,
703
+ hour,
704
+ minute,
705
+ tzOffset,
706
+ ampm,
707
+ });
708
+ /**
709
+ * Since `activeParts` indicates a value that
710
+ * been explicitly selected either by the
711
+ * user or the app, only update `activeParts`
712
+ * if the `value` property is set.
713
+ */
714
+ if (hasValue) {
715
+ if (Array.isArray(valueToProcess)) {
716
+ this.activeParts = [...valueToProcess];
717
+ }
718
+ else {
719
+ this.activeParts = {
720
+ month,
721
+ day,
722
+ year,
723
+ hour,
724
+ minute,
725
+ tzOffset,
726
+ ampm,
727
+ };
728
+ }
729
+ }
730
+ else {
731
+ /**
732
+ * Reset the active parts if the value is not set.
733
+ * This will clear the selected calendar day when
734
+ * performing a clear action or using the reset() method.
735
+ */
736
+ this.activeParts = [];
737
+ }
612
738
  };
613
739
  this.onFocus = () => {
614
740
  this.ionFocus.emit();
@@ -616,370 +742,936 @@ const Datetime = class {
616
742
  this.onBlur = () => {
617
743
  this.ionBlur.emit();
618
744
  };
745
+ this.hasValue = () => {
746
+ return this.value != null && this.value !== '';
747
+ };
748
+ this.nextMonth = () => {
749
+ const calendarBodyRef = this.calendarBodyRef;
750
+ if (!calendarBodyRef) {
751
+ return;
752
+ }
753
+ const nextMonth = calendarBodyRef.querySelector('.calendar-month:last-of-type');
754
+ if (!nextMonth) {
755
+ return;
756
+ }
757
+ const left = nextMonth.offsetWidth * 2;
758
+ calendarBodyRef.scrollTo({
759
+ top: 0,
760
+ left: left * (isRTL(this.el) ? -1 : 1),
761
+ behavior: 'smooth',
762
+ });
763
+ };
764
+ this.prevMonth = () => {
765
+ const calendarBodyRef = this.calendarBodyRef;
766
+ if (!calendarBodyRef) {
767
+ return;
768
+ }
769
+ const prevMonth = calendarBodyRef.querySelector('.calendar-month:first-of-type');
770
+ if (!prevMonth) {
771
+ return;
772
+ }
773
+ calendarBodyRef.scrollTo({
774
+ top: 0,
775
+ left: 0,
776
+ behavior: 'smooth',
777
+ });
778
+ };
779
+ this.toggleMonthAndYearView = () => {
780
+ this.showMonthAndYear = !this.showMonthAndYear;
781
+ };
619
782
  }
620
783
  disabledChanged() {
621
784
  this.emitStyle();
622
785
  }
786
+ minChanged() {
787
+ this.processMinParts();
788
+ }
789
+ maxChanged() {
790
+ this.processMaxParts();
791
+ }
792
+ yearValuesChanged() {
793
+ this.parsedYearValues = convertToArrayOfNumbers(this.yearValues);
794
+ }
795
+ monthValuesChanged() {
796
+ this.parsedMonthValues = convertToArrayOfNumbers(this.monthValues);
797
+ }
798
+ dayValuesChanged() {
799
+ this.parsedDayValues = convertToArrayOfNumbers(this.dayValues);
800
+ }
801
+ hourValuesChanged() {
802
+ this.parsedHourValues = convertToArrayOfNumbers(this.hourValues);
803
+ }
804
+ minuteValuesChanged() {
805
+ this.parsedMinuteValues = convertToArrayOfNumbers(this.minuteValues);
806
+ }
807
+ activePartsChanged() {
808
+ this.activePartsClone = this.activeParts;
809
+ }
623
810
  /**
624
811
  * Update the datetime value when the value changes
625
812
  */
626
813
  valueChanged() {
627
- this.updateDatetimeValue(this.value);
814
+ const { value, minParts, maxParts, workingParts, multiple } = this;
815
+ if (this.hasValue()) {
816
+ if (!multiple && Array.isArray(value)) {
817
+ this.value = value[0];
818
+ return; // setting this.value will trigger re-run of this function
819
+ }
820
+ /**
821
+ * Clones the value of the `activeParts` to the private clone, to update
822
+ * the date display on the current render cycle without causing another render.
823
+ *
824
+ * This allows us to update the current value's date/time display without
825
+ * refocusing or shifting the user's display (leaves the user in place).
826
+ */
827
+ const valueDateParts = parseDate(value);
828
+ if (valueDateParts) {
829
+ warnIfValueOutOfBounds(valueDateParts, minParts, maxParts);
830
+ if (Array.isArray(valueDateParts)) {
831
+ this.activePartsClone = [...valueDateParts];
832
+ }
833
+ else {
834
+ const { month, day, year, hour, minute } = valueDateParts;
835
+ const ampm = hour != null ? (hour >= 12 ? 'pm' : 'am') : undefined;
836
+ this.activePartsClone = Object.assign(Object.assign({}, this.activeParts), { month,
837
+ day,
838
+ year,
839
+ hour,
840
+ minute,
841
+ ampm });
842
+ /**
843
+ * The working parts am/pm value must be updated when the value changes, to
844
+ * ensure the time picker hour column values are generated correctly.
845
+ *
846
+ * Note that we don't need to do this if valueDateParts is an array, since
847
+ * multiple="true" does not apply to time pickers.
848
+ */
849
+ this.setWorkingParts(Object.assign(Object.assign({}, workingParts), { ampm }));
850
+ }
851
+ }
852
+ else {
853
+ printIonWarning(`Unable to parse date string: ${value}. Please provide a valid ISO 8601 datetime string.`);
854
+ }
855
+ }
628
856
  this.emitStyle();
629
- this.ionChange.emit({
630
- value: this.value
631
- });
857
+ this.ionChange.emit({ value });
632
858
  }
633
- componentWillLoad() {
634
- // first see if locale names were provided in the inputs
635
- // then check to see if they're in the config
636
- // if neither were provided then it will use default English names
637
- this.locale = {
638
- // this.locale[type] = convertToArrayOfStrings((this[type] ? this[type] : this.config.get(type), type);
639
- monthNames: convertToArrayOfStrings(this.monthNames, 'monthNames'),
640
- monthShortNames: convertToArrayOfStrings(this.monthShortNames, 'monthShortNames'),
641
- dayNames: convertToArrayOfStrings(this.dayNames, 'dayNames'),
642
- dayShortNames: convertToArrayOfStrings(this.dayShortNames, 'dayShortNames')
859
+ /**
860
+ * Confirms the selected datetime value, updates the
861
+ * `value` property, and optionally closes the popover
862
+ * or modal that the datetime was presented in.
863
+ */
864
+ async confirm(closeOverlay = false) {
865
+ const { isCalendarPicker, activeParts } = this;
866
+ /**
867
+ * We only update the value if the presentation is not a calendar picker.
868
+ */
869
+ if (activeParts !== undefined || !isCalendarPicker) {
870
+ const activePartsIsArray = Array.isArray(activeParts);
871
+ if (activePartsIsArray && activeParts.length === 0) {
872
+ this.value = undefined;
873
+ }
874
+ else {
875
+ /**
876
+ * Prevent convertDataToISO from doing any
877
+ * kind of transformation based on timezone
878
+ * This cancels out any change it attempts to make
879
+ *
880
+ * Important: Take the timezone offset based on
881
+ * the date that is currently selected, otherwise
882
+ * there can be 1 hr difference when dealing w/ DST
883
+ */
884
+ if (activePartsIsArray) {
885
+ const dates = convertDataToISO(activeParts).map((str) => new Date(str));
886
+ for (let i = 0; i < dates.length; i++) {
887
+ activeParts[i].tzOffset = dates[i].getTimezoneOffset() * -1;
888
+ }
889
+ }
890
+ else {
891
+ const date = new Date(convertDataToISO(activeParts));
892
+ activeParts.tzOffset = date.getTimezoneOffset() * -1;
893
+ }
894
+ this.value = convertDataToISO(activeParts);
895
+ }
896
+ }
897
+ if (closeOverlay) {
898
+ this.closeParentOverlay();
899
+ }
900
+ }
901
+ /**
902
+ * Resets the internal state of the datetime but does not update the value.
903
+ * Passing a valid ISO-8601 string will reset the state of the component to the provided date.
904
+ * If no value is provided, the internal state will be reset to the clamped value of the min, max and today.
905
+ */
906
+ async reset(startDate) {
907
+ this.processValue(startDate);
908
+ }
909
+ /**
910
+ * Emits the ionCancel event and
911
+ * optionally closes the popover
912
+ * or modal that the datetime was
913
+ * presented in.
914
+ */
915
+ async cancel(closeOverlay = false) {
916
+ this.ionCancel.emit();
917
+ if (closeOverlay) {
918
+ this.closeParentOverlay();
919
+ }
920
+ }
921
+ get isCalendarPicker() {
922
+ const { presentation } = this;
923
+ return presentation === 'date' || presentation === 'date-time' || presentation === 'time-date';
924
+ }
925
+ connectedCallback() {
926
+ this.clearFocusVisible = startFocusVisible(this.el).destroy;
927
+ }
928
+ disconnectedCallback() {
929
+ if (this.clearFocusVisible) {
930
+ this.clearFocusVisible();
931
+ this.clearFocusVisible = undefined;
932
+ }
933
+ }
934
+ initializeListeners() {
935
+ this.initializeCalendarListener();
936
+ this.initializeKeyboardListeners();
937
+ }
938
+ componentDidLoad() {
939
+ /**
940
+ * If a scrollable element is hidden using `display: none`,
941
+ * it will not have a scroll height meaning we cannot scroll elements
942
+ * into view. As a result, we will need to wait for the datetime to become
943
+ * visible if used inside of a modal or a popover otherwise the scrollable
944
+ * areas will not have the correct values snapped into place.
945
+ */
946
+ const visibleCallback = (entries) => {
947
+ const ev = entries[0];
948
+ if (!ev.isIntersecting) {
949
+ return;
950
+ }
951
+ this.initializeListeners();
952
+ /**
953
+ * TODO FW-2793: Datetime needs a frame to ensure that it
954
+ * can properly scroll contents into view. As a result
955
+ * we hide the scrollable content until after that frame
956
+ * so users do not see the content quickly shifting. The downside
957
+ * is that the content will pop into view a frame after. Maybe there
958
+ * is a better way to handle this?
959
+ */
960
+ writeTask(() => {
961
+ this.el.classList.add('datetime-ready');
962
+ });
643
963
  };
644
- this.updateDatetimeValue(this.value);
645
- this.emitStyle();
964
+ const visibleIO = new IntersectionObserver(visibleCallback, { threshold: 0.01 });
965
+ /**
966
+ * Use raf to avoid a race condition between the component loading and
967
+ * its display animation starting (such as when shown in a modal). This
968
+ * could cause the datetime to start at a visibility of 0, erroneously
969
+ * triggering the `hiddenIO` observer below.
970
+ */
971
+ raf(() => visibleIO === null || visibleIO === void 0 ? void 0 : visibleIO.observe(this.el));
972
+ /**
973
+ * We need to clean up listeners when the datetime is hidden
974
+ * in a popover/modal so that we can properly scroll containers
975
+ * back into view if they are re-presented. When the datetime is hidden
976
+ * the scroll areas have scroll widths/heights of 0px, so any snapping
977
+ * we did originally has been lost.
978
+ */
979
+ const hiddenCallback = (entries) => {
980
+ const ev = entries[0];
981
+ if (ev.isIntersecting) {
982
+ return;
983
+ }
984
+ this.destroyInteractionListeners();
985
+ /**
986
+ * When datetime is hidden, we need to make sure that
987
+ * the month/year picker is closed. Otherwise,
988
+ * it will be open when the datetime re-appears
989
+ * and the scroll area of the calendar grid will be 0.
990
+ * As a result, the wrong month will be shown.
991
+ */
992
+ this.showMonthAndYear = false;
993
+ writeTask(() => {
994
+ this.el.classList.remove('datetime-ready');
995
+ });
996
+ };
997
+ const hiddenIO = new IntersectionObserver(hiddenCallback, { threshold: 0 });
998
+ raf(() => hiddenIO === null || hiddenIO === void 0 ? void 0 : hiddenIO.observe(this.el));
999
+ /**
1000
+ * Datetime uses Ionic components that emit
1001
+ * ionFocus and ionBlur. These events are
1002
+ * composed meaning they will cross
1003
+ * the shadow dom boundary. We need to
1004
+ * stop propagation on these events otherwise
1005
+ * developers will see 2 ionFocus or 2 ionBlur
1006
+ * events at a time.
1007
+ */
1008
+ const root = getElementRoot(this.el);
1009
+ root.addEventListener('ionFocus', (ev) => ev.stopPropagation());
1010
+ root.addEventListener('ionBlur', (ev) => ev.stopPropagation());
646
1011
  }
647
1012
  /**
648
- * Opens the datetime overlay.
1013
+ * When the presentation is changed, all calendar content is recreated,
1014
+ * so we need to re-init behavior with the new elements.
649
1015
  */
650
- async open() {
651
- if (this.disabled || this.isExpanded) {
1016
+ componentDidRender() {
1017
+ const { presentation, prevPresentation, calendarBodyRef, minParts, preferWheel } = this;
1018
+ /**
1019
+ * TODO(FW-2165)
1020
+ * Remove this when https://bugs.webkit.org/show_bug.cgi?id=235960 is fixed.
1021
+ * When using `min`, we add `scroll-snap-align: none`
1022
+ * to the disabled month so that users cannot scroll to it.
1023
+ * This triggers a bug in WebKit where the scroll position is reset.
1024
+ * Since the month change logic is handled by a scroll listener,
1025
+ * this causes the month to change leading to `scroll-snap-align`
1026
+ * changing again, thus changing the scroll position again and causing
1027
+ * an infinite loop.
1028
+ * This issue only applies to the calendar grid, so we can disable
1029
+ * it if the calendar grid is not being used.
1030
+ */
1031
+ const hasCalendarGrid = !preferWheel && ['date-time', 'time-date', 'date'].includes(presentation);
1032
+ if (minParts !== undefined && hasCalendarGrid && calendarBodyRef) {
1033
+ const workingMonth = calendarBodyRef.querySelector('.calendar-month:nth-of-type(1)');
1034
+ if (workingMonth) {
1035
+ calendarBodyRef.scrollLeft = workingMonth.clientWidth * (isRTL(this.el) ? -1 : 1);
1036
+ }
1037
+ }
1038
+ if (prevPresentation === null) {
1039
+ this.prevPresentation = presentation;
652
1040
  return;
653
1041
  }
654
- const pickerOptions = this.generatePickerOptions();
655
- const picker = await pickerController.create(pickerOptions);
656
- this.isExpanded = true;
657
- picker.onDidDismiss().then(() => {
658
- this.isExpanded = false;
659
- this.setFocus();
1042
+ if (presentation === prevPresentation) {
1043
+ return;
1044
+ }
1045
+ this.prevPresentation = presentation;
1046
+ this.destroyInteractionListeners();
1047
+ this.initializeListeners();
1048
+ /**
1049
+ * The month/year picker from the date interface
1050
+ * should be closed as it is not available in non-date
1051
+ * interfaces.
1052
+ */
1053
+ this.showMonthAndYear = false;
1054
+ raf(() => {
1055
+ this.ionRender.emit();
660
1056
  });
661
- addEventListener(picker, 'ionPickerColChange', async (event) => {
662
- const data = event.detail;
663
- const colSelectedIndex = data.selectedIndex;
664
- const colOptions = data.options;
665
- const changeData = {};
666
- changeData[data.name] = {
667
- value: colOptions[colSelectedIndex].value
668
- };
669
- if (data.name !== 'ampm' && this.datetimeValue.ampm !== undefined) {
670
- changeData['ampm'] = {
671
- value: this.datetimeValue.ampm
672
- };
1057
+ }
1058
+ componentWillLoad() {
1059
+ const { el, multiple, presentation, preferWheel } = this;
1060
+ if (multiple) {
1061
+ if (presentation !== 'date') {
1062
+ printIonWarning('Multiple date selection is only supported for presentation="date".', el);
673
1063
  }
674
- this.updateDatetimeValue(changeData);
675
- picker.columns = this.generateColumns();
676
- });
677
- await picker.present();
1064
+ if (preferWheel) {
1065
+ printIonWarning('Multiple date selection is not supported with preferWheel="true".', el);
1066
+ }
1067
+ }
1068
+ this.processMinParts();
1069
+ this.processMaxParts();
1070
+ const hourValues = (this.parsedHourValues = convertToArrayOfNumbers(this.hourValues));
1071
+ const minuteValues = (this.parsedMinuteValues = convertToArrayOfNumbers(this.minuteValues));
1072
+ const monthValues = (this.parsedMonthValues = convertToArrayOfNumbers(this.monthValues));
1073
+ const yearValues = (this.parsedYearValues = convertToArrayOfNumbers(this.yearValues));
1074
+ const dayValues = (this.parsedDayValues = convertToArrayOfNumbers(this.dayValues));
1075
+ const todayParts = (this.todayParts = parseDate(getToday()));
1076
+ this.defaultParts = getClosestValidDate(todayParts, monthValues, dayValues, yearValues, hourValues, minuteValues);
1077
+ this.processValue(this.value);
1078
+ this.emitStyle();
678
1079
  }
679
1080
  emitStyle() {
680
1081
  this.ionStyle.emit({
681
- 'interactive': true,
682
- 'datetime': true,
683
- 'has-placeholder': this.placeholder != null,
684
- 'has-value': this.hasValue(),
1082
+ interactive: true,
1083
+ datetime: true,
685
1084
  'interactive-disabled': this.disabled,
686
1085
  });
687
1086
  }
688
- updateDatetimeValue(value) {
689
- updateDate(this.datetimeValue, value, this.displayTimezone);
690
- }
691
- generatePickerOptions() {
692
- const mode = getIonMode(this);
693
- this.locale = {
694
- monthNames: convertToArrayOfStrings(this.monthNames, 'monthNames'),
695
- monthShortNames: convertToArrayOfStrings(this.monthShortNames, 'monthShortNames'),
696
- dayNames: convertToArrayOfStrings(this.dayNames, 'dayNames'),
697
- dayShortNames: convertToArrayOfStrings(this.dayShortNames, 'dayShortNames')
1087
+ /**
1088
+ * Universal render methods
1089
+ * These are pieces of datetime that
1090
+ * are rendered independently of presentation.
1091
+ */
1092
+ renderFooter() {
1093
+ const { showDefaultButtons, showClearButton } = this;
1094
+ const hasSlottedButtons = this.el.querySelector('[slot="buttons"]') !== null;
1095
+ if (!hasSlottedButtons && !showDefaultButtons && !showClearButton) {
1096
+ return;
1097
+ }
1098
+ const clearButtonClick = () => {
1099
+ this.reset();
1100
+ this.value = undefined;
698
1101
  };
699
- const pickerOptions = Object.assign(Object.assign({ mode }, this.pickerOptions), { columns: this.generateColumns() });
700
- // If the user has not passed in picker buttons,
701
- // add a cancel and ok button to the picker
702
- const buttons = pickerOptions.buttons;
703
- if (!buttons || buttons.length === 0) {
704
- pickerOptions.buttons = [
705
- {
706
- text: this.cancelText,
707
- role: 'cancel',
708
- handler: () => {
709
- this.updateDatetimeValue(this.value);
710
- this.ionCancel.emit();
711
- }
712
- },
713
- {
714
- text: this.doneText,
715
- handler: (data) => {
716
- this.updateDatetimeValue(data);
717
- /**
718
- * Prevent convertDataToISO from doing any
719
- * kind of transformation based on timezone
720
- * This cancels out any change it attempts to make
721
- *
722
- * Important: Take the timezone offset based on
723
- * the date that is currently selected, otherwise
724
- * there can be 1 hr difference when dealing w/ DST
725
- */
726
- const date = new Date(convertDataToISO(this.datetimeValue));
727
- // If a custom display timezone is provided, use that tzOffset value instead
728
- this.datetimeValue.tzOffset = (this.displayTimezone !== undefined && this.displayTimezone.length > 0)
729
- ? ((getTimezoneOffset(date, this.displayTimezone)) / 1000 / 60) * -1
730
- : date.getTimezoneOffset() * -1;
731
- this.value = convertDataToISO(this.datetimeValue);
732
- }
1102
+ /**
1103
+ * By default we render two buttons:
1104
+ * Cancel - Dismisses the datetime and
1105
+ * does not update the `value` prop.
1106
+ * OK - Dismisses the datetime and
1107
+ * updates the `value` prop.
1108
+ */
1109
+ return (h("div", { class: "datetime-footer" }, h("div", { class: "datetime-buttons" }, h("div", { class: {
1110
+ ['datetime-action-buttons']: true,
1111
+ ['has-clear-button']: this.showClearButton,
1112
+ } }, h("slot", { name: "buttons" }, h("ion-buttons", null, showDefaultButtons && (h("ion-button", { id: "cancel-button", color: this.color, onClick: () => this.cancel(true) }, this.cancelText)), h("div", null, showClearButton && (h("ion-button", { id: "clear-button", color: this.color, onClick: () => clearButtonClick() }, this.clearText)), showDefaultButtons && (h("ion-button", { id: "confirm-button", color: this.color, onClick: () => this.confirm(true) }, this.doneText)))))))));
1113
+ }
1114
+ /**
1115
+ * Wheel picker render methods
1116
+ */
1117
+ renderWheelPicker(forcePresentation = this.presentation) {
1118
+ /**
1119
+ * If presentation="time-date" we switch the
1120
+ * order of the render array here instead of
1121
+ * manually reordering each date/time picker
1122
+ * column with CSS. This allows for additional
1123
+ * flexibility if we need to render subsets
1124
+ * of the date/time data or do additional ordering
1125
+ * within the child render functions.
1126
+ */
1127
+ const renderArray = forcePresentation === 'time-date'
1128
+ ? [this.renderTimePickerColumns(forcePresentation), this.renderDatePickerColumns(forcePresentation)]
1129
+ : [this.renderDatePickerColumns(forcePresentation), this.renderTimePickerColumns(forcePresentation)];
1130
+ return h("ion-picker-internal", null, renderArray);
1131
+ }
1132
+ renderDatePickerColumns(forcePresentation) {
1133
+ return forcePresentation === 'date-time' || forcePresentation === 'time-date'
1134
+ ? this.renderCombinedDatePickerColumn()
1135
+ : this.renderIndividualDatePickerColumns(forcePresentation);
1136
+ }
1137
+ renderCombinedDatePickerColumn() {
1138
+ const { defaultParts, workingParts, locale, minParts, maxParts, todayParts, isDateEnabled } = this;
1139
+ const activePart = this.getActivePartsWithFallback();
1140
+ /**
1141
+ * By default, generate a range of 3 months:
1142
+ * Previous month, current month, and next month
1143
+ */
1144
+ const monthsToRender = generateMonths(workingParts);
1145
+ const lastMonth = monthsToRender[monthsToRender.length - 1];
1146
+ /**
1147
+ * Ensure that users can select the entire window of dates.
1148
+ */
1149
+ monthsToRender[0].day = 1;
1150
+ lastMonth.day = getNumDaysInMonth(lastMonth.month, lastMonth.year);
1151
+ /**
1152
+ * Narrow the dates rendered based on min/max dates (if any).
1153
+ * The `min` date is used if the min is after the generated min month.
1154
+ * The `max` date is used if the max is before the generated max month.
1155
+ * This ensures that the sliding window always stays at 3 months
1156
+ * but still allows future dates to be lazily rendered based on any min/max
1157
+ * constraints.
1158
+ */
1159
+ const min = minParts !== undefined && isAfter(minParts, monthsToRender[0]) ? minParts : monthsToRender[0];
1160
+ const max = maxParts !== undefined && isBefore(maxParts, lastMonth) ? maxParts : lastMonth;
1161
+ const result = getCombinedDateColumnData(locale, todayParts, min, max, this.parsedDayValues, this.parsedMonthValues);
1162
+ let items = result.items;
1163
+ const parts = result.parts;
1164
+ if (isDateEnabled) {
1165
+ items = items.map((itemObject, index) => {
1166
+ const referenceParts = parts[index];
1167
+ let disabled;
1168
+ try {
1169
+ /**
1170
+ * The `isDateEnabled` implementation is try-catch wrapped
1171
+ * to prevent exceptions in the user's function from
1172
+ * interrupting the calendar rendering.
1173
+ */
1174
+ disabled = !isDateEnabled(convertDataToISO(referenceParts));
1175
+ }
1176
+ catch (e) {
1177
+ printIonError('Exception thrown from provided `isDateEnabled` function. Please check your function and try again.', e);
733
1178
  }
1179
+ return Object.assign(Object.assign({}, itemObject), { disabled });
1180
+ });
1181
+ }
1182
+ /**
1183
+ * If we have selected a day already, then default the column
1184
+ * to that value. Otherwise, set it to the default date.
1185
+ */
1186
+ const todayString = workingParts.day !== null
1187
+ ? `${workingParts.year}-${workingParts.month}-${workingParts.day}`
1188
+ : `${defaultParts.year}-${defaultParts.month}-${defaultParts.day}`;
1189
+ return (h("ion-picker-column-internal", { class: "date-column", color: this.color, items: items, value: todayString, onIonChange: (ev) => {
1190
+ // TODO(FW-1823) Remove this when iOS 14 support is dropped.
1191
+ // Due to a Safari 14 issue we need to destroy
1192
+ // the scroll listener before we update state
1193
+ // and trigger a re-render.
1194
+ if (this.destroyCalendarListener) {
1195
+ this.destroyCalendarListener();
1196
+ }
1197
+ const { value } = ev.detail;
1198
+ const findPart = parts.find(({ month, day, year }) => value === `${year}-${month}-${day}`);
1199
+ this.setWorkingParts(Object.assign(Object.assign({}, workingParts), findPart));
1200
+ this.setActiveParts(Object.assign(Object.assign({}, activePart), findPart));
1201
+ // We can re-attach the scroll listener after
1202
+ // the working parts have been updated.
1203
+ this.initializeCalendarListener();
1204
+ ev.stopPropagation();
1205
+ } }));
1206
+ }
1207
+ renderIndividualDatePickerColumns(forcePresentation) {
1208
+ const { workingParts, isDateEnabled } = this;
1209
+ const shouldRenderMonths = forcePresentation !== 'year' && forcePresentation !== 'time';
1210
+ const months = shouldRenderMonths
1211
+ ? getMonthColumnData(this.locale, workingParts, this.minParts, this.maxParts, this.parsedMonthValues)
1212
+ : [];
1213
+ const shouldRenderDays = forcePresentation === 'date';
1214
+ let days = shouldRenderDays
1215
+ ? getDayColumnData(this.locale, workingParts, this.minParts, this.maxParts, this.parsedDayValues)
1216
+ : [];
1217
+ if (isDateEnabled) {
1218
+ days = days.map((dayObject) => {
1219
+ const { value } = dayObject;
1220
+ const valueNum = typeof value === 'string' ? parseInt(value) : value;
1221
+ const referenceParts = {
1222
+ month: workingParts.month,
1223
+ day: valueNum,
1224
+ year: workingParts.year,
1225
+ };
1226
+ let disabled;
1227
+ try {
1228
+ /**
1229
+ * The `isDateEnabled` implementation is try-catch wrapped
1230
+ * to prevent exceptions in the user's function from
1231
+ * interrupting the calendar rendering.
1232
+ */
1233
+ disabled = !isDateEnabled(convertDataToISO(referenceParts));
1234
+ }
1235
+ catch (e) {
1236
+ printIonError('Exception thrown from provided `isDateEnabled` function. Please check your function and try again.', e);
1237
+ }
1238
+ return Object.assign(Object.assign({}, dayObject), { disabled });
1239
+ });
1240
+ }
1241
+ const shouldRenderYears = forcePresentation !== 'month' && forcePresentation !== 'time';
1242
+ const years = shouldRenderYears
1243
+ ? getYearColumnData(this.locale, this.defaultParts, this.minParts, this.maxParts, this.parsedYearValues)
1244
+ : [];
1245
+ /**
1246
+ * Certain locales show the day before the month.
1247
+ */
1248
+ const showMonthFirst = isMonthFirstLocale(this.locale, { month: 'numeric', day: 'numeric' });
1249
+ let renderArray = [];
1250
+ if (showMonthFirst) {
1251
+ renderArray = [
1252
+ this.renderMonthPickerColumn(months),
1253
+ this.renderDayPickerColumn(days),
1254
+ this.renderYearPickerColumn(years),
734
1255
  ];
735
1256
  }
736
- return pickerOptions;
1257
+ else {
1258
+ renderArray = [
1259
+ this.renderDayPickerColumn(days),
1260
+ this.renderMonthPickerColumn(months),
1261
+ this.renderYearPickerColumn(years),
1262
+ ];
1263
+ }
1264
+ return renderArray;
737
1265
  }
738
- generateColumns() {
739
- // if a picker format wasn't provided, then fallback
740
- // to use the display format
741
- let template = this.pickerFormat || this.displayFormat || DEFAULT_FORMAT;
742
- if (template.length === 0) {
1266
+ renderDayPickerColumn(days) {
1267
+ var _a;
1268
+ if (days.length === 0) {
743
1269
  return [];
744
1270
  }
745
- // make sure we've got up to date sizing information
746
- this.calcMinMax();
747
- // does not support selecting by day name
748
- // automatically remove any day name formats
749
- template = template.replace('DDDD', '{~}').replace('DDD', '{~}');
750
- if (template.indexOf('D') === -1) {
751
- // there is not a day in the template
752
- // replace the day name with a numeric one if it exists
753
- template = template.replace('{~}', 'D');
754
- }
755
- // make sure no day name replacer is left in the string
756
- template = template.replace(/{~}/g, '');
757
- // parse apart the given template into an array of "formats"
758
- const columns = parseTemplate(template).map((format) => {
759
- // loop through each format in the template
760
- // create a new picker column to build up with data
761
- const key = convertFormatToKey(format);
762
- let values;
763
- // check if they have exact values to use for this date part
764
- // otherwise use the default date part values
765
- const self = this;
766
- values = self[key + 'Values']
767
- ? convertToArrayOfNumbers(self[key + 'Values'], key)
768
- : dateValueRange(format, this.datetimeMin, this.datetimeMax);
769
- const colOptions = values.map(val => {
770
- return {
771
- value: val,
772
- text: renderTextFormat(format, val, undefined, this.locale),
773
- };
774
- });
775
- // cool, we've loaded up the columns with options
776
- // preselect the option for this column
777
- const optValue = getDateValue(this.datetimeValue, format);
778
- const selectedIndex = colOptions.findIndex(opt => opt.value === optValue);
779
- return {
780
- name: key,
781
- selectedIndex: selectedIndex >= 0 ? selectedIndex : 0,
782
- options: colOptions
783
- };
784
- });
785
- // Normalize min/max
786
- const min = this.datetimeMin;
787
- const max = this.datetimeMax;
788
- ['month', 'day', 'hour', 'minute']
789
- .filter(name => !columns.find(column => column.name === name))
790
- .forEach(name => {
791
- min[name] = 0;
792
- max[name] = 0;
793
- });
794
- return this.validateColumns(divyColumns(columns));
795
- }
796
- validateColumns(columns) {
797
- const today = new Date();
798
- const minCompareVal = dateDataSortValue(this.datetimeMin);
799
- const maxCompareVal = dateDataSortValue(this.datetimeMax);
800
- const yearCol = columns.find(c => c.name === 'year');
801
- let selectedYear = today.getFullYear();
802
- if (yearCol) {
803
- // default to the first value if the current year doesn't exist in the options
804
- if (!yearCol.options.find(col => col.value === today.getFullYear())) {
805
- selectedYear = yearCol.options[0].value;
806
- }
807
- const selectedIndex = yearCol.selectedIndex;
808
- if (selectedIndex !== undefined) {
809
- const yearOpt = yearCol.options[selectedIndex];
810
- if (yearOpt) {
811
- // they have a selected year value
812
- selectedYear = yearOpt.value;
1271
+ const { workingParts } = this;
1272
+ const activePart = this.getActivePartsWithFallback();
1273
+ return (h("ion-picker-column-internal", { class: "day-column", color: this.color, items: days, value: (_a = (workingParts.day !== null ? workingParts.day : this.defaultParts.day)) !== null && _a !== void 0 ? _a : undefined, onIonChange: (ev) => {
1274
+ // TODO(FW-1823) Remove this when iOS 14 support is dropped.
1275
+ // Due to a Safari 14 issue we need to destroy
1276
+ // the scroll listener before we update state
1277
+ // and trigger a re-render.
1278
+ if (this.destroyCalendarListener) {
1279
+ this.destroyCalendarListener();
813
1280
  }
814
- }
1281
+ this.setWorkingParts(Object.assign(Object.assign({}, workingParts), { day: ev.detail.value }));
1282
+ this.setActiveParts(Object.assign(Object.assign({}, activePart), { day: ev.detail.value }));
1283
+ // We can re-attach the scroll listener after
1284
+ // the working parts have been updated.
1285
+ this.initializeCalendarListener();
1286
+ ev.stopPropagation();
1287
+ } }));
1288
+ }
1289
+ renderMonthPickerColumn(months) {
1290
+ if (months.length === 0) {
1291
+ return [];
815
1292
  }
816
- const selectedMonth = this.validateColumn(columns, 'month', 1, minCompareVal, maxCompareVal, [selectedYear, 0, 0, 0, 0], [selectedYear, 12, 31, 23, 59]);
817
- const numDaysInMonth = daysInMonth(selectedMonth, selectedYear);
818
- const selectedDay = this.validateColumn(columns, 'day', 2, minCompareVal, maxCompareVal, [selectedYear, selectedMonth, 0, 0, 0], [selectedYear, selectedMonth, numDaysInMonth, 23, 59]);
819
- const selectedHour = this.validateColumn(columns, 'hour', 3, minCompareVal, maxCompareVal, [selectedYear, selectedMonth, selectedDay, 0, 0], [selectedYear, selectedMonth, selectedDay, 23, 59]);
820
- this.validateColumn(columns, 'minute', 4, minCompareVal, maxCompareVal, [selectedYear, selectedMonth, selectedDay, selectedHour, 0], [selectedYear, selectedMonth, selectedDay, selectedHour, 59]);
821
- return columns;
822
- }
823
- calcMinMax() {
824
- const todaysYear = new Date().getFullYear();
825
- if (this.yearValues !== undefined) {
826
- const years = convertToArrayOfNumbers(this.yearValues, 'year');
827
- if (this.min === undefined) {
828
- this.min = Math.min(...years).toString();
829
- }
830
- if (this.max === undefined) {
831
- this.max = Math.max(...years).toString();
832
- }
1293
+ const { workingParts } = this;
1294
+ const activePart = this.getActivePartsWithFallback();
1295
+ return (h("ion-picker-column-internal", { class: "month-column", color: this.color, items: months, value: workingParts.month, onIonChange: (ev) => {
1296
+ // TODO(FW-1823) Remove this when iOS 14 support is dropped.
1297
+ // Due to a Safari 14 issue we need to destroy
1298
+ // the scroll listener before we update state
1299
+ // and trigger a re-render.
1300
+ if (this.destroyCalendarListener) {
1301
+ this.destroyCalendarListener();
1302
+ }
1303
+ this.setWorkingParts(Object.assign(Object.assign({}, workingParts), { month: ev.detail.value }));
1304
+ this.setActiveParts(Object.assign(Object.assign({}, activePart), { month: ev.detail.value }));
1305
+ // We can re-attach the scroll listener after
1306
+ // the working parts have been updated.
1307
+ this.initializeCalendarListener();
1308
+ ev.stopPropagation();
1309
+ } }));
1310
+ }
1311
+ renderYearPickerColumn(years) {
1312
+ if (years.length === 0) {
1313
+ return [];
833
1314
  }
834
- else {
835
- if (this.min === undefined) {
836
- this.min = (todaysYear - 100).toString();
837
- }
838
- if (this.max === undefined) {
839
- this.max = todaysYear.toString();
840
- }
1315
+ const { workingParts } = this;
1316
+ const activePart = this.getActivePartsWithFallback();
1317
+ return (h("ion-picker-column-internal", { class: "year-column", color: this.color, items: years, value: workingParts.year, onIonChange: (ev) => {
1318
+ // TODO(FW-1823) Remove this when iOS 14 support is dropped.
1319
+ // Due to a Safari 14 issue we need to destroy
1320
+ // the scroll listener before we update state
1321
+ // and trigger a re-render.
1322
+ if (this.destroyCalendarListener) {
1323
+ this.destroyCalendarListener();
1324
+ }
1325
+ this.setWorkingParts(Object.assign(Object.assign({}, workingParts), { year: ev.detail.value }));
1326
+ this.setActiveParts(Object.assign(Object.assign({}, activePart), { year: ev.detail.value }));
1327
+ // We can re-attach the scroll listener after
1328
+ // the working parts have been updated.
1329
+ this.initializeCalendarListener();
1330
+ ev.stopPropagation();
1331
+ } }));
1332
+ }
1333
+ renderTimePickerColumns(forcePresentation) {
1334
+ if (['date', 'month', 'month-year', 'year'].includes(forcePresentation)) {
1335
+ return [];
841
1336
  }
842
- const min = this.datetimeMin = parseDate(this.min);
843
- const max = this.datetimeMax = parseDate(this.max);
844
- min.year = min.year || todaysYear;
845
- max.year = max.year || todaysYear;
846
- min.month = min.month || 1;
847
- max.month = max.month || 12;
848
- min.day = min.day || 1;
849
- max.day = max.day || 31;
850
- min.hour = min.hour || 0;
851
- max.hour = max.hour === undefined ? 23 : max.hour;
852
- min.minute = min.minute || 0;
853
- max.minute = max.minute === undefined ? 59 : max.minute;
854
- min.second = min.second || 0;
855
- max.second = max.second === undefined ? 59 : max.second;
856
- // Ensure min/max constraints
857
- if (min.year > max.year) {
858
- console.error('min.year > max.year');
859
- min.year = max.year - 100;
1337
+ /**
1338
+ * If a user has not selected a date,
1339
+ * then we should show all times. If the
1340
+ * user has selected a date (even if it has
1341
+ * not been confirmed yet), we should apply
1342
+ * the max and min restrictions so that the
1343
+ * time picker shows values that are
1344
+ * appropriate for the selected date.
1345
+ */
1346
+ const activePart = this.getActivePart();
1347
+ const userHasSelectedDate = activePart !== undefined;
1348
+ const { hoursData, minutesData, dayPeriodData } = getTimeColumnsData(this.locale, this.workingParts, this.hourCycle, userHasSelectedDate ? this.minParts : undefined, userHasSelectedDate ? this.maxParts : undefined, this.parsedHourValues, this.parsedMinuteValues);
1349
+ return [
1350
+ this.renderHourPickerColumn(hoursData),
1351
+ this.renderMinutePickerColumn(minutesData),
1352
+ this.renderDayPeriodPickerColumn(dayPeriodData),
1353
+ ];
1354
+ }
1355
+ renderHourPickerColumn(hoursData) {
1356
+ const { workingParts } = this;
1357
+ if (hoursData.length === 0)
1358
+ return [];
1359
+ const activePart = this.getActivePartsWithFallback();
1360
+ return (h("ion-picker-column-internal", { color: this.color, value: activePart.hour, items: hoursData, numericInput: true, onIonChange: (ev) => {
1361
+ this.setWorkingParts(Object.assign(Object.assign({}, workingParts), { hour: ev.detail.value }));
1362
+ this.setActiveParts(Object.assign(Object.assign({}, activePart), { hour: ev.detail.value }));
1363
+ ev.stopPropagation();
1364
+ } }));
1365
+ }
1366
+ renderMinutePickerColumn(minutesData) {
1367
+ const { workingParts } = this;
1368
+ if (minutesData.length === 0)
1369
+ return [];
1370
+ const activePart = this.getActivePartsWithFallback();
1371
+ return (h("ion-picker-column-internal", { color: this.color, value: activePart.minute, items: minutesData, numericInput: true, onIonChange: (ev) => {
1372
+ this.setWorkingParts(Object.assign(Object.assign({}, workingParts), { minute: ev.detail.value }));
1373
+ this.setActiveParts(Object.assign(Object.assign({}, activePart), { minute: ev.detail.value }));
1374
+ ev.stopPropagation();
1375
+ } }));
1376
+ }
1377
+ renderDayPeriodPickerColumn(dayPeriodData) {
1378
+ const { workingParts } = this;
1379
+ if (dayPeriodData.length === 0) {
1380
+ return [];
860
1381
  }
861
- if (min.year === max.year) {
862
- if (min.month > max.month) {
863
- console.error('min.month > max.month');
864
- min.month = 1;
865
- }
866
- else if (min.month === max.month && min.day > max.day) {
867
- console.error('min.day > max.day');
868
- min.day = 1;
1382
+ const activePart = this.getActivePartsWithFallback();
1383
+ const isDayPeriodRTL = isLocaleDayPeriodRTL(this.locale);
1384
+ return (h("ion-picker-column-internal", { style: isDayPeriodRTL ? { order: '-1' } : {}, color: this.color, value: activePart.ampm, items: dayPeriodData, onIonChange: (ev) => {
1385
+ const hour = calculateHourFromAMPM(workingParts, ev.detail.value);
1386
+ this.setWorkingParts(Object.assign(Object.assign({}, workingParts), { ampm: ev.detail.value, hour }));
1387
+ this.setActiveParts(Object.assign(Object.assign({}, activePart), { ampm: ev.detail.value, hour }));
1388
+ ev.stopPropagation();
1389
+ } }));
1390
+ }
1391
+ renderWheelView(forcePresentation) {
1392
+ const { locale } = this;
1393
+ const showMonthFirst = isMonthFirstLocale(locale);
1394
+ const columnOrder = showMonthFirst ? 'month-first' : 'year-first';
1395
+ return (h("div", { class: {
1396
+ [`wheel-order-${columnOrder}`]: true,
1397
+ } }, this.renderWheelPicker(forcePresentation)));
1398
+ }
1399
+ /**
1400
+ * Grid Render Methods
1401
+ */
1402
+ renderCalendarHeader(mode) {
1403
+ const expandedIcon = mode === 'ios' ? chevronDown : caretUpSharp;
1404
+ const collapsedIcon = mode === 'ios' ? chevronForward : caretDownSharp;
1405
+ const prevMonthDisabled = isPrevMonthDisabled(this.workingParts, this.minParts, this.maxParts);
1406
+ const nextMonthDisabled = isNextMonthDisabled(this.workingParts, this.maxParts);
1407
+ // don't use the inheritAttributes util because it removes dir from the host, and we still need that
1408
+ const hostDir = this.el.getAttribute('dir') || undefined;
1409
+ return (h("div", { class: "calendar-header" }, h("div", { class: "calendar-action-buttons" }, h("div", { class: "calendar-month-year" }, h("ion-item", { button: true, detail: false, lines: "none", onClick: () => this.toggleMonthAndYearView() }, h("ion-label", null, getMonthAndYear(this.locale, this.workingParts), ' ', h("ion-icon", { "aria-hidden": "true", icon: this.showMonthAndYear ? expandedIcon : collapsedIcon, lazy: false })))), h("div", { class: "calendar-next-prev" }, h("ion-buttons", null, h("ion-button", { "aria-label": "previous month", disabled: prevMonthDisabled, onClick: () => this.prevMonth() }, h("ion-icon", { dir: hostDir, "aria-hidden": "true", slot: "icon-only", icon: chevronBack, lazy: false, flipRtl: true })), h("ion-button", { "aria-label": "next month", disabled: nextMonthDisabled, onClick: () => this.nextMonth() }, h("ion-icon", { dir: hostDir, "aria-hidden": "true", slot: "icon-only", icon: chevronForward, lazy: false, flipRtl: true }))))), h("div", { class: "calendar-days-of-week" }, getDaysOfWeek(this.locale, mode, this.firstDayOfWeek % 7).map((d) => {
1410
+ return h("div", { class: "day-of-week" }, d);
1411
+ }))));
1412
+ }
1413
+ renderMonth(month, year) {
1414
+ const yearAllowed = this.parsedYearValues === undefined || this.parsedYearValues.includes(year);
1415
+ const monthAllowed = this.parsedMonthValues === undefined || this.parsedMonthValues.includes(month);
1416
+ const isCalMonthDisabled = !yearAllowed || !monthAllowed;
1417
+ const swipeDisabled = isMonthDisabled({
1418
+ month,
1419
+ year,
1420
+ day: null,
1421
+ }, {
1422
+ // The day is not used when checking if a month is disabled.
1423
+ // Users should be able to access the min or max month, even if the
1424
+ // min/max date is out of bounds (e.g. min is set to Feb 15, Feb should not be disabled).
1425
+ minParts: Object.assign(Object.assign({}, this.minParts), { day: null }),
1426
+ maxParts: Object.assign(Object.assign({}, this.maxParts), { day: null }),
1427
+ });
1428
+ // The working month should never have swipe disabled.
1429
+ // Otherwise the CSS scroll snap will not work and the user
1430
+ // can free-scroll the calendar.
1431
+ const isWorkingMonth = this.workingParts.month === month && this.workingParts.year === year;
1432
+ const activePart = this.getActivePartsWithFallback();
1433
+ return (h("div", { "aria-hidden": !isWorkingMonth ? 'true' : null, class: {
1434
+ 'calendar-month': true,
1435
+ // Prevents scroll snap swipe gestures for months outside of the min/max bounds
1436
+ 'calendar-month-disabled': !isWorkingMonth && swipeDisabled,
1437
+ } }, h("div", { class: "calendar-month-grid" }, getDaysOfMonth(month, year, this.firstDayOfWeek % 7).map((dateObject, index) => {
1438
+ const { day, dayOfWeek } = dateObject;
1439
+ const { isDateEnabled, multiple } = this;
1440
+ const referenceParts = { month, day, year };
1441
+ const { isActive, isToday, ariaLabel, ariaSelected, disabled, text } = getCalendarDayState(this.locale, referenceParts, this.activePartsClone, this.todayParts, this.minParts, this.maxParts, this.parsedDayValues);
1442
+ let isCalDayDisabled = isCalMonthDisabled || disabled;
1443
+ if (!isCalDayDisabled && isDateEnabled !== undefined) {
1444
+ try {
1445
+ /**
1446
+ * The `isDateEnabled` implementation is try-catch wrapped
1447
+ * to prevent exceptions in the user's function from
1448
+ * interrupting the calendar rendering.
1449
+ */
1450
+ isCalDayDisabled = !isDateEnabled(convertDataToISO(referenceParts));
1451
+ }
1452
+ catch (e) {
1453
+ printIonError('Exception thrown from provided `isDateEnabled` function. Please check your function and try again.', e);
1454
+ }
869
1455
  }
870
- }
1456
+ return (h("button", { tabindex: "-1", "data-day": day, "data-month": month, "data-year": year, "data-index": index, "data-day-of-week": dayOfWeek, disabled: isCalDayDisabled, class: {
1457
+ 'calendar-day-padding': day === null,
1458
+ 'calendar-day': true,
1459
+ 'calendar-day-active': isActive,
1460
+ 'calendar-day-today': isToday,
1461
+ }, "aria-selected": ariaSelected, "aria-label": ariaLabel, onClick: () => {
1462
+ if (day === null) {
1463
+ return;
1464
+ }
1465
+ this.setWorkingParts(Object.assign(Object.assign({}, this.workingParts), { month,
1466
+ day,
1467
+ year }));
1468
+ // multiple only needs date info, so we can wipe out other fields like time
1469
+ if (multiple) {
1470
+ this.setActiveParts({
1471
+ month,
1472
+ day,
1473
+ year,
1474
+ }, isActive);
1475
+ }
1476
+ else {
1477
+ this.setActiveParts(Object.assign(Object.assign({}, activePart), { month,
1478
+ day,
1479
+ year }));
1480
+ }
1481
+ } }, text));
1482
+ }))));
1483
+ }
1484
+ renderCalendarBody() {
1485
+ return (h("div", { class: "calendar-body ion-focusable", ref: (el) => (this.calendarBodyRef = el), tabindex: "0" }, generateMonths(this.workingParts).map(({ month, year }) => {
1486
+ return this.renderMonth(month, year);
1487
+ })));
871
1488
  }
872
- validateColumn(columns, name, index, min, max, lowerBounds, upperBounds) {
873
- const column = columns.find(c => c.name === name);
874
- if (!column) {
875
- return 0;
1489
+ renderCalendar(mode) {
1490
+ return (h("div", { class: "datetime-calendar", key: "datetime-calendar" }, this.renderCalendarHeader(mode), this.renderCalendarBody()));
1491
+ }
1492
+ renderTimeLabel() {
1493
+ const hasSlottedTimeLabel = this.el.querySelector('[slot="time-label"]') !== null;
1494
+ if (!hasSlottedTimeLabel && !this.showDefaultTimeLabel) {
1495
+ return;
876
1496
  }
877
- const lb = lowerBounds.slice();
878
- const ub = upperBounds.slice();
879
- const options = column.options;
880
- let indexMin = options.length - 1;
881
- let indexMax = 0;
882
- for (let i = 0; i < options.length; i++) {
883
- const opts = options[i];
884
- const value = opts.value;
885
- lb[index] = opts.value;
886
- ub[index] = opts.value;
887
- const disabled = opts.disabled = (value < lowerBounds[index] ||
888
- value > upperBounds[index] ||
889
- dateSortValue(ub[0], ub[1], ub[2], ub[3], ub[4]) < min ||
890
- dateSortValue(lb[0], lb[1], lb[2], lb[3], lb[4]) > max);
891
- if (!disabled) {
892
- indexMin = Math.min(indexMin, i);
893
- indexMax = Math.max(indexMax, i);
1497
+ return h("slot", { name: "time-label" }, "Time");
1498
+ }
1499
+ renderTimeOverlay() {
1500
+ const use24Hour = is24Hour(this.locale, this.hourCycle);
1501
+ const activePart = this.getActivePartsWithFallback();
1502
+ return [
1503
+ h("div", { class: "time-header" }, this.renderTimeLabel()),
1504
+ h("button", { class: {
1505
+ 'time-body': true,
1506
+ 'time-body-active': this.isTimePopoverOpen,
1507
+ }, "aria-expanded": "false", "aria-haspopup": "true", onClick: async (ev) => {
1508
+ const { popoverRef } = this;
1509
+ if (popoverRef) {
1510
+ this.isTimePopoverOpen = true;
1511
+ popoverRef.present(new CustomEvent('ionShadowTarget', {
1512
+ detail: {
1513
+ ionShadowTarget: ev.target,
1514
+ },
1515
+ }));
1516
+ await popoverRef.onWillDismiss();
1517
+ this.isTimePopoverOpen = false;
1518
+ }
1519
+ } }, getLocalizedTime(this.locale, activePart, use24Hour)),
1520
+ h("ion-popover", { alignment: "center", translucent: true, overlayIndex: 1, arrow: false, onWillPresent: (ev) => {
1521
+ /**
1522
+ * Intersection Observers do not consistently fire between Blink and Webkit
1523
+ * when toggling the visibility of the popover and trying to scroll the picker
1524
+ * column to the correct time value.
1525
+ *
1526
+ * This will correctly scroll the element position to the correct time value,
1527
+ * before the popover is fully presented.
1528
+ */
1529
+ const cols = ev.target.querySelectorAll('ion-picker-column-internal');
1530
+ // TODO (FW-615): Potentially remove this when intersection observers are fixed in picker column
1531
+ cols.forEach((col) => col.scrollActiveItemIntoView());
1532
+ }, style: {
1533
+ '--offset-y': '-10px',
1534
+ '--min-width': 'fit-content',
1535
+ },
1536
+ // Allow native browser keyboard events to support up/down/home/end key
1537
+ // navigation within the time picker.
1538
+ keyboardEvents: true, ref: (el) => (this.popoverRef = el) }, this.renderWheelPicker('time')),
1539
+ ];
1540
+ }
1541
+ getHeaderSelectedDateText() {
1542
+ const { activeParts, multiple, titleSelectedDatesFormatter } = this;
1543
+ const isArray = Array.isArray(activeParts);
1544
+ let headerText;
1545
+ if (multiple && isArray && activeParts.length !== 1) {
1546
+ headerText = `${activeParts.length} days`; // default/fallback for multiple selection
1547
+ if (titleSelectedDatesFormatter !== undefined) {
1548
+ try {
1549
+ headerText = titleSelectedDatesFormatter(convertDataToISO(activeParts));
1550
+ }
1551
+ catch (e) {
1552
+ printIonError('Exception in provided `titleSelectedDatesFormatter`: ', e);
1553
+ }
894
1554
  }
895
1555
  }
896
- const selectedIndex = column.selectedIndex = clamp(indexMin, column.selectedIndex, indexMax);
897
- const opt = column.options[selectedIndex];
898
- if (opt) {
899
- return opt.value;
1556
+ else {
1557
+ // for exactly 1 day selected (multiple set or not), show a formatted version of that
1558
+ headerText = getMonthAndDay(this.locale, this.getActivePartsWithFallback());
900
1559
  }
901
- return 0;
902
- }
903
- get text() {
904
- // create the text of the formatted data
905
- const template = this.displayFormat || this.pickerFormat || DEFAULT_FORMAT;
906
- if (this.value === undefined ||
907
- this.value === null ||
908
- this.value.length === 0) {
1560
+ return headerText;
1561
+ }
1562
+ renderCalendarViewHeader(showExpandedHeader = true) {
1563
+ const hasSlottedTitle = this.el.querySelector('[slot="title"]') !== null;
1564
+ if (!hasSlottedTitle && !this.showDefaultTitle) {
909
1565
  return;
910
1566
  }
911
- return renderDatetime(template, this.datetimeValue, this.locale);
1567
+ return (h("div", { class: "datetime-header" }, h("div", { class: "datetime-title" }, h("slot", { name: "title" }, "Select Date")), showExpandedHeader && h("div", { class: "datetime-selected-date" }, this.getHeaderSelectedDateText())));
1568
+ }
1569
+ /**
1570
+ * Render time picker inside of datetime.
1571
+ * Do not pass color prop to segment on
1572
+ * iOS mode. MD segment has been customized and
1573
+ * should take on the color prop, but iOS
1574
+ * should just be the default segment.
1575
+ */
1576
+ renderTime() {
1577
+ const { presentation } = this;
1578
+ const timeOnlyPresentation = presentation === 'time';
1579
+ return (h("div", { class: "datetime-time" }, timeOnlyPresentation ? this.renderWheelPicker() : this.renderTimeOverlay()));
912
1580
  }
913
- hasValue() {
914
- return this.text !== undefined;
1581
+ /**
1582
+ * Renders the month/year picker that is
1583
+ * displayed on the calendar grid.
1584
+ * The .datetime-year class has additional
1585
+ * styles that let us show/hide the
1586
+ * picker when the user clicks on the
1587
+ * toggle in the calendar header.
1588
+ */
1589
+ renderCalendarViewMonthYearPicker() {
1590
+ return h("div", { class: "datetime-year" }, this.renderWheelView('month-year'));
915
1591
  }
916
- setFocus() {
917
- if (this.buttonEl) {
918
- this.buttonEl.focus();
1592
+ /**
1593
+ * Render entry point
1594
+ * All presentation types are rendered from here.
1595
+ */
1596
+ renderDatetime(mode) {
1597
+ const { presentation, preferWheel } = this;
1598
+ /**
1599
+ * Certain presentation types have separate grid and wheel displays.
1600
+ * If preferWheel is true then we should show a wheel picker instead.
1601
+ */
1602
+ const hasWheelVariant = presentation === 'date' || presentation === 'date-time' || presentation === 'time-date';
1603
+ if (preferWheel && hasWheelVariant) {
1604
+ return [this.renderCalendarViewHeader(false), this.renderWheelView(), this.renderFooter()];
1605
+ }
1606
+ switch (presentation) {
1607
+ case 'date-time':
1608
+ return [
1609
+ this.renderCalendarViewHeader(),
1610
+ this.renderCalendar(mode),
1611
+ this.renderCalendarViewMonthYearPicker(),
1612
+ this.renderTime(),
1613
+ this.renderFooter(),
1614
+ ];
1615
+ case 'time-date':
1616
+ return [
1617
+ this.renderCalendarViewHeader(),
1618
+ this.renderTime(),
1619
+ this.renderCalendar(mode),
1620
+ this.renderCalendarViewMonthYearPicker(),
1621
+ this.renderFooter(),
1622
+ ];
1623
+ case 'time':
1624
+ return [this.renderTime(), this.renderFooter()];
1625
+ case 'month':
1626
+ case 'month-year':
1627
+ case 'year':
1628
+ return [this.renderWheelView(), this.renderFooter()];
1629
+ default:
1630
+ return [
1631
+ this.renderCalendarViewHeader(),
1632
+ this.renderCalendar(mode),
1633
+ this.renderCalendarViewMonthYearPicker(),
1634
+ this.renderFooter(),
1635
+ ];
919
1636
  }
920
1637
  }
921
1638
  render() {
922
- const { inputId, text, disabled, readonly, isExpanded, el, placeholder } = this;
1639
+ const { name, value, disabled, el, color, isPresented, readonly, showMonthAndYear, preferWheel, presentation, size, } = this;
923
1640
  const mode = getIonMode(this);
924
- const labelId = inputId + '-lbl';
925
- const label = findItemLabel(el);
926
- const addPlaceholderClass = (text === undefined && placeholder != null) ? true : false;
927
- // If selected text has been passed in, use that first
928
- // otherwise use the placeholder
929
- const datetimeText = text === undefined
930
- ? (placeholder != null ? placeholder : '')
931
- : text;
932
- const datetimeTextPart = text === undefined
933
- ? (placeholder != null ? 'placeholder' : undefined)
934
- : 'text';
935
- if (label) {
936
- label.id = labelId;
937
- }
938
- renderHiddenInput(true, el, this.name, this.value, this.disabled);
939
- return (h(Host, { onClick: this.onClick, "aria-disabled": disabled ? 'true' : null, "aria-expanded": `${isExpanded}`, "aria-haspopup": "true", "aria-labelledby": label ? labelId : null, class: {
1641
+ const isMonthAndYearPresentation = presentation === 'year' || presentation === 'month' || presentation === 'month-year';
1642
+ const shouldShowMonthAndYear = showMonthAndYear || isMonthAndYearPresentation;
1643
+ const monthYearPickerOpen = showMonthAndYear && !isMonthAndYearPresentation;
1644
+ const hasDatePresentation = presentation === 'date' || presentation === 'date-time' || presentation === 'time-date';
1645
+ const hasWheelVariant = hasDatePresentation && preferWheel;
1646
+ const hasGrid = hasDatePresentation && !preferWheel;
1647
+ renderHiddenInput(true, el, name, formatValue(value), disabled);
1648
+ return (h(Host, { "aria-disabled": disabled ? 'true' : null, onFocus: this.onFocus, onBlur: this.onBlur, class: Object.assign({}, createColorClasses(color, {
940
1649
  [mode]: true,
941
- 'datetime-disabled': disabled,
942
- 'datetime-readonly': readonly,
943
- 'datetime-placeholder': addPlaceholderClass,
944
- 'in-item': hostContext('ion-item', el)
945
- } }, h("div", { class: "datetime-text", part: datetimeTextPart }, datetimeText), h("button", { type: "button", onFocus: this.onFocus, onBlur: this.onBlur, disabled: this.disabled, ref: btnEl => this.buttonEl = btnEl })));
1650
+ ['datetime-presented']: isPresented,
1651
+ ['datetime-readonly']: readonly,
1652
+ ['datetime-disabled']: disabled,
1653
+ 'show-month-and-year': shouldShowMonthAndYear,
1654
+ 'month-year-picker-open': monthYearPickerOpen,
1655
+ [`datetime-presentation-${presentation}`]: true,
1656
+ [`datetime-size-${size}`]: true,
1657
+ [`datetime-prefer-wheel`]: hasWheelVariant,
1658
+ [`datetime-grid`]: hasGrid,
1659
+ })) }, this.renderDatetime(mode)));
946
1660
  }
947
1661
  get el() { return getElement(this); }
948
1662
  static get watchers() { return {
949
1663
  "disabled": ["disabledChanged"],
1664
+ "min": ["minChanged"],
1665
+ "max": ["maxChanged"],
1666
+ "yearValues": ["yearValuesChanged"],
1667
+ "monthValues": ["monthValuesChanged"],
1668
+ "dayValues": ["dayValuesChanged"],
1669
+ "hourValues": ["hourValuesChanged"],
1670
+ "minuteValues": ["minuteValuesChanged"],
1671
+ "activeParts": ["activePartsChanged"],
950
1672
  "value": ["valueChanged"]
951
1673
  }; }
952
1674
  };
953
- const divyColumns = (columns) => {
954
- const columnsWidth = [];
955
- let col;
956
- let width;
957
- for (let i = 0; i < columns.length; i++) {
958
- col = columns[i];
959
- columnsWidth.push(0);
960
- for (const option of col.options) {
961
- width = option.text.length;
962
- if (width > columnsWidth[i]) {
963
- columnsWidth[i] = width;
964
- }
965
- }
966
- }
967
- if (columnsWidth.length === 2) {
968
- width = Math.max(columnsWidth[0], columnsWidth[1]);
969
- columns[0].align = 'right';
970
- columns[1].align = 'left';
971
- columns[0].optionsWidth = columns[1].optionsWidth = `${width * 17}px`;
972
- }
973
- else if (columnsWidth.length === 3) {
974
- width = Math.max(columnsWidth[0], columnsWidth[2]);
975
- columns[0].align = 'right';
976
- columns[1].columnWidth = `${columnsWidth[1] * 17}px`;
977
- columns[0].optionsWidth = columns[2].optionsWidth = `${width * 17}px`;
978
- columns[2].align = 'left';
979
- }
980
- return columns;
981
- };
982
- const DEFAULT_FORMAT = 'MMM D, YYYY';
983
1675
  let datetimeIds = 0;
984
1676
  Datetime.style = {
985
1677
  ios: datetimeIosCss,