@ionic/core 8.7.17-dev.11767717752.14fe98a4 → 8.7.17-dev.11767891829.1a63afa3
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.
- package/components/content.js +68 -9
- package/components/modal.js +95 -27
- package/components/popover.js +11 -2
- package/dist/cjs/ion-app_8.cjs.entry.js +79 -21
- package/dist/cjs/ion-modal.cjs.entry.js +95 -27
- package/dist/cjs/ion-popover.cjs.entry.js +11 -2
- package/dist/collection/components/content/content.js +67 -8
- package/dist/collection/components/modal/modal.js +95 -26
- package/dist/collection/components/popover/animations/md.enter.js +11 -2
- package/dist/docs.json +1 -1
- package/dist/esm/ion-app_8.entry.js +68 -10
- package/dist/esm/ion-modal.entry.js +95 -27
- package/dist/esm/ion-popover.entry.js +11 -2
- package/dist/ionic/ionic.esm.js +1 -1
- package/dist/ionic/p-968a55d1.entry.js +4 -0
- package/dist/ionic/p-d9fd799f.entry.js +4 -0
- package/dist/ionic/p-ec9ca3fe.entry.js +4 -0
- package/dist/types/components/content/content.d.ts +15 -2
- package/dist/types/components/modal/modal.d.ts +18 -2
- package/hydrate/index.js +165 -37
- package/hydrate/index.mjs +165 -37
- package/package.json +1 -1
- package/dist/ionic/p-34cdcd15.entry.js +0 -4
- package/dist/ionic/p-a4827773.entry.js +0 -4
- package/dist/ionic/p-fedca459.entry.js +0 -4
package/components/content.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/*!
|
|
2
2
|
* (C) Ionic http://ionicframework.com - MIT License
|
|
3
3
|
*/
|
|
4
|
-
import { proxyCustomElement, HTMLElement, createEvent, Build, readTask,
|
|
4
|
+
import { proxyCustomElement, HTMLElement, createEvent, forceUpdate, Build, readTask, h, Host } from '@stencil/core/internal/client';
|
|
5
|
+
import { w as win } from './index9.js';
|
|
5
6
|
import { i as inheritAriaAttributes, k as hasLazyBuild, c as componentOnReady } from './helpers.js';
|
|
6
7
|
import { b as getIonMode, a as isPlatform } from './ionic-global.js';
|
|
7
8
|
import { i as isRTL } from './dir.js';
|
|
@@ -88,7 +89,11 @@ const Content = /*@__PURE__*/ proxyCustomElement(class Content extends HTMLEleme
|
|
|
88
89
|
this.inheritedAttributes = inheritAriaAttributes(this.el);
|
|
89
90
|
}
|
|
90
91
|
connectedCallback() {
|
|
91
|
-
|
|
92
|
+
var _a;
|
|
93
|
+
// Content is "main" if not inside menu/popover/modal and not nested in another ion-content
|
|
94
|
+
this.isMainContent =
|
|
95
|
+
this.el.closest('ion-menu, ion-popover, ion-modal') === null &&
|
|
96
|
+
((_a = this.el.parentElement) === null || _a === void 0 ? void 0 : _a.closest('ion-content')) === null;
|
|
92
97
|
// Detect sibling header/footer for safe-area handling
|
|
93
98
|
this.detectSiblingElements();
|
|
94
99
|
/**
|
|
@@ -121,21 +126,55 @@ const Content = /*@__PURE__*/ proxyCustomElement(class Content extends HTMLEleme
|
|
|
121
126
|
* bubbles, we can catch any instances of child tab bars loading by listening
|
|
122
127
|
* on IonTabs.
|
|
123
128
|
*/
|
|
124
|
-
this.tabsLoadCallback = () =>
|
|
129
|
+
this.tabsLoadCallback = () => {
|
|
130
|
+
this.resize();
|
|
131
|
+
// Re-detect footer when tab bar loads (it may not exist during initial detection)
|
|
132
|
+
this.updateSiblingDetection();
|
|
133
|
+
forceUpdate(this);
|
|
134
|
+
};
|
|
125
135
|
closestTabs.addEventListener('ionTabBarLoaded', this.tabsLoadCallback);
|
|
126
136
|
}
|
|
127
137
|
}
|
|
128
138
|
}
|
|
129
139
|
/**
|
|
130
|
-
* Detects sibling ion-header and ion-footer elements
|
|
131
|
-
*
|
|
140
|
+
* Detects sibling ion-header and ion-footer elements and sets up
|
|
141
|
+
* a mutation observer to handle dynamic changes (e.g., conditional rendering).
|
|
132
142
|
*/
|
|
133
143
|
detectSiblingElements() {
|
|
134
|
-
|
|
144
|
+
this.updateSiblingDetection();
|
|
145
|
+
// Watch for dynamic header/footer changes (common in React conditional rendering)
|
|
146
|
+
const parent = this.el.parentElement;
|
|
147
|
+
if (parent && !this.parentMutationObserver && win !== undefined && 'MutationObserver' in win) {
|
|
148
|
+
this.parentMutationObserver = new MutationObserver(() => {
|
|
149
|
+
const prevHasHeader = this.hasHeader;
|
|
150
|
+
const prevHasFooter = this.hasFooter;
|
|
151
|
+
this.updateSiblingDetection();
|
|
152
|
+
// Only trigger re-render if header/footer detection actually changed
|
|
153
|
+
if (prevHasHeader !== this.hasHeader || prevHasFooter !== this.hasFooter) {
|
|
154
|
+
forceUpdate(this);
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
this.parentMutationObserver.observe(parent, { childList: true });
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Updates hasHeader/hasFooter based on current DOM state.
|
|
162
|
+
* Checks both direct siblings and elements wrapped in custom components
|
|
163
|
+
* (e.g., <my-header><ion-header>...</ion-header></my-header>).
|
|
164
|
+
*/
|
|
165
|
+
updateSiblingDetection() {
|
|
135
166
|
const parent = this.el.parentElement;
|
|
136
167
|
if (parent) {
|
|
168
|
+
// First check for direct ion-header/ion-footer siblings
|
|
137
169
|
this.hasHeader = parent.querySelector(':scope > ion-header') !== null;
|
|
138
170
|
this.hasFooter = parent.querySelector(':scope > ion-footer') !== null;
|
|
171
|
+
// If not found, check if any sibling contains them (wrapped components)
|
|
172
|
+
if (!this.hasHeader) {
|
|
173
|
+
this.hasHeader = this.siblingContainsElement(parent, 'ion-header');
|
|
174
|
+
}
|
|
175
|
+
if (!this.hasFooter) {
|
|
176
|
+
this.hasFooter = this.siblingContainsElement(parent, 'ion-footer');
|
|
177
|
+
}
|
|
139
178
|
}
|
|
140
179
|
// If no footer found, check if we're inside ion-tabs which has ion-tab-bar
|
|
141
180
|
if (!this.hasFooter) {
|
|
@@ -145,8 +184,28 @@ const Content = /*@__PURE__*/ proxyCustomElement(class Content extends HTMLEleme
|
|
|
145
184
|
}
|
|
146
185
|
}
|
|
147
186
|
}
|
|
187
|
+
/**
|
|
188
|
+
* Checks if any sibling element of ion-content contains the specified element.
|
|
189
|
+
* Only searches one level deep to avoid finding elements in nested pages.
|
|
190
|
+
*/
|
|
191
|
+
siblingContainsElement(parent, tagName) {
|
|
192
|
+
for (const sibling of parent.children) {
|
|
193
|
+
// Skip ion-content itself
|
|
194
|
+
if (sibling === this.el)
|
|
195
|
+
continue;
|
|
196
|
+
// Check if this sibling contains the target element as an immediate child
|
|
197
|
+
if (sibling.querySelector(`:scope > ${tagName}`) !== null) {
|
|
198
|
+
return true;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return false;
|
|
202
|
+
}
|
|
148
203
|
disconnectedCallback() {
|
|
204
|
+
var _a;
|
|
149
205
|
this.onScrollEnd();
|
|
206
|
+
// Clean up mutation observer to prevent memory leaks
|
|
207
|
+
(_a = this.parentMutationObserver) === null || _a === void 0 ? void 0 : _a.disconnect();
|
|
208
|
+
this.parentMutationObserver = undefined;
|
|
150
209
|
if (hasLazyBuild(this.el)) {
|
|
151
210
|
/**
|
|
152
211
|
* The event listener and tabs caches need to
|
|
@@ -399,7 +458,7 @@ const Content = /*@__PURE__*/ proxyCustomElement(class Content extends HTMLEleme
|
|
|
399
458
|
const forceOverscroll = this.shouldForceOverscroll();
|
|
400
459
|
const transitionShadow = mode === 'ios';
|
|
401
460
|
this.resize();
|
|
402
|
-
return (h(Host, Object.assign({ key: '
|
|
461
|
+
return (h(Host, Object.assign({ key: 'f7218f733e4022a30875441bd949747537d28aa1', role: isMainContent ? 'main' : undefined, class: createColorClasses(this.color, {
|
|
403
462
|
[mode]: true,
|
|
404
463
|
'content-sizing': hostContext('ion-popover', this.el),
|
|
405
464
|
overscroll: forceOverscroll,
|
|
@@ -409,12 +468,12 @@ const Content = /*@__PURE__*/ proxyCustomElement(class Content extends HTMLEleme
|
|
|
409
468
|
}), style: {
|
|
410
469
|
'--offset-top': `${this.cTop}px`,
|
|
411
470
|
'--offset-bottom': `${this.cBottom}px`,
|
|
412
|
-
} }, inheritedAttributes), h("div", { key: '
|
|
471
|
+
} }, inheritedAttributes), h("div", { key: 'b735ec68c18c0b99c3595bb194029830e6542cde', ref: (el) => (this.backgroundContentEl = el), id: "background-content", part: "background" }), fixedSlotPlacement === 'before' ? h("slot", { name: "fixed" }) : null, h("div", { key: 'e76c00d030342d44ade6648c3f9e32ca990787ba', class: {
|
|
413
472
|
'inner-scroll': true,
|
|
414
473
|
'scroll-x': scrollX,
|
|
415
474
|
'scroll-y': scrollY,
|
|
416
475
|
overscroll: (scrollX || scrollY) && forceOverscroll,
|
|
417
|
-
}, ref: (scrollEl) => (this.scrollEl = scrollEl), onScroll: this.scrollEvents ? (ev) => this.onScroll(ev) : undefined, part: "scroll" }, h("slot", { key: '
|
|
476
|
+
}, ref: (scrollEl) => (this.scrollEl = scrollEl), onScroll: this.scrollEvents ? (ev) => this.onScroll(ev) : undefined, part: "scroll" }, h("slot", { key: '9049be4cea9b5da5ec1e1012248b05286fddeb7a' })), transitionShadow ? (h("div", { class: "transition-effect" }, h("div", { class: "transition-cover" }), h("div", { class: "transition-shadow" }))) : null, fixedSlotPlacement === 'after' ? h("slot", { name: "fixed" }) : null));
|
|
418
477
|
}
|
|
419
478
|
get el() { return this; }
|
|
420
479
|
static get style() { return contentCss; }
|
package/components/modal.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* (C) Ionic http://ionicframework.com - MIT License
|
|
3
3
|
*/
|
|
4
4
|
import { proxyCustomElement, HTMLElement, createEvent, writeTask, h, Host } from '@stencil/core/internal/client';
|
|
5
|
+
import { w as win } from './index9.js';
|
|
5
6
|
import { a as findClosestIonContent, i as isIonContent, d as disableContentScrollY, r as resetContentScrollY, f as findIonContent, p as printIonContentErrorMsg } from './index8.js';
|
|
6
7
|
import { C as CoreDelegate, a as attachComponent, d as detachComponent } from './framework-delegate.js';
|
|
7
8
|
import { f as clamp, g as getElementRoot, r as raf, d as inheritAttributes, k as hasLazyBuild } from './helpers.js';
|
|
@@ -16,7 +17,6 @@ import { KEYBOARD_DID_OPEN } from './keyboard.js';
|
|
|
16
17
|
import { c as createAnimation } from './animation.js';
|
|
17
18
|
import { g as getTimeGivenProgression } from './cubic-bezier.js';
|
|
18
19
|
import { createGesture } from './index3.js';
|
|
19
|
-
import { w as win } from './index9.js';
|
|
20
20
|
import { d as defineCustomElement$1 } from './backdrop.js';
|
|
21
21
|
|
|
22
22
|
var Style;
|
|
@@ -1506,6 +1506,8 @@ const Modal = /*@__PURE__*/ proxyCustomElement(class Modal extends HTMLElement {
|
|
|
1506
1506
|
this.gestureAnimationDismissing = false;
|
|
1507
1507
|
// Whether to skip coordinate-based safe-area detection (for fullscreen phone modals)
|
|
1508
1508
|
this.skipSafeAreaCoordinateDetection = false;
|
|
1509
|
+
// Track previous safe-area state to avoid redundant DOM writes
|
|
1510
|
+
this.prevSafeAreaState = { top: false, bottom: false, left: false, right: false };
|
|
1509
1511
|
this.presented = false;
|
|
1510
1512
|
/** @internal */
|
|
1511
1513
|
this.hasController = false;
|
|
@@ -1696,7 +1698,8 @@ const Modal = /*@__PURE__*/ proxyCustomElement(class Modal extends HTMLElement {
|
|
|
1696
1698
|
}
|
|
1697
1699
|
}
|
|
1698
1700
|
onWindowResize() {
|
|
1699
|
-
//
|
|
1701
|
+
// Invalidate safe-area cache on resize (device rotation may change values)
|
|
1702
|
+
this.cachedSafeAreas = undefined;
|
|
1700
1703
|
this.updateSafeAreaOverrides();
|
|
1701
1704
|
// Only handle view transition for iOS card modals when no custom animations are provided
|
|
1702
1705
|
if (getIonMode(this) !== 'ios' || !this.presentingElement || this.enterAnimation || this.leaveAnimation) {
|
|
@@ -1721,6 +1724,8 @@ const Modal = /*@__PURE__*/ proxyCustomElement(class Modal extends HTMLElement {
|
|
|
1721
1724
|
this.triggerController.removeClickListener();
|
|
1722
1725
|
this.cleanupViewTransitionListener();
|
|
1723
1726
|
this.cleanupParentRemovalObserver();
|
|
1727
|
+
// Reset safe-area state to handle removal without dismiss (e.g., framework unmount)
|
|
1728
|
+
this.resetSafeAreaState();
|
|
1724
1729
|
}
|
|
1725
1730
|
componentWillLoad() {
|
|
1726
1731
|
var _a;
|
|
@@ -2155,8 +2160,28 @@ const Modal = /*@__PURE__*/ proxyCustomElement(class Modal extends HTMLElement {
|
|
|
2155
2160
|
*/
|
|
2156
2161
|
applyFullscreenSafeArea() {
|
|
2157
2162
|
this.skipSafeAreaCoordinateDetection = true;
|
|
2163
|
+
this.updateFooterPadding();
|
|
2164
|
+
// Watch for dynamic footer additions/removals (e.g., async data loading)
|
|
2165
|
+
// Use subtree:true to support wrapped footers in framework components
|
|
2166
|
+
// (e.g., <my-footer><ion-footer>...</ion-footer></my-footer>)
|
|
2167
|
+
if (!this.footerObserver && win !== undefined && 'MutationObserver' in win) {
|
|
2168
|
+
this.footerObserver = new MutationObserver(() => this.updateFooterPadding());
|
|
2169
|
+
this.footerObserver.observe(this.el, { childList: true, subtree: true });
|
|
2170
|
+
}
|
|
2171
|
+
}
|
|
2172
|
+
/**
|
|
2173
|
+
* Updates wrapper padding based on footer presence.
|
|
2174
|
+
* Called initially and when footer is dynamically added/removed.
|
|
2175
|
+
*/
|
|
2176
|
+
updateFooterPadding() {
|
|
2177
|
+
if (!this.wrapperEl)
|
|
2178
|
+
return;
|
|
2158
2179
|
const hasFooter = this.el.querySelector('ion-footer') !== null;
|
|
2159
|
-
if (
|
|
2180
|
+
if (hasFooter) {
|
|
2181
|
+
this.wrapperEl.style.removeProperty('padding-bottom');
|
|
2182
|
+
this.wrapperEl.style.removeProperty('box-sizing');
|
|
2183
|
+
}
|
|
2184
|
+
else {
|
|
2160
2185
|
this.wrapperEl.style.setProperty('padding-bottom', 'var(--ion-safe-area-bottom, 0px)');
|
|
2161
2186
|
this.wrapperEl.style.setProperty('box-sizing', 'border-box');
|
|
2162
2187
|
}
|
|
@@ -2172,23 +2197,52 @@ const Modal = /*@__PURE__*/ proxyCustomElement(class Modal extends HTMLElement {
|
|
|
2172
2197
|
style.setProperty('--ion-safe-area-left', '0px');
|
|
2173
2198
|
style.setProperty('--ion-safe-area-right', '0px');
|
|
2174
2199
|
}
|
|
2200
|
+
/**
|
|
2201
|
+
* Resets all safe-area related state and styles.
|
|
2202
|
+
* Called during dismiss and disconnectedCallback to ensure clean state
|
|
2203
|
+
* for re-presentation of inline modals.
|
|
2204
|
+
*/
|
|
2205
|
+
resetSafeAreaState() {
|
|
2206
|
+
var _a;
|
|
2207
|
+
this.skipSafeAreaCoordinateDetection = false;
|
|
2208
|
+
this.cachedSafeAreas = undefined;
|
|
2209
|
+
this.prevSafeAreaState = { top: false, bottom: false, left: false, right: false };
|
|
2210
|
+
(_a = this.footerObserver) === null || _a === void 0 ? void 0 : _a.disconnect();
|
|
2211
|
+
this.footerObserver = undefined;
|
|
2212
|
+
// Clear wrapper styles that may have been set for safe-area handling
|
|
2213
|
+
if (this.wrapperEl) {
|
|
2214
|
+
this.wrapperEl.style.removeProperty('padding-bottom');
|
|
2215
|
+
this.wrapperEl.style.removeProperty('box-sizing');
|
|
2216
|
+
}
|
|
2217
|
+
// Clear safe-area CSS variable overrides
|
|
2218
|
+
const style = this.el.style;
|
|
2219
|
+
style.removeProperty('--ion-safe-area-top');
|
|
2220
|
+
style.removeProperty('--ion-safe-area-bottom');
|
|
2221
|
+
style.removeProperty('--ion-safe-area-left');
|
|
2222
|
+
style.removeProperty('--ion-safe-area-right');
|
|
2223
|
+
}
|
|
2175
2224
|
/**
|
|
2176
2225
|
* Gets the root safe-area values from the document element.
|
|
2177
|
-
*
|
|
2226
|
+
* Uses cached values during gestures to avoid getComputedStyle calls.
|
|
2178
2227
|
*/
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2228
|
+
getSafeAreaValues() {
|
|
2229
|
+
if (!this.cachedSafeAreas) {
|
|
2230
|
+
const rootStyle = getComputedStyle(document.documentElement);
|
|
2231
|
+
this.cachedSafeAreas = {
|
|
2232
|
+
top: parseFloat(rootStyle.getPropertyValue('--ion-safe-area-top')) || 0,
|
|
2233
|
+
bottom: parseFloat(rootStyle.getPropertyValue('--ion-safe-area-bottom')) || 0,
|
|
2234
|
+
left: parseFloat(rootStyle.getPropertyValue('--ion-safe-area-left')) || 0,
|
|
2235
|
+
right: parseFloat(rootStyle.getPropertyValue('--ion-safe-area-right')) || 0,
|
|
2236
|
+
};
|
|
2237
|
+
}
|
|
2238
|
+
return this.cachedSafeAreas;
|
|
2187
2239
|
}
|
|
2188
2240
|
/**
|
|
2189
2241
|
* Updates safe-area CSS variable overrides based on whether the modal
|
|
2190
2242
|
* extends into each safe-area region. Called after animation
|
|
2191
2243
|
* and during gestures to handle dynamic position changes.
|
|
2244
|
+
*
|
|
2245
|
+
* Optimized to avoid redundant DOM writes by tracking previous state.
|
|
2192
2246
|
*/
|
|
2193
2247
|
updateSafeAreaOverrides() {
|
|
2194
2248
|
if (this.skipSafeAreaCoordinateDetection) {
|
|
@@ -2199,20 +2253,34 @@ const Modal = /*@__PURE__*/ proxyCustomElement(class Modal extends HTMLElement {
|
|
|
2199
2253
|
return;
|
|
2200
2254
|
}
|
|
2201
2255
|
const rect = wrapper.getBoundingClientRect();
|
|
2202
|
-
const safeAreas = this.
|
|
2256
|
+
const safeAreas = this.getSafeAreaValues();
|
|
2203
2257
|
const extendsIntoTop = rect.top < safeAreas.top;
|
|
2204
2258
|
const extendsIntoBottom = rect.bottom > window.innerHeight - safeAreas.bottom;
|
|
2205
2259
|
const extendsIntoLeft = rect.left < safeAreas.left;
|
|
2206
2260
|
const extendsIntoRight = rect.right > window.innerWidth - safeAreas.right;
|
|
2261
|
+
// Only update DOM when state actually changes
|
|
2262
|
+
const prev = this.prevSafeAreaState;
|
|
2207
2263
|
const style = this.el.style;
|
|
2208
|
-
extendsIntoTop
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2264
|
+
if (extendsIntoTop !== prev.top) {
|
|
2265
|
+
extendsIntoTop ? style.removeProperty('--ion-safe-area-top') : style.setProperty('--ion-safe-area-top', '0px');
|
|
2266
|
+
prev.top = extendsIntoTop;
|
|
2267
|
+
}
|
|
2268
|
+
if (extendsIntoBottom !== prev.bottom) {
|
|
2269
|
+
extendsIntoBottom
|
|
2270
|
+
? style.removeProperty('--ion-safe-area-bottom')
|
|
2271
|
+
: style.setProperty('--ion-safe-area-bottom', '0px');
|
|
2272
|
+
prev.bottom = extendsIntoBottom;
|
|
2273
|
+
}
|
|
2274
|
+
if (extendsIntoLeft !== prev.left) {
|
|
2275
|
+
extendsIntoLeft ? style.removeProperty('--ion-safe-area-left') : style.setProperty('--ion-safe-area-left', '0px');
|
|
2276
|
+
prev.left = extendsIntoLeft;
|
|
2277
|
+
}
|
|
2278
|
+
if (extendsIntoRight !== prev.right) {
|
|
2279
|
+
extendsIntoRight
|
|
2280
|
+
? style.removeProperty('--ion-safe-area-right')
|
|
2281
|
+
: style.setProperty('--ion-safe-area-right', '0px');
|
|
2282
|
+
prev.right = extendsIntoRight;
|
|
2283
|
+
}
|
|
2216
2284
|
}
|
|
2217
2285
|
sheetOnDismiss() {
|
|
2218
2286
|
/**
|
|
@@ -2306,8 +2374,8 @@ const Modal = /*@__PURE__*/ proxyCustomElement(class Modal extends HTMLElement {
|
|
|
2306
2374
|
}
|
|
2307
2375
|
this.currentBreakpoint = undefined;
|
|
2308
2376
|
this.animation = undefined;
|
|
2309
|
-
// Reset safe-area
|
|
2310
|
-
this.
|
|
2377
|
+
// Reset safe-area state for potential re-presentation
|
|
2378
|
+
this.resetSafeAreaState();
|
|
2311
2379
|
unlock();
|
|
2312
2380
|
return dismissed;
|
|
2313
2381
|
}
|
|
@@ -2557,20 +2625,20 @@ const Modal = /*@__PURE__*/ proxyCustomElement(class Modal extends HTMLElement {
|
|
|
2557
2625
|
const isCardModal = presentingElement !== undefined && mode === 'ios';
|
|
2558
2626
|
const isHandleCycle = handleBehavior === 'cycle';
|
|
2559
2627
|
const isSheetModalWithHandle = isSheetModal && showHandle;
|
|
2560
|
-
return (h(Host, Object.assign({ key: '
|
|
2628
|
+
return (h(Host, Object.assign({ key: '44022099fcaf047b97d1c2cb45b9b51c930e707c', "no-router": true,
|
|
2561
2629
|
// Allow the modal to be navigable when the handle is focusable
|
|
2562
2630
|
tabIndex: isHandleCycle && isSheetModalWithHandle ? 0 : -1 }, htmlAttributes, { style: {
|
|
2563
2631
|
zIndex: `${20000 + this.overlayIndex}`,
|
|
2564
|
-
}, class: Object.assign({ [mode]: true, ['modal-default']: !isCardModal && !isSheetModal, [`modal-card`]: isCardModal, [`modal-sheet`]: isSheetModal, [`modal-no-expand-scroll`]: isSheetModal && !expandToScroll, 'overlay-hidden': true, [FOCUS_TRAP_DISABLE_CLASS]: focusTrap === false }, getClassMap(this.cssClass)), onIonBackdropTap: this.onBackdropTap, onIonModalDidPresent: this.onLifecycle, onIonModalWillPresent: this.onLifecycle, onIonModalWillDismiss: this.onLifecycle, onIonModalDidDismiss: this.onLifecycle, onFocus: this.onModalFocus }), h("ion-backdrop", { key: '
|
|
2632
|
+
}, class: Object.assign({ [mode]: true, ['modal-default']: !isCardModal && !isSheetModal, [`modal-card`]: isCardModal, [`modal-sheet`]: isSheetModal, [`modal-no-expand-scroll`]: isSheetModal && !expandToScroll, 'overlay-hidden': true, [FOCUS_TRAP_DISABLE_CLASS]: focusTrap === false }, getClassMap(this.cssClass)), onIonBackdropTap: this.onBackdropTap, onIonModalDidPresent: this.onLifecycle, onIonModalWillPresent: this.onLifecycle, onIonModalWillDismiss: this.onLifecycle, onIonModalDidDismiss: this.onLifecycle, onFocus: this.onModalFocus }), h("ion-backdrop", { key: 'ddd7e4f6eef51ac1f62ac70e0af10fb01e707f07', ref: (el) => (this.backdropEl = el), visible: this.showBackdrop, tappable: this.backdropDismiss, part: "backdrop" }), mode === 'ios' && h("div", { key: '58620980e3e4ec273c6787bde026e1c010b904b7', class: "modal-shadow" }), h("div", Object.assign({ key: '3fb7f6218644ba898fc504467775593eb89426a0',
|
|
2565
2633
|
/*
|
|
2566
2634
|
role and aria-modal must be used on the
|
|
2567
2635
|
same element. They must also be set inside the
|
|
2568
2636
|
shadow DOM otherwise ion-button will not be highlighted
|
|
2569
2637
|
when using VoiceOver: https://bugs.webkit.org/show_bug.cgi?id=247134
|
|
2570
2638
|
*/
|
|
2571
|
-
role: "dialog" }, inheritedAttributes, { "aria-modal": "true", class: "modal-wrapper ion-overlay-wrapper", part: "content", ref: (el) => (this.wrapperEl = el) }), showHandle && (h("button", { key: '
|
|
2639
|
+
role: "dialog" }, inheritedAttributes, { "aria-modal": "true", class: "modal-wrapper ion-overlay-wrapper", part: "content", ref: (el) => (this.wrapperEl = el) }), showHandle && (h("button", { key: '9745cd590fdaa9d023a14b487ec2c87ddbafd7f7', class: "modal-handle",
|
|
2572
2640
|
// Prevents the handle from receiving keyboard focus when it does not cycle
|
|
2573
|
-
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: '
|
|
2641
|
+
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: 'b9a8b5d2d3d3c9b06f99179f496c9f08907d0bad', onSlotchange: this.onSlotChange }))));
|
|
2574
2642
|
}
|
|
2575
2643
|
get el() { return this; }
|
|
2576
2644
|
static get watchers() { return {
|
package/components/popover.js
CHANGED
|
@@ -945,7 +945,7 @@ const mdEnterAnimation = (baseEl, opts) => {
|
|
|
945
945
|
};
|
|
946
946
|
const results = getPopoverPosition(isRTL, contentWidth, contentHeight, 0, 0, reference, side, align, defaultPosition, trigger, ev);
|
|
947
947
|
const padding = size === 'cover' ? 0 : POPOVER_MD_BODY_PADDING;
|
|
948
|
-
const { originX, originY, top, left, bottom, checkSafeAreaTop, checkSafeAreaBottom } = calculateWindowAdjustment(side, results.top, results.left, padding, bodyWidth, bodyHeight, contentWidth, contentHeight, 0, results.originX, results.originY, results.referenceCoordinates);
|
|
948
|
+
const { originX, originY, top, left, bottom, checkSafeAreaTop, checkSafeAreaBottom, checkSafeAreaLeft, checkSafeAreaRight, } = calculateWindowAdjustment(side, results.top, results.left, padding, bodyWidth, bodyHeight, contentWidth, contentHeight, 0, results.originX, results.originY, results.referenceCoordinates);
|
|
949
949
|
/**
|
|
950
950
|
* Safe area CSS variable adjustments.
|
|
951
951
|
* When the popover is positioned near an edge, we add the corresponding
|
|
@@ -954,14 +954,23 @@ const mdEnterAnimation = (baseEl, opts) => {
|
|
|
954
954
|
*/
|
|
955
955
|
const safeAreaTop = ' + var(--ion-safe-area-top, 0)';
|
|
956
956
|
const safeAreaBottom = ' + var(--ion-safe-area-bottom, 0)';
|
|
957
|
+
const safeAreaLeft = ' + var(--ion-safe-area-left, 0)';
|
|
958
|
+
const safeAreaRight = ' - var(--ion-safe-area-right, 0)';
|
|
957
959
|
let topValue = `${top}px`;
|
|
958
960
|
let bottomValue = bottom !== undefined ? `${bottom}px` : undefined;
|
|
961
|
+
let leftValue = `${left}px`;
|
|
959
962
|
if (checkSafeAreaTop) {
|
|
960
963
|
topValue = `${top}px${safeAreaTop}`;
|
|
961
964
|
}
|
|
962
965
|
if (checkSafeAreaBottom && bottomValue !== undefined) {
|
|
963
966
|
bottomValue = `${bottom}px${safeAreaBottom}`;
|
|
964
967
|
}
|
|
968
|
+
if (checkSafeAreaLeft) {
|
|
969
|
+
leftValue = `${left}px${safeAreaLeft}`;
|
|
970
|
+
}
|
|
971
|
+
if (checkSafeAreaRight) {
|
|
972
|
+
leftValue = `${left}px${safeAreaRight}`;
|
|
973
|
+
}
|
|
965
974
|
const baseAnimation = createAnimation();
|
|
966
975
|
const backdropAnimation = createAnimation();
|
|
967
976
|
const wrapperAnimation = createAnimation();
|
|
@@ -979,7 +988,7 @@ const mdEnterAnimation = (baseEl, opts) => {
|
|
|
979
988
|
.addElement(contentEl)
|
|
980
989
|
.beforeStyles({
|
|
981
990
|
top: `calc(${topValue} + var(--offset-y, 0px))`,
|
|
982
|
-
left: `calc(${
|
|
991
|
+
left: `calc(${leftValue} + var(--offset-x, 0px))`,
|
|
983
992
|
'transform-origin': `${originY} ${originX}`,
|
|
984
993
|
})
|
|
985
994
|
.beforeAddWrite(() => {
|
|
@@ -6,16 +6,16 @@
|
|
|
6
6
|
var index = require('./index-D6Wc6v08.js');
|
|
7
7
|
var hardwareBackButton = require('./hardware-back-button-VCK4V3mG.js');
|
|
8
8
|
var ionicGlobal = require('./ionic-global-HMVqOFGO.js');
|
|
9
|
+
var index$1 = require('./index-DkNv4J_i.js');
|
|
9
10
|
var helpers = require('./helpers-DrTqNghc.js');
|
|
10
11
|
var dir = require('./dir-Cn0z1rJH.js');
|
|
11
12
|
var theme = require('./theme-CeDs6Hcv.js');
|
|
12
|
-
var index$
|
|
13
|
+
var index$2 = require('./index-CO6eryBo.js');
|
|
13
14
|
var keyboardController = require('./keyboard-controller-GXBiBRKS.js');
|
|
14
15
|
var cubicBezier = require('./cubic-bezier-DAjy1V-e.js');
|
|
15
16
|
var frameworkDelegate = require('./framework-delegate-DMJRBuDi.js');
|
|
16
17
|
var lockController = require('./lock-controller-aDB9wrEf.js');
|
|
17
|
-
var index$
|
|
18
|
-
require('./index-DkNv4J_i.js');
|
|
18
|
+
var index$3 = require('./index-094mMFB-.js');
|
|
19
19
|
require('./keyboard-UuAS4D_9.js');
|
|
20
20
|
require('./capacitor-DmA66EwP.js');
|
|
21
21
|
|
|
@@ -231,7 +231,11 @@ const Content = class {
|
|
|
231
231
|
this.inheritedAttributes = helpers.inheritAriaAttributes(this.el);
|
|
232
232
|
}
|
|
233
233
|
connectedCallback() {
|
|
234
|
-
|
|
234
|
+
var _a;
|
|
235
|
+
// Content is "main" if not inside menu/popover/modal and not nested in another ion-content
|
|
236
|
+
this.isMainContent =
|
|
237
|
+
this.el.closest('ion-menu, ion-popover, ion-modal') === null &&
|
|
238
|
+
((_a = this.el.parentElement) === null || _a === void 0 ? void 0 : _a.closest('ion-content')) === null;
|
|
235
239
|
// Detect sibling header/footer for safe-area handling
|
|
236
240
|
this.detectSiblingElements();
|
|
237
241
|
/**
|
|
@@ -264,21 +268,55 @@ const Content = class {
|
|
|
264
268
|
* bubbles, we can catch any instances of child tab bars loading by listening
|
|
265
269
|
* on IonTabs.
|
|
266
270
|
*/
|
|
267
|
-
this.tabsLoadCallback = () =>
|
|
271
|
+
this.tabsLoadCallback = () => {
|
|
272
|
+
this.resize();
|
|
273
|
+
// Re-detect footer when tab bar loads (it may not exist during initial detection)
|
|
274
|
+
this.updateSiblingDetection();
|
|
275
|
+
index.forceUpdate(this);
|
|
276
|
+
};
|
|
268
277
|
closestTabs.addEventListener('ionTabBarLoaded', this.tabsLoadCallback);
|
|
269
278
|
}
|
|
270
279
|
}
|
|
271
280
|
}
|
|
272
281
|
/**
|
|
273
|
-
* Detects sibling ion-header and ion-footer elements
|
|
274
|
-
*
|
|
282
|
+
* Detects sibling ion-header and ion-footer elements and sets up
|
|
283
|
+
* a mutation observer to handle dynamic changes (e.g., conditional rendering).
|
|
275
284
|
*/
|
|
276
285
|
detectSiblingElements() {
|
|
277
|
-
|
|
286
|
+
this.updateSiblingDetection();
|
|
287
|
+
// Watch for dynamic header/footer changes (common in React conditional rendering)
|
|
288
|
+
const parent = this.el.parentElement;
|
|
289
|
+
if (parent && !this.parentMutationObserver && index$1.win !== undefined && 'MutationObserver' in index$1.win) {
|
|
290
|
+
this.parentMutationObserver = new MutationObserver(() => {
|
|
291
|
+
const prevHasHeader = this.hasHeader;
|
|
292
|
+
const prevHasFooter = this.hasFooter;
|
|
293
|
+
this.updateSiblingDetection();
|
|
294
|
+
// Only trigger re-render if header/footer detection actually changed
|
|
295
|
+
if (prevHasHeader !== this.hasHeader || prevHasFooter !== this.hasFooter) {
|
|
296
|
+
index.forceUpdate(this);
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
this.parentMutationObserver.observe(parent, { childList: true });
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* Updates hasHeader/hasFooter based on current DOM state.
|
|
304
|
+
* Checks both direct siblings and elements wrapped in custom components
|
|
305
|
+
* (e.g., <my-header><ion-header>...</ion-header></my-header>).
|
|
306
|
+
*/
|
|
307
|
+
updateSiblingDetection() {
|
|
278
308
|
const parent = this.el.parentElement;
|
|
279
309
|
if (parent) {
|
|
310
|
+
// First check for direct ion-header/ion-footer siblings
|
|
280
311
|
this.hasHeader = parent.querySelector(':scope > ion-header') !== null;
|
|
281
312
|
this.hasFooter = parent.querySelector(':scope > ion-footer') !== null;
|
|
313
|
+
// If not found, check if any sibling contains them (wrapped components)
|
|
314
|
+
if (!this.hasHeader) {
|
|
315
|
+
this.hasHeader = this.siblingContainsElement(parent, 'ion-header');
|
|
316
|
+
}
|
|
317
|
+
if (!this.hasFooter) {
|
|
318
|
+
this.hasFooter = this.siblingContainsElement(parent, 'ion-footer');
|
|
319
|
+
}
|
|
282
320
|
}
|
|
283
321
|
// If no footer found, check if we're inside ion-tabs which has ion-tab-bar
|
|
284
322
|
if (!this.hasFooter) {
|
|
@@ -288,8 +326,28 @@ const Content = class {
|
|
|
288
326
|
}
|
|
289
327
|
}
|
|
290
328
|
}
|
|
329
|
+
/**
|
|
330
|
+
* Checks if any sibling element of ion-content contains the specified element.
|
|
331
|
+
* Only searches one level deep to avoid finding elements in nested pages.
|
|
332
|
+
*/
|
|
333
|
+
siblingContainsElement(parent, tagName) {
|
|
334
|
+
for (const sibling of parent.children) {
|
|
335
|
+
// Skip ion-content itself
|
|
336
|
+
if (sibling === this.el)
|
|
337
|
+
continue;
|
|
338
|
+
// Check if this sibling contains the target element as an immediate child
|
|
339
|
+
if (sibling.querySelector(`:scope > ${tagName}`) !== null) {
|
|
340
|
+
return true;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
return false;
|
|
344
|
+
}
|
|
291
345
|
disconnectedCallback() {
|
|
346
|
+
var _a;
|
|
292
347
|
this.onScrollEnd();
|
|
348
|
+
// Clean up mutation observer to prevent memory leaks
|
|
349
|
+
(_a = this.parentMutationObserver) === null || _a === void 0 ? void 0 : _a.disconnect();
|
|
350
|
+
this.parentMutationObserver = undefined;
|
|
293
351
|
if (helpers.hasLazyBuild(this.el)) {
|
|
294
352
|
/**
|
|
295
353
|
* The event listener and tabs caches need to
|
|
@@ -542,7 +600,7 @@ const Content = class {
|
|
|
542
600
|
const forceOverscroll = this.shouldForceOverscroll();
|
|
543
601
|
const transitionShadow = mode === 'ios';
|
|
544
602
|
this.resize();
|
|
545
|
-
return (index.h(index.Host, Object.assign({ key: '
|
|
603
|
+
return (index.h(index.Host, Object.assign({ key: 'f7218f733e4022a30875441bd949747537d28aa1', role: isMainContent ? 'main' : undefined, class: theme.createColorClasses(this.color, {
|
|
546
604
|
[mode]: true,
|
|
547
605
|
'content-sizing': theme.hostContext('ion-popover', this.el),
|
|
548
606
|
overscroll: forceOverscroll,
|
|
@@ -552,12 +610,12 @@ const Content = class {
|
|
|
552
610
|
}), style: {
|
|
553
611
|
'--offset-top': `${this.cTop}px`,
|
|
554
612
|
'--offset-bottom': `${this.cBottom}px`,
|
|
555
|
-
} }, inheritedAttributes), index.h("div", { key: '
|
|
613
|
+
} }, inheritedAttributes), index.h("div", { key: 'b735ec68c18c0b99c3595bb194029830e6542cde', ref: (el) => (this.backgroundContentEl = el), id: "background-content", part: "background" }), fixedSlotPlacement === 'before' ? index.h("slot", { name: "fixed" }) : null, index.h("div", { key: 'e76c00d030342d44ade6648c3f9e32ca990787ba', class: {
|
|
556
614
|
'inner-scroll': true,
|
|
557
615
|
'scroll-x': scrollX,
|
|
558
616
|
'scroll-y': scrollY,
|
|
559
617
|
overscroll: (scrollX || scrollY) && forceOverscroll,
|
|
560
|
-
}, ref: (scrollEl) => (this.scrollEl = scrollEl), onScroll: this.scrollEvents ? (ev) => this.onScroll(ev) : undefined, part: "scroll" }, index.h("slot", { key: '
|
|
618
|
+
}, ref: (scrollEl) => (this.scrollEl = scrollEl), onScroll: this.scrollEvents ? (ev) => this.onScroll(ev) : undefined, part: "scroll" }, index.h("slot", { key: '9049be4cea9b5da5ec1e1012248b05286fddeb7a' })), transitionShadow ? (index.h("div", { class: "transition-effect" }, index.h("div", { class: "transition-cover" }), index.h("div", { class: "transition-shadow" }))) : null, fixedSlotPlacement === 'after' ? index.h("slot", { name: "fixed" }) : null));
|
|
561
619
|
}
|
|
562
620
|
get el() { return index.getElement(this); }
|
|
563
621
|
};
|
|
@@ -675,16 +733,16 @@ const Footer = class {
|
|
|
675
733
|
this.destroyCollapsibleFooter();
|
|
676
734
|
if (hasFade) {
|
|
677
735
|
const pageEl = this.el.closest('ion-app,ion-page,.ion-page,page-inner');
|
|
678
|
-
const contentEl = pageEl ? index$
|
|
736
|
+
const contentEl = pageEl ? index$2.findIonContent(pageEl) : null;
|
|
679
737
|
if (!contentEl) {
|
|
680
|
-
index$
|
|
738
|
+
index$2.printIonContentErrorMsg(this.el);
|
|
681
739
|
return;
|
|
682
740
|
}
|
|
683
741
|
this.setupFadeFooter(contentEl);
|
|
684
742
|
}
|
|
685
743
|
};
|
|
686
744
|
this.setupFadeFooter = async (contentEl) => {
|
|
687
|
-
const scrollEl = (this.scrollEl = await index$
|
|
745
|
+
const scrollEl = (this.scrollEl = await index$2.getScrollElement(contentEl));
|
|
688
746
|
/**
|
|
689
747
|
* Handle fading of toolbars on scroll
|
|
690
748
|
*/
|
|
@@ -997,7 +1055,7 @@ const Header = class {
|
|
|
997
1055
|
*/
|
|
998
1056
|
this.translucent = false;
|
|
999
1057
|
this.setupFadeHeader = async (contentEl, condenseHeader) => {
|
|
1000
|
-
const scrollEl = (this.scrollEl = await index$
|
|
1058
|
+
const scrollEl = (this.scrollEl = await index$2.getScrollElement(contentEl));
|
|
1001
1059
|
/**
|
|
1002
1060
|
* Handle fading of toolbars on scroll
|
|
1003
1061
|
*/
|
|
@@ -1031,7 +1089,7 @@ const Header = class {
|
|
|
1031
1089
|
this.destroyCollapsibleHeader();
|
|
1032
1090
|
if (hasCondense) {
|
|
1033
1091
|
const pageEl = this.el.closest('ion-app,ion-page,.ion-page,page-inner');
|
|
1034
|
-
const contentEl = pageEl ? index$
|
|
1092
|
+
const contentEl = pageEl ? index$2.findIonContent(pageEl) : null;
|
|
1035
1093
|
// Cloned elements are always needed in iOS transition
|
|
1036
1094
|
index.writeTask(() => {
|
|
1037
1095
|
const title = cloneElement('ion-title');
|
|
@@ -1042,9 +1100,9 @@ const Header = class {
|
|
|
1042
1100
|
}
|
|
1043
1101
|
else if (hasFade) {
|
|
1044
1102
|
const pageEl = this.el.closest('ion-app,ion-page,.ion-page,page-inner');
|
|
1045
|
-
const contentEl = pageEl ? index$
|
|
1103
|
+
const contentEl = pageEl ? index$2.findIonContent(pageEl) : null;
|
|
1046
1104
|
if (!contentEl) {
|
|
1047
|
-
index$
|
|
1105
|
+
index$2.printIonContentErrorMsg(this.el);
|
|
1048
1106
|
return;
|
|
1049
1107
|
}
|
|
1050
1108
|
const condenseHeader = contentEl.querySelector('ion-header[collapse="condense"]');
|
|
@@ -1067,13 +1125,13 @@ const Header = class {
|
|
|
1067
1125
|
}
|
|
1068
1126
|
async setupCondenseHeader(contentEl, pageEl) {
|
|
1069
1127
|
if (!contentEl || !pageEl) {
|
|
1070
|
-
index$
|
|
1128
|
+
index$2.printIonContentErrorMsg(this.el);
|
|
1071
1129
|
return;
|
|
1072
1130
|
}
|
|
1073
1131
|
if (typeof IntersectionObserver === 'undefined') {
|
|
1074
1132
|
return;
|
|
1075
1133
|
}
|
|
1076
|
-
this.scrollEl = await index$
|
|
1134
|
+
this.scrollEl = await index$2.getScrollElement(contentEl);
|
|
1077
1135
|
const headers = pageEl.querySelectorAll('ion-header');
|
|
1078
1136
|
this.collapsibleMainHeader = Array.from(headers).find((header) => header.collapse !== 'condense');
|
|
1079
1137
|
if (!this.collapsibleMainHeader) {
|
|
@@ -1271,7 +1329,7 @@ const RouterOutlet = class {
|
|
|
1271
1329
|
const { el, mode } = this;
|
|
1272
1330
|
const animated = this.animated && index.config.getBoolean('animated', true);
|
|
1273
1331
|
const animationBuilder = opts.animationBuilder || this.animation || index.config.get('navAnimation');
|
|
1274
|
-
await index$
|
|
1332
|
+
await index$3.transition(Object.assign(Object.assign({ mode,
|
|
1275
1333
|
animated,
|
|
1276
1334
|
enteringEl,
|
|
1277
1335
|
leavingEl, baseEl: el,
|