voyager-ionic-core 8.8.4 → 8.8.6
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/ion-action-sheet.js +1 -1
- package/components/ion-content.js +1 -1
- package/components/ion-datetime.js +1 -1
- package/components/ion-modal.js +1 -1
- package/components/ion-radio-group.js +1 -1
- package/components/ion-segment.js +1 -1
- package/components/ion-select-modal.js +1 -1
- package/components/ion-select-popover.js +1 -1
- package/components/ion-select.js +1 -1
- package/components/p-0z8QSI5b.js +4 -0
- package/components/{p-ApmKVjaE.js → p-BGHGpkPX.js} +1 -1
- package/components/p-BlNv564p.js +4 -0
- package/components/p-D-cP12ZN.js +4 -0
- package/components/p-DZhbcvo5.js +4 -0
- package/components/{p-Bk2zuNWT.js → p-DvOO1fxp.js} +1 -1
- package/dist/cjs/ion-action-sheet.cjs.entry.js +4 -4
- package/dist/cjs/ion-app_8.cjs.entry.js +1 -1
- package/dist/cjs/ion-datetime_3.cjs.entry.js +30 -14
- package/dist/cjs/ion-modal.cjs.entry.js +99 -45
- package/dist/cjs/ion-radio_2.cjs.entry.js +13 -1
- package/dist/cjs/ion-segment_2.cjs.entry.js +6 -2
- package/dist/cjs/ion-select-modal.cjs.entry.js +18 -7
- package/dist/cjs/ion-select_3.cjs.entry.js +18 -7
- package/dist/collection/components/action-sheet/action-sheet.js +4 -4
- package/dist/collection/components/content/content.css +1 -1
- package/dist/collection/components/datetime/datetime.js +30 -14
- package/dist/collection/components/modal/modal.js +73 -44
- package/dist/collection/components/modal/safe-area-utils.js +27 -2
- package/dist/collection/components/radio-group/radio-group.js +13 -1
- package/dist/collection/components/radio-group/test/fixtures.js +2 -2
- package/dist/collection/components/segment/segment.js +6 -2
- package/dist/collection/components/select-modal/select-modal.js +18 -7
- package/dist/collection/components/select-modal/test/fixtures.js +4 -0
- package/dist/collection/components/select-popover/select-popover.js +18 -7
- package/dist/collection/components/select-popover/test/fixtures.js +4 -0
- package/dist/docs.json +1 -1
- package/dist/esm/ion-action-sheet.entry.js +4 -4
- package/dist/esm/ion-app_8.entry.js +1 -1
- package/dist/esm/ion-datetime_3.entry.js +30 -14
- package/dist/esm/ion-modal.entry.js +99 -45
- package/dist/esm/ion-radio_2.entry.js +13 -1
- package/dist/esm/ion-segment_2.entry.js +6 -2
- package/dist/esm/ion-select-modal.entry.js +18 -7
- package/dist/esm/ion-select_3.entry.js +18 -7
- package/dist/ionic/ionic.esm.js +1 -1
- package/dist/ionic/p-1ca9c36b.entry.js +4 -0
- package/dist/ionic/p-28a9e720.entry.js +4 -0
- package/dist/ionic/p-7761ef65.entry.js +4 -0
- package/dist/ionic/{p-4dd5e8e0.entry.js → p-8fda6a62.entry.js} +1 -1
- package/dist/ionic/p-a893c61c.entry.js +4 -0
- package/dist/ionic/{p-9eac4eb1.entry.js → p-aa812c4b.entry.js} +1 -1
- package/dist/ionic/p-cb27fe68.entry.js +4 -0
- package/dist/ionic/p-ce2edb36.entry.js +4 -0
- package/dist/types/components/datetime/datetime.d.ts +7 -0
- package/dist/types/components/modal/modal.d.ts +41 -3
- package/dist/types/components/modal/safe-area-utils.d.ts +16 -0
- package/dist/types/components/radio-group/test/fixtures.d.ts +1 -1
- package/dist/types/components/select-modal/select-modal.d.ts +1 -0
- package/dist/types/components/select-modal/test/fixtures.d.ts +1 -0
- package/dist/types/components/select-popover/select-popover.d.ts +1 -0
- package/dist/types/components/select-popover/test/fixtures.d.ts +1 -0
- package/hydrate/index.js +189 -81
- package/hydrate/index.mjs +189 -81
- package/package.json +1 -1
- package/components/p-1KVKSLu5.js +0 -4
- package/components/p-BI7WNErr.js +0 -4
- package/components/p-BTF2nRLo.js +0 -4
- package/components/p-EK4xUz-q.js +0 -4
- package/dist/ionic/p-4c67ce4c.entry.js +0 -4
- package/dist/ionic/p-51c11c47.entry.js +0 -4
- package/dist/ionic/p-5681dde4.entry.js +0 -4
- package/dist/ionic/p-9cdbabbb.entry.js +0 -4
- package/dist/ionic/p-cb78f5a0.entry.js +0 -4
- package/dist/ionic/p-e6c5f060.entry.js +0 -4
|
@@ -21,7 +21,7 @@ import { mdEnterAnimation } from "./animations/md.enter";
|
|
|
21
21
|
import { mdLeaveAnimation } from "./animations/md.leave";
|
|
22
22
|
import { createSheetGesture } from "./gestures/sheet";
|
|
23
23
|
import { createSwipeToCloseGesture, SwipeToCloseDefaults } from "./gestures/swipe-to-close";
|
|
24
|
-
import { getInitialSafeAreaConfig, getPositionBasedSafeAreaConfig, applySafeAreaOverrides, clearSafeAreaOverrides, getRootSafeAreaTop, } from "./safe-area-utils";
|
|
24
|
+
import { getInitialSafeAreaConfig, getPositionBasedSafeAreaConfig, applySafeAreaOverrides, clearSafeAreaOverrides, getRootSafeAreaTop, hasCustomModalDimensions, } from "./safe-area-utils";
|
|
25
25
|
import { setCardStatusBarDark, setCardStatusBarDefault } from "./utils";
|
|
26
26
|
// TODO(FW-2832): types
|
|
27
27
|
/**
|
|
@@ -251,12 +251,10 @@ export class Modal {
|
|
|
251
251
|
// since the viewport may have crossed the centered-dialog breakpoint.
|
|
252
252
|
if (!context.isSheetModal && !context.isCardModal) {
|
|
253
253
|
this.updateSafeAreaOverrides();
|
|
254
|
-
// Re-evaluate fullscreen safe-area padding: clear first, then re-apply
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
}
|
|
259
|
-
this.applyFullscreenSafeArea();
|
|
254
|
+
// Re-evaluate fullscreen safe-area padding: clear first, then re-apply.
|
|
255
|
+
const { contentEl, hasFooter } = this.findContentAndFooter();
|
|
256
|
+
this.clearContentSafeAreaPadding(contentEl);
|
|
257
|
+
this.applyFullscreenSafeAreaTo(contentEl, hasFooter);
|
|
260
258
|
}
|
|
261
259
|
}, 50); // Debounce to avoid excessive calls during active resizing
|
|
262
260
|
}
|
|
@@ -1011,6 +1009,11 @@ export class Modal {
|
|
|
1011
1009
|
}
|
|
1012
1010
|
/**
|
|
1013
1011
|
* Creates the context object for safe-area utilities.
|
|
1012
|
+
*
|
|
1013
|
+
* `hasCustomDimensions` is only set by `setInitialSafeAreaOverrides()`
|
|
1014
|
+
* because it is only read by `getInitialSafeAreaConfig()`. Other callers
|
|
1015
|
+
* (resize handler, post-animation update, fullscreen-padding apply) would
|
|
1016
|
+
* pay a `getComputedStyle()` cost for a value they never consult.
|
|
1014
1017
|
*/
|
|
1015
1018
|
getSafeAreaContext() {
|
|
1016
1019
|
return {
|
|
@@ -1032,7 +1035,7 @@ export class Modal {
|
|
|
1032
1035
|
* sheets to prevent header content from getting double-offset padding).
|
|
1033
1036
|
*/
|
|
1034
1037
|
setInitialSafeAreaOverrides() {
|
|
1035
|
-
const context = this.getSafeAreaContext();
|
|
1038
|
+
const context = Object.assign(Object.assign({}, this.getSafeAreaContext()), { hasCustomDimensions: hasCustomModalDimensions(this.el) });
|
|
1036
1039
|
const safeAreaConfig = getInitialSafeAreaConfig(context);
|
|
1037
1040
|
applySafeAreaOverrides(this.el, safeAreaConfig);
|
|
1038
1041
|
// Set the internal offset property with the resolved root safe-area-top value
|
|
@@ -1072,59 +1075,85 @@ export class Modal {
|
|
|
1072
1075
|
applySafeAreaOverrides(el, safeAreaConfig);
|
|
1073
1076
|
}
|
|
1074
1077
|
/**
|
|
1075
|
-
* Applies
|
|
1076
|
-
*
|
|
1078
|
+
* Applies safe-area-bottom scroll padding to ion-content inside
|
|
1079
|
+
* fullscreen modals that have no ion-footer. This prevents content
|
|
1080
|
+
* from being hidden behind the system navigation bar while keeping
|
|
1081
|
+
* the modal background edge-to-edge (no visible gap).
|
|
1077
1082
|
*/
|
|
1078
1083
|
applyFullscreenSafeArea() {
|
|
1079
|
-
const { wrapperEl, el } = this;
|
|
1080
|
-
if (!wrapperEl)
|
|
1081
|
-
return;
|
|
1082
1084
|
const context = this.getSafeAreaContext();
|
|
1083
1085
|
if (context.isSheetModal || context.isCardModal)
|
|
1084
1086
|
return;
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1087
|
+
const { contentEl, hasFooter } = this.findContentAndFooter();
|
|
1088
|
+
this.applyFullscreenSafeAreaTo(contentEl, hasFooter);
|
|
1089
|
+
}
|
|
1090
|
+
/**
|
|
1091
|
+
* Sets --ion-content-safe-area-padding-bottom on the given ion-content
|
|
1092
|
+
* when no footer is present, so ion-content's .inner-scroll includes
|
|
1093
|
+
* safe-area-bottom in its scroll padding. This keeps the modal background
|
|
1094
|
+
* edge-to-edge while ensuring content scrolls clear of the system nav bar.
|
|
1095
|
+
*
|
|
1096
|
+
* --ion-content-safe-area-padding-bottom is an internal CSS property used
|
|
1097
|
+
* only by this code path. It is not part of ion-content's public API and
|
|
1098
|
+
* should not be set by consumers. The default of 0px makes it a no-op
|
|
1099
|
+
* when unset, which is the expected state for ion-content used outside of
|
|
1100
|
+
* a fullscreen modal without a footer.
|
|
1101
|
+
*/
|
|
1102
|
+
applyFullscreenSafeAreaTo(contentEl, hasFooter) {
|
|
1103
|
+
// Only apply for standard Ionic layouts (has ion-content but no
|
|
1104
|
+
// ion-footer). When a footer is present it handles its own safe-area
|
|
1105
|
+
// padding. Custom modals with raw HTML are developer-controlled.
|
|
1106
|
+
if (!contentEl || hasFooter)
|
|
1107
|
+
return;
|
|
1108
|
+
contentEl.style.setProperty('--ion-content-safe-area-padding-bottom', 'var(--ion-safe-area-bottom, 0px)');
|
|
1109
|
+
}
|
|
1110
|
+
/**
|
|
1111
|
+
* Removes the internal --ion-content-safe-area-padding-bottom property
|
|
1112
|
+
* from an already-located ion-content. Callers do their own
|
|
1113
|
+
* findContentAndFooter() so they can also read hasFooter if needed.
|
|
1114
|
+
*/
|
|
1115
|
+
clearContentSafeAreaPadding(contentEl) {
|
|
1116
|
+
if (!contentEl)
|
|
1117
|
+
return;
|
|
1118
|
+
contentEl.style.removeProperty('--ion-content-safe-area-padding-bottom');
|
|
1119
|
+
}
|
|
1120
|
+
/**
|
|
1121
|
+
* Finds ion-content and ion-footer among direct children and one level of
|
|
1122
|
+
* grandchildren (for wrapped components like <app-footer><ion-footer>).
|
|
1123
|
+
*
|
|
1124
|
+
* Intentionally does NOT use findIonContent() or querySelector() because
|
|
1125
|
+
* those search the full subtree and would match ion-content inside nested
|
|
1126
|
+
* routes/pages. We only want direct slot children (+ one wrapper level).
|
|
1127
|
+
*
|
|
1128
|
+
* Uses a manual loop instead of querySelector(':scope > ...') because
|
|
1129
|
+
* Stencil's mock-doc (used in spec tests) does not support :scope.
|
|
1130
|
+
*/
|
|
1131
|
+
findContentAndFooter() {
|
|
1132
|
+
let contentEl = null;
|
|
1091
1133
|
let hasFooter = false;
|
|
1092
|
-
for (const child of Array.from(el.children)) {
|
|
1134
|
+
for (const child of Array.from(this.el.children)) {
|
|
1093
1135
|
if (child.tagName === 'ION-CONTENT')
|
|
1094
|
-
|
|
1136
|
+
contentEl = child;
|
|
1095
1137
|
if (child.tagName === 'ION-FOOTER')
|
|
1096
1138
|
hasFooter = true;
|
|
1097
1139
|
for (const grandchild of Array.from(child.children)) {
|
|
1098
|
-
if (grandchild.tagName === 'ION-CONTENT')
|
|
1099
|
-
|
|
1140
|
+
if (grandchild.tagName === 'ION-CONTENT' && !contentEl)
|
|
1141
|
+
contentEl = grandchild;
|
|
1100
1142
|
if (grandchild.tagName === 'ION-FOOTER')
|
|
1101
1143
|
hasFooter = true;
|
|
1102
1144
|
}
|
|
1103
1145
|
}
|
|
1104
|
-
|
|
1105
|
-
// but no ion-footer). Custom modals with raw HTML are fully
|
|
1106
|
-
// developer-controlled and should not be modified.
|
|
1107
|
-
if (!hasContent || hasFooter)
|
|
1108
|
-
return;
|
|
1109
|
-
// Reduce wrapper height by safe-area and add equivalent padding so the
|
|
1110
|
-
// total visual size stays the same but the flex content area shrinks.
|
|
1111
|
-
// Using height + padding instead of box-sizing: border-box avoids
|
|
1112
|
-
// breaking custom modals that set --border-width (border-box would
|
|
1113
|
-
// include the border inside the height, changing the layout).
|
|
1114
|
-
wrapperEl.style.setProperty('height', 'calc(var(--height) - var(--ion-safe-area-bottom, 0px))');
|
|
1115
|
-
wrapperEl.style.setProperty('padding-bottom', 'var(--ion-safe-area-bottom, 0px)');
|
|
1146
|
+
return { contentEl, hasFooter };
|
|
1116
1147
|
}
|
|
1117
1148
|
/**
|
|
1118
|
-
* Clears all safe-area overrides and padding
|
|
1149
|
+
* Clears all safe-area overrides and padding.
|
|
1119
1150
|
*/
|
|
1120
1151
|
cleanupSafeAreaOverrides() {
|
|
1121
1152
|
clearSafeAreaOverrides(this.el);
|
|
1122
1153
|
// Remove internal sheet offset property
|
|
1123
1154
|
this.el.style.removeProperty('--ion-modal-offset-top');
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
this.wrapperEl.style.removeProperty('padding-bottom');
|
|
1127
|
-
}
|
|
1155
|
+
const { contentEl } = this.findContentAndFooter();
|
|
1156
|
+
this.clearContentSafeAreaPadding(contentEl);
|
|
1128
1157
|
}
|
|
1129
1158
|
render() {
|
|
1130
1159
|
const { handle, isSheetModal, presentingElement, htmlAttributes, handleBehavior, inheritedAttributes, focusTrap, expandToScroll, } = this;
|
|
@@ -1133,20 +1162,20 @@ export class Modal {
|
|
|
1133
1162
|
const isCardModal = presentingElement !== undefined && mode === 'ios';
|
|
1134
1163
|
const isHandleCycle = handleBehavior === 'cycle';
|
|
1135
1164
|
const isSheetModalWithHandle = isSheetModal && showHandle;
|
|
1136
|
-
return (h(Host, Object.assign({ key: '
|
|
1165
|
+
return (h(Host, Object.assign({ key: '4bf38aa67df9a3f977163bba5423960bbafd16de', "no-router": true,
|
|
1137
1166
|
// Allow the modal to be navigable when the handle is focusable
|
|
1138
1167
|
tabIndex: isHandleCycle && isSheetModalWithHandle ? 0 : -1 }, htmlAttributes, { style: {
|
|
1139
1168
|
zIndex: `${20000 + this.overlayIndex}`,
|
|
1140
|
-
}, 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: '
|
|
1169
|
+
}, 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: '866da40cc5fc8d3e36637098fb3066a5bc9f4e0f', ref: (el) => (this.backdropEl = el), visible: this.showBackdrop, tappable: this.backdropDismiss, part: "backdrop" }), mode === 'ios' && h("div", { key: '5a2a05514ea8592c8feb0465e504aa7c7af17963', class: "modal-shadow" }), h("div", Object.assign({ key: '4d327115306451f57d190b06ab8cbb6191a6f1d7',
|
|
1141
1170
|
/*
|
|
1142
1171
|
role and aria-modal must be used on the
|
|
1143
1172
|
same element. They must also be set inside the
|
|
1144
1173
|
shadow DOM otherwise ion-button will not be highlighted
|
|
1145
1174
|
when using VoiceOver: https://bugs.webkit.org/show_bug.cgi?id=247134
|
|
1146
1175
|
*/
|
|
1147
|
-
role: "dialog" }, inheritedAttributes, { "aria-modal": "true", class: "modal-wrapper ion-overlay-wrapper", part: "content", ref: (el) => (this.wrapperEl = el) }), showHandle && (h("button", { key: '
|
|
1176
|
+
role: "dialog" }, inheritedAttributes, { "aria-modal": "true", class: "modal-wrapper ion-overlay-wrapper", part: "content", ref: (el) => (this.wrapperEl = el) }), showHandle && (h("button", { key: 'd1882835cc049232c0d957e3ba1e79676a07d179', class: "modal-handle",
|
|
1148
1177
|
// Prevents the handle from receiving keyboard focus when it does not cycle
|
|
1149
|
-
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: '
|
|
1178
|
+
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: '81dc58b09cf7d7022b04cd170f53113604364d5e', onSlotchange: this.onSlotChange }))));
|
|
1150
1179
|
}
|
|
1151
1180
|
static get is() { return "ion-modal"; }
|
|
1152
1181
|
static get encapsulation() { return "shadow"; }
|
|
@@ -15,6 +15,12 @@ import { raf } from "../../utils/helpers";
|
|
|
15
15
|
const MODAL_INSET_MIN_WIDTH = 768;
|
|
16
16
|
const MODAL_INSET_MIN_HEIGHT = 600;
|
|
17
17
|
const EDGE_THRESHOLD = 5;
|
|
18
|
+
/**
|
|
19
|
+
* CSS values for `--width` / `--height` that are treated as fullscreen
|
|
20
|
+
* (modal touches the corresponding screen edges). Empty string means the
|
|
21
|
+
* property was not overridden. See `hasCustomModalDimensions()`.
|
|
22
|
+
*/
|
|
23
|
+
const FULLSCREEN_SIZE_VALUES = new Set(['', '100%', '100vw', '100vh', '100dvw', '100dvh', '100svw', '100svh']);
|
|
18
24
|
/**
|
|
19
25
|
* Cache for resolved root safe-area-top value, invalidated once per frame.
|
|
20
26
|
*/
|
|
@@ -63,6 +69,22 @@ export const getRootSafeAreaTop = () => {
|
|
|
63
69
|
}
|
|
64
70
|
return value;
|
|
65
71
|
};
|
|
72
|
+
/**
|
|
73
|
+
* True when the modal host declares BOTH a non-fullscreen `--width` AND a
|
|
74
|
+
* non-fullscreen `--height` (i.e. a centered-dialog-like modal that doesn't
|
|
75
|
+
* touch any screen edge).
|
|
76
|
+
*
|
|
77
|
+
* The conservative "both axes" check avoids mis-zeroing safe-area for
|
|
78
|
+
* partial-custom modals where the modal still touches top/bottom edges
|
|
79
|
+
* (e.g. only `--width` overridden). Partial cases fall through to the
|
|
80
|
+
* existing position-based post-animation correction.
|
|
81
|
+
*/
|
|
82
|
+
export const hasCustomModalDimensions = (hostEl) => {
|
|
83
|
+
const styles = getComputedStyle(hostEl);
|
|
84
|
+
const width = styles.getPropertyValue('--width').trim();
|
|
85
|
+
const height = styles.getPropertyValue('--height').trim();
|
|
86
|
+
return !FULLSCREEN_SIZE_VALUES.has(width) && !FULLSCREEN_SIZE_VALUES.has(height);
|
|
87
|
+
};
|
|
66
88
|
/**
|
|
67
89
|
* Returns the initial safe-area configuration based on modal type.
|
|
68
90
|
* This is called before animation starts and uses configuration-based prediction.
|
|
@@ -97,8 +119,11 @@ export const getInitialSafeAreaConfig = (context) => {
|
|
|
97
119
|
}
|
|
98
120
|
// On viewports that meet the centered dialog media query breakpoints,
|
|
99
121
|
// regular modals render as centered dialogs (not fullscreen), so they
|
|
100
|
-
// don't touch any screen edges and don't need safe-area insets.
|
|
101
|
-
|
|
122
|
+
// don't touch any screen edges and don't need safe-area insets. Also
|
|
123
|
+
// applies to phone viewports when the modal declares custom --width and
|
|
124
|
+
// --height; these don't touch screen edges either, so the initial
|
|
125
|
+
// prediction must be zero to avoid a post-animation correction flash.
|
|
126
|
+
if (isCenteredDialogViewport() || context.hasCustomDimensions) {
|
|
102
127
|
return {
|
|
103
128
|
top: '0px',
|
|
104
129
|
bottom: '0px',
|
|
@@ -206,6 +206,18 @@ export class RadioGroup {
|
|
|
206
206
|
// to the bottom of the screen
|
|
207
207
|
ev.preventDefault();
|
|
208
208
|
}
|
|
209
|
+
// Inside a select interface, Enter commits the focused radio
|
|
210
|
+
// value (matching native <select>). The !ev.repeat guard stops
|
|
211
|
+
// a held Enter on the triggering ion-select from re-committing
|
|
212
|
+
// once focus lands in the opened popover/modal.
|
|
213
|
+
if (ev.key === 'Enter' && inSelectInterface && !ev.repeat) {
|
|
214
|
+
const previousValue = this.value;
|
|
215
|
+
this.value = current.value;
|
|
216
|
+
if (previousValue !== this.value) {
|
|
217
|
+
this.emitValueChange(ev);
|
|
218
|
+
}
|
|
219
|
+
ev.preventDefault();
|
|
220
|
+
}
|
|
209
221
|
}
|
|
210
222
|
}
|
|
211
223
|
/** @internal */
|
|
@@ -238,7 +250,7 @@ export class RadioGroup {
|
|
|
238
250
|
const { label, labelId, el, name, value } = this;
|
|
239
251
|
const mode = getIonMode(this);
|
|
240
252
|
renderHiddenInput(true, el, name, value, false);
|
|
241
|
-
return (h(Host, { key: '
|
|
253
|
+
return (h(Host, { key: '377e4aa3a656cc84b742f9d7a7d4be65d20c69f5', role: "radiogroup", "aria-labelledby": label ? labelId : null, "aria-describedby": this.hintTextId, "aria-invalid": this.isInvalid ? 'true' : undefined, onClick: this.onClick, class: mode }, this.renderHintText(), h("slot", { key: 'c3187a2497773b4f15cea3b413b036502bcec8c0' })));
|
|
242
254
|
}
|
|
243
255
|
static get is() { return "ion-radio-group"; }
|
|
244
256
|
static get originalStyleUrls() {
|
|
@@ -6,12 +6,12 @@ export class RadioFixture {
|
|
|
6
6
|
constructor(page) {
|
|
7
7
|
this.page = page;
|
|
8
8
|
}
|
|
9
|
-
async checkRadio(method, selector = 'ion-radio') {
|
|
9
|
+
async checkRadio(method, selector = 'ion-radio', key = 'Space') {
|
|
10
10
|
const { page } = this;
|
|
11
11
|
const radio = (this.radio = page.locator(selector));
|
|
12
12
|
if (method === 'keyboard') {
|
|
13
13
|
await radio.focus();
|
|
14
|
-
await page.keyboard.press(
|
|
14
|
+
await page.keyboard.press(key);
|
|
15
15
|
}
|
|
16
16
|
else {
|
|
17
17
|
await radio.click();
|
|
@@ -315,6 +315,7 @@ export class Segment {
|
|
|
315
315
|
return segmentContent === null || segmentContent === void 0 ? void 0 : segmentContent.closest('ion-segment-view');
|
|
316
316
|
}
|
|
317
317
|
handleSegmentViewScroll(ev) {
|
|
318
|
+
var _a;
|
|
318
319
|
const { scrollRatio, isManualScroll } = ev.detail;
|
|
319
320
|
if (!isManualScroll) {
|
|
320
321
|
return;
|
|
@@ -331,6 +332,9 @@ export class Segment {
|
|
|
331
332
|
const index = buttons.findIndex((button) => button.value === this.value);
|
|
332
333
|
const current = buttons[index];
|
|
333
334
|
const nextIndex = Math.round(scrollRatio * (buttons.length - 1));
|
|
335
|
+
if ((_a = buttons[nextIndex]) === null || _a === void 0 ? void 0 : _a.disabled) {
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
334
338
|
if (this.lastNextIndex === undefined || this.lastNextIndex !== nextIndex) {
|
|
335
339
|
this.lastNextIndex = nextIndex;
|
|
336
340
|
this.triggerScrollOnValueChange = false;
|
|
@@ -543,14 +547,14 @@ export class Segment {
|
|
|
543
547
|
}
|
|
544
548
|
render() {
|
|
545
549
|
const mode = getIonMode(this);
|
|
546
|
-
return (h(Host, { key: '
|
|
550
|
+
return (h(Host, { key: 'eda6b7b88b7967b55cf9098c59b655b348a42224', role: "tablist", onClick: this.onClick, class: createColorClasses(this.color, {
|
|
547
551
|
[mode]: true,
|
|
548
552
|
'in-toolbar': hostContext('ion-toolbar', this.el),
|
|
549
553
|
'in-toolbar-color': hostContext('ion-toolbar[color]', this.el),
|
|
550
554
|
'segment-activated': this.activated,
|
|
551
555
|
'segment-disabled': this.disabled,
|
|
552
556
|
'segment-scrollable': this.scrollable,
|
|
553
|
-
}) }, h("slot", { key: '
|
|
557
|
+
}) }, h("slot", { key: 'fdb451f235ce59c5bb50c61a13c69160ece2d5df', onSlotchange: this.onSlottedItemsChange })));
|
|
554
558
|
}
|
|
555
559
|
static get is() { return "ion-segment"; }
|
|
556
560
|
static get encapsulation() { return "shadow"; }
|
|
@@ -7,6 +7,10 @@ import { safeCall } from "../../utils/overlays";
|
|
|
7
7
|
import { getClassMap } from "../../utils/theme";
|
|
8
8
|
export class SelectModal {
|
|
9
9
|
constructor() {
|
|
10
|
+
// Tracks the option that received Enter-keydown so keyup only
|
|
11
|
+
// dismisses when the press started on the same option. Prevents
|
|
12
|
+
// Enter on the triggering ion-select from auto-dismissing.
|
|
13
|
+
this.pendingEnterTarget = null;
|
|
10
14
|
/**
|
|
11
15
|
* The text to display on the cancel button.
|
|
12
16
|
*/
|
|
@@ -56,15 +60,22 @@ export class SelectModal {
|
|
|
56
60
|
return (h("ion-radio-group", { value: checked, onIonChange: (ev) => this.callOptionHandler(ev) }, this.options.map((option) => (h("ion-item", { lines: "none", class: Object.assign({
|
|
57
61
|
// TODO FW-4784
|
|
58
62
|
'item-radio-checked': option.value === checked
|
|
59
|
-
}, getClassMap(option.cssClass)) }, h("ion-radio", { value: option.value, disabled: option.disabled, justify: "start", labelPlacement: "end", onClick: () => this.closeModal(),
|
|
63
|
+
}, getClassMap(option.cssClass)) }, h("ion-radio", { value: option.value, disabled: option.disabled, justify: "start", labelPlacement: "end", onClick: () => this.closeModal(), onKeyDown: (ev) => {
|
|
64
|
+
if (ev.key === 'Enter' && !ev.repeat) {
|
|
65
|
+
this.pendingEnterTarget = ev.currentTarget;
|
|
66
|
+
}
|
|
67
|
+
}, onKeyUp: (ev) => {
|
|
60
68
|
if (ev.key === ' ') {
|
|
61
|
-
|
|
62
|
-
* Selecting a radio option with keyboard navigation,
|
|
63
|
-
* either through the Enter or Space keys, should
|
|
64
|
-
* dismiss the modal.
|
|
65
|
-
*/
|
|
69
|
+
// Space selects and dismisses in one press.
|
|
66
70
|
this.closeModal();
|
|
67
71
|
}
|
|
72
|
+
else if (ev.key === 'Enter') {
|
|
73
|
+
const shouldClose = this.pendingEnterTarget === ev.currentTarget;
|
|
74
|
+
this.pendingEnterTarget = null;
|
|
75
|
+
if (shouldClose) {
|
|
76
|
+
this.closeModal();
|
|
77
|
+
}
|
|
78
|
+
}
|
|
68
79
|
} }, option.text))))));
|
|
69
80
|
}
|
|
70
81
|
renderCheckboxOptions() {
|
|
@@ -79,7 +90,7 @@ export class SelectModal {
|
|
|
79
90
|
} }, option.text))));
|
|
80
91
|
}
|
|
81
92
|
render() {
|
|
82
|
-
return (h(Host, { key: '
|
|
93
|
+
return (h(Host, { key: 'fda0bf6f93cd5ec9f3c64f88a52de849e0e140a2', class: getIonMode(this) }, h("ion-header", { key: '27c0b17175a53db9ff159feeeb96451a3f011dab' }, h("ion-toolbar", { key: '91a4155ebc317fbc9f1bb3e26a7e94754b953c9b' }, this.header !== undefined && h("ion-title", { key: 'f6dae8e4e381f322cc90efefd9bb6ef81d4d2f3e' }, this.header), h("ion-buttons", { key: 'e7760532fb2e7e7385ed6e62097d92d96ff20148', slot: "end" }, h("ion-button", { key: '4999b6fc46cba138186546dca67b7950855e6fb7', onClick: () => this.closeModal() }, this.cancelText)))), h("ion-content", { key: 'c73f80a4bc25b9061ea65cf11e5d811c1a4d8704' }, h("ion-list", { key: 'b21905d15b36ad5eb45845e768918d2763cf48b1' }, this.multiple === true ? this.renderCheckboxOptions() : this.renderRadioOptions()))));
|
|
83
94
|
}
|
|
84
95
|
static get is() { return "ion-select-modal"; }
|
|
85
96
|
static get encapsulation() { return "scoped"; }
|
|
@@ -39,6 +39,10 @@ export class SelectModalPage {
|
|
|
39
39
|
const option = this.getOption(value);
|
|
40
40
|
await option.press('Space');
|
|
41
41
|
}
|
|
42
|
+
async pressEnterOnOption(value) {
|
|
43
|
+
const option = this.getOption(value);
|
|
44
|
+
await option.press('Enter');
|
|
45
|
+
}
|
|
42
46
|
getOption(value) {
|
|
43
47
|
const { multiple, selectModal } = this;
|
|
44
48
|
const selector = multiple ? 'ion-checkbox' : 'ion-radio';
|
|
@@ -10,6 +10,10 @@ import { getIonMode } from "../../global/ionic-global";
|
|
|
10
10
|
*/
|
|
11
11
|
export class SelectPopover {
|
|
12
12
|
constructor() {
|
|
13
|
+
// Tracks the option that received Enter-keydown so keyup only
|
|
14
|
+
// dismisses when the press started on the same option. Prevents
|
|
15
|
+
// Enter on the triggering ion-select from auto-dismissing.
|
|
16
|
+
this.pendingEnterTarget = null;
|
|
13
17
|
/**
|
|
14
18
|
* An array of options for the popover
|
|
15
19
|
*/
|
|
@@ -87,21 +91,28 @@ export class SelectPopover {
|
|
|
87
91
|
return (h("ion-radio-group", { value: checked, onIonChange: (ev) => this.callOptionHandler(ev) }, options.map((option) => (h("ion-item", { class: Object.assign({
|
|
88
92
|
// TODO FW-4784
|
|
89
93
|
'item-radio-checked': option.value === checked
|
|
90
|
-
}, getClassMap(option.cssClass)) }, h("ion-radio", { value: option.value, disabled: option.disabled, onClick: () => this.dismissParentPopover(),
|
|
94
|
+
}, getClassMap(option.cssClass)) }, h("ion-radio", { value: option.value, disabled: option.disabled, onClick: () => this.dismissParentPopover(), onKeyDown: (ev) => {
|
|
95
|
+
if (ev.key === 'Enter' && !ev.repeat) {
|
|
96
|
+
this.pendingEnterTarget = ev.currentTarget;
|
|
97
|
+
}
|
|
98
|
+
}, onKeyUp: (ev) => {
|
|
91
99
|
if (ev.key === ' ') {
|
|
92
|
-
|
|
93
|
-
* Selecting a radio option with keyboard navigation,
|
|
94
|
-
* either through the Enter or Space keys, should
|
|
95
|
-
* dismiss the popover.
|
|
96
|
-
*/
|
|
100
|
+
// Space selects and dismisses in one press.
|
|
97
101
|
this.dismissParentPopover();
|
|
98
102
|
}
|
|
103
|
+
else if (ev.key === 'Enter') {
|
|
104
|
+
const shouldDismiss = this.pendingEnterTarget === ev.currentTarget;
|
|
105
|
+
this.pendingEnterTarget = null;
|
|
106
|
+
if (shouldDismiss) {
|
|
107
|
+
this.dismissParentPopover();
|
|
108
|
+
}
|
|
109
|
+
}
|
|
99
110
|
} }, option.text))))));
|
|
100
111
|
}
|
|
101
112
|
render() {
|
|
102
113
|
const { header, message, options, subHeader } = this;
|
|
103
114
|
const hasSubHeaderOrMessage = subHeader !== undefined || message !== undefined;
|
|
104
|
-
return (h(Host, { key: '
|
|
115
|
+
return (h(Host, { key: 'e7449a1ecfcdbf45a79f8e26a00253c4e146448a', class: getIonMode(this) }, h("ion-list", { key: '52abdfc8668c3429a0dcefef8ddedb6647fdd894' }, header !== undefined && h("ion-list-header", { key: '978e5c03728756feafcc60a0e10e6ec59bf2ae11' }, header), hasSubHeaderOrMessage && (h("ion-item", { key: 'e93c44e7f07a76def16e4b11f0fb4780d84ed402' }, h("ion-label", { key: 'bba1aac43b0bc7f4f00978dd8301985233f3725c', class: "ion-text-wrap" }, subHeader !== undefined && h("h3", { key: 'ad96f6017cf2cc5219540bded2c4f1ca3b532de2' }, subHeader), message !== undefined && h("p", { key: '3fd038921dc40c4d0c29734433984b279ccaeec3' }, message)))), this.renderOptions(options))));
|
|
105
116
|
}
|
|
106
117
|
static get is() { return "ion-select-popover"; }
|
|
107
118
|
static get encapsulation() { return "scoped"; }
|
|
@@ -39,6 +39,10 @@ export class SelectPopoverPage {
|
|
|
39
39
|
const option = this.getOption(value);
|
|
40
40
|
await option.press('Space');
|
|
41
41
|
}
|
|
42
|
+
async pressEnterOnOption(value) {
|
|
43
|
+
const option = this.getOption(value);
|
|
44
|
+
await option.press('Enter');
|
|
45
|
+
}
|
|
42
46
|
getOption(value) {
|
|
43
47
|
const { multiple, selectPopover } = this;
|
|
44
48
|
const selector = multiple ? 'ion-checkbox' : 'ion-radio';
|
package/dist/docs.json
CHANGED
|
@@ -471,7 +471,7 @@ const ActionSheet = class {
|
|
|
471
471
|
if (isRadio) {
|
|
472
472
|
htmlAttrs['aria-checked'] = isActiveRadio ? 'true' : 'false';
|
|
473
473
|
}
|
|
474
|
-
return (h("button", Object.assign({}, htmlAttrs, { role: isRadio ? 'radio' : undefined, type: "button", id: buttonId, class: Object.assign(Object.assign({}, buttonClass(b)), { 'action-sheet-selected': isActiveRadio }), onClick: () => {
|
|
474
|
+
return (h("button", Object.assign({}, htmlAttrs, { role: isRadio ? 'radio' : undefined, type: "button", id: buttonId, class: Object.assign(Object.assign({}, buttonClass(b)), (isRadio && { 'action-sheet-selected': isActiveRadio })), onClick: () => {
|
|
475
475
|
if (isRadio) {
|
|
476
476
|
this.selectRadioButton(b);
|
|
477
477
|
}
|
|
@@ -486,12 +486,12 @@ const ActionSheet = class {
|
|
|
486
486
|
const cancelButton = allButtons.find((b) => b.role === 'cancel');
|
|
487
487
|
const buttons = allButtons.filter((b) => b.role !== 'cancel');
|
|
488
488
|
const headerID = `action-sheet-${overlayIndex}-header`;
|
|
489
|
-
return (h(Host, Object.assign({ key: '
|
|
489
|
+
return (h(Host, Object.assign({ key: '48b63b870f2816b4cad3c606f3d9956854cee79a', role: "dialog", "aria-modal": "true", "aria-labelledby": header !== undefined ? headerID : null, tabindex: "-1" }, htmlAttributes, { style: {
|
|
490
490
|
zIndex: `${20000 + this.overlayIndex}`,
|
|
491
|
-
}, class: Object.assign(Object.assign({ [mode]: true }, getClassMap(this.cssClass)), { 'overlay-hidden': true, 'action-sheet-translucent': this.translucent }), onIonActionSheetWillDismiss: this.dispatchCancelHandler, onIonBackdropTap: this.onBackdropTap }), h("ion-backdrop", { key: '
|
|
491
|
+
}, class: Object.assign(Object.assign({ [mode]: true }, getClassMap(this.cssClass)), { 'overlay-hidden': true, 'action-sheet-translucent': this.translucent }), onIonActionSheetWillDismiss: this.dispatchCancelHandler, onIonBackdropTap: this.onBackdropTap }), h("ion-backdrop", { key: '41dd5781f139d26b3feea33ab387451aeafacd51', tappable: this.backdropDismiss }), h("div", { key: 'f797c2657782e4e83adf90d2d796108e857a1fc0', tabindex: "0", "aria-hidden": "true" }), h("div", { key: '8e74209321fc5e8712e3e293c91a6fa036ea45ab', class: "action-sheet-wrapper ion-overlay-wrapper", ref: (el) => (this.wrapperEl = el) }, h("div", { key: 'c811860be2eed0b6c73fc2cc5a59cb94db2e8912', class: "action-sheet-container" }, h("div", { key: 'd36ee14b70d73eb5cd69e0c57bc5cc31daf86ab3', class: "action-sheet-group", ref: (el) => (this.groupEl = el), role: hasRadioButtons ? 'radiogroup' : undefined }, header !== undefined && (h("div", { key: '922695de191edc86451eed2552acc4f54993ea52', id: headerID, class: {
|
|
492
492
|
'action-sheet-title': true,
|
|
493
493
|
'action-sheet-has-sub-title': this.subHeader !== undefined,
|
|
494
|
-
} }, header, this.subHeader && h("div", { key: '
|
|
494
|
+
} }, header, this.subHeader && h("div", { key: 'a119d4a93668e829f7106f11cbbdd437310f3e80', class: "action-sheet-sub-title" }, this.subHeader))), this.renderActionSheetButtons(buttons)), cancelButton && (h("div", { key: '1d27f36d09bedd2e4c61fea9dd9d05f8f9271aef', class: "action-sheet-group action-sheet-group-cancel" }, h("button", Object.assign({ key: 'ed13658176c54cc45808a54a0331c297430d9bc6' }, cancelButton.htmlAttributes, { type: "button", class: buttonClass(cancelButton), onClick: () => this.buttonClick(cancelButton) }), h("span", { key: '5208e3e3535b775a443cef6fb1decd790db69b0c', class: "action-sheet-button-inner" }, cancelButton.icon && (h("ion-icon", { key: 'b7ba489e1ee50524a5b4af670eef787844a16286', icon: cancelButton.icon, "aria-hidden": "true", lazy: false, class: "action-sheet-icon" })), cancelButton.text), mode === 'md' && h("ion-ripple-effect", { key: 'b3f88898114855853259f0001def26b4bd6e4a98' })))))), h("div", { key: '0dfd0fcdc633bf565990eaa06679608e749cf8f9', tabindex: "0", "aria-hidden": "true" })));
|
|
495
495
|
}
|
|
496
496
|
get el() { return getElement(this); }
|
|
497
497
|
static get watchers() { return {
|
|
@@ -157,7 +157,7 @@ Buttons.style = {
|
|
|
157
157
|
md: buttonsMdCss()
|
|
158
158
|
};
|
|
159
159
|
|
|
160
|
-
const contentCss = () => `:host{--background:var(--ion-background-color, #fff);--color:var(--ion-text-color, #000);--padding-top:0px;--padding-bottom:0px;--padding-start:0px;--padding-end:0px;--keyboard-offset:0px;--offset-top:0px;--offset-bottom:0px;--overflow:auto;display:block;position:relative;-ms-flex:1;flex:1;width:100%;height:100%;margin:0 !important;padding:0 !important;font-family:var(--ion-font-family, inherit);contain:size style}:host(.ion-color) .inner-scroll{background:var(--ion-color-base);color:var(--ion-color-contrast)}#background-content{left:0px;right:0px;top:calc(var(--offset-top) * -1);bottom:calc(var(--offset-bottom) * -1);position:absolute;background:var(--background)}.inner-scroll{left:0px;right:0px;top:calc(var(--offset-top) * -1);bottom:calc(var(--offset-bottom) * -1);-webkit-padding-start:var(--padding-start);padding-inline-start:var(--padding-start);-webkit-padding-end:var(--padding-end);padding-inline-end:var(--padding-end);padding-top:calc(var(--padding-top) + var(--offset-top));padding-bottom:calc(var(--padding-bottom) + var(--keyboard-offset) + var(--offset-bottom));position:absolute;color:var(--color);-webkit-box-sizing:border-box;box-sizing:border-box;overflow:hidden;-ms-touch-action:pan-x pan-y pinch-zoom;touch-action:pan-x pan-y pinch-zoom}.scroll-y,.scroll-x{-webkit-overflow-scrolling:touch;z-index:0;will-change:scroll-position}.scroll-y{overflow-y:var(--overflow);overscroll-behavior-y:contain}.scroll-x{overflow-x:var(--overflow);overscroll-behavior-x:contain}.overscroll::before,.overscroll::after{position:absolute;width:1px;height:1px;content:""}.overscroll::before{bottom:-1px}.overscroll::after{top:-1px}:host(.content-sizing){display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;min-height:0;contain:none}:host(.content-sizing) .inner-scroll{position:relative;top:0;bottom:0;margin-top:calc(var(--offset-top) * -1);margin-bottom:calc(var(--offset-bottom) * -1)}.transition-effect{display:none;position:absolute;width:100%;height:100vh;opacity:0;pointer-events:none}:host(.content-ltr) .transition-effect{left:-100%;}:host(.content-rtl) .transition-effect{right:-100%;}.transition-cover{position:absolute;right:0;width:100%;height:100%;background:black;opacity:0.1}.transition-shadow{display:block;position:absolute;width:100%;height:100%;-webkit-box-shadow:inset -9px 0 9px 0 rgba(0, 0, 100, 0.03);box-shadow:inset -9px 0 9px 0 rgba(0, 0, 100, 0.03)}:host(.content-ltr) .transition-shadow{right:0;}:host(.content-rtl) .transition-shadow{left:0;-webkit-transform:scaleX(-1);transform:scaleX(-1)}::slotted([slot=fixed]){position:absolute;-webkit-transform:translateZ(0);transform:translateZ(0)}`;
|
|
160
|
+
const contentCss = () => `:host{--background:var(--ion-background-color, #fff);--color:var(--ion-text-color, #000);--padding-top:0px;--padding-bottom:0px;--padding-start:0px;--padding-end:0px;--keyboard-offset:0px;--offset-top:0px;--offset-bottom:0px;--overflow:auto;display:block;position:relative;-ms-flex:1;flex:1;width:100%;height:100%;margin:0 !important;padding:0 !important;font-family:var(--ion-font-family, inherit);contain:size style}:host(.ion-color) .inner-scroll{background:var(--ion-color-base);color:var(--ion-color-contrast)}#background-content{left:0px;right:0px;top:calc(var(--offset-top) * -1);bottom:calc(var(--offset-bottom) * -1);position:absolute;background:var(--background)}.inner-scroll{left:0px;right:0px;top:calc(var(--offset-top) * -1);bottom:calc(var(--offset-bottom) * -1);-webkit-padding-start:var(--padding-start);padding-inline-start:var(--padding-start);-webkit-padding-end:var(--padding-end);padding-inline-end:var(--padding-end);padding-top:calc(var(--padding-top) + var(--offset-top));padding-bottom:calc(var(--padding-bottom) + var(--keyboard-offset) + var(--offset-bottom) + var(--ion-content-safe-area-padding-bottom, 0px));position:absolute;color:var(--color);-webkit-box-sizing:border-box;box-sizing:border-box;overflow:hidden;-ms-touch-action:pan-x pan-y pinch-zoom;touch-action:pan-x pan-y pinch-zoom}.scroll-y,.scroll-x{-webkit-overflow-scrolling:touch;z-index:0;will-change:scroll-position}.scroll-y{overflow-y:var(--overflow);overscroll-behavior-y:contain}.scroll-x{overflow-x:var(--overflow);overscroll-behavior-x:contain}.overscroll::before,.overscroll::after{position:absolute;width:1px;height:1px;content:""}.overscroll::before{bottom:-1px}.overscroll::after{top:-1px}:host(.content-sizing){display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;min-height:0;contain:none}:host(.content-sizing) .inner-scroll{position:relative;top:0;bottom:0;margin-top:calc(var(--offset-top) * -1);margin-bottom:calc(var(--offset-bottom) * -1)}.transition-effect{display:none;position:absolute;width:100%;height:100vh;opacity:0;pointer-events:none}:host(.content-ltr) .transition-effect{left:-100%;}:host(.content-rtl) .transition-effect{right:-100%;}.transition-cover{position:absolute;right:0;width:100%;height:100%;background:black;opacity:0.1}.transition-shadow{display:block;position:absolute;width:100%;height:100%;-webkit-box-shadow:inset -9px 0 9px 0 rgba(0, 0, 100, 0.03);box-shadow:inset -9px 0 9px 0 rgba(0, 0, 100, 0.03)}:host(.content-ltr) .transition-shadow{right:0;}:host(.content-rtl) .transition-shadow{left:0;-webkit-transform:scaleX(-1);transform:scaleX(-1)}::slotted([slot=fixed]){position:absolute;-webkit-transform:translateZ(0);transform:translateZ(0)}`;
|
|
161
161
|
|
|
162
162
|
const Content = class {
|
|
163
163
|
constructor(hostRef) {
|
|
@@ -244,6 +244,12 @@ const Datetime = class {
|
|
|
244
244
|
this.ionStyle = createEvent(this, "ionStyle", 7);
|
|
245
245
|
this.ionRender = createEvent(this, "ionRender", 7);
|
|
246
246
|
this.inputId = `ion-dt-${datetimeIds++}`;
|
|
247
|
+
/**
|
|
248
|
+
* Set true only by `visibleCallback`. Lets `hiddenCallback` ignore the
|
|
249
|
+
* synthetic "not intersecting" entry IntersectionObserver fires on
|
|
250
|
+
* `observe()` when the host mounts offscreen.
|
|
251
|
+
*/
|
|
252
|
+
this.hasBeenIntersecting = false;
|
|
247
253
|
this.prevPresentation = null;
|
|
248
254
|
this.showMonthAndYear = false;
|
|
249
255
|
this.activeParts = [];
|
|
@@ -806,7 +812,21 @@ const Datetime = class {
|
|
|
806
812
|
if (rect.width === 0 || rect.height === 0) {
|
|
807
813
|
return;
|
|
808
814
|
}
|
|
815
|
+
this.markReady();
|
|
816
|
+
};
|
|
817
|
+
this.markReady = () => {
|
|
818
|
+
if (this.el.classList.contains('datetime-ready')) {
|
|
819
|
+
return;
|
|
820
|
+
}
|
|
809
821
|
this.initializeListeners();
|
|
822
|
+
/**
|
|
823
|
+
* TODO FW-2793: Datetime needs a frame to ensure that it
|
|
824
|
+
* can properly scroll contents into view. As a result
|
|
825
|
+
* we hide the scrollable content until after that frame
|
|
826
|
+
* so users do not see the content quickly shifting. The downside
|
|
827
|
+
* is that the content will pop into view a frame after. Maybe there
|
|
828
|
+
* is a better way to handle this?
|
|
829
|
+
*/
|
|
810
830
|
writeTask(() => {
|
|
811
831
|
this.el.classList.add('datetime-ready');
|
|
812
832
|
});
|
|
@@ -1099,6 +1119,7 @@ const Datetime = class {
|
|
|
1099
1119
|
this.clearFocusVisible = undefined;
|
|
1100
1120
|
}
|
|
1101
1121
|
this.loadTimeoutCleanup();
|
|
1122
|
+
this.hasBeenIntersecting = false;
|
|
1102
1123
|
}
|
|
1103
1124
|
initializeListeners() {
|
|
1104
1125
|
this.initializeCalendarListener();
|
|
@@ -1118,18 +1139,8 @@ const Datetime = class {
|
|
|
1118
1139
|
if (!ev.isIntersecting) {
|
|
1119
1140
|
return;
|
|
1120
1141
|
}
|
|
1121
|
-
this.
|
|
1122
|
-
|
|
1123
|
-
* TODO FW-2793: Datetime needs a frame to ensure that it
|
|
1124
|
-
* can properly scroll contents into view. As a result
|
|
1125
|
-
* we hide the scrollable content until after that frame
|
|
1126
|
-
* so users do not see the content quickly shifting. The downside
|
|
1127
|
-
* is that the content will pop into view a frame after. Maybe there
|
|
1128
|
-
* is a better way to handle this?
|
|
1129
|
-
*/
|
|
1130
|
-
writeTask(() => {
|
|
1131
|
-
this.el.classList.add('datetime-ready');
|
|
1132
|
-
});
|
|
1142
|
+
this.hasBeenIntersecting = true;
|
|
1143
|
+
this.markReady();
|
|
1133
1144
|
};
|
|
1134
1145
|
const visibleIO = new IntersectionObserver(visibleCallback, { threshold: 0.01, root: el });
|
|
1135
1146
|
/**
|
|
@@ -1165,6 +1176,11 @@ const Datetime = class {
|
|
|
1165
1176
|
if (ev.isIntersecting) {
|
|
1166
1177
|
return;
|
|
1167
1178
|
}
|
|
1179
|
+
// Ignore the initial "not intersecting" entry IntersectionObserver fires on observe().
|
|
1180
|
+
if (!this.hasBeenIntersecting) {
|
|
1181
|
+
return;
|
|
1182
|
+
}
|
|
1183
|
+
this.hasBeenIntersecting = false;
|
|
1168
1184
|
this.destroyInteractionListeners();
|
|
1169
1185
|
/**
|
|
1170
1186
|
* When datetime is hidden, we need to make sure that
|
|
@@ -1908,7 +1924,7 @@ const Datetime = class {
|
|
|
1908
1924
|
const hasDatePresentation = presentation === 'date' || presentation === 'date-time' || presentation === 'time-date';
|
|
1909
1925
|
const hasWheelVariant = hasDatePresentation && preferWheel;
|
|
1910
1926
|
renderHiddenInput(true, el, name, formatValue(value), disabled);
|
|
1911
|
-
return (h(Host, { key: '
|
|
1927
|
+
return (h(Host, { key: '323c8c2327088f00934b8c93c3306538cb9b5677', "aria-disabled": disabled ? 'true' : null, onFocus: this.onFocus, onBlur: this.onBlur, class: Object.assign({}, createColorClasses(color, {
|
|
1912
1928
|
[mode]: true,
|
|
1913
1929
|
['datetime-readonly']: readonly,
|
|
1914
1930
|
['datetime-disabled']: disabled,
|
|
@@ -1918,7 +1934,7 @@ const Datetime = class {
|
|
|
1918
1934
|
[`datetime-size-${size}`]: true,
|
|
1919
1935
|
[`datetime-prefer-wheel`]: hasWheelVariant,
|
|
1920
1936
|
[`datetime-grid`]: isGridStyle,
|
|
1921
|
-
})) }, h("div", { key: '
|
|
1937
|
+
})) }, h("div", { key: '1e0855c8909bc3f1e48a21ad68159fa782060691', class: "intersection-tracker", ref: (el) => (this.intersectionTrackerRef = el) }), this.renderDatetime(mode)));
|
|
1922
1938
|
}
|
|
1923
1939
|
get el() { return getElement(this); }
|
|
1924
1940
|
static get watchers() { return {
|