diff --git a/src/hooks/useAlign.ts b/src/hooks/useAlign.ts
index 679d0560..7e7839dd 100644
--- a/src/hooks/useAlign.ts
+++ b/src/hooks/useAlign.ts
@@ -189,7 +189,6 @@ export default function useAlign(
const originOverflowX = popupElement.style.overflowX;
const originOverflowY = popupElement.style.overflowY;
-
// Placement
const placementInfo: AlignType = {
...builtinPlacements[placement],
@@ -666,6 +665,19 @@ export default function useAlign(
nextOffsetY += targetRect.y - visibleRegionArea.bottom + numShiftY;
}
}
+
+ // 目标仍在平移范围内时,避免方向偏移量将弹层再次推到视口外。
+ if (
+ targetRect.y + targetHeight >= visibleRegionArea.top + numShiftY &&
+ targetRect.y <= visibleRegionArea.bottom - numShiftY
+ ) {
+ const minOffsetY = visibleRegionArea.top - popupRect.y;
+ const maxOffsetY = Math.max(
+ minOffsetY,
+ visibleRegionArea.bottom - popupHeight - popupRect.y,
+ );
+ nextOffsetY = Math.min(Math.max(nextOffsetY, minOffsetY), maxOffsetY);
+ }
}
// ============================ Arrow ============================
diff --git a/tests/flipShift.test.tsx b/tests/flipShift.test.tsx
index 57e88e77..668a5953 100644
--- a/tests/flipShift.test.tsx
+++ b/tests/flipShift.test.tsx
@@ -80,8 +80,10 @@ const builtinPlacements = {
describe('Trigger.Flip+Shift', () => {
let spanRect = { x: 0, y: 0, left: 0, top: 0, width: 0, height: 0 };
+ let popupHeight = 300;
beforeEach(() => {
+ popupHeight = 300;
spanRect = {
x: 0,
y: 100,
@@ -118,7 +120,7 @@ describe('Trigger.Flip+Shift', () => {
left: 0,
top: 0,
width: 100,
- height: 300,
+ height: popupHeight,
};
},
});
@@ -170,6 +172,59 @@ describe('Trigger.Flip+Shift', () => {
});
});
+ it.each([
+ { placement: 'top', offset: -4, targetY: 100, top: '100px' },
+ { placement: 'bottom', offset: 4, targetY: 100, top: '100px' },
+ { placement: 'top', offset: -4, targetY: 200, top: '0px' },
+ { placement: 'bottom', offset: 4, targetY: 200, top: '0px' },
+ ])(
+ '$placement 在目标位于 $targetY 时,平移不应被偏移量推回视口外',
+ async ({ placement, offset, targetY, top }) => {
+ spanRect.y = targetY;
+ spanRect.top = targetY;
+
+ render(
+ 日期面板}
+ >
+
+ ,
+ );
+
+ await act(async () => {
+ await Promise.resolve();
+ });
+
+ expect(document.querySelector('.rc-trigger-popup')).toHaveStyle({ top });
+ },
+ );
+
+ it('面板高于视口时优先保留顶部内容', async () => {
+ popupHeight = 500;
+ render(
+ 日期面板}
+ >
+
+ ,
+ );
+
+ await act(async () => {
+ await Promise.resolve();
+ });
+
+ expect(document.querySelector('.rc-trigger-popup')).toHaveStyle({
+ top: '0px',
+ });
+ });
+
it('top with visibleFirst region', async () => {
spanRect.x = -1000;
document.documentElement.scrollLeft = 500;