@ionic/core 8.7.10-dev.11762798751.11b77c85 → 8.7.10-dev.11763406256.1a4bac8c

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 (281) hide show
  1. package/components/checkbox.js +63 -9
  2. package/components/ion-input.js +7 -6
  3. package/components/ion-searchbar.js +6 -6
  4. package/components/ion-select.js +7 -6
  5. package/components/ion-textarea.js +2 -1
  6. package/components/ion-toggle.js +62 -12
  7. package/components/notch-controller.js +153 -0
  8. package/components/radio-group.js +60 -7
  9. package/components/validity.js +1 -150
  10. package/dist/cjs/{animation-D_c13yq_.js → animation-Bt3H9L1C.js} +1 -1
  11. package/dist/cjs/{app-globals--1qFsqEw.js → app-globals-Ciccnk9_.js} +1 -1
  12. package/dist/cjs/{button-active-CQiBOKCI.js → button-active-CMc8cD90.js} +1 -1
  13. package/dist/cjs/{config-DL8XJF0D.js → config-C5fsO43a.js} +1 -1
  14. package/dist/cjs/{data-DtEHxOpt.js → data-JwZKaIQB.js} +1 -1
  15. package/dist/cjs/{framework-delegate-DmSgIb_h.js → framework-delegate-DMJRBuDi.js} +1 -1
  16. package/dist/cjs/{hardware-back-button-C72O8m7E.js → hardware-back-button-VCK4V3mG.js} +1 -1
  17. package/dist/cjs/{helpers-xCoR213K.js → helpers-DrTqNghc.js} +1 -1
  18. package/dist/cjs/{index-WEBAL2Zz.js → index-094mMFB-.js} +4 -4
  19. package/dist/cjs/{index-DOeyHw27.js → index-C534ULug.js} +2 -2
  20. package/dist/cjs/{index-FugOCm8C.js → index-CO6eryBo.js} +2 -2
  21. package/dist/cjs/{index-qWlbeMTM.js → index-D6Wc6v08.js} +3 -41
  22. package/dist/cjs/{index-FdJ4-6Rg.js → index-DrMUZJj6.js} +5 -5
  23. package/dist/cjs/index.cjs.js +12 -12
  24. package/dist/cjs/{input-shims-CYpwizql.js → input-shims-CW0KUFTQ.js} +3 -3
  25. package/dist/cjs/{input.utils-DhlH-Ng5.js → input.utils-B_QROI2g.js} +2 -2
  26. package/dist/cjs/ion-accordion_2.cjs.entry.js +3 -3
  27. package/dist/cjs/ion-action-sheet.cjs.entry.js +8 -8
  28. package/dist/cjs/ion-alert.cjs.entry.js +9 -9
  29. package/dist/cjs/ion-app_8.cjs.entry.js +12 -12
  30. package/dist/cjs/ion-avatar_3.cjs.entry.js +2 -2
  31. package/dist/cjs/ion-back-button.cjs.entry.js +3 -3
  32. package/dist/cjs/ion-backdrop.cjs.entry.js +2 -2
  33. package/dist/cjs/ion-breadcrumb_2.cjs.entry.js +3 -3
  34. package/dist/cjs/ion-button_2.cjs.entry.js +3 -3
  35. package/dist/cjs/ion-card_5.cjs.entry.js +3 -3
  36. package/dist/cjs/ion-checkbox.cjs.entry.js +63 -11
  37. package/dist/cjs/ion-chip.cjs.entry.js +2 -2
  38. package/dist/cjs/ion-col_3.cjs.entry.js +2 -2
  39. package/dist/cjs/ion-datetime-button.cjs.entry.js +4 -4
  40. package/dist/cjs/ion-datetime_3.cjs.entry.js +8 -8
  41. package/dist/cjs/ion-fab_3.cjs.entry.js +3 -3
  42. package/dist/cjs/ion-img.cjs.entry.js +3 -3
  43. package/dist/cjs/ion-infinite-scroll_2.cjs.entry.js +5 -5
  44. package/dist/cjs/ion-input-otp.cjs.entry.js +3 -3
  45. package/dist/cjs/ion-input-password-toggle.cjs.entry.js +2 -2
  46. package/dist/cjs/ion-input.cjs.entry.js +11 -10
  47. package/dist/cjs/ion-item-option_3.cjs.entry.js +4 -4
  48. package/dist/cjs/ion-item_8.cjs.entry.js +3 -3
  49. package/dist/cjs/ion-loading.cjs.entry.js +8 -8
  50. package/dist/cjs/ion-menu_3.cjs.entry.js +8 -8
  51. package/dist/cjs/ion-modal.cjs.entry.js +9 -9
  52. package/dist/cjs/ion-nav_2.cjs.entry.js +6 -6
  53. package/dist/cjs/ion-picker-column-option.cjs.entry.js +3 -3
  54. package/dist/cjs/ion-picker-column.cjs.entry.js +3 -3
  55. package/dist/cjs/ion-picker.cjs.entry.js +2 -2
  56. package/dist/cjs/ion-popover.cjs.entry.js +8 -8
  57. package/dist/cjs/ion-progress-bar.cjs.entry.js +3 -3
  58. package/dist/cjs/ion-radio_2.cjs.entry.js +60 -9
  59. package/dist/cjs/ion-range.cjs.entry.js +4 -4
  60. package/dist/cjs/ion-refresher_2.cjs.entry.js +6 -6
  61. package/dist/cjs/ion-reorder_2.cjs.entry.js +4 -4
  62. package/dist/cjs/ion-ripple-effect.cjs.entry.js +2 -2
  63. package/dist/cjs/ion-route_4.cjs.entry.js +3 -3
  64. package/dist/cjs/ion-searchbar.cjs.entry.js +8 -8
  65. package/dist/cjs/ion-segment-content.cjs.entry.js +1 -1
  66. package/dist/cjs/ion-segment-view.cjs.entry.js +1 -1
  67. package/dist/cjs/ion-segment_2.cjs.entry.js +3 -3
  68. package/dist/cjs/ion-select-modal.cjs.entry.js +6 -6
  69. package/dist/cjs/ion-select_3.cjs.entry.js +13 -12
  70. package/dist/cjs/ion-spinner.cjs.entry.js +2 -2
  71. package/dist/cjs/ion-split-pane.cjs.entry.js +2 -2
  72. package/dist/cjs/ion-tab-bar_2.cjs.entry.js +3 -3
  73. package/dist/cjs/ion-tab_2.cjs.entry.js +3 -3
  74. package/dist/cjs/ion-text.cjs.entry.js +2 -2
  75. package/dist/cjs/ion-textarea.cjs.entry.js +7 -6
  76. package/dist/cjs/ion-toast.cjs.entry.js +8 -8
  77. package/dist/cjs/ion-toggle.cjs.entry.js +61 -13
  78. package/dist/cjs/{ionic-global-CR8JSFWa.js → ionic-global-HMVqOFGO.js} +1 -1
  79. package/dist/cjs/ionic.cjs.js +5 -5
  80. package/dist/cjs/{ios.transition-MQamiuLS.js → ios.transition-BOt_uW73.js} +4 -4
  81. package/dist/cjs/loader.cjs.js +4 -4
  82. package/dist/cjs/{md.transition-VK5nuqXu.js → md.transition-Dt968VXB.js} +4 -4
  83. package/dist/cjs/{validity-ilhIJL9Q.js → notch-controller-Bzqhjm4f.js} +1 -15
  84. package/dist/cjs/{overlays-DcMnmLe2.js → overlays-DxIZwUXI.js} +5 -5
  85. package/dist/cjs/{status-tap-C0NZlIwJ.js → status-tap-g0sWWkXk.js} +3 -3
  86. package/dist/cjs/{swipe-back-CdOHy9bk.js → swipe-back-BIayeNOD.js} +2 -2
  87. package/dist/cjs/validity-BpS37YFM.js +19 -0
  88. package/dist/collection/collection-manifest.json +2 -2
  89. package/dist/collection/components/checkbox/checkbox.js +67 -9
  90. package/dist/collection/components/input/input.js +8 -8
  91. package/dist/collection/components/radio-group/radio-group.js +64 -7
  92. package/dist/collection/components/searchbar/searchbar.js +9 -9
  93. package/dist/collection/components/select/select.js +5 -5
  94. package/dist/collection/components/toggle/toggle.js +62 -12
  95. package/dist/docs.json +25 -15
  96. package/dist/esm/{animation-BMjqGaKJ.js → animation-Dt8bGnA-.js} +1 -1
  97. package/dist/esm/{app-globals-CXKe5CYv.js → app-globals-BDSf8fOA.js} +1 -1
  98. package/dist/esm/{button-active-DsHCf5yJ.js → button-active-L570Swow.js} +1 -1
  99. package/dist/esm/{config-FyGnL1d5.js → config-mCdtaoPe.js} +1 -1
  100. package/dist/esm/{data-VwBJONjN.js → data-DCORV9FH.js} +1 -1
  101. package/dist/esm/{framework-delegate-DA71As64.js → framework-delegate-BYawdMXj.js} +1 -1
  102. package/dist/esm/{hardware-back-button-gEuwgxq4.js → hardware-back-button-CPLxO-Ev.js} +1 -1
  103. package/dist/esm/{helpers-Br4puuoA.js → helpers-DEn3pfjm.js} +1 -1
  104. package/dist/esm/{index-nzxJGPo-.js → index-Bs3kT4bc.js} +2 -2
  105. package/dist/esm/{index-COOKa-0k.js → index-C8IsBmNU.js} +3 -41
  106. package/dist/esm/{index-DNUVD4sY.js → index-CvDIirVx.js} +5 -5
  107. package/dist/esm/{index-CZzUL5H7.js → index-ceb5RaMT.js} +2 -2
  108. package/dist/esm/{index-DKnepNGf.js → index-r2D9DEro.js} +4 -4
  109. package/dist/esm/index.js +12 -12
  110. package/dist/esm/{input-shims-G0QQ_JXi.js → input-shims-DyOpfTg6.js} +3 -3
  111. package/dist/esm/{input.utils-BRuJbV55.js → input.utils-DrvTa8gz.js} +2 -2
  112. package/dist/esm/ion-accordion_2.entry.js +3 -3
  113. package/dist/esm/ion-action-sheet.entry.js +8 -8
  114. package/dist/esm/ion-alert.entry.js +9 -9
  115. package/dist/esm/ion-app_8.entry.js +12 -12
  116. package/dist/esm/ion-avatar_3.entry.js +2 -2
  117. package/dist/esm/ion-back-button.entry.js +3 -3
  118. package/dist/esm/ion-backdrop.entry.js +2 -2
  119. package/dist/esm/ion-breadcrumb_2.entry.js +3 -3
  120. package/dist/esm/ion-button_2.entry.js +3 -3
  121. package/dist/esm/ion-card_5.entry.js +3 -3
  122. package/dist/esm/ion-checkbox.entry.js +63 -11
  123. package/dist/esm/ion-chip.entry.js +2 -2
  124. package/dist/esm/ion-col_3.entry.js +2 -2
  125. package/dist/esm/ion-datetime-button.entry.js +4 -4
  126. package/dist/esm/ion-datetime_3.entry.js +8 -8
  127. package/dist/esm/ion-fab_3.entry.js +3 -3
  128. package/dist/esm/ion-img.entry.js +3 -3
  129. package/dist/esm/ion-infinite-scroll_2.entry.js +5 -5
  130. package/dist/esm/ion-input-otp.entry.js +3 -3
  131. package/dist/esm/ion-input-password-toggle.entry.js +2 -2
  132. package/dist/esm/ion-input.entry.js +10 -9
  133. package/dist/esm/ion-item-option_3.entry.js +4 -4
  134. package/dist/esm/ion-item_8.entry.js +3 -3
  135. package/dist/esm/ion-loading.entry.js +8 -8
  136. package/dist/esm/ion-menu_3.entry.js +8 -8
  137. package/dist/esm/ion-modal.entry.js +9 -9
  138. package/dist/esm/ion-nav_2.entry.js +6 -6
  139. package/dist/esm/ion-picker-column-option.entry.js +3 -3
  140. package/dist/esm/ion-picker-column.entry.js +3 -3
  141. package/dist/esm/ion-picker.entry.js +2 -2
  142. package/dist/esm/ion-popover.entry.js +8 -8
  143. package/dist/esm/ion-progress-bar.entry.js +3 -3
  144. package/dist/esm/ion-radio_2.entry.js +60 -9
  145. package/dist/esm/ion-range.entry.js +4 -4
  146. package/dist/esm/ion-refresher_2.entry.js +6 -6
  147. package/dist/esm/ion-reorder_2.entry.js +4 -4
  148. package/dist/esm/ion-ripple-effect.entry.js +2 -2
  149. package/dist/esm/ion-route_4.entry.js +3 -3
  150. package/dist/esm/ion-searchbar.entry.js +8 -8
  151. package/dist/esm/ion-segment-content.entry.js +1 -1
  152. package/dist/esm/ion-segment-view.entry.js +1 -1
  153. package/dist/esm/ion-segment_2.entry.js +3 -3
  154. package/dist/esm/ion-select-modal.entry.js +6 -6
  155. package/dist/esm/ion-select_3.entry.js +12 -11
  156. package/dist/esm/ion-spinner.entry.js +2 -2
  157. package/dist/esm/ion-split-pane.entry.js +2 -2
  158. package/dist/esm/ion-tab-bar_2.entry.js +3 -3
  159. package/dist/esm/ion-tab_2.entry.js +3 -3
  160. package/dist/esm/ion-text.entry.js +2 -2
  161. package/dist/esm/ion-textarea.entry.js +6 -5
  162. package/dist/esm/ion-toast.entry.js +8 -8
  163. package/dist/esm/ion-toggle.entry.js +61 -13
  164. package/dist/esm/{ionic-global-ssiordoc.js → ionic-global-CDrldh-5.js} +1 -1
  165. package/dist/esm/ionic.js +6 -6
  166. package/dist/esm/{ios.transition-CdWk5te_.js → ios.transition-BDzw0_Hm.js} +4 -4
  167. package/dist/esm/loader.js +5 -5
  168. package/dist/esm/{md.transition-CTwmjwnm.js → md.transition-BzDYi3qq.js} +4 -4
  169. package/dist/esm/{validity-CwxNVaqN.js → notch-controller-BwelN_JM.js} +2 -15
  170. package/dist/esm/{overlays-DNIbGUWk.js → overlays-BymNv-BL.js} +5 -5
  171. package/dist/esm/{status-tap-B6uPB7F5.js → status-tap-5DQ7Fc4V.js} +3 -3
  172. package/dist/esm/{swipe-back-INesdA_O.js → swipe-back-BKw2CAHc.js} +2 -2
  173. package/dist/esm/validity-DJztqcrH.js +17 -0
  174. package/dist/html.html-data.json +18 -2
  175. package/dist/ionic/index.esm.js +1 -1
  176. package/dist/ionic/ionic.esm.js +1 -1
  177. package/dist/ionic/{p-2b9e100d.entry.js → p-020af078.entry.js} +1 -1
  178. package/dist/ionic/p-074839fc.entry.js +4 -0
  179. package/dist/ionic/{p-964e89ac.entry.js → p-0b80d700.entry.js} +1 -1
  180. package/dist/ionic/{p-894e8953.entry.js → p-0dfa5a37.entry.js} +1 -1
  181. package/dist/ionic/{p-3807749f.entry.js → p-11518b31.entry.js} +1 -1
  182. package/dist/ionic/{p-fcd74818.entry.js → p-15193d01.entry.js} +1 -1
  183. package/dist/ionic/{p-c49f0f68.entry.js → p-1647c46c.entry.js} +1 -1
  184. package/dist/ionic/{p-116a97cb.entry.js → p-2a939845.entry.js} +1 -1
  185. package/dist/ionic/{p-3c522786.entry.js → p-316c0420.entry.js} +1 -1
  186. package/dist/ionic/{p-860f309a.entry.js → p-31f7095f.entry.js} +1 -1
  187. package/dist/ionic/{p-adde497f.entry.js → p-370e4237.entry.js} +1 -1
  188. package/dist/ionic/{p-ef8c7682.entry.js → p-3a6caca9.entry.js} +1 -1
  189. package/dist/ionic/p-40c261a3.entry.js +4 -0
  190. package/dist/ionic/{p-06881392.entry.js → p-43ed1ef5.entry.js} +1 -1
  191. package/dist/ionic/{p-a66bbfa0.entry.js → p-46d74291.entry.js} +1 -1
  192. package/dist/ionic/{p-7047a7b7.entry.js → p-49d06882.entry.js} +1 -1
  193. package/dist/ionic/{p-d0f6b9a7.entry.js → p-4c85d268.entry.js} +1 -1
  194. package/dist/ionic/p-4e41ea20.entry.js +4 -0
  195. package/dist/ionic/{p-5d600703.entry.js → p-4efea47a.entry.js} +1 -1
  196. package/dist/ionic/{p-dfd6acb5.entry.js → p-510d86e1.entry.js} +1 -1
  197. package/dist/ionic/p-51a60e0f.entry.js +4 -0
  198. package/dist/ionic/{p-dfc6f4b1.entry.js → p-582824c5.entry.js} +1 -1
  199. package/dist/ionic/{p-259e4068.entry.js → p-6241ce47.entry.js} +1 -1
  200. package/dist/ionic/{p-4ede0a73.entry.js → p-639dd543.entry.js} +1 -1
  201. package/dist/ionic/{p-7a19f3d1.entry.js → p-6444c606.entry.js} +1 -1
  202. package/dist/ionic/{p-c7f3e813.entry.js → p-675b1a31.entry.js} +1 -1
  203. package/dist/ionic/{p-db44afdd.entry.js → p-6d070558.entry.js} +1 -1
  204. package/dist/ionic/{p-5dcbb52f.entry.js → p-7268efa5.entry.js} +1 -1
  205. package/dist/ionic/{p-c6b68ac0.entry.js → p-72c38b88.entry.js} +1 -1
  206. package/dist/ionic/p-7380261c.entry.js +4 -0
  207. package/dist/ionic/{p-a41048a1.entry.js → p-79bd78f9.entry.js} +1 -1
  208. package/dist/ionic/{p-295a2119.entry.js → p-7da39a4d.entry.js} +1 -1
  209. package/dist/ionic/{p-d24d55cb.entry.js → p-83be404e.entry.js} +1 -1
  210. package/dist/ionic/{p-70c66710.entry.js → p-86f53961.entry.js} +1 -1
  211. package/dist/ionic/{p-db648b6a.entry.js → p-94de5cfa.entry.js} +1 -1
  212. package/dist/ionic/{p-6fe93af5.entry.js → p-9575b654.entry.js} +1 -1
  213. package/dist/ionic/{p-bef7bd83.entry.js → p-98fc09eb.entry.js} +1 -1
  214. package/dist/ionic/{p-BA44xiai.js → p-B0q1YL7N.js} +1 -1
  215. package/dist/ionic/p-BFvmZNyx.js +4 -0
  216. package/dist/ionic/p-BKc55Xev.js +4 -0
  217. package/dist/ionic/{p-baqdazzM.js → p-BOVrCkpJ.js} +1 -1
  218. package/dist/ionic/{p-CT3hZD31.js → p-BYEqWnSg.js} +1 -1
  219. package/dist/ionic/p-Bmgaetn_.js +4 -0
  220. package/dist/ionic/{p-CUek2VVk.js → p-C7hRNDhM.js} +1 -1
  221. package/dist/ionic/p-C8IsBmNU.js +5 -0
  222. package/dist/ionic/{p-CYTVJxfQ.js → p-CJxh_yLS.js} +1 -1
  223. package/dist/ionic/{p-C8VsQFea.js → p-CTfR9YZG.js} +1 -1
  224. package/dist/ionic/p-CwgG81ZD.js +4 -0
  225. package/dist/ionic/{p-UCpjvnQe.js → p-D-eFFUkA.js} +1 -1
  226. package/dist/ionic/{p-DddcVlzx.js → p-D0dMcSkw.js} +1 -1
  227. package/dist/ionic/{p-ynapDa0U.js → p-D87hU-Ly.js} +1 -1
  228. package/dist/ionic/{p-C9IcKRgw.js → p-DCv9sLH2.js} +1 -1
  229. package/dist/ionic/p-DDb5r57F.js +4 -0
  230. package/dist/ionic/p-DJztqcrH.js +4 -0
  231. package/dist/ionic/{p-yu2f42Nv.js → p-DNcfiJwE.js} +1 -1
  232. package/dist/ionic/{p-CETqdl8n.js → p-DUt5fQmA.js} +1 -1
  233. package/dist/ionic/p-DZRJwG4S.js +4 -0
  234. package/dist/ionic/{p-D8G3beZX.js → p-DbQ5QkTP.js} +1 -1
  235. package/dist/ionic/{p-BUluHF_z.js → p-Dc45iWE4.js} +1 -1
  236. package/dist/ionic/{p-Bzed_Vkc.js → p-QHYY4sjU.js} +1 -1
  237. package/dist/ionic/{p-3ca55cd6.entry.js → p-a127bee2.entry.js} +1 -1
  238. package/dist/ionic/{p-b3da5db0.entry.js → p-a80f1b04.entry.js} +1 -1
  239. package/dist/ionic/{p-948228a2.entry.js → p-ac4eb91d.entry.js} +1 -1
  240. package/dist/ionic/{p-7078b759.entry.js → p-b0a7585c.entry.js} +1 -1
  241. package/dist/ionic/{p-3549e539.entry.js → p-b57c6d3e.entry.js} +1 -1
  242. package/dist/ionic/p-c19f63d0.entry.js +4 -0
  243. package/dist/ionic/{p-e1547a17.entry.js → p-cc45bcbc.entry.js} +1 -1
  244. package/dist/ionic/{p-e1c3b35d.entry.js → p-d0a2a1ab.entry.js} +1 -1
  245. package/dist/ionic/{p-89c2d5ea.entry.js → p-d126e8d3.entry.js} +1 -1
  246. package/dist/ionic/p-d1f54e28.entry.js +4 -0
  247. package/dist/ionic/p-d3014190.entry.js +4 -0
  248. package/dist/ionic/{p-93d7685c.entry.js → p-da7d04cc.entry.js} +1 -1
  249. package/dist/ionic/p-dbbe606a.entry.js +4 -0
  250. package/dist/ionic/{p-0144ab54.entry.js → p-e16b69e1.entry.js} +1 -1
  251. package/dist/ionic/{p-d27a5c0b.entry.js → p-f8f22cc0.entry.js} +1 -1
  252. package/dist/types/components/checkbox/checkbox.d.ts +9 -1
  253. package/dist/types/components/header/header.utils.d.ts +1 -2
  254. package/dist/types/components/input/input.d.ts +1 -1
  255. package/dist/types/components/radio-group/radio-group.d.ts +9 -1
  256. package/dist/types/components/searchbar/searchbar.d.ts +1 -1
  257. package/dist/types/components/select/select.d.ts +2 -2
  258. package/dist/types/components/toggle/toggle.d.ts +7 -1
  259. package/dist/types/components.d.ts +8 -8
  260. package/dist/types/stencil-public-runtime.d.ts +9 -19
  261. package/dist/types/utils/forms/validity.d.ts +1 -1
  262. package/dist/types/utils/gesture/listener.d.ts +1 -2
  263. package/hydrate/index.js +369 -356
  264. package/hydrate/index.mjs +369 -356
  265. package/package.json +3 -3
  266. package/dist/ionic/p-0188f4a6.entry.js +0 -4
  267. package/dist/ionic/p-14e08559.entry.js +0 -4
  268. package/dist/ionic/p-22bcfae9.entry.js +0 -4
  269. package/dist/ionic/p-45c80fd7.entry.js +0 -4
  270. package/dist/ionic/p-49a21ffb.entry.js +0 -4
  271. package/dist/ionic/p-5cf136b5.entry.js +0 -4
  272. package/dist/ionic/p-9fc9dfae.entry.js +0 -4
  273. package/dist/ionic/p-BS-HlVX0.js +0 -4
  274. package/dist/ionic/p-BWS2Niqo.js +0 -4
  275. package/dist/ionic/p-CCKqmAua.js +0 -4
  276. package/dist/ionic/p-COOKa-0k.js +0 -5
  277. package/dist/ionic/p-CSQuMCUi.js +0 -4
  278. package/dist/ionic/p-PfJ1h_Zm.js +0 -4
  279. package/dist/ionic/p-edcc6f4f.entry.js +0 -4
  280. package/dist/ionic/p-f53597ee.entry.js +0 -4
  281. package/dist/ionic/p-w2EzNm9e.js +0 -4
