@@ -1105,6 +1105,26 @@ export class Datetime implements ComponentInterface {
11051105 this . initializeKeyboardListeners ( ) ;
11061106 }
11071107
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+
11081128 componentDidLoad ( ) {
11091129 const { el } = this ;
11101130
@@ -1115,11 +1135,31 @@ export class Datetime implements ComponentInterface {
11151135 * visible if used inside of a modal or a popover otherwise the scrollable
11161136 * areas will not have the correct values snapped into place.
11171137 *
1118- * FW-6931: We use ResizeObserver to detect when the element transitions
1119- * between having dimensions (visible) and zero dimensions (hidden). This
1120- * is more reliable than IntersectionObserver for detecting visibility
1121- * changes, especially when the element is inside a modal or popover.
1138+ * We use ResizeObserver to detect when the element transitions between
1139+ * having dimensions (visible) and zero dimensions (hidden). This is more
1140+ * reliable than IntersectionObserver for detecting visibility changes,
1141+ * especially when the element is inside a modal or popover.
11221142 */
1143+ const markReady = ( ) => {
1144+ this . initializeListeners ( ) ;
1145+ writeTask ( ( ) => {
1146+ el . classList . add ( 'datetime-ready' ) ;
1147+ } ) ;
1148+ } ;
1149+
1150+ const markHidden = ( ) => {
1151+ this . destroyInteractionListeners ( ) ;
1152+ this . showMonthAndYear = false ;
1153+ writeTask ( ( ) => {
1154+ el . classList . remove ( 'datetime-ready' ) ;
1155+ } ) ;
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 ) ;
1161+ } ;
1162+
11231163 if ( typeof ResizeObserver !== 'undefined' ) {
11241164 this . resizeObserver = new ResizeObserver ( ( entries ) => {
11251165 const entry = entries [ 0 ] ;
@@ -1128,36 +1168,9 @@ export class Datetime implements ComponentInterface {
11281168 const isReady = el . classList . contains ( 'datetime-ready' ) ;
11291169
11301170 if ( isVisible && ! isReady ) {
1131- this . initializeListeners ( ) ;
1132-
1133- /**
1134- * TODO FW-2793: Datetime needs a frame to ensure that it
1135- * can properly scroll contents into view. As a result
1136- * we hide the scrollable content until after that frame
1137- * so users do not see the content quickly shifting. The downside
1138- * is that the content will pop into view a frame after. Maybe there
1139- * is a better way to handle this?
1140- */
1141- writeTask ( ( ) => {
1142- el . classList . add ( 'datetime-ready' ) ;
1143- } ) ;
1171+ markReady ( ) ;
11441172 } else if ( ! isVisible && isReady ) {
1145- /**
1146- * Clean up listeners when hidden so we can properly
1147- * reinitialize scroll positions on re-presentation.
1148- */
1149- this . destroyInteractionListeners ( ) ;
1150-
1151- /**
1152- * Close month/year picker when hidden, otherwise
1153- * it will be open when re-presented with a 0-height
1154- * scroll area, showing the wrong month.
1155- */
1156- this . showMonthAndYear = false ;
1157-
1158- writeTask ( ( ) => {
1159- el . classList . remove ( 'datetime-ready' ) ;
1160- } ) ;
1173+ markHidden ( ) ;
11611174 }
11621175 } ) ;
11631176
0 commit comments