13
13
import { Collection } from '@react-types/shared' ;
14
14
import { focusWithoutScrolling , mergeProps , useLayoutEffect } from '@react-aria/utils' ;
15
15
import { Layout , Rect , ReusableView , useVirtualizerState , VirtualizerState } from '@react-stately/virtualizer' ;
16
- import React , { FocusEvent , HTMLAttributes , Key , ReactElement , RefObject , useCallback , useEffect , useRef } from 'react' ;
16
+ import React , { FocusEvent , HTMLAttributes , Key , ReactElement , RefObject , useCallback , useEffect , useMemo , useRef } from 'react' ;
17
17
import { ScrollView } from './ScrollView' ;
18
18
import { VirtualizerItem } from './VirtualizerItem' ;
19
19
@@ -140,6 +140,32 @@ export function useVirtualizer<T extends object, V, W>(props: VirtualizerOptions
140
140
lastFocusedKey . current = focusedKey ;
141
141
} , [ focusedKey , virtualizer . visibleRect . height , virtualizer , lastFocusedKey , scrollToItem ] ) ;
142
142
143
+ // Define an IntersectionObserver to evaluate whether the browser should scroll the element receiving focus into view.
144
+ let intersectionObserver = useMemo ( ( ) => {
145
+ const intersectionObserverOptions :IntersectionObserverInit = {
146
+ root : undefined ,
147
+ rootMargin : '0px' ,
148
+ threshold : 1
149
+ } ;
150
+
151
+ const scrollIntoViewOptions :ScrollIntoViewOptions = {
152
+ behavior : 'auto' ,
153
+ block : 'nearest' ,
154
+ inline : 'nearest'
155
+ } ;
156
+
157
+ const intersectionObserverCallback = ( entries :Array < IntersectionObserverEntry > , observer :IntersectionObserver ) => {
158
+ entries . forEach ( entry => {
159
+ if ( ! entry . isIntersecting ) {
160
+ entry . target . scrollIntoView ( scrollIntoViewOptions ) ;
161
+ }
162
+ observer . unobserve ( entry . target ) ;
163
+ } ) ;
164
+ } ;
165
+
166
+ return new IntersectionObserver ( intersectionObserverCallback , intersectionObserverOptions ) ;
167
+ } , [ ] ) ;
168
+
143
169
let isFocusWithin = useRef ( false ) ;
144
170
let onFocus = useCallback ( ( e : FocusEvent ) => {
145
171
// If the focused item is scrolled out of view and is not in the DOM, the collection
@@ -154,7 +180,12 @@ export function useVirtualizer<T extends object, V, W>(props: VirtualizerOptions
154
180
}
155
181
156
182
isFocusWithin . current = e . target !== ref . current ;
157
- } , [ ref , virtualizer , focusedKey , scrollToItem ] ) ;
183
+
184
+ // Evaluate whether the browser should scroll to bring element receiving focus into view.
185
+ if ( isFocusWithin . current ) {
186
+ intersectionObserver . observe ( e . target ) ;
187
+ }
188
+ } , [ ref , virtualizer , focusedKey , scrollToItem , intersectionObserver ] ) ;
158
189
159
190
let onBlur = useCallback ( ( e : FocusEvent ) => {
160
191
isFocusWithin . current = ref . current . contains ( e . relatedTarget as Element ) ;
0 commit comments