@@ -755,7 +755,7 @@ class VirtualizedList extends React.PureComponent<Props, State> {
755755 static _createRenderMask (
756756 props : Props ,
757757 cellsAroundViewport : { first : number , last : number } ,
758- lastFocusedItem : ?number ,
758+ additionalRegions ? : ?$ReadOnlyArray < { first : number , last : number } > ,
759759 ) : CellRenderMask {
760760 const itemCount = props . getItemCount ( props . data ) ;
761761
@@ -768,17 +768,12 @@ class VirtualizedList extends React.PureComponent<Props, State> {
768768
769769 const renderMask = new CellRenderMask ( itemCount ) ;
770770
771- // Keep the items around the last focused rendered, to allow for keyboard
772- // navigation
773- if ( lastFocusedItem ) {
774- const first = Math . max ( 0 , lastFocusedItem - 1 ) ;
775- const last = Math . min ( itemCount - 1 , lastFocusedItem + 1 ) ;
776- renderMask . addCells ( { first, last} ) ;
777- }
778-
779771 if ( itemCount > 0 ) {
780- if ( cellsAroundViewport . last >= cellsAroundViewport . first ) {
781- renderMask . addCells ( cellsAroundViewport ) ;
772+ const allRegions = [ cellsAroundViewport , ...( additionalRegions ?? [ ] ) ] ;
773+ for ( const region of allRegions ) {
774+ if ( region . last >= region . first ) {
775+ renderMask . addCells ( region ) ;
776+ }
782777 }
783778
784779 // The initially rendered cells are retained as part of the
@@ -1016,7 +1011,7 @@ class VirtualizedList extends React.PureComponent<Props, State> {
10161011 prevCellKey = { prevCellKey }
10171012 onUpdateSeparators = { this . _onUpdateSeparators }
10181013 onLayout = { e => this . _onCellLayout ( e , key , ii ) }
1019- onFocusCapture = { e => this . _onCellFocusCapture ( ii ) }
1014+ onFocusCapture = { e => this . _onCellFocusCapture ( key ) }
10201015 onUnmount = { this . _onCellUnmount }
10211016 parentProps = { this . props }
10221017 ref = { ref => {
@@ -1364,7 +1359,7 @@ class VirtualizedList extends React.PureComponent<Props, State> {
13641359 _averageCellLength = 0 ;
13651360 // Maps a cell key to the set of keys for all outermost child lists within that cell
13661361 _cellKeysToChildListKeys : Map < string , Set < string > > = new Map ( ) ;
1367- _cellRefs = { } ;
1362+ _cellRefs : { [ string ] : ? CellRenderer } = { } ;
13681363 _fillRateHelper: FillRateHelper;
13691364 _frames = { } ;
13701365 _footerLength = 0;
@@ -1376,7 +1371,7 @@ class VirtualizedList extends React.PureComponent<Props, State> {
13761371 _hiPriInProgress: boolean = false; // flag to prevent infinite hiPri cell limit update
13771372 _highestMeasuredFrameIndex = 0;
13781373 _indicesToKeys: Map< number , string > = new Map();
1379- _lastFocusedItem : ?number = null;
1374+ _lastFocusedCellKey : ?string = null;
13801375 _nestedChildLists: Map<
13811376 string ,
13821377 {
@@ -1485,12 +1480,12 @@ class VirtualizedList extends React.PureComponent<Props, State> {
14851480 this._updateViewableItems(this.props.data);
14861481 }
14871482
1488- _onCellFocusCapture ( itemIndex : number ) {
1489- this . _lastFocusedItem = itemIndex ;
1483+ _onCellFocusCapture ( cellKey : string ) {
1484+ this . _lastFocusedCellKey = cellKey ;
14901485 const renderMask = VirtualizedList . _createRenderMask (
14911486 this . props ,
14921487 this . state . cellsAroundViewport ,
1493- this . _lastFocusedItem ,
1488+ this . _getNonViewportRenderRegions ( ) ,
14941489 ) ;
14951490
14961491 if ( ! renderMask . equals ( this . state . renderMask ) ) {
@@ -1847,7 +1842,7 @@ class VirtualizedList extends React.PureComponent<Props, State> {
18471842 }
18481843 // Mark as high priority if we're close to the end of the last item
18491844 // But only if there are items after the last rendered item
1850- if (last < itemCount - 1 ) {
1845+ if (last > 0 && last < itemCount - 1 ) {
18511846 const distBottom =
18521847 this . _getFrameMetricsApprox ( last ) . offset - ( offset + visibleLength ) ;
18531848 hiPri =
@@ -1926,7 +1921,7 @@ class VirtualizedList extends React.PureComponent<Props, State> {
19261921 const renderMask = VirtualizedList . _createRenderMask (
19271922 props ,
19281923 cellsAroundViewport ,
1929- this . _lastFocusedItem ,
1924+ this . _getNonViewportRenderRegions ( ) ,
19301925 ) ;
19311926
19321927 if (
@@ -1959,7 +1954,11 @@ class VirtualizedList extends React.PureComponent<Props, State> {
19591954 // check for invalid frames due to row re-ordering
19601955 return frame ;
19611956 } else {
1962- const { getItemLayout } = this . props ;
1957+ const { data , getItemCount, getItemLayout} = this . props ;
1958+ invariant (
1959+ index >= 0 && index < getItemCount ( data ) ,
1960+ 'Tried to get frame for out of range index ' + index ,
1961+ ) ;
19631962 invariant (
19641963 ! getItemLayout ,
19651964 'Should not have to estimate frames when a measurement metrics function is provided' ,
@@ -1982,7 +1981,7 @@ class VirtualizedList extends React.PureComponent<Props, State> {
19821981 } => {
19831982 const { data, getItem, getItemCount, getItemLayout} = this . props ;
19841983 invariant (
1985- getItemCount ( data ) > index ,
1984+ index >= 0 && index < getItemCount ( data ) ,
19861985 'Tried to get frame for out of range index ' + index ,
19871986 ) ;
19881987 const item = getItem ( data , index ) ;
@@ -1998,6 +1997,57 @@ class VirtualizedList extends React.PureComponent<Props, State> {
19981997 return frame ;
19991998 } ;
20001999
2000+ _getNonViewportRenderRegions = ( ) : $ReadOnlyArray < {
2001+ first : number ,
2002+ last : number ,
2003+ } > => {
2004+ // Keep a viewport's worth of content around the last focused cell to allow
2005+ // random navigation around it without any blanking. E.g. tabbing from one
2006+ // focused item out of viewport to another.
2007+ if (
2008+ ! ( this . _lastFocusedCellKey && this . _cellRefs [ this . _lastFocusedCellKey ] )
2009+ ) {
2010+ return [ ] ;
2011+ }
2012+
2013+ const lastFocusedCellRenderer = this . _cellRefs [ this . _lastFocusedCellKey ] ;
2014+ const focusedCellIndex = lastFocusedCellRenderer . props . index ;
2015+ const itemCount = this . props . getItemCount ( this . props . data ) ;
2016+
2017+ // The cell may have been unmounted and have a stale index
2018+ if (
2019+ focusedCellIndex >= itemCount ||
2020+ this . _indicesToKeys . get ( focusedCellIndex ) !== this . _lastFocusedCellKey
2021+ ) {
2022+ return [ ] ;
2023+ }
2024+
2025+ let first = focusedCellIndex ;
2026+ let heightOfCellsBeforeFocused = 0 ;
2027+ for (
2028+ let i = first - 1 ;
2029+ i >= 0 && heightOfCellsBeforeFocused < this . _scrollMetrics . visibleLength ;
2030+ i --
2031+ ) {
2032+ first -- ;
2033+ heightOfCellsBeforeFocused += this . _getFrameMetricsApprox ( i ) . length ;
2034+ }
2035+
2036+ let last = focusedCellIndex ;
2037+ let heightOfCellsAfterFocused = 0 ;
2038+ for (
2039+ let i = last + 1 ;
2040+ i < itemCount &&
2041+ heightOfCellsAfterFocused < this . _scrollMetrics . visibleLength ;
2042+ i ++
2043+ ) {
2044+ last ++ ;
2045+ heightOfCellsAfterFocused += this . _getFrameMetricsApprox ( i ) . length ;
2046+ }
2047+
2048+ return [ { first , last } ] ;
2049+ } ;
2050+
20012051 _updateViewableItems ( data : any ) {
20022052 const { getItemCount} = this . props ;
20032053
0 commit comments