@@ -3265,14 +3265,17 @@ if (enableFragmentRefsScrollIntoView) {
32653265 const children : Array < Fiber > = [ ] ;
32663266 traverseFragmentInstance ( this . _fragmentFiber , collectChildren , children ) ;
32673267
3268+ const resolvedAlignToTop = alignToTop !== false ;
3269+
32683270 // If there are no children, we can use the parent and siblings to determine a position
32693271 if ( children . length === 0 ) {
32703272 const hostSiblings = getFragmentInstanceSiblings ( this . _fragmentFiber ) ;
3271- const targetFiber =
3272- ( alignToTop === false
3273- ? hostSiblings [ 0 ] || hostSiblings [ 1 ]
3274- : hostSiblings [ 1 ] || hostSiblings [ 0 ] ) ||
3275- getFragmentParentHostFiber ( this . _fragmentFiber ) ;
3273+ const targetFiber = resolvedAlignToTop
3274+ ? hostSiblings [ 1 ] ||
3275+ hostSiblings [ 0 ] ||
3276+ getFragmentParentHostFiber ( this . _fragmentFiber )
3277+ : hostSiblings [ 0 ] || hostSiblings [ 1 ] ;
3278+
32763279 if ( targetFiber === null ) {
32773280 if ( __DEV__ ) {
32783281 console . warn (
@@ -3287,197 +3290,14 @@ if (enableFragmentRefsScrollIntoView) {
32873290 return ;
32883291 }
32893292
3290- // If there are children, handle them per scroll container
3291- scrollIntoViewByScrollContainer ( children , alignToTop !== false ) ;
3292- } ;
3293- }
3294-
3295- const CONTAINER_STD = 0 ;
3296- const CONTAINER_FIXED = 1 ;
3297- const CONTAINER_SCROLL = 2 ;
3298- type ScrollableContainerType = 0 | 1 | 2 ;
3299- function isInstanceScrollable ( inst : Instance ) : ScrollableContainerType {
3300- const style = getComputedStyle ( inst ) ;
3301-
3302- if ( style . position === 'fixed' ) {
3303- return CONTAINER_FIXED ;
3304- }
3305-
3306- if (
3307- style . overflow === 'auto' ||
3308- style . overflow === 'scroll' ||
3309- style . overflowY === 'auto' ||
3310- style . overflowY === 'scroll' ||
3311- style . overflowX === 'auto' ||
3312- style . overflowX === 'scroll'
3313- ) {
3314- return CONTAINER_SCROLL ;
3315- }
3316-
3317- return CONTAINER_STD ;
3318- }
3319-
3320- function searchDOMUntilCommonAncestor < T > (
3321- instA : Instance ,
3322- instB : Instance ,
3323- testFn : ( instA : Instance ) => T ,
3324- ) : T | null {
3325- // Walk up from instA and count depth
3326- let currentNode : ?Instance = instA ;
3327- let depthA = 0 ;
3328- while ( currentNode ) {
3329- const result = testFn ( currentNode ) ;
3330- if ( result ) {
3331- return result ;
3332- }
3333- depthA ++ ;
3334- currentNode = currentNode . parentElement ;
3335- }
3336-
3337- // Walk up from instB and count depth
3338- currentNode = instB ;
3339- let depthB = 0 ;
3340- while ( currentNode ) {
3341- const result = testFn ( currentNode ) ;
3342- if ( result ) {
3343- return result ;
3344- }
3345-
3346- depthB ++ ;
3347- currentNode = currentNode . parentElement ;
3348- }
3349-
3350- // Reset currentNode to instA and instB
3351- let nodeA : ?Instance = instA ;
3352- let nodeB : ?Instance = instB ;
3353-
3354- // Align depths
3355- while ( depthA > depthB && nodeA ) {
3356- nodeA = nodeA . parentElement ;
3357- depthA -- ;
3358- }
3359- while ( depthB > depthA && nodeB ) {
3360- nodeB = nodeB . parentElement ;
3361- depthB -- ;
3362- }
3363-
3364- // Walk up both nodes to find common ancestor
3365- while ( nodeA && nodeB ) {
3366- if ( nodeA === nodeB ) {
3367- return testFn ( nodeA ) ;
3368- }
3369- nodeA = nodeA . parentElement ;
3370- nodeB = nodeB . parentElement ;
3371- }
3372-
3373- return null ;
3374- }
3375-
3376- function maybeScrollContainerIntoView (
3377- currentInstance : Instance ,
3378- prevInstance : Instance | null ,
3379- alignToTop : boolean ,
3380- prevContainerIsFixed : boolean ,
3381- ) : boolean {
3382- if ( prevInstance === null || prevContainerIsFixed ) {
3383- currentInstance . scrollIntoView ( alignToTop ) ;
3384- return true ;
3385- }
3386-
3387- const currentRect = currentInstance . getBoundingClientRect ( ) ;
3388- const prevRect = prevInstance . getBoundingClientRect ( ) ;
3389-
3390- // Check if scrolling to current element would push previous element out of viewport
3391- // alignToTop=true: current goes to top, check if prev would still be visible below
3392- // alignToTop=false: current goes to bottom, check if prev would still be visible above
3393- const canScrollVertical = alignToTop
3394- ? currentRect . top + window . innerHeight > prevRect . top
3395- : currentRect . bottom - window . innerHeight < prevRect . bottom ;
3396- const canScrollHorizontal = alignToTop
3397- ? currentRect . left + window . innerWidth > prevRect . left
3398- : currentRect . right - window . innerWidth < prevRect . right ;
3399-
3400- if ( canScrollVertical && canScrollHorizontal ) {
3401- currentInstance . scrollIntoView ( alignToTop ) ;
3402- return true ;
3403- }
3404-
3405- return false ;
3406- }
3407-
3408- function scrollIntoViewByScrollContainer (
3409- children : Array < Fiber > ,
3410- alignToTop : boolean ,
3411- ) : void {
3412- // Loop through the children, order dependent on alignToTop
3413- // Each time we reach a new scroll container, we look back at the last one
3414- // and scroll the first or last child in that container, depending on alignToTop
3415- // alignToTop=true means iterate in reverse, scrolling the first child of each container
3416- // alignToTop=false means iterate in normal order, scrolling the last child of each container
3417- let prevScrolledInstance = null ;
3418- let prevContainerIsFixed = false ;
3419- let i = alignToTop ? children . length - 1 : 0 ;
3420- // We extend the loop one iteration beyond the actual children to handle the last group
3421- while ( i !== ( alignToTop ? - 2 : children . length + 1 ) ) {
3422- const isLastGroup = i < 0 || i >= children . length ;
3423- let isNewScrollContainer : null | ScrollableContainerType = null ;
3424-
3425- if ( isLastGroup ) {
3426- // We're past the end, treat as new scroll container to complete the last group
3427- isNewScrollContainer = CONTAINER_SCROLL ;
3428- } else {
3293+ let i = resolvedAlignToTop ? children . length - 1 : 0 ;
3294+ while ( i !== ( resolvedAlignToTop ? - 1 : children . length ) ) {
34293295 const child = children [ i ] ;
34303296 const instance = getInstanceFromHostFiber < Instance > ( child ) ;
3431- const prevChild = children [ alignToTop ? i + 1 : i - 1 ] ;
3432-
3433- if ( prevChild ) {
3434- const prevInstance = getInstanceFromHostFiber < Instance > ( prevChild ) ;
3435- if ( prevInstance . parentNode === instance . parentNode ) {
3436- // If these are DOM siblings, check if either is fixed
3437- isNewScrollContainer =
3438- isInstanceScrollable ( prevInstance ) === CONTAINER_FIXED ||
3439- isInstanceScrollable ( instance ) === CONTAINER_FIXED
3440- ? CONTAINER_FIXED
3441- : CONTAINER_STD ;
3442- } else {
3443- isNewScrollContainer = searchDOMUntilCommonAncestor (
3444- instance ,
3445- prevInstance ,
3446- isInstanceScrollable ,
3447- ) ;
3448- }
3449- }
3297+ instance . scrollIntoView ( alignToTop ) ;
3298+ i += resolvedAlignToTop ? - 1 : 1 ;
34503299 }
3451-
3452- if ( isNewScrollContainer ) {
3453- // We found a new scroll container, so scroll the appropriate child from the previous group
3454- let childToScrollIndex ;
3455- if ( alignToTop ) {
3456- childToScrollIndex = isLastGroup ? 0 : alignToTop ? i + 1 : i - 1 ;
3457- } else {
3458- childToScrollIndex = alignToTop ? i + 1 : i - 1 ;
3459- }
3460-
3461- if ( childToScrollIndex >= 0 && childToScrollIndex < children . length ) {
3462- const childToScroll = children [ childToScrollIndex ] ;
3463- const instanceToScroll =
3464- getInstanceFromHostFiber < Instance > ( childToScroll ) ;
3465-
3466- const didScroll = maybeScrollContainerIntoView (
3467- instanceToScroll ,
3468- prevScrolledInstance ,
3469- alignToTop ,
3470- prevContainerIsFixed ,
3471- ) ;
3472- if ( didScroll ) {
3473- prevScrolledInstance = instanceToScroll ;
3474- prevContainerIsFixed = isNewScrollContainer === CONTAINER_FIXED ;
3475- }
3476- }
3477- }
3478-
3479- i += alignToTop ? - 1 : 1 ;
3480- }
3300+ } ;
34813301}
34823302
34833303export function createFragmentInstance (
0 commit comments