( overlay: OverlayInterface, name: keyof IonicConfig, iosEnterAnimation: AnimationBuilder, mdEnterAnimation: AnimationBuilder, opts?: OverlayPresentOptions )
| 516 | }; |
| 517 | |
| 518 | export const present = async <OverlayPresentOptions>( |
| 519 | overlay: OverlayInterface, |
| 520 | name: keyof IonicConfig, |
| 521 | iosEnterAnimation: AnimationBuilder, |
| 522 | mdEnterAnimation: AnimationBuilder, |
| 523 | opts?: OverlayPresentOptions |
| 524 | ) => { |
| 525 | if (overlay.presented) { |
| 526 | return; |
| 527 | } |
| 528 | |
| 529 | /** |
| 530 | * When an overlay that steals focus |
| 531 | * is dismissed, focus should be returned |
| 532 | * to the element that was focused |
| 533 | * prior to the overlay opening. Toast |
| 534 | * does not steal focus and is excluded |
| 535 | * from returning focus as a result. |
| 536 | */ |
| 537 | if (overlay.el.tagName !== 'ION-TOAST') { |
| 538 | restoreElementFocus(overlay.el); |
| 539 | } |
| 540 | |
| 541 | /** |
| 542 | * Due to accessibility guidelines, toasts do not have |
| 543 | * focus traps. |
| 544 | * |
| 545 | * All other overlays should have focus traps to prevent |
| 546 | * the keyboard focus from leaving the overlay unless |
| 547 | * developers explicitly opt out (for example, sheet |
| 548 | * modals that should permit background interaction). |
| 549 | * |
| 550 | * Note: Some apps move inline overlays to a specific container |
| 551 | * during the willPresent lifecycle (e.g., React portals via |
| 552 | * onWillPresent). Defer applying aria-hidden/inert to the app |
| 553 | * root until after willPresent so we can detect where the |
| 554 | * overlay is finally inserted. If the overlay is inside the |
| 555 | * view container subtree, skip adding aria-hidden/inert there |
| 556 | * to avoid disabling the overlay. |
| 557 | */ |
| 558 | const overlayEl = overlay.el as OverlayWithFocusTrapProps; |
| 559 | const shouldTrapFocus = overlayEl.tagName !== 'ION-TOAST' && overlayEl.focusTrap !== false; |
| 560 | const shouldLockRoot = shouldTrapFocus && isBackdropAlwaysBlocking(overlayEl); |
| 561 | |
| 562 | overlay.presented = true; |
| 563 | overlay.willPresent.emit(); |
| 564 | |
| 565 | if (shouldLockRoot) { |
| 566 | const root = getAppRoot(document); |
| 567 | const viewContainer = root.querySelector('ion-router-outlet, #ion-view-container-root'); |
| 568 | const overlayInsideViewContainer = viewContainer ? viewContainer.contains(overlayEl) : false; |
| 569 | |
| 570 | if (!overlayInsideViewContainer) { |
| 571 | setRootAriaHidden(true); |
| 572 | } |
| 573 | document.body.classList.add(BACKDROP_NO_SCROLL); |
| 574 | } |
| 575 | overlay.willPresentShorthand?.emit(); |
no test coverage detected