@ionic/core 9.0.1-dev.11787248963.10a00c73 → 9.0.1-dev.11787589191.1e183b55

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 (94) hide show
  1. package/components/index.js +1 -1
  2. package/components/ion-action-sheet.js +1 -1
  3. package/components/ion-alert.js +1 -1
  4. package/components/ion-datetime.js +1 -1
  5. package/components/ion-item-sliding.js +1 -1
  6. package/components/ion-loading.js +1 -1
  7. package/components/ion-menu.js +1 -1
  8. package/components/ion-modal.js +1 -1
  9. package/components/ion-popover.js +1 -1
  10. package/components/ion-segment-button.js +1 -1
  11. package/components/ion-select-modal.js +1 -1
  12. package/components/ion-select-popover.js +1 -1
  13. package/components/ion-select.js +1 -1
  14. package/components/ion-toast.js +1 -1
  15. package/components/p-BPnXVGaS.js +4 -0
  16. package/components/p-BQAiulS1.js +4 -0
  17. package/components/p-Bh2ta0iG.js +4 -0
  18. package/components/{p-CaiZOhfC.js → p-BnUOVmKh.js} +1 -1
  19. package/components/{p-Bnc0m4SW.js → p-D1OCo42s.js} +1 -1
  20. package/components/p-DD57rSP_.js +4 -0
  21. package/components/p-DnXLaaf7.js +4 -0
  22. package/dist/cjs/index.cjs.js +1 -1
  23. package/dist/cjs/ion-action-sheet.cjs.entry.js +35 -20
  24. package/dist/cjs/ion-alert.cjs.entry.js +20 -12
  25. package/dist/cjs/ion-datetime.cjs.entry.js +1 -1
  26. package/dist/cjs/ion-item-option_3.cjs.entry.js +11 -9
  27. package/dist/cjs/ion-loading.cjs.entry.js +7 -3
  28. package/dist/cjs/ion-menu_3.cjs.entry.js +1 -1
  29. package/dist/cjs/ion-modal.cjs.entry.js +75 -22
  30. package/dist/cjs/ion-popover.cjs.entry.js +13 -3
  31. package/dist/cjs/ion-segment_2.cjs.entry.js +7 -11
  32. package/dist/cjs/ion-select-modal.cjs.entry.js +1 -1
  33. package/dist/cjs/ion-select_3.cjs.entry.js +1 -1
  34. package/dist/cjs/ion-toast.cjs.entry.js +1 -1
  35. package/dist/cjs/{overlays-Bg0auTQa.js → overlays-B5JeC8OQ.js} +52 -29
  36. package/dist/collection/components/action-sheet/action-sheet.js +35 -20
  37. package/dist/collection/components/alert/alert.js +20 -12
  38. package/dist/collection/components/item-sliding/item-sliding.js +12 -10
  39. package/dist/collection/components/loading/loading.js +7 -3
  40. package/dist/collection/components/modal/modal.js +75 -22
  41. package/dist/collection/components/popover/popover.js +13 -3
  42. package/dist/collection/components/segment-button/segment-button.js +7 -11
  43. package/dist/collection/utils/overlays.js +51 -29
  44. package/dist/docs.json +1 -1
  45. package/dist/esm/index.js +1 -1
  46. package/dist/esm/ion-action-sheet.entry.js +35 -20
  47. package/dist/esm/ion-alert.entry.js +20 -12
  48. package/dist/esm/ion-datetime.entry.js +1 -1
  49. package/dist/esm/ion-item-option_3.entry.js +12 -10
  50. package/dist/esm/ion-loading.entry.js +7 -3
  51. package/dist/esm/ion-menu_3.entry.js +1 -1
  52. package/dist/esm/ion-modal.entry.js +75 -22
  53. package/dist/esm/ion-popover.entry.js +13 -3
  54. package/dist/esm/ion-segment_2.entry.js +7 -11
  55. package/dist/esm/ion-select-modal.entry.js +1 -1
  56. package/dist/esm/ion-select_3.entry.js +1 -1
  57. package/dist/esm/ion-toast.entry.js +1 -1
  58. package/dist/esm/{overlays-BpnIFlXI.js → overlays-CefdMZgF.js} +52 -30
  59. package/dist/ionic/index.esm.js +1 -1
  60. package/dist/ionic/ionic.esm.js +1 -1
  61. package/dist/ionic/p-36abc1ee.entry.js +4 -0
  62. package/dist/ionic/{p-873a315a.entry.js → p-408973f4.entry.js} +1 -1
  63. package/dist/ionic/{p-4437015c.entry.js → p-46a938a9.entry.js} +1 -1
  64. package/dist/ionic/p-47626333.entry.js +4 -0
  65. package/dist/ionic/p-8f464a8f.entry.js +4 -0
  66. package/dist/ionic/p-9455207b.entry.js +4 -0
  67. package/dist/ionic/p-DLLCmV-y.js +4 -0
  68. package/dist/ionic/{p-05800a02.entry.js → p-a2fa2de2.entry.js} +1 -1
  69. package/dist/ionic/p-a53b0908.entry.js +4 -0
  70. package/dist/ionic/{p-75887ee0.entry.js → p-b875a59e.entry.js} +1 -1
  71. package/dist/ionic/p-f3b671aa.entry.js +4 -0
  72. package/dist/ionic/p-f521b13c.entry.js +4 -0
  73. package/dist/ionic/{p-49b9d405.entry.js → p-f8713b2c.entry.js} +1 -1
  74. package/dist/types/components/action-sheet/action-sheet.d.ts +10 -0
  75. package/dist/types/components/alert/alert.d.ts +7 -0
  76. package/dist/types/components/modal/modal.d.ts +8 -0
  77. package/dist/types/utils/overlays.d.ts +9 -0
  78. package/hydrate/index.js +214 -104
  79. package/hydrate/index.mjs +214 -104
  80. package/package.json +1 -1
  81. package/components/p-Bg4OwDwb.js +0 -4
  82. package/components/p-CQAjyETU.js +0 -4
  83. package/components/p-DqAI6tg7.js +0 -4
  84. package/components/p-DrehWDfv.js +0 -4
  85. package/components/p-eFI3mtEX.js +0 -4
  86. package/dist/collection/utils/test/late-props/late-props.js +0 -69
  87. package/dist/ionic/p-34b9379b.entry.js +0 -4
  88. package/dist/ionic/p-48c859fe.entry.js +0 -4
  89. package/dist/ionic/p-5b906fd6.entry.js +0 -4
  90. package/dist/ionic/p-68507e70.entry.js +0 -4
  91. package/dist/ionic/p-886d9e66.entry.js +0 -4
  92. package/dist/ionic/p-DdhHqKj-.js +0 -4
  93. package/dist/ionic/p-b7a11b58.entry.js +0 -4
  94. package/dist/ionic/p-f7dd1ed9.entry.js +0 -4
