@@ -2,6 +2,16 @@ import React, { useCallback, useEffect, useMemo, useState, useRef } from 'react'
22import { InView } from 'react-intersection-observer'
33import { getViewPortScrollingState } from './viewPort'
44
5+ interface IElementMeasurements {
6+ width : string | number
7+ clientHeight : number
8+ marginLeft : string | number | undefined
9+ marginRight : string | number | undefined
10+ marginTop : string | number | undefined
11+ marginBottom : string | number | undefined
12+ id : string | undefined
13+ }
14+
515const IDLE_CALLBACK_TIMEOUT = 100
616// Increased delay for Safari, as Safari doesn't have scroll time when using 'smooth' scroll
717const SAFARI_VISIBILITY_DELAY = / ^ ( (? ! c h r o m e | a n d r o i d ) .) * s a f a r i / i. test ( navigator . userAgent ) ? 100 : 0
@@ -52,9 +62,15 @@ export function VirtualElement({
5262 id ?: string | undefined
5363 className ?: string
5464} > ) : JSX . Element | null {
65+ const [ waitForInitialLoad , setWaitForInitialLoad ] = useState ( true )
5566 const [ inView , setInView ] = useState ( initialShow ?? false )
5667 const [ isShowingChildren , setIsShowingChildren ] = useState ( inView )
5768
69+ const [ measurements , setMeasurements ] = useState < IElementMeasurements | null > ( null )
70+ const [ ref , setRef ] = useState < HTMLDivElement | null > ( null )
71+
72+ const showPlaceholder = ! isShowingChildren && ! initialShow
73+
5874 // Track the last visibility change to debounce
5975 const lastVisibilityChangeRef = useRef < number > ( 0 )
6076
@@ -107,6 +123,21 @@ export function VirtualElement({
107123 useEffect ( ( ) => {
108124 if ( inView === true ) {
109125 setIsShowingChildren ( true )
126+
127+ // Schedule a measurement after a short delay
128+ if ( waitForInitialLoad && ref ) {
129+ const initialMeasurementTimeout = window . setTimeout ( ( ) => {
130+ const measurements = measureElement ( ref )
131+ if ( measurements ) {
132+ setMeasurements ( measurements )
133+ setWaitForInitialLoad ( false )
134+ }
135+ } , 1000 )
136+
137+ return ( ) => {
138+ window . clearTimeout ( initialMeasurementTimeout )
139+ }
140+ }
110141 return
111142 }
112143
@@ -127,6 +158,13 @@ export function VirtualElement({
127158 }
128159 idleCallback = window . requestIdleCallback (
129160 ( ) => {
161+ // Measure the entire wrapper element instead of just the childRef
162+ if ( ref ) {
163+ const measurements = measureElement ( ref )
164+ if ( measurements ) {
165+ setMeasurements ( measurements )
166+ }
167+ }
130168 setIsShowingChildren ( false )
131169 } ,
132170 {
@@ -146,9 +184,7 @@ export function VirtualElement({
146184 window . clearTimeout ( optimizeTimeout )
147185 }
148186 }
149- } , [ inView ] )
150-
151- const showPlaceholder = ! isShowingChildren && ! initialShow
187+ } , [ ref , inView ] )
152188
153189 return (
154190 < InView
@@ -158,13 +194,96 @@ export function VirtualElement({
158194 className = { className }
159195 as = "div"
160196 >
161- < div >
197+ < div
198+ ref = { setRef }
199+ style = {
200+ // We need to set undefined if the height is not known, to allow the parent to calculate the height
201+ measurements
202+ ? {
203+ height : measurements . clientHeight + 'px' ,
204+ }
205+ : undefined
206+ }
207+ >
162208 { showPlaceholder ? (
163- < div id = { id } className = { `virtual-element-placeholder ${ placeholderClassName } ` } style = { styleObj } > </ div >
209+ < div
210+ id = { measurements ?. id ?? id }
211+ className = { `virtual-element-placeholder ${ placeholderClassName } ` }
212+ style = { styleObj }
213+ > </ div >
164214 ) : (
165215 children
166216 ) }
167217 </ div >
168218 </ InView >
169219 )
170220}
221+
222+ function measureElement ( wrapperEl : HTMLDivElement ) : IElementMeasurements | null {
223+ if ( ! wrapperEl || ! wrapperEl . firstElementChild ) {
224+ return null
225+ }
226+
227+ const el = wrapperEl . firstElementChild as HTMLElement
228+ const style = window . getComputedStyle ( el )
229+
230+ // Look for the complete wrapper that contains both the timeline and dashboard
231+ const timelineWrapper = el . closest ( '.segment-timeline-wrapper--shelf' )
232+
233+ if ( timelineWrapper ) {
234+ // Check if the direct child of the wrapper has an explicit height set
235+ const wrapperChild = timelineWrapper . querySelector ( ':scope > div' )
236+ let totalHeight = 0
237+
238+ if ( wrapperChild && wrapperChild instanceof HTMLElement ) {
239+ // Check for explicit height style
240+ const inlineHeight = wrapperChild . style . height
241+ if ( inlineHeight && inlineHeight . length > 0 ) {
242+ // Use the explicit height if it exists
243+ // Extract the numeric value if it's in pixels
244+ const heightValue = parseInt ( inlineHeight , 10 )
245+ if ( ! isNaN ( heightValue ) ) {
246+ totalHeight = heightValue
247+ }
248+ }
249+ }
250+
251+ // If no explicit height was found or it wasn't parseable, fall back to measuring
252+ if ( totalHeight === 0 ) {
253+ // Get the segment timeline height
254+ const segmentTimeline = timelineWrapper . querySelector ( '.segment-timeline' )
255+ if ( segmentTimeline ) {
256+ const segmentRect = segmentTimeline . getBoundingClientRect ( )
257+ totalHeight = segmentRect . height
258+ }
259+
260+ // Add the dashboard panel height if it exists
261+ const dashboardPanel = timelineWrapper . querySelector ( '.dashboard-panel' )
262+ if ( dashboardPanel ) {
263+ const panelRect = dashboardPanel . getBoundingClientRect ( )
264+ totalHeight += panelRect . height
265+ }
266+ }
267+
268+ return {
269+ width : style . width || 'auto' ,
270+ clientHeight : totalHeight ,
271+ marginTop : style . marginTop || undefined ,
272+ marginBottom : style . marginBottom || undefined ,
273+ marginLeft : style . marginLeft || undefined ,
274+ marginRight : style . marginRight || undefined ,
275+ id : el . id ,
276+ }
277+ }
278+
279+ // Fallback to just measuring the element itself if wrapper isn't found
280+ return {
281+ width : style . width || 'auto' ,
282+ clientHeight : el . clientHeight ,
283+ marginTop : style . marginTop || undefined ,
284+ marginBottom : style . marginBottom || undefined ,
285+ marginLeft : style . marginLeft || undefined ,
286+ marginRight : style . marginRight || undefined ,
287+ id : el . id ,
288+ }
289+ }
0 commit comments