@ionic/core 8.7.17-dev.11767796972.148e4bc4 → 8.7.17-dev.11767895575.16ea7cef
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 +6 -79
- package/components/ion-tab-bar.js +23 -3
- package/components/modal.js +12 -200
- package/components/popover.js +10 -73
- package/dist/cjs/ion-app_8.cjs.entry.js +5 -78
- package/dist/cjs/ion-modal.cjs.entry.js +12 -200
- package/dist/cjs/ion-popover.cjs.entry.js +10 -73
- package/dist/cjs/ion-tab-bar_2.cjs.entry.js +23 -3
- package/dist/collection/components/content/content.css +0 -10
- package/dist/collection/components/content/content.js +4 -77
- package/dist/collection/components/modal/gestures/sheet.js +1 -3
- package/dist/collection/components/modal/gestures/swipe-to-close.js +1 -3
- package/dist/collection/components/modal/modal.ios.css +4 -0
- package/dist/collection/components/modal/modal.js +8 -192
- package/dist/collection/components/modal/modal.md.css +4 -0
- package/dist/collection/components/popover/animations/ios.enter.js +5 -21
- package/dist/collection/components/popover/animations/md.enter.js +4 -20
- package/dist/collection/components/popover/utils.js +1 -32
- package/dist/collection/components/tab-bar/tab-bar.js +23 -3
- package/dist/docs.json +1 -1
- package/dist/esm/ion-app_8.entry.js +6 -79
- package/dist/esm/ion-modal.entry.js +12 -200
- package/dist/esm/ion-popover.entry.js +10 -73
- package/dist/esm/ion-tab-bar_2.entry.js +23 -3
- package/dist/ionic/ionic.esm.js +1 -1
- package/dist/ionic/p-172a579f.entry.js +4 -0
- package/dist/ionic/p-732b2fd6.entry.js +4 -0
- package/dist/ionic/p-91840a80.entry.js +4 -0
- package/dist/ionic/p-f9061316.entry.js +4 -0
- package/dist/types/components/content/content.d.ts +0 -24
- package/dist/types/components/modal/gestures/sheet.d.ts +1 -1
- package/dist/types/components/modal/gestures/swipe-to-close.d.ts +1 -1
- package/dist/types/components/modal/modal.d.ts +0 -39
- package/dist/types/components/popover/utils.d.ts +0 -2
- package/dist/types/components/tab-bar/tab-bar.d.ts +1 -0
- package/hydrate/index.js +50 -353
- package/hydrate/index.mjs +50 -353
- package/package.json +1 -1
- package/dist/ionic/p-3830217a.entry.js +0 -4
- package/dist/ionic/p-7268efa5.entry.js +0 -4
- package/dist/ionic/p-a4827773.entry.js +0 -4
- package/dist/ionic/p-cecd141b.entry.js +0 -4
|
@@ -42,10 +42,6 @@ export class Modal {
|
|
|
42
42
|
this.inline = false;
|
|
43
43
|
// Whether or not modal is being dismissed via gesture
|
|
44
44
|
this.gestureAnimationDismissing = false;
|
|
45
|
-
// Whether to skip coordinate-based safe-area detection (for fullscreen phone modals)
|
|
46
|
-
this.skipSafeAreaCoordinateDetection = false;
|
|
47
|
-
// Track previous safe-area state to avoid redundant DOM writes
|
|
48
|
-
this.prevSafeAreaState = { top: false, bottom: false, left: false, right: false };
|
|
49
45
|
this.presented = false;
|
|
50
46
|
/** @internal */
|
|
51
47
|
this.hasController = false;
|
|
@@ -236,10 +232,7 @@ export class Modal {
|
|
|
236
232
|
}
|
|
237
233
|
}
|
|
238
234
|
onWindowResize() {
|
|
239
|
-
//
|
|
240
|
-
this.cachedSafeAreas = undefined;
|
|
241
|
-
this.updateSafeAreaOverrides();
|
|
242
|
-
// Only handle view transition for iOS card modals when no custom animations are provided
|
|
235
|
+
// Only handle resize for iOS card modals when no custom animations are provided
|
|
243
236
|
if (getIonMode(this) !== 'ios' || !this.presentingElement || this.enterAnimation || this.leaveAnimation) {
|
|
244
237
|
return;
|
|
245
238
|
}
|
|
@@ -421,8 +414,6 @@ export class Modal {
|
|
|
421
414
|
else if (!this.keepContentsMounted) {
|
|
422
415
|
await waitForMount();
|
|
423
416
|
}
|
|
424
|
-
// Predict safe-area needs based on modal configuration to avoid visual snap
|
|
425
|
-
this.setInitialSafeAreaOverrides(presentingElement);
|
|
426
417
|
writeTask(() => this.el.classList.add('show-modal'));
|
|
427
418
|
const hasCardModal = presentingElement !== undefined;
|
|
428
419
|
/**
|
|
@@ -484,8 +475,6 @@ export class Modal {
|
|
|
484
475
|
else if (hasCardModal) {
|
|
485
476
|
this.initSwipeToClose();
|
|
486
477
|
}
|
|
487
|
-
// Now that animation is complete, update safe-area based on actual position
|
|
488
|
-
this.updateSafeAreaOverrides();
|
|
489
478
|
// Initialize view transition listener for iOS card modals
|
|
490
479
|
this.initViewTransitionListener();
|
|
491
480
|
// Initialize parent removal observer
|
|
@@ -537,7 +526,7 @@ export class Modal {
|
|
|
537
526
|
await this.dismiss(undefined, GESTURE);
|
|
538
527
|
this.gestureAnimationDismissing = false;
|
|
539
528
|
});
|
|
540
|
-
}
|
|
529
|
+
});
|
|
541
530
|
this.gesture.enable(true);
|
|
542
531
|
}
|
|
543
532
|
initSheetGesture() {
|
|
@@ -558,8 +547,7 @@ export class Modal {
|
|
|
558
547
|
this.currentBreakpoint = breakpoint;
|
|
559
548
|
this.ionBreakpointDidChange.emit({ breakpoint });
|
|
560
549
|
}
|
|
561
|
-
|
|
562
|
-
}, () => this.updateSafeAreaOverrides());
|
|
550
|
+
});
|
|
563
551
|
this.gesture = gesture;
|
|
564
552
|
this.moveSheetToBreakpoint = moveSheetToBreakpoint;
|
|
565
553
|
this.gesture.enable(true);
|
|
@@ -637,161 +625,6 @@ export class Modal {
|
|
|
637
625
|
// Clear the cached reference
|
|
638
626
|
this.cachedPageParent = undefined;
|
|
639
627
|
}
|
|
640
|
-
/**
|
|
641
|
-
* Sets initial safe-area overrides based on modal configuration before
|
|
642
|
-
* the modal becomes visible. This predicts whether the modal will touch
|
|
643
|
-
* screen edges to avoid a visual snap after animation completes.
|
|
644
|
-
*/
|
|
645
|
-
setInitialSafeAreaOverrides(presentingElement) {
|
|
646
|
-
const style = this.el.style;
|
|
647
|
-
const mode = getIonMode(this);
|
|
648
|
-
const isSheetModal = this.breakpoints !== undefined && this.initialBreakpoint !== undefined;
|
|
649
|
-
// Card modals only exist in iOS mode - in MD mode, presentingElement is ignored
|
|
650
|
-
const isCardModal = presentingElement !== undefined && mode === 'ios';
|
|
651
|
-
const isTablet = window.innerWidth >= 768;
|
|
652
|
-
// Sheet modals always touch bottom edge, never top/left/right
|
|
653
|
-
if (isSheetModal) {
|
|
654
|
-
style.setProperty('--ion-safe-area-top', '0px');
|
|
655
|
-
style.setProperty('--ion-safe-area-left', '0px');
|
|
656
|
-
style.setProperty('--ion-safe-area-right', '0px');
|
|
657
|
-
return;
|
|
658
|
-
}
|
|
659
|
-
// Card modals have rounded top corners
|
|
660
|
-
if (isCardModal) {
|
|
661
|
-
style.setProperty('--ion-safe-area-top', '0px');
|
|
662
|
-
if (isTablet) {
|
|
663
|
-
// On tablets, card modals are inset from all edges
|
|
664
|
-
this.zeroAllSafeAreas();
|
|
665
|
-
}
|
|
666
|
-
else {
|
|
667
|
-
// On phones, card modals still extend to the bottom edge
|
|
668
|
-
style.setProperty('--ion-safe-area-left', '0px');
|
|
669
|
-
style.setProperty('--ion-safe-area-right', '0px');
|
|
670
|
-
this.applyFullscreenSafeArea();
|
|
671
|
-
}
|
|
672
|
-
return;
|
|
673
|
-
}
|
|
674
|
-
// Phone-sized fullscreen modals inherit safe areas and use wrapper padding
|
|
675
|
-
if (!isTablet) {
|
|
676
|
-
this.applyFullscreenSafeArea();
|
|
677
|
-
return;
|
|
678
|
-
}
|
|
679
|
-
// Check if tablet modal is fullscreen via CSS custom properties
|
|
680
|
-
const computedStyle = getComputedStyle(this.el);
|
|
681
|
-
const width = computedStyle.getPropertyValue('--width').trim();
|
|
682
|
-
const height = computedStyle.getPropertyValue('--height').trim();
|
|
683
|
-
const isFullscreen = width === '100%' && height === '100%';
|
|
684
|
-
if (isFullscreen) {
|
|
685
|
-
this.applyFullscreenSafeArea();
|
|
686
|
-
}
|
|
687
|
-
else {
|
|
688
|
-
// Centered dialog doesn't touch edges
|
|
689
|
-
this.zeroAllSafeAreas();
|
|
690
|
-
}
|
|
691
|
-
}
|
|
692
|
-
/**
|
|
693
|
-
* Applies safe-area handling for fullscreen modals.
|
|
694
|
-
* Adds wrapper padding when no footer is present to prevent
|
|
695
|
-
* content from overlapping system navigation areas.
|
|
696
|
-
*/
|
|
697
|
-
applyFullscreenSafeArea() {
|
|
698
|
-
this.skipSafeAreaCoordinateDetection = true;
|
|
699
|
-
this.updateFooterPadding();
|
|
700
|
-
// Watch for dynamic footer additions/removals (e.g., async data loading)
|
|
701
|
-
if (!this.footerObserver) {
|
|
702
|
-
this.footerObserver = new MutationObserver(() => this.updateFooterPadding());
|
|
703
|
-
this.footerObserver.observe(this.el, { childList: true, subtree: true });
|
|
704
|
-
}
|
|
705
|
-
}
|
|
706
|
-
/**
|
|
707
|
-
* Updates wrapper padding based on footer presence.
|
|
708
|
-
* Called initially and when footer is dynamically added/removed.
|
|
709
|
-
*/
|
|
710
|
-
updateFooterPadding() {
|
|
711
|
-
if (!this.wrapperEl)
|
|
712
|
-
return;
|
|
713
|
-
const hasFooter = this.el.querySelector('ion-footer') !== null;
|
|
714
|
-
if (hasFooter) {
|
|
715
|
-
this.wrapperEl.style.removeProperty('padding-bottom');
|
|
716
|
-
this.wrapperEl.style.removeProperty('box-sizing');
|
|
717
|
-
}
|
|
718
|
-
else {
|
|
719
|
-
this.wrapperEl.style.setProperty('padding-bottom', 'var(--ion-safe-area-bottom, 0px)');
|
|
720
|
-
this.wrapperEl.style.setProperty('box-sizing', 'border-box');
|
|
721
|
-
}
|
|
722
|
-
}
|
|
723
|
-
/**
|
|
724
|
-
* Sets all safe-area CSS variables to 0px for modals that
|
|
725
|
-
* don't touch screen edges.
|
|
726
|
-
*/
|
|
727
|
-
zeroAllSafeAreas() {
|
|
728
|
-
const style = this.el.style;
|
|
729
|
-
style.setProperty('--ion-safe-area-top', '0px');
|
|
730
|
-
style.setProperty('--ion-safe-area-bottom', '0px');
|
|
731
|
-
style.setProperty('--ion-safe-area-left', '0px');
|
|
732
|
-
style.setProperty('--ion-safe-area-right', '0px');
|
|
733
|
-
}
|
|
734
|
-
/**
|
|
735
|
-
* Gets the root safe-area values from the document element.
|
|
736
|
-
* Uses cached values during gestures to avoid getComputedStyle calls.
|
|
737
|
-
*/
|
|
738
|
-
getSafeAreaValues() {
|
|
739
|
-
if (!this.cachedSafeAreas) {
|
|
740
|
-
const rootStyle = getComputedStyle(document.documentElement);
|
|
741
|
-
this.cachedSafeAreas = {
|
|
742
|
-
top: parseFloat(rootStyle.getPropertyValue('--ion-safe-area-top')) || 0,
|
|
743
|
-
bottom: parseFloat(rootStyle.getPropertyValue('--ion-safe-area-bottom')) || 0,
|
|
744
|
-
left: parseFloat(rootStyle.getPropertyValue('--ion-safe-area-left')) || 0,
|
|
745
|
-
right: parseFloat(rootStyle.getPropertyValue('--ion-safe-area-right')) || 0,
|
|
746
|
-
};
|
|
747
|
-
}
|
|
748
|
-
return this.cachedSafeAreas;
|
|
749
|
-
}
|
|
750
|
-
/**
|
|
751
|
-
* Updates safe-area CSS variable overrides based on whether the modal
|
|
752
|
-
* extends into each safe-area region. Called after animation
|
|
753
|
-
* and during gestures to handle dynamic position changes.
|
|
754
|
-
*
|
|
755
|
-
* Optimized to avoid redundant DOM writes by tracking previous state.
|
|
756
|
-
*/
|
|
757
|
-
updateSafeAreaOverrides() {
|
|
758
|
-
if (this.skipSafeAreaCoordinateDetection) {
|
|
759
|
-
return;
|
|
760
|
-
}
|
|
761
|
-
const wrapper = this.wrapperEl;
|
|
762
|
-
if (!wrapper) {
|
|
763
|
-
return;
|
|
764
|
-
}
|
|
765
|
-
const rect = wrapper.getBoundingClientRect();
|
|
766
|
-
const safeAreas = this.getSafeAreaValues();
|
|
767
|
-
const extendsIntoTop = rect.top < safeAreas.top;
|
|
768
|
-
const extendsIntoBottom = rect.bottom > window.innerHeight - safeAreas.bottom;
|
|
769
|
-
const extendsIntoLeft = rect.left < safeAreas.left;
|
|
770
|
-
const extendsIntoRight = rect.right > window.innerWidth - safeAreas.right;
|
|
771
|
-
// Only update DOM when state actually changes
|
|
772
|
-
const prev = this.prevSafeAreaState;
|
|
773
|
-
const style = this.el.style;
|
|
774
|
-
if (extendsIntoTop !== prev.top) {
|
|
775
|
-
extendsIntoTop ? style.removeProperty('--ion-safe-area-top') : style.setProperty('--ion-safe-area-top', '0px');
|
|
776
|
-
prev.top = extendsIntoTop;
|
|
777
|
-
}
|
|
778
|
-
if (extendsIntoBottom !== prev.bottom) {
|
|
779
|
-
extendsIntoBottom
|
|
780
|
-
? style.removeProperty('--ion-safe-area-bottom')
|
|
781
|
-
: style.setProperty('--ion-safe-area-bottom', '0px');
|
|
782
|
-
prev.bottom = extendsIntoBottom;
|
|
783
|
-
}
|
|
784
|
-
if (extendsIntoLeft !== prev.left) {
|
|
785
|
-
extendsIntoLeft ? style.removeProperty('--ion-safe-area-left') : style.setProperty('--ion-safe-area-left', '0px');
|
|
786
|
-
prev.left = extendsIntoLeft;
|
|
787
|
-
}
|
|
788
|
-
if (extendsIntoRight !== prev.right) {
|
|
789
|
-
extendsIntoRight
|
|
790
|
-
? style.removeProperty('--ion-safe-area-right')
|
|
791
|
-
: style.setProperty('--ion-safe-area-right', '0px');
|
|
792
|
-
prev.right = extendsIntoRight;
|
|
793
|
-
}
|
|
794
|
-
}
|
|
795
628
|
sheetOnDismiss() {
|
|
796
629
|
/**
|
|
797
630
|
* While the gesture animation is finishing
|
|
@@ -822,7 +655,7 @@ export class Modal {
|
|
|
822
655
|
* For example, `cancel` or `backdrop`.
|
|
823
656
|
*/
|
|
824
657
|
async dismiss(data, role) {
|
|
825
|
-
var _a
|
|
658
|
+
var _a;
|
|
826
659
|
if (this.gestureAnimationDismissing && role !== GESTURE) {
|
|
827
660
|
return false;
|
|
828
661
|
}
|
|
@@ -884,23 +717,6 @@ export class Modal {
|
|
|
884
717
|
}
|
|
885
718
|
this.currentBreakpoint = undefined;
|
|
886
719
|
this.animation = undefined;
|
|
887
|
-
// Reset safe-area state for potential re-presentation
|
|
888
|
-
this.skipSafeAreaCoordinateDetection = false;
|
|
889
|
-
this.cachedSafeAreas = undefined;
|
|
890
|
-
this.prevSafeAreaState = { top: false, bottom: false, left: false, right: false };
|
|
891
|
-
(_b = this.footerObserver) === null || _b === void 0 ? void 0 : _b.disconnect();
|
|
892
|
-
this.footerObserver = undefined;
|
|
893
|
-
// Clear styles that may have been set for safe-area handling
|
|
894
|
-
if (this.wrapperEl) {
|
|
895
|
-
this.wrapperEl.style.removeProperty('padding-bottom');
|
|
896
|
-
this.wrapperEl.style.removeProperty('box-sizing');
|
|
897
|
-
}
|
|
898
|
-
// Clear safe-area CSS variable overrides
|
|
899
|
-
const style = this.el.style;
|
|
900
|
-
style.removeProperty('--ion-safe-area-top');
|
|
901
|
-
style.removeProperty('--ion-safe-area-bottom');
|
|
902
|
-
style.removeProperty('--ion-safe-area-left');
|
|
903
|
-
style.removeProperty('--ion-safe-area-right');
|
|
904
720
|
unlock();
|
|
905
721
|
return dismissed;
|
|
906
722
|
}
|
|
@@ -1158,20 +974,20 @@ export class Modal {
|
|
|
1158
974
|
const isCardModal = presentingElement !== undefined && mode === 'ios';
|
|
1159
975
|
const isHandleCycle = handleBehavior === 'cycle';
|
|
1160
976
|
const isSheetModalWithHandle = isSheetModal && showHandle;
|
|
1161
|
-
return (h(Host, Object.assign({ key: '
|
|
977
|
+
return (h(Host, Object.assign({ key: '87328006ea6c75ebc518ace300438492a567223e', "no-router": true,
|
|
1162
978
|
// Allow the modal to be navigable when the handle is focusable
|
|
1163
979
|
tabIndex: isHandleCycle && isSheetModalWithHandle ? 0 : -1 }, htmlAttributes, { style: {
|
|
1164
980
|
zIndex: `${20000 + this.overlayIndex}`,
|
|
1165
|
-
}, 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: '
|
|
981
|
+
}, 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: 'ee94ff8e09b691dd4ad4e4db1720f06bc3c5a469', ref: (el) => (this.backdropEl = el), visible: this.showBackdrop, tappable: this.backdropDismiss, part: "backdrop" }), mode === 'ios' && h("div", { key: 'bffd69b4635c22d9f249725bd952c1e93d5615c7', class: "modal-shadow" }), h("div", Object.assign({ key: '1d394d3c68916e464ff1fbf5242419f4a3d3cca1',
|
|
1166
982
|
/*
|
|
1167
983
|
role and aria-modal must be used on the
|
|
1168
984
|
same element. They must also be set inside the
|
|
1169
985
|
shadow DOM otherwise ion-button will not be highlighted
|
|
1170
986
|
when using VoiceOver: https://bugs.webkit.org/show_bug.cgi?id=247134
|
|
1171
987
|
*/
|
|
1172
|
-
role: "dialog" }, inheritedAttributes, { "aria-modal": "true", class: "modal-wrapper ion-overlay-wrapper", part: "content", ref: (el) => (this.wrapperEl = el) }), showHandle && (h("button", { key: '
|
|
988
|
+
role: "dialog" }, inheritedAttributes, { "aria-modal": "true", class: "modal-wrapper ion-overlay-wrapper", part: "content", ref: (el) => (this.wrapperEl = el) }), showHandle && (h("button", { key: '2dcf58792018e557e0c323baad2d672bc99c0bb1', class: "modal-handle",
|
|
1173
989
|
// Prevents the handle from receiving keyboard focus when it does not cycle
|
|
1174
|
-
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: '
|
|
990
|
+
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: '44164b1e8710c3895400ad9f44ecd99873874ad5', onSlotchange: this.onSlotChange }))));
|
|
1175
991
|
}
|
|
1176
992
|
static get is() { return "ion-modal"; }
|
|
1177
993
|
static get encapsulation() { return "shadow"; }
|
|
@@ -135,6 +135,10 @@ ion-backdrop {
|
|
|
135
135
|
:host {
|
|
136
136
|
--width: 600px;
|
|
137
137
|
--height: 500px;
|
|
138
|
+
--ion-safe-area-top: 0px;
|
|
139
|
+
--ion-safe-area-bottom: 0px;
|
|
140
|
+
--ion-safe-area-right: 0px;
|
|
141
|
+
--ion-safe-area-left: 0px;
|
|
138
142
|
}
|
|
139
143
|
}
|
|
140
144
|
@media only screen and (min-width: 768px) and (min-height: 768px) {
|
|
@@ -31,7 +31,7 @@ export const iosEnterAnimation = (baseEl, opts) => {
|
|
|
31
31
|
const results = getPopoverPosition(isRTL, contentWidth, contentHeight, arrowWidth, arrowHeight, reference, side, align, defaultPosition, trigger, ev);
|
|
32
32
|
const padding = size === 'cover' ? 0 : POPOVER_IOS_BODY_PADDING;
|
|
33
33
|
const margin = size === 'cover' ? 0 : 25;
|
|
34
|
-
const { originX, originY, top, left, bottom,
|
|
34
|
+
const { originX, originY, top, left, bottom, checkSafeAreaLeft, checkSafeAreaRight, arrowTop, arrowLeft, addPopoverBottomClass, } = calculateWindowAdjustment(side, results.top, results.left, padding, bodyWidth, bodyHeight, contentWidth, contentHeight, margin, results.originX, results.originY, results.referenceCoordinates, results.arrowTop, results.arrowLeft, arrowHeight);
|
|
35
35
|
const baseAnimation = createAnimation();
|
|
36
36
|
const backdropAnimation = createAnimation();
|
|
37
37
|
const contentAnimation = createAnimation();
|
|
@@ -61,35 +61,19 @@ export const iosEnterAnimation = (baseEl, opts) => {
|
|
|
61
61
|
if (addPopoverBottomClass) {
|
|
62
62
|
baseEl.classList.add('popover-bottom');
|
|
63
63
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
* safe-area inset to ensure the popover doesn't overlap with system UI
|
|
68
|
-
* (status bars, home indicators, navigation bars on Android API 36+, etc.)
|
|
69
|
-
*/
|
|
70
|
-
const safeAreaTop = ' + var(--ion-safe-area-top, 0)';
|
|
71
|
-
const safeAreaBottom = ' + var(--ion-safe-area-bottom, 0)';
|
|
64
|
+
if (bottom !== undefined) {
|
|
65
|
+
contentEl.style.setProperty('bottom', `${bottom}px`);
|
|
66
|
+
}
|
|
72
67
|
const safeAreaLeft = ' + var(--ion-safe-area-left, 0)';
|
|
73
68
|
const safeAreaRight = ' - var(--ion-safe-area-right, 0)';
|
|
74
|
-
let topValue = `${top}px`;
|
|
75
|
-
let bottomValue = bottom !== undefined ? `${bottom}px` : undefined;
|
|
76
69
|
let leftValue = `${left}px`;
|
|
77
|
-
if (checkSafeAreaTop) {
|
|
78
|
-
topValue = `${top}px${safeAreaTop}`;
|
|
79
|
-
}
|
|
80
|
-
if (checkSafeAreaBottom && bottomValue !== undefined) {
|
|
81
|
-
bottomValue = `${bottom}px${safeAreaBottom}`;
|
|
82
|
-
}
|
|
83
70
|
if (checkSafeAreaLeft) {
|
|
84
71
|
leftValue = `${left}px${safeAreaLeft}`;
|
|
85
72
|
}
|
|
86
73
|
if (checkSafeAreaRight) {
|
|
87
74
|
leftValue = `${left}px${safeAreaRight}`;
|
|
88
75
|
}
|
|
89
|
-
|
|
90
|
-
contentEl.style.setProperty('bottom', `calc(${bottomValue})`);
|
|
91
|
-
}
|
|
92
|
-
contentEl.style.setProperty('top', `calc(${topValue} + var(--offset-y, 0))`);
|
|
76
|
+
contentEl.style.setProperty('top', `calc(${top}px + var(--offset-y, 0))`);
|
|
93
77
|
contentEl.style.setProperty('left', `calc(${leftValue} + var(--offset-x, 0))`);
|
|
94
78
|
contentEl.style.setProperty('transform-origin', `${originY} ${originX}`);
|
|
95
79
|
if (arrowEl !== null) {
|
|
@@ -28,23 +28,7 @@ export const mdEnterAnimation = (baseEl, opts) => {
|
|
|
28
28
|
};
|
|
29
29
|
const results = getPopoverPosition(isRTL, contentWidth, contentHeight, 0, 0, reference, side, align, defaultPosition, trigger, ev);
|
|
30
30
|
const padding = size === 'cover' ? 0 : POPOVER_MD_BODY_PADDING;
|
|
31
|
-
const { originX, originY, top, left, bottom
|
|
32
|
-
/**
|
|
33
|
-
* Safe area CSS variable adjustments.
|
|
34
|
-
* When the popover is positioned near an edge, we add the corresponding
|
|
35
|
-
* safe-area inset to ensure the popover doesn't overlap with system UI
|
|
36
|
-
* (status bars, home indicators, navigation bars on Android API 36+, etc.)
|
|
37
|
-
*/
|
|
38
|
-
const safeAreaTop = ' + var(--ion-safe-area-top, 0)';
|
|
39
|
-
const safeAreaBottom = ' + var(--ion-safe-area-bottom, 0)';
|
|
40
|
-
let topValue = `${top}px`;
|
|
41
|
-
let bottomValue = bottom !== undefined ? `${bottom}px` : undefined;
|
|
42
|
-
if (checkSafeAreaTop) {
|
|
43
|
-
topValue = `${top}px${safeAreaTop}`;
|
|
44
|
-
}
|
|
45
|
-
if (checkSafeAreaBottom && bottomValue !== undefined) {
|
|
46
|
-
bottomValue = `${bottom}px${safeAreaBottom}`;
|
|
47
|
-
}
|
|
31
|
+
const { originX, originY, top, left, bottom } = calculateWindowAdjustment(side, results.top, results.left, padding, bodyWidth, bodyHeight, contentWidth, contentHeight, 0, results.originX, results.originY, results.referenceCoordinates);
|
|
48
32
|
const baseAnimation = createAnimation();
|
|
49
33
|
const backdropAnimation = createAnimation();
|
|
50
34
|
const wrapperAnimation = createAnimation();
|
|
@@ -61,13 +45,13 @@ export const mdEnterAnimation = (baseEl, opts) => {
|
|
|
61
45
|
contentAnimation
|
|
62
46
|
.addElement(contentEl)
|
|
63
47
|
.beforeStyles({
|
|
64
|
-
top: `calc(${
|
|
48
|
+
top: `calc(${top}px + var(--offset-y, 0px))`,
|
|
65
49
|
left: `calc(${left}px + var(--offset-x, 0px))`,
|
|
66
50
|
'transform-origin': `${originY} ${originX}`,
|
|
67
51
|
})
|
|
68
52
|
.beforeAddWrite(() => {
|
|
69
|
-
if (
|
|
70
|
-
contentEl.style.setProperty('bottom',
|
|
53
|
+
if (bottom !== undefined) {
|
|
54
|
+
contentEl.style.setProperty('bottom', `${bottom}px`);
|
|
71
55
|
}
|
|
72
56
|
})
|
|
73
57
|
.fromTo('transform', 'scale(0.8)', 'scale(1)');
|
|
@@ -648,8 +648,6 @@ export const calculateWindowAdjustment = (side, coordTop, coordLeft, bodyPadding
|
|
|
648
648
|
let bottom;
|
|
649
649
|
let originX = contentOriginX;
|
|
650
650
|
let originY = contentOriginY;
|
|
651
|
-
let checkSafeAreaTop = false;
|
|
652
|
-
let checkSafeAreaBottom = false;
|
|
653
651
|
let checkSafeAreaLeft = false;
|
|
654
652
|
let checkSafeAreaRight = false;
|
|
655
653
|
const triggerTop = triggerCoordinates
|
|
@@ -694,18 +692,10 @@ export const calculateWindowAdjustment = (side, coordTop, coordLeft, bodyPadding
|
|
|
694
692
|
* We chose 12 here so that the popover position looks a bit nicer as
|
|
695
693
|
* it is not right up against the edge of the screen.
|
|
696
694
|
*/
|
|
697
|
-
top = Math.max(
|
|
695
|
+
top = Math.max(12, triggerTop - contentHeight - triggerHeight - (arrowHeight - 1));
|
|
698
696
|
arrowTop = top + contentHeight;
|
|
699
697
|
originY = 'bottom';
|
|
700
698
|
addPopoverBottomClass = true;
|
|
701
|
-
/**
|
|
702
|
-
* If the popover is positioned near the top edge, account for safe area.
|
|
703
|
-
* This ensures the popover doesn't overlap with status bars or notches.
|
|
704
|
-
*/
|
|
705
|
-
if (top <= bodyPadding + safeAreaMargin) {
|
|
706
|
-
checkSafeAreaTop = true;
|
|
707
|
-
top = bodyPadding;
|
|
708
|
-
}
|
|
709
699
|
/**
|
|
710
700
|
* If not enough room for popover to appear
|
|
711
701
|
* above trigger, then cut it off.
|
|
@@ -713,35 +703,14 @@ export const calculateWindowAdjustment = (side, coordTop, coordLeft, bodyPadding
|
|
|
713
703
|
}
|
|
714
704
|
else {
|
|
715
705
|
bottom = bodyPadding;
|
|
716
|
-
/**
|
|
717
|
-
* When the popover is pinned to the bottom, account for safe area.
|
|
718
|
-
* This ensures the popover doesn't overlap with home indicators
|
|
719
|
-
* or navigation bars (e.g., Android API 36+ edge-to-edge).
|
|
720
|
-
*/
|
|
721
|
-
checkSafeAreaBottom = true;
|
|
722
706
|
}
|
|
723
707
|
}
|
|
724
|
-
/**
|
|
725
|
-
* Final check: If the popover extends into any safe-area region,
|
|
726
|
-
* ensure the corresponding flag is set regardless of side.
|
|
727
|
-
* This handles cases where a side-positioned popover (left/right)
|
|
728
|
-
* still needs bottom safe-area padding because it extends into that region.
|
|
729
|
-
*/
|
|
730
|
-
const popoverBottom = bottom !== undefined ? bodyHeight - bottom : top + contentHeight;
|
|
731
|
-
if (popoverBottom + safeAreaMargin > bodyHeight) {
|
|
732
|
-
checkSafeAreaBottom = true;
|
|
733
|
-
}
|
|
734
|
-
if (top < safeAreaMargin) {
|
|
735
|
-
checkSafeAreaTop = true;
|
|
736
|
-
}
|
|
737
708
|
return {
|
|
738
709
|
top,
|
|
739
710
|
left,
|
|
740
711
|
bottom,
|
|
741
712
|
originX,
|
|
742
713
|
originY,
|
|
743
|
-
checkSafeAreaTop,
|
|
744
|
-
checkSafeAreaBottom,
|
|
745
714
|
checkSafeAreaLeft,
|
|
746
715
|
checkSafeAreaRight,
|
|
747
716
|
arrowTop,
|
|
@@ -11,6 +11,7 @@ import { getIonMode } from "../../global/ionic-global";
|
|
|
11
11
|
export class TabBar {
|
|
12
12
|
constructor() {
|
|
13
13
|
this.keyboardCtrl = null;
|
|
14
|
+
this.keyboardCtrlPromise = null;
|
|
14
15
|
this.didLoad = false;
|
|
15
16
|
this.keyboardVisible = false;
|
|
16
17
|
/**
|
|
@@ -46,7 +47,7 @@ export class TabBar {
|
|
|
46
47
|
}
|
|
47
48
|
}
|
|
48
49
|
async connectedCallback() {
|
|
49
|
-
|
|
50
|
+
const promise = createKeyboardController(async (keyboardOpen, waitForResize) => {
|
|
50
51
|
/**
|
|
51
52
|
* If the keyboard is hiding, then we need to wait
|
|
52
53
|
* for the webview to resize. Otherwise, the tab bar
|
|
@@ -57,21 +58,40 @@ export class TabBar {
|
|
|
57
58
|
}
|
|
58
59
|
this.keyboardVisible = keyboardOpen; // trigger re-render by updating state
|
|
59
60
|
});
|
|
61
|
+
this.keyboardCtrlPromise = promise;
|
|
62
|
+
const keyboardCtrl = await promise;
|
|
63
|
+
/**
|
|
64
|
+
* Only assign if this is still the current promise.
|
|
65
|
+
* Otherwise, a new connectedCallback has started or
|
|
66
|
+
* disconnectedCallback was called, so destroy this instance.
|
|
67
|
+
*/
|
|
68
|
+
if (this.keyboardCtrlPromise === promise) {
|
|
69
|
+
this.keyboardCtrl = keyboardCtrl;
|
|
70
|
+
this.keyboardCtrlPromise = null;
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
keyboardCtrl.destroy();
|
|
74
|
+
}
|
|
60
75
|
}
|
|
61
76
|
disconnectedCallback() {
|
|
77
|
+
if (this.keyboardCtrlPromise) {
|
|
78
|
+
this.keyboardCtrlPromise.then((ctrl) => ctrl.destroy());
|
|
79
|
+
this.keyboardCtrlPromise = null;
|
|
80
|
+
}
|
|
62
81
|
if (this.keyboardCtrl) {
|
|
63
82
|
this.keyboardCtrl.destroy();
|
|
83
|
+
this.keyboardCtrl = null;
|
|
64
84
|
}
|
|
65
85
|
}
|
|
66
86
|
render() {
|
|
67
87
|
const { color, translucent, keyboardVisible } = this;
|
|
68
88
|
const mode = getIonMode(this);
|
|
69
89
|
const shouldHide = keyboardVisible && this.el.getAttribute('slot') !== 'top';
|
|
70
|
-
return (h(Host, { key: '
|
|
90
|
+
return (h(Host, { key: '9daf4e2acaff6e3ce3878cf9dd5109fb1afbbebe', role: "tablist", "aria-hidden": shouldHide ? 'true' : null, class: createColorClasses(color, {
|
|
71
91
|
[mode]: true,
|
|
72
92
|
'tab-bar-translucent': translucent,
|
|
73
93
|
'tab-bar-hidden': shouldHide,
|
|
74
|
-
}) }, h("slot", { key: '
|
|
94
|
+
}) }, h("slot", { key: '1d15aa2da8501e8e7eff11ad4a491478be845c43' })));
|
|
75
95
|
}
|
|
76
96
|
static get is() { return "ion-tab-bar"; }
|
|
77
97
|
static get encapsulation() { return "shadow"; }
|
package/dist/docs.json
CHANGED