@@ -1,7 +1,8 @@
1
1
  /*!
2
2
  * (C) Ionic http://ionicframework.com - MIT License
3
3
  */
4
- import { Host, h } from "@stencil/core";
4
+ import { Build, Host, h } from "@stencil/core";
5
+ import { checkInvalidState } from "../../utils/forms/index";
5
6
  import { inheritAriaAttributes, renderHiddenInput } from "../../utils/helpers";
6
7
  import { createColorClasses, hostContext } from "../../utils/theme";
7
8
  import { getIonMode } from "../../global/ionic-global";
@@ -62,6 +63,10 @@ export class Checkbox {
62
63
  * submitting if the value is invalid.
63
64
  */
64
65
  this.required = false;
66
+ /**
67
+ * Track validation state for proper aria-live announcements.
68
+ */
69
+ this.isInvalid = false;
65
70
  /**
66
71
  * Sets the checked property and emits
67
72
  * the ionChange event. Use this to update the
@@ -108,16 +113,63 @@ export class Checkbox {
108
113
  ev.stopPropagation();
109
114
  };
110
115
  }
116
+ connectedCallback() {
117
+ const { el } = this;
118
+ // Watch for class changes to update validation state.
119
+ if (Build.isBrowser && typeof MutationObserver !== 'undefined') {
120
+ this.validationObserver = new MutationObserver(() => {
121
+ const newIsInvalid = checkInvalidState(el);
122
+ if (this.isInvalid !== newIsInvalid) {
123
+ this.isInvalid = newIsInvalid;
124
+ /**
125
+ * Screen readers tend to announce changes
126
+ * to `aria-describedby` when the attribute
127
+ * is changed during a blur event for a
128
+ * native form control.
129
+ * However, the announcement can be spotty
130
+ * when using a non-native form control
131
+ * and `forceUpdate()`.
132
+ * This is due to `forceUpdate()` internally
133
+ * rescheduling the DOM update to a lower
134
+ * priority queue regardless if it's called
135
+ * inside a Promise or not, thus causing
136
+ * the screen reader to potentially miss the
137
+ * change.
138
+ * By using a State variable inside a Promise,
139
+ * it guarantees a re-render immediately at
140
+ * a higher priority.
141
+ */
142
+ Promise.resolve().then(() => {
143
+ this.hintTextId = this.getHintTextId();
144
+ });
145
+ }
146
+ });
147
+ this.validationObserver.observe(el, {
148
+ attributes: true,
149
+ attributeFilter: ['class'],
150
+ });
151
+ }
152
+ // Always set initial state
153
+ this.isInvalid = checkInvalidState(el);
154
+ }
111
155
  componentWillLoad() {
112
156
  this.inheritedAttributes = Object.assign({}, inheritAriaAttributes(this.el));
157
+ this.hintTextId = this.getHintTextId();
158
+ }
159
+ disconnectedCallback() {
160
+ // Clean up validation observer to prevent memory leaks.
161
+ if (this.validationObserver) {
162
+ this.validationObserver.disconnect();
163
+ this.validationObserver = undefined;
164
+ }
113
165
  }
