@@ -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