Skip to content

Commit 4a1baa3

Browse files
committed
Merge branch 'popover-animation-fix' of github.com:adobe/react-spectrum into test_17
2 parents 176c9b8 + a99e476 commit 4a1baa3

File tree

8 files changed

+37
-28
lines changed

8 files changed

+37
-28
lines changed

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,6 @@ export class BaseCollection<T> implements ICollection<Node<T>> {
140140
private lastKey: Key | null = null;
141141
private frozen = false;
142142
private itemCount: number = 0;
143-
isComplete = true;
144143

145144
get size(): number {
146145
return this.itemCount;

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

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,8 +118,6 @@ function useCollectionDocument<T extends object, C extends BaseCollection<T>>(cr
118118
// console.log('setting not initial render')
119119
console.trace('in LAYOUTEFFECT, setting isComplete to true');
120120
document.isMounted = true;
121-
document.isInitialRender = false;
122-
// document.isComplete = true;
123121
return () => {
124122
// Mark unmounted so we can skip all of the collection updates caused by
125123
// React calling removeChild on every item in the collection.

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

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -416,9 +416,6 @@ 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;
420-
isComplete = true;
421-
422419
private collection: C;
423420
private nextCollection: C | null = null;
424421
private subscriptions: Set<() => void> = new Set();
@@ -527,8 +524,6 @@ export class Document<T, C extends BaseCollection<T> = BaseCollection<T>> extend
527524
this.nextCollection = null;
528525
}
529526
}
530-
531-
this.collection.isComplete = this.isComplete;
532527
}
533528

534529
queueUpdate(): void {

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

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ function getContainerDimensions(containerNode: Element): Dimensions {
127127
left = visualViewport.offsetLeft;
128128
}
129129
} else {
130-
({width, height, top, left} = getOffset(containerNode));
130+
({width, height, top, left} = getOffset(containerNode, false));
131131
scroll.top = containerNode.scrollTop;
132132
scroll.left = containerNode.scrollLeft;
133133
totalWidth = width;
@@ -488,15 +488,15 @@ export function calculatePosition(opts: PositionOpts): PositionResult {
488488
let isViewportContainer = container === document.documentElement;
489489
const containerPositionStyle = window.getComputedStyle(container).position;
490490
let isContainerPositioned = !!containerPositionStyle && containerPositionStyle !== 'static';
491-
let childOffset: Offset = isViewportContainer ? getOffset(targetNode) : getPosition(targetNode, container);
491+
let childOffset: Offset = isViewportContainer ? getOffset(targetNode, false) : getPosition(targetNode, container, false);
492492

493493
if (!isViewportContainer) {
494494
let {marginTop, marginLeft} = window.getComputedStyle(targetNode);
495495
childOffset.top += parseInt(marginTop, 10) || 0;
496496
childOffset.left += parseInt(marginLeft, 10) || 0;
497497
}
498498

499-
let overlaySize: Offset = getOffset(overlayNode);
499+
let overlaySize: Offset = getOffset(overlayNode, true);
500500
let margins = getMargins(overlayNode);
501501
overlaySize.width += (margins.left ?? 0) + (margins.right ?? 0);
502502
overlaySize.height += (margins.top ?? 0) + (margins.bottom ?? 0);
@@ -507,7 +507,7 @@ export function calculatePosition(opts: PositionOpts): PositionResult {
507507
// If the container is the HTML element wrapping the body element, the retrieved scrollTop/scrollLeft will be equal to the
508508
// body element's scroll. Set the container's scroll values to 0 since the overlay's edge position value in getDelta don't then need to be further offset
509509
// by the container scroll since they are essentially the same containing element and thus in the same coordinate system
510-
let containerOffsetWithBoundary: Offset = boundaryElement.tagName === 'BODY' ? getOffset(container) : getPosition(container, boundaryElement);
510+
let containerOffsetWithBoundary: Offset = boundaryElement.tagName === 'BODY' ? getOffset(container, false) : getPosition(container, boundaryElement, false);
511511
if (container.tagName === 'HTML' && boundaryElement.tagName === 'BODY') {
512512
containerDimensions.scroll.top = 0;
513513
containerDimensions.scroll.left = 0;
@@ -533,8 +533,21 @@ export function calculatePosition(opts: PositionOpts): PositionResult {
533533
);
534534
}
535535

536-
function getOffset(node: Element): Offset {
536+
export function getRect(node: Element, ignoreScale: boolean) {
537537
let {top, left, width, height} = node.getBoundingClientRect();
538+
539+
// Use offsetWidth and offsetHeight if this is an HTML element, so that
540+
// the size is not affected by scale transforms.
541+
if (ignoreScale && node instanceof node.ownerDocument.defaultView!.HTMLElement) {
542+
width = node.offsetWidth;
543+
height = node.offsetHeight;
544+
}
545+
546+
return {top, left, width, height};
547+
}
548+
549+
function getOffset(node: Element, ignoreScale: boolean): Offset {
550+
let {top, left, width, height} = getRect(node, ignoreScale);
538551
let {scrollTop, scrollLeft, clientTop, clientLeft} = document.documentElement;
539552
return {
540553
top: top + scrollTop - clientTop,
@@ -544,15 +557,14 @@ function getOffset(node: Element): Offset {
544557
};
545558
}
546559

547-
function getPosition(node: Element, parent: Element): Offset {
560+
function getPosition(node: Element, parent: Element, ignoreScale: boolean): Offset {
548561
let style = window.getComputedStyle(node);
549562
let offset: Offset;
550563
if (style.position === 'fixed') {
551-
let {top, left, width, height} = node.getBoundingClientRect();
552-
offset = {top, left, width, height};
564+
offset = getRect(node, ignoreScale);
553565
} else {
554-
offset = getOffset(node);
555-
let parentOffset = getOffset(parent);
566+
offset = getOffset(node, ignoreScale);
567+
let parentOffset = getOffset(parent, ignoreScale);
556568
let parentStyle = window.getComputedStyle(parent);
557569
parentOffset.top += (parseInt(parentStyle.borderTopWidth, 10) || 0) - parent.scrollTop;
558570
parentOffset.left += (parseInt(parentStyle.borderLeftWidth, 10) || 0) - parent.scrollLeft;

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

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
* governing permissions and limitations under the License.
1111
*/
1212

13-
import {calculatePosition, PositionResult} from './calculatePosition';
13+
import {calculatePosition, getRect, PositionResult} from './calculatePosition';
1414
import {DOMAttributes, RefObject} from '@react-types/shared';
1515
import {Placement, PlacementAxis, PositionProps} from '@react-types/overlays';
1616
import {useCallback, useEffect, useRef, useState} from 'react';
@@ -140,7 +140,6 @@ export function useOverlayPosition(props: AriaPositionProps): PositionAria {
140140
}
141141
}, [isOpen]);
142142

143-
let hasPositioned = useRef(false);
144143
let updatePosition = useCallback(() => {
145144
if (shouldUpdatePosition === false || !isOpen || !overlayRef.current || !targetRef.current || !boundaryElement) {
146145
return;
@@ -150,6 +149,7 @@ export function useOverlayPosition(props: AriaPositionProps): PositionAria {
150149
return;
151150
}
152151

152+
<<<<<<< HEAD
153153
// // Delay updating the position until children are finished rendering (e.g. collections).
154154
// if (overlayRef.current.querySelector('[data-react-aria-incomplete]')) {
155155
// return;
@@ -174,6 +174,8 @@ export function useOverlayPosition(props: AriaPositionProps): PositionAria {
174174
}
175175
}
176176

177+
=======
178+
>>>>>>> a99e47697ef287e22f6ef368094a11e4a9c1a7ff
177179
// Determine a scroll anchor based on the focused element.
178180
// This stores the offset of the anchor element from the scroll container
179181
// so it can be restored after repositioning. This way if the overlay height
@@ -214,15 +216,10 @@ export function useOverlayPosition(props: AriaPositionProps): PositionAria {
214216
offset,
215217
crossOffset,
216218
maxHeight,
217-
arrowSize: arrowSize ?? arrowRef?.current?.getBoundingClientRect().width ?? 0,
219+
arrowSize: arrowSize ?? (arrowRef?.current ? getRect(arrowRef.current, true).width : 0),
218220
arrowBoundaryOffset
219221
});
220222

221-
for (let [anim, time] of savedAnimations) {
222-
anim.currentTime = time;
223-
anim.play();
224-
}
225-
226223
if (!position.position) {
227224
return;
228225
}
@@ -247,7 +244,6 @@ export function useOverlayPosition(props: AriaPositionProps): PositionAria {
247244
// debugger
248245
// Trigger a set state for a second render anyway for arrow positioning
249246
setPosition(position);
250-
hasPositioned.current = true;
251247
// eslint-disable-next-line react-hooks/exhaustive-deps
252248
}, deps);
253249

packages/@react-aria/overlays/test/calculatePosition.test.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ function createElementWithDimensions(elemName, dimensions, margins = {}) {
6666
bottom: dimensions.bottom || 0
6767
});
6868

69+
jest.spyOn(elem, 'offsetWidth', 'get').mockImplementation(() => dimensions.width || 0);
70+
jest.spyOn(elem, 'offsetHeight', 'get').mockImplementation(() => dimensions.height || 0);
71+
6972
jest.spyOn(elem, 'scrollWidth', 'get').mockImplementation(() => dimensions.width || 0);
7073
jest.spyOn(elem, 'scrollHeight', 'get').mockImplementation(() => dimensions.height || 0);
7174

packages/@react-aria/overlays/test/useOverlayPosition.test.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,13 @@ describe('useOverlayPosition', function () {
5151
beforeEach(() => {
5252
Object.defineProperty(HTMLElement.prototype, 'clientHeight', {configurable: true, value: 768});
5353
Object.defineProperty(HTMLElement.prototype, 'clientWidth', {configurable: true, value: 500});
54+
55+
jest.spyOn(HTMLElement.prototype, 'offsetWidth', 'get').mockImplementation(function (this: HTMLElement) {
56+
return parseInt(this.style.width, 10) || 0;
57+
});
58+
jest.spyOn(HTMLElement.prototype, 'offsetHeight', 'get').mockImplementation(function (this: HTMLElement) {
59+
return parseInt(this.style.height, 10) || 0;
60+
});
5461
});
5562

5663
it('should position the overlay relative to the trigger', function () {

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,6 @@ 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}
245244
onScroll={props.onScroll}>
246245
<Provider
247246
values={[

0 commit comments

Comments
 (0)