@@ -149,8 +149,6 @@ export default defineComponent({
149149 // skidding sign isn't auto-mirrored, so we flip it here. Snapshot
150150 // at init: Nextcloud's language doesn't change at runtime.
151151 popoverSkidding: isRTL () ? 82 : - 82 ,
152- // Re-fires recomputeGridMaxHeight on layout changes.
153- gridResizeObserver: null as ResizeObserver | null ,
154152 }
155153 },
156154
@@ -180,13 +178,13 @@ export default defineComponent({
180178 },
181179
182180 watch: {
183- // On open, land the roving stop on the active app rather than index 0.
181+ // On open, land the roving stop on the active app rather than index 0
182+ // and measure the grid as soon as it mounts (before the open
183+ // transition finishes, so the cap is set without a flash).
184184 opened(isOpen : boolean ) {
185185 if (isOpen ) {
186186 this .focusedIndex = this .activeGridIndex ()
187- this .$nextTick (() => this .attachGridObserver ())
188- } else {
189- this .detachGridObserver ()
187+ this .tryRecomputeGridMaxHeight (5 )
190188 }
191189 },
192190 },
@@ -204,7 +202,6 @@ export default defineComponent({
204202 beforeUnmount() {
205203 unsubscribe (' nextcloud:app-menu.refresh' , this .setApps )
206204 ;(this .$refs .popover as { $off: (e : string , fn : () => void ) => void } | undefined )?.$off (' after-hide' , this .onPopoverAfterHide )
207- this .detachGridObserver ()
208205 },
209206
210207 methods: {
@@ -243,27 +240,19 @@ export default defineComponent({
243240 }
244241 },
245242
246- // NcPopover renders the slot lazily; poll for the grid ref via rAF.
247- attachGridObserver(retries = 30 ) {
243+ // Poll briefly for the grid ref (NcPopover renders the slot async)
244+ // then measure once. Bounded so a missing ref can never leak frames.
245+ tryRecomputeGridMaxHeight(retries : number ) {
248246 if (! this .opened || retries <= 0 ) {
249247 return
250248 }
251- const grid = this .$refs .grid as HTMLElement | undefined
252- if (! grid ) {
253- requestAnimationFrame (() => this .attachGridObserver (retries - 1 ))
249+ if (! this .$refs .grid ) {
250+ requestAnimationFrame (() => this .tryRecomputeGridMaxHeight (retries - 1 ))
254251 return
255252 }
256- this .detachGridObserver ()
257- this .gridResizeObserver = new ResizeObserver (() => this .recomputeGridMaxHeight ())
258- this .gridResizeObserver .observe (grid )
259253 this .recomputeGridMaxHeight ()
260254 },
261255
262- detachGridObserver() {
263- this .gridResizeObserver ?.disconnect ()
264- this .gridResizeObserver = null
265- },
266-
267256 // Cap = sum of first 6 row heights + baseline × 6, so the peek of
268257 // row 7 stays constant when wraps grow rows.
269258 recomputeGridMaxHeight() {
@@ -274,9 +263,7 @@ export default defineComponent({
274263 const VISIBLE_CELLS = 24 // 4 cols × 6 visible rows
275264 const cells = grid .children
276265 if (cells .length <= VISIBLE_CELLS ) {
277- if (grid .style .maxHeight !== ' ' ) {
278- grid .style .maxHeight = ' '
279- }
266+ grid .style .maxHeight = ' '
280267 return
281268 }
282269 const firstHidden = cells [VISIBLE_CELLS ] as HTMLElement | undefined
@@ -287,11 +274,7 @@ export default defineComponent({
287274 const sumOfFirstRows = firstHidden .getBoundingClientRect ().top
288275 - firstCell .getBoundingClientRect ().top
289276 const baseline = parseFloat (getComputedStyle (grid ).getPropertyValue (' --default-grid-baseline' )) || 4
290- const cap = ` ${sumOfFirstRows + baseline * 6 }px `
291- // Skip identical writes — they re-fire the ResizeObserver.
292- if (grid .style .maxHeight !== cap ) {
293- grid .style .maxHeight = cap
294- }
277+ grid .style .maxHeight = ` ${sumOfFirstRows + baseline * 6 }px `
295278 },
296279
297280 // Index of the active app within `gridItems`, or 0 if none is active.
0 commit comments