+
+ {renderNode({ placement: 'top' })}
+
+
+ {renderNode({ placement: 'right' })}
+
+
+ {renderNode({ placement: 'top' })}
+
+
+ {renderNode({ placement: 'left' })}
+
+
+ );
+};
+
+export default Demo;
diff --git a/src/hooks/useAlign.ts b/src/hooks/useAlign.ts
index d4cc296b..e88344da 100644
--- a/src/hooks/useAlign.ts
+++ b/src/hooks/useAlign.ts
@@ -87,6 +87,52 @@ function reversePoints(points: Points, index: number): string {
.join('');
}
+const EDGE_OFFSET = 6
+
+const calcOffsetByVisibleArea = (params: {
+ offset: number;
+ direction: 'horizontal' | 'vertical',
+ widthOrHeight: number;
+ visibleRegionArea: {
+ left: number;
+ right: number;
+ bottom: number;
+ top: number;
+ }
+}) => {
+ let arrowOffset = 0
+ const { offset, widthOrHeight, direction, visibleRegionArea } = params
+ /** horizontal offset < 0 = l otherwise = r */
+
+ if (offset < 0) {
+ return {
+ offset: 0,
+ arrowOffset: direction === 'horizontal' ? EDGE_OFFSET : EDGE_OFFSET,
+ }
+ }
+ /**
+ * when nextOffsetX add popupElementWidth is large than visibleRegionArea
+ * we should calculate arrowOffset, and set nextOffsetX to visibleRegionArea.right - width
+ */
+ if (direction === 'horizontal' && (offset + widthOrHeight) > visibleRegionArea.right) {
+ return {
+ offset: visibleRegionArea.right - widthOrHeight,
+ arrowOffset: -EDGE_OFFSET
+ }
+ }
+ if (direction === 'vertical' && (offset + widthOrHeight) > visibleRegionArea.bottom) {
+ return {
+ offset: visibleRegionArea.bottom - widthOrHeight,
+ arrowOffset: -EDGE_OFFSET
+ }
+ }
+
+ return {
+ offset,
+ arrowOffset,
+ }
+}
+
export default function useAlign(
open: boolean,
popupEle: HTMLElement,
@@ -645,6 +691,21 @@ export default function useAlign(
// ============================ Arrow ============================
// Arrow center align
+ const { offset: offsetX, arrowOffset: arrowXOffset } = calcOffsetByVisibleArea({
+ offset: nextOffsetX,
+ direction: 'horizontal',
+ widthOrHeight: popupWidth,
+ visibleRegionArea,
+ })
+ const { offset: offsetY, arrowOffset: arrowYOffset } = calcOffsetByVisibleArea({
+ offset: nextOffsetY,
+ direction: 'vertical',
+ widthOrHeight: popupHeight,
+ visibleRegionArea,
+ });
+ nextOffsetX = offsetX
+ nextOffsetY = offsetY
+
const popupLeft = popupRect.x + nextOffsetX;
const popupRight = popupLeft + popupWidth;
const popupTop = popupRect.y + nextOffsetY;
@@ -659,13 +720,13 @@ export default function useAlign(
const minRight = Math.min(popupRight, targetRight);
const xCenter = (maxLeft + minRight) / 2;
- const nextArrowX = xCenter - popupLeft;
+ const nextArrowX = xCenter - popupLeft + arrowXOffset;
const maxTop = Math.max(popupTop, targetTop);
const minBottom = Math.min(popupBottom, targetBottom);
const yCenter = (maxTop + minBottom) / 2;
- const nextArrowY = yCenter - popupTop;
+ const nextArrowY = yCenter - popupTop + arrowYOffset;
onPopupAlign?.(popupEle, nextAlignInfo);
From 54208ba7bc39d2b874d7187adfda3afc12e99e48 Mon Sep 17 00:00:00 2001
From: Zhou-Bill <735051883@qq.com>
Date: Tue, 5 Dec 2023 10:17:35 +0800
Subject: [PATCH 2/2] =?UTF-8?q?fix:=20=E4=BF=AE=E6=94=B9=E9=80=BB=E8=BE=91?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/hooks/useAlign.ts | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/src/hooks/useAlign.ts b/src/hooks/useAlign.ts
index e88344da..a604d99c 100644
--- a/src/hooks/useAlign.ts
+++ b/src/hooks/useAlign.ts
@@ -102,12 +102,16 @@ const calcOffsetByVisibleArea = (params: {
}) => {
let arrowOffset = 0
const { offset, widthOrHeight, direction, visibleRegionArea } = params
- /** horizontal offset < 0 = l otherwise = r */
-
+
+ /**
+ * When offset is less than 0, it means that it is blocked by the visible area.
+ * eg left: -10, it means that the left side of the popup is blocked by the visible area.
+ * or top: -10, it means that the top side of the popup is blocked by the visible area.
+ * */
if (offset < 0) {
return {
offset: 0,
- arrowOffset: direction === 'horizontal' ? EDGE_OFFSET : EDGE_OFFSET,
+ arrowOffset: EDGE_OFFSET,
}
}
/**