Skip to content

Commit dd349ce

Browse files
devongovettLFDanLu
andauthored
fix: Overlay positioning with collection double render (adobe#8757)
* fix: Overlay positioning with collection double render * fix menu wiggle due to usePreventScroll and delayed positioning --------- Co-authored-by: Daniel Lu <dl1644@gmail.com>
1 parent c4bf4af commit dd349ce

File tree

5 files changed

+16
-1
lines changed

5 files changed

+16
-1
lines changed

packages/@react-aria/collections/src/BaseCollection.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ export class BaseCollection<T> implements ICollection<Node<T>> {
142142
private lastKey: Key | null = null;
143143
private frozen = false;
144144
private itemCount: number = 0;
145+
isComplete = true;
145146

146147
get size(): number {
147148
return this.itemCount;

packages/@react-aria/collections/src/CollectionBuilder.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ function useCollectionDocument<T extends object, C extends BaseCollection<T>>(cr
116116
let collection = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
117117
useLayoutEffect(() => {
118118
document.isMounted = true;
119+
document.isInitialRender = false;
119120
return () => {
120121
// Mark unmounted so we can skip all of the collection updates caused by
121122
// React calling removeChild on every item in the collection.

packages/@react-aria/collections/src/Document.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,7 @@ export class Document<T, C extends BaseCollection<T> = BaseCollection<T>> extend
416416
nodeId = 0;
417417
nodesByProps: WeakMap<object, ElementNode<T>> = new WeakMap<object, ElementNode<T>>();
418418
isMounted = true;
419+
isInitialRender = true;
419420
private collection: C;
420421
private nextCollection: C | null = null;
421422
private subscriptions: Set<() => void> = new Set();
@@ -522,6 +523,10 @@ export class Document<T, C extends BaseCollection<T> = BaseCollection<T>> extend
522523
this.nextCollection = null;
523524
}
524525
}
526+
527+
if (this.isInitialRender) {
528+
this.collection.isComplete = false;
529+
}
525530
}
526531

527532
queueUpdate(): void {

packages/@react-aria/overlays/src/useOverlayPosition.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,11 @@ export function useOverlayPosition(props: AriaPositionProps): PositionAria {
149149
return;
150150
}
151151

152+
// Delay updating the position until children are finished rendering (e.g. collections).
153+
if (overlayRef.current.querySelector('[data-react-aria-incomplete]')) {
154+
return;
155+
}
156+
152157
// Don't update while the overlay is animating.
153158
// Things like scale animations can mess up positioning by affecting the overlay's computed size.
154159
if (overlayRef.current.getAnimations?.().length > 0) {
@@ -294,7 +299,9 @@ export function useOverlayPosition(props: AriaPositionProps): PositionAria {
294299
return {
295300
overlayProps: {
296301
style: {
297-
position: 'absolute',
302+
position: position ? 'absolute' : 'fixed',
303+
top: !position ? 0 : undefined,
304+
left: !position ? 0 : undefined,
298305
zIndex: 100000, // should match the z-index in ModalTrigger
299306
...position?.position,
300307
maxHeight: position?.maxHeight ?? '100vh'

packages/react-aria-components/src/Menu.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,7 @@ function MenuInner<T extends object>({props, collection, menuRef: ref}: MenuInne
241241
ref={ref as RefObject<HTMLDivElement>}
242242
slot={props.slot || undefined}
243243
data-empty={state.collection.size === 0 || undefined}
244+
data-react-aria-incomplete={!(state.collection as BaseCollection<T>).isComplete || undefined}
244245
onScroll={props.onScroll}>
245246
<Provider
246247
values={[

0 commit comments

Comments
 (0)