@ionic/core 9.0.1-nightly.20260825 → 9.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (429) hide show
  1. package/components/index.js +1 -1
  2. package/components/ion-accordion-group.js +1 -1
  3. package/components/ion-accordion.js +1 -1
  4. package/components/ion-action-sheet.js +1 -1
  5. package/components/ion-alert.js +1 -1
  6. package/components/ion-app.js +1 -1
  7. package/components/ion-avatar.js +1 -1
  8. package/components/ion-back-button.js +1 -1
  9. package/components/ion-backdrop.js +1 -1
  10. package/components/ion-badge.js +1 -1
  11. package/components/ion-breadcrumb.js +1 -1
  12. package/components/ion-breadcrumbs.js +1 -1
  13. package/components/ion-button.js +1 -1
  14. package/components/ion-buttons.js +1 -1
  15. package/components/ion-card-content.js +1 -1
  16. package/components/ion-card-header.js +1 -1
  17. package/components/ion-card-subtitle.js +1 -1
  18. package/components/ion-card-title.js +1 -1
  19. package/components/ion-card.js +1 -1
  20. package/components/ion-checkbox.js +1 -1
  21. package/components/ion-chip.js +1 -1
  22. package/components/ion-col.js +1 -1
  23. package/components/ion-content.js +1 -1
  24. package/components/ion-datetime-button.js +1 -1
  25. package/components/ion-datetime.js +1 -1
  26. package/components/ion-fab-button.js +1 -1
  27. package/components/ion-fab-list.js +1 -1
  28. package/components/ion-fab.js +1 -1
  29. package/components/ion-footer.js +1 -1
  30. package/components/ion-grid.js +1 -1
  31. package/components/ion-header.js +1 -1
  32. package/components/ion-icon.js +1 -1
  33. package/components/ion-img.js +1 -1
  34. package/components/ion-infinite-scroll-content.js +1 -1
  35. package/components/ion-infinite-scroll.js +1 -1
  36. package/components/ion-input-otp.js +1 -1
  37. package/components/ion-input-password-toggle.js +1 -1
  38. package/components/ion-input.js +1 -1
  39. package/components/ion-item-divider.js +1 -1
  40. package/components/ion-item-group.js +1 -1
  41. package/components/ion-item-option.js +1 -1
  42. package/components/ion-item-options.js +1 -1
  43. package/components/ion-item-sliding.js +1 -1
  44. package/components/ion-item.js +1 -1
  45. package/components/ion-label.js +1 -1
  46. package/components/ion-list-header.js +1 -1
  47. package/components/ion-list.js +1 -1
  48. package/components/ion-loading.js +1 -1
  49. package/components/ion-menu-button.js +1 -1
  50. package/components/ion-menu-toggle.js +1 -1
  51. package/components/ion-menu.js +1 -1
  52. package/components/ion-modal.js +1 -1
  53. package/components/ion-nav-link.js +1 -1
  54. package/components/ion-nav.js +1 -1
  55. package/components/ion-note.js +1 -1
  56. package/components/ion-picker-column-option.js +1 -1
  57. package/components/ion-picker-column.js +1 -1
  58. package/components/ion-picker.js +1 -1
  59. package/components/ion-popover.js +1 -1
  60. package/components/ion-progress-bar.js +1 -1
  61. package/components/ion-radio-group.js +1 -1
  62. package/components/ion-radio.js +1 -1
  63. package/components/ion-range.js +1 -1
  64. package/components/ion-refresher-content.js +1 -1
  65. package/components/ion-refresher.js +1 -1
  66. package/components/ion-reorder-group.js +1 -1
  67. package/components/ion-reorder.js +1 -1
  68. package/components/ion-ripple-effect.js +1 -1
  69. package/components/ion-route-redirect.js +1 -1
  70. package/components/ion-route.js +1 -1
  71. package/components/ion-router-link.js +1 -1
  72. package/components/ion-router-outlet.js +1 -1
  73. package/components/ion-router.js +1 -1
  74. package/components/ion-row.js +1 -1
  75. package/components/ion-searchbar.js +1 -1
  76. package/components/ion-segment-button.js +1 -1
  77. package/components/ion-segment-content.js +1 -1
  78. package/components/ion-segment-view.js +1 -1
  79. package/components/ion-segment.js +1 -1
  80. package/components/ion-select-modal.js +1 -1
  81. package/components/ion-select-option.js +1 -1
  82. package/components/ion-select-popover.js +1 -1
  83. package/components/ion-select.js +1 -1
  84. package/components/ion-skeleton-text.js +1 -1
  85. package/components/ion-spinner.js +1 -1
  86. package/components/ion-split-pane.js +1 -1
  87. package/components/ion-tab-bar.js +1 -1
  88. package/components/ion-tab-button.js +1 -1
  89. package/components/ion-tab.js +1 -1
  90. package/components/ion-tabs.js +1 -1
  91. package/components/ion-text.js +1 -1
  92. package/components/ion-textarea.js +1 -1
  93. package/components/ion-thumbnail.js +1 -1
  94. package/components/ion-title.js +1 -1
  95. package/components/ion-toast.js +1 -1
  96. package/components/ion-toggle.js +1 -1
  97. package/components/ion-toolbar.js +1 -1
  98. package/components/{p-CpYeYl3u.js → p-1sJ0ZDPe.js} +1 -1
  99. package/components/{p-RRH-7cpd.js → p-5ldEpNdG.js} +1 -1
  100. package/{dist/ionic/p-CoEa7CiN.js → components/p-6ZLn6K5e.js} +1 -1
  101. package/components/{p-BqE7e5gH.js → p-B4lxwJ8N.js} +1 -1
  102. package/components/{p-BAJ7NctL.js → p-BF48Oc0v.js} +1 -1
  103. package/components/{p-C8mPeq0D.js → p-BJr2B5hZ.js} +1 -1
  104. package/components/p-BNBnVi_1.js +4 -0
  105. package/components/p-BNIsryb2.js +4 -0
  106. package/components/{p-CJTKYpZv.js → p-BQyuFxYc.js} +1 -1
  107. package/components/{p-QgdyH-Zs.js → p-BT9-hMMz.js} +1 -1
  108. package/components/{p-BE9noJ1O.js → p-BWoa-cki.js} +1 -1
  109. package/components/{p-C12SAK00.js → p-BYJRZKHa.js} +1 -1
  110. package/{dist/ionic/p-DoUx_N0R.js → components/p-BgkOsq-d.js} +1 -1
  111. package/components/{p-BhlP16sB.js → p-Bi-it1EH.js} +1 -1
  112. package/components/p-C-NbvXu4.js +4 -0
  113. package/components/{p-Dcxr2vKX.js → p-C0493tbX.js} +1 -1
  114. package/components/{p-mXsQnyWB.js → p-C09TXohi.js} +1 -1
  115. package/{dist/ionic/p-C10K-FJg.js → components/p-C4VpVfrJ.js} +1 -1
  116. package/components/p-CG_zZq_I.js +4 -0
  117. package/components/{p-7qDu1bID.js → p-CJgnoKcN.js} +1 -1
  118. package/components/{p-CEkamjav.js → p-CMRYD_yR.js} +1 -1
  119. package/components/{p-CyKlfXdt.js → p-CNLioZ3B.js} +1 -1
  120. package/components/{p-CBhGZ6rF.js → p-COO0eJOA.js} +1 -1
  121. package/components/p-CWAks_S_.js +4 -0
  122. package/components/p-CWuIRJQN.js +4 -0
  123. package/components/{p-Bcm-GVQM.js → p-Cd1WaN-0.js} +1 -1
  124. package/components/{p-DMkSsriG.js → p-CqbGBmqb.js} +1 -1
  125. package/components/p-CrAKlX71.js +4 -0
  126. package/components/{p-CdSLe3Q3.js → p-Cw6WocLJ.js} +1 -1
  127. package/components/{p-CDKY4TZ_.js → p-D5w_FlVa.js} +1 -1
  128. package/{dist/ionic/p-wdSUbx5C.js → components/p-D7wDJdFn.js} +1 -1
  129. package/components/p-DFLJvuRK.js +4 -0
  130. package/components/p-DGPjJNOU.js +4 -0
  131. package/components/p-DJR0R0e1.js +4 -0
  132. package/components/{p-l-zxxFre.js → p-DL13ieTa.js} +1 -1
  133. package/components/{p-BWHBdLfY.js → p-DM1gJSMP.js} +1 -1
  134. package/components/{p-CLZPNP7x.js → p-DMBiAyOR.js} +1 -1
  135. package/components/{p-VTfnz_8u.js → p-DZRZic9C.js} +1 -1
  136. package/components/{p-CQxPXWNM.js → p-DhNuxvJH.js} +1 -1
  137. package/components/p-DiuOa1vw.js +4 -0
  138. package/components/{p-DkEb3SCT.js → p-Dj40C8gm.js} +1 -1
  139. package/components/p-DnBNYc7u.js +4 -0
  140. package/components/{p-D6dNJoqU.js → p-DngGb-NV.js} +1 -1
  141. package/components/{p-BFwL2yyK.js → p-DxhHlJln.js} +1 -1
  142. package/components/{p-COxY7PLK.js → p-GGbIHyEF.js} +1 -1
  143. package/components/{p-B2L09UOs.js → p-Hp8t4RMX.js} +1 -1
  144. package/components/{p-D6PwfJ9F.js → p-M34T2gyX.js} +1 -1
  145. package/components/{p-5rFO7X8J.js → p-TWw4e4sO.js} +1 -1
  146. package/components/p-YjMkHuvH.js +4 -0
  147. package/components/{p-CB06XtlF.js → p-cEN43Ahr.js} +1 -1
  148. package/{dist/ionic/p-BpG7jkT9.js → components/p-qAXsfUff.js} +1 -1
  149. package/dist/cjs/{animation-ihPP_z2n.js → animation-Kd1N7WTN.js} +1 -1
  150. package/dist/cjs/{app-globals-BlkzA410.js → app-globals-b1G4t26O.js} +1 -1
  151. package/dist/cjs/{button-active-ItGBjzmO.js → button-active-CQCFubJJ.js} +1 -1
  152. package/dist/cjs/{format-CPIqtp4x.js → format-bHU8N1g5.js} +1 -1
  153. package/dist/cjs/{framework-delegate-DhyWUPGg.js → framework-delegate-d-fWfeI6.js} +1 -1
  154. package/dist/cjs/{hardware-back-button-D5f_kASb.js → hardware-back-button-Bu4Dzw8l.js} +1 -1
  155. package/dist/cjs/{helpers-BCLkFTTd.js → helpers-7wzdhdeg.js} +1 -1
  156. package/dist/cjs/{index-BocV_y-c.js → index-BbqUBe2T.js} +1 -1
  157. package/dist/cjs/{index-xum7jdbq.js → index-C74Q3t6a.js} +2 -2
  158. package/dist/cjs/{index-CldjPPyP.js → index-CQfEIyLK.js} +4 -4
  159. package/dist/cjs/{index-BcyiqD6R.js → index-DFNrCX7M.js} +5 -5
  160. package/dist/cjs/{index-Bgz6BcKx.js → index-DXoo08JC.js} +9 -2
  161. package/dist/cjs/{index-49DTLc_Z.js → index-DtvKGwdh.js} +2 -2
  162. package/dist/cjs/index.cjs.js +12 -12
  163. package/dist/cjs/{input-shims-Jisy7U7-.js → input-shims-VJIiKBKv.js} +3 -3
  164. package/dist/cjs/{input.utils-BDTpGrfn.js → input.utils-EHVE2RK_.js} +1 -1
  165. package/dist/cjs/ion-accordion_2.cjs.entry.js +3 -3
  166. package/dist/cjs/ion-action-sheet.cjs.entry.js +45 -29
  167. package/dist/cjs/ion-alert.cjs.entry.js +29 -21
  168. package/dist/cjs/ion-app_8.cjs.entry.js +12 -12
  169. package/dist/cjs/ion-avatar_3.cjs.entry.js +2 -2
  170. package/dist/cjs/ion-back-button.cjs.entry.js +3 -3
  171. package/dist/cjs/ion-backdrop.cjs.entry.js +2 -2
  172. package/dist/cjs/ion-breadcrumb_2.cjs.entry.js +3 -3
  173. package/dist/cjs/ion-button_2.cjs.entry.js +3 -3
  174. package/dist/cjs/ion-card_5.cjs.entry.js +3 -3
  175. package/dist/cjs/ion-checkbox.cjs.entry.js +4 -4
  176. package/dist/cjs/ion-chip.cjs.entry.js +2 -2
  177. package/dist/cjs/ion-col_3.cjs.entry.js +2 -2
  178. package/dist/cjs/ion-datetime-button.cjs.entry.js +4 -4
  179. package/dist/cjs/ion-datetime.cjs.entry.js +7 -7
  180. package/dist/cjs/ion-fab_3.cjs.entry.js +3 -3
  181. package/dist/cjs/ion-img.cjs.entry.js +3 -3
  182. package/dist/cjs/ion-infinite-scroll_2.cjs.entry.js +5 -5
  183. package/dist/cjs/ion-input-otp.cjs.entry.js +3 -3
  184. package/dist/cjs/ion-input-password-toggle.cjs.entry.js +2 -2
  185. package/dist/cjs/ion-input.cjs.entry.js +5 -5
  186. package/dist/cjs/ion-item-option_3.cjs.entry.js +4 -4
  187. package/dist/cjs/ion-item_8.cjs.entry.js +3 -3
  188. package/dist/cjs/ion-loading.cjs.entry.js +14 -10
  189. package/dist/cjs/ion-menu_3.cjs.entry.js +8 -8
  190. package/dist/cjs/ion-modal.cjs.entry.js +83 -30
  191. package/dist/cjs/ion-nav_2.cjs.entry.js +6 -6
  192. package/dist/cjs/ion-picker-column-option.cjs.entry.js +3 -3
  193. package/dist/cjs/ion-picker-column.cjs.entry.js +3 -3
  194. package/dist/cjs/ion-picker.cjs.entry.js +2 -2
  195. package/dist/cjs/ion-popover.cjs.entry.js +23 -10
  196. package/dist/cjs/ion-progress-bar.cjs.entry.js +3 -3
  197. package/dist/cjs/ion-radio_2.cjs.entry.js +4 -4
  198. package/dist/cjs/ion-range.cjs.entry.js +4 -4
  199. package/dist/cjs/ion-refresher_2.cjs.entry.js +6 -6
  200. package/dist/cjs/ion-reorder_2.cjs.entry.js +4 -4
  201. package/dist/cjs/ion-ripple-effect.cjs.entry.js +2 -2
  202. package/dist/cjs/ion-route_4.cjs.entry.js +4 -4
  203. package/dist/cjs/ion-searchbar.cjs.entry.js +3 -3
  204. package/dist/cjs/ion-segment-content.cjs.entry.js +1 -1
  205. package/dist/cjs/ion-segment-view.cjs.entry.js +1 -1
  206. package/dist/cjs/ion-segment_2.cjs.entry.js +3 -3
  207. package/dist/cjs/ion-select-modal.cjs.entry.js +8 -8
  208. package/dist/cjs/ion-select_3.cjs.entry.js +9 -9
  209. package/dist/cjs/ion-spinner.cjs.entry.js +2 -2
  210. package/dist/cjs/ion-split-pane.cjs.entry.js +2 -2
  211. package/dist/cjs/ion-tab-bar_2.cjs.entry.js +3 -3
  212. package/dist/cjs/ion-tab_2.cjs.entry.js +3 -3
  213. package/dist/cjs/ion-text.cjs.entry.js +2 -2
  214. package/dist/cjs/ion-textarea.cjs.entry.js +5 -5
  215. package/dist/cjs/ion-toast.cjs.entry.js +8 -8
  216. package/dist/cjs/ion-toggle.cjs.entry.js +4 -4
  217. package/dist/cjs/{ionic-global-BMU-Us1Q.js → ionic-global-CYJNT-2P.js} +1 -1
  218. package/dist/cjs/ionic.cjs.js +4 -4
  219. package/dist/cjs/{ios.transition-OCcCN--B.js → ios.transition-DYGiD6gM.js} +4 -4
  220. package/dist/cjs/{item-multiple-inputs-BH9yPC66.js → item-multiple-inputs-Dv6qrMhm.js} +1 -1
  221. package/dist/cjs/loader.cjs.js +3 -3
  222. package/dist/cjs/{md.transition-D0o2IIbo.js → md.transition-CrSkaoz6.js} +4 -4
  223. package/dist/cjs/{overlays-hCtsdAvn.js → overlays-BR21NUIG.js} +59 -34
  224. package/dist/cjs/{select-option-render-BPjGDlsG.js → select-option-render-CrbLc-1j.js} +2 -2
  225. package/dist/cjs/{slot-mutation-controller-IQ250LoG.js → slot-mutation-controller-D6IjSEIh.js} +1 -1
  226. package/dist/cjs/{status-tap-Bakl9czs.js → status-tap-CNP5eD1R.js} +3 -3
  227. package/dist/cjs/{swipe-back-Cv4OZVJ5.js → swipe-back-BYb_nLJe.js} +2 -2
  228. package/dist/collection/collection-manifest.json +1 -1
  229. package/dist/collection/components/action-sheet/action-sheet.js +36 -20
  230. package/dist/collection/components/alert/alert.js +20 -12
  231. package/dist/collection/components/loading/loading.js +7 -3
  232. package/dist/collection/components/modal/modal.js +75 -22
  233. package/dist/collection/components/popover/popover.js +16 -3
  234. package/dist/collection/utils/overlays.js +53 -29
  235. package/dist/collection/utils/test/playwright/detach-reattach.js +23 -0
  236. package/dist/collection/utils/test/playwright/index.js +1 -0
  237. package/dist/docs.json +2 -2
  238. package/dist/esm/{animation-B6KGUJwl.js → animation-BFNVlYum.js} +1 -1
  239. package/dist/esm/{app-globals-BvutUX8P.js → app-globals-BgX0BGL1.js} +1 -1
  240. package/dist/esm/{button-active-DRNebs9r.js → button-active-BfOvDr3b.js} +1 -1
  241. package/dist/esm/{format-BF3btM7P.js → format-DDFE9jM5.js} +1 -1
  242. package/dist/esm/{framework-delegate-60cEIAgt.js → framework-delegate-BwNhx9NR.js} +1 -1
  243. package/dist/esm/{hardware-back-button-rhkwRNk2.js → hardware-back-button-DmkiRgOT.js} +1 -1
  244. package/dist/esm/{helpers-DLLANtRS.js → helpers-BSmc0nst.js} +1 -1
  245. package/dist/esm/{index-D_7MeuVD.js → index-BuPaALjU.js} +5 -5
  246. package/dist/esm/{index-DOrtAUWV.js → index-C1b9CLFg.js} +2 -2
  247. package/dist/esm/{index-h81G-QbJ.js → index-C23AVPx9.js} +9 -2
  248. package/dist/esm/{index-Bl4BUvBL.js → index-CJln0gS3.js} +2 -2
  249. package/dist/esm/{index-JgU5uTSY.js → index-CqoUoxgG.js} +1 -1
  250. package/dist/esm/{index-BZHVn0iF.js → index-DnA_7Bnx.js} +4 -4
  251. package/dist/esm/index.js +12 -12
  252. package/dist/esm/{input-shims-C2ipc_XX.js → input-shims-Dqk-Ou25.js} +3 -3
  253. package/dist/esm/{input.utils-D-qoPjqr.js → input.utils-rqoH22pW.js} +1 -1
  254. package/dist/esm/ion-accordion_2.entry.js +3 -3
  255. package/dist/esm/ion-action-sheet.entry.js +45 -29
  256. package/dist/esm/ion-alert.entry.js +29 -21
  257. package/dist/esm/ion-app_8.entry.js +12 -12
  258. package/dist/esm/ion-avatar_3.entry.js +2 -2
  259. package/dist/esm/ion-back-button.entry.js +3 -3
  260. package/dist/esm/ion-backdrop.entry.js +2 -2
  261. package/dist/esm/ion-breadcrumb_2.entry.js +3 -3
  262. package/dist/esm/ion-button_2.entry.js +3 -3
  263. package/dist/esm/ion-card_5.entry.js +3 -3
  264. package/dist/esm/ion-checkbox.entry.js +4 -4
  265. package/dist/esm/ion-chip.entry.js +2 -2
  266. package/dist/esm/ion-col_3.entry.js +2 -2
  267. package/dist/esm/ion-datetime-button.entry.js +4 -4
  268. package/dist/esm/ion-datetime.entry.js +7 -7
  269. package/dist/esm/ion-fab_3.entry.js +3 -3
  270. package/dist/esm/ion-img.entry.js +3 -3
  271. package/dist/esm/ion-infinite-scroll_2.entry.js +5 -5
  272. package/dist/esm/ion-input-otp.entry.js +3 -3
  273. package/dist/esm/ion-input-password-toggle.entry.js +2 -2
  274. package/dist/esm/ion-input.entry.js +5 -5
  275. package/dist/esm/ion-item-option_3.entry.js +4 -4
  276. package/dist/esm/ion-item_8.entry.js +3 -3
  277. package/dist/esm/ion-loading.entry.js +14 -10
  278. package/dist/esm/ion-menu_3.entry.js +8 -8
  279. package/dist/esm/ion-modal.entry.js +83 -30
  280. package/dist/esm/ion-nav_2.entry.js +6 -6
  281. package/dist/esm/ion-picker-column-option.entry.js +3 -3
  282. package/dist/esm/ion-picker-column.entry.js +3 -3
  283. package/dist/esm/ion-picker.entry.js +2 -2
  284. package/dist/esm/ion-popover.entry.js +23 -10
  285. package/dist/esm/ion-progress-bar.entry.js +3 -3
  286. package/dist/esm/ion-radio_2.entry.js +4 -4
  287. package/dist/esm/ion-range.entry.js +4 -4
  288. package/dist/esm/ion-refresher_2.entry.js +6 -6
  289. package/dist/esm/ion-reorder_2.entry.js +4 -4
  290. package/dist/esm/ion-ripple-effect.entry.js +2 -2
  291. package/dist/esm/ion-route_4.entry.js +4 -4
  292. package/dist/esm/ion-searchbar.entry.js +3 -3
  293. package/dist/esm/ion-segment-content.entry.js +1 -1
  294. package/dist/esm/ion-segment-view.entry.js +1 -1
  295. package/dist/esm/ion-segment_2.entry.js +3 -3
  296. package/dist/esm/ion-select-modal.entry.js +8 -8
  297. package/dist/esm/ion-select_3.entry.js +9 -9
  298. package/dist/esm/ion-spinner.entry.js +2 -2
  299. package/dist/esm/ion-split-pane.entry.js +2 -2
  300. package/dist/esm/ion-tab-bar_2.entry.js +3 -3
  301. package/dist/esm/ion-tab_2.entry.js +3 -3
  302. package/dist/esm/ion-text.entry.js +2 -2
  303. package/dist/esm/ion-textarea.entry.js +5 -5
  304. package/dist/esm/ion-toast.entry.js +8 -8
  305. package/dist/esm/ion-toggle.entry.js +4 -4
  306. package/dist/esm/{ionic-global-B7I1sdvg.js → ionic-global-ZXjCXKH0.js} +1 -1
  307. package/dist/esm/ionic.js +5 -5
  308. package/dist/esm/{ios.transition-C-6IaIxe.js → ios.transition-CWAgfQ1r.js} +4 -4
  309. package/dist/esm/{item-multiple-inputs-DI91syHS.js → item-multiple-inputs-DZnIiOO_.js} +1 -1
  310. package/dist/esm/loader.js +4 -4
  311. package/dist/esm/{md.transition-dyqt0t0d.js → md.transition-ZuqDDdDW.js} +4 -4
  312. package/dist/esm/{overlays-CiFoZEq1.js → overlays-DKE0DgvE.js} +59 -35
  313. package/dist/esm/{select-option-render-CUJpruSC.js → select-option-render-1W6W9pbC.js} +2 -2
  314. package/dist/esm/{slot-mutation-controller-wFuztlVD.js → slot-mutation-controller-B5NpUZ-N.js} +1 -1
  315. package/dist/esm/{status-tap-Dn_42wiy.js → status-tap-Cv9gb3ja.js} +3 -3
  316. package/dist/esm/{swipe-back-CqOqrHW7.js → swipe-back-B2CmabKY.js} +2 -2
  317. package/dist/ionic/index.esm.js +1 -1
  318. package/dist/ionic/ionic.esm.js +1 -1
  319. package/dist/ionic/{p-e5a54d99.entry.js → p-06c6b540.entry.js} +1 -1
  320. package/dist/ionic/{p-dadcb3d2.entry.js → p-099b7f9f.entry.js} +1 -1
  321. package/dist/ionic/{p-e090eca3.entry.js → p-171a2278.entry.js} +1 -1
  322. package/dist/ionic/{p-2aabd568.entry.js → p-191a651e.entry.js} +1 -1
  323. package/dist/ionic/{p-1f66c230.entry.js → p-1f24cb24.entry.js} +1 -1
  324. package/dist/ionic/{p-83aa7d47.entry.js → p-271c3640.entry.js} +1 -1
  325. package/dist/ionic/{p-5dc53bdf.entry.js → p-2c52fe3e.entry.js} +1 -1
  326. package/dist/ionic/{p-da5a7bcd.entry.js → p-2ea4bb36.entry.js} +1 -1
  327. package/dist/ionic/{p-fe80ebb1.entry.js → p-338a02e0.entry.js} +1 -1
  328. package/dist/ionic/{p-326e0d05.entry.js → p-354d38d3.entry.js} +1 -1
  329. package/dist/ionic/{p-0b52d921.entry.js → p-38b9375b.entry.js} +1 -1
  330. package/dist/ionic/{p-a2fccdaa.entry.js → p-3bba83ee.entry.js} +1 -1
  331. package/dist/ionic/{p-cc749877.entry.js → p-4712a8a9.entry.js} +1 -1
  332. package/dist/ionic/{p-ca57763d.entry.js → p-4c3e50f8.entry.js} +1 -1
  333. package/dist/ionic/p-4e2fef42.entry.js +4 -0
  334. package/dist/ionic/{p-2ff12c8a.entry.js → p-54cbb0a7.entry.js} +1 -1
  335. package/dist/ionic/{p-5ed60413.entry.js → p-561af487.entry.js} +1 -1
  336. package/dist/ionic/{p-ecfc9c71.entry.js → p-5906ea6f.entry.js} +1 -1
  337. package/dist/ionic/{p-9f2d3987.entry.js → p-5cc09b40.entry.js} +1 -1
  338. package/dist/ionic/{p-9eba0d8a.entry.js → p-5d9094ac.entry.js} +1 -1
  339. package/dist/ionic/{p-986a1db8.entry.js → p-612ea3b1.entry.js} +1 -1
  340. package/dist/ionic/p-66f150ec.entry.js +4 -0
  341. package/dist/ionic/{p-d1539609.entry.js → p-68cc89a6.entry.js} +1 -1
  342. package/dist/ionic/{p-f607ad4f.entry.js → p-6e386853.entry.js} +1 -1
  343. package/dist/ionic/p-77c96e08.entry.js +4 -0
  344. package/dist/ionic/{p-9e688396.entry.js → p-7bfeccb3.entry.js} +1 -1
  345. package/dist/ionic/{p-6d84fa9f.entry.js → p-7e394cc3.entry.js} +1 -1
  346. package/dist/ionic/{p-fb4dabbb.entry.js → p-7fbb3319.entry.js} +1 -1
  347. package/dist/ionic/{p-7c859f42.entry.js → p-85599fe3.entry.js} +1 -1
  348. package/dist/ionic/{p-53ba5651.entry.js → p-8b80d9f9.entry.js} +1 -1
  349. package/dist/ionic/{p-1a20571c.entry.js → p-90f50a65.entry.js} +1 -1
  350. package/dist/ionic/{p-392079fa.entry.js → p-9595e305.entry.js} +1 -1
  351. package/dist/ionic/{p-e93cc1e1.entry.js → p-9c5f2691.entry.js} +1 -1
  352. package/dist/ionic/p-BY6BzzPF.js +4 -0
  353. package/dist/ionic/{p-BRc8gq5v.js → p-BaeKWZ-a.js} +1 -1
  354. package/dist/ionic/p-Bd72UYI2.js +4 -0
  355. package/dist/ionic/{p-C2BY_4Wp.js → p-BgOvhHy_.js} +1 -1
  356. package/dist/ionic/{p-KJ5pLPiN.js → p-BijjLYjT.js} +1 -1
  357. package/dist/ionic/{p-h81G-QbJ.js → p-C23AVPx9.js} +1 -1
  358. package/dist/ionic/p-C8XilMh0.js +4 -0
  359. package/dist/ionic/{p-Cc-fFNy8.js → p-CER8qW8Y.js} +1 -1
  360. package/dist/ionic/{p-06ZcwRF5.js → p-CFuOr1Tu.js} +1 -1
  361. package/dist/ionic/p-CJv-wOAd.js +4 -0
  362. package/dist/ionic/p-CMZYmFb4.js +4 -0
  363. package/dist/ionic/{p-BC17SPKH.js → p-CkxVYPtX.js} +1 -1
  364. package/dist/ionic/{p-DMj-M2c4.js → p-CmXuiRT6.js} +1 -1
  365. package/dist/ionic/p-D4OrdtJf.js +4 -0
  366. package/dist/ionic/p-D6rHcCvc.js +4 -0
  367. package/{components/p-DG-dUCKp.js → dist/ionic/p-DLM95R4x.js} +1 -1
  368. package/{components/p-DH0rRXjx.js → dist/ionic/p-DTxBioUO.js} +1 -1
  369. package/{components/p-BcuLPPAE.js → dist/ionic/p-DYUjHcuH.js} +1 -1
  370. package/dist/ionic/{p-DA88iqXQ.js → p-DZ6Hmc-8.js} +1 -1
  371. package/dist/ionic/p-D_gb-kdd.js +4 -0
  372. package/dist/ionic/{p-S1shXW5M.js → p-DajLgJ4d.js} +1 -1
  373. package/dist/ionic/{p-CTBfxfjC.js → p-Do3zTvCA.js} +1 -1
  374. package/dist/ionic/{p-5fce4bf4.entry.js → p-a1cc2861.entry.js} +1 -1
  375. package/dist/ionic/{p-4dcd329a.entry.js → p-a20a2ebe.entry.js} +1 -1
  376. package/dist/ionic/{p-d1b888de.entry.js → p-a8ff2489.entry.js} +1 -1
  377. package/dist/ionic/{p-b8cd338d.entry.js → p-b3532b8d.entry.js} +1 -1
  378. package/dist/ionic/{p-8d72244b.entry.js → p-b80c86c4.entry.js} +1 -1
  379. package/dist/ionic/{p-43bae3f5.entry.js → p-b9b4e85d.entry.js} +1 -1
  380. package/dist/ionic/p-c57fc396.entry.js +4 -0
  381. package/dist/ionic/{p-082b8125.entry.js → p-ca1b8865.entry.js} +1 -1
  382. package/dist/ionic/{p-eab77a74.entry.js → p-cc4320d3.entry.js} +1 -1
  383. package/dist/ionic/{p-f0645c98.entry.js → p-d6755739.entry.js} +1 -1
  384. package/dist/ionic/{p-ac71362a.entry.js → p-d8c138d9.entry.js} +1 -1
  385. package/dist/ionic/{p-026e2e4c.entry.js → p-dd01be0a.entry.js} +1 -1
  386. package/dist/ionic/p-e7f4b1e0.entry.js +4 -0
  387. package/dist/ionic/{p-eaeac54e.entry.js → p-ec00fd0d.entry.js} +1 -1
  388. package/dist/ionic/{p-546b1109.entry.js → p-ee09ac9d.entry.js} +1 -1
  389. package/dist/ionic/{p-8e388a30.entry.js → p-efc0cf82.entry.js} +1 -1
  390. package/dist/ionic/{p-47b8b59a.entry.js → p-f0fb23c1.entry.js} +1 -1
  391. package/dist/ionic/{p-9452849d.entry.js → p-f83b3e0c.entry.js} +1 -1
  392. package/dist/ionic/{p-CH8tk7Pr.js → p-fU3rOYad.js} +1 -1
  393. package/dist/ionic/{p-eeb65463.entry.js → p-fe9ed9bd.entry.js} +1 -1
  394. package/{components/p-ntAkt4Ra.js → dist/ionic/p-zWf7R2xV.js} +1 -1
  395. package/dist/types/components/action-sheet/action-sheet.d.ts +10 -0
  396. package/dist/types/components/alert/alert.d.ts +7 -0
  397. package/dist/types/components/modal/modal.d.ts +8 -0
  398. package/dist/types/utils/overlays.d.ts +9 -0
  399. package/dist/types/utils/test/playwright/detach-reattach.d.ts +13 -0
  400. package/dist/types/utils/test/playwright/index.d.ts +1 -0
  401. package/hydrate/index.js +204 -88
  402. package/hydrate/index.mjs +204 -88
  403. package/package.json +3 -3
  404. package/components/p-CFtAWaB6.js +0 -4
  405. package/components/p-CRcX0EGs.js +0 -4
  406. package/components/p-CXQzYyda.js +0 -4
  407. package/components/p-CXpy69Ei.js +0 -4
  408. package/components/p-CoGD-oMG.js +0 -4
  409. package/components/p-D8BOfNYz.js +0 -4
  410. package/components/p-Dr-rBSUW.js +0 -4
  411. package/components/p-FQTgh7aH.js +0 -4
  412. package/components/p-KusMkAnB.js +0 -4
  413. package/components/p-MHpJ2A_m.js +0 -4
  414. package/components/p-U1X_lw_6.js +0 -4
  415. package/components/p-UKksD3f9.js +0 -4
  416. package/components/p-fP0QF3iG.js +0 -4
  417. package/components/p-ufB0gCSU.js +0 -4
  418. package/dist/ionic/p-39415b06.entry.js +0 -4
  419. package/dist/ionic/p-3bad6750.entry.js +0 -4
  420. package/dist/ionic/p-70334101.entry.js +0 -4
  421. package/dist/ionic/p-8db6e422.entry.js +0 -4
  422. package/dist/ionic/p-BRa1Jjxr.js +0 -4
  423. package/dist/ionic/p-Cc4w3fVf.js +0 -4
  424. package/dist/ionic/p-CvRL-Eju.js +0 -4
  425. package/dist/ionic/p-DA52akcQ.js +0 -4
  426. package/dist/ionic/p-D_IgV7tz.js +0 -4
  427. package/dist/ionic/p-Q9w8L_Sw.js +0 -4
  428. package/dist/ionic/p-b62c04b2.entry.js +0 -4
  429. package/dist/ionic/p-w3KTiug9.js +0 -4
