@@ -755,7 +755,7 @@ class VirtualizedList extends React.PureComponent<Props, State> {
755
755
static _createRenderMask (
756
756
props : Props ,
757
757
cellsAroundViewport : { first : number , last : number } ,
758
- lastFocusedItem : ?number ,
758
+ additionalRegions ? : ?$ReadOnlyArray < { first : number , last : number } > ,
759
759
) : CellRenderMask {
760
760
const itemCount = props . getItemCount ( props . data ) ;
761
761
@@ -768,17 +768,12 @@ class VirtualizedList extends React.PureComponent<Props, State> {
768
768
769
769
const renderMask = new CellRenderMask ( itemCount ) ;
770
770
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
-
779
771
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
+ }
782
777
}
783
778
784
779
// The initially rendered cells are retained as part of the
@@ -1016,7 +1011,7 @@ class VirtualizedList extends React.PureComponent<Props, State> {
1016
1011
prevCellKey = { prevCellKey }
1017
1012
onUpdateSeparators = { this . _onUpdateSeparators }
1018
1013
onLayout = { e => this . _onCellLayout ( e , key , ii ) }
1019
- onFocusCapture = { e => this . _onCellFocusCapture ( ii ) }
1014
+ onFocusCapture = { e => this . _onCellFocusCapture ( key ) }
1020
1015
onUnmount = { this . _onCellUnmount }
1021
1016
parentProps = { this . props }
1022
1017
ref = { ref => {
@@ -1364,7 +1359,7 @@ class VirtualizedList extends React.PureComponent<Props, State> {
1364
1359
_averageCellLength = 0 ;
1365
1360
// Maps a cell key to the set of keys for all outermost child lists within that cell
1366
1361
_cellKeysToChildListKeys : Map < string , Set < string > > = new Map ( ) ;
1367
- _cellRefs = { } ;
1362
+ _cellRefs : { [ string ] : ? CellRenderer } = { } ;
1368
1363
_fillRateHelper: FillRateHelper;
1369
1364
_frames = { } ;
1370
1365
_footerLength = 0;
@@ -1376,7 +1371,7 @@ class VirtualizedList extends React.PureComponent<Props, State> {
1376
1371
_hiPriInProgress: boolean = false; // flag to prevent infinite hiPri cell limit update
1377
1372
_highestMeasuredFrameIndex = 0;
1378
1373
_indicesToKeys: Map< number , string > = new Map();
1379
- _lastFocusedItem : ?number = null;
1374
+ _lastFocusedCellKey : ?string = null;
1380
1375
_nestedChildLists: Map<
1381
1376
string ,
1382
1377
{
@@ -1485,12 +1480,12 @@ class VirtualizedList extends React.PureComponent<Props, State> {
1485
1480
this._updateViewableItems(this.props.data);
1486
1481
}
1487
1482
1488
- _onCellFocusCapture ( itemIndex : number ) {
1489
- this . _lastFocusedItem = itemIndex ;
1483
+ _onCellFocusCapture ( cellKey : string ) {
1484
+ this . _lastFocusedCellKey = cellKey ;
1490
1485
const renderMask = VirtualizedList . _createRenderMask (
1491
1486
this . props ,
1492
1487
this . state . cellsAroundViewport ,
1493
- this . _lastFocusedItem ,
1488
+ this . _getNonViewportRenderRegions ( ) ,
1494
1489
) ;
1495
1490
1496
1491
if ( ! renderMask . equals ( this . state . renderMask ) ) {
@@ -1926,7 +1921,7 @@ class VirtualizedList extends React.PureComponent<Props, State> {
1926
1921
const renderMask = VirtualizedList . _createRenderMask (
1927
1922
props ,
1928
1923
cellsAroundViewport ,
1929
- this . _lastFocusedItem ,
1924
+ this . _getNonViewportRenderRegions ( ) ,
1930
1925
) ;
1931
1926
1932
1927
if (
@@ -1998,6 +1993,57 @@ class VirtualizedList extends React.PureComponent<Props, State> {
1998
1993
return frame ;
1999
1994
} ;
2000
1995
1996
+ _getNonViewportRenderRegions = ( ) : $ReadOnlyArray < {
1997
+ first : number ,
1998
+ last : number ,
1999
+ } > => {
2000
+ // Keep a viewport's worth of content around the last focused cell to allow
2001
+ // random navigation around it without any blanking. E.g. tabbing from one
2002
+ // focused item out of viewport to another.
2003
+ if (
2004
+ ! ( this . _lastFocusedCellKey && this . _cellRefs [ this . _lastFocusedCellKey ] )
2005
+ ) {
2006
+ return [ ] ;
2007
+ }
2008
+
2009
+ const lastFocusedCellRenderer = this . _cellRefs [ this . _lastFocusedCellKey ] ;
2010
+ const focusedCellIndex = lastFocusedCellRenderer . props . index ;
2011
+ const itemCount = this . props . getItemCount ( this . props . data ) ;
2012
+
2013
+ // The cell may have been unmounted and have a stale index
2014
+ if (
2015
+ focusedCellIndex >= itemCount ||
2016
+ this . _indicesToKeys . get ( focusedCellIndex ) !== this . _lastFocusedCellKey
2017
+ ) {
2018
+ return [ ] ;
2019
+ }
2020
+
2021
+ let first = focusedCellIndex ;
2022
+ let heightOfCellsBeforeFocused = 0 ;
2023
+ for (
2024
+ let i = first - 1 ;
2025
+ i >= 0 && heightOfCellsBeforeFocused < this . _scrollMetrics . visibleLength ;
2026
+ i --
2027
+ ) {
2028
+ first -- ;
2029
+ heightOfCellsBeforeFocused += this . _getFrameMetricsApprox ( i ) . length ;
2030
+ }
2031
+
2032
+ let last = focusedCellIndex ;
2033
+ let heightOfCellsAfterFocused = 0 ;
2034
+ for (
2035
+ let i = last + 1 ;
2036
+ i < itemCount &&
2037
+ heightOfCellsAfterFocused < this . _scrollMetrics . visibleLength ;
2038
+ i ++
2039
+ ) {
2040
+ last ++ ;
2041
+ heightOfCellsAfterFocused += this . _getFrameMetricsApprox ( i ) . length ;
2042
+ }
2043
+
2044
+ return [ { first , last } ] ;
2045
+ } ;
2046
+
2001
2047
_updateViewableItems ( data : any ) {
2002
2048
const { getItemCount} = this . props ;
2003
2049
0 commit comments