@@ -43,6 +43,7 @@ const SAFARI_VISIBILITY_DELAY = /^((?!chrome|android).)*safari/i.test(navigator.
4343 * }
4444 * @return {* } {(JSX.Element | null)}
4545 */
46+
4647export function VirtualElement ( {
4748 initialShow,
4849 placeholderHeight,
@@ -62,19 +63,21 @@ export function VirtualElement({
6263 id ?: string | undefined
6364 className ?: string
6465} > ) : JSX . Element | null {
66+ const resizeObserverManager = ResizeObserverManager . getInstance ( )
6567 const [ waitForInitialLoad , setWaitForInitialLoad ] = useState ( true )
6668 const [ inView , setInView ] = useState ( initialShow ?? false )
6769 const [ isShowingChildren , setIsShowingChildren ] = useState ( inView )
6870
6971 const [ measurements , setMeasurements ] = useState < IElementMeasurements | null > ( null )
7072 const [ ref , setRef ] = useState < HTMLDivElement | null > ( null )
7173
72- // Store the old dashboard height to detect changes:
73- let oldElementHeight : number | undefined
74- let resizeTimeout : NodeJS . Timeout
74+ let inViewChangetimer : NodeJS . Timeout | undefined
7575
7676 const showPlaceholder = ! isShowingChildren && ! initialShow
7777
78+ const isCurrentlyObserving = useRef ( false )
79+ const isTransitioning = useRef ( false )
80+
7881 // Track the last visibility change to debounce
7982 const lastVisibilityChangeRef = useRef < number > ( 0 )
8083
@@ -95,6 +98,27 @@ export function VirtualElement({
9598 [ width , placeholderHeight ]
9699 )
97100
101+ const handleResize = useCallback ( ( ) => {
102+ if ( ref ) {
103+ // Show children during measurement
104+ setIsShowingChildren ( true )
105+
106+ requestAnimationFrame ( ( ) => {
107+ const measurements = measureElement ( ref , placeholderHeight )
108+ if ( measurements ) {
109+ setMeasurements ( measurements )
110+
111+ // Only hide children again if not in view
112+ if ( ! inView && measurements . clientHeight > 0 ) {
113+ setIsShowingChildren ( false )
114+ } else {
115+ setIsShowingChildren ( true )
116+ }
117+ }
118+ } )
119+ }
120+ } , [ ref , inView , placeholderHeight ] )
121+
98122 const onVisibleChanged = useCallback (
99123 ( visible : boolean ) => {
100124 const now = Date . now ( )
@@ -109,20 +133,33 @@ export function VirtualElement({
109133 return
110134 }
111135
112- if ( visible ) {
113- oldElementHeight = findElementHeight ( )
114- if ( ref ) {
115- console . log ( 'Connecting observer for element' , ref )
116- resizeObserver . observe ( ref )
117- }
118- } else {
119- console . log ( 'Disconnecting observer for element' , ref )
120- resizeObserver . disconnect ( )
136+ setInView ( visible )
137+
138+ // Don't do updates while transitioning:
139+ if ( isTransitioning . current ) {
140+ return
121141 }
142+ isTransitioning . current = true
122143
123- setInView ( visible )
144+ // Clear any existing timers
145+ if ( inViewChangetimer ) {
146+ clearTimeout ( inViewChangetimer )
147+ }
148+
149+ // Delay the observer connection to prevent flickering
150+ inViewChangetimer = setTimeout ( ( ) => {
151+ if ( visible ) {
152+ if ( ref ) {
153+ if ( ! isCurrentlyObserving . current ) {
154+ resizeObserverManager . observe ( ref , handleResize )
155+ isCurrentlyObserving . current = true
156+ }
157+ }
158+ }
159+ isTransitioning . current = false
160+ } , 100 )
124161 } ,
125- [ inView ]
162+ [ ref ]
126163 )
127164
128165 const isScrolling = ( ) : boolean => {
@@ -138,62 +175,26 @@ export function VirtualElement({
138175 return false
139176 }
140177
141- function handleResize ( ) {
142- if ( ref ) {
143- // Show children during measurement
144- setIsShowingChildren ( true )
145- if ( resizeTimeout ) {
146- clearTimeout ( resizeTimeout )
147- }
148- resizeTimeout = setTimeout ( ( ) => {
149- requestAnimationFrame ( ( ) => {
150- const measurements = measureElement ( ref , placeholderHeight )
151- if ( measurements ) {
152- setMeasurements ( measurements )
153-
154- // Only hide children again if not in view
155- if ( ! inView && measurements . clientHeight > 0 ) {
156- setIsShowingChildren ( false )
157- } else {
158- setIsShowingChildren ( true )
159- }
160- }
161- } )
162- } , 50 )
163- }
164- }
165-
166- const findDashboardPanel = ( ) : HTMLElement | null => {
167- const timelineWrapper = ref ?. closest ( '.segment-timeline-wrapper--shelf' )
168- const dashboardPanel = timelineWrapper ?. querySelector ( '.dashboard-panel' )
169- if ( dashboardPanel ) {
170- return dashboardPanel as HTMLElement
178+ useEffect ( ( ) => {
179+ // Setup initial observer if element is in view
180+ if ( ref && inView && ! isCurrentlyObserving . current ) {
181+ resizeObserverManager . observe ( ref , handleResize )
182+ isCurrentlyObserving . current = true
171183 }
172- return null
173- }
174-
175- function findElementHeight ( ) : number {
176- const dashboardElement = findDashboardPanel ( )
177- // Get heigth of timeline wrapper
178- const dashboardHeight = dashboardElement ?. clientHeight || 0
179- const totalHeight = ref ?. clientHeight || placeholderHeight || 160 + dashboardHeight
180184
181- return totalHeight
182- }
183-
184- // Handle Viewport heigth changes:
185- const resizeObserver = new ResizeObserver ( ( ) => {
186- console . log ( 'Observing resize' )
187-
188- const elementHeight = findElementHeight ( )
189- console . log ( 'elementHeight' , elementHeight , 'oldElementHeight' , oldElementHeight )
185+ // Cleanup function
186+ return ( ) => {
187+ // Clean up resize observer
188+ if ( ref && isCurrentlyObserving . current ) {
189+ resizeObserverManager . unobserve ( ref )
190+ isCurrentlyObserving . current = false
191+ }
190192
191- if ( elementHeight && elementHeight !== oldElementHeight ) {
192- console . log ( 'dashboard containerHeigth changed to ' , elementHeight , 'from' , oldElementHeight )
193- oldElementHeight = elementHeight
194- handleResize ( )
193+ if ( inViewChangetimer ) {
194+ clearTimeout ( inViewChangetimer )
195+ }
195196 }
196- } )
197+ } , [ ref , inView ] )
197198
198199 useEffect ( ( ) => {
199200 if ( inView === true ) {
@@ -207,12 +208,7 @@ export function VirtualElement({
207208 setMeasurements ( measurements )
208209 setWaitForInitialLoad ( false )
209210 }
210- // Setup initial resize observers
211- oldElementHeight = findElementHeight ( )
212- if ( ref ) {
213- resizeObserver . observe ( ref )
214- }
215- } , 2000 )
211+ } , 800 )
216212
217213 return ( ) => {
218214 window . clearTimeout ( initialMeasurementTimeout )
@@ -351,3 +347,50 @@ function measureElement(wrapperEl: HTMLDivElement, placeholderHeight?: number):
351347 id : el . id ,
352348 }
353349}
350+
351+ // Singleton class to manage ResizeObserver instances
352+ export class ResizeObserverManager {
353+ private static instance : ResizeObserverManager
354+ private resizeObserver : ResizeObserver
355+ private observedElements : Map < HTMLElement , ( entry : ResizeObserverEntry ) => void >
356+
357+ private constructor ( ) {
358+ this . observedElements = new Map ( )
359+ this . resizeObserver = new ResizeObserver ( ( entries ) => {
360+ entries . forEach ( ( entry ) => {
361+ const element = entry . target as HTMLElement
362+ const callback = this . observedElements . get ( element )
363+ if ( callback ) {
364+ callback ( entry )
365+ }
366+ } )
367+ } )
368+ }
369+
370+ public static getInstance ( ) : ResizeObserverManager {
371+ if ( ! ResizeObserverManager . instance ) {
372+ ResizeObserverManager . instance = new ResizeObserverManager ( )
373+ }
374+ return ResizeObserverManager . instance
375+ }
376+
377+ public observe ( element : HTMLElement , callback : ( entry : ResizeObserverEntry ) => void ) : void {
378+ if ( ! element ) return
379+
380+ // Store the callback in our map
381+ this . observedElements . set ( element , callback )
382+
383+ // Start observing
384+ this . resizeObserver . observe ( element )
385+ }
386+
387+ public unobserve ( element : HTMLElement ) : void {
388+ if ( ! element ) return
389+
390+ // Remove from our map
391+ this . observedElements . delete ( element )
392+
393+ // Stop observing
394+ this . resizeObserver . unobserve ( element )
395+ }
396+ }
0 commit comments