@@ -5,7 +5,7 @@ import { Host, h, readTask } from "@stencil/core";
5
5
  import { createButtonActiveGesture } from "../../utils/gesture/button-active";
6
6
  import { raf } from "../../utils/helpers";
7
7
  import { createLockController } from "../../utils/lock-controller";
8
- import { BACKDROP, cleanupRootFocusTrapAccessibility, createDelegateController, createTriggerController, dismiss, eventMethod, isCancel, prepareOverlay, present, safeCall, setOverlayId, } from "../../utils/overlays";
8
+ import { BACKDROP, cleanupRootFocusTrapAccessibility, createDelegateController, createTriggerController, dismiss, eventMethod, isCancel, prepareOverlay, present, restoreRootFocusTrapAccessibility, safeCall, setOverlayId, } from "../../utils/overlays";
9
9
  import { renderOptionLabel } from "../../utils/select-option-render";
10
10
  import { getClassMap } from "../../utils/theme";
11
11
  import { getIonMode } from "../../global/ionic-global";
@@ -277,6 +277,13 @@ export class ActionSheet {
277
277
  connectedCallback() {
278
278
  prepareOverlay(this.el);
279
279
  this.triggerChanged();
280
+ // `componentDidLoad` only fires once per instance, so a reconnect has to
281
+ // rebuild the gesture its disconnect destroyed.
282
+ this.setupButtonActiveGesture();
283
+ // Re-apply the root lock if moved without dismiss() being called
284
+ if (this.presented) {
285
+ restoreRootFocusTrapAccessibility(this.el);
286
+ }
280
287
  }
281
288
  disconnectedCallback() {
282
289
  if (this.gesture) {
@@ -296,24 +303,33 @@ export class ActionSheet {
296
303
  // Initialize activeRadioId for radio buttons
297
304
  this.buttonsChanged();
298
305
  }
299
- componentDidLoad() {
300
- /**
301
- * Only create gesture if:
302
- * 1. A gesture does not already exist
303
- * 2. App is running in iOS mode
304
- * 3. A wrapper ref exists
305
- * 4. A group ref exists
306
- */
306
+ /**
307
+ * Only create gesture if:
308
+ * 1. A gesture does not already exist
309
+ * 2. App is running in iOS mode
310
+ * 3. A wrapper ref exists
311
+ * 4. A group ref exists
312
+ * 5. The host is still connected, since a reconnect can schedule this and
313
+ * disconnect again before the task runs
314
+ */
315
+ setupButtonActiveGesture() {
307
316
  const { groupEl, wrapperEl } = this;
308
- if (!this.gesture && getIonMode(this) === 'ios' && wrapperEl && groupEl) {
309
- readTask(() => {
310
- const isScrollable = groupEl.scrollHeight > groupEl.clientHeight;
311
- if (!isScrollable) {
312
- this.gesture = createButtonActiveGesture(wrapperEl, (refEl) => refEl.classList.contains('action-sheet-button'));
313
- this.gesture.enable(true);
314
- }
315
- });
317
+ if (getIonMode(this) !== 'ios' || !wrapperEl || !groupEl) {
318
+ return;
316
319
  }
320
+ readTask(() => {
321
+ // Bail if a call queued ahead of this one already built the gesture
322
+ // (a second would orphan the first with its listeners still bound), if
323
+ // the host disconnected while this task waited, or if the group scrolls.
324
+ if (this.gesture || !this.el.isConnected || groupEl.scrollHeight > groupEl.clientHeight) {
325
+ return;
326
+ }
327
+ this.gesture = createButtonActiveGesture(wrapperEl, (refEl) => refEl.classList.contains('action-sheet-button'));
328
+ this.gesture.enable(true);
329
+ });
330
+ }
331
+ componentDidLoad() {
332
+ this.setupButtonActiveGesture();
317
333
  /**
318
334
  * If action sheet was rendered with isOpen="true"
319
335
  * then we should open action sheet immediately.
@@ -406,17 +422,17 @@ export class ActionSheet {
406
422
  const cancelButton = allButtons.find((b) => b.role === 'cancel');
407
423
  const buttons = allButtons.filter((b) => b.role !== 'cancel');
408
424
  const headerID = `action-sheet-${overlayIndex}-header`;
409
- return (h(Host, { key: '7685831da9a78b73473246a2cea95cefa79a53c3', role: "dialog", "aria-modal": "true", "aria-labelledby": header !== undefined ? headerID : null, tabindex: "-1", ...htmlAttributes, style: {
425
+ return (h(Host, { key: '83b47ea6b81d4eec6582c1dcaae6f267c78ba634', role: "dialog", "aria-modal": "true", "aria-labelledby": header !== undefined ? headerID : null, tabindex: "-1", ...htmlAttributes, style: {
410
426
  zIndex: `${20000 + this.overlayIndex}`,
411
427
  }, class: {
412
428
  [mode]: true,
413
429
  ...getClassMap(this.cssClass),
414
430
  'overlay-hidden': true,
415
431
  'action-sheet-translucent': this.translucent,
416
- }, onIonActionSheetWillDismiss: this.dispatchCancelHandler, onIonBackdropTap: this.onBackdropTap }, h("ion-backdrop", { key: 'c70f00e401cac99b89412d1adb867f35bc855d57', tappable: this.backdropDismiss }), h("div", { key: '3332893ce3098f1435875480b1269b2821d41660', tabindex: "0", "aria-hidden": "true" }), h("div", { key: 'eb406b0a9e303d6f94c5c7aa99c6f4c089bfbd41', class: "action-sheet-wrapper ion-overlay-wrapper", ref: (el) => (this.wrapperEl = el) }, h("div", { key: '92ec855379f10e1e0bfce6ff68b45d3cda0c137c', class: "action-sheet-container" }, h("div", { key: 'bc88a396d75396f574961471bf7e6c4edb513353', class: "action-sheet-group", ref: (el) => (this.groupEl = el), role: hasRadioButtons ? 'radiogroup' : undefined }, header !== undefined && (h("div", { key: '33329e0193818888bdcaa443ff5ea301802e0787', id: headerID, class: {
432
+ }, onIonActionSheetWillDismiss: this.dispatchCancelHandler, onIonBackdropTap: this.onBackdropTap }, h("ion-backdrop", { key: '3652793ec76adcfb87b814eb65d1260ae26f6910', tappable: this.backdropDismiss }), h("div", { key: '7de3a51daf95b50e02734a2cc8eec6f55b1473f3', tabindex: "0", "aria-hidden": "true" }), h("div", { key: '9b76bbf12901b956f7ee6d984f7f71e169bb5939', class: "action-sheet-wrapper ion-overlay-wrapper", ref: (el) => (this.wrapperEl = el) }, h("div", { key: 'cb52c905275a46ff27810fb1b4748ab533a78c45', class: "action-sheet-container" }, h("div", { key: '578c7194cfbcb65c1e41e15c390af41f179a2686', class: "action-sheet-group", ref: (el) => (this.groupEl = el), role: hasRadioButtons ? 'radiogroup' : undefined }, header !== undefined && (h("div", { key: 'ee961307c25cb1fcb0c6bbf667a8ed76ab717860', id: headerID, class: {
417
433
  'action-sheet-title': true,
418
434
  'action-sheet-has-sub-title': this.subHeader !== undefined,
419
- } }, header, this.subHeader && h("div", { key: '92ac5a9217a12b7417f7aca9c4fdb7bf6b051364', class: "action-sheet-sub-title" }, this.subHeader))), this.renderActionSheetButtons(buttons)), cancelButton && (h("div", { key: '020463b830fa48fc3ae647ea64f2aaa4d0969305', class: "action-sheet-group action-sheet-group-cancel" }, h("button", { key: 'cefa9c70a2233f6652a81b1a2eae6e717712144d', ...cancelButton.htmlAttributes, type: "button", class: buttonClass(cancelButton), onClick: () => this.buttonClick(cancelButton) }, h("span", { key: '56d7e2f8bfb3644e4476895164d334445fda9b2f', class: "action-sheet-button-inner" }, cancelButton.icon && (h("ion-icon", { key: '6a4a49254ec5cd96f4e0dce9447f90d8cac81913', icon: cancelButton.icon, "aria-hidden": "true", lazy: false, class: "action-sheet-icon" })), cancelButton.text), mode === 'md' && h("ion-ripple-effect", { key: '7d3c6e8d70b0922ea0f75520856285ce1005c891' })))))), h("div", { key: '958fc43e603c6a7f5d1e649d3326fce0f163b5d0', tabindex: "0", "aria-hidden": "true" })));
435
+ } }, header, this.subHeader && h("div", { key: '82d4016010db14d3f3b9911c276cb9a63b960ef4', class: "action-sheet-sub-title" }, this.subHeader))), this.renderActionSheetButtons(buttons)), cancelButton && (h("div", { key: 'a88fbfab194e13c6642a65f2e19a12299dd4ac23', class: "action-sheet-group action-sheet-group-cancel" }, h("button", { key: 'a232b8d23f1fc2efb7b37a605102b6994761a2f6', ...cancelButton.htmlAttributes, type: "button", class: buttonClass(cancelButton), onClick: () => this.buttonClick(cancelButton) }, h("span", { key: '19a7d95d3428d78a56b7dacabdec8fc0b722d8c2', class: "action-sheet-button-inner" }, cancelButton.icon && (h("ion-icon", { key: '5f3f3cb996a48e6365a3a09ea72ddc68ac9ccb78', icon: cancelButton.icon, "aria-hidden": "true", lazy: false, class: "action-sheet-icon" })), cancelButton.text), mode === 'md' && h("ion-ripple-effect", { key: '6e8fcb5f103ac19aa4b0682f19791cef0a147001' })))))), h("div", { key: '36f63ab77949c88f662e2cf6bb18510cb9de6a9f', tabindex: "0", "aria-hidden": "true" })));
420
436
  }
421
437
  static get is() { return "ion-action-sheet"; }
422
438
  static get encapsulation() { return "scoped"; }
@@ -8,7 +8,7 @@ import { raf } from "../../utils/helpers";
8
8
  import { createLockController } from "../../utils/lock-controller";
9
9
  import { printIonWarning } from "../../utils/logging/index";
10
10
  import { getOverlayLabelJustify, getOverlayLabelPlacement } from "../../utils/overlay-control-label";
11
- import { BACKDROP, cleanupRootFocusTrapAccessibility, createDelegateController, createTriggerController, dismiss, eventMethod, isCancel, prepareOverlay, present, safeCall, setOverlayId, } from "../../utils/overlays";
11
+ import { BACKDROP, cleanupRootFocusTrapAccessibility, createDelegateController, createTriggerController, dismiss, eventMethod, isCancel, prepareOverlay, present, restoreRootFocusTrapAccessibility, safeCall, setOverlayId, } from "../../utils/overlays";
12
12
  import { sanitizeDOMString } from "../../utils/sanitization/index";
13
13
  import { renderOptionLabel } from "../../utils/select-option-render";
14
14
  import { getClassMap } from "../../utils/theme";
@@ -198,10 +198,15 @@ export class Alert {
198
198
  this.triggerChanged();
199
199
  /**
200
200
  * If the alert was previously connected and is being reattached, the
201
- * ResizeObserver was disconnected. componentDidLoad only fires once per
202
- * instance, so re-establish the observer here on reconnect.
201
+ * `ResizeObserver` and the button gesture were torn down. `componentDidLoad`
202
+ * only fires once per instance, so re-establish both here on reconnect.
203
203
  */
204
204
  this.setupButtonGroupResizeObserver();
205
+ this.setupButtonActiveGesture();
206
+ // Re-apply the root lock if moved without dismiss() being called
207
+ if (this.presented) {
208
+ restoreRootFocusTrapAccessibility(this.el);
209
+ }
205
210
  }
206
211
  componentWillLoad() {
207
212
  if (!this.htmlAttributes?.id) {
@@ -223,17 +228,20 @@ export class Alert {
223
228
  this.buttonGroupResizeObserver?.disconnect();
224
229
  this.buttonGroupResizeObserver = undefined;
225
230
  }
226
- componentDidLoad() {
227
- /**
228
- * Only create gesture if:
229
- * 1. A gesture does not already exist
230
- * 2. App is running in iOS mode
231
- * 3. A wrapper ref exists
232
- */
231
+ /**
232
+ * Only create gesture if:
233
+ * 1. A gesture does not already exist
234
+ * 2. App is running in iOS mode
235
+ * 3. A wrapper ref exists
236
+ */
237
+ setupButtonActiveGesture() {
233
238
  if (!this.gesture && getIonMode(this) === 'ios' && this.wrapperEl) {
234
239
  this.gesture = createButtonActiveGesture(this.wrapperEl, (refEl) => refEl.classList.contains('alert-button'));
235
240
  this.gesture.enable(true);
236
241
  }
242
+ }
243
+ componentDidLoad() {
244
+ this.setupButtonActiveGesture();
237
245
  this.setupButtonGroupResizeObserver();
238
246
  /**
239
247
  * If alert was rendered with isOpen="true"
@@ -556,14 +564,14 @@ export class Alert {
556
564
  * If neither are defined, do not set aria-labelledby.
557
565
  */
558
566
  const ariaLabelledBy = header && subHeader ? `${hdrId} ${subHdrId}` : header ? hdrId : subHeader ? subHdrId : null;
559
- return (h(Host, { key: '022f3463b8e6c52064b90ec304c524847c3d5ad3', tabindex: "-1", style: {
567
+ return (h(Host, { key: '083b5250dc4c76e85240e0225abcb7ca2b66de2f', tabindex: "-1", style: {
560
568
  zIndex: `${20000 + overlayIndex}`,
561
569
  }, class: {
562
570
  ...getClassMap(this.cssClass),
563
571
  [mode]: true,
564
572
  'overlay-hidden': true,
565
573
  'alert-translucent': this.translucent,
566
- }, onIonAlertWillDismiss: this.dispatchCancelHandler, onIonBackdropTap: this.onBackdropTap }, h("ion-backdrop", { key: 'c3cd05738bc2834f54707edeb2fae06ec10d6fd2', tappable: this.backdropDismiss }), h("div", { key: 'b44fadd9ba7ddf073854c5035538d11ae8e66bc2', tabindex: "0", "aria-hidden": "true" }), h("div", { key: 'fafea048141a6c7957e62aef31faa8041dd610c1', class: "alert-wrapper ion-overlay-wrapper", role: role, "aria-modal": "true", "aria-labelledby": ariaLabelledBy, "aria-describedby": message !== undefined ? msgId : null, tabindex: "0", ref: (el) => (this.wrapperEl = el), ...htmlAttributes }, h("div", { key: '3ad9d2d4b7cf425eab8293ddc12eff0fe8df1517', class: "alert-head" }, header && (h("h2", { key: '7d9154f5ee595b7e4da6ecf3a20871f104c1256e', id: hdrId, class: "alert-title" }, header)), subHeader && !header && (h("h2", { key: '1f891f38004be6964e9406b84dd580145c42516a', id: subHdrId, class: "alert-sub-title" }, subHeader)), subHeader && header && (h("h3", { key: '2ab4d344ecb7e384e8f6d3d3e59ee5a1bbcc832c', id: subHdrId, class: "alert-sub-title" }, subHeader))), this.renderAlertMessage(msgId), this.renderAlertInputs(), this.renderAlertButtons()), h("div", { key: '78a4ff09aa3aaafcd0dc2a42d036767d83b3cfa2', tabindex: "0", "aria-hidden": "true" })));
574
+ }, onIonAlertWillDismiss: this.dispatchCancelHandler, onIonBackdropTap: this.onBackdropTap }, h("ion-backdrop", { key: '65c92a956d998ae9016396dd2a29c42ab7376d0e', tappable: this.backdropDismiss }), h("div", { key: 'f2288fa0b7ef9d0bf031c168fe78c48422cf91c8', tabindex: "0", "aria-hidden": "true" }), h("div", { key: 'dbb109435c378f77c6b0231e58756f8a0467732d', class: "alert-wrapper ion-overlay-wrapper", role: role, "aria-modal": "true", "aria-labelledby": ariaLabelledBy, "aria-describedby": message !== undefined ? msgId : null, tabindex: "0", ref: (el) => (this.wrapperEl = el), ...htmlAttributes }, h("div", { key: '075597f94719673cf1f894a8566e1036849afa75', class: "alert-head" }, header && (h("h2", { key: 'f96f4b1db8473c62040beed6e96108c733978d73', id: hdrId, class: "alert-title" }, header)), subHeader && !header && (h("h2", { key: 'dbd824ef19a07bddde4eb928f11de7c7cadf98b8', id: subHdrId, class: "alert-sub-title" }, subHeader)), subHeader && header && (h("h3", { key: '4774fa13700dd23aec7930492acae80de3bdf5d1', id: subHdrId, class: "alert-sub-title" }, subHeader))), this.renderAlertMessage(msgId), this.renderAlertInputs(), this.renderAlertButtons()), h("div", { key: '5c728b7457e231f64294bbec821bdb547c6437dd', tabindex: "0", "aria-hidden": "true" })));
567
575
  }
568
576
  static get is() { return "ion-alert"; }
569
577
  static get encapsulation() { return "scoped"; }
@@ -5,7 +5,7 @@ import { Host, h } from "@stencil/core";
5
5
  import { ENABLE_HTML_CONTENT_DEFAULT } from "../../utils/config";
6
6
  import { raf } from "../../utils/helpers";
7
7
  import { createLockController } from "../../utils/lock-controller";
8
- import { BACKDROP, cleanupRootFocusTrapAccessibility, createDelegateController, createTriggerController, dismiss, eventMethod, prepareOverlay, present, setOverlayId, } from "../../utils/overlays";
8
+ import { BACKDROP, cleanupRootFocusTrapAccessibility, createDelegateController, createTriggerController, dismiss, eventMethod, prepareOverlay, present, restoreRootFocusTrapAccessibility, setOverlayId, } from "../../utils/overlays";
9
9
  import { sanitizeDOMString } from "../../utils/sanitization/index";
10
10
  import { getClassMap } from "../../utils/theme";
11
11
  import { config } from "../../global/config";
@@ -82,6 +82,10 @@ export class Loading {
82
82
  connectedCallback() {
83
83
  prepareOverlay(this.el);
84
84
  this.triggerChanged();
85
+ // Re-apply the root lock if moved without dismiss() being called
86
+ if (this.presented) {
87
+ restoreRootFocusTrapAccessibility(this.el);
88
+ }
85
89
  }
86
90
  componentWillLoad() {
87
91
  if (this.spinner === undefined) {
@@ -182,14 +186,14 @@ export class Loading {
182
186
  * Otherwise, don't set aria-labelledby.
183
187
  */
184
188
  const ariaLabelledBy = message !== undefined ? msgId : null;
185
- return (h(Host, { key: '2d14122ff3081de73631a7fdd4affdd5c73b8618', role: "dialog", "aria-modal": "true", "aria-labelledby": ariaLabelledBy, tabindex: "-1", ...htmlAttributes, style: {
189
+ return (h(Host, { key: 'b4430bc66aedeca19d5645667cba73c169c4bd76', role: "dialog", "aria-modal": "true", "aria-labelledby": ariaLabelledBy, tabindex: "-1", ...htmlAttributes, style: {
186
190
  zIndex: `${40000 + this.overlayIndex}`,
187
191
  }, onIonBackdropTap: this.onBackdropTap, class: {
188
192
  ...getClassMap(this.cssClass),
189
193
  [mode]: true,
190
194
  'overlay-hidden': true,
191
195
  'loading-translucent': this.translucent,
192
- } }, h("ion-backdrop", { key: 'b7d5bb0d670a20c159d7f67327bf3b1489449188', visible: this.showBackdrop, tappable: this.backdropDismiss }), h("div", { key: '085ce92bd2004d323ab47eb472b83826e48dc619', tabindex: "0", "aria-hidden": "true" }), h("div", { key: '4ecd5bfc0bbd3da434d32f0f319f867a44c5e1f1', class: "loading-wrapper ion-overlay-wrapper" }, spinner && (h("div", { key: '5a4498e1421e09d01c0ee4312eae5272e95c3127', class: "loading-spinner" }, h("ion-spinner", { key: '1b3bc7d45d775195657ead3535f65e6b79f37e84', name: spinner, "aria-hidden": "true" }))), message !== undefined && this.renderLoadingMessage(msgId)), h("div", { key: 'bc7c5467b9f1c6ab957f79934e97acc713bdec37', tabindex: "0", "aria-hidden": "true" })));
196
+ } }, h("ion-backdrop", { key: 'dec0df942ae778c99ebd4dbf08d6c2a0a992639d', visible: this.showBackdrop, tappable: this.backdropDismiss }), h("div", { key: '71b2d5fc259666225047fc901648ada1efcd5d4b', tabindex: "0", "aria-hidden": "true" }), h("div", { key: '1d83dc3b57ee70c9e8c0f006c6fa576bd96ecded', class: "loading-wrapper ion-overlay-wrapper" }, spinner && (h("div", { key: 'd1cd127e044b8831e396b53122322c1db7e89904', class: "loading-spinner" }, h("ion-spinner", { key: '38c2580cf5659da612872f01801a5fbdc2c023ba', name: spinner, "aria-hidden": "true" }))), message !== undefined && this.renderLoadingMessage(msgId)), h("div", { key: '90e5e32ffe0df2c691f364149f43374e676bfaa3', tabindex: "0", "aria-hidden": "true" })));
193
197
  }
194
198
  static get is() { return "ion-loading"; }
195
199
  static get encapsulation() { return "scoped"; }
@@ -8,7 +8,7 @@ import { raf, inheritAttributes, hasLazyBuild, getElementRoot } from "../../util
8
8
  import { createLockController } from "../../utils/lock-controller";
9
9
  import { printIonWarning } from "../../utils/logging/index";
10
10
  import { Style as StatusBarStyle, StatusBar } from "../../utils/native/status-bar";
11
- import { BACKDROP, cleanupRootFocusTrapAccessibility, createTriggerController, dismiss, eventMethod, FOCUS_TRAP_DISABLE_CLASS, GESTURE, prepareOverlay, present, setOverlayId, } from "../../utils/overlays";
11
+ import { BACKDROP, cleanupRootFocusTrapAccessibility, createTriggerController, dismiss, eventMethod, FOCUS_TRAP_DISABLE_CLASS, GESTURE, prepareOverlay, present, restoreRootFocusTrapAccessibility, setOverlayId, } from "../../utils/overlays";
12
12
  import { getClassMap } from "../../utils/theme";
13
13
  import { deepReady, waitForMount } from "../../utils/transition/index";
14
14
  import { config } from "../../global/config";
@@ -43,6 +43,9 @@ export class Modal {
43
43
  this.inline = false;
44
44
  // Whether or not modal is being dismissed via gesture
45
45
  this.gestureAnimationDismissing = false;
46
+ // True from the first safe-area write in `present()` until the enter
47
+ // animation settles. A position-based read in that window is not the rest position.
48
+ this.isPresenting = false;
46
49
  this.presented = false;
47
50
  /** @internal */
48
51
  this.hasController = false;
@@ -279,6 +282,17 @@ export class Modal {
279
282
  const { el } = this;
280
283
  prepareOverlay(el);
281
284
  this.triggerChanged();
285
+ // A disconnect tears down state this presentation still needs and cannot
286
+ // tell a re-insert from a removal, so put it back.
287
+ if (this.presented || this.isPresenting) {
288
+ this.restoreSafeAreaOverrides();
289
+ }
290
+ // A relocation from `willPresent` reaches these before `present()` has
291
+ // applied the lock, so they apply it instead. Both are idempotent.
292
+ if (this.presented) {
293
+ restoreRootFocusTrapAccessibility(el);
294
+ this.initParentRemovalObserver();
295
+ }
282
296
  }
283
297
  disconnectedCallback() {
284
298
  this.triggerController.removeClickListener();
@@ -456,25 +470,33 @@ export class Modal {
456
470
  // Recalculate isSheetModal before safe-area setup because framework
457
471
  // bindings (e.g., Angular) may not have been applied when componentWillLoad ran.
458
472
  this.isSheetModal = this.breakpoints !== undefined && this.initialBreakpoint !== undefined;
459
- // Set initial safe-area overrides before animation
460
- this.setInitialSafeAreaOverrides();
473
+ this.isPresenting = true;
461
474
  const hasCardModal = presentingElement !== undefined;
462
- /**
463
- * We need to change the status bar at the
464
- * start of the animation so that it completes
465
- * by the time the card animation is done.
466
- */
467
- if (hasCardModal && getIonMode(this) === 'ios') {
468
- // Cache the original status bar color before the modal is presented
469
- this.statusBarStyle = await StatusBar.getStyle();
470
- setCardStatusBarDark();
475
+ // Wrapped so a throw cannot strand `isPresenting`, which would suppress
476
+ // the position-based correction for the life of this instance.
477
+ try {
478
+ // Set initial safe-area overrides before animation
479
+ this.setInitialSafeAreaOverrides();
480
+ /**
481
+ * We need to change the status bar at the
482
+ * start of the animation so that it completes
483
+ * by the time the card animation is done.
484
+ */
485
+ if (hasCardModal && getIonMode(this) === 'ios') {
486
+ // Cache the original status bar color before the modal is presented
487
+ this.statusBarStyle = await StatusBar.getStyle();
488
+ setCardStatusBarDark();
489
+ }
490
+ await present(this, 'modalEnter', iosEnterAnimation, mdEnterAnimation, {
491
+ presentingEl: presentingElement,
492
+ currentBreakpoint: this.initialBreakpoint,
493
+ backdropBreakpoint: this.backdropBreakpoint,
494
+ expandToScroll: this.expandToScroll,
495
+ });
496
+ }
497
+ finally {
498
+ this.isPresenting = false;
471
499
  }
472
- await present(this, 'modalEnter', iosEnterAnimation, mdEnterAnimation, {
473
- presentingEl: presentingElement,
474
- currentBreakpoint: this.initialBreakpoint,
475
- backdropBreakpoint: this.backdropBreakpoint,
476
- expandToScroll: this.expandToScroll,
477
- });
478
500
  // Update safe-area based on actual position after animation
479
501
  this.updateSafeAreaOverrides();
480
502
  // Apply fullscreen safe-area padding if needed
@@ -984,6 +1006,9 @@ export class Modal {
984
1006
  this.cachedOriginalParent.tagName === 'ION-APP') {
985
1007
  return;
986
1008
  }
1009
+ // Both `present()` and a reconnect call this, so drop any existing
1010
+ // observer. Below the guards, so a call that bails cannot leave none.
1011
+ this.cleanupParentRemovalObserver();
987
1012
  this.parentRemovalObserver = new MutationObserver((mutations) => {
988
1013
  mutations.forEach((mutation) => {
989
1014
  if (mutation.type === 'childList' && mutation.removedNodes.length > 0) {
@@ -1062,6 +1087,9 @@ export class Modal {
1062
1087
  // Set the internal offset property with the resolved root safe-area-top value
1063
1088
  if (context.isSheetModal) {
1064
1089
  this.updateSheetOffsetTop();
1090
+ // A restore after a DOM move runs this a second time for the same
1091
+ // modal, so drop the previous subscription rather than stacking one.
1092
+ this.unsubscribeRootSafeAreaTop?.();
1065
1093
  this.unsubscribeRootSafeAreaTop = onRootSafeAreaTopChange((safeAreaTop) => this.updateSheetOffsetTop(safeAreaTop));
1066
1094
  }
1067
1095
  }
@@ -1180,6 +1208,31 @@ export class Modal {
1180
1208
  const { contentEl } = this.findContentAndFooter();
1181
1209
  this.clearContentSafeAreaPadding(contentEl);
1182
1210
  }
1211
+ /**
1212
+ * Re-applies what `cleanupSafeAreaOverrides()` removed for a modal moved
1213
+ * while presented. Anything needing no measurement goes on synchronously so
1214
+ * the modal is never painted without safe-area. The position-based
1215
+ * correction waits a frame for the layout pass it reads.
1216
+ */
1217
+ restoreSafeAreaOverrides() {
1218
+ this.setInitialSafeAreaOverrides();
1219
+ this.applyFullscreenSafeArea();
1220
+ raf(() => {
1221
+ // A dismiss or another move can land first, and a read before the enter
1222
+ // animation settles is not the rest position. `present()` redoes it.
1223
+ if (!this.presented || !this.el.isConnected || this.isPresenting) {
1224
+ return;
1225
+ }
1226
+ // A hidden subtree carries `display: none`, so there is no box and the
1227
+ // read would write the safe-area for a modal touching no edge. The
1228
+ // prediction applied above holds until the modal is shown again.
1229
+ const { width, height } = this.el.getBoundingClientRect();
1230
+ if (width === 0 && height === 0) {
1231
+ return;
1232
+ }
1233
+ this.updateSafeAreaOverrides();
1234
+ });
1235
+ }
1183
1236
  render() {
1184
1237
  const { handle, isSheetModal, presentingElement, htmlAttributes, handleBehavior, inheritedAttributes, focusTrap, expandToScroll, } = this;
1185
1238
  const showHandle = handle !== false && isSheetModal;
@@ -1187,7 +1240,7 @@ export class Modal {
1187
1240
  const isCardModal = presentingElement !== undefined && mode === 'ios';
1188
1241
  const isHandleCycle = handleBehavior === 'cycle';
1189
1242
  const isSheetModalWithHandle = isSheetModal && showHandle;
1190
- return (h(Host, { key: 'a471c293319cb6ee5ffb7ffafec0b8413e4289de', "no-router": true,
1243
+ return (h(Host, { key: '7c9c736052c5a788cf31040b6b2386dd9ea7454b', "no-router": true,
1191
1244
  // Allow the modal to be navigable when the handle is focusable
1192
1245
  tabIndex: isHandleCycle && isSheetModalWithHandle ? 0 : -1, ...htmlAttributes, style: {
1193
1246
  zIndex: `${20000 + this.overlayIndex}`,
@@ -1200,7 +1253,7 @@ export class Modal {
1200
1253
  'overlay-hidden': true,
1201
1254
  [FOCUS_TRAP_DISABLE_CLASS]: focusTrap === false,
1202
1255
  ...getClassMap(this.cssClass),
1203
- }, onIonBackdropTap: this.onBackdropTap, onIonModalDidPresent: this.onLifecycle, onIonModalWillPresent: this.onLifecycle, onIonModalWillDismiss: this.onLifecycle, onIonModalDidDismiss: this.onLifecycle, onFocus: this.onModalFocus }, h("ion-backdrop", { key: 'f67934afd4ddad64623856fb641cb6db846df428', ref: (el) => (this.backdropEl = el), visible: this.showBackdrop, tappable: this.backdropDismiss, part: "backdrop" }), mode === 'ios' && h("div", { key: 'a23f5cb6cf60c8f717c1dd556bce375829c80a43', class: "modal-shadow" }), h("div", { key: '51eb6cd1070e4b7e49840a78dc7437238c867a01',
1256
+ }, onIonBackdropTap: this.onBackdropTap, onIonModalDidPresent: this.onLifecycle, onIonModalWillPresent: this.onLifecycle, onIonModalWillDismiss: this.onLifecycle, onIonModalDidDismiss: this.onLifecycle, onFocus: this.onModalFocus }, h("ion-backdrop", { key: '01b5b5765fb3a32393c736065c824bf760728823', ref: (el) => (this.backdropEl = el), visible: this.showBackdrop, tappable: this.backdropDismiss, part: "backdrop" }), mode === 'ios' && h("div", { key: '40fa7727a0923f7d7461dd027f1511a6a148d84a', class: "modal-shadow" }), h("div", { key: '9af934b607f60795bd5bff7bee6eacfc9b14fa35',
1204
1257
  /*
1205
1258
  role and aria-modal must be used on the
1206
1259
  same element. They must also be set inside the
@@ -1213,9 +1266,9 @@ export class Modal {
1213
1266
  without the tabindex focus() would be a no-op and screen readers
1214
1267
  may not properly announce the dialog and its content when it opens.
1215
1268
  */
1216
- role: "dialog", ...inheritedAttributes, "aria-modal": "true", tabIndex: -1, class: "modal-wrapper ion-overlay-wrapper", part: "content", ref: (el) => (this.wrapperEl = el) }, showHandle && (h("button", { key: 'f49a144642b5c227c0c2b00b33f4c7e37a2bf088', class: "modal-handle",
1269
+ role: "dialog", ...inheritedAttributes, "aria-modal": "true", tabIndex: -1, class: "modal-wrapper ion-overlay-wrapper", part: "content", ref: (el) => (this.wrapperEl = el) }, showHandle && (h("button", { key: 'df84a6806413b13efc5b03c862edde83151579a7', class: "modal-handle",
1217
1270
  // Prevents the handle from receiving keyboard focus when it does not cycle
1218
- tabIndex: !isHandleCycle ? -1 : 0, "aria-label": "Activate to adjust the size of the dialog overlaying the screen", onClick: isHandleCycle ? this.onHandleClick : undefined, part: "handle", ref: (el) => (this.dragHandleEl = el) })), h("slot", { key: 'e781c0d5881762c6b61aa432721a38b49580ff64', onSlotchange: this.onSlotChange }))));
1271
+ tabIndex: !isHandleCycle ? -1 : 0, "aria-label": "Activate to adjust the size of the dialog overlaying the screen", onClick: isHandleCycle ? this.onHandleClick : undefined, part: "handle", ref: (el) => (this.dragHandleEl = el) })), h("slot", { key: '072d3929ec6f25481946b184d8ac24219011e783', onSlotchange: this.onSlotChange }))));
1219
1272
  }
1220
1273
  static get is() { return "ion-modal"; }
1221
1274
  static get encapsulation() { return "shadow"; }
@@ -7,7 +7,7 @@ import { CoreDelegate, attachComponent, detachComponent } from "../../utils/fram
7
7
  import { addEventListener, raf, hasLazyBuild } from "../../utils/helpers";
8
8
  import { createLockController } from "../../utils/lock-controller";
9
9
  import { printIonWarning } from "../../utils/logging/index";
10
- import { BACKDROP, cleanupRootFocusTrapAccessibility, dismiss, eventMethod, FOCUS_TRAP_DISABLE_CLASS, prepareOverlay, present, setOverlayId, } from "../../utils/overlays";
10
+ import { BACKDROP, cleanupRootFocusTrapAccessibility, dismiss, eventMethod, FOCUS_TRAP_DISABLE_CLASS, prepareOverlay, present, restoreRootFocusTrapAccessibility, setOverlayId, } from "../../utils/overlays";
11
11
  import { isPlatform } from "../../utils/platform";
12
12
  import { getClassMap } from "../../utils/theme";
13
13
  import { deepReady, waitForMount } from "../../utils/transition/index";
@@ -219,6 +219,13 @@ export class Popover {
219
219
  const { configureTriggerInteraction, el } = this;
220
220
  prepareOverlay(el);
221
221
  configureTriggerInteraction();
222
+ // Re-apply the root lock if moved without dismiss() being called
223
+ if (this.presented) {
224
+ restoreRootFocusTrapAccessibility(el);
225
+ // The disconnect tore down the header observer, and `present()` is its
226
+ // only other caller, so a move mid-layout would lose the recalculation.
227
+ this.recalculateContentOnHeaderReady();
228
+ }
222
229
  }
223
230
  disconnectedCallback() {
224
231
  const { destroyTriggerInteraction } = this;
@@ -396,6 +403,12 @@ export class Popover {
396
403
  if (!header || contentElements.length === 0) {
397
404
  return;
398
405
  }
406
+ // Both `present()` and a reconnect call this, so drop any existing
407
+ // observer. Below the guards, so a call that bails cannot leave none.
408
+ if (this.headerResizeObserver) {
409
+ this.headerResizeObserver.disconnect();
410
+ this.headerResizeObserver = undefined;
411
+ }
399
412
  this.headerResizeObserver = new ResizeObserver(async () => {
400
413
  if (header.offsetHeight > 0) {
401
414
  this.headerResizeObserver?.disconnect();
@@ -468,7 +481,7 @@ export class Popover {
468
481
  const { onLifecycle, parentPopover, dismissOnSelect, side, arrow, htmlAttributes, focusTrap } = this;
469
482
  const desktop = isPlatform('desktop');
470
483
  const enableArrow = arrow && !parentPopover;
471
- return (h(Host, { key: '52ffc7116969ae1b8b927e67f2acbfe5f5434f70', "aria-modal": "true", "no-router": true, tabindex: "-1", ...htmlAttributes, style: {
484
+ return (h(Host, { key: 'b5e9c788f58397203976d5f8302dfb89e916efe1', "aria-modal": "true", "no-router": true, tabindex: "-1", ...htmlAttributes, style: {
472
485
  zIndex: `${20000 + this.overlayIndex}`,
473
486
  }, class: {
474
487
  ...getClassMap(this.cssClass),
@@ -479,7 +492,7 @@ export class Popover {
479
492
  [`popover-side-${side}`]: true,
480
493
  [FOCUS_TRAP_DISABLE_CLASS]: focusTrap === false,
481
494
  'popover-nested': !!parentPopover,
482
- }, onIonPopoverDidPresent: onLifecycle, onIonPopoverWillPresent: onLifecycle, onIonPopoverWillDismiss: onLifecycle, onIonPopoverDidDismiss: onLifecycle, onIonBackdropTap: this.onBackdropTap }, !parentPopover && h("ion-backdrop", { key: '79a8e851dbd22119e21ffb2f4709c555251045c8', tappable: this.backdropDismiss, visible: this.showBackdrop, part: "backdrop" }), h("div", { key: 'c46c9f62c3ef3f902e23f27404eaa446a5d8cf88', class: "popover-wrapper ion-overlay-wrapper", onClick: dismissOnSelect ? () => this.dismiss() : undefined }, enableArrow && h("div", { key: '9fdcbbc4ea6c7bde74f646d48eb77296b2c9dd83', class: "popover-arrow", part: "arrow" }), h("div", { key: '138e56bb83462ebb0908edb522b45ecf94b82aeb', class: "popover-content", part: "content" }, h("slot", { key: 'daeefcf002e8d89e91d6e0faf7f63f75b68a2dbe' })))));
495
+ }, onIonPopoverDidPresent: onLifecycle, onIonPopoverWillPresent: onLifecycle, onIonPopoverWillDismiss: onLifecycle, onIonPopoverDidDismiss: onLifecycle, onIonBackdropTap: this.onBackdropTap }, !parentPopover && h("ion-backdrop", { key: 'f3977833c5b2c0087c286ee54363b478d69eb115', tappable: this.backdropDismiss, visible: this.showBackdrop, part: "backdrop" }), h("div", { key: '1059893d6fd2932ca8adcdf8d45a5d66f4794727', class: "popover-wrapper ion-overlay-wrapper", onClick: dismissOnSelect ? () => this.dismiss() : undefined }, enableArrow && h("div", { key: '24d7988bdd95882ad77fcaa7112aeda55878bee4', class: "popover-arrow", part: "arrow" }), h("div", { key: '4d6992e0a9d629f11763045f9199f9a77fa521ab', class: "popover-content", part: "content" }, h("slot", { key: 'b29747419620a01a6c34e9e846cd529b0b210145' })))));
483
496
  }
484
497
  static get is() { return "ion-popover"; }
485
498
  static get encapsulation() { return "shadow"; }
@@ -21,6 +21,15 @@ export const activeAnimations = new WeakMap();
21
21
  const isBackdropAlwaysBlocking = (el) => {
22
22
  return el.showBackdrop !== false && !((el.backdropBreakpoint ?? 0) > 0);
23
23
  };
24
+ /**
25
+ * Whether the overlay takes the app root out of the accessibility tree and
26
+ * blocks body scroll while presented. Toasts never do, modal and popover can
27
+ * opt out with `focusTrap={false}`, and a backdrop that does not block
28
+ * (`showBackdrop={false}`, or a `backdropBreakpoint` above 0) is excluded.
29
+ */
30
+ const locksAppRoot = (el) => {
31
+ return el.tagName !== 'ION-TOAST' && el.focusTrap !== false && isBackdropAlwaysBlocking(el);
32
+ };
24
33
  const createController = (tagName) => {
25
34
  return {
26
35
  create(options) {
@@ -391,6 +400,13 @@ export const getPresentedOverlay = (doc, overlayTag, id) => {
391
400
  // Otherwise, return the last overlay with the given id
392
401
  return (id === undefined ? overlays : overlays.filter((o) => o.id === id)).slice(-1)[0];
393
402
  };
403
+ /**
404
+ * The element an app nests its views under. Overlays hide this rather than the
405
+ * whole root, so the overlay itself (a sibling) stays reachable.
406
+ */
407
+ const getViewContainer = () => {
408
+ return getAppRoot(document).querySelector('ion-router-outlet, #ion-view-container-root');
409
+ };
394
410
  /**
395
411
  * When an overlay is presented, the main
396
412
  * focus is the overlay not the page content.
@@ -414,8 +430,7 @@ export const getPresentedOverlay = (doc, overlayTag, id) => {
414
430
  * for main content.
415
431
  */
416
432
  export const setRootAriaHidden = (hidden = false) => {
417
- const root = getAppRoot(document);
418
- const viewContainer = root.querySelector('ion-router-outlet, #ion-view-container-root');
433
+ const viewContainer = getViewContainer();
419
434
  if (!viewContainer) {
420
435
  return;
421
436
  }
@@ -440,15 +455,42 @@ export const cleanupRootFocusTrapAccessibility = () => {
440
455
  return;
441
456
  }
442
457
  const remainingOverlays = getPresentedOverlays(document);
443
- const hasRemainingLocking = remainingOverlays.some((o) => {
444
- const el = o;
445
- return el.tagName !== 'ION-TOAST' && el.focusTrap !== false && isBackdropAlwaysBlocking(el);
446
- });
458
+ const hasRemainingLocking = remainingOverlays.some((o) => locksAppRoot(o));
447
459
  if (!hasRemainingLocking) {
448
460
  setRootAriaHidden(false);
449
461
  document.body.classList.remove(BACKDROP_NO_SCROLL);
450
462
  }
451
463
  };
464
+ /**
465
+ * Applies the root lock itself: `aria-hidden` on the view container, and
466
+ * `backdrop-no-scroll` on the body. Shared by `present()` and the restore
467
+ * below, which have to stay in lockstep.
468
+ */
469
+ const applyRootLock = (el) => {
470
+ // Hiding the container the overlay now sits in would hide the overlay too.
471
+ if (!getViewContainer()?.contains(el)) {
472
+ setRootAriaHidden(true);
473
+ }
474
+ document.body.classList.add(BACKDROP_NO_SCROLL);
475
+ };
476
+ /**
477
+ * Re-applies the root lock that `cleanupRootFocusTrapAccessibility()` released.
478
+ * Call from `connectedCallback` when the overlay is still presented.
479
+ *
480
+ * A synchronous move keeps the overlay connected, so the lock survives. A
481
+ * detach with a re-insert in a later task releases it, which is the shape a
482
+ * framework produces when it takes a subtree out and puts it back.
483
+ */
484
+ export const restoreRootFocusTrapAccessibility = (overlayEl) => {
485
+ if (typeof document === 'undefined') {
486
+ return;
487
+ }
488
+ const el = overlayEl;
489
+ if (!locksAppRoot(el)) {
490
+ return;
491
+ }
492
+ applyRootLock(el);
493
+ };
452
494
  export const present = async (overlay, name, iosEnterAnimation, mdEnterAnimation, opts) => {
453
495
  if (overlay.presented) {
454
496
  return;
@@ -465,15 +507,7 @@ export const present = async (overlay, name, iosEnterAnimation, mdEnterAnimation
465
507
  restoreElementFocus(overlay.el);
466
508
  }
467
509
  /**
468
- * Due to accessibility guidelines, toasts do not have
469
- * focus traps.
470
- *
471
- * All other overlays should have focus traps to prevent
472
- * the keyboard focus from leaving the overlay unless
473
- * developers explicitly opt out (for example, sheet
474
- * modals that should permit background interaction).
475
- *
476
- * Note: Some apps move inline overlays to a specific container
510
+ * Some apps move inline overlays to a specific container
477
511
  * during the willPresent lifecycle (e.g., React portals via
478
512
  * onWillPresent). Defer applying aria-hidden/inert to the app
479
513
  * root until after willPresent so we can detect where the
@@ -482,18 +516,11 @@ export const present = async (overlay, name, iosEnterAnimation, mdEnterAnimation
482
516
  * to avoid disabling the overlay.
483
517
  */
484
518
  const overlayEl = overlay.el;
485
- const shouldTrapFocus = overlayEl.tagName !== 'ION-TOAST' && overlayEl.focusTrap !== false;
486
- const shouldLockRoot = shouldTrapFocus && isBackdropAlwaysBlocking(overlayEl);
519
+ const shouldLockRoot = locksAppRoot(overlayEl);
487
520
  overlay.presented = true;
488
521
  overlay.willPresent.emit();
489
522
  if (shouldLockRoot) {
490
- const root = getAppRoot(document);
491
- const viewContainer = root.querySelector('ion-router-outlet, #ion-view-container-root');
492
- const overlayInsideViewContainer = viewContainer ? viewContainer.contains(overlayEl) : false;
493
- if (!overlayInsideViewContainer) {
494
- setRootAriaHidden(true);
495
- }
496
- document.body.classList.add(BACKDROP_NO_SCROLL);
523
+ applyRootLock(overlayEl);
497
524
  }
498
525
  overlay.willPresentShorthand?.emit();
499
526
  const mode = getIonMode(overlay);
@@ -619,12 +646,9 @@ export const dismiss = async (overlay, data, role, name, iosLeaveAnimation, mdLe
619
646
  * from the root element when the last focus-trapping overlay
620
647
  * is dismissed.
621
648
  */
622
- const overlaysLockingRoot = presentedOverlays.filter((o) => {
623
- const el = o;
624
- return el.tagName !== 'ION-TOAST' && el.focusTrap !== false && isBackdropAlwaysBlocking(el);
625
- });
649
+ const overlaysLockingRoot = presentedOverlays.filter((o) => locksAppRoot(o));
626
650
  const overlayEl = overlay.el;
627
- const locksRoot = overlayEl.tagName !== 'ION-TOAST' && overlayEl.focusTrap !== false && isBackdropAlwaysBlocking(overlayEl);
651
+ const locksRoot = locksAppRoot(overlayEl);
628
652
  /**
629
653
  * If this is the last visible overlay that is trapping focus
630
654
  * then we want to re-add the root to the accessibility tree.
@@ -0,0 +1,23 @@
1
+ /*!
2
+ * (C) Ionic http://ionicframework.com - MIT License
3
+ */
4
+ /**
5
+ * Detaches an element and puts it back in a later task, the way a framework
6
+ * binding relocates a subtree it owns.
7
+ *
8
+ * A single `appendChild` won't do it. Custom element reactions run at the end
9
+ * of the same task, so `disconnectedCallback` fires with the element already
10
+ * back and nothing tears down. The detach has to survive a task before the
11
+ * disconnect is observable.
12
+ *
13
+ * Pass `containerSelector` to hold the element somewhere other than the body.
14
+ */
15
+ export const detachAndReattach = async (locator, containerSelector = 'body') => {
16
+ await locator.evaluate(async (el, selector) => {
17
+ const holder = document.createElement('div');
18
+ document.querySelector(selector).appendChild(holder);
19
+ el.remove();
20
+ await new Promise((resolve) => setTimeout(resolve, 0));
21
+ holder.appendChild(el);
22
+ }, containerSelector);
23
+ };
@@ -10,3 +10,4 @@ export * from './apply-keyboard-focus';
10
10
  export * from './matchers';
11
11
  export * from './viewports';
12
12
  export * from './generator';
13
+ export * from './detach-reattach';
package/dist/docs.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
- "timestamp": "2026-08-25T06:11:31",
2
+ "timestamp": "2026-08-26T18:02:56",
3
3
  "compiler": {
4
4
  "name": "@stencil/core",
5
- "version": "4.44.1",
5
+ "version": "4.44.2",
6
6
  "typescriptVersion": "5.8.3"
7
7
  },
8
8
  "components": [
@@ -1,7 +1,7 @@
1
1
  /*!
2
2
  * (C) Ionic http://ionicframework.com - MIT License
3
3
  */
4
- import { j as printIonError } from './index-h81G-QbJ.js';
4
+ import { j as printIonError } from './index-C23AVPx9.js';
5
5
  import { w as win } from './index-ZjP4CjeZ.js';
6
6
 
7
7
  let animationPrefix;