From 355e50d8b82a0d6eb83c730189e8bb39760e4774 Mon Sep 17 00:00:00 2001 From: Zhou-Bill <735051883@qq.com> Date: Mon, 4 Dec 2023 22:16:56 +0800 Subject: [PATCH 1/2] =?UTF-8?q?fix:=20=E6=BA=A2=E5=87=BA=E5=86=85=E5=AE=B9?= =?UTF-8?q?bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/demos/edge-popup.md | 8 +++ docs/examples/edge-popup.tsx | 121 +++++++++++++++++++++++++++++++++++ src/hooks/useAlign.ts | 65 ++++++++++++++++++- 3 files changed, 192 insertions(+), 2 deletions(-) create mode 100644 docs/demos/edge-popup.md create mode 100644 docs/examples/edge-popup.tsx diff --git a/docs/demos/edge-popup.md b/docs/demos/edge-popup.md new file mode 100644 index 00000000..1ea4e6fa --- /dev/null +++ b/docs/demos/edge-popup.md @@ -0,0 +1,8 @@ +--- +title: edge-popup +nav: + title: Demo + path: /demo +--- + + \ No newline at end of file diff --git a/docs/examples/edge-popup.tsx b/docs/examples/edge-popup.tsx new file mode 100644 index 00000000..1f146e9e --- /dev/null +++ b/docs/examples/edge-popup.tsx @@ -0,0 +1,121 @@ +/** + * iframe: true + */ + +import React from 'react'; +import type { CSSMotionProps } from 'rc-motion'; +import type { BuildInPlacements, TriggerProps } from 'rc-trigger'; +import Trigger from 'rc-trigger'; +import './case.less'; + +const builtinPlacements: BuildInPlacements = { + left: { + points: ['cr', 'cl'], + }, + right: { + points: ['cl', 'cr'], + }, + top: { + points: ['bc', 'tc'], + }, + bottom: { + points: ['tc', 'bc'], + }, + topLeft: { + points: ['bl', 'tl'], + }, + topRight: { + points: ['br', 'tr'], + }, + bottomRight: { + points: ['tr', 'br'], + }, + bottomLeft: { + points: ['tl', 'bl'], + }, +}; + +const Motion: CSSMotionProps = { + motionName: 'case-motion', +}; + +const MaskMotion: CSSMotionProps = { + motionName: 'mask-motion', +}; + +function useControl(valuePropName: string, defaultValue: T): [T, any] { + const [value, setValue] = React.useState(defaultValue); + + return [ + value, + { + value, + checked: value, + onChange({ target }) { + setValue(target[valuePropName]); + }, + }, + ]; +} + +const Demo = () => { + const renderNode = (params: { placement?: TriggerProps['popupPlacement'] }) => { + const { placement = 'top' } = params; + return ( + 3000} + popupMotion={null} + onPopupAlign={() => { + console.warn('Aligned!'); + }} + > + + T + + + ) + } + + return ( + +
+ {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, } } /**