@ionic/core 8.6.3-dev.11751376263.167f1d05 → 8.6.3-dev.11751478001.1cb7436f
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/focus-visible.js +27 -18
- package/components/ion-select.js +3 -3
- package/components/modal.js +8 -211
- package/dist/cjs/{focus-visible-CCvKiLh3.js → focus-visible-ZV7jHMPt.js} +27 -18
- package/dist/cjs/ion-app_8.cjs.entry.js +1 -1
- package/dist/cjs/ion-datetime_3.cjs.entry.js +1 -1
- package/dist/cjs/ion-modal.cjs.entry.js +8 -211
- package/dist/cjs/ion-select_3.cjs.entry.js +3 -3
- package/dist/collection/components/modal/animations/ios.enter.js +2 -2
- package/dist/collection/components/modal/animations/ios.leave.js +2 -2
- package/dist/collection/components/modal/modal.js +4 -78
- package/dist/collection/components/select/select.js +3 -3
- package/dist/collection/utils/focus-visible.js +27 -18
- package/dist/docs.json +1 -1
- package/dist/esm/{focus-visible-BmVRXR1y.js → focus-visible-DZ2gZBzK.js} +27 -18
- package/dist/esm/ion-app_8.entry.js +1 -1
- package/dist/esm/ion-datetime_3.entry.js +1 -1
- package/dist/esm/ion-modal.entry.js +8 -211
- package/dist/esm/ion-select_3.entry.js +3 -3
- package/dist/ionic/ionic.esm.js +1 -1
- package/dist/ionic/{p-01123ecf.entry.js → p-2d46fbe7.entry.js} +1 -1
- package/dist/ionic/{p-5f671887.entry.js → p-78a61b3f.entry.js} +1 -1
- package/dist/ionic/p-7d5cf8c1.entry.js +4 -0
- package/dist/ionic/p-9e32212d.entry.js +4 -0
- package/dist/ionic/p-DZ2gZBzK.js +4 -0
- package/dist/types/components/modal/modal.d.ts +0 -6
- package/hydrate/index.js +38 -232
- package/hydrate/index.mjs +38 -232
- package/package.json +1 -1
- package/dist/collection/components/modal/animations/ios.transition.js +0 -143
- package/dist/ionic/p-4ddc10ef.entry.js +0 -4
- package/dist/ionic/p-85cedfd0.entry.js +0 -4
- package/dist/ionic/p-BmVRXR1y.js +0 -4
- package/dist/types/components/modal/animations/ios.transition.d.ts +0 -14
|
@@ -19,33 +19,44 @@ const FOCUS_KEYS = [
|
|
|
19
19
|
];
|
|
20
20
|
const startFocusVisible = (rootEl) => {
|
|
21
21
|
let currentFocus = [];
|
|
22
|
-
|
|
22
|
+
// Tracks if the last interaction was a pointer event (mouse, touch, pen)
|
|
23
|
+
// Used to distinguish between pointer and keyboard navigation for focus styling
|
|
24
|
+
let hadPointerEvent = false;
|
|
23
25
|
const ref = rootEl ? rootEl.shadowRoot : document;
|
|
24
26
|
const root = rootEl ? rootEl : document.body;
|
|
27
|
+
// Adds or removes the focused class for styling
|
|
25
28
|
const setFocus = (elements) => {
|
|
26
29
|
currentFocus.forEach((el) => el.classList.remove(ION_FOCUSED));
|
|
27
30
|
elements.forEach((el) => el.classList.add(ION_FOCUSED));
|
|
28
31
|
currentFocus = elements;
|
|
29
32
|
};
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
+
// Do not set focus on pointer interactions
|
|
34
|
+
const pointerDown = (ev) => {
|
|
35
|
+
if (ev instanceof PointerEvent && ev.pointerType !== '') {
|
|
36
|
+
hadPointerEvent = true;
|
|
37
|
+
// Reset after the event loop so only the immediate focusin is suppressed
|
|
38
|
+
setTimeout(() => {
|
|
39
|
+
hadPointerEvent = false;
|
|
40
|
+
}, 0);
|
|
41
|
+
}
|
|
33
42
|
};
|
|
43
|
+
// Clear hadPointerEvent so keyboard navigation shows focus
|
|
44
|
+
// Also, clear focus if the key is not a navigation key
|
|
34
45
|
const onKeydown = (ev) => {
|
|
35
|
-
|
|
36
|
-
|
|
46
|
+
hadPointerEvent = false;
|
|
47
|
+
const keyboardEvent = ev;
|
|
48
|
+
if (!FOCUS_KEYS.includes(keyboardEvent.key)) {
|
|
37
49
|
setFocus([]);
|
|
38
50
|
}
|
|
39
51
|
};
|
|
52
|
+
// Set focus if the last interaction was NOT a pointer event
|
|
53
|
+
// This works around iOS/Safari bugs where keydown is not fired for Tab
|
|
40
54
|
const onFocusin = (ev) => {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
}
|
|
47
|
-
return false;
|
|
48
|
-
});
|
|
55
|
+
const target = ev.target;
|
|
56
|
+
if (target.classList.contains(ION_FOCUSABLE) && !hadPointerEvent) {
|
|
57
|
+
const toFocus = ev
|
|
58
|
+
.composedPath()
|
|
59
|
+
.filter((el) => el instanceof HTMLElement && el.classList.contains(ION_FOCUSABLE));
|
|
49
60
|
setFocus(toFocus);
|
|
50
61
|
}
|
|
51
62
|
};
|
|
@@ -57,14 +68,12 @@ const startFocusVisible = (rootEl) => {
|
|
|
57
68
|
ref.addEventListener('keydown', onKeydown);
|
|
58
69
|
ref.addEventListener('focusin', onFocusin);
|
|
59
70
|
ref.addEventListener('focusout', onFocusout);
|
|
60
|
-
ref.addEventListener('
|
|
61
|
-
ref.addEventListener('mousedown', pointerDown);
|
|
71
|
+
ref.addEventListener('pointerdown', pointerDown, { passive: true });
|
|
62
72
|
const destroy = () => {
|
|
63
73
|
ref.removeEventListener('keydown', onKeydown);
|
|
64
74
|
ref.removeEventListener('focusin', onFocusin);
|
|
65
75
|
ref.removeEventListener('focusout', onFocusout);
|
|
66
|
-
ref.removeEventListener('
|
|
67
|
-
ref.removeEventListener('mousedown', pointerDown);
|
|
76
|
+
ref.removeEventListener('pointerdown', pointerDown);
|
|
68
77
|
};
|
|
69
78
|
return {
|
|
70
79
|
destroy,
|
package/components/ion-select.js
CHANGED
|
@@ -235,7 +235,7 @@ const Select = /*@__PURE__*/ proxyCustomElement(class Select extends HTMLElement
|
|
|
235
235
|
const scrollSelectedIntoView = () => {
|
|
236
236
|
const indexOfSelected = this.childOpts.findIndex((o) => o.value === this.value);
|
|
237
237
|
if (indexOfSelected > -1) {
|
|
238
|
-
const selectedItem = overlay.querySelector(`.select-interface-option:nth-
|
|
238
|
+
const selectedItem = overlay.querySelector(`.select-interface-option:nth-of-type(${indexOfSelected + 1})`);
|
|
239
239
|
if (selectedItem) {
|
|
240
240
|
/**
|
|
241
241
|
* Browsers such as Firefox do not
|
|
@@ -755,7 +755,7 @@ const Select = /*@__PURE__*/ proxyCustomElement(class Select extends HTMLElement
|
|
|
755
755
|
* TODO(FW-5592): Remove hasStartEndSlots condition
|
|
756
756
|
*/
|
|
757
757
|
const labelShouldFloat = labelPlacement === 'stacked' || (labelPlacement === 'floating' && (hasValue || isExpanded || hasStartEndSlots));
|
|
758
|
-
return (h(Host, { key: '
|
|
758
|
+
return (h(Host, { key: 'c03fb65e8fc9f9aab295e07b282377d57d910519', onClick: this.onClick, class: createColorClasses(this.color, {
|
|
759
759
|
[mode]: true,
|
|
760
760
|
'in-item': inItem,
|
|
761
761
|
'in-item-color': hostContext('ion-item.ion-color', el),
|
|
@@ -773,7 +773,7 @@ const Select = /*@__PURE__*/ proxyCustomElement(class Select extends HTMLElement
|
|
|
773
773
|
[`select-justify-${justify}`]: justifyEnabled,
|
|
774
774
|
[`select-shape-${shape}`]: shape !== undefined,
|
|
775
775
|
[`select-label-placement-${labelPlacement}`]: true,
|
|
776
|
-
}) }, h("label", { key: '
|
|
776
|
+
}) }, h("label", { key: '0d0c8ec55269adcac625f2899a547f4e7f3e3741', class: "select-wrapper", id: "select-label", onClick: this.onLabelClick }, this.renderLabelContainer(), h("div", { key: 'f6dfc93c0e23cbe75a2947abde67d842db2dad78', class: "select-wrapper-inner" }, h("slot", { key: '957bfadf9f101f519091419a362d3abdc2be66f6', name: "start" }), h("div", { key: 'ca349202a484e7f2e884533fd330f0b136754f7d', class: "native-wrapper", ref: (el) => (this.nativeWrapperEl = el), part: "container" }, this.renderSelectText(), this.renderListbox()), h("slot", { key: 'f0e62a6533ff1c8f62bd2d27f60b23385c4fa9ed', name: "end" }), !hasFloatingOrStackedLabel && this.renderSelectIcon()), hasFloatingOrStackedLabel && this.renderSelectIcon(), shouldRenderHighlight && h("div", { key: 'fb840d46bafafb09898ebeebbe8c181906a3d8a2', class: "select-highlight" })), this.renderBottomContent()));
|
|
777
777
|
}
|
|
778
778
|
get el() { return this; }
|
|
779
779
|
static get watchers() { return {
|
package/components/modal.js
CHANGED
|
@@ -587,7 +587,7 @@ const iosEnterAnimation = (baseEl, opts) => {
|
|
|
587
587
|
baseAnimation.addAnimation(contentAnimation);
|
|
588
588
|
}
|
|
589
589
|
if (presentingEl) {
|
|
590
|
-
const
|
|
590
|
+
const isMobile = window.innerWidth < 768;
|
|
591
591
|
const hasCardModal = presentingEl.tagName === 'ION-MODAL' && presentingEl.presentingElement !== undefined;
|
|
592
592
|
const presentingElRoot = getElementRoot(presentingEl);
|
|
593
593
|
const presentingAnimation = createAnimation().beforeStyles({
|
|
@@ -596,7 +596,7 @@ const iosEnterAnimation = (baseEl, opts) => {
|
|
|
596
596
|
overflow: 'hidden',
|
|
597
597
|
});
|
|
598
598
|
const bodyEl = document.body;
|
|
599
|
-
if (
|
|
599
|
+
if (isMobile) {
|
|
600
600
|
/**
|
|
601
601
|
* Fallback for browsers that does not support `max()` (ex: Firefox)
|
|
602
602
|
* No need to worry about statusbar padding since engines like Gecko
|
|
@@ -674,7 +674,7 @@ const iosLeaveAnimation = (baseEl, opts, duration = 500) => {
|
|
|
674
674
|
.duration(duration)
|
|
675
675
|
.addAnimation(wrapperAnimation);
|
|
676
676
|
if (presentingEl) {
|
|
677
|
-
const
|
|
677
|
+
const isMobile = window.innerWidth < 768;
|
|
678
678
|
const hasCardModal = presentingEl.tagName === 'ION-MODAL' && presentingEl.presentingElement !== undefined;
|
|
679
679
|
const presentingElRoot = getElementRoot(presentingEl);
|
|
680
680
|
const presentingAnimation = createAnimation()
|
|
@@ -692,7 +692,7 @@ const iosLeaveAnimation = (baseEl, opts, duration = 500) => {
|
|
|
692
692
|
}
|
|
693
693
|
});
|
|
694
694
|
const bodyEl = document.body;
|
|
695
|
-
if (
|
|
695
|
+
if (isMobile) {
|
|
696
696
|
const transformOffset = !CSS.supports('width', 'max(0px, 1px)') ? '30px' : 'max(30px, var(--ion-safe-area-top))';
|
|
697
697
|
const modalTransform = hasCardModal ? '-10px' : transformOffset;
|
|
698
698
|
const toPresentingScale = SwipeToCloseDefaults.MIN_PRESENTING_SCALE;
|
|
@@ -739,144 +739,6 @@ const iosLeaveAnimation = (baseEl, opts, duration = 500) => {
|
|
|
739
739
|
return baseAnimation;
|
|
740
740
|
};
|
|
741
741
|
|
|
742
|
-
/**
|
|
743
|
-
* Transition animation from portrait view to landscape view
|
|
744
|
-
* This handles the case where a card modal is open in portrait view
|
|
745
|
-
* and the user switches to landscape view
|
|
746
|
-
*/
|
|
747
|
-
const portraitToLandscapeTransition = (baseEl, opts, duration = 300) => {
|
|
748
|
-
const { presentingEl } = opts;
|
|
749
|
-
if (!presentingEl) {
|
|
750
|
-
// No transition needed for non-card modals
|
|
751
|
-
return createAnimation('portrait-to-landscape-transition');
|
|
752
|
-
}
|
|
753
|
-
const hasCardModal = presentingEl.tagName === 'ION-MODAL' && presentingEl.presentingElement !== undefined;
|
|
754
|
-
const presentingElRoot = getElementRoot(presentingEl);
|
|
755
|
-
const bodyEl = document.body;
|
|
756
|
-
const baseAnimation = createAnimation('portrait-to-landscape-transition')
|
|
757
|
-
.addElement(baseEl)
|
|
758
|
-
.easing('cubic-bezier(0.32,0.72,0,1)')
|
|
759
|
-
.duration(duration);
|
|
760
|
-
const presentingAnimation = createAnimation().beforeStyles({
|
|
761
|
-
transform: 'translateY(0)',
|
|
762
|
-
'transform-origin': 'top center',
|
|
763
|
-
overflow: 'hidden',
|
|
764
|
-
});
|
|
765
|
-
if (!hasCardModal) {
|
|
766
|
-
// Non-card modal: transition from portrait state to landscape state
|
|
767
|
-
// Portrait: presentingEl has transform and body has black background
|
|
768
|
-
// Landscape: no transform, no body background, modal wrapper opacity changes
|
|
769
|
-
const root = getElementRoot(baseEl);
|
|
770
|
-
const wrapperAnimation = createAnimation()
|
|
771
|
-
.addElement(root.querySelectorAll('.modal-wrapper, .modal-shadow'))
|
|
772
|
-
.fromTo('opacity', '1', '1'); // Keep wrapper visible in landscape
|
|
773
|
-
const backdropAnimation = createAnimation()
|
|
774
|
-
.addElement(root.querySelector('ion-backdrop'))
|
|
775
|
-
.fromTo('opacity', 'var(--backdrop-opacity)', 'var(--backdrop-opacity)'); // Keep backdrop visible
|
|
776
|
-
// Animate presentingEl from portrait state back to normal
|
|
777
|
-
const transformOffset = !CSS.supports('width', 'max(0px, 1px)') ? '30px' : 'max(30px, var(--ion-safe-area-top))';
|
|
778
|
-
const toPresentingScale = SwipeToCloseDefaults.MIN_PRESENTING_SCALE;
|
|
779
|
-
const fromTransform = `translateY(${transformOffset}) scale(${toPresentingScale})`;
|
|
780
|
-
presentingAnimation
|
|
781
|
-
.addElement(presentingEl)
|
|
782
|
-
.afterStyles({
|
|
783
|
-
transform: 'translateY(0px) scale(1)',
|
|
784
|
-
'border-radius': '0px',
|
|
785
|
-
})
|
|
786
|
-
.beforeAddWrite(() => bodyEl.style.setProperty('background-color', ''))
|
|
787
|
-
.fromTo('transform', fromTransform, 'translateY(0px) scale(1)')
|
|
788
|
-
.fromTo('filter', 'contrast(0.85)', 'contrast(1)')
|
|
789
|
-
.fromTo('border-radius', '10px 10px 0 0', '0px');
|
|
790
|
-
baseAnimation.addAnimation([presentingAnimation, wrapperAnimation, backdropAnimation]);
|
|
791
|
-
}
|
|
792
|
-
else {
|
|
793
|
-
// Card modal: transition from portrait card state to landscape card state
|
|
794
|
-
const toPresentingScale = SwipeToCloseDefaults.MIN_PRESENTING_SCALE;
|
|
795
|
-
const transformOffset = !CSS.supports('width', 'max(0px, 1px)') ? '30px' : 'max(30px, var(--ion-safe-area-top))';
|
|
796
|
-
const fromTransform = `translateY(${transformOffset}) scale(${toPresentingScale})`;
|
|
797
|
-
const toTransform = `translateY(-10px) scale(${toPresentingScale})`;
|
|
798
|
-
presentingAnimation
|
|
799
|
-
.addElement(presentingElRoot.querySelector('.modal-wrapper'))
|
|
800
|
-
.fromTo('transform', fromTransform, toTransform)
|
|
801
|
-
.fromTo('filter', 'contrast(0.85)', 'contrast(0.85)'); // Keep same contrast for card
|
|
802
|
-
const shadowAnimation = createAnimation()
|
|
803
|
-
.addElement(presentingElRoot.querySelector('.modal-shadow'))
|
|
804
|
-
.fromTo('opacity', '0', '0') // Shadow stays hidden in landscape for card modals
|
|
805
|
-
.fromTo('transform', fromTransform, toTransform);
|
|
806
|
-
baseAnimation.addAnimation([presentingAnimation, shadowAnimation]);
|
|
807
|
-
}
|
|
808
|
-
return baseAnimation;
|
|
809
|
-
};
|
|
810
|
-
/**
|
|
811
|
-
* Transition animation from landscape view to portrait view
|
|
812
|
-
* This handles the case where a card modal is open in landscape view
|
|
813
|
-
* and the user switches to portrait view
|
|
814
|
-
*/
|
|
815
|
-
const landscapeToPortraitTransition = (baseEl, opts, duration = 300) => {
|
|
816
|
-
const { presentingEl } = opts;
|
|
817
|
-
if (!presentingEl) {
|
|
818
|
-
// No transition needed for non-card modals
|
|
819
|
-
return createAnimation('landscape-to-portrait-transition');
|
|
820
|
-
}
|
|
821
|
-
const hasCardModal = presentingEl.tagName === 'ION-MODAL' && presentingEl.presentingElement !== undefined;
|
|
822
|
-
const presentingElRoot = getElementRoot(presentingEl);
|
|
823
|
-
const bodyEl = document.body;
|
|
824
|
-
const baseAnimation = createAnimation('landscape-to-portrait-transition')
|
|
825
|
-
.addElement(baseEl)
|
|
826
|
-
.easing('cubic-bezier(0.32,0.72,0,1)')
|
|
827
|
-
.duration(duration);
|
|
828
|
-
const presentingAnimation = createAnimation().beforeStyles({
|
|
829
|
-
transform: 'translateY(0)',
|
|
830
|
-
'transform-origin': 'top center',
|
|
831
|
-
overflow: 'hidden',
|
|
832
|
-
});
|
|
833
|
-
if (!hasCardModal) {
|
|
834
|
-
// Non-card modal: transition from landscape state to portrait state
|
|
835
|
-
const root = getElementRoot(baseEl);
|
|
836
|
-
const wrapperAnimation = createAnimation()
|
|
837
|
-
.addElement(root.querySelectorAll('.modal-wrapper, .modal-shadow'))
|
|
838
|
-
.fromTo('opacity', '1', '1'); // Keep wrapper visible
|
|
839
|
-
const backdropAnimation = createAnimation()
|
|
840
|
-
.addElement(root.querySelector('ion-backdrop'))
|
|
841
|
-
.fromTo('opacity', 'var(--backdrop-opacity)', 'var(--backdrop-opacity)'); // Keep backdrop visible
|
|
842
|
-
// Animate presentingEl from normal state to portrait state
|
|
843
|
-
const transformOffset = !CSS.supports('width', 'max(0px, 1px)') ? '30px' : 'max(30px, var(--ion-safe-area-top))';
|
|
844
|
-
const toPresentingScale = SwipeToCloseDefaults.MIN_PRESENTING_SCALE;
|
|
845
|
-
const toTransform = `translateY(${transformOffset}) scale(${toPresentingScale})`;
|
|
846
|
-
presentingAnimation
|
|
847
|
-
.addElement(presentingEl)
|
|
848
|
-
.afterStyles({
|
|
849
|
-
transform: toTransform,
|
|
850
|
-
'border-radius': '10px 10px 0 0',
|
|
851
|
-
filter: 'contrast(0.85)',
|
|
852
|
-
overflow: 'hidden',
|
|
853
|
-
'transform-origin': 'top center',
|
|
854
|
-
})
|
|
855
|
-
.beforeAddWrite(() => bodyEl.style.setProperty('background-color', 'black'))
|
|
856
|
-
.fromTo('transform', 'translateY(0px) scale(1)', toTransform)
|
|
857
|
-
.fromTo('filter', 'contrast(1)', 'contrast(0.85)')
|
|
858
|
-
.fromTo('border-radius', '0px', '10px 10px 0 0');
|
|
859
|
-
baseAnimation.addAnimation([presentingAnimation, wrapperAnimation, backdropAnimation]);
|
|
860
|
-
}
|
|
861
|
-
else {
|
|
862
|
-
// Card modal: transition from landscape card state to portrait card state
|
|
863
|
-
const toPresentingScale = SwipeToCloseDefaults.MIN_PRESENTING_SCALE;
|
|
864
|
-
const transformOffset = !CSS.supports('width', 'max(0px, 1px)') ? '30px' : 'max(30px, var(--ion-safe-area-top))';
|
|
865
|
-
const fromTransform = `translateY(-10px) scale(${toPresentingScale})`;
|
|
866
|
-
const toTransform = `translateY(${transformOffset}) scale(${toPresentingScale})`;
|
|
867
|
-
presentingAnimation
|
|
868
|
-
.addElement(presentingElRoot.querySelector('.modal-wrapper'))
|
|
869
|
-
.fromTo('transform', fromTransform, toTransform)
|
|
870
|
-
.fromTo('filter', 'contrast(0.85)', 'contrast(0.85)'); // Keep same contrast for card
|
|
871
|
-
const shadowAnimation = createAnimation()
|
|
872
|
-
.addElement(presentingElRoot.querySelector('.modal-shadow'))
|
|
873
|
-
.fromTo('opacity', '0', '0') // Shadow stays hidden
|
|
874
|
-
.fromTo('transform', fromTransform, toTransform);
|
|
875
|
-
baseAnimation.addAnimation([presentingAnimation, shadowAnimation]);
|
|
876
|
-
}
|
|
877
|
-
return baseAnimation;
|
|
878
|
-
};
|
|
879
|
-
|
|
880
742
|
const createEnterAnimation = () => {
|
|
881
743
|
const backdropAnimation = createAnimation()
|
|
882
744
|
.fromTo('opacity', 0.01, 'var(--backdrop-opacity)')
|
|
@@ -1660,7 +1522,6 @@ const Modal = /*@__PURE__*/ proxyCustomElement(class Modal extends HTMLElement {
|
|
|
1660
1522
|
}
|
|
1661
1523
|
disconnectedCallback() {
|
|
1662
1524
|
this.triggerController.removeClickListener();
|
|
1663
|
-
this.cleanupViewTransitionListener();
|
|
1664
1525
|
}
|
|
1665
1526
|
componentWillLoad() {
|
|
1666
1527
|
var _a;
|
|
@@ -1871,8 +1732,6 @@ const Modal = /*@__PURE__*/ proxyCustomElement(class Modal extends HTMLElement {
|
|
|
1871
1732
|
else if (hasCardModal) {
|
|
1872
1733
|
this.initSwipeToClose();
|
|
1873
1734
|
}
|
|
1874
|
-
// Initialize view transition listener for iOS card modals
|
|
1875
|
-
this.initViewTransitionListener();
|
|
1876
1735
|
unlock();
|
|
1877
1736
|
}
|
|
1878
1737
|
initSwipeToClose() {
|
|
@@ -2026,7 +1885,6 @@ const Modal = /*@__PURE__*/ proxyCustomElement(class Modal extends HTMLElement {
|
|
|
2026
1885
|
if (this.gesture) {
|
|
2027
1886
|
this.gesture.destroy();
|
|
2028
1887
|
}
|
|
2029
|
-
this.cleanupViewTransitionListener();
|
|
2030
1888
|
}
|
|
2031
1889
|
this.currentBreakpoint = undefined;
|
|
2032
1890
|
this.animation = undefined;
|
|
@@ -2102,67 +1960,6 @@ const Modal = /*@__PURE__*/ proxyCustomElement(class Modal extends HTMLElement {
|
|
|
2102
1960
|
await this.setCurrentBreakpoint(nextBreakpoint);
|
|
2103
1961
|
return true;
|
|
2104
1962
|
}
|
|
2105
|
-
initViewTransitionListener() {
|
|
2106
|
-
// Only enable for iOS card modals when no custom animations are provided
|
|
2107
|
-
if (getIonMode(this) !== 'ios' || !this.presentingElement || this.enterAnimation || this.leaveAnimation) {
|
|
2108
|
-
return;
|
|
2109
|
-
}
|
|
2110
|
-
// Set initial view state
|
|
2111
|
-
this.currentViewIsPortrait = window.innerWidth < 768;
|
|
2112
|
-
// Create debounced resize handler
|
|
2113
|
-
let resizeTimeout;
|
|
2114
|
-
this.resizeListener = () => {
|
|
2115
|
-
clearTimeout(resizeTimeout);
|
|
2116
|
-
resizeTimeout = setTimeout(() => {
|
|
2117
|
-
this.handleViewTransition();
|
|
2118
|
-
}, 100); // Debounce for 100ms to avoid excessive calls
|
|
2119
|
-
};
|
|
2120
|
-
window.addEventListener('resize', this.resizeListener);
|
|
2121
|
-
}
|
|
2122
|
-
handleViewTransition() {
|
|
2123
|
-
const isPortrait = window.innerWidth < 768;
|
|
2124
|
-
// Only transition if view state actually changed
|
|
2125
|
-
if (this.currentViewIsPortrait === isPortrait) {
|
|
2126
|
-
return;
|
|
2127
|
-
}
|
|
2128
|
-
// Cancel any ongoing transition animation
|
|
2129
|
-
if (this.viewTransitionAnimation) {
|
|
2130
|
-
this.viewTransitionAnimation.destroy();
|
|
2131
|
-
this.viewTransitionAnimation = undefined;
|
|
2132
|
-
}
|
|
2133
|
-
const { presentingElement } = this;
|
|
2134
|
-
if (!presentingElement) {
|
|
2135
|
-
return;
|
|
2136
|
-
}
|
|
2137
|
-
// Create transition animation
|
|
2138
|
-
let transitionAnimation;
|
|
2139
|
-
if (this.currentViewIsPortrait && !isPortrait) {
|
|
2140
|
-
// Portrait to landscape transition
|
|
2141
|
-
transitionAnimation = portraitToLandscapeTransition(this.el, {
|
|
2142
|
-
presentingEl: presentingElement});
|
|
2143
|
-
}
|
|
2144
|
-
else {
|
|
2145
|
-
// Landscape to portrait transition
|
|
2146
|
-
transitionAnimation = landscapeToPortraitTransition(this.el, {
|
|
2147
|
-
presentingEl: presentingElement});
|
|
2148
|
-
}
|
|
2149
|
-
// Update state and play animation
|
|
2150
|
-
this.currentViewIsPortrait = isPortrait;
|
|
2151
|
-
this.viewTransitionAnimation = transitionAnimation;
|
|
2152
|
-
transitionAnimation.play().then(() => {
|
|
2153
|
-
this.viewTransitionAnimation = undefined;
|
|
2154
|
-
});
|
|
2155
|
-
}
|
|
2156
|
-
cleanupViewTransitionListener() {
|
|
2157
|
-
if (this.resizeListener) {
|
|
2158
|
-
window.removeEventListener('resize', this.resizeListener);
|
|
2159
|
-
this.resizeListener = undefined;
|
|
2160
|
-
}
|
|
2161
|
-
if (this.viewTransitionAnimation) {
|
|
2162
|
-
this.viewTransitionAnimation.destroy();
|
|
2163
|
-
this.viewTransitionAnimation = undefined;
|
|
2164
|
-
}
|
|
2165
|
-
}
|
|
2166
1963
|
render() {
|
|
2167
1964
|
const { handle, isSheetModal, presentingElement, htmlAttributes, handleBehavior, inheritedAttributes, focusTrap, expandToScroll, } = this;
|
|
2168
1965
|
const showHandle = handle !== false && isSheetModal;
|
|
@@ -2170,20 +1967,20 @@ const Modal = /*@__PURE__*/ proxyCustomElement(class Modal extends HTMLElement {
|
|
|
2170
1967
|
const isCardModal = presentingElement !== undefined && mode === 'ios';
|
|
2171
1968
|
const isHandleCycle = handleBehavior === 'cycle';
|
|
2172
1969
|
const isSheetModalWithHandle = isSheetModal && showHandle;
|
|
2173
|
-
return (h(Host, Object.assign({ key: '
|
|
1970
|
+
return (h(Host, Object.assign({ key: '8add05bb43a2cdb5e3cf180147d31eb85a018fe0', "no-router": true,
|
|
2174
1971
|
// Allow the modal to be navigable when the handle is focusable
|
|
2175
1972
|
tabIndex: isHandleCycle && isSheetModalWithHandle ? 0 : -1 }, htmlAttributes, { style: {
|
|
2176
1973
|
zIndex: `${20000 + this.overlayIndex}`,
|
|
2177
|
-
}, 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: '
|
|
1974
|
+
}, 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: '90a6605a9564a699d6f66cf71cf6b506796a2963', ref: (el) => (this.backdropEl = el), visible: this.showBackdrop, tappable: this.backdropDismiss, part: "backdrop" }), mode === 'ios' && h("div", { key: 'a97d071395333bf803c0a9347bda000cf7500d8d', class: "modal-shadow" }), h("div", Object.assign({ key: 'e7b7985c7414a13e3ba8dcecf497b76e92edf53e',
|
|
2178
1975
|
/*
|
|
2179
1976
|
role and aria-modal must be used on the
|
|
2180
1977
|
same element. They must also be set inside the
|
|
2181
1978
|
shadow DOM otherwise ion-button will not be highlighted
|
|
2182
1979
|
when using VoiceOver: https://bugs.webkit.org/show_bug.cgi?id=247134
|
|
2183
1980
|
*/
|
|
2184
|
-
role: "dialog" }, inheritedAttributes, { "aria-modal": "true", class: "modal-wrapper ion-overlay-wrapper", part: "content", ref: (el) => (this.wrapperEl = el) }), showHandle && (h("button", { key: '
|
|
1981
|
+
role: "dialog" }, inheritedAttributes, { "aria-modal": "true", class: "modal-wrapper ion-overlay-wrapper", part: "content", ref: (el) => (this.wrapperEl = el) }), showHandle && (h("button", { key: '8258b65570b11a8ee9c9df2537d6419cd2e34536', class: "modal-handle",
|
|
2185
1982
|
// Prevents the handle from receiving keyboard focus when it does not cycle
|
|
2186
|
-
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: '
|
|
1983
|
+
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: '394370d0ed03ee03152f8f8abae7ff7664ca5c13' }))));
|
|
2187
1984
|
}
|
|
2188
1985
|
get el() { return this; }
|
|
2189
1986
|
static get watchers() { return {
|
|
@@ -21,33 +21,44 @@ const FOCUS_KEYS = [
|
|
|
21
21
|
];
|
|
22
22
|
const startFocusVisible = (rootEl) => {
|
|
23
23
|
let currentFocus = [];
|
|
24
|
-
|
|
24
|
+
// Tracks if the last interaction was a pointer event (mouse, touch, pen)
|
|
25
|
+
// Used to distinguish between pointer and keyboard navigation for focus styling
|
|
26
|
+
let hadPointerEvent = false;
|
|
25
27
|
const ref = rootEl ? rootEl.shadowRoot : document;
|
|
26
28
|
const root = rootEl ? rootEl : document.body;
|
|
29
|
+
// Adds or removes the focused class for styling
|
|
27
30
|
const setFocus = (elements) => {
|
|
28
31
|
currentFocus.forEach((el) => el.classList.remove(ION_FOCUSED));
|
|
29
32
|
elements.forEach((el) => el.classList.add(ION_FOCUSED));
|
|
30
33
|
currentFocus = elements;
|
|
31
34
|
};
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
+
// Do not set focus on pointer interactions
|
|
36
|
+
const pointerDown = (ev) => {
|
|
37
|
+
if (ev instanceof PointerEvent && ev.pointerType !== '') {
|
|
38
|
+
hadPointerEvent = true;
|
|
39
|
+
// Reset after the event loop so only the immediate focusin is suppressed
|
|
40
|
+
setTimeout(() => {
|
|
41
|
+
hadPointerEvent = false;
|
|
42
|
+
}, 0);
|
|
43
|
+
}
|
|
35
44
|
};
|
|
45
|
+
// Clear hadPointerEvent so keyboard navigation shows focus
|
|
46
|
+
// Also, clear focus if the key is not a navigation key
|
|
36
47
|
const onKeydown = (ev) => {
|
|
37
|
-
|
|
38
|
-
|
|
48
|
+
hadPointerEvent = false;
|
|
49
|
+
const keyboardEvent = ev;
|
|
50
|
+
if (!FOCUS_KEYS.includes(keyboardEvent.key)) {
|
|
39
51
|
setFocus([]);
|
|
40
52
|
}
|
|
41
53
|
};
|
|
54
|
+
// Set focus if the last interaction was NOT a pointer event
|
|
55
|
+
// This works around iOS/Safari bugs where keydown is not fired for Tab
|
|
42
56
|
const onFocusin = (ev) => {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
}
|
|
49
|
-
return false;
|
|
50
|
-
});
|
|
57
|
+
const target = ev.target;
|
|
58
|
+
if (target.classList.contains(ION_FOCUSABLE) && !hadPointerEvent) {
|
|
59
|
+
const toFocus = ev
|
|
60
|
+
.composedPath()
|
|
61
|
+
.filter((el) => el instanceof HTMLElement && el.classList.contains(ION_FOCUSABLE));
|
|
51
62
|
setFocus(toFocus);
|
|
52
63
|
}
|
|
53
64
|
};
|
|
@@ -59,14 +70,12 @@ const startFocusVisible = (rootEl) => {
|
|
|
59
70
|
ref.addEventListener('keydown', onKeydown);
|
|
60
71
|
ref.addEventListener('focusin', onFocusin);
|
|
61
72
|
ref.addEventListener('focusout', onFocusout);
|
|
62
|
-
ref.addEventListener('
|
|
63
|
-
ref.addEventListener('mousedown', pointerDown);
|
|
73
|
+
ref.addEventListener('pointerdown', pointerDown, { passive: true });
|
|
64
74
|
const destroy = () => {
|
|
65
75
|
ref.removeEventListener('keydown', onKeydown);
|
|
66
76
|
ref.removeEventListener('focusin', onFocusin);
|
|
67
77
|
ref.removeEventListener('focusout', onFocusout);
|
|
68
|
-
ref.removeEventListener('
|
|
69
|
-
ref.removeEventListener('mousedown', pointerDown);
|
|
78
|
+
ref.removeEventListener('pointerdown', pointerDown);
|
|
70
79
|
};
|
|
71
80
|
return {
|
|
72
81
|
destroy,
|
|
@@ -60,7 +60,7 @@ const App = class {
|
|
|
60
60
|
if (typeof window !== 'undefined') {
|
|
61
61
|
Promise.resolve().then(function () { return require('./keyboard-hHzlEQpk.js'); }).then((module) => module.startKeyboardAssist(window));
|
|
62
62
|
}
|
|
63
|
-
Promise.resolve().then(function () { return require('./focus-visible-
|
|
63
|
+
Promise.resolve().then(function () { return require('./focus-visible-ZV7jHMPt.js'); }).then((module) => (this.focusVisible = module.startFocusVisible()));
|
|
64
64
|
});
|
|
65
65
|
}
|
|
66
66
|
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
'use strict';
|
|
5
5
|
|
|
6
6
|
var index = require('./index-DODXXb_r.js');
|
|
7
|
-
var focusVisible = require('./focus-visible-
|
|
7
|
+
var focusVisible = require('./focus-visible-ZV7jHMPt.js');
|
|
8
8
|
var helpers = require('./helpers-BITAzJfi.js');
|
|
9
9
|
var dir = require('./dir-Cn0z1rJH.js');
|
|
10
10
|
var theme = require('./theme-CeDs6Hcv.js');
|