mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2026-03-13 10:22:08 +08:00
fix(react): fixing inline overlays being a bit eager to dismiss
This commit is contained in:
@@ -71,11 +71,11 @@ export const createInlineOverlayComponent = <PropType, ElementType>(
|
||||
|
||||
setupPageVisibilityObserver() {
|
||||
/**
|
||||
* Watch for when ANY element in the document gets the ion-page-hidden class.
|
||||
* Watch for when ancestor pages get the ion-page-hidden class.
|
||||
* We use a subtree observer on a parent container because:
|
||||
* 1. The overlay's component might not have an IonPage wrapper
|
||||
* 2. Pages might be added dynamically after this component mounts
|
||||
* 3. We want to dismiss overlays when ANY navigation occurs
|
||||
* 3. We want to dismiss overlays when navigation hides the containing page
|
||||
*
|
||||
* This handles React Router 6 where pages stay mounted but get hidden.
|
||||
*/
|
||||
@@ -87,8 +87,17 @@ export const createInlineOverlayComponent = <PropType, ElementType>(
|
||||
const target = mutation.target as HTMLElement;
|
||||
// If any element gets the ion-page-hidden or ion-page-invisible class, dismiss overlay
|
||||
if (target.classList.contains('ion-page-hidden') || target.classList.contains('ion-page-invisible')) {
|
||||
this.dismissOverlay();
|
||||
return;
|
||||
/**
|
||||
* Only dismiss the overlay if the hidden page is an ancestor of the overlay,
|
||||
* not a descendant. Pages inside the overlay (e.g., IonPage in modal content)
|
||||
* may get ion-page-invisible when they mount inside an outlet context, but
|
||||
* this should not dismiss the overlay.
|
||||
*/
|
||||
const overlayElement = this.ref.current;
|
||||
if (overlayElement && !overlayElement.contains(target)) {
|
||||
this.dismissOverlay();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,18 +2,22 @@
|
||||
"name": "test-app",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"overrides": {
|
||||
"@ionic/react-router": {
|
||||
"react-router": "$react-router",
|
||||
"react-router-dom": "$react-router-dom"
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@ionic/react": "^6.6.1",
|
||||
"@ionic/react-router": "^6.6.1",
|
||||
"@types/react": "^17.0.53",
|
||||
"@types/react-dom": "^17.0.19",
|
||||
"@types/react-router": "^5.1.20",
|
||||
"@types/react-router-dom": "^5.3.3",
|
||||
"ionicons": "^8.0.13",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-router": "^5.3.4",
|
||||
"react-router-dom": "^5.3.4",
|
||||
"react-router": "^6.0.0",
|
||||
"react-router-dom": "^6.0.0",
|
||||
"react-scripts": "^5.0.0",
|
||||
"typescript": "^4.1.3"
|
||||
},
|
||||
|
||||
@@ -2,14 +2,20 @@
|
||||
"name": "test-app",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"overrides": {
|
||||
"@ionic/react-router": {
|
||||
"react-router": "$react-router",
|
||||
"react-router-dom": "$react-router-dom"
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@ionic/react": "^7.0.0",
|
||||
"@ionic/react-router": "^7.0.0",
|
||||
"ionicons": "^8.0.13",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-router": "^5.3.4",
|
||||
"react-router-dom": "^5.3.4"
|
||||
"react-router": "^6.0.0",
|
||||
"react-router-dom": "^6.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
@@ -27,8 +33,6 @@
|
||||
"@testing-library/user-event": "^14.4.3",
|
||||
"@types/react": "^18.0.27",
|
||||
"@types/react-dom": "^18.0.10",
|
||||
"@types/react-router": "^5.1.20",
|
||||
"@types/react-router-dom": "^5.3.3",
|
||||
"@vitejs/plugin-legacy": "^4.0.2",
|
||||
"@vitejs/plugin-react": "^4.0.1",
|
||||
"concurrently": "^6.3.0",
|
||||
|
||||
@@ -2,14 +2,20 @@
|
||||
"name": "test-app",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"overrides": {
|
||||
"@ionic/react-router": {
|
||||
"react-router": "$react-router",
|
||||
"react-router-dom": "$react-router-dom"
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@ionic/react": "^8.4.0",
|
||||
"@ionic/react-router": "^8.4.0",
|
||||
"ionicons": "^8.0.13",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0",
|
||||
"react-router": "^5.3.4",
|
||||
"react-router-dom": "^5.3.4"
|
||||
"react-router": "^6.0.0",
|
||||
"react-router-dom": "^6.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
@@ -27,8 +33,6 @@
|
||||
"@testing-library/user-event": "^14.4.3",
|
||||
"@types/react": "19.0.10",
|
||||
"@types/react-dom": "19.0.4",
|
||||
"@types/react-router": "^5.1.20",
|
||||
"@types/react-router-dom": "^5.3.3",
|
||||
"@vitejs/plugin-legacy": "^4.0.2",
|
||||
"@vitejs/plugin-react": "^4.0.1",
|
||||
"concurrently": "^6.3.0",
|
||||
|
||||
@@ -15,4 +15,4 @@ npm pack ../../../
|
||||
npm pack ../../../../react-router
|
||||
|
||||
# Install Dependencies
|
||||
npm install *.tgz --no-save
|
||||
npm install *.tgz --no-save --legacy-peer-deps
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { IonApp, IonRouterOutlet, setupIonicReact } from '@ionic/react';
|
||||
import { IonReactRouter } from '@ionic/react-router';
|
||||
import React from 'react';
|
||||
import { Route } from 'react-router-dom';
|
||||
import { Route } from 'react-router';
|
||||
|
||||
/* Core CSS required for Ionic components to work properly */
|
||||
import '@ionic/react/css/core.css';
|
||||
@@ -45,32 +45,32 @@ const App: React.FC = () => (
|
||||
<IonApp>
|
||||
<IonReactRouter>
|
||||
<IonRouterOutlet>
|
||||
<Route exact path="/" component={Main} />
|
||||
<Route path="/overlay-hooks" component={OverlayHooks} />
|
||||
<Route path="/overlay-components" component={OverlayComponents} />
|
||||
<Route path="/overlay-components/nested-popover" component={IonPopoverNested} />
|
||||
<Route path="/" element={<Main />} />
|
||||
<Route path="/overlay-hooks/*" element={<OverlayHooks />} />
|
||||
<Route path="/overlay-components/*" element={<OverlayComponents />} />
|
||||
<Route path="/overlay-components/nested-popover" element={<IonPopoverNested />} />
|
||||
<Route
|
||||
path="/overlay-components/modal-conditional-sibling"
|
||||
component={IonModalConditionalSibling}
|
||||
element={<IonModalConditionalSibling />}
|
||||
/>
|
||||
<Route path="/overlay-components/modal-conditional" component={IonModalConditional} />
|
||||
<Route path="/overlay-components/modal-conditional" element={<IonModalConditional />} />
|
||||
<Route
|
||||
path="/overlay-components/modal-datetime-button"
|
||||
component={IonModalDatetimeButton}
|
||||
element={<IonModalDatetimeButton />}
|
||||
/>
|
||||
<Route
|
||||
path="/overlay-components/modal-multiple-children"
|
||||
component={IonModalMultipleChildren}
|
||||
element={<IonModalMultipleChildren />}
|
||||
/>
|
||||
<Route path="/keep-contents-mounted" component={KeepContentsMounted} />
|
||||
<Route path="/navigation" component={NavComponent} />
|
||||
<Route path="/tabs" component={Tabs} />
|
||||
<Route path="/tabs-basic" component={TabsBasic} />
|
||||
<Route path="/tabs-direct-navigation" component={TabsDirectNavigation} />
|
||||
<Route path="/icons" component={Icons} />
|
||||
<Route path="/inputs" component={Inputs} />
|
||||
<Route path="/reorder-group" component={ReorderGroup} />
|
||||
<Route path="/accordion-group" component={AccordionGroup} />
|
||||
<Route path="/keep-contents-mounted" element={<KeepContentsMounted />} />
|
||||
<Route path="/navigation" element={<NavComponent />} />
|
||||
<Route path="/tabs/*" element={<Tabs />} />
|
||||
<Route path="/tabs-basic/*" element={<TabsBasic />} />
|
||||
<Route path="/tabs-direct-navigation/*" element={<TabsDirectNavigation />} />
|
||||
<Route path="/icons" element={<Icons />} />
|
||||
<Route path="/inputs" element={<Inputs />} />
|
||||
<Route path="/reorder-group" element={<ReorderGroup />} />
|
||||
<Route path="/accordion-group" element={<AccordionGroup />} />
|
||||
</IonRouterOutlet>
|
||||
</IonReactRouter>
|
||||
</IonApp>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import { IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs, IonPage } from '@ionic/react';
|
||||
import { Route, Redirect } from 'react-router';
|
||||
import { Route, Navigate } from 'react-router';
|
||||
|
||||
interface TabsProps {}
|
||||
|
||||
@@ -9,8 +9,8 @@ const Tabs: React.FC<TabsProps> = () => {
|
||||
<IonPage>
|
||||
<IonTabs>
|
||||
<IonRouterOutlet>
|
||||
<Redirect from="/tabs" to="/tabs/tab1" exact />
|
||||
<Route path="/tabs/tab1" render={() => <IonLabel>Tab 1</IonLabel>} />
|
||||
<Route index element={<Navigate to="/tabs/tab1" replace />} />
|
||||
<Route path="tab1" element={<IonLabel>Tab 1</IonLabel>} />
|
||||
</IonRouterOutlet>
|
||||
<IonTabBar slot="bottom">
|
||||
<IonTabButton tab="tab1" onClick={() => window.alert('Tab was clicked')}>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { IonContent, IonHeader, IonIcon, IonLabel, IonPage, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs, IonTitle, IonToolbar } from '@ionic/react';
|
||||
import { homeOutline, radioOutline, libraryOutline, searchOutline } from 'ionicons/icons';
|
||||
import React from 'react';
|
||||
import { Route, Redirect } from 'react-router-dom';
|
||||
import { Route, Navigate } from 'react-router';
|
||||
|
||||
const HomePage: React.FC = () => (
|
||||
<IonPage>
|
||||
@@ -59,11 +59,11 @@ const TabsDirectNavigation: React.FC = () => {
|
||||
return (
|
||||
<IonTabs data-testid="tabs-direct-navigation">
|
||||
<IonRouterOutlet>
|
||||
<Redirect exact path="/tabs-direct-navigation" to="/tabs-direct-navigation/home" />
|
||||
<Route path="/tabs-direct-navigation/home" render={() => <HomePage />} exact={true} />
|
||||
<Route path="/tabs-direct-navigation/radio" render={() => <RadioPage />} exact={true} />
|
||||
<Route path="/tabs-direct-navigation/library" render={() => <LibraryPage />} exact={true} />
|
||||
<Route path="/tabs-direct-navigation/search" render={() => <SearchPage />} exact={true} />
|
||||
<Route index element={<Navigate to="/tabs-direct-navigation/home" replace />} />
|
||||
<Route path="home" element={<HomePage />} />
|
||||
<Route path="radio" element={<RadioPage />} />
|
||||
<Route path="library" element={<LibraryPage />} />
|
||||
<Route path="search" element={<SearchPage />} />
|
||||
</IonRouterOutlet>
|
||||
|
||||
<IonTabBar slot="bottom" data-testid="tab-bar">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from '@ionic/react';
|
||||
import { Route, Redirect } from 'react-router';
|
||||
import { Route, Navigate } from 'react-router';
|
||||
import {
|
||||
addCircleOutline,
|
||||
alarm,
|
||||
@@ -26,16 +26,16 @@ const OverlayHooks: React.FC<OverlayHooksProps> = () => {
|
||||
return (
|
||||
<IonTabs>
|
||||
<IonRouterOutlet>
|
||||
<Redirect from="/overlay-components" to="/overlay-components/actionsheet" exact />
|
||||
<Route path="/overlay-components/actionsheet" component={ActionSheetComponent} />
|
||||
<Route path="/overlay-components/alert" component={AlertComponent} />
|
||||
<Route path="/overlay-components/loading" component={LoadingComponent} />
|
||||
<Route path="/overlay-components/modal-basic" component={ModalComponent} />
|
||||
<Route path="/overlay-components/modal-focus-trap" component={ModalFocusTrap} />
|
||||
<Route path="/overlay-components/modal-teleport" component={ModalTeleport} />
|
||||
<Route path="/overlay-components/picker" component={PickerComponent} />
|
||||
<Route path="/overlay-components/popover" component={PopoverComponent} />
|
||||
<Route path="/overlay-components/toast" component={ToastComponent} />
|
||||
<Route index element={<Navigate to="/overlay-components/actionsheet" replace />} />
|
||||
<Route path="actionsheet" element={<ActionSheetComponent />} />
|
||||
<Route path="alert" element={<AlertComponent />} />
|
||||
<Route path="loading" element={<LoadingComponent />} />
|
||||
<Route path="modal-basic" element={<ModalComponent />} />
|
||||
<Route path="modal-focus-trap" element={<ModalFocusTrap />} />
|
||||
<Route path="modal-teleport" element={<ModalTeleport />} />
|
||||
<Route path="picker" element={<PickerComponent />} />
|
||||
<Route path="popover" element={<PopoverComponent />} />
|
||||
<Route path="toast" element={<ToastComponent />} />
|
||||
</IonRouterOutlet>
|
||||
<IonTabBar slot="bottom">
|
||||
<IonTabButton tab="actionsheet" href="/overlay-components/actionsheet">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from '@ionic/react';
|
||||
import { Route, Redirect } from 'react-router';
|
||||
import { Route, Navigate } from 'react-router';
|
||||
import ActionSheetHook from './ActionSheetHook';
|
||||
import {
|
||||
addCircleOutline,
|
||||
@@ -24,14 +24,14 @@ const OverlayHooks: React.FC<OverlayHooksProps> = () => {
|
||||
return (
|
||||
<IonTabs>
|
||||
<IonRouterOutlet>
|
||||
<Redirect from="/overlay-hooks" to="/overlay-hooks/actionsheet" exact />
|
||||
<Route path="/overlay-hooks/actionsheet" component={ActionSheetHook} />
|
||||
<Route path="/overlay-hooks/alert" component={AlertHook} />
|
||||
<Route path="/overlay-hooks/loading" component={LoadingHook} />
|
||||
<Route path="/overlay-hooks/modal" component={ModalHook} />
|
||||
<Route path="/overlay-hooks/picker" component={PickerHook} />
|
||||
<Route path="/overlay-hooks/popover" component={PopoverHook} />
|
||||
<Route path="/overlay-hooks/toast" component={ToastHook} />
|
||||
<Route index element={<Navigate to="/overlay-hooks/actionsheet" replace />} />
|
||||
<Route path="actionsheet" element={<ActionSheetHook />} />
|
||||
<Route path="alert" element={<AlertHook />} />
|
||||
<Route path="loading" element={<LoadingHook />} />
|
||||
<Route path="modal" element={<ModalHook />} />
|
||||
<Route path="picker" element={<PickerHook />} />
|
||||
<Route path="popover" element={<PopoverHook />} />
|
||||
<Route path="toast" element={<ToastHook />} />
|
||||
</IonRouterOutlet>
|
||||
<IonTabBar slot="bottom">
|
||||
<IonTabButton tab="actionsheet" href="/overlay-hooks/actionsheet">
|
||||
|
||||
Reference in New Issue
Block a user