Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 19 additions & 19 deletions packages/@react-aria/overlays/src/calculatePosition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ interface Offset {
}

interface PositionOpts {
arrowCrossSize: number,
arrowSize: number,
placement: Placement,
targetNode: Element,
overlayNode: Element,
Expand All @@ -56,7 +56,7 @@ interface PositionOpts {
offset: number,
crossOffset: number,
maxHeight?: number,
minOverlayArrowOffset?: number
arrowBoundaryOffset?: number
}

export interface PositionResult {
Expand Down Expand Up @@ -185,8 +185,8 @@ function computePosition(
crossOffset: number,
containerOffsetWithBoundary: Offset,
isContainerPositioned: boolean,
arrowCrossSize: number,
minOverlayArrowOffset: number
arrowSize: number,
arrowBoundaryOffset: number
) {
let {placement, crossPlacement, axis, crossAxis, size, crossSize} = placementInfo;
let position: Position = {};
Expand All @@ -208,9 +208,9 @@ function computePosition(
position[crossAxis] += crossOffset;

// overlay top overlapping arrow with button bottom
const minPosition = childOffset[crossAxis] - overlaySize[crossSize] + arrowCrossSize + minOverlayArrowOffset;
const minPosition = childOffset[crossAxis] - overlaySize[crossSize] + arrowSize + arrowBoundaryOffset;
// overlay bottom overlapping arrow with button top
const maxPosition = childOffset[crossAxis] + childOffset[crossSize] - arrowCrossSize - minOverlayArrowOffset;
const maxPosition = childOffset[crossAxis] + childOffset[crossSize] - arrowSize - arrowBoundaryOffset;
position[crossAxis] = clamp(position[crossAxis], minPosition, maxPosition);

// Floor these so the position isn't placed on a partial pixel, only whole pixels. Shouldn't matter if it was floored or ceiled, so chose one.
Expand Down Expand Up @@ -281,12 +281,12 @@ export function calculatePositionInternal(
crossOffset: number,
isContainerPositioned: boolean,
userSetMaxHeight: number | undefined,
arrowCrossSize: number,
minOverlayArrowOffset: number
arrowSize: number,
arrowBoundaryOffset: number
): PositionResult {
let placementInfo = parsePlacement(placementInput);
let {size, crossAxis, crossSize, placement, crossPlacement} = placementInfo;
let position = computePosition(childOffset, boundaryDimensions, overlaySize, placementInfo, offset, crossOffset, containerOffsetWithBoundary, isContainerPositioned, arrowCrossSize, minOverlayArrowOffset);
let position = computePosition(childOffset, boundaryDimensions, overlaySize, placementInfo, offset, crossOffset, containerOffsetWithBoundary, isContainerPositioned, arrowSize, arrowBoundaryOffset);
let normalizedOffset = offset;
let space = getAvailableSpace(
boundaryDimensions,
Expand All @@ -300,7 +300,7 @@ export function calculatePositionInternal(
// Check if the scroll size of the overlay is greater than the available space to determine if we need to flip
if (flip && scrollSize[size] > space) {
let flippedPlacementInfo = parsePlacement(`${FLIPPED_DIRECTION[placement]} ${crossPlacement}` as Placement);
let flippedPosition = computePosition(childOffset, boundaryDimensions, overlaySize, flippedPlacementInfo, offset, crossOffset, containerOffsetWithBoundary, isContainerPositioned, arrowCrossSize, minOverlayArrowOffset);
let flippedPosition = computePosition(childOffset, boundaryDimensions, overlaySize, flippedPlacementInfo, offset, crossOffset, containerOffsetWithBoundary, isContainerPositioned, arrowSize, arrowBoundaryOffset);
let flippedSpace = getAvailableSpace(
boundaryDimensions,
containerOffsetWithBoundary,
Expand Down Expand Up @@ -336,7 +336,7 @@ export function calculatePositionInternal(

overlaySize.height = Math.min(overlaySize.height, maxHeight);

position = computePosition(childOffset, boundaryDimensions, overlaySize, placementInfo, normalizedOffset, crossOffset, containerOffsetWithBoundary, isContainerPositioned, arrowCrossSize, minOverlayArrowOffset);
position = computePosition(childOffset, boundaryDimensions, overlaySize, placementInfo, normalizedOffset, crossOffset, containerOffsetWithBoundary, isContainerPositioned, arrowSize, arrowBoundaryOffset);
delta = getDelta(crossAxis, position[crossAxis], overlaySize[crossSize], boundaryDimensions, padding);
position[crossAxis] += delta;

Expand All @@ -347,12 +347,12 @@ export function calculatePositionInternal(
let preferredArrowPosition = childOffset[crossAxis] + .5 * childOffset[crossSize] - overlaySize[crossAxis];

// Min/Max position limits for the arrow with respect to the overlay
const arrowMinPosition = arrowCrossSize / 2 + minOverlayArrowOffset;
const arrowMaxPosition = overlaySize[crossSize] - (arrowCrossSize / 2) - minOverlayArrowOffset;
const arrowMinPosition = arrowSize / 2 + arrowBoundaryOffset;
const arrowMaxPosition = overlaySize[crossSize] - (arrowSize / 2) - arrowBoundaryOffset;

// Min/Max position limits for the arrow with respect to the trigger/overlay anchor element
const arrowOverlappingChildMinEdge = childOffset[crossAxis] - overlaySize[crossAxis] + (arrowCrossSize / 2);
const arrowOverlappingChildMaxEdge = childOffset[crossAxis] + childOffset[crossSize] - overlaySize[crossAxis] - (arrowCrossSize / 2);
const arrowOverlappingChildMinEdge = childOffset[crossAxis] - overlaySize[crossAxis] + (arrowSize / 2);
const arrowOverlappingChildMaxEdge = childOffset[crossAxis] + childOffset[crossSize] - overlaySize[crossAxis] - (arrowSize / 2);

// Clamp the arrow positioning so that it always is within the bounds of the anchor and the overlay
const arrowPositionOverlappingChild = clamp(preferredArrowPosition, arrowOverlappingChildMinEdge, arrowOverlappingChildMaxEdge);
Expand Down Expand Up @@ -382,8 +382,8 @@ export function calculatePosition(opts: PositionOpts): PositionResult {
offset,
crossOffset,
maxHeight,
arrowCrossSize,
minOverlayArrowOffset = 0
arrowSize,
arrowBoundaryOffset = 0
} = opts;

let container = ((overlayNode instanceof HTMLElement && overlayNode.offsetParent) || document.body) as Element;
Expand Down Expand Up @@ -421,8 +421,8 @@ export function calculatePosition(opts: PositionOpts): PositionResult {
crossOffset,
isContainerPositioned,
maxHeight,
arrowCrossSize,
minOverlayArrowOffset
arrowSize,
arrowBoundaryOffset
);
}

Expand Down
15 changes: 8 additions & 7 deletions packages/@react-aria/overlays/src/useOverlayPosition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export interface AriaPositionProps extends PositionProps {
* Cross size of the overlay arrow in pixels.
* @default 0
*/
arrowCrossSize?: number,
arrowSize?: number,
/**
* Element that that serves as the positioning boundary.
* @default document.body
Expand Down Expand Up @@ -58,7 +58,7 @@ export interface AriaPositionProps extends PositionProps {
* The minimum distance the arrow's edge should be from the edge of the overlay element.
* @default 0
*/
minOverlayArrowOffset?: number
arrowBoundaryOffset?: number
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if we need to update the doc comments at all

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like a fine description to me, i vote for keeping as is

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I considered updating the comment for the arrowSize but decided against it since other libraries (floatingUI) use the same kind of "cross" vocab

}

export interface PositionAria {
Expand All @@ -82,7 +82,7 @@ let visualViewport = typeof window !== 'undefined' && window.visualViewport;
export function useOverlayPosition(props: AriaPositionProps): PositionAria {
let {direction} = useLocale();
let {
arrowCrossSize = 0,
arrowSize = 0,
targetRef,
overlayRef,
scrollRef = overlayRef,
Expand All @@ -96,7 +96,7 @@ export function useOverlayPosition(props: AriaPositionProps): PositionAria {
isOpen = true,
onClose,
maxHeight,
minOverlayArrowOffset = 0
arrowBoundaryOffset = 0
} = props;
let [position, setPosition] = useState<PositionResult>({
position: {},
Expand All @@ -120,7 +120,8 @@ export function useOverlayPosition(props: AriaPositionProps): PositionAria {
isOpen,
direction,
maxHeight,
minOverlayArrowOffset
arrowBoundaryOffset,
arrowSize
];

let updatePosition = useCallback(() => {
Expand All @@ -139,8 +140,8 @@ export function useOverlayPosition(props: AriaPositionProps): PositionAria {
offset,
crossOffset,
maxHeight,
arrowCrossSize,
minOverlayArrowOffset
arrowSize,
arrowBoundaryOffset
});

// Modify overlay styles directly so positioning happens immediately without the need of a second render
Expand Down
38 changes: 19 additions & 19 deletions packages/@react-aria/overlays/test/calculatePosition.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ const overlaySize = {
const PROVIDER_OFFSET = 50;

describe('calculatePosition', function () {
function checkPositionCommon(title, expected, placement, targetDimension, boundaryDimensions, offset, crossOffset, flip, providerOffset = 0, arrowCrossSize = 8, minOverlayArrowOffset = 0) {
function checkPositionCommon(title, expected, placement, targetDimension, boundaryDimensions, offset, crossOffset, flip, providerOffset = 0, arrowSize = 8, arrowBoundaryOffset = 0) {
const placementAxis = placement.split(' ')[0];

// The tests are all based on top/left positioning. Convert to bottom/right positioning if needed.
Expand Down Expand Up @@ -155,16 +155,16 @@ describe('calculatePosition', function () {
boundaryElement: boundariesElem,
offset,
crossOffset,
arrowCrossSize,
minOverlayArrowOffset
arrowSize,
arrowBoundaryOffset
});

expect(result).toEqual(expectedPosition);
document.documentElement.removeChild(parentElement);
});
}

function checkPosition(placement, targetDimension, expected, offset = 0, crossOffset = 0, flip = false, providerOffset, arrowCrossSize, minOverlayArrowOffset) {
function checkPosition(placement, targetDimension, expected, offset = 0, crossOffset = 0, flip = false, providerOffset, arrowSize, arrowBoundaryOffset) {
checkPositionCommon(
'Should calculate the correct position',
expected,
Expand All @@ -175,8 +175,8 @@ describe('calculatePosition', function () {
crossOffset,
flip,
providerOffset,
arrowCrossSize,
minOverlayArrowOffset
arrowSize,
arrowBoundaryOffset
);
}

Expand All @@ -203,7 +203,7 @@ describe('calculatePosition', function () {
crossAxisOffsetPositive: [50, 210, undefined, 196, 340],
crossAxisOffsetNegative: [50, 190, undefined, 196, 360],
mainAxisOffset: [40, 200, undefined, 196, 350],
minOverlayArrowOffset: [50, 322, undefined, 176, 228]
arrowBoundaryOffset: [50, 322, undefined, 176, 228]
},
{
placement: 'left top',
Expand All @@ -213,7 +213,7 @@ describe('calculatePosition', function () {
crossAxisOffsetPositive: [50, 260, undefined, 196, 290],
crossAxisOffsetNegative: [50, 240, undefined, 196, 310],
mainAxisOffset: [40, 250, undefined, 196, 300],
minOverlayArrowOffset: [50, 322, undefined, 176, 228]
arrowBoundaryOffset: [50, 322, undefined, 176, 228]
},
{
placement: 'left bottom',
Expand All @@ -223,7 +223,7 @@ describe('calculatePosition', function () {
crossAxisOffsetPositive: [50, 160, undefined, 196, 390],
crossAxisOffsetNegative: [50, 140, undefined, 196, 410],
mainAxisOffset: [40, 150, undefined, 196, 400],
minOverlayArrowOffset: [50, 322, undefined, 176, 228]
arrowBoundaryOffset: [50, 322, undefined, 176, 228]
},
{
placement: 'top',
Expand All @@ -233,7 +233,7 @@ describe('calculatePosition', function () {
crossAxisOffsetPositive: [210, 50, 196, undefined, 200],
crossAxisOffsetNegative: [190, 50, 196, undefined, 200],
mainAxisOffset: [200, 40, 196, undefined, 200],
minOverlayArrowOffset: [322, 50, 176, undefined, 200]
arrowBoundaryOffset: [322, 50, 176, undefined, 200]
},
{
placement: 'top left',
Expand All @@ -243,7 +243,7 @@ describe('calculatePosition', function () {
crossAxisOffsetPositive: [260, 50, 196, undefined, 200],
crossAxisOffsetNegative: [240, 50, 196, undefined, 200],
mainAxisOffset: [250, 40, 196, undefined, 200],
minOverlayArrowOffset: [322, 50, 176, undefined, 200]
arrowBoundaryOffset: [322, 50, 176, undefined, 200]
},
{
placement: 'top right',
Expand All @@ -253,7 +253,7 @@ describe('calculatePosition', function () {
crossAxisOffsetPositive: [160, 50, 196, undefined, 200],
crossAxisOffsetNegative: [140, 50, 196, undefined, 200],
mainAxisOffset: [150, 40, 196, undefined, 200],
minOverlayArrowOffset: [322, 50, 176, undefined, 200]
arrowBoundaryOffset: [322, 50, 176, undefined, 200]
},
{
placement: 'bottom',
Expand All @@ -263,7 +263,7 @@ describe('calculatePosition', function () {
crossAxisOffsetPositive: [210, 350, 196, undefined, 200],
crossAxisOffsetNegative: [190, 350, 196, undefined, 200],
mainAxisOffset: [200, 360, 196, undefined, 190],
minOverlayArrowOffset: [322, 350, 176, undefined, 200]
arrowBoundaryOffset: [322, 350, 176, undefined, 200]
},
{
placement: 'bottom left',
Expand All @@ -273,7 +273,7 @@ describe('calculatePosition', function () {
crossAxisOffsetPositive: [260, 350, 196, undefined, 200],
crossAxisOffsetNegative: [240, 350, 196, undefined, 200],
mainAxisOffset: [250, 360, 196, undefined, 190],
minOverlayArrowOffset: [322, 350, 176, undefined, 200]
arrowBoundaryOffset: [322, 350, 176, undefined, 200]
},
{
placement: 'bottom right',
Expand All @@ -283,7 +283,7 @@ describe('calculatePosition', function () {
crossAxisOffsetPositive: [160, 350, 196, undefined, 200],
crossAxisOffsetNegative: [140, 350, 196, undefined, 200],
mainAxisOffset: [150, 360, 196, undefined, 190],
minOverlayArrowOffset: [322, 350, 176, undefined, 200]
arrowBoundaryOffset: [322, 350, 176, undefined, 200]
},
{
placement: 'right',
Expand All @@ -293,7 +293,7 @@ describe('calculatePosition', function () {
crossAxisOffsetPositive: [350, 210, undefined, 196, 340],
crossAxisOffsetNegative: [350, 190, undefined, 196, 360],
mainAxisOffset: [360, 200, undefined, 196, 350],
minOverlayArrowOffset: [350, 322, undefined, 176, 228]
arrowBoundaryOffset: [350, 322, undefined, 176, 228]
},
{
placement: 'right top',
Expand All @@ -303,7 +303,7 @@ describe('calculatePosition', function () {
crossAxisOffsetPositive: [350, 260, undefined, 196, 290],
crossAxisOffsetNegative: [350, 240, undefined, 196, 310],
mainAxisOffset: [360, 250, undefined, 196, 300],
minOverlayArrowOffset: [350, 322, undefined, 176, 228]
arrowBoundaryOffset: [350, 322, undefined, 176, 228]
},
{
placement: 'right bottom',
Expand All @@ -313,7 +313,7 @@ describe('calculatePosition', function () {
crossAxisOffsetPositive: [350, 160, undefined, 196, 390],
crossAxisOffsetNegative: [350, 140, undefined, 196, 410],
mainAxisOffset: [360, 150, undefined, 196, 400],
minOverlayArrowOffset: [350, 322, undefined, 176, 228]
arrowBoundaryOffset: [350, 322, undefined, 176, 228]
}
];

Expand Down Expand Up @@ -365,7 +365,7 @@ describe('calculatePosition', function () {

describe('minimum overlay arrow offset', function () {
checkPosition(
placement, getTargetDimension({left: 250, top: 250}), testCase.minOverlayArrowOffset, 0, 1000, false, undefined, undefined, 20
placement, getTargetDimension({left: 250, top: 250}), testCase.arrowBoundaryOffset, 0, 1000, false, undefined, undefined, 20
);
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ function Example({triggerTop = 250, ...props}) {
let targetRef = useRef();
let containerRef = useRef();
let overlayRef = useRef();
let {overlayProps, placement, arrowProps} = useOverlayPosition({targetRef, containerRef, overlayRef, arrowCrossSize: 8, ...props});
let {overlayProps, placement, arrowProps} = useOverlayPosition({targetRef, containerRef, overlayRef, arrowSize: 8, ...props});
let style = {width: 300, height: 200, ...overlayProps.style};
return (
<React.Fragment>
Expand Down
6 changes: 3 additions & 3 deletions packages/@react-spectrum/overlays/src/Popover.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ let arrowPlacement = {
right: 'right',
top: 'bottom',
bottom: 'bottom'
} as const;
};

function Popover(props: PopoverProps, ref: DOMRef<HTMLDivElement>) {
let {
Expand Down Expand Up @@ -93,8 +93,8 @@ const PopoverWrapper = forwardRef((props: PopoverWrapperProps, ref: RefObject<HT
...props,
popoverRef: ref,
maxHeight: null,
arrowCrossSize: hideArrow ? 0 : secondary,
minOverlayArrowOffset: borderRadius
arrowSize: hideArrow ? 0 : secondary,
arrowBoundaryOffset: borderRadius
}, state);

// Attach Transition's nodeRef to outermost wrapper for node.reflow: https://github.com/reactjs/react-transition-group/blob/c89f807067b32eea6f68fd6c622190d88ced82e2/src/Transition.js#L231
Expand Down
4 changes: 2 additions & 2 deletions packages/@react-spectrum/tooltip/src/TooltipTrigger.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@ function TooltipTrigger(props: SpectrumTooltipTriggerProps) {
isOpen: state.isOpen,
shouldFlip: props.shouldFlip,
containerPadding: props.containerPadding,
arrowCrossSize: arrowWidth,
minOverlayArrowOffset: borderRadius
arrowSize: arrowWidth,
arrowBoundaryOffset: borderRadius
});

return (
Expand Down