114
166
  /** @internal */
115
167
  async setFocus() {
116
168
  this.el.focus();
117
169
  }
118
- getHintTextID() {
119
- const { el, helperText, errorText, helperTextId, errorTextId } = this;
120
- if (el.classList.contains('ion-touched') && el.classList.contains('ion-invalid') && errorText) {
170
+ getHintTextId() {
171
+ const { helperText, errorText, helperTextId, errorTextId, isInvalid } = this;
172
+ if (isInvalid && errorText) {
121
173
  return errorTextId;
122
174
  }
123
175
  if (helperText) {
@@ -130,7 +182,7 @@ export class Checkbox {
130
182
  * This element should only be rendered if hint text is set.
131
183
  */
132
184
  renderHintText() {
133
- const { helperText, errorText, helperTextId, errorTextId } = this;
185
+ const { helperText, errorText, helperTextId, errorTextId, isInvalid } = this;
134
186
  /**
135
187
  * undefined and empty string values should
136
188
  * be treated as not having helper/error text.
@@ -139,7 +191,7 @@ export class Checkbox {
139
191
  if (!hasHintText) {
140
192
  return;
141
193
  }
142
- return (h("div", { class: "checkbox-bottom" }, h("div", { id: helperTextId, class: "helper-text", part: "supporting-text helper-text" }, helperText), h("div", { id: errorTextId, class: "error-text", part: "supporting-text error-text" }, errorText)));
194
+ return (h("div", { class: "checkbox-bottom" }, h("div", { id: helperTextId, class: "helper-text", part: "supporting-text helper-text", "aria-live": "polite" }, !isInvalid ? helperText : null), h("div", { id: errorTextId, class: "error-text", part: "supporting-text error-text", role: "alert" }, isInvalid ? errorText : null)));
143
195
  }
144
196
  render() {
145
197
  const { color, checked, disabled, el, getSVGPath, indeterminate, inheritedAttributes, inputId, justify, labelPlacement, name, value, alignment, required, } = this;
@@ -149,7 +201,7 @@ export class Checkbox {
149
201
  renderHiddenInput(true, el, name, checked ? value : '', disabled);
150
202
  // The host element must have a checkbox role to ensure proper VoiceOver
151
203
  // support in Safari for accessibility.
152
- return (h(Host, { key: 'ee2e02d28f9d15a1ec746609f7e9559444f621e5', role: "checkbox", "aria-checked": indeterminate ? 'mixed' : `${checked}`, "aria-describedby": this.getHintTextID(), "aria-invalid": this.getHintTextID() === this.errorTextId, "aria-labelledby": hasLabelContent ? this.inputLabelId : null, "aria-label": inheritedAttributes['aria-label'] || null, "aria-disabled": disabled ? 'true' : null, tabindex: disabled ? undefined : 0, onKeyDown: this.onKeyDown, onFocus: this.onFocus, onBlur: this.onBlur, onClick: this.onClick, class: createColorClasses(color, {
204
+ return (h(Host, { key: 'ae0fbd4b21accbac132e6b85c513512ad9179394', role: "checkbox", "aria-checked": indeterminate ? 'mixed' : `${checked}`, "aria-describedby": this.hintTextId, "aria-invalid": this.isInvalid ? 'true' : undefined, "aria-labelledby": hasLabelContent ? this.inputLabelId : null, "aria-label": inheritedAttributes['aria-label'] || null, "aria-disabled": disabled ? 'true' : null, "aria-required": required ? 'true' : undefined, tabindex: disabled ? undefined : 0, onKeyDown: this.onKeyDown, onFocus: this.onFocus, onBlur: this.onBlur, onClick: this.onClick, class: createColorClasses(color, {
153
205
  [mode]: true,
154
206
  'in-item': hostContext('ion-item', el),
155
207
  'checkbox-checked': checked,
@@ -159,10 +211,10 @@ export class Checkbox {
159
211
  [`checkbox-justify-${justify}`]: justify !== undefined,
160
212
  [`checkbox-alignment-${alignment}`]: alignment !== undefined,
161
213
  [`checkbox-label-placement-${labelPlacement}`]: true,
162
- }) }, h("label", { key: '84d4c33da0348dc65ad36fb0fafd48be366dcf3b', class: "checkbox-wrapper", htmlFor: inputId }, h("input", Object.assign({ key: '427db69a3ab8a17aa0867519c90f585b8930406b', type: "checkbox", checked: checked ? true : undefined, disabled: disabled, id: inputId, onChange: this.toggleChecked, required: required }, inheritedAttributes)), h("div", { key: '9dda7024b3a4f1ee55351f783f9a10f9b4ad0d12', class: {
214
+ }) }, h("label", { key: '7a3d7f3c27dde514f2dbf2e34f4629fad33ec3bf', class: "checkbox-wrapper", htmlFor: inputId }, h("input", Object.assign({ key: '4130d77ddf034271fecccda14e101a5a809921b6', type: "checkbox", checked: checked ? true : undefined, disabled: disabled, id: inputId, onChange: this.toggleChecked, required: required }, inheritedAttributes)), h("div", { key: '5daa74f4e62b0947e37764762524001ee42609d9', class: {
163
215
  'label-text-wrapper': true,
164
216
  'label-text-wrapper-hidden': !hasLabelContent,
165
- }, part: "label", id: this.inputLabelId, onClick: this.onDivLabelClick }, h("slot", { key: 'f9d1d545ffd4164b650808241b51ea1bedc6a42c' }), this.renderHintText()), h("div", { key: 'a96d61ac324864228f14caa0e9f2c0d15418882e', class: "native-wrapper" }, h("svg", { key: '64ff3e4d87e190601811ef64323edec18d510cd1', class: "checkbox-icon", viewBox: "0 0 24 24", part: "container", "aria-hidden": "true" }, path)))));
217
+ }, part: "label", id: this.inputLabelId, onClick: this.onDivLabelClick }, h("slot", { key: '23ff66138f8c3a2f56f39113fc842d54b2f7952a' }), this.renderHintText()), h("div", { key: 'ab914d9623c19fc46821d5e62db92f1192ebbe7e', class: "native-wrapper" }, h("svg", { key: '66e3f4f5dcaa9756fb0e9452299954f9ed3dcb7b', class: "checkbox-icon", viewBox: "0 0 24 24", part: "container", "aria-hidden": "true" }, path)))));
166
218
  }
167
219
  getSVGPath(mode, indeterminate) {
168
220
  let path = indeterminate ? (h("path", { d: "M6 12L18 12", part: "mark" })) : (h("path", { d: "M5.9,12.5l3.8,3.8l8.8-8.8", part: "mark" }));
@@ -430,6 +482,12 @@ export class Checkbox {
430
482
  }
431
483
  };
432
484
  }
485
+ static get states() {
486
+ return {
487
+ "isInvalid": {},
488
+ "hintTextId": {}
489
+ };
490
+ }
433
491
  static get events() {
434
492
  return [{
435
493
  "method": "ionChange",
@@ -55,7 +55,7 @@ export class Input {
55
55
  /**
56
56
  * Whether auto correction should be enabled when the user is entering/editing the text value.
57
57
  */
58
- this.autocorrect = false;
58
+ this.autocorrect = 'off';
59
59
  /**
60
60
  * Sets the [`autofocus` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/autofocus) on the native input element.
61
61
  *
@@ -511,7 +511,7 @@ export class Input {
511
511
  * TODO(FW-5592): Remove hasStartEndSlots condition
512
512
  */
513
513
  const labelShouldFloat = labelPlacement === 'stacked' || (labelPlacement === 'floating' && (hasValue || hasFocus || hasStartEndSlots));
514
- return (h(Host, { key: '39ddb0c5882d377a96979aa2b78ca6430071b277', class: createColorClasses(this.color, {
514
+ return (h(Host, { key: '97b5308021064d9e7434ef2d3d96f27045c1b0c4', class: createColorClasses(this.color, {
515
515
  [mode]: true,
516
516
  'has-value': hasValue,
517
517
  'has-focus': hasFocus,
@@ -522,14 +522,14 @@ export class Input {
522
522
  'in-item': inItem,
523
523
  'in-item-color': hostContext('ion-item.ion-color', this.el),
524
524
  'input-disabled': disabled,
525
- }) }, h("label", { key: '624920541bb673225cb3aee82b5ac71f4abaefbd', class: "input-wrapper", htmlFor: inputId, onClick: this.onLabelClick }, this.renderLabelContainer(), h("div", { key: '5d1a22755ae9e14b704da04f763b177983474a97', class: "native-wrapper", onClick: this.onLabelClick }, h("slot", { key: '088e13ac7550537e4e7466d0eda1053d6255f48e', name: "start" }), h("input", Object.assign({ key: 'e851301b91e7e70a794c49260fe1b3e8d1f97999', class: "native-input", ref: (input) => (this.nativeInput = input), id: inputId, disabled: disabled, autoCapitalize: this.autocapitalize, autoComplete: this.autocomplete, autoCorrect: this.autocorrect ? 'on' : 'off', autoFocus: this.autofocus, enterKeyHint: this.enterkeyhint, inputMode: this.inputmode, min: this.min, max: this.max, minLength: this.minlength, maxLength: this.maxlength, multiple: this.multiple, name: this.name, pattern: this.pattern, placeholder: this.placeholder || '', readOnly: readonly, required: this.required, spellcheck: this.spellcheck, step: this.step, type: this.type, value: value, onInput: this.onInput, onChange: this.onChange, onBlur: this.onBlur, onFocus: this.onFocus, onKeyDown: this.onKeydown, onCompositionstart: this.onCompositionStart, onCompositionend: this.onCompositionEnd, "aria-describedby": this.getHintTextID(), "aria-invalid": this.isInvalid ? 'true' : undefined }, this.inheritedAttributes)), this.clearInput && !readonly && !disabled && (h("button", { key: '65541ba96111fc978c428d9950a7e6fe1a589a75', "aria-label": "reset", type: "button", class: "input-clear-icon", onPointerDown: (ev) => {
525
+ }) }, h("label", { key: '353f68726ce180299bd9adc81e5ff7d26a48f54f', class: "input-wrapper", htmlFor: inputId, onClick: this.onLabelClick }, this.renderLabelContainer(), h("div", { key: '2034b4bad04fc157f3298a1805819216b6f439d0', class: "native-wrapper", onClick: this.onLabelClick }, h("slot", { key: '96bb5e30176b2bd76dfb75bfbf6c1c3d4403f4bb', name: "start" }), h("input", Object.assign({ key: '1a1d75b0e414a95c89d5a760757c33548d234aca', class: "native-input", ref: (input) => (this.nativeInput = input), id: inputId, disabled: disabled, autoCapitalize: this.autocapitalize, autoComplete: this.autocomplete, autoCorrect: this.autocorrect, autoFocus: this.autofocus, enterKeyHint: this.enterkeyhint, inputMode: this.inputmode, min: this.min, max: this.max, minLength: this.minlength, maxLength: this.maxlength, multiple: this.multiple, name: this.name, pattern: this.pattern, placeholder: this.placeholder || '', readOnly: readonly, required: this.required, spellcheck: this.spellcheck, step: this.step, type: this.type, value: value, onInput: this.onInput, onChange: this.onChange, onBlur: this.onBlur, onFocus: this.onFocus, onKeyDown: this.onKeydown, onCompositionstart: this.onCompositionStart, onCompositionend: this.onCompositionEnd, "aria-describedby": this.getHintTextID(), "aria-invalid": this.isInvalid ? 'true' : undefined }, this.inheritedAttributes)), this.clearInput && !readonly && !disabled && (h("button", { key: '95f3df17b7691d9a2e7dcd4a51f16a94aa3ca36f', "aria-label": "reset", type: "button", class: "input-clear-icon", onPointerDown: (ev) => {
526
526
  /**
527
527
  * This prevents mobile browsers from
528
528
  * blurring the input when the clear
529
529
  * button is activated.
530
530
  */
531
531
  ev.preventDefault();
532
- }, onClick: this.clearTextInput }, h("ion-icon", { key: '3f4bc559ce36e32c7235bf642547c012805b14ab', "aria-hidden": "true", icon: clearIconData }))), h("slot", { key: '0b4cd18c49350d28b3d31df73304b84748c0ed0b', name: "end" })), shouldRenderHighlight && h("div", { key: '5fcbb19f0ed1f2f7b7105c040256c783132da046', class: "input-highlight" })), this.renderBottomContent()));
532
+ }, onClick: this.clearTextInput }, h("ion-icon", { key: '16b0af75eed50c8115fb5597f73b5fbf71c2530e', "aria-hidden": "true", icon: clearIconData }))), h("slot", { key: 'c48da0f8ddb3764ac43efa705bb4a6bb2d9cc2fd', name: "end" })), shouldRenderHighlight && h("div", { key: 'f15238481fc20de56ca7ecb6e350b3c024cc755e', class: "input-highlight" })), this.renderBottomContent()));
533
533
  }
534
534
  static get is() { return "ion-input"; }
535
535
  static get encapsulation() { return "scoped"; }
@@ -619,11 +619,11 @@ export class Input {
619
619
  "defaultValue": "'off'"
620
620
  },
621
621
  "autocorrect": {
622
- "type": "boolean",
622
+ "type": "string",
623
623
  "mutable": false,
624
624
  "complexType": {
625
- "original": "boolean",
626
- "resolved": "boolean",
625
+ "original": "'on' | 'off'",
626
+ "resolved": "\"off\" | \"on\"",
627
627
  "references": {}
628
628
  },
629
629
  "required": false,
@@ -636,7 +636,7 @@ export class Input {
636
636
  "setter": false,
637
637
  "reflect": false,
638
638
  "attribute": "autocorrect",
639
- "defaultValue": "false"
639
+ "defaultValue": "'off'"
640
640
  },
641
641
  "autofocus": {
642
642
  "type": "boolean",
@@ -1,7 +1,8 @@
1
1
  /*!
2
2
  * (C) Ionic http://ionicframework.com - MIT License
3
3
  */
4
- import { Host, h } from "@stencil/core";
4
+ import { Build, Host, h } from "@stencil/core";
5
+ import { checkInvalidState } from "../../utils/forms/index";
5
6
  import { renderHiddenInput } from "../../utils/helpers";
6
7
  import { getIonMode } from "../../global/ionic-global";
7
8
  export class RadioGroup {
@@ -10,6 +11,10 @@ export class RadioGroup {
10
11
  this.helperTextId = `${this.inputId}-helper-text`;
11
12
  this.errorTextId = `${this.inputId}-error-text`;
12
13
  this.labelId = `${this.inputId}-lbl`;
14
+ /**
15
+ * Track validation state for proper aria-live announcements.
16
+ */
17
+ this.isInvalid = false;
13
18
  /**
14
19
  * If `true`, the radios can be deselected.
15
20
  */
@@ -91,6 +96,52 @@ export class RadioGroup {
91
96
  this.labelId = label.id = this.name + '-lbl';
92
97
  }
93
98
  }
99
+ // Watch for class changes to update validation state.
100
+ if (Build.isBrowser && typeof MutationObserver !== 'undefined') {
101
+ this.validationObserver = new MutationObserver(() => {
102
+ const newIsInvalid = checkInvalidState(this.el);
103
+ if (this.isInvalid !== newIsInvalid) {
104
+ this.isInvalid = newIsInvalid;
105
+ /**
106
+ * Screen readers tend to announce changes
107
+ * to `aria-describedby` when the attribute
108
+ * is changed during a blur event for a
109
+ * native form control.
110
+ * However, the announcement can be spotty
111
+ * when using a non-native form control
112
+ * and `forceUpdate()`.
113
+ * This is due to `forceUpdate()` internally
114
+ * rescheduling the DOM update to a lower
115
+ * priority queue regardless if it's called
116
+ * inside a Promise or not, thus causing
117
+ * the screen reader to potentially miss the
118
+ * change.
119
+ * By using a State variable inside a Promise,
120
+ * it guarantees a re-render immediately at
121
+ * a higher priority.
122
+ */
123
+ Promise.resolve().then(() => {
124
+ this.hintTextId = this.getHintTextId();
125
+ });
126
+ }
127
+ });
128
+ this.validationObserver.observe(this.el, {
129
+ attributes: true,
130
+ attributeFilter: ['class'],
131
+ });
132
+ }
133
+ // Always set initial state
134
+ this.isInvalid = checkInvalidState(this.el);
135
+ }
136
+ componentWillLoad() {
137
+ this.hintTextId = this.getHintTextId();
138
+ }
139
+ disconnectedCallback() {
140
+ // Clean up validation observer to prevent memory leaks.
141
+ if (this.validationObserver) {
142
+ this.validationObserver.disconnect();
143
+ this.validationObserver = undefined;
144
+ }
94
145
  }
95
146
  getRadios() {
96
147
  return Array.from(this.el.querySelectorAll('ion-radio'));
@@ -166,16 +217,16 @@ export class RadioGroup {
166
217
  * Renders the helper text or error text values
167
218
  */
168
219
  renderHintText() {
169
- const { helperText, errorText, helperTextId, errorTextId } = this;
220
+ const { helperText, errorText, helperTextId, errorTextId, isInvalid } = this;
170
221
  const hasHintText = !!helperText || !!errorText;
171
222
  if (!hasHintText) {
172
223
  return;
173
224
  }
174
- return (h("div", { class: "radio-group-top" }, h("div", { id: helperTextId, class: "helper-text" }, helperText), h("div", { id: errorTextId, class: "error-text" }, errorText)));
225
+ return (h("div", { class: "radio-group-top" }, h("div", { id: helperTextId, class: "helper-text", "aria-live": "polite" }, !isInvalid ? helperText : null), h("div", { id: errorTextId, class: "error-text", role: "alert" }, isInvalid ? errorText : null)));
175
226
  }
176
- getHintTextID() {
177
- const { el, helperText, errorText, helperTextId, errorTextId } = this;
178
- if (el.classList.contains('ion-touched') && el.classList.contains('ion-invalid') && errorText) {
227
+ getHintTextId() {
228
+ const { helperText, errorText, helperTextId, errorTextId, isInvalid } = this;
229
+ if (isInvalid && errorText) {
179
230
  return errorTextId;
180
231
  }
181
232
  if (helperText) {
@@ -187,7 +238,7 @@ export class RadioGroup {
187
238
  const { label, labelId, el, name, value } = this;
188
239
  const mode = getIonMode(this);
189
240
  renderHiddenInput(true, el, name, value, false);
190
- return (h(Host, { key: '81b8ebc96b2f383c36717f290d2959cc921ad6e8', role: "radiogroup", "aria-labelledby": label ? labelId : null, "aria-describedby": this.getHintTextID(), "aria-invalid": this.getHintTextID() === this.errorTextId, onClick: this.onClick, class: mode }, this.renderHintText(), h("div", { key: '45b09efc10776b889a8f372cba80d25a3fc849da', class: "radio-group-wrapper" }, h("slot", { key: '58714934542c2fdd7396de160364f3f06b32e8f8' }))));
241
+ return (h(Host, { key: 'db593b3ed511e9395e3c7bfd91b787328692cd6d', role: "radiogroup", "aria-labelledby": label ? labelId : null, "aria-describedby": this.hintTextId, "aria-invalid": this.isInvalid ? 'true' : undefined, onClick: this.onClick, class: mode }, this.renderHintText(), h("div", { key: '85045b45a0100a45f3b9a35d1c5a25ec63d525c4', class: "radio-group-wrapper" }, h("slot", { key: '53dacb87ce62398e78771fb2efaf839ab922d946' }))));
191
242
  }
192
243
  static get is() { return "ion-radio-group"; }
193
244
  static get originalStyleUrls() {
@@ -328,6 +379,12 @@ export class RadioGroup {
328
379
  }
329
380
  };
330
381
  }
382
+ static get states() {
383
+ return {
384
+ "isInvalid": {},
385
+ "hintTextId": {}
386
+ };
387
+ }
331
388
  static get events() {
332
389
  return [{
333
390
  "method": "ionChange",
@@ -35,7 +35,7 @@ export class Searchbar {
35
35
  /**
36
36
  * Set the input's autocorrect property.
37
37
  */
38
- this.autocorrect = false;
38
+ this.autocorrect = 'off';
39
39
  /**
40
40
  * Set the cancel button icon. Only applies to `md` mode.
41
41
  * Defaults to `arrow-back-sharp`.
@@ -427,8 +427,8 @@ export class Searchbar {
427
427
  const clearIcon = this.clearIcon || (mode === 'ios' ? closeCircle : closeSharp);
428
428
  const searchIcon = this.searchIcon || (mode === 'ios' ? searchOutline : searchSharp);
429
429
  const shouldShowCancelButton = this.shouldShowCancelButton();
430
- const cancelButton = this.showCancelButton !== 'never' && (h("button", { key: 'c40019b58933a2e6e260de877f5d05419f477aca', "aria-label": cancelButtonText, "aria-hidden": shouldShowCancelButton ? undefined : 'true', type: "button", tabIndex: mode === 'ios' && !shouldShowCancelButton ? -1 : undefined, onMouseDown: this.onCancelSearchbar, onTouchStart: this.onCancelSearchbar, class: "searchbar-cancel-button" }, h("div", { key: '1c6b2caf907b0d457c8da2960068bfa922364996', "aria-hidden": "true" }, mode === 'md' ? (h("ion-icon", { "aria-hidden": "true", mode: mode, icon: this.cancelButtonIcon, lazy: false })) : (cancelButtonText))));
431
- return (h(Host, { key: '4b5d8f2d7a6a1dc104d85cae6e76105d1e5123c6', role: "search", "aria-disabled": this.disabled ? 'true' : null, class: createColorClasses(this.color, {
430
+ const cancelButton = this.showCancelButton !== 'never' && (h("button", { key: '19e18775856db87daeb4b9e3d7bca0461915a0df', "aria-label": cancelButtonText, "aria-hidden": shouldShowCancelButton ? undefined : 'true', type: "button", tabIndex: mode === 'ios' && !shouldShowCancelButton ? -1 : undefined, onMouseDown: this.onCancelSearchbar, onTouchStart: this.onCancelSearchbar, class: "searchbar-cancel-button" }, h("div", { key: 'b3bbdcc033f3bd3441d619e4a252cef0dad4d07e', "aria-hidden": "true" }, mode === 'md' ? (h("ion-icon", { "aria-hidden": "true", mode: mode, icon: this.cancelButtonIcon, lazy: false })) : (cancelButtonText))));
431
+ return (h(Host, { key: '074aa60e051bfb3225e87d44bbb6346c59c73574', role: "search", "aria-disabled": this.disabled ? 'true' : null, class: createColorClasses(this.color, {
432
432
  [mode]: true,
433
433
  'searchbar-animated': animated,
434
434
  'searchbar-disabled': this.disabled,
@@ -438,14 +438,14 @@ export class Searchbar {
438
438
  'searchbar-has-focus': this.focused,
439
439
  'searchbar-should-show-clear': this.shouldShowClearButton(),
440
440
  'searchbar-should-show-cancel': this.shouldShowCancelButton(),
441
- }) }, h("div", { key: '2aa1dd3bbd82b7df543ecb08fc54f106543e6c55', class: "searchbar-input-container" }, h("input", Object.assign({ key: 'e0a7e751e4a84803ad8367ab0cd251e7e2a17ef3', "aria-label": "search text", disabled: this.disabled, ref: (el) => (this.nativeInput = el), class: "searchbar-input", inputMode: this.inputmode, enterKeyHint: this.enterkeyhint, name: this.name, onInput: this.onInput, onChange: this.onChange, onBlur: this.onBlur, onFocus: this.onFocus, minLength: this.minlength, maxLength: this.maxlength, placeholder: this.placeholder, type: this.type, value: this.getValue(), autoCapitalize: autocapitalize === 'default' ? undefined : autocapitalize, autoComplete: this.autocomplete, autoCorrect: this.autocorrect ? 'on' : 'off', spellcheck: this.spellcheck }, this.inheritedAttributes)), mode === 'md' && cancelButton, h("ion-icon", { key: '346f943e83b3236087ca78f56525c56997f60884', "aria-hidden": "true", mode: mode, icon: searchIcon, lazy: false, class: "searchbar-search-icon" }), h("button", { key: '76a685d35e47b13230f449c58946239137510cfc', "aria-label": "reset", type: "button", "no-blur": true, class: "searchbar-clear-button", onPointerDown: (ev) => {
441
+ }) }, h("div", { key: '54f58a79fe36e85d9295157303f1be89c98bbdaf', class: "searchbar-input-container" }, h("input", Object.assign({ key: 'f991a37fcf54d26b7ad10d89084764e03d97b9de', "aria-label": "search text", disabled: this.disabled, ref: (el) => (this.nativeInput = el), class: "searchbar-input", inputMode: this.inputmode, enterKeyHint: this.enterkeyhint, name: this.name, onInput: this.onInput, onChange: this.onChange, onBlur: this.onBlur, onFocus: this.onFocus, minLength: this.minlength, maxLength: this.maxlength, placeholder: this.placeholder, type: this.type, value: this.getValue(), autoCapitalize: autocapitalize === 'default' ? undefined : autocapitalize, autoComplete: this.autocomplete, autoCorrect: this.autocorrect, spellcheck: this.spellcheck }, this.inheritedAttributes)), mode === 'md' && cancelButton, h("ion-icon", { key: '8b44dd90a3292c5cf872ef16a8520675f5673494', "aria-hidden": "true", mode: mode, icon: searchIcon, lazy: false, class: "searchbar-search-icon" }), h("button", { key: '79d9cfed8f01268044f82811a35d323a12dca749', "aria-label": "reset", type: "button", "no-blur": true, class: "searchbar-clear-button", onPointerDown: (ev) => {
442
442
  /**
443
443
  * This prevents mobile browsers from
444
444
  * blurring the input when the clear
445
445
  * button is activated.
446
446
  */
447
447
  ev.preventDefault();
448
- }, onClick: () => this.onClearInput(true) }, h("ion-icon", { key: '88d8cff792861f3ae5646252db212e2461445c47', "aria-hidden": "true", mode: mode, icon: clearIcon, lazy: false, class: "searchbar-clear-icon" }))), mode === 'ios' && cancelButton));
448
+ }, onClick: () => this.onClearInput(true) }, h("ion-icon", { key: 'aa3b9fa8a61f853236783ac7bcd0b113ea65ece2', "aria-hidden": "true", mode: mode, icon: clearIcon, lazy: false, class: "searchbar-clear-icon" }))), mode === 'ios' && cancelButton));
449
449
  }
450
450
  static get is() { return "ion-searchbar"; }
451
451
  static get encapsulation() { return "scoped"; }
@@ -555,11 +555,11 @@ export class Searchbar {
555
555
  "defaultValue": "'off'"
556
556
  },
557
557
  "autocorrect": {
558
- "type": "boolean",
558
+ "type": "string",
559
559
  "mutable": false,
560
560
  "complexType": {
561
- "original": "boolean",
562
- "resolved": "boolean",
561
+ "original": "'on' | 'off'",
562
+ "resolved": "\"off\" | \"on\"",
563
563
  "references": {}
564
564
  },
565
565
  "required": false,
@@ -572,7 +572,7 @@ export class Searchbar {
572
572
  "setter": false,
573
573
  "reflect": false,
574
574
  "attribute": "autocorrect",
575
- "defaultValue": "false"
575
+ "defaultValue": "'off'"
576
576
  },
577
577
  "cancelButtonIcon": {
578
578
  "type": "string",
@@ -202,7 +202,7 @@ export class Select {
202
202
  * a higher priority.
203
203
  */
204
204
  Promise.resolve().then(() => {
205
- this.hintTextID = this.getHintTextID();
205
+ this.hintTextId = this.getHintTextId();
206
206
  });
207
207
  }
208
208
  });
@@ -216,7 +216,7 @@ export class Select {
216
216
  }
217
217
  componentWillLoad() {
218
218
  this.inheritedAttributes = inheritAttributes(this.el, ['aria-label']);
219
- this.hintTextID = this.getHintTextID();
219
+ this.hintTextId = this.getHintTextId();
220
220
  }
221
221
  componentDidLoad() {
222
222
  /**
@@ -761,9 +761,9 @@ export class Select {
761
761
  }
762
762
  renderListbox() {
763
763
  const { disabled, inputId, isExpanded, required } = this;
764
- return (h("button", { disabled: disabled, id: inputId, "aria-label": this.ariaLabel, "aria-haspopup": "dialog", "aria-expanded": `${isExpanded}`, "aria-describedby": this.hintTextID, "aria-invalid": this.isInvalid ? 'true' : undefined, "aria-required": `${required}`, onFocus: this.onFocus, onBlur: this.onBlur, ref: (focusEl) => (this.focusEl = focusEl) }));
764
+ return (h("button", { disabled: disabled, id: inputId, "aria-label": this.ariaLabel, "aria-haspopup": "dialog", "aria-expanded": `${isExpanded}`, "aria-describedby": this.hintTextId, "aria-invalid": this.isInvalid ? 'true' : undefined, "aria-required": `${required}`, onFocus: this.onFocus, onBlur: this.onBlur, ref: (focusEl) => (this.focusEl = focusEl) }));
765
765
  }
766
- getHintTextID() {
766
+ getHintTextId() {
767
767
  const { helperText, errorText, helperTextId, errorTextId, isInvalid } = this;
768
768
  if (isInvalid && errorText) {
769
769
  return errorTextId;
@@ -1316,7 +1316,7 @@ export class Select {
1316
1316
  "isExpanded": {},
1317
1317
  "hasFocus": {},
1318
1318
  "isInvalid": {},
1319
- "hintTextID": {}
1319
+ "hintTextId": {}
1320
1320
  };
1321
1321
  }
1322
1322
  static get events() {
@@ -1,7 +1,8 @@
1
1
  /*!
2
2
  * (C) Ionic http://ionicframework.com - MIT License
3
3
  */
4
- import { Host, h } from "@stencil/core";
4
+ import { Build, Host, h } from "@stencil/core";
5
+ import { checkInvalidState } from "../../utils/forms/index";
5
6
  import { renderHiddenInput, inheritAriaAttributes } from "../../utils/helpers";
6
7
  import { hapticSelection } from "../../utils/native/haptic";
7
8
  import { isPlatform } from "../../utils/platform";
@@ -32,6 +33,10 @@ export class Toggle {
32
33
  this.inheritedAttributes = {};
33
34
  this.didLoad = false;
34
35
  this.activated = false;
36
+ /**
37
+ * Track validation state for proper aria-live announcements.
38
+ */
39
+ this.isInvalid = false;
35
40
  /**
36
41
  * The name of the control, which is submitted with the form data.
37
42
  */
@@ -145,15 +150,52 @@ export class Toggle {
145
150
  });
146
151
  }
147
152
  async connectedCallback() {
153
+ const { didLoad, el } = this;
148
154
  /**
149
155
  * If we have not yet rendered
150
156
  * ion-toggle, then toggleTrack is not defined.
151
157
  * But if we are moving ion-toggle via appendChild,
152
158
  * then toggleTrack will be defined.
153
159
  */
154
- if (this.didLoad) {
160
+ if (didLoad) {
155
161
  this.setupGesture();
156
162
  }
163
+ // Watch for class changes to update validation state.
164
+ if (Build.isBrowser && typeof MutationObserver !== 'undefined') {
165
+ this.validationObserver = new MutationObserver(() => {
166
+ const newIsInvalid = checkInvalidState(el);
167
+ if (this.isInvalid !== newIsInvalid) {
168
+ this.isInvalid = newIsInvalid;
169
+ /**
170
+ * Screen readers tend to announce changes
171
+ * to `aria-describedby` when the attribute
172
+ * is changed during a blur event for a
173
+ * native form control.
174
+ * However, the announcement can be spotty
175
+ * when using a non-native form control
176
+ * and `forceUpdate()`.
177
+ * This is due to `forceUpdate()` internally
178
+ * rescheduling the DOM update to a lower
179
+ * priority queue regardless if it's called
180
+ * inside a Promise or not, thus causing
181
+ * the screen reader to potentially miss the
182
+ * change.
183
+ * By using a State variable inside a Promise,
184
+ * it guarantees a re-render immediately at
185
+ * a higher priority.
186
+ */
187
+ Promise.resolve().then(() => {
188
+ this.hintTextId = this.getHintTextId();
189
+ });
190
+ }
191
+ });
192
+ this.validationObserver.observe(el, {
193
+ attributes: true,
194
+ attributeFilter: ['class'],
195
+ });
196
+ }
197
+ // Always set initial state
198
+ this.isInvalid = checkInvalidState(el);
157
199
  }
158
200
  componentDidLoad() {
159
201
  this.setupGesture();
@@ -164,9 +206,15 @@ export class Toggle {
164
206
  this.gesture.destroy();
165
207
  this.gesture = undefined;
166
208
  }
209
+ // Clean up validation observer to prevent memory leaks.
210
+ if (this.validationObserver) {
211
+ this.validationObserver.disconnect();
212
+ this.validationObserver = undefined;
213
+ }
167
214
  }
168
215
  componentWillLoad() {
169
216
  this.inheritedAttributes = Object.assign({}, inheritAriaAttributes(this.el));
217
+ this.hintTextId = this.getHintTextId();
170
218
  }
171
219
  onStart() {
172
220
  this.activated = true;
@@ -207,9 +255,9 @@ export class Toggle {
207
255
  get hasLabel() {
208
256
  return this.el.textContent !== '';
209
257
  }
210
- getHintTextID() {
211
- const { el, helperText, errorText, helperTextId, errorTextId } = this;
212
- if (el.classList.contains('ion-touched') && el.classList.contains('ion-invalid') && errorText) {
258
+ getHintTextId() {
259
+ const { helperText, errorText, helperTextId, errorTextId, isInvalid } = this;
260
+ if (isInvalid && errorText) {
213
261
  return errorTextId;
214
262
  }
215
263
  if (helperText) {
@@ -222,7 +270,7 @@ export class Toggle {
222
270
  * This element should only be rendered if hint text is set.
223
271
  */
224
272
  renderHintText() {
225
- const { helperText, errorText, helperTextId, errorTextId } = this;
273
+ const { helperText, errorText, helperTextId, errorTextId, isInvalid } = this;
226
274
  /**
227
275
  * undefined and empty string values should
228
276
  * be treated as not having helper/error text.
@@ -231,15 +279,15 @@ export class Toggle {
231
279
  if (!hasHintText) {
232
280
  return;
233
281
  }
234
- return (h("div", { class: "toggle-bottom" }, h("div", { id: helperTextId, class: "helper-text", part: "supporting-text helper-text" }, helperText), h("div", { id: errorTextId, class: "error-text", part: "supporting-text error-text" }, errorText)));
282
+ return (h("div", { class: "toggle-bottom" }, h("div", { id: helperTextId, class: "helper-text", part: "supporting-text helper-text", "aria-live": "polite" }, !isInvalid ? helperText : null), h("div", { id: errorTextId, class: "error-text", part: "supporting-text error-text", role: "alert" }, isInvalid ? errorText : null)));
235
283
  }
236
284
  render() {
237
- const { activated, alignment, checked, color, disabled, el, errorTextId, hasLabel, inheritedAttributes, inputId, inputLabelId, justify, labelPlacement, name, required, } = this;
285
+ const { activated, alignment, checked, color, disabled, el, hasLabel, inheritedAttributes, inputId, inputLabelId, justify, labelPlacement, name, required, } = this;
238
286
  const mode = getIonMode(this);
239
287
  const value = this.getValue();
240
288
  const rtl = isRTL(el) ? 'rtl' : 'ltr';
241
289
  renderHiddenInput(true, el, name, checked ? value : '', disabled);
242
- return (h(Host, { key: '17bbbc8d229868e5c872b2bc5a3faf579780c5e0', role: "switch", "aria-checked": `${checked}`, "aria-describedby": this.getHintTextID(), "aria-invalid": this.getHintTextID() === errorTextId, onClick: this.onClick, "aria-labelledby": hasLabel ? inputLabelId : null, "aria-label": inheritedAttributes['aria-label'] || null, "aria-disabled": disabled ? 'true' : null, tabindex: disabled ? undefined : 0, onKeyDown: this.onKeyDown, onFocus: this.onFocus, onBlur: this.onBlur, class: createColorClasses(color, {
290
+ return (h(Host, { key: 'f569148edd89ee041a4719ffc4733c16b05229bd', role: "switch", "aria-checked": `${checked}`, "aria-describedby": this.hintTextId, "aria-invalid": this.isInvalid ? 'true' : undefined, onClick: this.onClick, "aria-labelledby": hasLabel ? inputLabelId : null, "aria-label": inheritedAttributes['aria-label'] || null, "aria-disabled": disabled ? 'true' : null, "aria-required": required ? 'true' : undefined, tabindex: disabled ? undefined : 0, onKeyDown: this.onKeyDown, onFocus: this.onFocus, onBlur: this.onBlur, class: createColorClasses(color, {
243
291
  [mode]: true,
244
292
  'in-item': hostContext('ion-item', el),
245
293
  'toggle-activated': activated,
@@ -249,10 +297,10 @@ export class Toggle {
249
297
  [`toggle-alignment-${alignment}`]: alignment !== undefined,
250
298
  [`toggle-label-placement-${labelPlacement}`]: true,
251
299
  [`toggle-${rtl}`]: true,
252
- }) }, h("label", { key: '673625b62a2c909e95dccb642c91312967a6cd1c', class: "toggle-wrapper", htmlFor: inputId }, h("input", Object.assign({ key: '7dc3f357b4708116663970047765da9f8f845bf0', type: "checkbox", role: "switch", "aria-checked": `${checked}`, checked: checked, disabled: disabled, id: inputId, required: required }, inheritedAttributes)), h("div", { key: '8f1c6a182031e8cbc6727e5f4ac0e00ad4247447', class: {
300
+ }) }, h("label", { key: '3027f2ac4be6de422a14486d847fbee77f615db1', class: "toggle-wrapper", htmlFor: inputId }, h("input", Object.assign({ key: '4b0304c9e879e432b80184b4e5de37d55c11b436', type: "checkbox", role: "switch", "aria-checked": `${checked}`, checked: checked, disabled: disabled, id: inputId, required: required }, inheritedAttributes)), h("div", { key: '8ef265ec942e7f01ff31cbb202ed146c6bf94e02', class: {
253
301
  'label-text-wrapper': true,
254
302
  'label-text-wrapper-hidden': !hasLabel,
255
- }, part: "label", id: inputLabelId, onClick: this.onDivLabelClick }, h("slot", { key: '8322b9d54dc7edeb4e16fefcde9f7ebca8d5c3e1' }), this.renderHintText()), h("div", { key: 'fe6984143db817a7b3020a3f57cf5418fc3dcc0e', class: "native-wrapper" }, this.renderToggleControl()))));
303
+ }, part: "label", id: inputLabelId, onClick: this.onDivLabelClick }, h("slot", { key: '7b162b7dd27199cca2a4c995276a18b9f8e44aaf' }), this.renderHintText()), h("div", { key: 'd13c34bd42fca01cc73ddb4ea7e471b33a282a3e', class: "native-wrapper" }, this.renderToggleControl()))));
256
304
  }
257
305
  static get is() { return "ion-toggle"; }
258
306
  static get encapsulation() { return "shadow"; }
@@ -515,7 +563,9 @@ export class Toggle {
515
563
  }
516
564
  static get states() {
517
565
  return {
518
- "activated": {}
566
+ "activated": {},
567
+ "isInvalid": {},
568
+ "hintTextId": {}
519
569
  };
520
570
  }
521
571
  static get events() {