@@ -9,7 +9,7 @@ var buttonActive = require('./button-active-BlWSD7uK.js');
9
9
  var helpers = require('./helpers-Dj5o76mq.js');
10
10
  var lockController = require('./lock-controller-aDB9wrEf.js');
11
11
  var overlayControlLabel = require('./overlay-control-label-D2sdL2O8.js');
12
- var overlays = require('./overlays-Bg0auTQa.js');
12
+ var overlays = require('./overlays-B5JeC8OQ.js');
13
13
  var index$1 = require('./index-EJLjthTK.js');
14
14
  var selectOptionRender = require('./select-option-render-7m1WdFzF.js');
15
15
  var theme = require('./theme-Bw0rpaNN.js');
@@ -298,10 +298,15 @@ const Alert = class {
298
298
  this.triggerChanged();
299
299
  /**
300
300
  * If the alert was previously connected and is being reattached, the
301
- * ResizeObserver was disconnected. componentDidLoad only fires once per
302
- * instance, so re-establish the observer here on reconnect.
301
+ * `ResizeObserver` and the button gesture were torn down. `componentDidLoad`
302
+ * only fires once per instance, so re-establish both here on reconnect.
303
303
  */
304
304
  this.setupButtonGroupResizeObserver();
305
+ this.setupButtonActiveGesture();
306
+ // Re-apply the root lock if moved without dismiss() being called
307
+ if (this.presented) {
308
+ overlays.restoreRootFocusTrapAccessibility(this.el);
309
+ }
305
310
  }
306
311
  componentWillLoad() {
307
312
  if (!this.htmlAttributes?.id) {
@@ -323,17 +328,20 @@ const Alert = class {
323
328
  this.buttonGroupResizeObserver?.disconnect();
324
329
  this.buttonGroupResizeObserver = undefined;
325
330
  }
326
- componentDidLoad() {
327
- /**
328
- * Only create gesture if:
329
- * 1. A gesture does not already exist
330
- * 2. App is running in iOS mode
331
- * 3. A wrapper ref exists
332
- */
331
+ /**
332
+ * Only create gesture if:
333
+ * 1. A gesture does not already exist
334
+ * 2. App is running in iOS mode
335
+ * 3. A wrapper ref exists
336
+ */
337
+ setupButtonActiveGesture() {
333
338
  if (!this.gesture && ionicGlobal.getIonMode(this) === 'ios' && this.wrapperEl) {
334
339
  this.gesture = buttonActive.createButtonActiveGesture(this.wrapperEl, (refEl) => refEl.classList.contains('alert-button'));
335
340
  this.gesture.enable(true);
336
341
  }
342
+ }
343
+ componentDidLoad() {
344
+ this.setupButtonActiveGesture();
337
345
  this.setupButtonGroupResizeObserver();
338
346
  /**
339
347
  * If alert was rendered with isOpen="true"
@@ -656,14 +664,14 @@ const Alert = class {
656
664
  * If neither are defined, do not set aria-labelledby.
657
665
  */
658
666
  const ariaLabelledBy = header && subHeader ? `${hdrId} ${subHdrId}` : header ? hdrId : subHeader ? subHdrId : null;
659
- return (index.h(index.Host, { key: '022f3463b8e6c52064b90ec304c524847c3d5ad3', tabindex: "-1", style: {
667
+ return (index.h(index.Host, { key: '083b5250dc4c76e85240e0225abcb7ca2b66de2f', tabindex: "-1", style: {
660
668
  zIndex: `${20000 + overlayIndex}`,
661
669
  }, class: {
662
670
  ...theme.getClassMap(this.cssClass),
663
671
  [mode]: true,
664
672
  'overlay-hidden': true,
665
673
  'alert-translucent': this.translucent,
666
- }, onIonAlertWillDismiss: this.dispatchCancelHandler, onIonBackdropTap: this.onBackdropTap }, index.h("ion-backdrop", { key: 'c3cd05738bc2834f54707edeb2fae06ec10d6fd2', tappable: this.backdropDismiss }), index.h("div", { key: 'b44fadd9ba7ddf073854c5035538d11ae8e66bc2', tabindex: "0", "aria-hidden": "true" }), index.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 }, index.h("div", { key: '3ad9d2d4b7cf425eab8293ddc12eff0fe8df1517', class: "alert-head" }, header && (index.h("h2", { key: '7d9154f5ee595b7e4da6ecf3a20871f104c1256e', id: hdrId, class: "alert-title" }, header)), subHeader && !header && (index.h("h2", { key: '1f891f38004be6964e9406b84dd580145c42516a', id: subHdrId, class: "alert-sub-title" }, subHeader)), subHeader && header && (index.h("h3", { key: '2ab4d344ecb7e384e8f6d3d3e59ee5a1bbcc832c', id: subHdrId, class: "alert-sub-title" }, subHeader))), this.renderAlertMessage(msgId), this.renderAlertInputs(), this.renderAlertButtons()), index.h("div", { key: '78a4ff09aa3aaafcd0dc2a42d036767d83b3cfa2', tabindex: "0", "aria-hidden": "true" })));
674
+ }, onIonAlertWillDismiss: this.dispatchCancelHandler, onIonBackdropTap: this.onBackdropTap }, index.h("ion-backdrop", { key: '65c92a956d998ae9016396dd2a29c42ab7376d0e', tappable: this.backdropDismiss }), index.h("div", { key: 'f2288fa0b7ef9d0bf031c168fe78c48422cf91c8', tabindex: "0", "aria-hidden": "true" }), index.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 }, index.h("div", { key: '075597f94719673cf1f894a8566e1036849afa75', class: "alert-head" }, header && (index.h("h2", { key: 'f96f4b1db8473c62040beed6e96108c733978d73', id: hdrId, class: "alert-title" }, header)), subHeader && !header && (index.h("h2", { key: 'dbd824ef19a07bddde4eb928f11de7c7cadf98b8', id: subHdrId, class: "alert-sub-title" }, subHeader)), subHeader && header && (index.h("h3", { key: '4774fa13700dd23aec7930492acae80de3bdf5d1', id: subHdrId, class: "alert-sub-title" }, subHeader))), this.renderAlertMessage(msgId), this.renderAlertInputs(), this.renderAlertButtons()), index.h("div", { key: '5c728b7457e231f64294bbec821bdb547c6437dd', tabindex: "0", "aria-hidden": "true" })));
667
675
  }
