@@ -7,7 +7,7 @@ import type {
77 AlignPointTopBottom ,
88 AlignType ,
99} from '../interface' ;
10- import { getWin } from '../util' ;
10+ import { collectScroller , getWin } from '../util' ;
1111
1212type Rect = Record < 'x' | 'y' | 'width' | 'height' , number > ;
1313
@@ -107,6 +107,15 @@ export default function useAlign(
107107 } ) ;
108108 const alignCountRef = React . useRef ( 0 ) ;
109109
110+ const scrollerList = React . useMemo ( ( ) => {
111+ if ( ! popupEle ) {
112+ return [ ] ;
113+ }
114+
115+ return collectScroller ( popupEle ) ;
116+ } , [ popupEle ] ) ;
117+
118+ // ========================= Align =========================
110119 const onAlign = useEvent ( ( ) => {
111120 if ( popupEle && target && open ) {
112121 const popupElement = popupEle ;
@@ -146,6 +155,41 @@ export default function useAlign(
146155 const popupHeight = popupRect . height ;
147156 const popupWidth = popupRect . width ;
148157
158+ // Get bounding of visible area
159+ const visibleArea = {
160+ left : 0 ,
161+ top : 0 ,
162+ right : clientWidth ,
163+ bottom : clientHeight ,
164+ } ;
165+
166+ ( scrollerList || [ ] ) . forEach ( ( ele ) => {
167+ const eleRect = ele . getBoundingClientRect ( ) ;
168+ const {
169+ offsetHeight : eleOutHeight ,
170+ clientHeight : eleInnerHeight ,
171+ offsetWidth : eleOutWidth ,
172+ clientWidth : eleInnerWidth ,
173+ } = ele ;
174+
175+ const scaleX = toNum (
176+ Math . round ( ( eleRect . width / eleOutWidth ) * 1000 ) / 1000 ,
177+ ) ;
178+ const scaleY = toNum (
179+ Math . round ( ( eleRect . height / eleOutHeight ) * 1000 ) / 1000 ,
180+ ) ;
181+
182+ const scrollWidth = ( eleOutWidth - eleInnerWidth ) * scaleX ;
183+ const scrollHeight = ( eleOutHeight - eleInnerHeight ) * scaleY ;
184+ const eleRight = eleRect . x + eleRect . width - scrollWidth ;
185+ const eleBottom = eleRect . y + eleRect . height - scrollHeight ;
186+
187+ visibleArea . left = Math . max ( visibleArea . left , eleRect . left ) ;
188+ visibleArea . top = Math . max ( visibleArea . top , eleRect . top ) ;
189+ visibleArea . right = Math . min ( visibleArea . right , eleRight ) ;
190+ visibleArea . bottom = Math . min ( visibleArea . bottom , eleBottom ) ;
191+ } ) ;
192+
149193 // Reset back
150194 popupElement . style . left = originLeft ;
151195 popupElement . style . top = originTop ;
@@ -206,7 +250,7 @@ export default function useAlign(
206250 if (
207251 needAdjustY &&
208252 popupPoints [ 0 ] === 't' &&
209- nextPopupBottom > clientHeight
253+ nextPopupBottom > visibleArea . bottom
210254 ) {
211255 nextOffsetY = targetAlignPointTL . y - popupAlignPointBR . y - popupOffsetY ;
212256
@@ -217,7 +261,11 @@ export default function useAlign(
217261 }
218262
219263 // Top to Bottom
220- if ( needAdjustY && popupPoints [ 0 ] === 'b' && nextPopupY < 0 ) {
264+ if (
265+ needAdjustY &&
266+ popupPoints [ 0 ] === 'b' &&
267+ nextPopupY < visibleArea . top
268+ ) {
221269 nextOffsetY = targetAlignPointBR . y - popupAlignPointTL . y - popupOffsetY ;
222270
223271 nextAlignInfo . points = [
@@ -237,7 +285,7 @@ export default function useAlign(
237285 if (
238286 needAdjustX &&
239287 popupPoints [ 1 ] === 'l' &&
240- nextPopupRight > clientWidth
288+ nextPopupRight > visibleArea . right
241289 ) {
242290 nextOffsetX = targetAlignPointTL . x - popupAlignPointBR . x - popupOffsetX ;
243291
@@ -248,7 +296,11 @@ export default function useAlign(
248296 }
249297
250298 // Left to Right
251- if ( needAdjustX && popupPoints [ 1 ] === 'r' && nextPopupX < 0 ) {
299+ if (
300+ needAdjustX &&
301+ popupPoints [ 1 ] === 'r' &&
302+ nextPopupX < visibleArea . left
303+ ) {
252304 nextOffsetX = targetAlignPointBR . x - popupAlignPointTL . x - popupOffsetX ;
253305
254306 nextAlignInfo . points = [
@@ -261,41 +313,43 @@ export default function useAlign(
261313 const numShiftX = shiftX === true ? 0 : shiftX ;
262314 if ( typeof numShiftX === 'number' ) {
263315 // Left
264- if ( nextPopupX < 0 ) {
265- nextOffsetX -= nextPopupX ;
316+ if ( nextPopupX < visibleArea . left ) {
317+ nextOffsetX -= nextPopupX - visibleArea . left ;
266318
267- if ( targetRect . x + targetRect . width < numShiftX ) {
268- nextOffsetX += targetRect . x + targetRect . width - numShiftX ;
319+ if ( targetRect . x + targetRect . width < visibleArea . left + numShiftX ) {
320+ nextOffsetX +=
321+ targetRect . x - visibleArea . left + targetRect . width - numShiftX ;
269322 }
270323 }
271324
272325 // Right
273- if ( nextPopupRight > clientWidth ) {
274- nextOffsetX -= nextPopupRight - clientWidth ;
326+ if ( nextPopupRight > visibleArea . right ) {
327+ nextOffsetX -= nextPopupRight - visibleArea . right ;
275328
276- if ( targetRect . x > clientWidth - numShiftX ) {
277- nextOffsetX += targetRect . x - clientWidth + numShiftX ;
329+ if ( targetRect . x > visibleArea . right - numShiftX ) {
330+ nextOffsetX += targetRect . x - visibleArea . right + numShiftX ;
278331 }
279332 }
280333 }
281334
282335 const numShiftY = shiftY === true ? 0 : shiftY ;
283336 if ( typeof numShiftY === 'number' ) {
284337 // Top
285- if ( nextPopupY < 0 ) {
286- nextOffsetY -= nextPopupY ;
338+ if ( nextPopupY < visibleArea . top ) {
339+ nextOffsetY -= nextPopupY - visibleArea . top ;
287340
288- if ( targetRect . y + targetRect . height < numShiftY ) {
289- nextOffsetY += targetRect . y + targetRect . height - numShiftY ;
341+ if ( targetRect . y + targetRect . height < visibleArea . top + numShiftY ) {
342+ nextOffsetY +=
343+ targetRect . y - visibleArea . top + targetRect . height - numShiftY ;
290344 }
291345 }
292346
293347 // Bottom
294- if ( nextPopupBottom > clientHeight ) {
295- nextOffsetY -= nextPopupBottom - clientHeight ;
348+ if ( nextPopupBottom > visibleArea . bottom ) {
349+ nextOffsetY -= nextPopupBottom - visibleArea . bottom ;
296350
297- if ( targetRect . y > clientHeight - numShiftY ) {
298- nextOffsetY += targetRect . y - clientHeight + numShiftY ;
351+ if ( targetRect . y > visibleArea . bottom - numShiftY ) {
352+ nextOffsetY += targetRect . y - visibleArea . bottom + numShiftY ;
299353 }
300354 }
301355 }
0 commit comments