@ionic/core 8.7.13-dev.11765552290.11441928 → 8.7.13-dev.11765560568.1a8772e8

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.
@@ -1071,78 +1071,103 @@ const Datetime = /*@__PURE__*/ proxyCustomElement(class Datetime extends HTMLEle
1071
1071
  this.resizeObserver.disconnect();
1072
1072
  this.resizeObserver = undefined;
1073
1073
  }
1074
+ if (this.destroyOverlayListeners) {
1075
+ this.destroyOverlayListeners();
1076
+ this.destroyOverlayListeners = undefined;
1077
+ }
1074
1078
  }
1075
1079
  initializeListeners() {
1076
1080
  this.initializeCalendarListener();
1077
1081
  this.initializeKeyboardListeners();
1078
1082
  }
1079
- componentDidLoad() {
1083
+ /**
1084
+ * Sets up visibility detection for the datetime component.
1085
+ *
1086
+ * Uses multiple strategies to reliably detect when the datetime becomes
1087
+ * visible, which is necessary for proper initialization of scrollable areas:
1088
+ * 1. ResizeObserver - detects dimension changes
1089
+ * 2. Overlay event listeners - for datetime inside modals/popovers
1090
+ * 3. Polling fallback - for browsers where observers are unreliable (WebKit)
1091
+ */
1092
+ initializeVisibilityObserver() {
1080
1093
  const { el } = this;
1094
+ const markReady = () => {
1095
+ if (el.classList.contains('datetime-ready')) {
1096
+ return;
1097
+ }
1098
+ this.initializeListeners();
1099
+ writeTask(() => {
1100
+ el.classList.add('datetime-ready');
1101
+ });
1102
+ };
1103
+ const markHidden = () => {
1104
+ this.destroyInteractionListeners();
1105
+ this.showMonthAndYear = false;
1106
+ writeTask(() => {
1107
+ el.classList.remove('datetime-ready');
1108
+ });
1109
+ startVisibilityPolling();
1110
+ };
1081
1111
  /**
1082
- * If a scrollable element is hidden using `display: none`,
1083
- * it will not have a scroll height meaning we cannot scroll elements
1084
- * into view. As a result, we will need to wait for the datetime to become
1085
- * visible if used inside of a modal or a popover otherwise the scrollable
1086
- * areas will not have the correct values snapped into place.
1087
- *
1088
- * FW-6931: We use ResizeObserver to detect when the element transitions
1089
- * between having dimensions (visible) and zero dimensions (hidden). This
1090
- * is more reliable than IntersectionObserver for detecting visibility
1091
- * changes, especially when the element is inside a modal or popover.
1112
+ * FW-6931: Poll for visibility as a fallback for browsers where
1113
+ * ResizeObserver doesn't fire reliably (e.g., WebKit).
1114
+ */
1115
+ const startVisibilityPolling = () => {
1116
+ let pollCount = 0;
1117
+ const poll = () => {
1118
+ if (el.classList.contains('datetime-ready') || pollCount++ >= 60) {
1119
+ return;
1120
+ }
1121
+ const { width, height } = el.getBoundingClientRect();
1122
+ if (width > 0 && height > 0) {
1123
+ markReady();
1124
+ }
1125
+ else {
1126
+ raf(poll);
1127
+ }
1128
+ };
1129
+ raf(poll);
1130
+ };
1131
+ /**
1132
+ * FW-6931: Listen for overlay present/dismiss events when datetime
1133
+ * is inside a modal or popover.
1092
1134
  */
1135
+ const parentOverlay = el.closest('ion-modal, ion-popover');
1136
+ if (parentOverlay) {
1137
+ const handlePresent = () => markReady();
1138
+ const handleDismiss = () => markHidden();
1139
+ parentOverlay.addEventListener('didPresent', handlePresent);
1140
+ parentOverlay.addEventListener('didDismiss', handleDismiss);
1141
+ this.destroyOverlayListeners = () => {
1142
+ parentOverlay.removeEventListener('didPresent', handlePresent);
1143
+ parentOverlay.removeEventListener('didDismiss', handleDismiss);
1144
+ };
1145
+ }
1093
1146
  if (typeof ResizeObserver !== 'undefined') {
1094
1147
  this.resizeObserver = new ResizeObserver((entries) => {
1095
- const entry = entries[0];
1096
- const { width, height } = entry.contentRect;
1148
+ const { width, height } = entries[0].contentRect;
1097
1149
  const isVisible = width > 0 && height > 0;
1098
1150
  const isReady = el.classList.contains('datetime-ready');
1099
1151
  if (isVisible && !isReady) {
1100
- this.initializeListeners();
1101
- /**
1102
- * TODO FW-2793: Datetime needs a frame to ensure that it
1103
- * can properly scroll contents into view. As a result
1104
- * we hide the scrollable content until after that frame
1105
- * so users do not see the content quickly shifting. The downside
1106
- * is that the content will pop into view a frame after. Maybe there
1107
- * is a better way to handle this?
1108
- */
1109
- writeTask(() => {
1110
- el.classList.add('datetime-ready');
1111
- });
1152
+ markReady();
1112
1153
  }
1113
1154
  else if (!isVisible && isReady) {
1114
- /**
1115
- * Clean up listeners when hidden so we can properly
1116
- * reinitialize scroll positions on re-presentation.
1117
- */
1118
- this.destroyInteractionListeners();
1119
- /**
1120
- * Close month/year picker when hidden, otherwise
1121
- * it will be open when re-presented with a 0-height
1122
- * scroll area, showing the wrong month.
1123
- */
1124
- this.showMonthAndYear = false;
1125
- writeTask(() => {
1126
- el.classList.remove('datetime-ready');
1127
- });
1155
+ markHidden();
1128
1156
  }
1129
1157
  });
1130
- /**
1131
- * Use raf to avoid a race condition between the component loading and
1132
- * its display animation starting (such as when shown in a modal).
1133
- */
1158
+ // Use raf to avoid race condition with modal/popover animations
1134
1159
  raf(() => { var _a; return (_a = this.resizeObserver) === null || _a === void 0 ? void 0 : _a.observe(el); });
1160
+ startVisibilityPolling();
1135
1161
  }
1136
1162
  else {
1137
- /**
1138
- * Fallback for test environments where ResizeObserver is not available.
1139
- * Just mark as ready without initializing scroll/keyboard listeners
1140
- * since those also require browser APIs not available in Jest.
1141
- */
1163
+ // Test environment fallback - mark ready immediately
1142
1164
  writeTask(() => {
1143
1165
  el.classList.add('datetime-ready');
1144
1166
  });
1145
1167
  }
1168
+ }
1169
+ componentDidLoad() {
1170
+ this.initializeVisibilityObserver();
1146
1171
  /**
1147
1172
  * Datetime uses Ionic components that emit
1148
1173
  * ionFocus and ionBlur. These events are
@@ -1871,7 +1896,7 @@ const Datetime = /*@__PURE__*/ proxyCustomElement(class Datetime extends HTMLEle
1871
1896
  const hasDatePresentation = presentation === 'date' || presentation === 'date-time' || presentation === 'time-date';
1872
1897
  const hasWheelVariant = hasDatePresentation && preferWheel;
1873
1898
  renderHiddenInput(true, el, name, formatValue(value), disabled);
1874
- return (h(Host, { key: '187a5b5cf1413449a9db33f8c5b0fd3b7fded302', "aria-disabled": disabled ? 'true' : null, onFocus: this.onFocus, onBlur: this.onBlur, class: Object.assign({}, createColorClasses(color, {
1899
+ return (h(Host, { key: '2fb2938db507a134622a3feac44804f11071c589', "aria-disabled": disabled ? 'true' : null, onFocus: this.onFocus, onBlur: this.onBlur, class: Object.assign({}, createColorClasses(color, {
1875
1900
  [mode]: true,
1876
1901
  ['datetime-readonly']: readonly,
1877
1902
  ['datetime-disabled']: disabled,
@@ -1067,78 +1067,103 @@ const Datetime = class {
1067
1067
  this.resizeObserver.disconnect();
1068
1068
  this.resizeObserver = undefined;
1069
1069
  }
1070
+ if (this.destroyOverlayListeners) {
1071
+ this.destroyOverlayListeners();
1072
+ this.destroyOverlayListeners = undefined;
1073
+ }
1070
1074
  }
1071
1075
  initializeListeners() {
1072
1076
  this.initializeCalendarListener();
1073
1077
  this.initializeKeyboardListeners();
1074
1078
  }
1075
- componentDidLoad() {
1079
+ /**
1080
+ * Sets up visibility detection for the datetime component.
1081
+ *
1082
+ * Uses multiple strategies to reliably detect when the datetime becomes
1083
+ * visible, which is necessary for proper initialization of scrollable areas:
1084
+ * 1. ResizeObserver - detects dimension changes
1085
+ * 2. Overlay event listeners - for datetime inside modals/popovers
1086
+ * 3. Polling fallback - for browsers where observers are unreliable (WebKit)
1087
+ */
1088
+ initializeVisibilityObserver() {
1076
1089
  const { el } = this;
1090
+ const markReady = () => {
1091
+ if (el.classList.contains('datetime-ready')) {
1092
+ return;
1093
+ }
1094
+ this.initializeListeners();
1095
+ index.writeTask(() => {
1096
+ el.classList.add('datetime-ready');
1097
+ });
1098
+ };
1099
+ const markHidden = () => {
1100
+ this.destroyInteractionListeners();
1101
+ this.showMonthAndYear = false;
1102
+ index.writeTask(() => {
1103
+ el.classList.remove('datetime-ready');
1104
+ });
1105
+ startVisibilityPolling();
1106
+ };
1077
1107
  /**
1078
- * If a scrollable element is hidden using `display: none`,
1079
- * it will not have a scroll height meaning we cannot scroll elements
1080
- * into view. As a result, we will need to wait for the datetime to become
1081
- * visible if used inside of a modal or a popover otherwise the scrollable
1082
- * areas will not have the correct values snapped into place.
1083
- *
1084
- * FW-6931: We use ResizeObserver to detect when the element transitions
1085
- * between having dimensions (visible) and zero dimensions (hidden). This
1086
- * is more reliable than IntersectionObserver for detecting visibility
1087
- * changes, especially when the element is inside a modal or popover.
1108
+ * FW-6931: Poll for visibility as a fallback for browsers where
1109
+ * ResizeObserver doesn't fire reliably (e.g., WebKit).
1110
+ */
1111
+ const startVisibilityPolling = () => {
1112
+ let pollCount = 0;
1113
+ const poll = () => {
1114
+ if (el.classList.contains('datetime-ready') || pollCount++ >= 60) {
1115
+ return;
1116
+ }
1117
+ const { width, height } = el.getBoundingClientRect();
1118
+ if (width > 0 && height > 0) {
1119
+ markReady();
1120
+ }
1121
+ else {
1122
+ helpers.raf(poll);
1123
+ }
1124
+ };
1125
+ helpers.raf(poll);
1126
+ };
1127
+ /**
1128
+ * FW-6931: Listen for overlay present/dismiss events when datetime
1129
+ * is inside a modal or popover.
1088
1130
  */
1131
+ const parentOverlay = el.closest('ion-modal, ion-popover');
1132
+ if (parentOverlay) {
1133
+ const handlePresent = () => markReady();
1134
+ const handleDismiss = () => markHidden();
1135
+ parentOverlay.addEventListener('didPresent', handlePresent);
1136
+ parentOverlay.addEventListener('didDismiss', handleDismiss);
1137
+ this.destroyOverlayListeners = () => {
1138
+ parentOverlay.removeEventListener('didPresent', handlePresent);
1139
+ parentOverlay.removeEventListener('didDismiss', handleDismiss);
1140
+ };
1141
+ }
1089
1142
  if (typeof ResizeObserver !== 'undefined') {
1090
1143
  this.resizeObserver = new ResizeObserver((entries) => {
1091
- const entry = entries[0];
1092
- const { width, height } = entry.contentRect;
1144
+ const { width, height } = entries[0].contentRect;
1093
1145
  const isVisible = width > 0 && height > 0;
1094
1146
  const isReady = el.classList.contains('datetime-ready');
1095
1147
  if (isVisible && !isReady) {
1096
- this.initializeListeners();
1097
- /**
1098
- * TODO FW-2793: Datetime needs a frame to ensure that it
1099
- * can properly scroll contents into view. As a result
1100
- * we hide the scrollable content until after that frame
1101
- * so users do not see the content quickly shifting. The downside
1102
- * is that the content will pop into view a frame after. Maybe there
1103
- * is a better way to handle this?
1104
- */
1105
- index.writeTask(() => {
1106
- el.classList.add('datetime-ready');
1107
- });
1148
+ markReady();
1108
1149
  }
1109
1150
  else if (!isVisible && isReady) {
1110
- /**
1111
- * Clean up listeners when hidden so we can properly
1112
- * reinitialize scroll positions on re-presentation.
1113
- */
1114
- this.destroyInteractionListeners();
1115
- /**
1116
- * Close month/year picker when hidden, otherwise
1117
- * it will be open when re-presented with a 0-height
1118
- * scroll area, showing the wrong month.
1119
- */
1120
- this.showMonthAndYear = false;
1121
- index.writeTask(() => {
1122
- el.classList.remove('datetime-ready');
1123
- });
1151
+ markHidden();
1124
1152
  }
1125
1153
  });
1126
- /**
1127
- * Use raf to avoid a race condition between the component loading and
1128
- * its display animation starting (such as when shown in a modal).
1129
- */
1154
+ // Use raf to avoid race condition with modal/popover animations
1130
1155
  helpers.raf(() => { var _a; return (_a = this.resizeObserver) === null || _a === void 0 ? void 0 : _a.observe(el); });
1156
+ startVisibilityPolling();
1131
1157
  }
1132
1158
  else {
1133
- /**
1134
- * Fallback for test environments where ResizeObserver is not available.
1135
- * Just mark as ready without initializing scroll/keyboard listeners
1136
- * since those also require browser APIs not available in Jest.
1137
- */
1159
+ // Test environment fallback - mark ready immediately
1138
1160
  index.writeTask(() => {
1139
1161
  el.classList.add('datetime-ready');
1140
1162
  });
1141
1163
  }
1164
+ }
1165
+ componentDidLoad() {
1166
+ this.initializeVisibilityObserver();
1142
1167
  /**
1143
1168
  * Datetime uses Ionic components that emit
1144
1169
  * ionFocus and ionBlur. These events are
@@ -1867,7 +1892,7 @@ const Datetime = class {
1867
1892
  const hasDatePresentation = presentation === 'date' || presentation === 'date-time' || presentation === 'time-date';
1868
1893
  const hasWheelVariant = hasDatePresentation && preferWheel;
1869
1894
  helpers.renderHiddenInput(true, el, name, data.formatValue(value), disabled);
1870
- return (index.h(index.Host, { key: '187a5b5cf1413449a9db33f8c5b0fd3b7fded302', "aria-disabled": disabled ? 'true' : null, onFocus: this.onFocus, onBlur: this.onBlur, class: Object.assign({}, theme.createColorClasses(color, {
1895
+ return (index.h(index.Host, { key: '2fb2938db507a134622a3feac44804f11071c589', "aria-disabled": disabled ? 'true' : null, onFocus: this.onFocus, onBlur: this.onBlur, class: Object.assign({}, theme.createColorClasses(color, {
1871
1896
  [mode]: true,
1872
1897
  ['datetime-readonly']: readonly,
1873
1898
  ['datetime-disabled']: disabled,
@@ -866,78 +866,103 @@ export class Datetime {
866
866
  this.resizeObserver.disconnect();
867
867
  this.resizeObserver = undefined;
868
868
  }
869
+ if (this.destroyOverlayListeners) {
870
+ this.destroyOverlayListeners();
871
+ this.destroyOverlayListeners = undefined;
872
+ }
869
873
  }
870
874
  initializeListeners() {
871
875
  this.initializeCalendarListener();
872
876
  this.initializeKeyboardListeners();
873
877
  }
874
- componentDidLoad() {
878
+ /**
879
+ * Sets up visibility detection for the datetime component.
880
+ *
881
+ * Uses multiple strategies to reliably detect when the datetime becomes
882
+ * visible, which is necessary for proper initialization of scrollable areas:
883
+ * 1. ResizeObserver - detects dimension changes
884
+ * 2. Overlay event listeners - for datetime inside modals/popovers
885
+ * 3. Polling fallback - for browsers where observers are unreliable (WebKit)
886
+ */
887
+ initializeVisibilityObserver() {
875
888
  const { el } = this;
889
+ const markReady = () => {
890
+ if (el.classList.contains('datetime-ready')) {
891
+ return;
892
+ }
893
+ this.initializeListeners();
894
+ writeTask(() => {
895
+ el.classList.add('datetime-ready');
896
+ });
897
+ };
898
+ const markHidden = () => {
899
+ this.destroyInteractionListeners();
900
+ this.showMonthAndYear = false;
901
+ writeTask(() => {
902
+ el.classList.remove('datetime-ready');
903
+ });
904
+ startVisibilityPolling();
905
+ };
876
906
  /**
877
- * If a scrollable element is hidden using `display: none`,
878
- * it will not have a scroll height meaning we cannot scroll elements
879
- * into view. As a result, we will need to wait for the datetime to become
880
- * visible if used inside of a modal or a popover otherwise the scrollable
881
- * areas will not have the correct values snapped into place.
882
- *
883
- * FW-6931: We use ResizeObserver to detect when the element transitions
884
- * between having dimensions (visible) and zero dimensions (hidden). This
885
- * is more reliable than IntersectionObserver for detecting visibility
886
- * changes, especially when the element is inside a modal or popover.
907
+ * FW-6931: Poll for visibility as a fallback for browsers where
908
+ * ResizeObserver doesn't fire reliably (e.g., WebKit).
909
+ */
910
+ const startVisibilityPolling = () => {
911
+ let pollCount = 0;
912
+ const poll = () => {
913
+ if (el.classList.contains('datetime-ready') || pollCount++ >= 60) {
914
+ return;
915
+ }
916
+ const { width, height } = el.getBoundingClientRect();
917
+ if (width > 0 && height > 0) {
918
+ markReady();
919
+ }
920
+ else {
921
+ raf(poll);
922
+ }
923
+ };
924
+ raf(poll);
925
+ };
926
+ /**
927
+ * FW-6931: Listen for overlay present/dismiss events when datetime
928
+ * is inside a modal or popover.
887
929
  */
930
+ const parentOverlay = el.closest('ion-modal, ion-popover');
931
+ if (parentOverlay) {
932
+ const handlePresent = () => markReady();
933
+ const handleDismiss = () => markHidden();
934
+ parentOverlay.addEventListener('didPresent', handlePresent);
935
+ parentOverlay.addEventListener('didDismiss', handleDismiss);
936
+ this.destroyOverlayListeners = () => {
937
+ parentOverlay.removeEventListener('didPresent', handlePresent);
938
+ parentOverlay.removeEventListener('didDismiss', handleDismiss);
939
+ };
940
+ }
888
941
  if (typeof ResizeObserver !== 'undefined') {
889
942
  this.resizeObserver = new ResizeObserver((entries) => {
890
- const entry = entries[0];
891
- const { width, height } = entry.contentRect;
943
+ const { width, height } = entries[0].contentRect;
892
944
  const isVisible = width > 0 && height > 0;
893
945
  const isReady = el.classList.contains('datetime-ready');
894
946
  if (isVisible && !isReady) {
895
- this.initializeListeners();
896
- /**
897
- * TODO FW-2793: Datetime needs a frame to ensure that it
898
- * can properly scroll contents into view. As a result
899
- * we hide the scrollable content until after that frame
900
- * so users do not see the content quickly shifting. The downside
901
- * is that the content will pop into view a frame after. Maybe there
902
- * is a better way to handle this?
903
- */
904
- writeTask(() => {
905
- el.classList.add('datetime-ready');
906
- });
947
+ markReady();
907
948
  }
908
949
  else if (!isVisible && isReady) {
909
- /**
910
- * Clean up listeners when hidden so we can properly
911
- * reinitialize scroll positions on re-presentation.
912
- */
913
- this.destroyInteractionListeners();
914
- /**
915
- * Close month/year picker when hidden, otherwise
916
- * it will be open when re-presented with a 0-height
917
- * scroll area, showing the wrong month.
918
- */
919
- this.showMonthAndYear = false;
920
- writeTask(() => {
921
- el.classList.remove('datetime-ready');
922
- });
950
+ markHidden();
923
951
  }
924
952
  });
925
- /**
926
- * Use raf to avoid a race condition between the component loading and
927
- * its display animation starting (such as when shown in a modal).
928
- */
953
+ // Use raf to avoid race condition with modal/popover animations
929
954
  raf(() => { var _a; return (_a = this.resizeObserver) === null || _a === void 0 ? void 0 : _a.observe(el); });
955
+ startVisibilityPolling();
930
956
  }
931
957
  else {
932
- /**
933
- * Fallback for test environments where ResizeObserver is not available.
934
- * Just mark as ready without initializing scroll/keyboard listeners
935
- * since those also require browser APIs not available in Jest.
936
- */
958
+ // Test environment fallback - mark ready immediately
937
959
  writeTask(() => {
938
960
  el.classList.add('datetime-ready');
939
961
  });
940
962
  }
963
+ }
964
+ componentDidLoad() {
965
+ this.initializeVisibilityObserver();
941
966
  /**
942
967
  * Datetime uses Ionic components that emit
943
968
  * ionFocus and ionBlur. These events are
@@ -1666,7 +1691,7 @@ export class Datetime {
1666
1691
  const hasDatePresentation = presentation === 'date' || presentation === 'date-time' || presentation === 'time-date';
1667
1692
  const hasWheelVariant = hasDatePresentation && preferWheel;
1668
1693
  renderHiddenInput(true, el, name, formatValue(value), disabled);
1669
- return (h(Host, { key: '187a5b5cf1413449a9db33f8c5b0fd3b7fded302', "aria-disabled": disabled ? 'true' : null, onFocus: this.onFocus, onBlur: this.onBlur, class: Object.assign({}, createColorClasses(color, {
1694
+ return (h(Host, { key: '2fb2938db507a134622a3feac44804f11071c589', "aria-disabled": disabled ? 'true' : null, onFocus: this.onFocus, onBlur: this.onBlur, class: Object.assign({}, createColorClasses(color, {
1670
1695
  [mode]: true,
1671
1696
  ['datetime-readonly']: readonly,
1672
1697
  ['datetime-disabled']: disabled,
package/dist/docs.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "timestamp": "2025-12-12T15:13:24",
2
+ "timestamp": "2025-12-12T17:31:38",
3
3
  "compiler": {
4
4
  "name": "@stencil/core",
5
5
  "version": "4.38.0",
@@ -1065,78 +1065,103 @@ const Datetime = class {
1065
1065
  this.resizeObserver.disconnect();
1066
1066
  this.resizeObserver = undefined;
1067
1067
  }
1068
+ if (this.destroyOverlayListeners) {
1069
+ this.destroyOverlayListeners();
1070
+ this.destroyOverlayListeners = undefined;
1071
+ }
1068
1072
  }
1069
1073
  initializeListeners() {
1070
1074
  this.initializeCalendarListener();
1071
1075
  this.initializeKeyboardListeners();
1072
1076
  }
1073
- componentDidLoad() {
1077
+ /**
1078
+ * Sets up visibility detection for the datetime component.
1079
+ *
1080
+ * Uses multiple strategies to reliably detect when the datetime becomes
1081
+ * visible, which is necessary for proper initialization of scrollable areas:
1082
+ * 1. ResizeObserver - detects dimension changes
1083
+ * 2. Overlay event listeners - for datetime inside modals/popovers
1084
+ * 3. Polling fallback - for browsers where observers are unreliable (WebKit)
1085
+ */
1086
+ initializeVisibilityObserver() {
1074
1087
  const { el } = this;
1088
+ const markReady = () => {
1089
+ if (el.classList.contains('datetime-ready')) {
1090
+ return;
1091
+ }
1092
+ this.initializeListeners();
1093
+ writeTask(() => {
1094
+ el.classList.add('datetime-ready');
1095
+ });
1096
+ };
1097
+ const markHidden = () => {
1098
+ this.destroyInteractionListeners();
1099
+ this.showMonthAndYear = false;
1100
+ writeTask(() => {
1101
+ el.classList.remove('datetime-ready');
1102
+ });
1103
+ startVisibilityPolling();
1104
+ };
1075
1105
  /**
1076
- * If a scrollable element is hidden using `display: none`,
1077
- * it will not have a scroll height meaning we cannot scroll elements
1078
- * into view. As a result, we will need to wait for the datetime to become
1079
- * visible if used inside of a modal or a popover otherwise the scrollable
1080
- * areas will not have the correct values snapped into place.
1081
- *
1082
- * FW-6931: We use ResizeObserver to detect when the element transitions
1083
- * between having dimensions (visible) and zero dimensions (hidden). This
1084
- * is more reliable than IntersectionObserver for detecting visibility
1085
- * changes, especially when the element is inside a modal or popover.
1106
+ * FW-6931: Poll for visibility as a fallback for browsers where
1107
+ * ResizeObserver doesn't fire reliably (e.g., WebKit).
1108
+ */
1109
+ const startVisibilityPolling = () => {
1110
+ let pollCount = 0;
1111
+ const poll = () => {
1112
+ if (el.classList.contains('datetime-ready') || pollCount++ >= 60) {
1113
+ return;
1114
+ }
1115
+ const { width, height } = el.getBoundingClientRect();
1116
+ if (width > 0 && height > 0) {
1117
+ markReady();
1118
+ }
1119
+ else {
1120
+ raf(poll);
1121
+ }
1122
+ };
1123
+ raf(poll);
1124
+ };
1125
+ /**
1126
+ * FW-6931: Listen for overlay present/dismiss events when datetime
1127
+ * is inside a modal or popover.
1086
1128
  */
1129
+ const parentOverlay = el.closest('ion-modal, ion-popover');
1130
+ if (parentOverlay) {
1131
+ const handlePresent = () => markReady();
1132
+ const handleDismiss = () => markHidden();
1133
+ parentOverlay.addEventListener('didPresent', handlePresent);
1134
+ parentOverlay.addEventListener('didDismiss', handleDismiss);
1135
+ this.destroyOverlayListeners = () => {
1136
+ parentOverlay.removeEventListener('didPresent', handlePresent);
1137
+ parentOverlay.removeEventListener('didDismiss', handleDismiss);
1138
+ };
1139
+ }
1087
1140
  if (typeof ResizeObserver !== 'undefined') {
1088
1141
  this.resizeObserver = new ResizeObserver((entries) => {
1089
- const entry = entries[0];
1090
- const { width, height } = entry.contentRect;
1142
+ const { width, height } = entries[0].contentRect;
1091
1143
  const isVisible = width > 0 && height > 0;
1092
1144
  const isReady = el.classList.contains('datetime-ready');
1093
1145
  if (isVisible && !isReady) {
1094
- this.initializeListeners();
1095
- /**
1096
- * TODO FW-2793: Datetime needs a frame to ensure that it
1097
- * can properly scroll contents into view. As a result
1098
- * we hide the scrollable content until after that frame
1099
- * so users do not see the content quickly shifting. The downside
1100
- * is that the content will pop into view a frame after. Maybe there
1101
- * is a better way to handle this?
1102
- */
1103
- writeTask(() => {
1104
- el.classList.add('datetime-ready');
1105
- });
1146
+ markReady();
1106
1147
  }
1107
1148
  else if (!isVisible && isReady) {
1108
- /**
1109
- * Clean up listeners when hidden so we can properly
1110
- * reinitialize scroll positions on re-presentation.
1111
- */
1112
- this.destroyInteractionListeners();
1113
- /**
1114
- * Close month/year picker when hidden, otherwise
1115
- * it will be open when re-presented with a 0-height
1116
- * scroll area, showing the wrong month.
1117
- */
1118
- this.showMonthAndYear = false;
1119
- writeTask(() => {
1120
- el.classList.remove('datetime-ready');
1121
- });
1149
+ markHidden();
1122
1150
  }
1123
1151
  });
1124
- /**
1125
- * Use raf to avoid a race condition between the component loading and
1126
- * its display animation starting (such as when shown in a modal).
1127
- */
1152
+ // Use raf to avoid race condition with modal/popover animations
1128
1153
  raf(() => { var _a; return (_a = this.resizeObserver) === null || _a === void 0 ? void 0 : _a.observe(el); });
1154
+ startVisibilityPolling();
1129
1155
  }
1130
1156
  else {
1131
- /**
1132
- * Fallback for test environments where ResizeObserver is not available.
1133
- * Just mark as ready without initializing scroll/keyboard listeners
1134
- * since those also require browser APIs not available in Jest.
1135
- */
1157
+ // Test environment fallback - mark ready immediately
1136
1158
  writeTask(() => {
1137
1159
  el.classList.add('datetime-ready');
1138
1160
  });
1139
1161
  }
1162
+ }
1163
+ componentDidLoad() {
1164
+ this.initializeVisibilityObserver();
1140
1165
  /**
1141
1166
  * Datetime uses Ionic components that emit
1142
1167
  * ionFocus and ionBlur. These events are
@@ -1865,7 +1890,7 @@ const Datetime = class {
1865
1890
  const hasDatePresentation = presentation === 'date' || presentation === 'date-time' || presentation === 'time-date';
1866
1891
  const hasWheelVariant = hasDatePresentation && preferWheel;
1867
1892
  renderHiddenInput(true, el, name, formatValue(value), disabled);
1868
- return (h(Host, { key: '187a5b5cf1413449a9db33f8c5b0fd3b7fded302', "aria-disabled": disabled ? 'true' : null, onFocus: this.onFocus, onBlur: this.onBlur, class: Object.assign({}, createColorClasses(color, {
1893
+ return (h(Host, { key: '2fb2938db507a134622a3feac44804f11071c589', "aria-disabled": disabled ? 'true' : null, onFocus: this.onFocus, onBlur: this.onBlur, class: Object.assign({}, createColorClasses(color, {
1869
1894
  [mode]: true,
1870
1895
  ['datetime-readonly']: readonly,
1871
1896
  ['datetime-disabled']: disabled,