668
676
  get el() { return index.getElement(this); }
669
677
  static get watchers() { return {
@@ -6,7 +6,7 @@
6
6
  var index = require('./index-BrVMoqUk.js');
7
7
  var focusVisible = require('./focus-visible-CCvKiLh3.js');
8
8
  var helpers = require('./helpers-Dj5o76mq.js');
9
- var overlays = require('./overlays-Bg0auTQa.js');
9
+ var overlays = require('./overlays-B5JeC8OQ.js');
10
10
  var dir = require('./dir-zwDPRE5K.js');
11
11
  var theme = require('./theme-Bw0rpaNN.js');
12
12
  var index$1 = require('./index-wOmugbdg.js');
@@ -283,17 +283,19 @@ const ItemSliding = class {
283
283
  }
284
284
  }
285
285
  async updateOptions() {
286
- const options = Array.from(this.el.querySelectorAll('ion-item-options'));
287
- /**
288
- * Frameworks that assign element props after inserting the element haven't set
289
- * `side` while `connectedCallback` runs, so reading it any earlier reports every
290
- * option as `end`.
291
- */
292
- await Promise.all(options.map((option) => new Promise((resolve) => helpers.componentOnReady(option, resolve))));
286
+ const options = this.el.querySelectorAll('ion-item-options');
293
287
  let sides = 0;
294
288
  // Reset left and right options in case they were removed
295
289
  this.leftOptions = this.rightOptions = undefined;
296
- for (const option of options) {
290
+ for (let i = 0; i < options.length; i++) {
291
+ const item = options.item(i);
292
+ /**
293
+ * We cannot use the componentOnReady helper
294
+ * util here since we need to wait for all of these items
295
+ * to be ready before we set `this.sides` and `this.optsDirty`.
296
+ */
297
+ // eslint-disable-next-line custom-rules/no-component-on-ready-method
298
+ const option = item.componentOnReady !== undefined ? await item.componentOnReady() : item;
297
299
  const side = helpers.isEndSide(option.side ?? option.getAttribute('side')) ? 'end' : 'start';
298
300
  if (side === 'start') {
299
301
  this.leftOptions = option;
@@ -492,7 +494,7 @@ const ItemSliding = class {
492
494
  }
493
495
  render() {
494
496
  const mode = ionicGlobal.getIonMode(this);
495
- return (index.h(index.Host, { key: '02174ca963753d3a51a4249068f3d436f8acb03d', class: {
497
+ return (index.h(index.Host, { key: '15f6a98f8c888e86c29827cdf55d98ace08699dd', class: {
496
498
  [mode]: true,
497
499
  'item-sliding-active-slide': this.state !== 2 /* SlidingState.Disabled */,
498
500
  'item-sliding-active-options-end': (this.state & 8 /* SlidingState.End */) !== 0,
@@ -7,7 +7,7 @@ var index = require('./index-BrVMoqUk.js');
7
7
  var config = require('./config-CjUbvdPW.js');
8
8
  var helpers = require('./helpers-Dj5o76mq.js');
9
9
  var lockController = require('./lock-controller-aDB9wrEf.js');
10
- var overlays = require('./overlays-Bg0auTQa.js');
10
+ var overlays = require('./overlays-B5JeC8OQ.js');
11
11
  var index$1 = require('./index-EJLjthTK.js');
12
12
  var theme = require('./theme-Bw0rpaNN.js');
13
13
  var ionicGlobal = require('./ionic-global-DvgIzM2F.js');
@@ -183,6 +183,10 @@ const Loading = class {
183
183
  connectedCallback() {
184
184
  overlays.prepareOverlay(this.el);
185
185
  this.triggerChanged();
186
+ // Re-apply the root lock if moved without dismiss() being called
187
+ if (this.presented) {
188
+ overlays.restoreRootFocusTrapAccessibility(this.el);
189
+ }
186
190
  }
187
191
  componentWillLoad() {
188
192
  if (this.spinner === undefined) {
@@ -283,14 +287,14 @@ const Loading = class {
283
287
  * Otherwise, don't set aria-labelledby.
284
288
  */
285
289
  const ariaLabelledBy = message !== undefined ? msgId : null;
286
- return (index.h(index.Host, { key: '2d14122ff3081de73631a7fdd4affdd5c73b8618', role: "dialog", "aria-modal": "true", "aria-labelledby": ariaLabelledBy, tabindex: "-1", ...htmlAttributes, style: {
290
+ return (index.h(index.Host, { key: 'b4430bc66aedeca19d5645667cba73c169c4bd76', role: "dialog", "aria-modal": "true", "aria-labelledby": ariaLabelledBy, tabindex: "-1", ...htmlAttributes, style: {
287
291
  zIndex: `${40000 + this.overlayIndex}`,
288
292
  }, onIonBackdropTap: this.onBackdropTap, class: {
289
293
  ...theme.getClassMap(this.cssClass),
290
294
  [mode]: true,
291
295
  'overlay-hidden': true,
292
296
  'loading-translucent': this.translucent,
293
- } }, index.h("ion-backdrop", { key: 'b7d5bb0d670a20c159d7f67327bf3b1489449188', visible: this.showBackdrop, tappable: this.backdropDismiss }), index.h("div", { key: '085ce92bd2004d323ab47eb472b83826e48dc619', tabindex: "0", "aria-hidden": "true" }), index.h("div", { key: '4ecd5bfc0bbd3da434d32f0f319f867a44c5e1f1', class: "loading-wrapper ion-overlay-wrapper" }, spinner && (index.h("div", { key: '5a4498e1421e09d01c0ee4312eae5272e95c3127', class: "loading-spinner" }, index.h("ion-spinner", { key: '1b3bc7d45d775195657ead3535f65e6b79f37e84', name: spinner, "aria-hidden": "true" }))), message !== undefined && this.renderLoadingMessage(msgId)), index.h("div", { key: 'bc7c5467b9f1c6ab957f79934e97acc713bdec37', tabindex: "0", "aria-hidden": "true" })));
297
+ } }, index.h("ion-backdrop", { key: 'dec0df942ae778c99ebd4dbf08d6c2a0a992639d', visible: this.showBackdrop, tappable: this.backdropDismiss }), index.h("div", { key: '71b2d5fc259666225047fc901648ada1efcd5d4b', tabindex: "0", "aria-hidden": "true" }), index.h("div", { key: '1d83dc3b57ee70c9e8c0f006c6fa576bd96ecded', class: "loading-wrapper ion-overlay-wrapper" }, spinner && (index.h("div", { key: 'd1cd127e044b8831e396b53122322c1db7e89904', class: "loading-spinner" }, index.h("ion-spinner", { key: '38c2580cf5659da612872f01801a5fbdc2c023ba', name: spinner, "aria-hidden": "true" }))), message !== undefined && this.renderLoadingMessage(msgId)), index.h("div", { key: '90e5e32ffe0df2c691f364149f43374e676bfaa3', tabindex: "0", "aria-hidden": "true" })));
294
298
  }
295
299
  get el() { return index.getElement(this); }
296
300
  static get watchers() { return {
@@ -5,7 +5,7 @@
5
5
 
6
6
  var index = require('./index-BrVMoqUk.js');
7
7
  var cubicBezier = require('./cubic-bezier-DAjy1V-e.js');
8
- var overlays = require('./overlays-Bg0auTQa.js');
8
+ var overlays = require('./overlays-B5JeC8OQ.js');
9
9
  var gestureController = require('./gesture-controller-CeJslptU.js');
10
10
  var hardwareBackButton = require('./hardware-back-button-JOAEFSzc.js');
11
11
  var helpers = require('./helpers-Dj5o76mq.js');
@@ -9,7 +9,7 @@ var frameworkDelegate = require('./framework-delegate-BZUx4TnE.js');
9
9
  var helpers = require('./helpers-Dj5o76mq.js');
10
10
  var lockController = require('./lock-controller-aDB9wrEf.js');
11
11
  var capacitor = require('./capacitor-DmA66EwP.js');
12
- var overlays = require('./overlays-Bg0auTQa.js');
12
+ var overlays = require('./overlays-B5JeC8OQ.js');
13
13
  var theme = require('./theme-Bw0rpaNN.js');
14
14
  var index$4 = require('./index-2sO-zkZ2.js');
15
15
  var ionicGlobal = require('./ionic-global-DvgIzM2F.js');
@@ -1904,6 +1904,9 @@ const Modal = class {
1904
1904
  this.inline = false;
1905
1905
  // Whether or not modal is being dismissed via gesture
1906
1906
  this.gestureAnimationDismissing = false;
1907
+ // True from the first safe-area write in `present()` until the enter
1908
+ // animation settles. A position-based read in that window is not the rest position.
1909
+ this.isPresenting = false;
1907
1910
  this.presented = false;
1908
1911
  /** @internal */
1909
1912
  this.hasController = false;
@@ -2140,6 +2143,17 @@ const Modal = class {
2140
2143
  const { el } = this;
2141
2144
  overlays.prepareOverlay(el);
2142
2145
  this.triggerChanged();
2146
+ // A disconnect tears down state this presentation still needs and cannot
2147
+ // tell a re-insert from a removal, so put it back.
2148
+ if (this.presented || this.isPresenting) {
2149
+ this.restoreSafeAreaOverrides();
2150
+ }
2151
+ // A relocation from `willPresent` reaches these before `present()` has
2152
+ // applied the lock, so they apply it instead. Both are idempotent.
2153
+ if (this.presented) {
2154
+ overlays.restoreRootFocusTrapAccessibility(el);
2155
+ this.initParentRemovalObserver();
2156
+ }
2143
2157
  }
2144
2158
  disconnectedCallback() {
2145
2159
  this.triggerController.removeClickListener();
@@ -2317,25 +2331,33 @@ const Modal = class {
2317
2331
  // Recalculate isSheetModal before safe-area setup because framework
2318
2332
  // bindings (e.g., Angular) may not have been applied when componentWillLoad ran.
2319
2333
  this.isSheetModal = this.breakpoints !== undefined && this.initialBreakpoint !== undefined;
2320
- // Set initial safe-area overrides before animation
2321
- this.setInitialSafeAreaOverrides();
2334
+ this.isPresenting = true;
2322
2335
  const hasCardModal = presentingElement !== undefined;
2323
- /**
2324
- * We need to change the status bar at the
2325
- * start of the animation so that it completes
2326
- * by the time the card animation is done.
2327
- */
2328
- if (hasCardModal && ionicGlobal.getIonMode(this) === 'ios') {
2329
- // Cache the original status bar color before the modal is presented
2330
- this.statusBarStyle = await StatusBar.getStyle();
2331
- setCardStatusBarDark();
2336
+ // Wrapped so a throw cannot strand `isPresenting`, which would suppress
2337
+ // the position-based correction for the life of this instance.
2338
+ try {
2339
+ // Set initial safe-area overrides before animation
2340
+ this.setInitialSafeAreaOverrides();
2341
+ /**
2342
+ * We need to change the status bar at the
2343
+ * start of the animation so that it completes
2344
+ * by the time the card animation is done.
2345
+ */
2346
+ if (hasCardModal && ionicGlobal.getIonMode(this) === 'ios') {
2347
+ // Cache the original status bar color before the modal is presented
2348
+ this.statusBarStyle = await StatusBar.getStyle();
2349
+ setCardStatusBarDark();
2350
+ }
2351
+ await overlays.present(this, 'modalEnter', iosEnterAnimation, mdEnterAnimation, {
2352
+ presentingEl: presentingElement,
2353
+ currentBreakpoint: this.initialBreakpoint,
2354
+ backdropBreakpoint: this.backdropBreakpoint,
2355
+ expandToScroll: this.expandToScroll,
2356
+ });
2357
+ }
2358
+ finally {
2359
+ this.isPresenting = false;
2332
2360
  }
2333
- await overlays.present(this, 'modalEnter', iosEnterAnimation, mdEnterAnimation, {
2334
- presentingEl: presentingElement,
2335
- currentBreakpoint: this.initialBreakpoint,
2336
- backdropBreakpoint: this.backdropBreakpoint,
2337
- expandToScroll: this.expandToScroll,
2338
- });
2339
2361
  // Update safe-area based on actual position after animation
2340
2362
  this.updateSafeAreaOverrides();
2341
2363
  // Apply fullscreen safe-area padding if needed
@@ -2837,6 +2859,9 @@ const Modal = class {
2837
2859
  this.cachedOriginalParent.tagName === 'ION-APP') {
2838
2860
  return;
2839
2861
  }
2862
+ // Both `present()` and a reconnect call this, so drop any existing
2863
+ // observer. Below the guards, so a call that bails cannot leave none.
2864
+ this.cleanupParentRemovalObserver();
2840
2865
  this.parentRemovalObserver = new MutationObserver((mutations) => {
2841
2866
  mutations.forEach((mutation) => {
2842
2867
  if (mutation.type === 'childList' && mutation.removedNodes.length > 0) {
@@ -2915,6 +2940,9 @@ const Modal = class {
2915
2940
  // Set the internal offset property with the resolved root safe-area-top value
2916
2941
  if (context.isSheetModal) {
2917
2942
  this.updateSheetOffsetTop();
2943
+ // A restore after a DOM move runs this a second time for the same
2944
+ // modal, so drop the previous subscription rather than stacking one.
2945
+ this.unsubscribeRootSafeAreaTop?.();
2918
2946
  this.unsubscribeRootSafeAreaTop = onRootSafeAreaTopChange((safeAreaTop) => this.updateSheetOffsetTop(safeAreaTop));
2919
2947
  }
2920
2948
  }
@@ -3033,6 +3061,31 @@ const Modal = class {
3033
3061
  const { contentEl } = this.findContentAndFooter();
3034
3062
  this.clearContentSafeAreaPadding(contentEl);
3035
3063
  }
3064
+ /**
3065
+ * Re-applies what `cleanupSafeAreaOverrides()` removed for a modal moved
3066
+ * while presented. Anything needing no measurement goes on synchronously so
3067
+ * the modal is never painted without safe-area. The position-based
3068
+ * correction waits a frame for the layout pass it reads.
3069
+ */
3070
+ restoreSafeAreaOverrides() {
3071
+ this.setInitialSafeAreaOverrides();
3072
+ this.applyFullscreenSafeArea();
3073
+ helpers.raf(() => {
3074
+ // A dismiss or another move can land first, and a read before the enter
3075
+ // animation settles is not the rest position. `present()` redoes it.
3076
+ if (!this.presented || !this.el.isConnected || this.isPresenting) {
3077
+ return;
3078
+ }
3079
+ // A hidden subtree carries `display: none`, so there is no box and the
3080
+ // read would write the safe-area for a modal touching no edge. The
3081
+ // prediction applied above holds until the modal is shown again.
3082
+ const { width, height } = this.el.getBoundingClientRect();
3083
+ if (width === 0 && height === 0) {
3084
+ return;
3085
+ }
3086
+ this.updateSafeAreaOverrides();
3087
+ });
3088
+ }
3036
3089
  render() {
3037
3090
  const { handle, isSheetModal, presentingElement, htmlAttributes, handleBehavior, inheritedAttributes, focusTrap, expandToScroll, } = this;
3038
3091
  const showHandle = handle !== false && isSheetModal;
@@ -3040,7 +3093,7 @@ const Modal = class {
3040
3093
  const isCardModal = presentingElement !== undefined && mode === 'ios';
3041
3094
  const isHandleCycle = handleBehavior === 'cycle';
3042
3095
  const isSheetModalWithHandle = isSheetModal && showHandle;
3043
- return (index$3.h(index$3.Host, { key: 'a471c293319cb6ee5ffb7ffafec0b8413e4289de', "no-router": true,
3096
+ return (index$3.h(index$3.Host, { key: '7c9c736052c5a788cf31040b6b2386dd9ea7454b', "no-router": true,
3044
3097
  // Allow the modal to be navigable when the handle is focusable
3045
3098
  tabIndex: isHandleCycle && isSheetModalWithHandle ? 0 : -1, ...htmlAttributes, style: {
3046
3099
  zIndex: `${20000 + this.overlayIndex}`,
@@ -3053,7 +3106,7 @@ const Modal = class {
3053
3106
  'overlay-hidden': true,
3054
3107
  [overlays.FOCUS_TRAP_DISABLE_CLASS]: focusTrap === false,
3055
3108
  ...theme.getClassMap(this.cssClass),
3056
- }, onIonBackdropTap: this.onBackdropTap, onIonModalDidPresent: this.onLifecycle, onIonModalWillPresent: this.onLifecycle, onIonModalWillDismiss: this.onLifecycle, onIonModalDidDismiss: this.onLifecycle, onFocus: this.onModalFocus }, index$3.h("ion-backdrop", { key: 'f67934afd4ddad64623856fb641cb6db846df428', ref: (el) => (this.backdropEl = el), visible: this.showBackdrop, tappable: this.backdropDismiss, part: "backdrop" }), mode === 'ios' && index$3.h("div", { key: 'a23f5cb6cf60c8f717c1dd556bce375829c80a43', class: "modal-shadow" }), index$3.h("div", { key: '51eb6cd1070e4b7e49840a78dc7437238c867a01',
3109
+ }, onIonBackdropTap: this.onBackdropTap, onIonModalDidPresent: this.onLifecycle, onIonModalWillPresent: this.onLifecycle, onIonModalWillDismiss: this.onLifecycle, onIonModalDidDismiss: this.onLifecycle, onFocus: this.onModalFocus }, index$3.h("ion-backdrop", { key: '01b5b5765fb3a32393c736065c824bf760728823', ref: (el) => (this.backdropEl = el), visible: this.showBackdrop, tappable: this.backdropDismiss, part: "backdrop" }), mode === 'ios' && index$3.h("div", { key: '40fa7727a0923f7d7461dd027f1511a6a148d84a', class: "modal-shadow" }), index$3.h("div", { key: '9af934b607f60795bd5bff7bee6eacfc9b14fa35',
3057
3110
  /*
3058
3111
  role and aria-modal must be used on the
3059
3112
  same element. They must also be set inside the
@@ -3066,9 +3119,9 @@ const Modal = class {
3066
3119
  without the tabindex focus() would be a no-op and screen readers
3067
3120
  may not properly announce the dialog and its content when it opens.
3068
3121
  */
3069
- role: "dialog", ...inheritedAttributes, "aria-modal": "true", tabIndex: -1, class: "modal-wrapper ion-overlay-wrapper", part: "content", ref: (el) => (this.wrapperEl = el) }, showHandle && (index$3.h("button", { key: 'f49a144642b5c227c0c2b00b33f4c7e37a2bf088', class: "modal-handle",
3122
+ role: "dialog", ...inheritedAttributes, "aria-modal": "true", tabIndex: -1, class: "modal-wrapper ion-overlay-wrapper", part: "content", ref: (el) => (this.wrapperEl = el) }, showHandle && (index$3.h("button", { key: 'df84a6806413b13efc5b03c862edde83151579a7', class: "modal-handle",
3070
3123
  // Prevents the handle from receiving keyboard focus when it does not cycle
3071
- 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) })), index$3.h("slot", { key: 'e781c0d5881762c6b61aa432721a38b49580ff64', onSlotchange: this.onSlotChange }))));
3124
+ 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) })), index$3.h("slot", { key: '072d3929ec6f25481946b184d8ac24219011e783', onSlotchange: this.onSlotChange }))));
3072
3125
  }
3073
3126
  get el() { return index$3.getElement(this); }
3074
3127
  static get watchers() { return {
@@ -4,7 +4,7 @@
4
4
  'use strict';
5
5
 
6
6
  var index = require('./index-BrVMoqUk.js');
7
- var overlays = require('./overlays-Bg0auTQa.js');
7
+ var overlays = require('./overlays-B5JeC8OQ.js');
8
8
  var frameworkDelegate = require('./framework-delegate-BZUx4TnE.js');
9
9
  var helpers = require('./helpers-Dj5o76mq.js');
10
10
  var lockController = require('./lock-controller-aDB9wrEf.js');
@@ -1293,6 +1293,13 @@ const Popover = class {
1293
1293
  const { configureTriggerInteraction, el } = this;
1294
1294
  overlays.prepareOverlay(el);
1295
1295
  configureTriggerInteraction();
1296
+ // Re-apply the root lock if moved without dismiss() being called
1297
+ if (this.presented) {
1298
+ overlays.restoreRootFocusTrapAccessibility(el);
1299
+ // The disconnect tore down the header observer, and `present()` is its
1300
+ // only other caller, so a move mid-layout would lose the recalculation.
1301
+ this.recalculateContentOnHeaderReady();
1302
+ }
1296
1303
  }
1297
1304
  disconnectedCallback() {
1298
1305
  const { destroyTriggerInteraction } = this;
@@ -1470,6 +1477,9 @@ const Popover = class {
1470
1477
  if (!header || contentElements.length === 0) {
1471
1478
  return;
1472
1479
  }
1480
+ // Both `present()` and a reconnect call this, so drop any existing
1481
+ // observer. Below the guards, so a call that bails cannot leave none.
1482
+ this.headerResizeObserver?.disconnect();
1473
1483
  this.headerResizeObserver = new ResizeObserver(async () => {
1474
1484
  if (header.offsetHeight > 0) {
1475
1485
  this.headerResizeObserver?.disconnect();
@@ -1542,7 +1552,7 @@ const Popover = class {
1542
1552
  const { onLifecycle, parentPopover, dismissOnSelect, side, arrow, htmlAttributes, focusTrap } = this;
1543
1553
  const desktop = ionicGlobal.isPlatform('desktop');
1544
1554
  const enableArrow = arrow && !parentPopover;
1545
- return (index.h(index.Host, { key: '52ffc7116969ae1b8b927e67f2acbfe5f5434f70', "aria-modal": "true", "no-router": true, tabindex: "-1", ...htmlAttributes, style: {
1555
+ return (index.h(index.Host, { key: '5a41565e6129c73112835aab18b7ae213de040f2', "aria-modal": "true", "no-router": true, tabindex: "-1", ...htmlAttributes, style: {
1546
1556
  zIndex: `${20000 + this.overlayIndex}`,
1547
1557
  }, class: {
1548
1558
  ...theme.getClassMap(this.cssClass),
@@ -1553,7 +1563,7 @@ const Popover = class {
1553
1563
  [`popover-side-${side}`]: true,
1554
1564
  [overlays.FOCUS_TRAP_DISABLE_CLASS]: focusTrap === false,
1555
1565
  'popover-nested': !!parentPopover,
1556
- }, onIonPopoverDidPresent: onLifecycle, onIonPopoverWillPresent: onLifecycle, onIonPopoverWillDismiss: onLifecycle, onIonPopoverDidDismiss: onLifecycle, onIonBackdropTap: this.onBackdropTap }, !parentPopover && index.h("ion-backdrop", { key: '79a8e851dbd22119e21ffb2f4709c555251045c8', tappable: this.backdropDismiss, visible: this.showBackdrop, part: "backdrop" }), index.h("div", { key: 'c46c9f62c3ef3f902e23f27404eaa446a5d8cf88', class: "popover-wrapper ion-overlay-wrapper", onClick: dismissOnSelect ? () => this.dismiss() : undefined }, enableArrow && index.h("div", { key: '9fdcbbc4ea6c7bde74f646d48eb77296b2c9dd83', class: "popover-arrow", part: "arrow" }), index.h("div", { key: '138e56bb83462ebb0908edb522b45ecf94b82aeb', class: "popover-content", part: "content" }, index.h("slot", { key: 'daeefcf002e8d89e91d6e0faf7f63f75b68a2dbe' })))));
1566
+ }, onIonPopoverDidPresent: onLifecycle, onIonPopoverWillPresent: onLifecycle, onIonPopoverWillDismiss: onLifecycle, onIonPopoverDidDismiss: onLifecycle, onIonBackdropTap: this.onBackdropTap }, !parentPopover && index.h("ion-backdrop", { key: '59e724e4a6ce25eae78ccf1668effd988be7232a', tappable: this.backdropDismiss, visible: this.showBackdrop, part: "backdrop" }), index.h("div", { key: '88d787ca20ac7cbacc4190bd771645dbe6083ad9', class: "popover-wrapper ion-overlay-wrapper", onClick: dismissOnSelect ? () => this.dismiss() : undefined }, enableArrow && index.h("div", { key: 'f766e6413cc4545f27a43d5931c033261e3daa2c', class: "popover-arrow", part: "arrow" }), index.h("div", { key: '675bddff0c071378858bb2d07bec91da68c2652a', class: "popover-content", part: "content" }, index.h("slot", { key: '1c446b867083016b24b1b3baccd19b9fc08e114a' })))));
1557
1567
  }
1558
1568
  get el() { return index.getElement(this); }
1559
1569
  static get watchers() { return {
@@ -631,6 +631,11 @@ const SegmentButton = class {
631
631
  helpers.addEventListener(segmentEl, 'ionSelect', this.updateState);
632
632
  helpers.addEventListener(segmentEl, 'ionStyle', this.updateStyle);
633
633
  }
634
+ // Prevent buttons from being disabled when associated with segment content
635
+ if (this.contentId && this.disabled) {
636
+ index.printIonWarning(`[ion-segment-button] - Segment buttons cannot be disabled when associated with an <ion-segment-content>.`);
637
+ this.disabled = false;
638
+ }
634
639
  }
635
640
  disconnectedCallback() {
636
641
  const segmentEl = this.segmentEl;
@@ -647,15 +652,6 @@ const SegmentButton = class {
647
652
  // Return if there is no contentId defined
648
653
  if (!this.contentId)
649
654
  return;
650
- /**
651
- * Checked here rather than in `connectedCallback` so frameworks that assign element
652
- * props after inserting the element have set `disabled` by now. A disabled ion-segment
653
- * pushes that onto its buttons too, which this guard should not undo.
654
- */
655
- if (this.disabled && this.segmentEl?.disabled !== true) {
656
- index.printIonWarning(`[ion-segment-button] - Segment buttons cannot be disabled when associated with an <ion-segment-content>.`);
657
- this.disabled = false;
658
- }
659
655
  // Attempt to find the Segment Content by its contentId
660
656
  const segmentContent = document.getElementById(this.contentId);
661
657
  // If no associated Segment Content exists, log an error and return
@@ -690,7 +686,7 @@ const SegmentButton = class {
690
686
  const { checked, type, disabled, hasIcon, hasLabel, layout, segmentEl } = this;
691
687
  const mode = ionicGlobal.getIonMode(this);
692
688
  const hasSegmentColor = () => segmentEl?.color !== undefined;
693
- return (index.h(index.Host, { key: 'd8acc71d87e26b32f20b2d0f9a6acc9f0dab0dee', class: {
689
+ return (index.h(index.Host, { key: '2b4b7ad8b981d2ee39aba5b9c734de8a5bd045ba', class: {
694
690
  [mode]: true,
695
691
  'in-toolbar': theme.hostContext('ion-toolbar', this.el),
696
692
  'in-toolbar-color': theme.hostContext('ion-toolbar[color]', this.el),
@@ -706,7 +702,7 @@ const SegmentButton = class {
706
702
  'ion-activatable': true,
707
703
  'ion-activatable-instant': true,
708
704
  'ion-focusable': true,
709
- } }, index.h("button", { key: 'ce65da07b1a012bce353b3c6c24eecfbd4071d54', "aria-selected": checked ? 'true' : 'false', role: "tab", ref: (el) => (this.nativeEl = el), type: type, class: "button-native", part: "native", disabled: disabled, ...this.inheritedAttributes }, index.h("span", { key: '6c4b4dcd20e407e5dc31674bea23633d8a09187a', class: "button-inner" }, index.h("slot", { key: '817145ae77a3c5ada88ab95723d881f1bde96b2a' })), mode === 'md' && index.h("ion-ripple-effect", { key: '054d7e5e2a13013914fac6bbde8efdb28d9b459e' })), index.h("div", { key: 'c48074881a302510ad4752eeb9383222cd3a54cb', part: "indicator", class: "segment-button-indicator segment-button-indicator-animated" }, index.h("div", { key: '7672a6b10ee18ad026b798fbd8207dc3cd5683f9', part: "indicator-background", class: "segment-button-indicator-background" }))));
705
+ } }, index.h("button", { key: '9cc58cfbe70844a0f5e1a307002e0d04e044be99', "aria-selected": checked ? 'true' : 'false', role: "tab", ref: (el) => (this.nativeEl = el), type: type, class: "button-native", part: "native", disabled: disabled, ...this.inheritedAttributes }, index.h("span", { key: '6443551951ce589efdb2d0e1fd1a14e35b82ed19', class: "button-inner" }, index.h("slot", { key: 'c3548b469ae6e5477d8372cb28bc70dff3a24d18' })), mode === 'md' && index.h("ion-ripple-effect", { key: 'fb176415ca1702766c4d7a5964ec0bb467bdcb55' })), index.h("div", { key: 'aa22b4752f8336d0e232a1e0894e87023b284a08', part: "indicator", class: "segment-button-indicator segment-button-indicator-animated" }, index.h("div", { key: '61e29ea52048fa0583fce6c20c81614a4d0199cb', part: "indicator-background", class: "segment-button-indicator-background" }))));
710
706
  }
711
707
  get el() { return index.getElement(this); }
712
708
  static get watchers() { return {
@@ -6,7 +6,7 @@
6
6
  var index = require('./index-BrVMoqUk.js');
7
7
  var ionicGlobal = require('./ionic-global-DvgIzM2F.js');
8
8
  var overlayControlLabel = require('./overlay-control-label-D2sdL2O8.js');
9
- var overlays = require('./overlays-Bg0auTQa.js');
9
+ var overlays = require('./overlays-B5JeC8OQ.js');
10
10
  var selectOptionRender = require('./select-option-render-7m1WdFzF.js');
11
11
  var theme = require('./theme-Bw0rpaNN.js');
12
12
  require('./index-DkNv4J_i.js');
@@ -9,7 +9,7 @@ var slotMutationController = require('./slot-mutation-controller-BJEJzx_e.js');
9
9
  var compareWithUtils = require('./compare-with-utils-DSicavqM.js');
10
10
  var validity = require('./validity-BpS37YFM.js');
11
11
  var helpers = require('./helpers-Dj5o76mq.js');
12
- var overlays = require('./overlays-Bg0auTQa.js');
12
+ var overlays = require('./overlays-B5JeC8OQ.js');
13
13
  var dir = require('./dir-zwDPRE5K.js');
14
14
  var index$2 = require('./index-EJLjthTK.js');
15
15
  var theme = require('./theme-Bw0rpaNN.js');
@@ -7,7 +7,7 @@ var index$1 = require('./index-BrVMoqUk.js');
7
7
  var config = require('./config-CjUbvdPW.js');
8
8
  var helpers = require('./helpers-Dj5o76mq.js');
9
9
  var lockController = require('./lock-controller-aDB9wrEf.js');
10
- var overlays = require('./overlays-Bg0auTQa.js');
10
+ var overlays = require('./overlays-B5JeC8OQ.js');
11
11
  var index$3 = require('./index-EJLjthTK.js');
12
12
  var theme = require('./theme-Bw0rpaNN.js');
13
13
  var ionicGlobal = require('./ionic-global-DvgIzM2F.js');
@@ -102,6 +102,15 @@ const activeAnimations = new WeakMap();
102
102
  const isBackdropAlwaysBlocking = (el) => {
103
103
  return el.showBackdrop !== false && !((el.backdropBreakpoint ?? 0) > 0);
104
104
  };
105
+ /**
106
+ * Whether the overlay takes the app root out of the accessibility tree and
107
+ * blocks body scroll while presented. Toasts never do, modal and popover can
108
+ * opt out with `focusTrap={false}`, and a backdrop that does not block
109
+ * (`showBackdrop={false}`, or a `backdropBreakpoint` above 0) is excluded.
110
+ */
111
+ const locksAppRoot = (el) => {
112
+ return el.tagName !== 'ION-TOAST' && el.focusTrap !== false && isBackdropAlwaysBlocking(el);
113
+ };
105
114
  const createController = (tagName) => {
106
115
  return {
107
116
  create(options) {
@@ -472,6 +481,13 @@ const getPresentedOverlay = (doc, overlayTag, id) => {
472
481
  // Otherwise, return the last overlay with the given id
473
482
  return (id === undefined ? overlays : overlays.filter((o) => o.id === id)).slice(-1)[0];
474
483
  };
484
+ /**
485
+ * The element an app nests its views under. Overlays hide this rather than the
486
+ * whole root, so the overlay itself (a sibling) stays reachable.
487
+ */
488
+ const getViewContainer = () => {
489
+ return getAppRoot(document).querySelector('ion-router-outlet, #ion-view-container-root');
490
+ };
475
491
  /**
476
492
  * When an overlay is presented, the main
477
493
  * focus is the overlay not the page content.
@@ -495,8 +511,7 @@ const getPresentedOverlay = (doc, overlayTag, id) => {
495
511
  * for main content.
496
512
  */
497
513
  const setRootAriaHidden = (hidden = false) => {
498
- const root = getAppRoot(document);
499
- const viewContainer = root.querySelector('ion-router-outlet, #ion-view-container-root');
514
+ const viewContainer = getViewContainer();
500
515
  if (!viewContainer) {
501
516
  return;
502
517
  }
@@ -521,15 +536,40 @@ const cleanupRootFocusTrapAccessibility = () => {
521
536
  return;
522
537
  }
523
538
  const remainingOverlays = getPresentedOverlays(document);
524
- const hasRemainingLocking = remainingOverlays.some((o) => {
525
- const el = o;
526
- return el.tagName !== 'ION-TOAST' && el.focusTrap !== false && isBackdropAlwaysBlocking(el);
527
- });
539
+ const hasRemainingLocking = remainingOverlays.some((o) => locksAppRoot(o));
528
540
  if (!hasRemainingLocking) {
529
541
  setRootAriaHidden(false);
530
542
  document.body.classList.remove(gestureController.BACKDROP_NO_SCROLL);
531
543
  }
532
544
  };
545
+ /**
546
+ * Shared by `present()` and the restore below, which have to stay in lockstep.
547
+ */
548
+ const applyRootLock = (el) => {
549
+ // Hiding the container the overlay now sits in would hide the overlay too.
550
+ if (!getViewContainer()?.contains(el)) {
551
+ setRootAriaHidden(true);
552
+ }
553
+ document.body.classList.add(gestureController.BACKDROP_NO_SCROLL);
554
+ };
555
+ /**
556
+ * Re-applies the root lock that `cleanupRootFocusTrapAccessibility()` released.
557
+ * Call from `connectedCallback` when the overlay is still presented.
558
+ *
559
+ * A synchronous move keeps the overlay connected, so the lock survives. A
560
+ * detach with a re-insert in a later task releases it, which is the shape a
561
+ * framework produces when it takes a subtree out and puts it back.
562
+ */
563
+ const restoreRootFocusTrapAccessibility = (overlayEl) => {
564
+ if (typeof document === 'undefined') {
565
+ return;
566
+ }
567
+ const el = overlayEl;
568
+ if (!locksAppRoot(el)) {
569
+ return;
570
+ }
571
+ applyRootLock(el);
572
+ };
533
573
  const present = async (overlay, name, iosEnterAnimation, mdEnterAnimation, opts) => {
534
574
  if (overlay.presented) {
535
575
  return;
@@ -546,15 +586,7 @@ const present = async (overlay, name, iosEnterAnimation, mdEnterAnimation, opts)
546
586
  restoreElementFocus(overlay.el);
547
587
  }
548
588
  /**
549
- * Due to accessibility guidelines, toasts do not have
550
- * focus traps.
551
- *
552
- * All other overlays should have focus traps to prevent
553
- * the keyboard focus from leaving the overlay unless
554
- * developers explicitly opt out (for example, sheet
555
- * modals that should permit background interaction).
556
- *
557
- * Note: Some apps move inline overlays to a specific container
589
+ * Some apps move inline overlays to a specific container
558
590
  * during the willPresent lifecycle (e.g., React portals via
559
591
  * onWillPresent). Defer applying aria-hidden/inert to the app
560
592
  * root until after willPresent so we can detect where the
@@ -563,18 +595,11 @@ const present = async (overlay, name, iosEnterAnimation, mdEnterAnimation, opts)
563
595
  * to avoid disabling the overlay.
564
596
  */
565
597
  const overlayEl = overlay.el;
566
- const shouldTrapFocus = overlayEl.tagName !== 'ION-TOAST' && overlayEl.focusTrap !== false;
567
- const shouldLockRoot = shouldTrapFocus && isBackdropAlwaysBlocking(overlayEl);
598
+ const shouldLockRoot = locksAppRoot(overlayEl);
568
599
  overlay.presented = true;
569
600
  overlay.willPresent.emit();
570
601
  if (shouldLockRoot) {
571
- const root = getAppRoot(document);
572
- const viewContainer = root.querySelector('ion-router-outlet, #ion-view-container-root');
573
- const overlayInsideViewContainer = viewContainer ? viewContainer.contains(overlayEl) : false;
574
- if (!overlayInsideViewContainer) {
575
- setRootAriaHidden(true);
576
- }
577
- document.body.classList.add(gestureController.BACKDROP_NO_SCROLL);
602
+ applyRootLock(overlayEl);
578
603
  }
579
604
  overlay.willPresentShorthand?.emit();
580
605
  const mode = ionicGlobal.getIonMode(overlay);
@@ -700,12 +725,9 @@ const dismiss = async (overlay, data, role, name, iosLeaveAnimation, mdLeaveAnim
700
725
  * from the root element when the last focus-trapping overlay
701
726
  * is dismissed.
702
727
  */
703
- const overlaysLockingRoot = presentedOverlays.filter((o) => {
704
- const el = o;
705
- return el.tagName !== 'ION-TOAST' && el.focusTrap !== false && isBackdropAlwaysBlocking(el);
706
- });
728
+ const overlaysLockingRoot = presentedOverlays.filter((o) => locksAppRoot(o));
707
729
  const overlayEl = overlay.el;
708
- const locksRoot = overlayEl.tagName !== 'ION-TOAST' && overlayEl.focusTrap !== false && isBackdropAlwaysBlocking(overlayEl);
730
+ const locksRoot = locksAppRoot(overlayEl);
709
731
  /**
710
732
  * If this is the last visible overlay that is trapping focus
711
733
  * then we want to re-add the root to the accessibility tree.
@@ -972,6 +994,7 @@ exports.modalController = modalController;
972
994
  exports.popoverController = popoverController;
973
995
  exports.prepareOverlay = prepareOverlay;
974
996
  exports.present = present;
997
+ exports.restoreRootFocusTrapAccessibility = restoreRootFocusTrapAccessibility;
975
998
  exports.safeCall = safeCall;
976
999
  exports.setOverlayId = setOverlayId;
977
1000
  exports.toastController = toastController;