Skip to content

Commit 7c8b35e

Browse files
committed
fix(datetime): moving to overlay listeners for showing instead
1 parent 12ef934 commit 7c8b35e

File tree

1 file changed

+53
-30
lines changed

1 file changed

+53
-30
lines changed

core/src/components/datetime/datetime.tsx

Lines changed: 53 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ export class Datetime implements ComponentInterface {
118118

119119
private destroyCalendarListener?: () => void;
120120
private destroyKeyboardMO?: () => void;
121+
private destroyOverlayListeners?: () => void;
121122

122123
// TODO(FW-2832): types (DatetimeParts causes some errors that need untangling)
123124
private minParts?: any;
@@ -1081,6 +1082,10 @@ export class Datetime implements ComponentInterface {
10811082
this.resizeObserver.disconnect();
10821083
this.resizeObserver = undefined;
10831084
}
1085+
if (this.destroyOverlayListeners) {
1086+
this.destroyOverlayListeners();
1087+
this.destroyOverlayListeners = undefined;
1088+
}
10841089
}
10851090

10861091
/**
@@ -1105,26 +1110,6 @@ export class Datetime implements ComponentInterface {
11051110
this.initializeKeyboardListeners();
11061111
}
11071112

1108-
/**
1109-
* FW-6931: Fallback check for when ResizeObserver doesn't fire reliably
1110-
* (e.g., WebKit during modal re-presentation). Called after element is
1111-
* hidden to catch when it becomes visible again.
1112-
*/
1113-
private checkVisibilityFallback = () => {
1114-
const { el } = this;
1115-
if (el.classList.contains('datetime-ready')) {
1116-
return;
1117-
}
1118-
1119-
const rect = el.getBoundingClientRect();
1120-
if (rect.width > 0 && rect.height > 0) {
1121-
this.initializeListeners();
1122-
writeTask(() => {
1123-
el.classList.add('datetime-ready');
1124-
});
1125-
}
1126-
};
1127-
11281113
componentDidLoad() {
11291114
const { el } = this;
11301115

@@ -1141,25 +1126,66 @@ export class Datetime implements ComponentInterface {
11411126
* especially when the element is inside a modal or popover.
11421127
*/
11431128
const markReady = () => {
1129+
if (el.classList.contains('datetime-ready')) {
1130+
return;
1131+
}
11441132
this.initializeListeners();
11451133
writeTask(() => {
11461134
el.classList.add('datetime-ready');
11471135
});
11481136
};
11491137

1138+
/**
1139+
* FW-6931: Poll for visibility as a fallback for browsers where
1140+
* ResizeObserver and event listeners don't fire reliably (e.g., WebKit).
1141+
*/
1142+
const startVisibilityPolling = () => {
1143+
let pollCount = 0;
1144+
const pollVisibility = () => {
1145+
if (el.classList.contains('datetime-ready') || pollCount >= 60) {
1146+
return;
1147+
}
1148+
pollCount++;
1149+
const rect = el.getBoundingClientRect();
1150+
if (rect.width > 0 && rect.height > 0) {
1151+
markReady();
1152+
} else {
1153+
raf(pollVisibility);
1154+
}
1155+
};
1156+
raf(pollVisibility);
1157+
};
1158+
11501159
const markHidden = () => {
11511160
this.destroyInteractionListeners();
11521161
this.showMonthAndYear = false;
11531162
writeTask(() => {
11541163
el.classList.remove('datetime-ready');
11551164
});
1156-
/**
1157-
* Schedule fallback check for browsers where ResizeObserver
1158-
* doesn't fire reliably on re-presentation (e.g., WebKit).
1159-
*/
1160-
setTimeout(() => this.checkVisibilityFallback(), 100);
1165+
// Start polling for when the element becomes visible again
1166+
startVisibilityPolling();
11611167
};
11621168

1169+
/**
1170+
* FW-6931: If datetime is inside a popover or modal, listen for the
1171+
* overlay's present/dismiss events. This is more reliable than
1172+
* ResizeObserver in some browsers (e.g., WebKit) where the observer
1173+
* doesn't always fire when the overlay opens.
1174+
*/
1175+
const parentOverlay = el.closest('ion-modal, ion-popover') as HTMLIonModalElement | HTMLIonPopoverElement | null;
1176+
if (parentOverlay) {
1177+
const handlePresent = () => markReady();
1178+
const handleDismiss = () => markHidden();
1179+
1180+
parentOverlay.addEventListener('didPresent', handlePresent);
1181+
parentOverlay.addEventListener('didDismiss', handleDismiss);
1182+
1183+
this.destroyOverlayListeners = () => {
1184+
parentOverlay.removeEventListener('didPresent', handlePresent);
1185+
parentOverlay.removeEventListener('didDismiss', handleDismiss);
1186+
};
1187+
}
1188+
11631189
if (typeof ResizeObserver !== 'undefined') {
11641190
this.resizeObserver = new ResizeObserver((entries) => {
11651191
const entry = entries[0];
@@ -1180,11 +1206,8 @@ export class Datetime implements ComponentInterface {
11801206
*/
11811207
raf(() => this.resizeObserver?.observe(el));
11821208

1183-
/**
1184-
* Fallback for initial presentation in case ResizeObserver
1185-
* doesn't fire reliably (e.g., WebKit).
1186-
*/
1187-
setTimeout(() => this.checkVisibilityFallback(), 100);
1209+
// Start initial visibility polling
1210+
startVisibilityPolling();
11881211
} else {
11891212
/**
11901213
* Fallback for test environments where ResizeObserver is not available.

0 commit comments

Comments
 (0)